[Python] - Improve Model Deserialization Perf - #11705
Kashif Khan (kashifkhan) wants to merge 6 commits into
Conversation
commit: |
|
❌ There is undocummented changes. Run The following packages have changes but are not documented.
Show changes |
Python emitter diffBaseline Diff summary: 206 file(s), +32759 / -3295 Rendered diff: inline on the run summary, or the emitter-diff-html artifact. Informational check (eng/emitter-diff); does not block the PR. |
|
You can try these changes here
|
d85b447 to
5227b40
Compare
|
Hi @Kashif Khan (@kashifkhan). Your PR has had no update for 14 days and it is marked as a stale PR. If it is not updated within 14 additional days, the PR will automatically be closed. If you want to refresh the PR, please remove the |
iscai-msft
left a comment
There was a problem hiding this comment.
had copilot take a look and it found these regressions, can you look into them? thanks!
| self.value = value | ||
|
|
||
|
|
||
| def _construct_from_wire(cls: type, data: typing.Any) -> typing.Any: |
There was a problem hiding this comment.
Python values stop being converted into JSON-friendly values
Imagine Parent contains a Child, and Child has a datetime field:
parent = Parent(child={
"timestamp": datetime(2026, 1, 1, tzinfo=timezone.utc)
})Previously, creating the child converted that datetime into its wire representation:
parent.as_dict()
# Before: {"child": {"timestamp": "2026-01-01T00:00:00Z"}}
# After: {"child": {"timestamp": datetime(...)}}Why: The PR routes nested model construction through the new “already serialized” path. That assumption is correct for a server’s JSON response, but not for this caller-provided dictionary.
Impact: json.dumps(parent.as_dict()) now raises an error because JSON cannot directly encode a datetime. The datetime attribute should still be a datetime; the problem is its representation in as_dict().
Requested fix: Distinguish response data from caller input, and keep serializing caller-provided Python values.
| return value | ||
|
|
||
|
|
||
| def _create_public_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typing.Any: |
There was a problem hiding this comment.
Changing your input dictionary unexpectedly changes the model
Consider a nested child with a list:
data = {"labels": ["a"]}
parent = Parent(child=data)
# Later, change the original input:
data["labels"].append("b")The resulting model differs:
parent.as_dict()
# Before: {"child": {"labels": ["a"]}}
# After: {"child": {"labels": ["a", "b"]}}Why: Previously, serialization created a separate list for the model. The new path stores the caller’s list directly, so both reference the same object.
The PR adds copying for positional construction:
Parent({"child": data}) # Protected by the new cloning helper
But not for these routes:
Parent(child=data) # Keyword construction
parent.child = data # Property assignmentImpact: Modifying input data can silently change a model you already constructed.
Requested fix: Preserve the previous separation from caller-owned containers on all public input paths. This is distinct from issue 1: copying a datetime does not serialize it, and copying containers solves a different problem.
| return type(obj)(map(builtin, obj)) | ||
| except (TypeError, ValueError): | ||
|
|
||
| def _lenient(entry: typing.Any) -> typing.Any: |
There was a problem hiding this comment.
XML numeric lists return XML objects instead of numbers
Suppose an XML model declares an unwrapped List[int] field:
<Numbers>
<value>1</value>
<value>2</value>
</Numbers>
The XML parser supplies a list of XML elements, not a list of strings or numbers.
Previously, the element deserializer extracted each element’s text and converted it:
int(element.text) # int("1") → 1The new numeric shortcut instead does:
int(element) # Fails: an XML element is not a numberIts fallback catches that failure and returns the original element.
model.values
# Before: [1, 2]
# After: [<Element 'value' ...>, <Element 'value' ...>]Requested fix: When the shortcut cannot convert an entry, use the existing element deserializer, which understands XML, rather than returning the entry unchanged.
5227b40 to
3a1483c
Compare
|
Hi @Kashif Khan (@kashifkhan). The PR will be closed since the PR has no update for 28 days. If this is still relevant please reopen. |
This PR focusses on improving the performance of the deserialization path of python generated files. I wanted to focus on a couple areas where speed ups could be done and see some results
On a ~5.6 MB DocumentIntelligence-shaped (why I started this) response on Python 3.10:
• Building the model tree: ~2.7× faster (~880 ms dropped to ~330 ms)
• Build + read every field: roughly halved (~2.0 s dropped to ~1.15 s)