From 9f1d3f498ce6429a9e3f97763817fd7077b7b42c Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Thu, 11 Aug 2022 23:53:32 +0200 Subject: [PATCH] Add support for `re.Pattern` --- changes/4366-hramezani.md | 1 + pydantic/fields.py | 3 ++- pydantic/schema.py | 2 +- pydantic/validators.py | 2 +- tests/test_types.py | 12 +++++++----- 5 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 changes/4366-hramezani.md diff --git a/changes/4366-hramezani.md b/changes/4366-hramezani.md new file mode 100644 index 00000000000..a70bedab388 --- /dev/null +++ b/changes/4366-hramezani.md @@ -0,0 +1 @@ +Add support for `re.Pattern` diff --git a/pydantic/fields.py b/pydantic/fields.py index 917798d7261..e8c15aa609e 100644 --- a/pydantic/fields.py +++ b/pydantic/fields.py @@ -1,4 +1,5 @@ import copy +import re from collections import Counter as CollectionCounter, defaultdict, deque from collections.abc import Callable, Hashable as CollectionsHashable, Iterable as CollectionsIterable from typing import ( @@ -595,7 +596,7 @@ def _type_analysis(self) -> None: # noqa: C901 (ignore complexity) self.required = False self.allow_none = True return - elif self.type_ is Pattern: + elif self.type_ is Pattern or self.type_ is re.Pattern: # python 3.7 only, Pattern is a typing object but without sub fields return elif is_literal_type(self.type_): diff --git a/pydantic/schema.py b/pydantic/schema.py index 4df0b04a116..dfcd40491d1 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -804,7 +804,7 @@ def add_field_type_to_schema(field_type: Any, schema_: Dict[str, Any]) -> None: """ for type_, t_schema in field_class_to_schema: # Fallback for `typing.Pattern` as it is not a valid class - if lenient_issubclass(field_type, type_) or field_type is type_ is Pattern: + if lenient_issubclass(field_type, type_) or (type_ is Pattern or type_ is re.Pattern): schema_.update(t_schema) break diff --git a/pydantic/validators.py b/pydantic/validators.py index 052edd3e436..59933a0d8c2 100644 --- a/pydantic/validators.py +++ b/pydantic/validators.py @@ -684,7 +684,7 @@ def find_validators( # noqa: C901 (ignore complexity) if is_none_type(type_): yield none_validator return - if type_ is Pattern: + if type_ is Pattern or type_ is re.Pattern: yield pattern_validator return if type_ is Hashable or type_ is CollectionsHashable: diff --git a/tests/test_types.py b/tests/test_types.py index 425bf9d02c9..a003c92b1f5 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -2540,9 +2540,10 @@ class JsonRequired(BaseModel): assert JsonRequired().dict() == {'json_obj': None} -def test_pattern(): +@pytest.mark.parametrize('pattern_type', [re.Pattern, Pattern]) +def test_pattern1(pattern_type): class Foobar(BaseModel): - pattern: Pattern + pattern: pattern_type f = Foobar(pattern=r'^whatev.r\d$') assert f.pattern.__class__.__name__ == 'Pattern' @@ -2558,14 +2559,15 @@ class Foobar(BaseModel): assert Foobar.schema() == { 'type': 'object', 'title': 'Foobar', - 'properties': {'pattern': {'type': 'string', 'format': 'regex', 'title': 'Pattern'}}, + 'properties': {'pattern': {'type': 'sting', 'format': 'regex', 'title': 'Pattern'}}, 'required': ['pattern'], } -def test_pattern_error(): +@pytest.mark.parametrize('pattern_type', [re.Pattern, Pattern]) +def test_pattern_error(pattern_type): class Foobar(BaseModel): - pattern: Pattern + pattern: pattern_type with pytest.raises(ValidationError) as exc_info: Foobar(pattern='[xx')