Skip to content

Commit 8522ebb

Browse files
committed
Preserve RootModel core metadata
Backport of: #13129
1 parent a37f3af commit 8522ebb

3 files changed

Lines changed: 44 additions & 5 deletions

File tree

pydantic/_internal/_generate_schema.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -840,8 +840,17 @@ def _model_schema(self, cls: type[BaseModel]) -> core_schema.CoreSchema:
840840
generic_origin: type[BaseModel] | None = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin')
841841

842842
if cls.__pydantic_root_model__:
843-
# FIXME: should the common field metadata be used here?
844-
inner_schema, _ = self._common_field_schema('root', fields['root'], decorators)
843+
inner_schema, metadata = self._common_field_schema('root', fields['root'], decorators)
844+
if cls.__doc__ and metadata.get('pydantic_js_updates', {}).get('description'):
845+
# This is a bit of a leaky abstraction, but as the model docstring takes priority
846+
# over the root field's description, we need to override it here. This can't be done
847+
# in the JSON Schema generation logic because the metadata's `pydantic_js_updates` are
848+
# applied last, and overrides any value previously set (so the description set from the
849+
# docstring in `GenerateJsonSchema._update_class_schema()` is overridden):
850+
update_core_metadata(
851+
metadata, pydantic_js_updates={'description': inspect.cleandoc(cls.__doc__)}
852+
)
853+
845854
inner_schema = apply_model_validators(inner_schema, model_validators, 'inner')
846855
model_schema = core_schema.model_schema(
847856
cls,
@@ -852,6 +861,7 @@ def _model_schema(self, cls: type[BaseModel]) -> core_schema.CoreSchema:
852861
post_init=getattr(cls, '__pydantic_post_init__', None),
853862
config=core_config,
854863
ref=model_ref,
864+
metadata=metadata,
855865
)
856866
else:
857867
fields_schema: core_schema.CoreSchema = core_schema.model_fields_schema(

pydantic/json_schema.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,7 +1636,6 @@ def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], con
16361636
"""
16371637
from ._internal._dataclasses import is_stdlib_dataclass
16381638
from .main import BaseModel
1639-
from .root_model import RootModel
16401639

16411640
if (config_title := config.get('title')) is not None:
16421641
json_schema.setdefault('title', config_title)
@@ -1664,8 +1663,6 @@ def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], con
16641663

16651664
if docstring:
16661665
json_schema.setdefault('description', inspect.cleandoc(docstring))
1667-
elif issubclass(cls, RootModel) and (root_description := cls.__pydantic_fields__['root'].description):
1668-
json_schema.setdefault('description', root_description)
16691666

16701667
extra = config.get('extra')
16711668
if 'additionalProperties' not in json_schema: # This check is particularly important for `typed_dict_schema()`

tests/test_json_schema.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5391,6 +5391,38 @@ class C(RootModel[A]):
53915391
}
53925392

53935393

5394+
def test_root_model_annotated_root_type_parameterized() -> None:
5395+
"""https://github.com/pydantic/pydantic/issues/13123"""
5396+
5397+
MyType = Annotated[str, Field(examples=['hello'], description='desc', deprecated=True)]
5398+
5399+
class MyModel(RootModel[MyType]):
5400+
pass
5401+
5402+
assert MyModel.model_json_schema() == {
5403+
'deprecated': True,
5404+
'description': 'desc',
5405+
'examples': ['hello'],
5406+
'title': 'MyModel',
5407+
'type': 'string',
5408+
}
5409+
5410+
5411+
def test_root_model_annotated_root_type() -> None:
5412+
"""https://github.com/pydantic/pydantic/issues/13123"""
5413+
5414+
class MyModel(RootModel):
5415+
root: Annotated[str, Field(examples=['hello'], description='desc', deprecated=True)]
5416+
5417+
assert MyModel.model_json_schema() == {
5418+
'deprecated': True,
5419+
'description': 'desc',
5420+
'examples': ['hello'],
5421+
'title': 'MyModel',
5422+
'type': 'string',
5423+
}
5424+
5425+
53945426
def test_type_adapter_json_schemas_title_description():
53955427
class Model(BaseModel):
53965428
a: str

0 commit comments

Comments
 (0)