You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, argh has some somewhat inconsistent behaviour with respect to how arguments are presented based on their name:
Arg
Type
Name
Metavar
Usage
foo_bar
Positional
foo_bar
foo_bar
example.py foo_bar
_foo_bar
Positional
_foo_bar
_foo_bar
example.py _foo_bar
foo_bar=default
Option
--foo-bar
FOO_BAR
example.py [--foo-bar FOO_BAR]
_foo_bar=default
Option
---foo-bar
N/A
Crashes during assembly
*foo_bar
Multiple Positional
foo_bar
foo_bar
example.py [foo_bar [foo_bar ...]]
*_foo_bar
Multiple Positional
_foo_bar
_foo_bar
example.py [_foo_bar [_foo_bar ...]]
Note that positional args are lowercased with underscores, whereas options are replaced with dashes (for name) or uppercased (for metavar).
Note also that leading underscores are treated naively, which leads to a bug for options.
We cannot safely change the behaviour of names, as this will break compatability in a serious way (people's @arg decorators will no longer match the right args), but this is fine since it's not name that matters to the usage string for positional args.
I pretty strongly believe that lowercased metavars for positional args is a mistake. In most usage conventions, lowercase strings are intended to be taken as literals and uppercase as variables, eg. from ip link help: Usage: ip link add [link DEV] [ name ] NAME. This also clears up the consistency of dashes vs underscores, since positional arg metavars will now act the same as option metavars, ie. FOO_BAR.
The other option to resolve the inconsistency would be to set the metavar as foo-bar, and this is the solution suggested in neithere#106. But I would rather have positional metavars match option metavars rather than option names.
Then there is the question of underscore-prefixed args.
By python conventions this would denote an "internal use only" argument.
This seems like a reasonable convention for us to adopt, where underscore-prefixed arguments with defaults are simply ignored when assembling, as we assume they're for use internally within the application.
This also works for *_args - we can assume the extra args are to be passed only in internal calls, though this would be a backwards-incompatible change.
However, this doesn't map cleanly to single arguments without defaults - we need to give some value for these args.
I see three options:
Keep the current behaviour of treating these args no differently to ones without underscores
Pass None for these args
Raise an error for this case during assembly
I am leaning towards option 2, but I could see any of these options as being fairly sane behaviour here (especially since the user could trivially add a =None if option 2 is what they actually want).
If we go with option 1, it is a strong argument for also keeping the current behaviour for *_args for consistency.
So then, assuming option 1 above, this would be our new behaviour:
Arg
Type
Name
Metavar
Usage
foo_bar
Positional
foo_bar
FOO_BAR
example.py FOO_BAR
_foo_bar
Positional
_foo_bar
_FOO_BAR
example.py _FOO_BAR
foo_bar=default
Option
--foo-bar
FOO_BAR
example.py [--foo-bar FOO_BAR]
_foo_bar=default
Internal
N/A
N/A
Ignored, always called with default
*foo_bar
Multiple Positional
foo_bar
FOO_BAR
example.py [FOO_BAR [FOO_BAR ...]]
*_foo_bar
Multiple Positional
_foo_bar
_FOO_BAR
example.py [_FOO_BAR [_FOO_BAR ...]]
One very nice thing about these choices is that the only backwards-incompatible changes are to the exact syntax of the usage (which no-one should be programatically relying on), plus behaviour that used to be an error.
The text was updated successfully, but these errors were encountered:
Currently, argh has some somewhat inconsistent behaviour with respect to how arguments are presented based on their name:
foo_bar
foo_bar
foo_bar
example.py foo_bar
_foo_bar
_foo_bar
_foo_bar
example.py _foo_bar
foo_bar=default
--foo-bar
FOO_BAR
example.py [--foo-bar FOO_BAR]
_foo_bar=default
---foo-bar
*foo_bar
foo_bar
foo_bar
example.py [foo_bar [foo_bar ...]]
*_foo_bar
_foo_bar
_foo_bar
example.py [_foo_bar [_foo_bar ...]]
Note that positional args are lowercased with underscores, whereas options are replaced with dashes (for name) or uppercased (for metavar).
Note also that leading underscores are treated naively, which leads to a bug for options.
We cannot safely change the behaviour of names, as this will break compatability in a serious way (people's
@arg
decorators will no longer match the right args), but this is fine since it's not name that matters to the usage string for positional args.See also discussion from upstream: neithere#79, neithere#106
I pretty strongly believe that lowercased metavars for positional args is a mistake. In most usage conventions, lowercase strings are intended to be taken as literals and uppercase as variables, eg. from
ip link help
:Usage: ip link add [link DEV] [ name ] NAME
. This also clears up the consistency of dashes vs underscores, since positional arg metavars will now act the same as option metavars, ie.FOO_BAR
.The other option to resolve the inconsistency would be to set the metavar as
foo-bar
, and this is the solution suggested in neithere#106. But I would rather have positional metavars match option metavars rather than option names.Then there is the question of underscore-prefixed args.
By python conventions this would denote an "internal use only" argument.
This seems like a reasonable convention for us to adopt, where underscore-prefixed arguments with defaults are simply ignored when assembling, as we assume they're for use internally within the application.
This also works for
*_args
- we can assume the extra args are to be passed only in internal calls, though this would be a backwards-incompatible change.However, this doesn't map cleanly to single arguments without defaults - we need to give some value for these args.
I see three options:
None
for these argsI am leaning towards option 2, but I could see any of these options as being fairly sane behaviour here (especially since the user could trivially add a
=None
if option 2 is what they actually want).If we go with option 1, it is a strong argument for also keeping the current behaviour for
*_args
for consistency.So then, assuming option 1 above, this would be our new behaviour:
foo_bar
foo_bar
FOO_BAR
example.py FOO_BAR
_foo_bar
_foo_bar
_FOO_BAR
example.py _FOO_BAR
foo_bar=default
--foo-bar
FOO_BAR
example.py [--foo-bar FOO_BAR]
_foo_bar=default
*foo_bar
foo_bar
FOO_BAR
example.py [FOO_BAR [FOO_BAR ...]]
*_foo_bar
_foo_bar
_FOO_BAR
example.py [_FOO_BAR [_FOO_BAR ...]]
One very nice thing about these choices is that the only backwards-incompatible changes are to the exact syntax of the usage (which no-one should be programatically relying on), plus behaviour that used to be an error.
The text was updated successfully, but these errors were encountered: