[Python-Dev] signature.object, argument clinic and grouped parameters

Yury Selivanov yselivanov.ml at gmail.com
Mon Jan 20 03:42:18 CET 2014


In the midst of work on the issue #17481, it became apparent that we need 
a way of specifying optional/grouped parameters.

One good example of grouped parameters in python is the `type` function.
Basically, it has two different signatures:

* type(name, bases, dict)

* type(object)

Which we can combine in one, if we define a notion of grouped parameters:

* type(object_or_name, [bases, dict])

Another good example, is 'itertools.repeat'. Its signature is "(elem[, n])".
If "n" argument is passed, then it's how many times the "elem" will be
repeated, and if it is not passed at all, then "elem" will be repeated
endlessly.

One way of emulating this behavior in pure python is to define a special
private marker object and use it as a default value:

    _optional = object()

    def repeat(elem, n=_optional):
        if n is _optional:
            # `n` wasn't passed, repeat indefinitely
        else:
            # we have something for `n`

One of the problems with the above approach is how to represent its
signature, how to document it clearly. Another one, is that there is no
common marker, so whenever this is needed, a new marker is invented.

Now, the more I think about having a concept of grouped parameters, the
more different things to consider and take care of appear:

* In issue #17481 Larry proposed that parameters will have a group id
(arbitrary), and perhaps parent group id, to make it possible to have
nested groups.

* API for retrieving grouped parameters for the Signature objects.
Something akin to what we have for ``regex.Match`` objects, probably. 

* An accepted and documented method of declaring groups for pure python
function would be a nice thing to have.

* Will we have groups for keyword-only parameters? Can we combine ``*args``
and a keyword-only parameter in a group? etc.

That seems to be a lot of work (some of it is maybe enough for a PEP.)

So before committing to the parameters groups idea, I'd like to propose
somewhat simpler, but powerful enough to solve our todays problems 
solution.

What if we add a notion of "optional" parameters?

* ``Parameter.__init__ `` will receive one more keyword-only argument:
``optional``, ``False`` by default.

* We add a special marker ``Parameter.optional`` (or some other name, like
``inspect.optional`` or ``functools.optional``), and teach
``inspect.signature`` to recognize it. So for pure-python functions,  if you
want to define an optional parameter, you would write: 
``def mytype(name_or_obj, bases=Parameter.optional, dict=Parameter.optional)``

* Argument Clinic may get a new syntax for specifying if parameter is 
optional.

* We standardize how optional parameters should look like in documentation
and ``Signature.__str__``. In PEP 362 we used <optional> notation for 
optional parameters: ``foo(param=<optional>)``, but we also can use square 
brackets for that: ``bar([spam][, ham])``.

With this approach, a signature of the ``type`` function would look like:
``type(object_or_name[, bases][, dict])``. The main downside is that it's
not immediately apparent, that you can only pass either one argument 
"(object)", or all three arguments "(name, bases, dict)". But that's 
something, that a good documentation (and meaningful exceptions) could
help with.

The advantages if this approach, is that it works for all types of parameters,
and that the implementation is going to be simpler than groups (and we will 
need fewer new APIs).

Yury


More information about the Python-Dev mailing list