Skip to content

Commit

Permalink
Package Version Performance Regression (#970)
Browse files Browse the repository at this point in the history
* Fix package version performance regression

* Update tests/agent_unittests/test_package_version_utils.py

* Update tests/agent_unittests/test_package_version_utils.py

* Update tests/agent_unittests/test_package_version_utils.py

* Skip test in python 2

---------

Co-authored-by: Hannah Stepanek <hstepanek@newrelic.com>
  • Loading branch information
TimPansino and hmstepanek authored Nov 13, 2023
1 parent 72aa6e8 commit f939014
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 27 deletions.
32 changes: 18 additions & 14 deletions newrelic/common/package_version_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

import sys
import warnings

try:
from functools import cache as _cache_package_versions
Expand Down Expand Up @@ -110,6 +111,23 @@ def _get_package_version(name):
module = sys.modules.get(name, None)
version = None

with warnings.catch_warnings(record=True):
for attr in VERSION_ATTRS:
try:
version = getattr(module, attr, None)

# In certain cases like importlib_metadata.version, version is a callable
# function.
if callable(version):
continue

# 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:
pass

# importlib was introduced into the standard library starting in Python3.8.
if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"):
try:
Expand All @@ -126,20 +144,6 @@ def _get_package_version(name):
except Exception:
pass

for attr in VERSION_ATTRS:
try:
version = getattr(module, attr, None)
# In certain cases like importlib_metadata.version, version is a callable
# function.
if callable(version):
continue
# 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:
pass

if "pkg_resources" in sys.modules:
try:
version = sys.modules["pkg_resources"].get_distribution(name).version
Expand Down
41 changes: 28 additions & 13 deletions tests/agent_unittests/test_package_version_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
# limitations under the License.

import sys
import warnings

import pytest
import six
from testing_support.validators.validate_function_called import validate_function_called

from newrelic.common.package_version_utils import (
Expand Down Expand Up @@ -66,30 +68,26 @@ def cleared_package_version_cache():
("version_tuple", [3, 1, "0b2"], "3.1.0b2"),
),
)
def test_get_package_version(attr, value, expected_value):
def test_get_package_version(monkeypatch, attr, value, expected_value):
# There is no file/module here, so we monkeypatch
# pytest instead for our purposes
setattr(pytest, attr, value)
monkeypatch.setattr(pytest, attr, value, raising=False)
version = get_package_version("pytest")
assert version == expected_value
delattr(pytest, attr)


# This test only works on Python 3.7
@SKIP_IF_IMPORTLIB_METADATA
def test_skips_version_callables():
def test_skips_version_callables(monkeypatch):
# There is no file/module here, so we monkeypatch
# pytest instead for our purposes
setattr(pytest, "version", lambda x: "1.2.3.4")
setattr(pytest, "version_tuple", [3, 1, "0b2"])
monkeypatch.setattr(pytest, "version", lambda x: "1.2.3.4", raising=False)
monkeypatch.setattr(pytest, "version_tuple", [3, 1, "0b2"], raising=False)

version = get_package_version("pytest")

assert version == "3.1.0b2"

delattr(pytest, "version")
delattr(pytest, "version_tuple")


# This test only works on Python 3.7
@SKIP_IF_IMPORTLIB_METADATA
Expand All @@ -102,13 +100,12 @@ def test_skips_version_callables():
("version_tuple", [3, 1, "0b2"], (3, 1, "0b2")),
),
)
def test_get_package_version_tuple(attr, value, expected_value):
def test_get_package_version_tuple(monkeypatch, attr, value, expected_value):
# There is no file/module here, so we monkeypatch
# pytest instead for our purposes
setattr(pytest, attr, value)
monkeypatch.setattr(pytest, attr, value, raising=False)
version = get_package_version_tuple("pytest")
assert version == expected_value
delattr(pytest, attr)


@SKIP_IF_NOT_IMPORTLIB_METADATA
Expand All @@ -132,10 +129,28 @@ def test_pkg_resources_metadata():
assert version not in NULL_VERSIONS, version


def _getattr_deprecation_warning(attr):
if attr == "__version__":
warnings.warn("Testing deprecation warnings.", DeprecationWarning)
return "3.2.1"
else:
raise NotImplementedError()


@pytest.mark.skipif(six.PY2, reason="Can't add Deprecation in __version__ in Python 2.")
def test_deprecation_warning_suppression(monkeypatch, recwarn):
# Add fake module to be deleted later
monkeypatch.setattr(pytest, "__getattr__", _getattr_deprecation_warning, raising=False)

assert get_package_version("pytest") == "3.2.1"

assert not recwarn.list, "Warnings not suppressed."


def test_version_caching(monkeypatch):
# Add fake module to be deleted later
sys.modules["mymodule"] = sys.modules["pytest"]
setattr(pytest, "__version__", "1.0.0")
monkeypatch.setattr(pytest, "__version__", "1.0.0", raising=False)
version = get_package_version("mymodule")
assert version not in NULL_VERSIONS, version

Expand Down

0 comments on commit f939014

Please sign in to comment.