Fix discriminated union schema gen bug#8904
Conversation
CodSpeed Performance ReportMerging #8904 will not alter performanceComparing Summary
|
Deploying with
|
| Latest commit: |
d35cd68
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://36f1154d.pydantic-docs2.pages.dev |
| Branch Preview URL: | https://fix-schema-building-bug.pydantic-docs2.pages.dev |
adriangb
left a comment
There was a problem hiding this comment.
I think I'd like a little bit more documentation on how this fixes things (maybe resultant in a comment in the code) so we don't make the same mistake again, but other than that LGTM.
@sydney-runkle this is really impressive work in record time, great job.
| if definitions is None: | ||
| definitions = collect_definitions(schema) | ||
| definitions = {k: recurse(v, inner) for k, v in definitions.items()} | ||
| s = apply_discriminator(s, discriminator, definitions) |
There was a problem hiding this comment.
🤯 care to explain how this fixes things?
There was a problem hiding this comment.
Sure thing! I'll add some more comments in the code to explain things 😄!
I'll also add a more in-depth note to this PR, so we can reference that for more detail, if needed 👍.
There was a problem hiding this comment.
So previously, with the above snippet of code, the resultant core_schema was the following:
{'definitions': [{'cls': <class '__main__.LeafState'>,
'config': {'title': 'LeafState'},
'custom_init': False,
'metadata': {'pydantic.internal.needs_apply_discriminated_union': False,
'pydantic_js_annotation_functions': [],
'pydantic_js_functions': [functools.partial(<function modify_model_json_schema at 0x101bcd900>, cls=<class '__main__.LeafState'>),
<bound method BaseModel.__get_pydantic_json_schema__ of <class '__main__.LeafState'>>]},
'ref': '__main__.LeafState:5484303040',
'root_model': False,
'schema': {'computed_fields': [],
'fields': {'state_type': {'metadata': {'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x101c09d80>],
'pydantic_js_functions': []},
'schema': {'expected': ['leaf'],
'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'type': 'literal'},
'type': 'model-field'}},
'model_name': 'LeafState',
'type': 'model-fields'},
'type': 'model'},
{'cls': <class '__main__.LoopState'>,
'config': {'title': 'LoopState'},
'custom_init': False,
'metadata': {'pydantic.internal.needs_apply_discriminated_union': False,
'pydantic_js_annotation_functions': [],
'pydantic_js_functions': [functools.partial(<function modify_model_json_schema at 0x101bcd900>, cls=<class '__main__.LoopState'>),
<bound method BaseModel.__get_pydantic_json_schema__ of <class '__main__.LoopState'>>]},
'ref': '__main__.LoopState:5484288784',
'root_model': False,
'schema': {'computed_fields': [],
'fields': {'state_type': {'metadata': {'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x101cf8310>],
'pydantic_js_functions': []},
'schema': {'expected': ['loop'],
'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'type': 'literal'},
'type': 'model-field'},
'substate': {'metadata': {'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x101cf83a0>],
'pydantic_js_functions': []},
'schema': {'default': Ellipsis,
'schema': {'choices': [{'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'schema_ref': '__main__.NestedState:5484231952',
'type': 'definition-ref'},
{'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'schema_ref': '__main__.LoopState:5484288784',
'type': 'definition-ref'},
{'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'schema_ref': '__main__.LeafState:5484303040',
'type': 'definition-ref'}],
'metadata': {'pydantic.internal.needs_apply_discriminated_union': True,
'pydantic.internal.union_discriminator': 'state_type'},
'type': 'union'},
'type': 'default'},
'type': 'model-field'}},
'model_name': 'LoopState',
'type': 'model-fields'},
'type': 'model'},
{'cls': <class '__main__.NestedState'>,
'config': {'title': 'NestedState'},
'custom_init': False,
'metadata': {'pydantic.internal.needs_apply_discriminated_union': False,
'pydantic_js_annotation_functions': [],
'pydantic_js_functions': [functools.partial(<function modify_model_json_schema at 0x101bcd900>, cls=<class '__main__.NestedState'>),
<bound method BaseModel.__get_pydantic_json_schema__ of <class '__main__.NestedState'>>]},
'ref': '__main__.NestedState:5484231952',
'root_model': False,
'schema': {'computed_fields': [],
'fields': {'state_type': {'metadata': {'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x101cf80d0>],
'pydantic_js_functions': []},
'schema': {'expected': ['nested'],
'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'type': 'literal'},
'type': 'model-field'},
'substate': {'metadata': {'pydantic_js_annotation_functions': [<function get_json_schema_update_func.<locals>.json_schema_update_func at 0x101cf8160>],
'pydantic_js_functions': []},
'schema': {'default': Ellipsis,
'schema': {'choices': [{'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'schema_ref': '__main__.NestedState:5484231952',
'type': 'definition-ref'},
{'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'schema_ref': '__main__.LoopState:5484288784',
'type': 'definition-ref'},
{'metadata': {'pydantic.internal.needs_apply_discriminated_union': False},
'schema_ref': '__main__.LeafState:5484303040',
'type': 'definition-ref'}],
'metadata': {'pydantic.internal.needs_apply_discriminated_union': True,
'pydantic.internal.union_discriminator': 'state_type'},
'type': 'union'},
'type': 'default'},
'type': 'model-field'}},
'model_name': 'NestedState',
'type': 'model-fields'},
'type': 'model'}],
'schema': {'choices': {'leaf': {'schema_ref': '__main__.LeafState:5484303040',
'type': 'definition-ref'},
'loop': {'schema_ref': '__main__.LoopState:5484288784',
'type': 'definition-ref'},
'nested': {'schema_ref': '__main__.NestedState:5484231952',
'type': 'definition-ref'}},
'discriminator': 'state_type',
'from_attributes': True,
'metadata': {'pydantic.internal.needs_apply_discriminated_union': True,
'pydantic.internal.union_discriminator': 'state_type'},
'strict': False,
'type': 'tagged-union'},
'type': 'definitions'}We already identified that the schemas for NestedState and LoopState within the definitions list were marked as having union type substate schemas, rather than tagged-union substate schemas. Interestingly, though, the metadata for those schemas had the necessary discriminated-union related information:
'metadata': {'pydantic.internal.needs_apply_discriminated_union': True, 'pydantic.internal.union_discriminator': 'state_type'}
So the question, then, is why aren't we "applying" the discriminators to those schemas? This is where my change comes in - after we collect the definitions schemas, we also need to pass over them + apply discriminators where appropriate. Within the inner function, we were already calling recurse, but we weren't calling it on any definitions schemas, which was the missing piece.
|
Update -- I believe this fixed the performance issue for the above case, but didn't entirely fix the schema generation process. Here's a fix for that: #8932. I'd like to work on a few other schema generation / performance related issues, but hopefully we can get these out in another patch release soon 😄. Feel free to ping me if you have any questions! |
Fix #8709
With this code snippet:
Previously:
Now, note the speedups: