Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions src/defopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ def run(
argparse_kwargs: dict = {},
intermixed: bool = False,
argv: Optional[List[str]] = None,
defaults: Optional[Dict[str, Any]] = None
):
"""
Process command-line arguments and run the given functions.
Expand Down Expand Up @@ -302,20 +303,26 @@ def run(
listed in the `argparse` documentation.
:param argv:
Command line arguments to parse (default: ``sys.argv[1:]``).
:param defaults:
Mapping for argument defaults passed to
`~argparse.ArgumentParser.set_defaults`. Key must be the command name,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

chore: fixme, this isn't true anymore

value is a mapping of argument name to default value.
:return:
The value returned by the function that was run.
"""
return bind(
funcs, parsers=parsers, short=short, cli_options=cli_options,
show_defaults=show_defaults, show_types=show_types,
no_negated_flags=no_negated_flags, version=version,
argparse_kwargs=argparse_kwargs, intermixed=intermixed, argv=argv)()
argparse_kwargs=argparse_kwargs, intermixed=intermixed, argv=argv,
defaults=defaults)()


_DefoptOptions = namedtuple(
'_DefoptOptions',
['parsers', 'short', 'cli_options', 'show_defaults', 'show_types',
'no_negated_flags', 'version', 'argparse_kwargs', 'intermixed', 'argv'])
'no_negated_flags', 'version', 'argparse_kwargs', 'intermixed', 'argv',
'defaults'])


def _options(**kwargs):
Expand Down Expand Up @@ -359,12 +366,17 @@ def _create_parser(funcs, opts):
**opts.argparse_kwargs})
version_sources = []
if callable(funcs):
_populate_parser(funcs, parser, opts)
_populate_parser(funcs, parser, opts, opts.defaults)
version_sources.append(funcs)
else:
subparsers = parser.add_subparsers()
for func, subparser in _recurse_functions(funcs, subparsers):
_populate_parser(func, subparser, opts)
# FIXME: need fully qualitified name for nested commands...
defaults = None if opts.defaults is None else opts.defaults.get(
func.__name__,
opts.defaults.get(func.__name__.replace('_', '-'), None)
)
_populate_parser(func, subparser, opts, defaults)
version_sources.append(func)
if isinstance(opts.version, str):
version_string = opts.version
Expand Down Expand Up @@ -563,7 +575,7 @@ def _get_type_from_hint(hint):
return hint


def _populate_parser(func, parser, opts):
def _populate_parser(func, parser, opts, defaults):
sig = signature(func)
parser.description = sig.doc

Expand Down Expand Up @@ -605,6 +617,11 @@ def _populate_parser(func, parser, opts):
raise ValueError(f'no type found for parameter {name}')
hasdefault = param.default is not param.empty
default = param.default if hasdefault else SUPPRESS

if not hasdefault and defaults is not None and name in defaults:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

question: This makes the function argument take precedence. Perhaps it should be the other way around?

Copy link
Owner

Choose a reason for hiding this comment

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

It would seem that if you pass an explicit default, that one should be preferred?

hasdefault = True
default = defaults[name]

required = not hasdefault and param.kind != param.VAR_POSITIONAL
positional = name in positionals

Expand Down