Exemple #1
0
def test_consistent_order_of_args(intf, spec_posargs):
    f = getattr(intf, '__call__')
    args, varargs, varkw, defaults = getargspec(f)
    # now verify that those spec_posargs are first among args
    if not spec_posargs:
        pytest.skip("no positional args")
    eq_(set(args[:len(spec_posargs)]), spec_posargs)
Exemple #2
0
 def call_from_parser(cls, args):
     # XXX needs safety check for name collisions
     from reproman.utils import getargspec
     argnames = getargspec(cls.__call__)[0]
     kwargs = {k: getattr(args, k) for k in argnames if k != 'self'}
     try:
         return cls.__call__(**kwargs)
     except KeyboardInterrupt:
         ui.error("\nInterrupted by user while doing magic")
         sys.exit(1)
Exemple #3
0
 def setup_parser(cls, parser):
     # XXX needs safety check for name collisions
     # XXX allow for parser kwargs customization
     parser_kwargs = {}
     from reproman.utils import getargspec
     # get the signature
     ndefaults = 0
     args, varargs, varkw, defaults = getargspec(cls.__call__)
     if not defaults is None:
         ndefaults = len(defaults)
     for i, arg in enumerate(args):
         if arg == 'self':
             continue
         param = cls._params_[arg]
         defaults_idx = ndefaults - len(args) + i
         cmd_args = param.cmd_args
         if cmd_args is None:
             cmd_args = []
         if not len(cmd_args):
             if defaults_idx >= 0:
                 # dealing with a kwarg
                 template = '--%s'
             else:
                 # positional arg
                 template = '%s'
             # use parameter name as default argument name
             parser_args = (template % arg.replace('_', '-'),)
         else:
             parser_args = [c.replace('_', '-') for c in cmd_args]
         parser_kwargs = param.cmd_kwargs
         if defaults_idx >= 0:
             parser_kwargs['default'] = defaults[defaults_idx]
         help = alter_interface_docs_for_cmdline(param._doc)
         if help and help[-1] != '.':
             help += '.'
         if param.constraints is not None:
             parser_kwargs['type'] = param.constraints
             # include value constraint description and default
             # into the help string
             cdoc = alter_interface_docs_for_cmdline(
                 param.constraints.long_description())
             if cdoc[0] == '(' and cdoc[-1] == ')':
                 cdoc = cdoc[1:-1]
             help += '  Constraints: %s' % cdoc
         if defaults_idx >= 0:
             help += " [Default: %r]" % (defaults[defaults_idx],)
         # create the parameter, using the constraint instance for type
         # conversion
         parser.add_argument(*parser_args, help=help,
                             **parser_kwargs)
Exemple #4
0
def update_docstring_with_parameters(func, params, prefix=None, suffix=None):
    """Generate a useful docstring from a parameter spec

    Amends any existing docstring of a callable with a textual
    description of its parameters. The Parameter spec needs to match
    the number and names of the callables arguments.
    """
    from reproman.utils import getargspec
    # get the signature
    ndefaults = 0
    args, varargs, varkw, defaults = getargspec(func)
    if not defaults is None:
        ndefaults = len(defaults)
    # start documentation with what the callable brings with it
    doc = prefix if prefix else u''
    if len(args) > 1:
        if len(doc):
            doc += '\n'
        doc += "Parameters\n----------\n"
        for i, arg in enumerate(args):
            if arg == 'self':
                continue
            # we need a parameter spec for each argument
            if not arg in params:
                raise ValueError(
                    "function has argument '%s' not described as a parameter" %
                    arg)
            param = params[arg]
            # validate the default -- to make sure that the parameter description is
            # somewhat OK
            defaults_idx = ndefaults - len(args) + i
            if defaults_idx >= 0:
                if not param.constraints is None:
                    param.constraints(defaults[defaults_idx])
            orig_docs = param._doc
            param._doc = alter_interface_docs_for_api(param._doc)
            doc += param.get_autodoc(
                arg,
                default=defaults[defaults_idx] if defaults_idx >= 0 else None,
                has_default=defaults_idx >= 0)
            param._doc = orig_docs
            doc += '\n'
    doc += suffix if suffix else u""
    # assign the amended docs
    func.__doc__ = doc
    return func
Exemple #5
0
class Parameter(object):
    """This class shall serve as a representation of a parameter.
    """

    # Known keyword arguments which we want to allow to pass over into
    # argparser.add_argument . Mentioned explicitly, since otherwise
    # are not verified while working in Python-only API
    _KNOWN_ARGS = getargspec(argparse.Action.__init__)[0] + ['action']

    def __init__(self, constraints=None, doc=None, args=None, **kwargs):
        """Add constraints (validator) specifications and a docstring for
        a parameter.

        Parameters
        ----------
        constraints : callable
          A functor that takes any input value, performs checks or type
          conversions and finally returns a value that is appropriate for a
          parameter or raises an exception. This will also be used to set up
          the ``type`` functionality of argparse.add_argument.
        doc : str
          Documentation about the purpose of this parameter.
        args : tuple or None
          Any additional positional args for argparser.add_argument. This is
          most useful for assigned multiple alternative argument names or
          create positional arguments.
        **kwargs :
          Any additional keyword args for argparser.add_argument.

        Examples
        --------
        Ensure a parameter is a float
        >>> from reproman.support.param import Parameter
        >>> from reproman.support.constraints import (EnsureFloat, EnsureRange,
        ...                              AltConstraints, Constraints)
        >>> C = Parameter(constraints=EnsureFloat())

        Ensure a parameter is of type float or None:
        >>> C = Parameter(constraints=AltConstraints(EnsureFloat(), None))

        Ensure a parameter is None or of type float and lies in the inclusive
        range (7.0,44.0):
        >>> C = Parameter(
        ...         AltConstraints(
        ...             Constraints(EnsureFloat(),
        ...                         EnsureRange(min=7.0, max=44.0)),
        ...             None))
        """
        self.constraints = expand_constraint_spec(constraints)
        self._doc = doc
        self.cmd_args = args

        # Verify that no mistyped kwargs present
        unknown_args = set(kwargs).difference(self._KNOWN_ARGS)
        if unknown_args:
            raise ValueError(
                "Detected unknown argument(s) for the Parameter: %s.  Known are: %s"
                % (', '.join(unknown_args), ', '.join(self._KNOWN_ARGS)))
        self.cmd_kwargs = kwargs

    def get_autodoc(self,
                    name,
                    indent="  ",
                    width=70,
                    default=None,
                    has_default=False):
        """Docstring for the parameter to be used in lists of parameters

        Returns
        -------
        string or list of strings (if indent is None)
        """
        paramsdoc = '%s' % name
        sdoc = None
        if self.constraints is not None:
            sdoc = self.constraints.short_description()
        elif 'action' in self.cmd_kwargs \
                and self.cmd_kwargs['action'] in ("store_true", "store_false"):
            sdoc = 'bool'
        if not sdoc is None:
            if sdoc[0] == '(' and sdoc[-1] == ')':
                sdoc = sdoc[1:-1]
            paramsdoc += " : %s" % sdoc
            if has_default:
                paramsdoc += ", optional"
        paramsdoc = [paramsdoc]

        doc = self._doc
        if doc is None:
            doc = ''
        doc.strip()
        if len(doc) and not doc.endswith('.'):
            doc += '.'
        if self.constraints is not None:
            cdoc = self.constraints.long_description()
            if cdoc[0] == '(' and cdoc[-1] == ')':
                cdoc = cdoc[1:-1]
            addinfo = ''
            if 'nargs' in self.cmd_kwargs \
                    and not self.cmd_kwargs['nargs'] == '?':
                addinfo = 'list expected, each '
            doc += ' Constraints: %s%s.' % (addinfo, cdoc)
        if has_default:
            doc += " [Default: %r]" % (default, )
        # Explicitly deal with multiple spaces, for some reason
        # replace_whitespace is non-effective
        doc = _whitespace_re.sub(' ', doc)
        paramsdoc += [
            indent + x for x in textwrap.wrap(
                doc, width=width - len(indent), replace_whitespace=True)
        ]
        return '\n'.join(paramsdoc)