-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
CoreMetadata
refactor with an emphasis on documentation and reducing complexity
#10675
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
Conversation
…lue is overridden by introduced complexity and overhead
Deploying pydantic-docs with
|
Latest commit: |
2a08ddf
|
Status: | ✅ Deploy successful! |
Preview URL: | https://1b3fed65.pydantic-docs.pages.dev |
Branch Preview URL: | https://metadata-stuck-on-a-flight.pydantic-docs.pages.dev |
CodSpeed Performance ReportMerging #10675 will not alter performanceComparing Summary
|
CoreMetadata
refactor as a consequence of a very long flightCoreMetadata
refactor with an emphasis on documentation and reducing complexity
…py and migrating to json_schema.py
…fers some typing support - to be improved with migration to pydantic-core
pydantic/_internal/_core_metadata.py
Outdated
pydantic_js_prefer_positional_arguments: bool | None = None, | ||
pydantic_js_input_core_schema: CoreSchema | None = None, |
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 is probably overkill (these params specifically). Happy to remove. The others make more sense given that they need special update logic.
def get_json_schema_update_func( | ||
json_schema_update: JsonSchemaValue, json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None | ||
) -> GetJsonSchemaFunction: | ||
def json_schema_update_func( | ||
core_schema_or_field: CoreSchemaOrField, handler: GetJsonSchemaHandler | ||
) -> JsonSchemaValue: | ||
json_schema = {**handler(core_schema_or_field), **json_schema_update} | ||
add_json_schema_extra(json_schema, json_schema_extra) | ||
return json_schema | ||
|
||
return json_schema_update_func | ||
|
||
|
||
def add_json_schema_extra( | ||
json_schema: JsonSchemaValue, json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None | ||
): | ||
if isinstance(json_schema_extra, dict): | ||
json_schema.update(to_jsonable_python(json_schema_extra)) | ||
elif callable(json_schema_extra): | ||
json_schema_extra(json_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.
Also, shouldn't be in json_schema.py
def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], config: ConfigDict) -> None: | ||
"""Update json_schema with the following, extracted from `config` and `cls`: | ||
|
||
* title | ||
* description | ||
* additional properties | ||
* json_schema_extra | ||
* deprecated | ||
|
||
Done in place, hence there's no return value as the original json_schema is mutated. | ||
No ref resolving is involved here, as that's not appropriate for simple updates. |
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 now has lots of the logic that was mistakenly in _generate_schema.py
schema_to_update = self.get_schema_from_definitions(JsonRef(ref)) | ||
if schema_to_update is None: | ||
raise RuntimeError(f'Cannot update undefined schema for $ref={ref}') | ||
return self.resolve_ref_schema(schema_to_update) |
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.
Renamed and simplified. There's a function called this in GenerateJsonSchemaHandler
, and ideally, we'd get rid of that eventually, so I think this helps draw some parallels to make that simplification easier in the future.
def test_typeddict_with_extra_behavior_allow(): | ||
class Model: | ||
@classmethod | ||
def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: | ||
return core_schema.typed_dict_schema( | ||
{'a': core_schema.typed_dict_field(core_schema.str_schema())}, | ||
extra_behavior='allow', | ||
) | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
'additionalProperties': True, | ||
} |
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.
So, I think we should only be pulling the extra_behavior
from the config here. If folks disagree, I can pull from the schema as well, though not sure why we have this setting there - seems redundant.
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.
extra items are going to be supported soon for typed dicts in Python. Is Pydantic support for it going to be easy to implement with these changes?
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.
1 files reviewed, 1 total issue(s) found.
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
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.
Generally looking good to me other than the couple comments I made above about the warning message; not sure if you plan to address any of the TODO comments you added before merging, but I'm okay even if they aren't addressed.
Wasn't planning on addressing the TODOs before merging - I feel like this is big enough already, and the callable stuff would represent a pretty significant structural change. I will work on getting the fastapi CI step to green, though. |
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.
Changes look reasonable and indeed having less stuff in the GenerateSchema
class is much better.
Move the
CoreMetadata
structure to rust so that we can pull out some of thecast
calls and just get natural type checking benefits there.
Will we get any benefits to move things to Rust here? Will this introduce an extra level of indirection, making things harder to understand?
def test_typeddict_with_extra_behavior_allow(): | ||
class Model: | ||
@classmethod | ||
def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: | ||
return core_schema.typed_dict_schema( | ||
{'a': core_schema.typed_dict_field(core_schema.str_schema())}, | ||
extra_behavior='allow', | ||
) | ||
|
||
assert TypeAdapter(Model).json_schema() == { | ||
'type': 'object', | ||
'properties': {'a': {'title': 'A', 'type': 'string'}}, | ||
'required': ['a'], | ||
'additionalProperties': True, | ||
} |
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.
extra items are going to be supported soon for typed dicts in Python. Is Pydantic support for it going to be easy to implement with these changes?
My only thought here was then we could then type |
This is where moving this to rust would be super helpful, then we can type |
Yes, I haven't removed the |
I would rather support the PEP 728 added feature and slowly deprecate pulling the info from the config (or support both, but disallow having both specified at the same time?) |
…tic/pydantic into metadata-stuck-on-a-flight
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.
I'm also fine with removing extra_behavior
for now, and re-add it in some way with PEP 728 coming soon
if TYPE_CHECKING: | ||
from ..annotated_handlers import GetJsonSchemaHandler | ||
pass |
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.
Can be removed
# Remove the first one once that test is fixed, see https://github.com/pydantic/pydantic/pull/10029 | ||
# the remaining tests all failing bc we now correctly add a `'deprecated': True` attribute to the JSON schema, | ||
# So it's the FastAPI tests that need to be updated here | ||
./scripts/test.sh -vv \ |
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.
FYI I think the first one is already passing and the others should be fixed by https://github.com/fastapi/fastapi/pull/12971/files.
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.
Awesome, thanks!
If you wouldn't mind, could you open a PR against pydantic
to remove these once your FastAPI
PR is merged? Thanks!!
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.
I'd be happy to! Judging by my past experience with FastAPI I wouldn't expect that to happen any time soon 🙃
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.
Thanks so much!!
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.
I would recommend reviewing this commit by commit, though looking over the whole thing afterwards gives some helpful perspective as well.
Changes in this PR:
CoreMetadataHandler
structure - it offered minimal type checking benefits, some overhead, and a good amount of unnecessary complexity.CoreMetadata
dict so that it's clear what we're adding to it and why._generate_schema.py
and_known_annotated_metadata.py
and consolidate it injson_schema.py
json_schema_extra
updates, no longer build a callable ahead of time.TODOs, in the future:
CoreMetadata
structure to rust so that we can pull out some of thecast
calls and just get natural type checking benefits there. One hiccup here is that replacingmetadata: dict[str, Any]
withmetadata: CoreMetadata
, doesn't allow for arbitrary injection (say, in a custom schema), so I'm not sure what we want to do about that. It also then raises errors when we do our temporary injects for say, discriminator application, but we shouldn't be doing that anyway.pydantic_js_functions
andpydantic_js_annotation_functions