Skip to content

Commit 7ae0c94

Browse files
lrafeeiTimPansinohmstepanekumaannamalai
authored
Fix package_version_utils.py logic (#689)
* Fix package_version_utils.py logic Co-authored-by: Timothy Pansino <[email protected]> Co-authored-by: Hannah Stepanek <[email protected]> Co-authored-by: Uma Annamalai <[email protected]> * Move description of func into func itself * typecast lists into tuples * Remove breakpoints * Empty _test_package_version_utils.py * Make changes to the test Co-authored-by: Timothy Pansino <[email protected]> Co-authored-by: Hannah Stepanek <[email protected]> Co-authored-by: Uma Annamalai <[email protected]>
1 parent 1bb2be4 commit 7ae0c94

File tree

2 files changed

+123
-5
lines changed

2 files changed

+123
-5
lines changed

newrelic/common/package_version_utils.py

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,57 @@
1414

1515
import sys
1616

17+
# Need to account for 4 possible variations of version declaration specified in (rejected) PEP 396
18+
VERSION_ATTRS = ("__version__", "version", "__version_tuple__", "version_tuple") # nosec
19+
NULL_VERSIONS = frozenset((None, "", "0", "0.0", "0.0.0", "0.0.0.0", (0,), (0, 0), (0, 0, 0), (0, 0, 0, 0))) # nosec
20+
1721

1822
def get_package_version(name):
19-
# importlib was introduced into the standard library starting in Python3.8.
20-
if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"):
21-
return sys.modules["importlib"].metadata.version(name) # pylint: disable=E1101
22-
elif "pkg_resources" in sys.modules:
23-
return sys.modules["pkg_resources"].get_distribution(name).version
23+
"""Gets the version of the library.
24+
:param name: The name of library.
25+
:type name: str
26+
:return: The version of the library. Returns None if can't determine version.
27+
:type return: str or None
28+
29+
Usage::
30+
>>> get_package_version("botocore")
31+
"1.1.0"
32+
"""
33+
34+
def _get_package_version(name):
35+
module = sys.modules.get(name, None)
36+
version = None
37+
for attr in VERSION_ATTRS:
38+
try:
39+
version = getattr(module, attr, None)
40+
# Cast any version specified as a list into a tuple.
41+
version = tuple(version) if isinstance(version, list) else version
42+
if version not in NULL_VERSIONS:
43+
return version
44+
except Exception:
45+
pass
46+
47+
# importlib was introduced into the standard library starting in Python3.8.
48+
if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"):
49+
try:
50+
version = sys.modules["importlib"].metadata.version(name) # pylint: disable=E1101
51+
if version not in NULL_VERSIONS:
52+
return version
53+
except Exception:
54+
pass
55+
56+
if "pkg_resources" in sys.modules:
57+
try:
58+
version = sys.modules["pkg_resources"].get_distribution(name).version
59+
if version not in NULL_VERSIONS:
60+
return version
61+
except Exception:
62+
pass
63+
64+
version = _get_package_version(name)
65+
66+
# Coerce iterables into a string
67+
if isinstance(version, tuple):
68+
version = ".".join(str(v) for v in version)
69+
70+
return version
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright 2010 New Relic, Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import sys
16+
17+
import pytest
18+
from testing_support.validators.validate_function_called import validate_function_called
19+
20+
from newrelic.common.package_version_utils import (
21+
NULL_VERSIONS,
22+
VERSION_ATTRS,
23+
get_package_version,
24+
)
25+
26+
IS_PY38_PLUS = sys.version_info[:2] >= (3, 8)
27+
SKIP_IF_NOT_IMPORTLIB_METADATA = pytest.mark.skipif(not IS_PY38_PLUS, reason="importlib.metadata is not supported.")
28+
SKIP_IF_IMPORTLIB_METADATA = pytest.mark.skipif(
29+
IS_PY38_PLUS, reason="importlib.metadata is preferred over pkg_resources."
30+
)
31+
32+
33+
@pytest.fixture(scope="function", autouse=True)
34+
def patched_pytest_module(monkeypatch):
35+
for attr in VERSION_ATTRS:
36+
if hasattr(pytest, attr):
37+
monkeypatch.delattr(pytest, attr)
38+
39+
yield pytest
40+
41+
42+
@pytest.mark.parametrize(
43+
"attr,value,expected_value",
44+
(
45+
("version", "1.2.3.4", "1.2.3.4"),
46+
("__version__", "1.3.5rc2", "1.3.5rc2"),
47+
("__version_tuple__", (3, 5, 8), "3.5.8"),
48+
("version_tuple", [3, 1, "0b2"], "3.1.0b2"),
49+
),
50+
)
51+
def test_get_package_version(attr, value, expected_value):
52+
# There is no file/module here, so we monkeypatch
53+
# pytest instead for our purposes
54+
setattr(pytest, attr, value)
55+
version = get_package_version("pytest")
56+
assert version == expected_value
57+
delattr(pytest, attr)
58+
59+
60+
@SKIP_IF_NOT_IMPORTLIB_METADATA
61+
@validate_function_called("importlib.metadata", "version")
62+
def test_importlib_metadata():
63+
version = get_package_version("pytest")
64+
assert version not in NULL_VERSIONS, version
65+
66+
67+
@SKIP_IF_IMPORTLIB_METADATA
68+
@validate_function_called("pkg_resources", "get_distribution")
69+
def test_pkg_resources_metadata():
70+
version = get_package_version("pytest")
71+
assert version not in NULL_VERSIONS, version

0 commit comments

Comments
 (0)