diff --git a/changes/1767-PrettyWood.md b/changes/1767-PrettyWood.md new file mode 100644 index 00000000000..7262d7eca70 --- /dev/null +++ b/changes/1767-PrettyWood.md @@ -0,0 +1 @@ +add basic support of Pattern type in schema generation diff --git a/docs/build/schema_mapping.py b/docs/build/schema_mapping.py index 0972c4b9a30..f9731c0a006 100755 --- a/docs/build/schema_mapping.py +++ b/docs/build/schema_mapping.py @@ -145,6 +145,13 @@ 'JSON Schema Validation', '' ], + [ + 'Pattern', + 'string', + {'format': 'regex'}, + 'JSON Schema Validation', + '' + ], [ 'bytes', 'string', diff --git a/pydantic/schema.py b/pydantic/schema.py index 27c66b2bd37..0bb6db4e117 100644 --- a/pydantic/schema.py +++ b/pydantic/schema.py @@ -14,6 +14,7 @@ Iterable, List, Optional, + Pattern, Sequence, Set, Tuple, @@ -618,6 +619,7 @@ def field_singleton_sub_fields_schema( (IPv6Interface, {'type': 'string', 'format': 'ipv6interface'}), (IPv4Address, {'type': 'string', 'format': 'ipv4'}), (IPv6Address, {'type': 'string', 'format': 'ipv6'}), + (Pattern, {'type': 'string', 'format': 'regex'}), (str, {'type': 'string'}), (bytes, {'type': 'string', 'format': 'binary'}), (bool, {'type': 'boolean'}), @@ -643,7 +645,7 @@ def add_field_type_to_schema(field_type: Any, schema: Dict[str, Any]) -> None: and then modifies the given `schema` with the information from that type. """ for type_, t_schema in field_class_to_schema: - if issubclass(field_type, type_): + if lenient_issubclass(field_type, type_): schema.update(t_schema) break @@ -716,7 +718,7 @@ def field_singleton_schema( # noqa: C901 (ignore complexity) if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel): field_type = field_type.__pydantic_model__ - if issubclass(field_type, BaseModel): + if lenient_issubclass(field_type, BaseModel): model_name = model_name_map[field_type] if field_type not in known_models: sub_schema, sub_definitions, sub_nested_models = model_process_schema( diff --git a/pydantic/utils.py b/pydantic/utils.py index d692f4f53c6..3591fe6d9c8 100644 --- a/pydantic/utils.py +++ b/pydantic/utils.py @@ -12,6 +12,7 @@ List, Mapping, Optional, + Pattern, Set, Tuple, Type, @@ -104,7 +105,8 @@ def validate_field_name(bases: List[Type['BaseModel']], field_name: str) -> None def lenient_issubclass(cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]) -> bool: - return isinstance(cls, type) and issubclass(cls, class_or_tuple) + """Fallback for `typing.Pattern` as it is not a valid class""" + return isinstance(cls, type) and issubclass(cls, class_or_tuple) or cls is class_or_tuple is Pattern def in_ipython() -> bool: diff --git a/tests/test_types.py b/tests/test_types.py index 084b9d071a2..6752c607224 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -2055,6 +2055,13 @@ class Foobar(BaseModel): f2 = Foobar(pattern=p) assert f2.pattern is p + assert Foobar.schema() == { + 'type': 'object', + 'title': 'Foobar', + 'properties': {'pattern': {'type': 'string', 'format': 'regex', 'title': 'Pattern'}}, + 'required': ['pattern'], + } + def test_pattern_error(): class Foobar(BaseModel):