-
Notifications
You must be signed in to change notification settings - Fork 278
perf: lazily calculate _key in Version
#989
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
77f5e26 to
b14e92f
Compare
|
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
|
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
|
Saves about 15% (total time) if |
|
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) |
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.