Repository navigation
fix: exclude pydantic 2.13, which is ~9x slower to construct - #178
Open
simontaurus wants to merge 3 commits into
Open
simontaurus wants to merge 3 commits into
simontaurus wants to merge 3 commits into
Conversation
- 2.13 calls inspect.signature on a private-attr factory twice per attribute per construction, uncached - LinkedBaseModel declares two, so every model costs ~38 us extra - measured 43.0 us on 2.13.4 against 4.9 us on 2.14.3 - 2.12 and earlier stay supported; only the 2.13 series is excluded
- absolute timings drift by tens of percent per run, the ratio holds within 3.38x to 3.53x - budget 8x, between the measured 3.4x and the 17.8x of pydantic 2.13 - imports the descriptor binding directly, so it runs under either value of OOLD_DESCRIPTOR_BINDING
Contributor
Release previewMerging this PR would release v2.3.1 (current: Changelog preview (truncated)## v2.3.1 (2026-11-15)
### Bug Fixes
- Exclude pydantic 2.13, which is ~9x slower to construct
([`48c405c`](https://github.com/OO-LD/oold-python/commit/48c405c00f90b2349de5198b5d355cb1e7cf3fde))
### Documentation
- **controller**: Name controller members for what they do
([`648018f`](https://github.com/OO-LD/oold-python/commit/648018fc480934b5b5185c6d0702860392e90c25))
### Testing
- Guard construction cost as a ratio of plain pydantic
([`084ca45`](https://github.com/OO-LD/oold-python/commit/084ca45820289c3228b53091106586189cc22fbc))
- Raise the construction budget to 15x
([`2a9f89b`](https://github.com/OO-LD/oold-python/commit/2a9f89b234842cead44dca827b3d8136d55bbc09))
Preview via python-semantic-release and conventional commits. |
Contributor
📊 Benchmark ResultsClick to see benchmark comparisonThreshold: 1.3x (30% slower triggers a regression warning) Note: Benchmarks are informational only and won't fail the build. 💡 Tip: Download the |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
- CI measures up to 9.0x healthy, against the 8.0x budget: three of 21 jobs failed on margin - the 2.13 regression adds a fixed ~40 us, so it reads 18.7x locally and higher on slower runners
Contributor
📊 Benchmark ResultsClick to see benchmark comparisonThreshold: 1.3x (30% slower triggers a regression warning) Note: Benchmarks are informational only and won't fail the build. 💡 Tip: Download the |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pydantic>=2.0.0becomespydantic>=2.0.0,!=2.13.*, plus a regression guard.pydantic 2.13 routes
ModelPrivateAttr.get_default()throughresolve_default_value, which callsinspect.signatureon the factory twice per private attribute per construction, uncached.LinkedBaseModeldeclares two factory-backed private attrs,_linksand_extra_iris(src/oold/model/_descriptor.py:1319,:1322), so the cost lands on every model in the stack. Fixed upstream in 2.14.Measured on one machine, same source, min of interleaved rounds:
PrivateAttr(default_factory=dict), bare BaseModelLinkedBaseModelconstructionProfile on 2.13.4:
init_private_attributesis 80.6% of construction cumtime, with 80000inspect.signaturecalls for 20000 constructions.Changes:
uv.lockregenerated, resolves to 2.14.3tests/test_construction_cost.pyasserts construction stays under 8x plain pydantic. Absolute timings on this machine drift by tens of percent between runs; the ratio holds within 3.38x to 3.53x. Measured 3.4x on 2.14 and 17.8x on 2.13, so the budget sits in the gap. Verified it fails on 2.13.4 (24.3x) and passes on 2.14.3OOLD_DESCRIPTOR_BINDING; the legacy binding constructs an order of magnitude slower by design and would need a threshold that measures nothing801 passed under the descriptor binding, 785 under
OOLD_DESCRIPTOR_BINDING=0. Thepydantic.v1shim is still present in 2.14, so the v1 binding is unaffected.Reworking
_linksand_extra_irisinto lazy dicts was the alternative. It buys 3.6x to 4.9x on real classes against 9x here, and it breaks__eq__atsrc/oold/model/_descriptor.py:1532, which compares__pydantic_private__directly: a fresh instance holdsNone, one that has been read holds{}, and the__iris__getter alone is enough to flip it.