1414
1515import sys
1616
17+ try :
18+ from functools import cache as _cache_package_versions
19+ except ImportError :
20+ from functools import wraps
21+ from threading import Lock
22+
23+ _package_version_cache = {}
24+ _package_version_cache_lock = Lock ()
25+
26+ def _cache_package_versions (wrapped ):
27+ """
28+ Threadsafe implementation of caching for _get_package_version.
29+
30+ Python 2.7 does not have the @functools.cache decorator, and
31+ must be reimplemented with support for clearing the cache.
32+ """
33+
34+ @wraps (wrapped )
35+ def _wrapper (name ):
36+ if name in _package_version_cache :
37+ return _package_version_cache [name ]
38+
39+ with _package_version_cache_lock :
40+ if name in _package_version_cache :
41+ return _package_version_cache [name ]
42+
43+ version = _package_version_cache [name ] = wrapped (name )
44+ return version
45+
46+ def cache_clear ():
47+ """Cache clear function to mimic @functools.cache"""
48+ with _package_version_cache_lock :
49+ _package_version_cache .clear ()
50+
51+ _wrapper .cache_clear = cache_clear
52+ return _wrapper
53+
54+
1755# Need to account for 4 possible variations of version declaration specified in (rejected) PEP 396
1856VERSION_ATTRS = ("__version__" , "version" , "__version_tuple__" , "version_tuple" ) # nosec
1957NULL_VERSIONS = frozenset ((None , "" , "0" , "0.0" , "0.0.0" , "0.0.0.0" , (0 ,), (0 , 0 ), (0 , 0 , 0 ), (0 , 0 , 0 , 0 ))) # nosec
@@ -67,6 +105,7 @@ def int_or_str(value):
67105 return version
68106
69107
108+ @_cache_package_versions
70109def _get_package_version (name ):
71110 module = sys .modules .get (name , None )
72111 version = None
@@ -75,7 +114,7 @@ def _get_package_version(name):
75114 if "importlib" in sys .modules and hasattr (sys .modules ["importlib" ], "metadata" ):
76115 try :
77116 # In Python3.10+ packages_distribution can be checked for as well
78- if hasattr (sys .modules ["importlib" ].metadata , "packages_distributions" ): # pylint: disable=E1101
117+ if hasattr (sys .modules ["importlib" ].metadata , "packages_distributions" ): # pylint: disable=E1101
79118 distributions = sys .modules ["importlib" ].metadata .packages_distributions () # pylint: disable=E1101
80119 distribution_name = distributions .get (name , name )
81120 else :
0 commit comments