Skip to content

Commit 23295c0

Browse files
rupeshjSFDCgithub-actions[bot]arvindbhamidipatijstvzvsbharath
authored
migrate entrypoints handling to standard library importlib.metadata (#3904)
Declaring a namespace package has gone through a few revisions. pkg_resources has a version that is heavily deprecated. pkgutil provides a python2/python3 compatible version that is also compatible with native python3 namespaces. https://packaging.python.org/en/latest/guides/packaging-namespace-packages/ pkg_resources is very very deprecated and importing or using it results in deprecation warnings. It's time to move off of it entirely. --------- Signed-off-by: Rupesh J <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Arvind Bhamidipati <[email protected]> Co-authored-by: James Estevez <[email protected]> Co-authored-by: Bharath Chadarajupalli <[email protected]>
1 parent 515a706 commit 23295c0

File tree

10 files changed

+46
-44
lines changed

10 files changed

+46
-44
lines changed

AUTHORS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ For example:
3939
* Chandler Anderson (zenibako)
4040
* Ben French (BenjaminFrench)
4141
* Rupert Barrow (rupertbarrow)
42+
* Rupesh J (rupeshjSFDC)

cumulusci/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import os
22
import sys
3+
from importlib.metadata import PackageNotFoundError, version
34

45
from simple_salesforce import api, bulk
56

6-
from cumulusci.__about__ import __version__
7-
8-
__import__("pkg_resources").declare_namespace("cumulusci")
9-
107
__location__ = os.path.dirname(os.path.realpath(__file__))
118

12-
__version__ = __version__
9+
try:
10+
__version__ = version("cumulusci")
11+
except PackageNotFoundError:
12+
__version__ = "unknown"
1313

1414
if sys.version_info < (3, 8): # pragma: no cover
1515
raise Exception("CumulusCI requires Python 3.8+.")

cumulusci/cli/robot.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import importlib.metadata
12
import sys
23

34
import click
4-
import pkg_resources
55
import sarge
66

77
from .runtime import pass_runtime
@@ -109,5 +109,8 @@ def _require_npm():
109109

110110
def _is_package_installed(package_name):
111111
"""Return True if the given package is installed"""
112-
# technique shamelessly stolen from https://stackoverflow.com/a/44210735/7432
113-
return package_name in {pkg.key for pkg in pkg_resources.working_set}
112+
try:
113+
importlib.metadata.distribution(package_name)
114+
return True
115+
except importlib.metadata.PackageNotFoundError:
116+
return False

cumulusci/cli/runtime.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import click
88
import keyring
9-
import pkg_resources
9+
from packaging import version
1010

1111
from cumulusci.cli.utils import get_installed_version
1212
from cumulusci.core.exceptions import ConfigError, KeychainKeyNotFound, OrgNotFound
@@ -143,7 +143,7 @@ def check_cumulusci_version(self):
143143
if self.project_config:
144144
min_cci_version = self.project_config.minimum_cumulusci_version
145145
if min_cci_version:
146-
parsed_version = pkg_resources.parse_version(min_cci_version)
146+
parsed_version = version.parse(min_cci_version)
147147
if get_installed_version() < parsed_version:
148148
raise click.UsageError(
149149
f"This project requires CumulusCI version {min_cci_version} or later. "

cumulusci/cli/tests/test_cci.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from unittest import mock
99

1010
import click
11-
import pkg_resources
1211
import pytest
12+
from packaging import version
1313
from requests.exceptions import ConnectionError
1414
from rich.console import Console
1515

@@ -402,7 +402,7 @@ def test_cli():
402402

403403
@mock.patch(
404404
"cumulusci.cli.cci.get_latest_final_version",
405-
mock.Mock(return_value=pkg_resources.parse_version("100")),
405+
mock.Mock(return_value=version.parse("100")),
406406
)
407407
def test_version(capsys):
408408
run_click_command(cci.version)
@@ -413,7 +413,7 @@ def test_version(capsys):
413413

414414
@mock.patch(
415415
"cumulusci.cli.cci.get_latest_final_version",
416-
mock.Mock(return_value=pkg_resources.parse_version("1")),
416+
mock.Mock(return_value=version.parse("1")),
417417
)
418418
def test_version__latest(capsys):
419419
run_click_command(cci.version)

cumulusci/cli/tests/test_robot.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,11 @@
99
from .utils import run_cli_command
1010

1111

12-
class MockWorkingSet(list):
13-
"""Mocks a pkg_resources.workingset object
12+
class MockDistribution:
13+
"""Mocks an importlib.metadata.Distribution object"""
1414

15-
Basically, a list of mock packages representing all of the packages
16-
installed in the current environment
17-
"""
18-
19-
def __init__(self, *package_names):
20-
super().__init__()
21-
for package_name in package_names:
22-
pkg = mock.Mock()
23-
pkg.key = package_name
24-
self.append(pkg)
15+
def __init__(self, name):
16+
self.name = name
2517

2618

2719
def mock_Command(returncodes={}):
@@ -49,16 +41,18 @@ def run():
4941
return the_real_mock
5042

5143

52-
@mock.patch("cumulusci.cli.robot.pkg_resources")
53-
def test_is_package_installed(pkg_resources):
44+
@mock.patch("cumulusci.cli.robot.importlib.metadata.distribution")
45+
def test_is_package_installed(mock_distribution):
5446
"""Verify that the helper _is_package_installed returns the correct result"""
55-
pkg_resources.working_set = MockWorkingSet(
56-
"robotframework", "robotframework-browser", "snowfakery"
57-
)
47+
# Test when package is installed
48+
mock_distribution.return_value = MockDistribution("robotframework-browser")
5849
result = _is_package_installed("robotframework-browser")
5950
assert result is True
6051

61-
pkg_resources.working_set = MockWorkingSet("robotframework", "snowfakery")
52+
# Test when package is not installed
53+
from importlib.metadata import PackageNotFoundError
54+
55+
mock_distribution.side_effect = PackageNotFoundError("Package not found")
6256
result = _is_package_installed("robotframework-browser")
6357
assert result is False
6458

cumulusci/cli/tests/test_utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import time
44
from unittest import mock
55

6-
import pkg_resources
76
import pytest
87
import requests
98
import responses
9+
from packaging import version
1010

1111
import cumulusci
1212

@@ -46,8 +46,8 @@ def test_get_latest_final_version():
4646
def test_check_latest_version(click, get_latest_final_version, get_installed_version):
4747
with utils.timestamp_file() as f:
4848
f.write(str(time.time() - 4000))
49-
get_latest_final_version.return_value = pkg_resources.parse_version("2")
50-
get_installed_version.return_value = pkg_resources.parse_version("1")
49+
get_latest_final_version.return_value = version.parse("2")
50+
get_installed_version.return_value = version.parse("1")
5151

5252
utils.check_latest_version()
5353
if sys.version_info > utils.LOWEST_SUPPORTED_VERSION:

cumulusci/cli/utils.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from collections import defaultdict
77

88
import click
9-
import pkg_resources
109
import requests
10+
from packaging import version as packaging_version
1111
from rich.console import Console
1212

1313
from cumulusci import __version__
@@ -74,7 +74,7 @@ def get_latest_final_version():
7474
for versionstring in res["releases"].keys():
7575
if not is_final_release(versionstring):
7676
continue
77-
versions.append(pkg_resources.parse_version(versionstring))
77+
versions.append(packaging_version.parse(versionstring))
7878
versions.sort(reverse=True)
7979
return versions[0]
8080

@@ -110,9 +110,14 @@ def check_latest_version():
110110
)
111111

112112

113-
def get_installed_version():
114-
"""returns the version name (e.g. 2.0.0b58) that is installed"""
115-
return pkg_resources.parse_version(__version__)
113+
def parse_version(versionstring: str) -> packaging_version.Version:
114+
"""Parse a version string into a Version object."""
115+
return packaging_version.parse(versionstring)
116+
117+
118+
def get_installed_version() -> packaging_version.Version:
119+
"""Get the installed version of CumulusCI."""
120+
return parse_version(__version__)
116121

117122

118123
def win32_long_paths_enabled() -> bool:

cumulusci/tasks/salesforce/update_profile.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
from collections import defaultdict
33

4-
import pkg_resources
4+
from packaging import version
55

66
from cumulusci.core.exceptions import CumulusCIException, TaskOptionsError
77
from cumulusci.core.utils import process_bool_arg, process_list_arg
@@ -69,10 +69,8 @@ def _init_options(self, kwargs):
6969
# not be using a custom `package.xml`
7070
min_cci_version = self.project_config.minimum_cumulusci_version
7171
if min_cci_version and "package_xml" not in self.options:
72-
parsed_version = pkg_resources.parse_version(min_cci_version)
73-
default_packages_arg = parsed_version >= pkg_resources.parse_version(
74-
"3.9.0"
75-
)
72+
parsed_version = version.parse(min_cci_version)
73+
default_packages_arg = parsed_version >= version.parse("3.9.0")
7674
else:
7775
default_packages_arg = False
7876

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dependencies = [
3434
"defusedxml",
3535
"lxml",
3636
"MarkupSafe",
37+
"packaging>=23.0",
3738
"psutil",
3839
"pydantic<2",
3940
"PyJWT",

0 commit comments

Comments
 (0)