From e54b2d5bb017c788d64e9d7efe29172b1450c5ec Mon Sep 17 00:00:00 2001 From: Sarbjot Singh Date: Tue, 6 Feb 2024 04:07:19 +0000 Subject: [PATCH] fixes __pydantic_config__ ignored for TypeDict --- pydantic/json_schema.py | 24 ++++++++++++++++------- tests/test_json_schema.py | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/pydantic/json_schema.py b/pydantic/json_schema.py index e24a5fcc9c..c1e7733c34 100644 --- a/pydantic/json_schema.py +++ b/pydantic/json_schema.py @@ -1214,18 +1214,24 @@ def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaVa ] if self.mode == 'serialization': named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) - - config = _get_typed_dict_config(schema) + cls = _get_typed_dict_cls(schema) + config = _get_typed_dict_config(cls) with self._config_wrapper_stack.push(config): json_schema = self._named_required_fields_schema(named_required_fields) + json_schema_extra = config.get('json_schema_extra') extra = schema.get('extra_behavior') if extra is None: extra = config.get('extra', 'ignore') - if extra == 'forbid': - json_schema['additionalProperties'] = False - elif extra == 'allow': - json_schema['additionalProperties'] = True + + if cls is not None: + title = config.get('title') or cls.__name__ + json_schema = self._update_class_schema(json_schema, title, extra, cls, json_schema_extra) + else: + if extra == 'forbid': + json_schema['additionalProperties'] = False + elif extra == 'allow': + json_schema['additionalProperties'] = True return json_schema @@ -2414,9 +2420,13 @@ def __hash__(self) -> int: return hash(type(self)) -def _get_typed_dict_config(schema: core_schema.TypedDictSchema) -> ConfigDict: +def _get_typed_dict_cls(schema: core_schema.TypedDictSchema) -> type[Any] | None: metadata = _core_metadata.CoreMetadataHandler(schema).metadata cls = metadata.get('pydantic_typed_dict_cls') + return cls + + +def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: if cls is not None: try: return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py index 9a7671e52e..b086f375f2 100644 --- a/tests/test_json_schema.py +++ b/tests/test_json_schema.py @@ -2545,6 +2545,46 @@ def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHa } +def test_typeddict_with_title(): + class Model(TypedDict): + __pydantic_config__ = ConfigDict(title='Test') # type: ignore + a: str + + assert TypeAdapter(Model).json_schema() == { + 'title': 'Test', + 'type': 'object', + 'properties': {'a': {'title': 'A', 'type': 'string'}}, + 'required': ['a'], + } + + +def test_typeddict_with_json_schema_extra(): + class Model(TypedDict): + __pydantic_config__ = ConfigDict(title='Test', json_schema_extra={'foobar': 'hello'}) # type: ignore + a: str + + assert TypeAdapter(Model).json_schema() == { + 'title': 'Test', + 'type': 'object', + 'properties': {'a': {'title': 'A', 'type': 'string'}}, + 'required': ['a'], + 'foobar': 'hello', + } + + +def test_typeddict_with__callable_json_schema_extra(): + def json_schema_extra(schema, model_class): + schema.pop('properties') + schema['type'] = 'override' + assert model_class is Model + + class Model(TypedDict): + __pydantic_config__ = ConfigDict(title='Test', json_schema_extra=json_schema_extra) # type: ignore + a: str + + assert TypeAdapter(Model).json_schema() == {'title': 'Test', 'type': 'override', 'required': ['a']} + + @pytest.mark.parametrize( 'annotation,kwargs,field_schema', [