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

Support customizable short flags #2

Closed
evanunderscore opened this issue Feb 16, 2016 · 18 comments
Closed

Support customizable short flags #2

evanunderscore opened this issue Feb 16, 2016 · 18 comments
Milestone

Comments

@evanunderscore
Copy link
Collaborator

Currently defopt allows for no customization of the command line. There are some things that could be made more flexible without violating the spirit of defopt if implemented well.

Possibilities:

  • Short flags, e.g. -c/--count
  • "store_const" flags, e.g. --thing/--not-thing
  • Others?

One potential solution is to parse this information out of RST comments so the generated documentation is not polluted.

def func(count=1, up=False):
    """Do something

    :param int count: desc
    :param bool up: desc

    .. count: -c/--count COUNT
    .. up: --up=True --down=False
    """
@evanunderscore evanunderscore changed the title Allow customization of flag names Allow customization of flag names (concept) Feb 19, 2016
@evanunderscore
Copy link
Collaborator Author

These comments are valid for Google/Numpy docstrings but must be above the fields.

@evanunderscore
Copy link
Collaborator Author

Going to hold off on implementing anything until I have a better idea of potential use cases.

@evanunderscore
Copy link
Collaborator Author

One downside to doing this inside the docstring is that it introduces magic that doesn't exist outside defopt, which I've so far avoided. Perhaps it would be better to just pass something to defopt.run?

@flying-sheep
Copy link

flying-sheep commented Apr 15, 2016

i filed #12 for flags (called “store_const” in the original comment), this can be about customization.

my idea: provide a decorator after all. it would add metadata to the function object.

apart from custom flag/option names it would be cool to have a simple way to use dashes instead of underscores.

we’d have the decorator defopt.opts:

# Supports either arg_name='s', arg_name='long' or arg_name=('long', 's', 'alt-long', ...)
def opts(_use_dashes=False, **opt_names):
    final_opt_names = {}
    for arg_name, spec in opt_names.items():
        if isinstance(spec, str):
            spec = (spec,)
        assert isinstance(spec, tuple), 'spec must be str or tuple, not {}'.format(type(spec))
        assert not any(len(n) < 1 for n in spec), 'empty string found in spec {!r}'.format(spec)

        long = [n for n in spec if len(n) > 1]
        short = [n for n in spec if len(n) == 1]

        # default to python kwarg name if no long name is given
        # explicit long name trumps _use_dashes
        if not long:
            long = [arg_name.replace('_', '-') if use_dashes else arg_name]

        final_opt_names[arg_name] = {'short': short, 'long': long}

    def decorator(func):
        setattr(func, '__defopt_names', final_opt_names)
        return func

    return decorator

file my-cmd (shebang, no .py):

#!/usr/bin/env python
import defopt

@defopt.opts(True, long_opt='l', legend='g', custom=('individual', 'c'))
def my_cmd(long_opt=1, legend=True, custom='a'):
    print(long, legend, custom)

if __name__ == '__main__':
    defopt.run(my_cmd, use_dashes=True)
$ ./my-cmd --long-opt 5 -g -c foo
5 True foo
$ ./my-cmd --no-legend
1 False a

@evanunderscore
Copy link
Collaborator Author

On dashes versus underscores: I hadn't considered this originally, but I feel like this would be good for the default behavior. If someone actually wants underscores in their flags, it could probably be an argument to defopt.run.

For decorators, this sort of thing is already quite nicely covered by argh, and also by plac which I mentioned in the readme (I might put argh in there instead). Part of the reason I didn't want to include a decorator interface is that I don't want defopt to just become a less flexible equivalent of these existing parsers.

My thinking lately is that it would be nice to handle this in a similar way to parsers. In the same way the parsers argument to defopt.run maps types to parsers globally, this could map flag names to short flags globally. This is less flexible, but very simple and consistent.

The rough equivalent for your example:

def my_cmd(long_opt=1, legend=True, custom='a'):
    print(long_opt, legend, custom)

if __name__ == '__main__':
    defopt.run(my_cmd, short={'long_opt': 'l', 'legend': 'g', 'custom': 'c'})

How would you feel about something like this?

@evanunderscore evanunderscore changed the title Allow customization of flag names (concept) Allow customization of flag names Apr 16, 2016
@evanunderscore evanunderscore changed the title Allow customization of flag names Support customizable short flags Apr 16, 2016
@evanunderscore
Copy link
Collaborator Author

I've renamed this issue so that this can focus on short flags and #12 can handle boolean flags.

@flying-sheep
Copy link

My thinking lately is that it would be nice to handle this in a similar way to parsers. In the same way the parsers argument to defopt.run maps types to parsers globally, this could map flag names to short flags globally. This is less flexible, but very simple and consistent.
[…]

defopt.run(my_cmd, short={'long_opt': 'l', 'legend': 'g', 'custom': 'c'})

i like it, but we should consider doing alternatives={'long_opt': ('l', 'long', 'long2'), 'legend': 'g'} instead. it’s a more flexible than short. the behavior would be to

  1. only have a long opt (from arg name) per default
  2. if (one or more) short opts are given, use the default long opt and them
  3. if (one or more) long opts are given, replace the default long opt

@evanunderscore
Copy link
Collaborator Author

Can you give me an example of where you would want to rename a flag but not just change the name of the function parameter?

@flying-sheep
Copy link

e.g. a backwards compatible flag name change?

it doesn’t matter though, as adding this part later would still work (i.e. implementing like you propose and later add the ability to handle tuples)

@evanunderscore
Copy link
Collaborator Author

My preference would be to implement a simpler solution first and expand on it later if necessary. I'm open to implementing something like you proposed in the future if there's a demand for it.

@flying-sheep
Copy link

yeah, sure 😄

@evanunderscore
Copy link
Collaborator Author

Very simple implementation here: 9478299

Might have to change a bit once #12 is done. Thoughts?

@evanunderscore
Copy link
Collaborator Author

I've made the change from underscores to hyphens here: 75c8755

@flying-sheep
Copy link

flying-sheep commented Apr 25, 2016

cool! this is a breaking change though, so it’ll be released in 2.0, correct?

@evanunderscore
Copy link
Collaborator Author

evanunderscore commented Apr 25, 2016

Indeed, all of these changes will be in 2.0. The short flags could go separately into 1.x, but unless someone asks, I won't bother.

@evanunderscore
Copy link
Collaborator Author

Implemented in c44e398 taking into account the other changes that have happened.

@evanunderscore
Copy link
Collaborator Author

evanunderscore commented May 10, 2016

Going to close this since most of it has been addressed. If there's anything you want that the current solution doesn't cover, feel free to create a new issue.

@flying-sheep
Copy link

very cool, thank you!

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

No branches or pull requests

2 participants