From 2712443fb262eb43058c7588c3389cf7827e5dfe Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Thu, 10 Nov 2022 18:37:05 -0800 Subject: [PATCH 1/6] Fix package_version_utils.py logic Co-authored-by: Timothy Pansino Co-authored-by: Hannah Stepanek Co-authored-by: Uma Annamalai --- newrelic/common/package_version_utils.py | 50 +++++++++++-- .../_test_package_version_utils.py | 14 ++++ .../test_package_version_utils.py | 72 +++++++++++++++++++ 3 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 tests/agent_unittests/_test_package_version_utils.py create mode 100644 tests/agent_unittests/test_package_version_utils.py diff --git a/newrelic/common/package_version_utils.py b/newrelic/common/package_version_utils.py index c881d7ada..ef39a1e27 100644 --- a/newrelic/common/package_version_utils.py +++ b/newrelic/common/package_version_utils.py @@ -12,12 +12,52 @@ # See the License for the specific language governing permissions and # limitations under the License. +# To use: +# Input: name of library as a string. +# example: +# get_package_version("botocore") +# Output: version of library as a string + import sys +# Need to account for 4 possible variations of version declaration specified in (rejected) PEP 396 +VERSION_ATTRS = ("__version__", "version", "__version_tuple__", "version_tuple") # nosec +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 + def get_package_version(name): - # importlib was introduced into the standard library starting in Python3.8. - if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"): - return sys.modules["importlib"].metadata.version(name) # pylint: disable=E1101 - elif "pkg_resources" in sys.modules: - return sys.modules["pkg_resources"].get_distribution(name).version + def _get_package_version(name): + module = sys.modules.get(name, None) + version = None + for attr in VERSION_ATTRS: + try: + version = getattr(module, attr, None) + if version not in NULL_VERSIONS: + return version + except Exception: + pass + + # importlib was introduced into the standard library starting in Python3.8. + if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"): + try: + version = sys.modules["importlib"].metadata.version(name) # pylint: disable=E1101 + if version not in NULL_VERSIONS: + return version + except Exception: + pass + + if "pkg_resources" in sys.modules: + try: + version = sys.modules["pkg_resources"].get_distribution(name).version + if version not in NULL_VERSIONS: + return version + except Exception: + pass + + version = _get_package_version(name) + + # Coerce iterables into a string + if isinstance(version, (tuple, list)): + version = ".".join(str(v) for v in version) + + return version diff --git a/tests/agent_unittests/_test_package_version_utils.py b/tests/agent_unittests/_test_package_version_utils.py new file mode 100644 index 000000000..05c649bf3 --- /dev/null +++ b/tests/agent_unittests/_test_package_version_utils.py @@ -0,0 +1,14 @@ +def test___version__(): + pass + + +def test__version(): + pass + + +def test___version(): + pass + + +def module(): + pass diff --git a/tests/agent_unittests/test_package_version_utils.py b/tests/agent_unittests/test_package_version_utils.py new file mode 100644 index 000000000..2dfb348ec --- /dev/null +++ b/tests/agent_unittests/test_package_version_utils.py @@ -0,0 +1,72 @@ +# Copyright 2010 New Relic, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +# Empty module for running tests against +import _test_package_version_utils as module +import pytest +from testing_support.validators.validate_function_called import validate_function_called + +from newrelic.common.package_version_utils import ( + NULL_VERSIONS, + VERSION_ATTRS, + get_package_version, +) + +IS_PY38_PLUS = sys.version_info[:2] >= (3, 8) +SKIP_IF_NOT_IMPORTLIB_METADATA = pytest.mark.skipif(not IS_PY38_PLUS, reason="importlib.metadata is not supported.") +SKIP_IF_IMPORTLIB_METADATA = pytest.mark.skipif( + IS_PY38_PLUS, reason="importlib.metadata is preferred over pkg_resources." +) + + +@pytest.fixture(scope="function", autouse=True) +def patched_pytest_module(monkeypatch): + for attr in VERSION_ATTRS: + if hasattr(pytest, attr): + monkeypatch.delattr(pytest, attr) + + yield pytest + + +@pytest.mark.parametrize( + "attr,value,expected_value", + ( + ("version", "1.2.3.4", "1.2.3.4"), + ("__version__", "1.3.5rc2", "1.3.5rc2"), + ("__version_tuple__", (3, 5, 8), "3.5.8"), + ("version_tuple", (3, 1, "0b2"), "3.1.0b2"), + ), +) +def test_get_package_version(attr, value, expected_value): + setattr(module, attr, value) + version = get_package_version(module.__name__) + delattr(module, attr) + + assert version == expected_value + + +@SKIP_IF_NOT_IMPORTLIB_METADATA +@validate_function_called("importlib.metadata", "version") +def test_importlib_metadata(): + version = get_package_version("pytest") + assert version not in NULL_VERSIONS, version + + +@SKIP_IF_IMPORTLIB_METADATA +@validate_function_called("pkg_resources", "get_distribution") +def test_pkg_resources_metadata(): + version = get_package_version("pytest") + assert version not in NULL_VERSIONS, version From 4ffaf984629049719d90343a475bd1dbb08508cb Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Mon, 14 Nov 2022 11:31:49 -0800 Subject: [PATCH 2/6] Move description of func into func itself --- newrelic/common/package_version_utils.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/newrelic/common/package_version_utils.py b/newrelic/common/package_version_utils.py index ef39a1e27..0e5392fa6 100644 --- a/newrelic/common/package_version_utils.py +++ b/newrelic/common/package_version_utils.py @@ -12,12 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -# To use: -# Input: name of library as a string. -# example: -# get_package_version("botocore") -# Output: version of library as a string - import sys # Need to account for 4 possible variations of version declaration specified in (rejected) PEP 396 @@ -26,6 +20,17 @@ def get_package_version(name): + """Gets the version of the library. + :param name: The name of library. + :type name: str + :return: The version of the library. Returns None if can't determine version. + :type return: str or None + + Usage:: + >>> get_package_version("botocore") + "1.1.0" + """ + def _get_package_version(name): module = sys.modules.get(name, None) version = None From e7591eada18e689164f42cdb2f3cf6f6226cb88d Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Mon, 14 Nov 2022 13:29:19 -0800 Subject: [PATCH 3/6] typecast lists into tuples --- newrelic/common/package_version_utils.py | 10 ++++++++++ tests/agent_unittests/test_package_version_utils.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/newrelic/common/package_version_utils.py b/newrelic/common/package_version_utils.py index 0e5392fa6..fce27d315 100644 --- a/newrelic/common/package_version_utils.py +++ b/newrelic/common/package_version_utils.py @@ -37,7 +37,14 @@ def _get_package_version(name): for attr in VERSION_ATTRS: try: version = getattr(module, attr, None) + version = tuple(version) if version else None # cast potential lists into tuples + import pdb + + pdb.set_trace() if version not in NULL_VERSIONS: + import pdb + + pdb.set_trace() return version except Exception: pass @@ -61,6 +68,9 @@ def _get_package_version(name): version = _get_package_version(name) + import pdb + + pdb.set_trace() # Coerce iterables into a string if isinstance(version, (tuple, list)): version = ".".join(str(v) for v in version) diff --git a/tests/agent_unittests/test_package_version_utils.py b/tests/agent_unittests/test_package_version_utils.py index 2dfb348ec..7ce76e44d 100644 --- a/tests/agent_unittests/test_package_version_utils.py +++ b/tests/agent_unittests/test_package_version_utils.py @@ -47,7 +47,7 @@ def patched_pytest_module(monkeypatch): ("version", "1.2.3.4", "1.2.3.4"), ("__version__", "1.3.5rc2", "1.3.5rc2"), ("__version_tuple__", (3, 5, 8), "3.5.8"), - ("version_tuple", (3, 1, "0b2"), "3.1.0b2"), + ("version_tuple", [3, 1, "0b2"], "3.1.0b2"), ), ) def test_get_package_version(attr, value, expected_value): From 111976d389d7267a365f1b289a01d384b5304e42 Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Mon, 14 Nov 2022 13:34:34 -0800 Subject: [PATCH 4/6] Remove breakpoints --- newrelic/common/package_version_utils.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/newrelic/common/package_version_utils.py b/newrelic/common/package_version_utils.py index fce27d315..285f4e5bf 100644 --- a/newrelic/common/package_version_utils.py +++ b/newrelic/common/package_version_utils.py @@ -37,14 +37,8 @@ def _get_package_version(name): for attr in VERSION_ATTRS: try: version = getattr(module, attr, None) - version = tuple(version) if version else None # cast potential lists into tuples - import pdb - - pdb.set_trace() + version = tuple(version) if isinstance(version, list) else version # cast potential lists into tuples if version not in NULL_VERSIONS: - import pdb - - pdb.set_trace() return version except Exception: pass @@ -68,9 +62,6 @@ def _get_package_version(name): version = _get_package_version(name) - import pdb - - pdb.set_trace() # Coerce iterables into a string if isinstance(version, (tuple, list)): version = ".".join(str(v) for v in version) From dc6d34fd82f2651fc8d10790b75ff08e4c922ba7 Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Mon, 14 Nov 2022 14:33:45 -0800 Subject: [PATCH 5/6] Empty _test_package_version_utils.py --- .../_test_package_version_utils.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/agent_unittests/_test_package_version_utils.py b/tests/agent_unittests/_test_package_version_utils.py index 05c649bf3..8030baccf 100644 --- a/tests/agent_unittests/_test_package_version_utils.py +++ b/tests/agent_unittests/_test_package_version_utils.py @@ -1,14 +1,13 @@ -def test___version__(): - pass - - -def test__version(): - pass - - -def test___version(): - pass - - -def module(): - pass +# Copyright 2010 New Relic, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. From 2bf700d5f37ee82bd74e68e1f2551d5fcde2cb30 Mon Sep 17 00:00:00 2001 From: Lalleh Rafeei Date: Tue, 15 Nov 2022 17:31:10 -0800 Subject: [PATCH 6/6] Make changes to the test --- newrelic/common/package_version_utils.py | 5 +++-- .../agent_unittests/_test_package_version_utils.py | 13 ------------- tests/agent_unittests/test_package_version_utils.py | 11 +++++------ 3 files changed, 8 insertions(+), 21 deletions(-) delete mode 100644 tests/agent_unittests/_test_package_version_utils.py diff --git a/newrelic/common/package_version_utils.py b/newrelic/common/package_version_utils.py index 285f4e5bf..a785f7ab1 100644 --- a/newrelic/common/package_version_utils.py +++ b/newrelic/common/package_version_utils.py @@ -37,7 +37,8 @@ def _get_package_version(name): for attr in VERSION_ATTRS: try: version = getattr(module, attr, None) - version = tuple(version) if isinstance(version, list) else version # cast potential lists into tuples + # Cast any version specified as a list into a tuple. + version = tuple(version) if isinstance(version, list) else version if version not in NULL_VERSIONS: return version except Exception: @@ -63,7 +64,7 @@ def _get_package_version(name): version = _get_package_version(name) # Coerce iterables into a string - if isinstance(version, (tuple, list)): + if isinstance(version, tuple): version = ".".join(str(v) for v in version) return version diff --git a/tests/agent_unittests/_test_package_version_utils.py b/tests/agent_unittests/_test_package_version_utils.py deleted file mode 100644 index 8030baccf..000000000 --- a/tests/agent_unittests/_test_package_version_utils.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2010 New Relic, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/tests/agent_unittests/test_package_version_utils.py b/tests/agent_unittests/test_package_version_utils.py index 7ce76e44d..14ee454fd 100644 --- a/tests/agent_unittests/test_package_version_utils.py +++ b/tests/agent_unittests/test_package_version_utils.py @@ -14,8 +14,6 @@ import sys -# Empty module for running tests against -import _test_package_version_utils as module import pytest from testing_support.validators.validate_function_called import validate_function_called @@ -51,11 +49,12 @@ def patched_pytest_module(monkeypatch): ), ) def test_get_package_version(attr, value, expected_value): - setattr(module, attr, value) - version = get_package_version(module.__name__) - delattr(module, attr) - + # There is no file/module here, so we monkeypatch + # pytest instead for our purposes + setattr(pytest, attr, value) + version = get_package_version("pytest") assert version == expected_value + delattr(pytest, attr) @SKIP_IF_NOT_IMPORTLIB_METADATA