Esempio n. 1
0
def _validate_signature(func, locator):
    act_stack = list(_inspect_signature(func).parameters.values())
    exp_stack = list(_formals)

    while True:
        exp_name = exp_stack.pop()
        if 0 == len(act_stack):
            xx(f"{locator!r} must take at least {len(_formals)} params")

        param = act_stack.pop()

        while True:
            # If this one has a default, assume it's an optional kw arg at end
            if param.default == param.empty:
                break
            param = act_stack.pop()

        if exp_name != param.name:
            xx("this will probably change but very strict for now: "
               f"{locator!r} parameter {param.name!r} should be "
               f"called {exp_name!r}")

        if param.kind != param.POSITIONAL_OR_KEYWORD:
            xx(f"expected {locator!r} parameter {param.name!r} "
               f"to be POSITIONAL_OR_KEYWORD, had {param.kind!s}")

        if 0 == len(exp_stack):
            break

    res = len(act_stack)
    assert all(param.kind == param.POSITIONAL_OR_KEYWORD
               for param in act_stack)  # right? getting crazy
    return res
Esempio n. 2
0
def _call_function(func, args):
    positionals = []
    keywords = {}
    sig = _inspect_signature(func)
    for name, param in sig.parameters.items():
        arg = getattr(args, name)
        if param.kind in [param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD]:
            positionals.append(arg)
        elif param.kind == param.VAR_POSITIONAL:
            positionals.extend(arg)
        else:
            keywords[name] = arg
    return func(*positionals, **keywords)
Esempio n. 3
0
def _public_signature(func):
    full_sig = _inspect_signature(func)
    return full_sig.replace(
        parameters=list(param for param in full_sig.parameters.values()
                        if not param.name.startswith('_')))
Esempio n. 4
0
def _populate_parser(func, parser, parsers, short):
    sig = _inspect_signature(func)
    doc = _parse_doc(func)
    hints = _get_type_hints(func)
    parser.description = doc.text

    types = dict((name, _get_type(func, name, doc, hints))
                 for name, param in sig.parameters.items())
    positionals = set(
        name for name, param in sig.parameters.items()
        if (param.default == param.empty and not types[name].container
            and param.kind != param.KEYWORD_ONLY))
    if short is None:
        count_initials = Counter(name[0] for name in sig.parameters
                                 if name not in positionals)
        short = dict(
            (name.replace('_', '-'), name[0]) for name in sig.parameters
            if name not in positionals and count_initials[name[0]] == 1)

    for name, param in sig.parameters.items():
        kwargs = {}
        if name in doc.params:
            help_ = doc.params[name].text
            if help_ is not None:
                kwargs['help'] = help_.replace('%', '%%')
        type_ = types[name]
        if param.kind == param.VAR_KEYWORD:
            raise ValueError('**kwargs not supported')
        hasdefault = param.default != param.empty
        default = param.default if hasdefault else SUPPRESS
        required = not hasdefault and param.kind != param.VAR_POSITIONAL
        positional = name in positionals
        if type_.type == bool and not positional and not type_.container:
            # Special case: just add parameterless --name and --no-name flags.
            group = parser.add_mutually_exclusive_group(required=required)
            _add_argument(
                group,
                name,
                short,
                action='store_true',
                default=default,
                # Add help if available.
                **kwargs)
            _add_argument(group,
                          'no-' + name,
                          short,
                          action='store_false',
                          default=default,
                          dest=name)
            continue
        if positional:
            kwargs['_positional'] = True
            if param.kind == param.VAR_POSITIONAL:
                kwargs['nargs'] = '*'
                # This is purely to override the displayed default of None.
                # Ideally we wouldn't want to show a default at all.
                kwargs['default'] = []
        else:
            kwargs['required'] = required
            kwargs['default'] = default
        if type_.container:
            assert type_.container == list
            kwargs['nargs'] = '*'
            if param.kind == param.VAR_POSITIONAL:
                kwargs['action'] = 'append'
                kwargs['default'] = []
        if inspect.isclass(type_.type) and issubclass(type_.type, Enum):
            # Want these to behave like argparse choices.
            kwargs['choices'] = _ValueOrderedDict(
                (x.name, x) for x in type_.type)
            kwargs['type'] = _enum_getter(type_.type)
        else:
            kwargs['type'] = _get_parser(type_.type, parsers)
        _add_argument(parser, name, short, **kwargs)
Esempio n. 5
0
def _populate_parser(func, parser, parsers, short, strict_kwonly):
    full_sig = _inspect_signature(func)
    sig = full_sig.replace(parameters=list(
        param for param in full_sig.parameters.values()
        if not param.name.startswith('_')))
    doc = _parse_function_docstring(func)
    hints = _get_type_hints(func)
    parser.description = doc.text

    types = dict((name, _get_type(func, name, doc, hints))
                 for name, param in sig.parameters.items())
    positionals = set(
        name for name, param in sig.parameters.items()
        if ((param.default is param.empty or strict_kwonly) and
            not types[name].container and param.kind != param.KEYWORD_ONLY))
    if short is None:
        count_initials = Counter(name[0] for name in sig.parameters
                                 if name not in positionals)
        short = dict(
            (name.replace('_', '-'), name[0]) for name in sig.parameters
            if name not in positionals and count_initials[name[0]] == 1)

    for name, param in sig.parameters.items():
        kwargs = {}
        if name in doc.params:
            help_ = doc.params[name].text
            if help_ is not None:
                kwargs['help'] = help_.replace('%', '%%')
        type_ = types[name]
        if param.kind == param.VAR_KEYWORD:
            raise ValueError('**kwargs not supported')
        hasdefault = param.default is not param.empty
        default = param.default if hasdefault else SUPPRESS
        required = not hasdefault and param.kind != param.VAR_POSITIONAL
        positional = name in positionals
        if type_.type == bool and not positional and not type_.container:
            # Special case: just add parameterless --name and --no-name flags.
            group = parser.add_mutually_exclusive_group(required=required)
            _add_argument(
                group,
                name,
                short,
                action='store_true',
                default=default,
                # Add help if available.
                **kwargs)
            _add_argument(group,
                          'no-' + name,
                          short,
                          action='store_false',
                          default=default,
                          dest=name)
            continue
        if positional:
            kwargs['_positional'] = True
            if param.default is not param.empty:
                kwargs['nargs'] = '?'
                kwargs['default'] = default
            if param.kind == param.VAR_POSITIONAL:
                kwargs['nargs'] = '*'
                # This is purely to override the displayed default of None.
                # Ideally we wouldn't want to show a default at all.
                kwargs['default'] = []
        else:
            kwargs['required'] = required
            kwargs['default'] = default
        if type_.container:
            assert type_.container == list
            kwargs['nargs'] = '*'
            if param.kind == param.VAR_POSITIONAL:
                kwargs['action'] = 'append'
                kwargs['default'] = []
        make_tuple = member_types = None
        if _is_generic_type(type_.type, Tuple):
            make_tuple = tuple
            member_types = type_.type.__args__
            kwargs['nargs'] = len(member_types)
            kwargs['action'] = _make_store_tuple_action_class(
                tuple, member_types, parsers)
        elif (inspect.isclass(type_.type) and issubclass(type_.type, tuple)
              and hasattr(type_.type, '_fields')
              and hasattr(type_.type, '_field_types')):
            # Before Py3.6, `_field_types` does not preserve order, so retrieve
            # the order from `_fields`.
            member_types = tuple(type_.type._field_types[field]
                                 for field in type_.type._fields)
            kwargs['nargs'] = len(member_types)
            kwargs['action'] = _make_store_tuple_action_class(
                lambda args, type_=type_: type_.type(*args),
                member_types,
                parsers)
            if not positional:  # http://bugs.python.org/issue14074
                kwargs['metavar'] = type_.type._fields
        elif inspect.isclass(type_.type) and issubclass(type_.type, Enum):
            # Want these to behave like argparse choices.
            kwargs['choices'] = _ValueOrderedDict(
                (x.name, x) for x in type_.type)
            kwargs['type'] = _enum_getter(type_.type)
        else:
            kwargs['type'] = _get_parser(type_.type, parsers)
        _add_argument(parser, name, short, **kwargs)