Skip to content

Commit 216a607

Browse files
authored
Merge pull request #606 from plone/ale/248/fix-missing-default
Improve the `@required_parameters` decorator
2 parents f826d41 + 48ac7ea commit 216a607

3 files changed

Lines changed: 54 additions & 7 deletions

File tree

news/248.bugfix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The `@required_parameters` decorator can work with arguments that do not have a default. @ale-rt

src/plone/api/tests/test_validation.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for plone.api.validation."""
22

3+
from plone.api.exc import MissingParameterError
34
from plone.api.tests.base import INTEGRATION_TESTING
45
from plone.api.validation import _get_supplied_args as _gsa
56
from plone.api.validation import at_least_one_of
@@ -56,6 +57,38 @@ def test_non_existant_required_arg(self):
5657
)
5758
_func(undecorated_func)
5859

60+
def test_required_parameters_with_no_default(self):
61+
"""Test that we have a nice validation message even if the
62+
required parameter doesn't have a default value.
63+
"""
64+
65+
@required_parameters("arg1")
66+
def _func1(arg1, arg2=None, arg3=None):
67+
pass
68+
69+
with self.assertRaises(MissingParameterError) as e:
70+
_func1()
71+
72+
self.assertEqual(str(e.exception), "Missing required parameter(s): arg1")
73+
74+
@required_parameters("arg1", "arg2")
75+
def _func2(arg1, arg2=None, arg3=None):
76+
pass
77+
78+
with self.assertRaises(MissingParameterError) as e:
79+
_func2()
80+
81+
self.assertEqual(str(e.exception), "Missing required parameter(s): arg1, arg2")
82+
83+
@required_parameters("arg1", "arg2")
84+
def _func3(arg1, arg2=1, arg3=None):
85+
pass
86+
87+
with self.assertRaises(MissingParameterError) as e:
88+
_func3()
89+
90+
self.assertEqual(str(e.exception), "Missing required parameter(s): arg1, arg2")
91+
5992
def test_get_supplied_args(self):
6093
"""Test that positional and keyword args are recognised correctly."""
6194
# the arguments specified in the function signature

src/plone/api/validation.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Provide decorators for validating parameters."""
22

33
from decorator import decorator
4+
from functools import wraps
45
from plone.api.exc import InvalidParameterError
56
from plone.api.exc import MissingParameterError
67

@@ -12,7 +13,17 @@ def _get_arg_spec(func, validator_args):
1213
1314
and check that the decorator doesn't refer to non-existent args.
1415
"""
15-
signature_args = inspect.getfullargspec(func).args
16+
signature = inspect.signature(func)
17+
signature_args = [
18+
name
19+
for name, param in signature.parameters.items()
20+
if param.kind
21+
in (
22+
inspect.Parameter.POSITIONAL_ONLY,
23+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
24+
inspect.Parameter.KEYWORD_ONLY,
25+
)
26+
]
1627
extra_args = set(validator_args) - set(signature_args)
1728
if extra_args:
1829
raise ValueError(
@@ -32,9 +43,10 @@ def _get_supplied_args(signature_params, args, kwargs):
3243
either as positional or keyword arguments, and are not None.
3344
"""
3445
supplied_args = []
35-
for index in range(len(args)):
36-
if args[index] is not None:
37-
supplied_args.append(signature_params[index])
46+
for index, arg in enumerate(args):
47+
if arg is not None:
48+
arg_name = signature_params[index]
49+
supplied_args.append(arg_name)
3850

3951
for keyword in kwargs:
4052
if kwargs[keyword] is not None:
@@ -58,7 +70,8 @@ def _required_parameters(func):
5870
"""Provide actual decorator."""
5971
signature_params = _get_arg_spec(func, required_params)
6072

61-
def wrapped(function, *args, **kwargs):
73+
@wraps(func)
74+
def wrapped(*args, **kwargs):
6275
"""Provide wrapped function (whose docstring will get replaced)."""
6376
supplied_args = _get_supplied_args(signature_params, args, kwargs)
6477

@@ -70,9 +83,9 @@ def wrapped(function, *args, **kwargs):
7083
),
7184
)
7285

73-
return function(*args, **kwargs)
86+
return func(*args, **kwargs)
7487

75-
return decorator(wrapped, func)
88+
return wrapped
7689

7790
return _required_parameters
7891

0 commit comments

Comments
 (0)