From c7b1f614daffdf709b7de24622895ffb294d0f89 Mon Sep 17 00:00:00 2001 From: Ian Stapleton Cordasco Date: Wed, 16 May 2018 08:30:01 -0500 Subject: [PATCH] Add no_positional decorator helper for planned API This allows us to design the APIs so that they only take keyword arguments to make it easier and more explicit for users. Refs #361 --- tests/test_utils.py | 23 +++++++++++++++++++++++ twine/utils.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index 7338ccbf..ae62c45b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -256,3 +256,26 @@ def test_get_password_runtime_error_suppressed( assert len(recwarn) == 1 warning = recwarn.pop(UserWarning) assert 'fail!' in str(warning) + + +def test_no_positional_on_method(): + class T(object): + @utils.no_positional(allow_self=True) + def __init__(self, foo=False): + self.foo = foo + + with pytest.raises(TypeError): + T(1) + + t = T(foo=True) + assert t.foo + +def test_no_positional_on_function(): + @utils.no_positional() + def t(foo=False): + return foo + + with pytest.raises(TypeError): + t(1) + + assert t(foo=True) diff --git a/twine/utils.py b/twine/utils.py index 4cd1a18f..d83e0806 100644 --- a/twine/utils.py +++ b/twine/utils.py @@ -283,3 +283,31 @@ def get_password(system, username, cli_value, config): username, ), ) + + +def no_positional(allow_self=False): + """A decorator that doesn't allow for positional arguments. + + :param bool allow_self: + Whether to allow ``self`` as a positional argument. + """ + def reject_positional_args(function): + @functools.wraps(function) + def wrapper(*args, **kwargs): + allowed_positional_args = 0 + if allow_self: + allowed_positional_args = 1 + received_positional_args = len(args) + if received_positional_args > allowed_positional_args: + function_name = function.__name__ + verb = 'were' if received_positional_args > 1 else 'was' + raise TypeError(('{}() takes {} positional arguments but {} ' + '{} given').format( + function_name, + allowed_positional_args, + received_positional_args, + verb, + )) + return function(*args, **kwargs) + return wrapper + return reject_positional_args