Skip to content

Conversation

@daveads
Copy link
Contributor

@daveads daveads commented Nov 5, 2025

Addresses this issue... #2547

@daveads
Copy link
Contributor Author

daveads commented Nov 5, 2025

@andychu this seems to be a mycpp issue.

@andychu
Copy link
Contributor

andychu commented Nov 6, 2025

Thanks for looking at this! Can you add a failing test first in spec/builtin-getopts ?

Oh yeah and I guess this is the bug from help getopts

getopts reports errors in one of two ways.  If the first character

of OPTSTRING is a colon, getopts uses silent error reporting.

We didn't support that


I will look at the C++ errors afterward ... this does appear to be a mycpp problem, since MyPy passes

Not sure exactly why

@andychu
Copy link
Contributor

andychu commented Nov 6, 2025

(And btw, I find this leading : to be a very confusing special case, since : means something else usually! It means that the flag takes an agument)

@daveads
Copy link
Contributor Author

daveads commented Nov 10, 2025

Thanks for looking at this! Can you add a failing test first in spec/builtin-getopts ?

Oh yeah and I guess this is the bug from help getopts

getopts reports errors in one of two ways.  If the first character

of OPTSTRING is a colon, getopts uses silent error reporting.

We didn't support that

I will look at the C++ errors afterward ... this does appear to be a mycpp problem, since MyPy passes

Not sure exactly why

done

@andychu
Copy link
Contributor

andychu commented Nov 12, 2025

Hm there are 4 failures in OSH Python , but only 3 are allowed

https://op.oilshell.org/uuu/github-jobs/10775/

https://op.oilshell.org/uuu/github-jobs/10775/ovm-tarball.wwz/_tmp/spec/osh-py/builtin-getopts.html

( The ovm-tarball task has the Python spec test failures ... cpp-spec is for C++. It would be nice to move them closer)

Copy link
Contributor

@andychu andychu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm how about changing my_state.Fail() instead?

  • Right now there 3 invocations of my_state.Fail(), which sets OPTARG to ''
  • With this change there are 4 invocations
    • but you only changed 2 of them?

Should the other 2 invocations also be changed?

If so, I think it is cleaner to change my_state.Fail() to have the if statement

And then the _GetOpts() code will look cleaner, with fewer if statements -- we may not need one every time that we need to Fail()

And then the behavior will also be consistent


I would start by trying to tickle the other error paths with tests

Are they not respecting leading : now?

err:?
## END

#### getopts silent error reporting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm can you describe what this is testing? e.g. what is "silent error reporting" exactly?

Also should we assert STDERR does not contain an error message? I think it could be

## STDERR:
## END

which is different than if you don't have a leading : ?


I would like to see the two modes compared

from help getopts

If the first character of OPTSTRING is a colon, getopts uses silent error reporting.

In this mode, no error messages are printed.

If an invalid option is seen, getopts places the option character found into OPTARG.

OK good, we are testing this -- OPTARG=Z -- let's write a comment about this

If a required argument is not found, getopts places a ':' into NAME and sets OPTARG to the option character found.

Can we write a test for this too?

self.spec_cache[spec_str] = spec
# OPTERR defaults to 1
try:
opterr = state.GetInteger(self.mem, 'OPTERR')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding support for OPTERR

Can you also add a test for it?


# Hm doesn't cause status 1?
my_state.Fail()
if opterr != 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment that OPTERR=0 means that getopts disables error messages

( And also this is different than silent mode? That's weird ...)

@andychu
Copy link
Contributor

andychu commented Nov 20, 2025

Hm I also think it makes sense to put OPTERR in the GetOptsState() class as well . That is persisted across invocations

Then we can handle OPTERR in Fail() as well, rather than in _GetOpts() ?

(Or maybe we have to read OPTERR every time that Fail() is called? That is worth a test)

silent = spec_str.startswith(':')
if silent:
spec_str = spec_str[1:]
spec = _ParseOptSpec(spec_str)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm I also think we should preserve the spec_cache. Because getopts is often called in a loop like this

while getopts ':a:b:c:' flag; do
  ...
done

that prevents us from parsing the spec over and over again, on each iteration

So instead of Dict[str, bool], we could have a class, or we could use Zephyr ASDL


e.g. in core/runtime.asdl, it could be something like

GetOptsSpec = (Dict[str, bool] flags, bool silent)

And then we can cache that parsing in a dictionary self.spec_cache , e.g. Dict[str, GetOpsSpec]


I'm not sure if you've used Zephyr ASDL yet, but, it is a fairly important part of the codebase. It lets us define algebraic data types concisely

If you write out the same thing with a Python class, it is more verbose

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants