perf: lazily calculate _key in Version#989
Merged
Conversation
77f5e26 to
b14e92f
Compare
henryiii
reviewed
Nov 25, 2025
Contributor
|
There's also diff --git a/src/packaging/version.py b/src/packaging/version.py
index 5261cd5..319ce66 100644
--- a/src/packaging/version.py
+++ b/src/packaging/version.py
@@ -9,6 +9,7 @@
from __future__ import annotations
+import functools
import re
from typing import Any, Callable, NamedTuple, SupportsInt, Tuple, Union
@@ -185,7 +186,6 @@ class Version(_BaseVersion):
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
_version: _Version
- __key: CmpKey | None
def __init__(self, version: str) -> None:
"""Initialize a Version object.
@@ -216,18 +216,16 @@ class Version(_BaseVersion):
# Key which will be used for sorting
self.__key = None
- @property
+ @functools.cached_property
def _key(self) -> CmpKey:
- if self.__key is None:
- self.__key = _cmpkey(
- self._version.epoch,
- self._version.release,
- self._version.pre,
- self._version.post,
- self._version.dev,
- self._version.local,
- )
- return self.__key
+ return _cmpkey(
+ self._version.epoch,
+ self._version.release,
+ self._version.pre,
+ self._version.post,
+ self._version.dev,
+ self._version.local,
+ )
def __repr__(self) -> str:
"""A representation of the Version that shows all internal state. |
_key in Version_key in Version
Member
Author
|
I took a look at cached property and there are too many nuances, dependencies, and behavior changes for me to be comfortable using it: https://docs.python.org/3/library/functools.html#functools.cached_property |
9ca65d6 to
aa834e7
Compare
henryiii
approved these changes
Nov 26, 2025
Contributor
|
Saves about 15% (total time) if |
Contributor
|
After this and all the recent optimizations (regex, etc), loading every version from PyPI takes 82.4 seconds. On 25.0, it takes 116.1 seconds. (Python 3.14) |
1 task
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.<
2FA6
span class="js-validation-on-left-blob">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.
Inspired by #987 I investigated if calling
_cmpkeyto calculate_keyon everyVersioninitialization made sense.At least for pip,
_keyis only accessed on less than 30% ofVersionobjects ever created, with longer resolutions going down to below 20%, so precalculating_keywith_cmpkeyis doing work that's often not needed._versioncould also be made lazy, but at least for pip_versionis accessed on 100% of allVersionobjects.