Comments (3)
I have been marinating on this for a while. And now I think I might be changing my mind.
Back in the day, options were required to come before (positional) arguments on command-lines. You couldn't say fgrep foo -i
, you had to say fgrep -i foo
. Allowing the former is a modern relaxation of the old rules.
So maybe "painful" isn't the word to describe the boundary condition described above. It's just a fact of life.
If my example above:
def o2(a, *, verbose=False): ...
@app.command()
def command2(arg, *things: o2): ...
had usage like this:
command2 arg [[-v|--verbose] a]...
where you were required to put option -v
before the positional argument a
, that doesn't seem all that bad.
To be precise: Appeal would require the options to be specified on the command-line before the last positional argument mapping to the args
parameter. If your code looked like this:
def o3(a, b, c, *, verbose=False): ...
@app.command()
def command3(arg, *things: o3): ...
then usage would look like this:
command3 arg [[-v|--verbose] a b c]...
but in actuality you could specify the -v
before a
, after a
but before b
, or after b
but before c
. Just as long as it was before c
. If you specified -v
after c
, it would map to the next invocation of o3()
.
So in this command-line:
% myscript.py command3 arg a b -v c
we'd call o3()
once with verbose=True
.
As a reminder, in this new world order, options in an optional group get optimistically lazy-mapped to the command-line when we complete all the positional arguments from the previous group.
--
I worry I'm not explaining this well, so I'm going to walk you--the reader--through it all the way.
When Appeal parses the command-line, it's building function calls. At at the point that Appeal runs out of input, if the function call is valid, Appeal is happy. If the function call is invalid, Appeal is unhappy and throws an exception.
Let's say Appeal is in the middle of parsing this command-line:
% myscript command3 myarg ...
myscript
was the program the user ran, from their shell; the rest are arguments to that program, which Appeal is parsing. So far Appeal has seen two things, command3
and myarg
. (The ellipses there indicate "the rest of the input, whatever it is".) From this, Appeal has started building a call to command3()
, and has filled its arg
parameter with myarg
. At this exact moment, the function is callable--if there was no more input, Appeal would be happy and could successfully call command3()
.
Internally, Appeal has finished command3
's first argument group, which is a mandatory group. All it contained was one positional argument, arg
. The next argument group is optional, and it maps one option (-v
) and three positional arguments. So if there was another positional argument on the command-line, Appeal would say "okay, we're building a call to o3()
now`, and it would require a total of three positional arguments.
At this exact moment--where we haven't noticed whether or not there's another positional argument on the command-line--Appeal will now provisionally map -v
to the command-line, by which I mean it will add -v
to its internal list of options that are valid right now. But that wouldn't necessitate calling o3()
. If there were no more arguments, Appeal will say "oh I guess we're not calling o3()
after all, that's fine", and it'd be happy and call command3()
as per the above.
Anyway. After Appeal provisionally maps -v
to the command-line, the next thing it will do is pull out the next argument from the command-line argument iterator. Which, if we want to be all-encompassing here, could be one of four things:
- The command-line could already be exhausted, and there are no more arguments of any description. In which case, Appeal is happy, and it can call
command3()
, and it doesn't mind that it's not ever going to callo3()
. (Thethings
argument tocommand3()
will be an empty iterable.) - The next command-line argument doesn't start with a dash, in which case it's a positional argument. Appeal will say "okay, I have to build a call to
o3()
". This argument will be the first argument too3()
, and Appeal is gonna need two more if we're going to have a valid command-line. (The result of the call too3()
will be passed in tocommand3()
as an entry in thethings
iterable.) - The next command-line argument could be
-v
, in which case Appeal will also say "okay, I have to build a call too3()
". Appeal is gonna need three more arguments to fill in for this call too3()
, for this to be a valid command-line. - The next command-line argument starts with a dash, but isn't
-v
or--verbose
, in which case the command-line is illegal. (I told you we were covering all bases.)
So finally, here's the funny side-effect of these semantics. In this world, the following command-line:
% myscript.py command3 arg a b c -v
is invalid, because Appeal wants to call o3()
twice. By the time Appeal sees the -v
, it's already built the first call to o3()
--that one's all done and ready to go. The -v
it sees at the end there tells Appeal to build a second call to o3()
, and we don't have any more positional arguments to use to fill it in, and so Appeal is unhappy and throws an exception. And now we need to figure out how to intelligibly communicate that to the user in a short string we can display before usage.
--
There are really three possibilities for how to handle options in repeating optional groups (*args
):
- They're provisionally bound before the first positional parameter in the repeating optional group, and get rebound after we see the last provisional parameter. (This is the scenario I described in this post in this issue.)
- They're always bound after the first positional parameter in the repeating optional group, and only get rebound after we see the first provisional parameter in the next instance of that repeating optional group. (This is the scenario I described in my previous post in this issue.)
- Like 2., except that the first time we bind them provisionally before the first positional parameter as per 1.
I can see the benefits of each of these. I don't expect to do 3, because I think consistency is probably better than the mild convenience afforded by its inconsistency. And, as I said at the top of this message, I'm now tilting away from 2 and towards 1.
from appeal.
Done!
from appeal.
And for the record, I went with the early-binding (and therefore early-un-binding). I think the mild surprise folks might experience on the rare occasion an option is un-bound (or re-bound) earlier than they expected is minuscule, compared to the big win of options being available early the way folks would normally expect.
from appeal.
Related Issues (13)
- Suppress ValueError in "convert" phase, convert to usage exception HOT 1
- Type annotation of "bool" causes error producing usage docs HOT 3
- Typing loguru logger - AttributeError: 'NoneType' object has no attribute 'get_signature' HOT 2
- "--" can not be passed as argument
- Wrong command use response is too unspecific
- appeal steals all stdout (bug?)
- Bug: README mentions missing document for how to write documentation HOT 1
- Passing global options to commands HOT 10
- Traceback is not useful when a command is missing arguments HOT 5
- Improve the error message generated when a required parameter appears after a converter with a VAR_POSITIONAL parameter HOT 3
- Appeal uses equals-signs-in-f-strings, but that's a 3.8 feature and Appeal supports 3.6+ HOT 2
- Figure out how to communicate the concept of "argument groups" better in usage HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from appeal.