Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inspect if an argument was passed positionally or via keyword

Consider a function with signature f(a, b). In future, I would like to change the signature to f(a, *, b), disallowing b to be passed as positional argument. To reduce the impact of the change, I want to first deprecate specifying b positionally, warning users that do so.

For that I would like to write something like:

def f(a, b):
    frame = inspect.currentframe()
    if b in frame.specified_as_positional:
        print('Do not do that')
    else:
        print('Good')

The result would be that

>>> f(1, 2)
'Do not do that'
>>> f(1, b=2)
'Good'

inspect.getargvalues(frame) does not seem to be sufficient. The ArgInfo object just provides

>>> f(1,b=2)
ArgInfo(args=['a', 'b'], varargs=None, keywords=None, locals={'a': 1, 'b': 2})

Is such inspection even possible in Python? Conceptually the interpreter does not seem to be required to remember if a argument was specified positionally or as keyword.

Python 2 support would be nice to have but is not strictly required.

like image 453
leezu Avatar asked Jun 05 '19 21:06

leezu


People also ask

What is the difference between keyword and positional arguments?

Python functions can contain two types of arguments: positional arguments and keyword arguments. Positional arguments must be included in the correct order. Keyword arguments are included with a keyword and equals sign.

What is a keyword argument?

Keyword arguments (or named arguments) are values that, when passed into a function, are identifiable by specific parameter names. A keyword argument is preceded by a parameter and the assignment operator, = . Keyword arguments can be likened to dictionaries in that they map a value to a keyword.

When you use keyword arguments in a function call The caller identifies the arguments by the parameter name?

When you use keyword arguments in a function call, the caller identifies the arguments by the parameter name. This allows you to skip arguments or place them out of order because the Python interpreter is able to use the keywords provided to match the values with parameters.

How do you use keyword arguments in Python?

Embrace keyword arguments in PythonConsider using the * operator to require those arguments be specified as keyword arguments. And remember that you can accept arbitrary keyword arguments to the functions you define and pass arbitrary keyword arguments to the functions you call by using the ** operator.


1 Answers

You can use a wrapper to add an extra step between the user and the function. In that step, you can examine the arguments before the names matter. Note that this depends on the fact that b doesn't have a default value and always must be given as an arg.

functools.wraps is used to make the decorated function resemble the original in a bunch of ways.

import functools
import warnings

def deprecate_positional(fun):
    @functools.wraps(fun)
    def wrapper(*args, **kwargs):
        if 'b' not in kwargs:
            warnings.warn(
                'b will soon be keyword-only',
                DeprecationWarning,
                stacklevel=2
                )
        return fun(*args, **kwargs)
    return wrapper

@deprecate_positional
def f(a, b):
    return a + b
>>> f(1, b=2)
3
>>> f(1, 2)
Warning (from warnings module):
  File "C:/Users/nwerth/Desktop/deprecate_positional.py", line 36
    print(f(1, 2))
DeprecationWarning: b will soon be keyword-only
3
like image 136
Nathan Werth Avatar answered Sep 20 '22 04:09

Nathan Werth