Module for declaring keyword-only arguments in python

This module provides a decorator for indicating named arguments are keyword-only arguments.

kwonly
------

To decorate a function with ``kwonly``, any keyword-only arguments should appear as positional arguments in the signature, after any arguments that should be accepted positionally and before the varargs arguments if one is present. Pass the 0-based index of the first keyword-only argument and all positional arguments at or after that index will be treated as keyword-only arguments. ::

    >>> from kwonly import kwonly
    >>> @kwonly(0)
    ... def foo(a, b, c):
    ...     print a, b, c
    ... 
    >>> foo(1, 2, 3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: foo() takes exactly 0 arguments (3 given)
    >>> foo(c=3, a=1, b=2)
    1 2 3
    >>> @kwonly(2)
    ... def foo(a, b, c, d=4):
    ...     print a, b, c, d
    ... 
    >>> foo(1, 2, 3)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: foo() takes exactly 2 arguments (3 given)
    >>> foo(1, 2, c=3)
    1 2 3 4
    >>> foo(1, 2, d=5, c=3)
    1 2 3 5
    >>> @kwonly(0)
    ... def foo(a, b, c, *args):
    ...     print a, b, c, args
    ... 
    >>> foo(4, 5, 6, a=1, b=2, c=3)
    1 2 3 (4, 5, 6)

``kwonly`` is of course not a signature-preserving decorator. The argspec will include the non-keyword-only arguments and their default values and the varargs argument if one is given. If no keyword arguments are given, the argspec will have the keywords set to ``restricted_kwargs``, otherwise it won't be touched. The keyword-only arguments won't appear in the argspec at all.

NoDefault
---------

Because the keyword-only arguments are declared as positional arguments and must appear after all actual positional arguments, the presence of default arguments in the positional arguments can interfere with the ability to declare required keyword arguments. In such a case, the ``NoDefault`` value can be used to indicate that there isn't actually a default argument supplied for the function, so if no value is passed in the call a ``TypeError`` will be raised. ::

    >>> from kwonly import kwonly, NoDefault
    >>> @kwonly(2)
    ... def foo(a, b=2, c=NoDefault, d=4):
    ...     print a, b, c, d
    ... 
    >>> foo(1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<string>", line 3, in foo
    TypeError: foo needs keyword-only argument c
    >>> foo(1, c=3)
    1 2 3 4

Python versions
---------------

This module enables keyword-only arguments in python 2.x, but also works in python 3. This allows modules to be written for both lines of python without sacrificing the utility of keyword-only arguments or having to maintain two separate versions. The python 3 declaration syntax for keyword-only arguments can't be used, but python 3 will get the full benefit of keyword-arguments in that the signature of the decorated function will match the function that it logically models. ::

    >>> from kwonly import kwonly, NoDefault
    >>> @kwonly(2)
    ... def foo(a, b=2, c=NoDefault, d=4, *args, **kwargs):
    ...     print(a, b, c, d, args, kwargs)
    ... 
    >>> foo(1, 2, 5, 6, c=3, g=7, h=8)
    1 2 3 4 (5, 6) {'h': 8, 'g': 7}
    >>> foo(1, c=3, g=7, h=8)
    1 2 3 4 () {'h': 8, 'g': 7}
    >>> def bar(a, b=2, *args, c, d=4, **kwargs):
    ...     print(a, b, c, d, args, kwargs)
    ... 
    >>> bar(1, 2, 5, 6, c=3, g=7, h=8)
    1 2 3 4 (5, 6) {'h': 8, 'g': 7}
    >>> bar(1, c=3, g=7, h=8)
    1 2 3 4 () {'h': 8, 'g': 7}
    >>> import inspect
    >>> inspect.getfullargspec(foo)
    FullArgSpec(args=['a', 'b'], varargs='args', varkw='kwargs', defaults=(2,), kwonlyargs=['c', 'd'], kwonlydefaults={'d': 4}, annotations={})
    >>> inspect.getfullargspec(bar)
    FullArgSpec(args=['a', 'b'], varargs='args', varkw='kwargs', defaults=(2,), kwonlyargs=['c', 'd'], kwonlydefaults={'d': 4}, annotations={})
