mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
Support instance methods and class methods with @validate_arguments (#1272)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Support instance methods and class methods with `@validate_arguments`
|
||||
+11
-4
@@ -1,4 +1,4 @@
|
||||
from functools import update_wrapper
|
||||
from functools import wraps
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Tuple, TypeVar, cast, get_type_hints
|
||||
|
||||
from . import validator
|
||||
@@ -19,8 +19,15 @@ def validate_arguments(function: 'Callable') -> 'Callable':
|
||||
Decorator to validate the arguments passed to a function.
|
||||
"""
|
||||
vd = ValidatedFunction(function)
|
||||
vd = update_wrapper(vd, function) # type: ignore
|
||||
return cast('Callable', vd)
|
||||
|
||||
@wraps(function)
|
||||
def wrapper_function(*args: Any, **kwargs: Any) -> Any:
|
||||
return vd.call(*args, **kwargs)
|
||||
|
||||
wrapper_function.vd = vd # type: ignore
|
||||
wrapper_function.raw_function = vd.raw_function # type: ignore
|
||||
wrapper_function.model = vd.model # type: ignore
|
||||
return cast('Callable', wrapper_function)
|
||||
|
||||
|
||||
ALT_V_ARGS = 'v__args'
|
||||
@@ -95,7 +102,7 @@ class ValidatedFunction:
|
||||
|
||||
self.create_model(fields, takes_args, takes_kwargs)
|
||||
|
||||
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
||||
def call(self, *args: Any, **kwargs: Any) -> Any:
|
||||
values = self.build_values(args, kwargs)
|
||||
m = self.model(**values)
|
||||
return self.execute(m)
|
||||
|
||||
+47
-3
@@ -63,10 +63,10 @@ def test_wrap():
|
||||
assert foo_bar.__name__ == 'foo_bar'
|
||||
assert foo_bar.__module__ == 'tests.test_decorator'
|
||||
assert foo_bar.__qualname__ == 'test_wrap.<locals>.foo_bar'
|
||||
assert isinstance(foo_bar, ValidatedFunction)
|
||||
assert isinstance(foo_bar.vd, ValidatedFunction)
|
||||
assert callable(foo_bar.raw_function)
|
||||
assert foo_bar.arg_mapping == {0: 'a', 1: 'b'}
|
||||
assert foo_bar.positional_only_args == set()
|
||||
assert foo_bar.vd.arg_mapping == {0: 'a', 1: 'b'}
|
||||
assert foo_bar.vd.positional_only_args == set()
|
||||
assert issubclass(foo_bar.model, BaseModel)
|
||||
assert foo_bar.model.__fields__.keys() == {'a', 'b', 'args', 'kwargs'}
|
||||
assert foo_bar.model.__name__ == 'FooBar'
|
||||
@@ -218,3 +218,47 @@ def test_string_annotation():
|
||||
{'loc': ('a', 0), 'msg': 'value is not a valid integer', 'type': 'type_error.integer'},
|
||||
{'loc': ('b',), 'msg': 'field required', 'type': 'value_error.missing'},
|
||||
]
|
||||
|
||||
|
||||
def test_item_method():
|
||||
class X:
|
||||
def __init__(self, v):
|
||||
self.v = v
|
||||
|
||||
@validate_arguments
|
||||
def foo(self, a: int, b: int):
|
||||
assert self.v == a
|
||||
return f'{a}, {b}'
|
||||
|
||||
x = X(4)
|
||||
assert x.foo(4, 2) == '4, 2'
|
||||
assert x.foo(*[4, 2]) == '4, 2'
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
x.foo()
|
||||
|
||||
assert exc_info.value.errors() == [
|
||||
{'loc': ('a',), 'msg': 'field required', 'type': 'value_error.missing'},
|
||||
{'loc': ('b',), 'msg': 'field required', 'type': 'value_error.missing'},
|
||||
]
|
||||
|
||||
|
||||
def test_class_method():
|
||||
class X:
|
||||
@classmethod
|
||||
@validate_arguments
|
||||
def foo(cls, a: int, b: int):
|
||||
assert cls == X
|
||||
return f'{a}, {b}'
|
||||
|
||||
x = X()
|
||||
assert x.foo(4, 2) == '4, 2'
|
||||
assert x.foo(*[4, 2]) == '4, 2'
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
x.foo()
|
||||
|
||||
assert exc_info.value.errors() == [
|
||||
{'loc': ('a',), 'msg': 'field required', 'type': 'value_error.missing'},
|
||||
{'loc': ('b',), 'msg': 'field required', 'type': 'value_error.missing'},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user