How to block unexpected arguments in a function with **kwargs?

Question:

For example:

def f2(**kwargs):
    b = kwargs.get('b',2)
    print(b)

def f():
    f2(c=2)

f()

What can I do to show an error when I put this c as an argument and only allow the variables with name b (what the function is expecting).

Asked By: user12419272

||

Answers:

kwargs is given as a dictionary. You can validate in any way you want.

def f2(**kwargs):
    assert "c" not in kwargs
    b = kwargs.get('b',2)
    print(b)

def f():
    f2(c=2)

f()
Answered By: Kota Mori

Sometimes we need to got which arguments was unexpected, and send some error messages to users.

I created a decorator to do that.

# exceptions.py
class UnexpectedKwargsError(Exception):
    def __init__(self, unexpected_kwargs, *args):
        self.unexpected_kwargs = unexpected_kwargs # It is a `list` of unexpected kwargs
        self.message = f'Invalid arguments: {self.unexpected_kwargs}.'
        super(UnexpectedKwargsError, self).__init__(unexpected_kwargs, *args)

    def __str__(self):
        return self.message

    def __repr__(self):
        return self.message
# decorators.py
import functools
import inspect
from .exceptions import UnexpectedKwargsError

def unexpected_kwargs_validate(func=None, is_static_method=True, arguments=None):
    """
    A decorator of unexpected kwargs.
    :param func: `function` which be decorated.
    :param is_static_method: `bool`
        Set True, when function is a `staticmethod`.
        Set False, when function is not `classmethod` or instance method(The function has `self` or `cls` argument).
    :param arguments: `list` which arguments you want to validate.
    """
    if func is None:
        return functools.partial(
            unexpected_kwargs_validate,
            is_static_method=is_static_method,
            arguments=arguments
        )

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if arguments:
            args_spec = arguments
        else:
            args_spec = inspect.getfullargspec(func).args[0 if is_static_method else 1:]
        unexpected_kwargs = {}
        for kwarg_key, kwarg_value in kwargs.items():
            if kwarg_key not in args_spec:
                unexpected_kwargs |= {kwarg_key: kwarg_value}
        if unexpected_kwargs:
            raise UnexpectedKwargsError(unexpected_kwargs)
        return func(*args, **kwargs)

    return wrapper
# demo1.py
@unexpected_kwargs_validate(arguments=['b'])
# We need to provide the arguments manually,
# when the function has args and(or) kwargs only.
def f2(**kwargs):
    b = kwargs.get('b', 2)
    print(b)

def f():
    try:
        f2(c=2)
    except UnexpectedKwargsError as e:
        logging.error(e.unexpected_kwargs)
        logging.error(e.message)

f()
# demo2.py
@unexpected_kwargs_validate
def f1(a='x', b='x', c='x'):
    print(a, b, c)

def f():
    try:
        f1(**{'d': 'd'})  # Or try the `f1(c='c', d='d')`.
    except UnexpectedKwargsError as e:
        logging.error(e.unexpected_kwargs)
        logging.error(e.message)

f()
Answered By: 954
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.