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).
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()
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()
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).
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()
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()