New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix extra fields behavior for TypedDict and make additionalProperties explicit in JSON schema #6115
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,8 +31,9 @@ | |
|
||
import pytest | ||
from pydantic_core import CoreSchema, core_schema, to_json | ||
from typing_extensions import Annotated, Literal | ||
from typing_extensions import Annotated, Literal, TypedDict | ||
|
||
import pydantic | ||
from pydantic import ( | ||
BaseModel, | ||
Field, | ||
|
@@ -2083,6 +2084,117 @@ class Model(BaseModel): | |
} | ||
|
||
|
||
def test_model_with_extra_allow(): | ||
class Model(BaseModel): | ||
model_config = ConfigDict(extra='allow') | ||
a: str | ||
|
||
assert Model.model_json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
'additionalProperties': True, | ||
} | ||
|
||
|
||
def test_model_with_extra_ignore(): | ||
class Model(BaseModel): | ||
model_config = ConfigDict(extra='ignore') | ||
a: str | ||
|
||
assert Model.model_json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
} | ||
|
||
|
||
def test_dataclass_with_extra_allow(): | ||
@pydantic.dataclasses.dataclass | ||
class Model: | ||
__pydantic_config__ = ConfigDict(extra='allow') | ||
a: str | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
'additionalProperties': True, | ||
} | ||
|
||
|
||
def test_dataclass_with_extra_ignore(): | ||
@pydantic.dataclasses.dataclass | ||
class Model: | ||
__pydantic_config__ = ConfigDict(extra='ignore') | ||
a: str | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
} | ||
|
||
|
||
def test_dataclass_with_extra_forbid(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are at least a couple cases with mismatched test names and contents (including at least this one and @pytest.mark.parametrize(
'extra,additional_properties', [('allow', {}), ('ignore', None), ('forbid', False), (None, None)]
)
def test_basemodel_extra(extra, additional_properties):
class A(BaseModel):
if extra is not None:
model_config = dict(extra=extra)
data: str
json_schema = A.model_json_schema()
if additional_properties is None:
assert 'additionalProperties' not in json_schema
else:
assert json_schema['additionalProperties'] == additional_properties and similar for typeddict, dataclass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just fixed the one name |
||
@pydantic.dataclasses.dataclass | ||
class Model: | ||
__pydantic_config__ = ConfigDict(extra='ignore') | ||
a: str | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
} | ||
|
||
|
||
def test_typeddict_with_extra_allow(): | ||
class Model(TypedDict): | ||
__pydantic_config__ = ConfigDict(extra='allow') # type: ignore | ||
a: str | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
'additionalProperties': True, | ||
} | ||
|
||
|
||
def test_typeddict_with_extra_ignore(): | ||
class Model(TypedDict): | ||
__pydantic_config__ = ConfigDict(extra='ignore') # type: ignore | ||
a: str | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
} | ||
|
||
|
||
def test_typeddict_with_extra_forbid(): | ||
@pydantic.dataclasses.dataclass | ||
class Model: | ||
__pydantic_config__ = ConfigDict(extra='ignore') | ||
a: str | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'title': 'Model', | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
} | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'annotation,kwargs,field_schema', | ||
[ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you see somewhere saying that this should be set to
True
when additionalProperties are allowed? Looking at https://json-schema.org/understanding-json-schema/reference/object.html#additional-properties I only see it saying it should beFalse
or a valid JSON schema. (That's why in my dmontagu/6082 branch I set it to{}
, which is the JSON schema forAny
.)If
True
means the same thing as{}
here then this is fine, I just haven't seen that before.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(This could also be handled in
_update_class_schema
, I just see it was passed through without modification down there)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True
seems ot be valid, at least according to SwaggerUI