Accepting request 861663 from home:mcalabkova:branches:devel:languages:python
Staging:I - Update to 1.7.3 * python 3.9 support * Private model attributes * "secrets files" support in BaseSettings * convert stdlib dataclasses to pydantic dataclasses and use stdlib dataclasses in models * few bugfixes - Drop validate-config.patch OBS-URL: https://build.opensuse.org/request/show/861663 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-pydantic?expand=0&rev=16
This commit is contained in:
parent
eefd1e9b88
commit
2980a88758
@ -1,3 +0,0 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:41f71147910f26944770cdcac90131241d36fd8212b34468d043e871da065e20
|
|
||||||
size 244677
|
|
3
pydantic-1.7.3.tar.gz
Normal file
3
pydantic-1.7.3.tar.gz
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:0fef39e969d69cc73af8c98728c27e719ccb38b16a52ef2e69a088540731f1fa
|
||||||
|
size 266702
|
@ -1,3 +1,14 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Fri Jan 8 13:10:40 UTC 2021 - Markéta Machová <mmachova@suse.com>
|
||||||
|
|
||||||
|
- Update to 1.7.3
|
||||||
|
* python 3.9 support
|
||||||
|
* Private model attributes
|
||||||
|
* "secrets files" support in BaseSettings
|
||||||
|
* convert stdlib dataclasses to pydantic dataclasses and use stdlib dataclasses in models
|
||||||
|
* few bugfixes
|
||||||
|
- Drop validate-config.patch
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Mon Dec 7 01:00:22 UTC 2020 - Benjamin Greiner <code@bnavigator.de>
|
Mon Dec 7 01:00:22 UTC 2020 - Benjamin Greiner <code@bnavigator.de>
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#
|
#
|
||||||
# spec file for package python-pydantic
|
# spec file for package python-pydantic
|
||||||
#
|
#
|
||||||
# Copyright (c) 2020 SUSE LLC
|
# Copyright (c) 2021 SUSE LLC
|
||||||
# Copyright (c) 2019, Martin Hauke <mardnh@gmx.de>
|
# Copyright (c) 2019, Martin Hauke <mardnh@gmx.de>
|
||||||
#
|
#
|
||||||
# All modifications and additions to the file contributed by third parties
|
# All modifications and additions to the file contributed by third parties
|
||||||
@ -20,24 +20,23 @@
|
|||||||
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
|
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
|
||||||
%define skip_python2 1
|
%define skip_python2 1
|
||||||
Name: python-pydantic
|
Name: python-pydantic
|
||||||
Version: 1.6.1
|
Version: 1.7.3
|
||||||
Release: 0
|
Release: 0
|
||||||
Summary: Data validation and settings management using python type hinting
|
Summary: Data validation and settings management using python type hinting
|
||||||
License: MIT
|
License: MIT
|
||||||
Group: Development/Languages/Python
|
Group: Development/Languages/Python
|
||||||
URL: https://github.com/samuelcolvin/pydantic
|
URL: https://github.com/samuelcolvin/pydantic
|
||||||
Source: https://github.com/samuelcolvin/pydantic/archive/v%{version}.tar.gz#/pydantic-%{version}.tar.gz
|
Source: https://github.com/samuelcolvin/pydantic/archive/v%{version}.tar.gz#/pydantic-%{version}.tar.gz
|
||||||
# PATCH-FIX-UPSTREAM https://github.com/samuelcolvin/pydantic/commit/9c4860ce964a4eb2e22eedc21f21d406c596a82f Valdiate arguments config (#1663)
|
|
||||||
Patch0: validate-config.patch
|
|
||||||
BuildRequires: %{python_module email_validator >= 1.0.3}
|
BuildRequires: %{python_module email_validator >= 1.0.3}
|
||||||
|
BuildRequires: %{python_module pytest-mock}
|
||||||
BuildRequires: %{python_module pytest}
|
BuildRequires: %{python_module pytest}
|
||||||
BuildRequires: %{python_module python-dotenv >= 0.10.4}
|
BuildRequires: %{python_module python-dotenv >= 0.10.4}
|
||||||
BuildRequires: %{python_module setuptools}
|
BuildRequires: %{python_module setuptools}
|
||||||
BuildRequires: %{python_module typing_extensions >= 3.7.2}
|
BuildRequires: %{python_module typing_extensions >= 3.7.2}
|
||||||
BuildRequires: (python36-dataclasses if python36-base)
|
|
||||||
BuildRequires: (python3-dataclasses if python3-base < 3.7)
|
|
||||||
BuildRequires: fdupes
|
BuildRequires: fdupes
|
||||||
BuildRequires: python-rpm-macros
|
BuildRequires: python-rpm-macros
|
||||||
|
BuildRequires: (python3-dataclasses if python3-base < 3.7)
|
||||||
|
BuildRequires: (python36-dataclasses if python36-base)
|
||||||
%if 0%{?python_version_nodots} == 36
|
%if 0%{?python_version_nodots} == 36
|
||||||
Requires: python-dataclasses
|
Requires: python-dataclasses
|
||||||
%endif
|
%endif
|
||||||
@ -52,7 +51,8 @@ Data validation and settings management using Python type hinting.
|
|||||||
|
|
||||||
%prep
|
%prep
|
||||||
%setup -q -n pydantic-%{version}
|
%setup -q -n pydantic-%{version}
|
||||||
%patch0 -p1
|
# compatibility with new pytest
|
||||||
|
sed -i 's/yield_fixture/fixture/' tests/conftest.py
|
||||||
|
|
||||||
%build
|
%build
|
||||||
%python_build
|
%python_build
|
||||||
|
@ -1,304 +0,0 @@
|
|||||||
From 9c4860ce964a4eb2e22eedc21f21d406c596a82f Mon Sep 17 00:00:00 2001
|
|
||||||
From: Samuel Colvin <s@muelcolvin.com>
|
|
||||||
Date: Sun, 6 Sep 2020 23:17:52 +0100
|
|
||||||
Subject: [PATCH] Valdiate arguments config (#1663)
|
|
||||||
|
|
||||||
* add `configs` to validate_arguments
|
|
||||||
|
|
||||||
* simplify `validate_arguments` and add annotation for parameter `configs`
|
|
||||||
|
|
||||||
* change double quotes to single quotes
|
|
||||||
|
|
||||||
* reformat code
|
|
||||||
|
|
||||||
* fix mypy error
|
|
||||||
|
|
||||||
* fix mypy 'maximum semantic analysis' error
|
|
||||||
|
|
||||||
* rename 'configs' > 'config_params'
|
|
||||||
|
|
||||||
* change name and usage, start tests
|
|
||||||
|
|
||||||
* prevent setting fields on custom config
|
|
||||||
|
|
||||||
* add docs and fix mypy
|
|
||||||
|
|
||||||
* tweak docs
|
|
||||||
|
|
||||||
* add change
|
|
||||||
|
|
||||||
Co-authored-by: quantpy <quantpy@qq.com>
|
|
||||||
---
|
|
||||||
changes/1663-samuelcolvin.md | 1 +
|
|
||||||
docs/examples/validation_decorator_config.py | 26 +++++++++
|
|
||||||
docs/usage/validation_decorator.md | 21 +++++++-
|
|
||||||
pydantic/decorator.py | 51 +++++++++++++-----
|
|
||||||
setup.cfg | 1 -
|
|
||||||
tests/test_decorator.py | 55 ++++++++++++++++++++
|
|
||||||
6 files changed, 138 insertions(+), 17 deletions(-)
|
|
||||||
create mode 100644 changes/1663-samuelcolvin.md
|
|
||||||
create mode 100644 docs/examples/validation_decorator_config.py
|
|
||||||
|
|
||||||
Index: pydantic-1.6.1/changes/1663-samuelcolvin.md
|
|
||||||
===================================================================
|
|
||||||
--- /dev/null
|
|
||||||
+++ pydantic-1.6.1/changes/1663-samuelcolvin.md
|
|
||||||
@@ -0,0 +1 @@
|
|
||||||
+add `config` to `@validate_arguments`
|
|
||||||
Index: pydantic-1.6.1/docs/examples/validation_decorator_config.py
|
|
||||||
===================================================================
|
|
||||||
--- /dev/null
|
|
||||||
+++ pydantic-1.6.1/docs/examples/validation_decorator_config.py
|
|
||||||
@@ -0,0 +1,26 @@
|
|
||||||
+from pydantic import ValidationError, validate_arguments
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class Foobar:
|
|
||||||
+ def __init__(self, v: str):
|
|
||||||
+ self.v = v
|
|
||||||
+
|
|
||||||
+ def __add__(self, other: 'Foobar') -> str:
|
|
||||||
+ return f'{self} + {other}'
|
|
||||||
+
|
|
||||||
+ def __str__(self) -> str:
|
|
||||||
+ return f'Foobar({self.v})'
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+@validate_arguments(config=dict(arbitrary_types_allowed=True))
|
|
||||||
+def add_foobars(a: Foobar, b: Foobar):
|
|
||||||
+ return a + b
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+c = add_foobars(Foobar('a'), Foobar('b'))
|
|
||||||
+print(c)
|
|
||||||
+
|
|
||||||
+try:
|
|
||||||
+ add_foobars(1, 2)
|
|
||||||
+except ValidationError as e:
|
|
||||||
+ print(e)
|
|
||||||
Index: pydantic-1.6.1/docs/usage/validation_decorator.md
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-1.6.1.orig/docs/usage/validation_decorator.md
|
|
||||||
+++ pydantic-1.6.1/docs/usage/validation_decorator.md
|
|
||||||
@@ -78,8 +78,23 @@ _(This script is complete, it should run
|
|
||||||
```py
|
|
||||||
{!.tmp_examples/validation_decorator_async.py!}
|
|
||||||
```
|
|
||||||
-_(This script is complete, it should run "as is")_
|
|
||||||
|
|
||||||
+## Custom Config
|
|
||||||
+
|
|
||||||
+The model behind `validate_arguments` can be customised using a config setting which is equivalent to
|
|
||||||
+setting the `Config` sub-class in normal models.
|
|
||||||
+
|
|
||||||
+!!! warning
|
|
||||||
+ The `fields` and `alias_generator` properties of `Config` which allow aliases to be configured are not supported
|
|
||||||
+ yet with `@validate_arguments`, using them will raise an error.
|
|
||||||
+
|
|
||||||
+Configuration is set using the `config` keyword argument to the decorator, it may be either a config class
|
|
||||||
+or a dict of properties which are converted to a class later.
|
|
||||||
+
|
|
||||||
+```py
|
|
||||||
+{!.tmp_examples/validation_decorator_config.py!}
|
|
||||||
+```
|
|
||||||
+_(This script is complete, it should run "as is")_
|
|
||||||
|
|
||||||
## Limitations
|
|
||||||
|
|
||||||
@@ -126,7 +141,9 @@ in future.
|
|
||||||
|
|
||||||
### Config and Validators
|
|
||||||
|
|
||||||
-Custom [`Config`](model_config.md) and [validators](validators.md) are not yet supported.
|
|
||||||
+`fields` and `alias_generator` on custom [`Config`](model_config.md) are not supported, see [above](#custom-config).
|
|
||||||
+
|
|
||||||
+Neither are [validators](validators.md).
|
|
||||||
|
|
||||||
### Model fields and reserved arguments
|
|
||||||
|
|
||||||
Index: pydantic-1.6.1/pydantic/decorator.py
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-1.6.1.orig/pydantic/decorator.py
|
|
||||||
+++ pydantic-1.6.1/pydantic/decorator.py
|
|
||||||
@@ -1,5 +1,5 @@
|
|
||||||
from functools import wraps
|
|
||||||
-from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Tuple, TypeVar, cast, get_type_hints
|
|
||||||
+from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Tuple, Type, TypeVar, Union, cast, get_type_hints
|
|
||||||
|
|
||||||
from . import validator
|
|
||||||
from .errors import ConfigError
|
|
||||||
@@ -12,22 +12,30 @@ if TYPE_CHECKING:
|
|
||||||
from .typing import AnyCallable
|
|
||||||
|
|
||||||
Callable = TypeVar('Callable', bound=AnyCallable)
|
|
||||||
+ ConfigType = Union[None, Type[Any], Dict[str, Any]]
|
|
||||||
|
|
||||||
|
|
||||||
-def validate_arguments(function: 'Callable') -> 'Callable':
|
|
||||||
+def validate_arguments(func: 'Callable' = None, *, config: 'ConfigType' = None) -> 'Callable':
|
|
||||||
"""
|
|
||||||
Decorator to validate the arguments passed to a function.
|
|
||||||
"""
|
|
||||||
- vd = ValidatedFunction(function)
|
|
||||||
|
|
||||||
- @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)
|
|
||||||
+ def validate(_func: 'Callable') -> 'Callable':
|
|
||||||
+ vd = ValidatedFunction(_func, config)
|
|
||||||
+
|
|
||||||
+ @wraps(_func)
|
|
||||||
+ 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)
|
|
||||||
+
|
|
||||||
+ if func:
|
|
||||||
+ return validate(func)
|
|
||||||
+ else:
|
|
||||||
+ return cast('Callable', validate)
|
|
||||||
|
|
||||||
|
|
||||||
ALT_V_ARGS = 'v__args'
|
|
||||||
@@ -36,7 +44,7 @@ V_POSITIONAL_ONLY_NAME = 'v__positional_
|
|
||||||
|
|
||||||
|
|
||||||
class ValidatedFunction:
|
|
||||||
- def __init__(self, function: 'Callable'):
|
|
||||||
+ def __init__(self, function: 'Callable', config: 'ConfigType'): # noqa C901
|
|
||||||
from inspect import signature, Parameter
|
|
||||||
|
|
||||||
parameters: Mapping[str, Parameter] = signature(function).parameters
|
|
||||||
@@ -100,7 +108,7 @@ class ValidatedFunction:
|
|
||||||
# same with kwargs
|
|
||||||
fields[self.v_kwargs_name] = Dict[Any, Any], None
|
|
||||||
|
|
||||||
- self.create_model(fields, takes_args, takes_kwargs)
|
|
||||||
+ self.create_model(fields, takes_args, takes_kwargs, config)
|
|
||||||
|
|
||||||
def call(self, *args: Any, **kwargs: Any) -> Any:
|
|
||||||
values = self.build_values(args, kwargs)
|
|
||||||
@@ -170,9 +178,24 @@ class ValidatedFunction:
|
|
||||||
else:
|
|
||||||
return self.raw_function(**d)
|
|
||||||
|
|
||||||
- def create_model(self, fields: Dict[str, Any], takes_args: bool, takes_kwargs: bool) -> None:
|
|
||||||
+ def create_model(self, fields: Dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None:
|
|
||||||
pos_args = len(self.arg_mapping)
|
|
||||||
|
|
||||||
+ class CustomConfig:
|
|
||||||
+ pass
|
|
||||||
+
|
|
||||||
+ if not TYPE_CHECKING: # pragma: no branch
|
|
||||||
+ if isinstance(config, dict):
|
|
||||||
+ CustomConfig = type('Config', (), config) # noqa: F811
|
|
||||||
+ elif config is not None:
|
|
||||||
+ CustomConfig = config # noqa: F811
|
|
||||||
+
|
|
||||||
+ if hasattr(CustomConfig, 'fields') or hasattr(CustomConfig, 'alias_generator'):
|
|
||||||
+ raise ConfigError(
|
|
||||||
+ 'Setting the "fields" and "alias_generator" property on custom Config for '
|
|
||||||
+ '@validate_arguments is not yet supported, please remove.'
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
class DecoratorBaseModel(BaseModel):
|
|
||||||
@validator(self.v_args_name, check_fields=False, allow_reuse=True)
|
|
||||||
def check_args(cls, v: List[Any]) -> List[Any]:
|
|
||||||
@@ -196,7 +219,7 @@ class ValidatedFunction:
|
|
||||||
keys = ', '.join(map(repr, v))
|
|
||||||
raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}')
|
|
||||||
|
|
||||||
- class Config:
|
|
||||||
+ class Config(CustomConfig):
|
|
||||||
extra = Extra.forbid
|
|
||||||
|
|
||||||
self.model = create_model(to_camel(self.raw_function.__name__), __base__=DecoratorBaseModel, **fields)
|
|
||||||
Index: pydantic-1.6.1/setup.cfg
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-1.6.1.orig/setup.cfg
|
|
||||||
+++ pydantic-1.6.1/setup.cfg
|
|
||||||
@@ -1,6 +1,5 @@
|
|
||||||
[tool:pytest]
|
|
||||||
testpaths = tests
|
|
||||||
-timeout = 10
|
|
||||||
filterwarnings =
|
|
||||||
error
|
|
||||||
ignore::DeprecationWarning:distutils
|
|
||||||
Index: pydantic-1.6.1/tests/test_decorator.py
|
|
||||||
===================================================================
|
|
||||||
--- pydantic-1.6.1.orig/tests/test_decorator.py
|
|
||||||
+++ pydantic-1.6.1/tests/test_decorator.py
|
|
||||||
@@ -70,6 +70,7 @@ def test_wrap():
|
|
||||||
assert issubclass(foo_bar.model, BaseModel)
|
|
||||||
assert foo_bar.model.__fields__.keys() == {'a', 'b', 'args', 'kwargs'}
|
|
||||||
assert foo_bar.model.__name__ == 'FooBar'
|
|
||||||
+ assert foo_bar.model.schema()['title'] == 'FooBar'
|
|
||||||
# signature is slightly different on 3.6
|
|
||||||
if sys.version_info >= (3, 7):
|
|
||||||
assert repr(inspect.signature(foo_bar)) == '<Signature (a: int, b: int)>'
|
|
||||||
@@ -262,3 +263,57 @@ def test_class_method():
|
|
||||||
{'loc': ('a',), 'msg': 'field required', 'type': 'value_error.missing'},
|
|
||||||
{'loc': ('b',), 'msg': 'field required', 'type': 'value_error.missing'},
|
|
||||||
]
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def test_config_title():
|
|
||||||
+ @validate_arguments(config=dict(title='Testing'))
|
|
||||||
+ def foo(a: int, b: int):
|
|
||||||
+ return f'{a}, {b}'
|
|
||||||
+
|
|
||||||
+ assert foo(1, 2) == '1, 2'
|
|
||||||
+ assert foo(1, b=2) == '1, 2'
|
|
||||||
+ assert foo.model.schema()['title'] == 'Testing'
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def test_config_title_cls():
|
|
||||||
+ class Config:
|
|
||||||
+ title = 'Testing'
|
|
||||||
+
|
|
||||||
+ @validate_arguments(config=Config)
|
|
||||||
+ def foo(a: int, b: int):
|
|
||||||
+ return f'{a}, {b}'
|
|
||||||
+
|
|
||||||
+ assert foo(1, 2) == '1, 2'
|
|
||||||
+ assert foo(1, b=2) == '1, 2'
|
|
||||||
+ assert foo.model.schema()['title'] == 'Testing'
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def test_config_fields():
|
|
||||||
+ with pytest.raises(ConfigError, match='Setting the "fields" and "alias_generator" property on custom Config for @'):
|
|
||||||
+
|
|
||||||
+ @validate_arguments(config=dict(fields={'b': 'bang'}))
|
|
||||||
+ def foo(a: int, b: int):
|
|
||||||
+ return f'{a}, {b}'
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def test_config_arbitrary_types_allowed():
|
|
||||||
+ class EggBox:
|
|
||||||
+ def __str__(self) -> str:
|
|
||||||
+ return 'EggBox()'
|
|
||||||
+
|
|
||||||
+ @validate_arguments(config=dict(arbitrary_types_allowed=True))
|
|
||||||
+ def foo(a: int, b: EggBox):
|
|
||||||
+ return f'{a}, {b}'
|
|
||||||
+
|
|
||||||
+ assert foo(1, EggBox()) == '1, EggBox()'
|
|
||||||
+ with pytest.raises(ValidationError) as exc_info:
|
|
||||||
+ assert foo(1, 2) == '1, 2'
|
|
||||||
+
|
|
||||||
+ assert exc_info.value.errors() == [
|
|
||||||
+ {
|
|
||||||
+ 'loc': ('b',),
|
|
||||||
+ 'msg': 'instance of EggBox expected',
|
|
||||||
+ 'type': 'type_error.arbitrary_type',
|
|
||||||
+ 'ctx': {'expected_arbitrary_type': 'EggBox'},
|
|
||||||
+ },
|
|
||||||
+ ]
|
|
Loading…
x
Reference in New Issue
Block a user