diff --git a/changes/3135-PrettyWood.md b/changes/3135-PrettyWood.md new file mode 100644 index 0000000..643e1f5 --- /dev/null +++ b/changes/3135-PrettyWood.md @@ -0,0 +1 @@ +fix `validate_arguments` issue with `Config.validate_all` diff --git a/pydantic/decorator.py b/pydantic/decorator.py index 869afee..ca709b4 100644 --- a/pydantic/decorator.py +++ b/pydantic/decorator.py @@ -220,15 +220,15 @@ class ValidatedFunction: class DecoratorBaseModel(BaseModel): @validator(self.v_args_name, check_fields=False, allow_reuse=True) - def check_args(cls, v: List[Any]) -> List[Any]: - if takes_args: + def check_args(cls, v: Optional[List[Any]]) -> Optional[List[Any]]: + if takes_args or v is None: return v raise TypeError(f'{pos_args} positional arguments expected but {pos_args + len(v)} given') @validator(self.v_kwargs_name, check_fields=False, allow_reuse=True) - def check_kwargs(cls, v: Dict[str, Any]) -> Dict[str, Any]: - if takes_kwargs: + def check_kwargs(cls, v: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]: + if takes_kwargs or v is None: return v plural = '' if len(v) == 1 else 's' @@ -236,13 +236,19 @@ class ValidatedFunction: raise TypeError(f'unexpected keyword argument{plural}: {keys}') @validator(V_POSITIONAL_ONLY_NAME, check_fields=False, allow_reuse=True) - def check_positional_only(cls, v: List[str]) -> None: + def check_positional_only(cls, v: Optional[List[str]]) -> None: + if v is None: + return + plural = '' if len(v) == 1 else 's' keys = ', '.join(map(repr, v)) raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}') @validator(V_DUPLICATE_KWARGS, check_fields=False, allow_reuse=True) - def check_duplicate_kwargs(cls, v: List[str]) -> None: + def check_duplicate_kwargs(cls, v: Optional[List[str]]) -> None: + if v is None: + return + plural = '' if len(v) == 1 else 's' keys = ', '.join(map(repr, v)) raise TypeError(f'multiple values for argument{plural}: {keys}') diff --git a/tests/test_decorator.py b/tests/test_decorator.py index cbfc2dd..ea06368 100644 --- a/tests/test_decorator.py +++ b/tests/test_decorator.py @@ -1,6 +1,7 @@ import asyncio import inspect import sys +from datetime import datetime, timezone from pathlib import Path from typing import List @@ -399,3 +400,30 @@ def test_validate(mocker): func.validate(['qwe'], 2) stub.assert_not_called() + + +def test_validate_all(): + @validate_arguments(config=dict(validate_all=True)) + def foo(dt: datetime = Field(default_factory=lambda: 946684800)): + return dt + + assert foo() == datetime(2000, 1, 1, tzinfo=timezone.utc) + assert foo(0) == datetime(1970, 1, 1, tzinfo=timezone.utc) + + +@skip_pre_38 +def test_validate_all_positional(create_module): + module = create_module( + # language=Python + """ +from datetime import datetime + +from pydantic import Field, validate_arguments + +@validate_arguments(config=dict(validate_all=True)) +def foo(dt: datetime = Field(default_factory=lambda: 946684800), /): + return dt +""" + ) + assert module.foo() == datetime(2000, 1, 1, tzinfo=timezone.utc) + assert module.foo(0) == datetime(1970, 1, 1, tzinfo=timezone.utc)