Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic short flags #23

Merged
merged 1 commit into from
Mar 23, 2017
Merged

Automatic short flags #23

merged 1 commit into from
Mar 23, 2017

Conversation

anntzer
Copy link
Owner

@anntzer anntzer commented Mar 1, 2017

See #21. Note that this PR depends on #20 (the first few commits are from that PR).

Turning off automatic generation of short flags can be achieved by replacing the short = kwargs.pop('short', None) line by short = kwargs.pop('short', {}) (and updating the docs). But I'd rather keep it on by default.

@anntzer
Copy link
Owner Author

anntzer commented Mar 4, 2017

Rebased.

@anntzer
Copy link
Owner Author

anntzer commented Mar 16, 2017

Kindly bumping the issue.

@evanunderscore
Copy link
Collaborator

Thanks for your work on this. I've been intending to get to it but haven't found a large enough block of time to read through it properly. I'll try to review it this weekend.

@anntzer
Copy link
Owner Author

anntzer commented Mar 16, 2017

No worries. I have been using https://github.com/neithere/argh for most of my projects until recently. I learnt to get around the kinks, but the main problem is that the project is essentially abandoned at that point. I am looking towards switching to defopt, as I think parsing the docstring using a widespread standard (as opposed to, say, docopt) and using standard annotations (rather than attaching ad hoc semantics to them) were very, very good ideas; however, there are some kinks that need to be ironed out before the switch is worth it.

defopt.py Outdated
@@ -54,16 +54,18 @@ def run(*funcs, **kwargs):
:param Callable funcs: Function or functions to process and run
:param parsers: Dictionary mapping types to parsers to use for parsing
function arguments.
:type parsers: Dict[type, Callable[[str], type]]
:type parsers: Optional[Dict[type, Callable[[str], type]]]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it necessary to mark this as optional? The documentation for it says "An optional argument with a default needn’t use the Optional qualifier on its type annotation". Is there anything about this argument in particular that distinguishes it from short or argv?

Copy link
Owner Author

Choose a reason for hiding this comment

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

seems reasonable, changed

defopt.py Outdated
count_initials = Counter(name[0] for name in sig.parameters
if name not in positionals)
short = {name.replace('_', '-'): name[0] for name in sig.parameters
if name not in positionals and count_initials[name[0]] == 1}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would prefer to use single letter variable names in list and dictionary comprehensions. This code works fine currently, but the difference in behavior between Python 2 and 3 has caused me some grief in the past. (The variables remain in the current scope in 2, but not in 3).

Copy link
Owner Author

Choose a reason for hiding this comment

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

Switched to using generator expressions, which do not leak variables even on py2.

You can add alternative short flags to arguments by passing a dictionary
to `defopt.run` which maps flag names to single letters. You can
prevent automatic addition of short flags by passing an empty dictionary
as argument.
Copy link
Collaborator

Choose a reason for hiding this comment

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

These module docstrings are intended to be brief descriptions of what's happening in the example rather than full explanations of the features. Most of this probably belongs under the flags section in features.rst. (I have ended up duplicating a lot of documentation myself, which is not ideal.)

Copy link
Owner Author

Choose a reason for hiding this comment

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

moved to features.rst

@evanunderscore
Copy link
Collaborator

I think there are some edge cases where the global short dict you pass in won't be able to replicate exactly what the auto short generation does once more than one command is involved, but I think we can solve that later on if it becomes a problem. There would have to be a more granular way to specify flag names. Perhaps we'll need a decorator-based approach as was proposed in #2 after all.

@anntzer
Copy link
Owner Author

anntzer commented Mar 19, 2017

What are the edge cases you're thinking about?

You can add alternative short flags to arguments by passing a dictionary
to `defopt.run` which maps flag names to single letters. You can
prevent automatic addition of short flags by passing an empty dictionary
as argument::
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be "as an argument"?

Copy link
Owner Author

Choose a reason for hiding this comment

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

no?

Copy link
Collaborator

Choose a reason for hiding this comment

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

After rereading this section, I think it may need to be reworded - specifically "add alternative short flags" may be taken to imply that the given flags are used in addition to the generated ones, as opposed to overriding them entirely. How about something like this?

You can override these generated short flags by passing a dictionary to defopt.run which maps flag names to single letters::

[example]

Passing an empty dictionary will suppress automatic short flag generation without adding new flags.

I think the second part belongs after the example is it's not actually demonstrated in there. How does that seem to you?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Sounds good, fixed (with minor rewording).

@evanunderscore
Copy link
Collaborator

Consider:

def func1(bar: int = 1): pass
def func2(baz: int = 1): pass
def func3(bar: int = 1): pass
defopt.run(func1, func2, func3)

Each of these have a -b flag mapping to either bar or baz. If you then want to add baz to func3 (ie change the definition to def func3(bar: int = 1, baz: int = 1)), you lose the -b flag, and you can't restore it. Specifying {'bar': 'b', 'baz': 'b'} breaks for func3, and just {'bar': 'b'} doesn't fix func2 (though this could possibly work if the dict added to the defaults instead of overriding them entirely).

What are your thoughts on this?

@anntzer
Copy link
Owner Author

anntzer commented Mar 20, 2017

Perhaps short={"funcname:argname": "short", ...} when disambiguation is needed?

@evanunderscore
Copy link
Collaborator

That would work, but I'm not sure I like encoding information into the strings. Either way, I think we can defer this problem until the need arises. Once the documentation is cleared up I'm happy to merge this.

@anntzer anntzer mentioned this pull request Mar 22, 2017
@evanunderscore
Copy link
Collaborator

Thanks again for your work. Merging.

@evanunderscore evanunderscore merged commit 6ac0c38 into anntzer:master Mar 23, 2017
@anntzer anntzer deleted the autoshort branch June 19, 2017 03:00
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.

2 participants