These are a few utility decorators I wrote on a tangent for a Python 2 project I was working on. The exceptions raised mirror, as closely as possible, the ones raised by functions in Python 3 that use the keyword-only arguments syntax.
They don't disallow positional arguments, but they can require/restrict keyword arguments. You could create another decorator that disallowed positional arguments.
import functools
def original_wrapped_function(f):
try:
while True:
f = f.__wrapped__
except AttributeError:
return f
def restrict_kwargs(*allowed_keywords):
def restrict_kwargs_decorator(func):
@functools.wraps(original_wrapped_function(func))
def restrict_wrapper(*args, **kwargs):
for keyword in kwargs:
if keyword not in allowed_keywords:
msg = "%s() got an unexpected keyword argument '%s'"
raise TypeError(msg % (func.__name__, keyword))
return func(*args, **kwargs)
restrict_wrapper.__wrapped__ = func
return restrict_wrapper
return restrict_kwargs_decorator
def require_kwargs(*required_keywords):
def require_kwargs_decorator(func):
@functools.wraps(original_wrapped_function(func))
def require_wrapper(*args, **kwargs):
missing_keywords = []
for keyword in required_keywords:
if keyword not in kwargs:
missing_keywords.append(keyword)
if missing_keywords:
func_name = func.__name__
count = len(missing_keywords)
if count == 1:
arg_word = 'argument'
missing_keywords_str = "'%s'" % missing_keywords[0]
else:
arg_word = 'arguments'
and_join_str = ' and ' if count == 2 else ', and '
missing_keywords_str = ', '.join(
("'%s'" % mk) for mk in missing_keywords[:-1])
missing_keywords_str = and_join_str.join((
missing_keywords_str, ("'%s'" % missing_keywords[-1])))
msg = "%s() missing %d required keyword-only %s: %s"
raise TypeError(msg % (func_name, count, arg_word,
missing_keywords_str))
return func(*args, **kwargs)
require_wrapper.__wrapped__ = func
return require_wrapper
return require_kwargs_decorator
def exact_kwargs(*exact_keywords):
def exact_kwargs_decorator(func):
@restrict_kwargs(*exact_keywords)
@require_kwargs(*exact_keywords)
@functools.wraps(original_wrapped_function(func))
def exact_wrapper(*args, **kwargs):
return func(*args, **kwargs)
exact_wrapper.__wrapped__ = func
return exact_wrapper
return exact_kwargs_decorator
Some examples:
>>> @restrict_kwargs('five', 'six')
... def test_restrict_kwargs(arg1, arg2, *moreargs, **kwargs):
... return (arg1, arg2, moreargs, kwargs)
...
>>>
>>> @require_kwargs('five', 'six')
... def test_require_kwargs(arg1, arg2, *moreargs, **kwargs):
... return (arg1, arg2, moreargs, kwargs)
...
>>>
>>> @exact_kwargs('five', 'six')
... def test_exact_kwargs(arg1, arg2, *moreargs, **kwargs):
... return (arg1, arg2, moreargs, kwargs)
...
>>>
>>>
>>>
>>> test_restrict_kwargs(1, 2, 3, 4, five=5)
(1, 2, (3, 4), {'five': 5})
>>>
>>> test_restrict_kwargs(1, 2, 3, 4, five=5, six=6)
(1, 2, (3, 4), {'six': 6, 'five': 5})
>>>
>>> test_restrict_kwargs(1, 2, 3, 4, five=5, six=6, seven=7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 19, in restrict_wrapper
raise TypeError(msg % (func.__name__, keyword))
TypeError: test_restrict_kwargs() got an unexpected keyword argument 'seven'
>>>
>>>
>>>
>>> test_require_kwargs(1, 2, 3, 4, five=5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 49, in require_wrapper
missing_keywords_str))
TypeError: test_require_kwargs() missing 1 required keyword-only argument: 'six'
>>>
>>> test_require_kwargs(1, 2, 3, 4, five=5, six=6)
(1, 2, (3, 4), {'six': 6, 'five': 5})
>>>
>>> test_require_kwargs(1, 2, 3, 4, five=5, six=6, seven=7)
(1, 2, (3, 4), {'seven': 7, 'six': 6, 'five': 5})
>>>
>>>
>>>
>>> test_exact_kwargs(1, 2, 3, 4, five=5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 20, in restrict_wrapper
return func(*args, **kwargs)
File "SO_31939890.py", line 49, in require_wrapper
missing_keywords_str))
TypeError: test_exact_kwargs() missing 1 required keyword-only argument: 'six'
>>>
>>> test_exact_kwargs(1, 2, 3, 4, five=5, six=6)
(1, 2, (3, 4), {'six': 6, 'five': 5})
>>>
>>> test_exact_kwargs(1, 2, 3, 4, five=5, six=6, seven=7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "SO_31939890.py", line 19, in restrict_wrapper
raise TypeError(msg % (func.__name__, keyword))
TypeError: test_exact_kwargs() got an unexpected keyword argument 'seven'
>>>
star
alone, what are you trying to do with that? – Mcgruter