From 8c9cb02bdbf338ba872428345ca66efd510bc8a7 Mon Sep 17 00:00:00 2001 From: David Zaslavsky Date: Fri, 3 May 2024 01:10:58 -0700 Subject: [PATCH] Replace most pkg_resources calls with importlib.metadata and packaging pkg_resources is deprecated and, under some circumstances, causes import errors in Python 3.12. This commit replaces the deprecated pkg_resources functions that access distribution metadata with calls to corresponding functions from importlib.metadata (or its backport importlib_metadata) and packaging. This change will bring pyinfra more in line with modern recommendations and should resolve the errors. The change introduces a new dependency on importlib_metadata in Python versions prior to 3.10. Even though importlib.metadata was available in Python 3.8, its entry_points() function did not have the group keyword argument until 3.10, hence this commit prefers the backport until that Python version. The only remaining use of pkg_resources is the require() function, which will take more work to replace since there's no ready equivalent in any of the importlib* or packaging modules. --- pyinfra/api/config.py | 11 ++++++----- pyinfra/api/connectors.py | 7 +++++-- pyinfra/version.py | 7 +++++-- setup.py | 3 +++ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/pyinfra/api/config.py b/pyinfra/api/config.py index e5ac27a03..887e3fdbe 100644 --- a/pyinfra/api/config.py +++ b/pyinfra/api/config.py @@ -1,8 +1,9 @@ from os import path from typing import Optional -# TODO: move to importlib.resources -from pkg_resources import Requirement, ResolutionError, parse_version, require +from pkg_resources import ResolutionError, require +from packaging.specifiers import SpecifierSet +from packaging.version import Version from pyinfra import __version__, state @@ -53,10 +54,10 @@ class ConfigDefaults: def check_pyinfra_version(version: str): if not version: return - running_version = parse_version(__version__) - required_versions = Requirement.parse("pyinfra{0}".format(version)) + running_version = Version(__version__) + required_versions = SpecifierSet(version) - if running_version not in required_versions: # type: ignore[operator] + if running_version not in required_versions: raise PyinfraError( f"pyinfra version requirement not met (requires {version}, running {__version__})" ) diff --git a/pyinfra/api/connectors.py b/pyinfra/api/connectors.py index ad007f50e..d32d17a31 100644 --- a/pyinfra/api/connectors.py +++ b/pyinfra/api/connectors.py @@ -1,4 +1,7 @@ -import pkg_resources +try: + from importlib_metadata import entry_points +except ImportError: + from importlib.metadata import entry_points def _load_connector(entrypoint): @@ -8,7 +11,7 @@ def _load_connector(entrypoint): def get_all_connectors(): return { entrypoint.name: _load_connector(entrypoint) - for entrypoint in pkg_resources.iter_entry_points("pyinfra.connectors") + for entrypoint in entry_points(group="pyinfra.connectors") } diff --git a/pyinfra/version.py b/pyinfra/version.py index 1db93c667..be1eeb478 100644 --- a/pyinfra/version.py +++ b/pyinfra/version.py @@ -1,6 +1,9 @@ try: - from pkg_resources import get_distribution + try: + from importlib_metadata import version + except ImportError: + from importlib.metadata import version - __version__ = get_distribution("pyinfra").version + __version__ = version("pyinfra") except Exception: __version__ = "unknown" diff --git a/setup.py b/setup.py index b8ca7733c..204c6be13 100644 --- a/setup.py +++ b/setup.py @@ -29,10 +29,13 @@ "pywinrm", "typeguard", "distro>=1.6,<2", + "packaging>=16.1", # Backport of graphlib used for DAG operation ordering 'graphlib_backport ; python_version < "3.9"', # Backport of typing for Unpack (added 3.11) 'typing-extensions ; python_version < "3.11"', + # Backport of importlib.metadata for entry_points(group=...) (added 3.10) + 'importlib_metadata>=3.6 ; python_version < "3.10"', ) TEST_REQUIRES = (