Skip to content

Commit 956ed04

Browse files
committed
Remove pkg_resources usage in direct_url_helper
1 parent 6f1c5dc commit 956ed04

File tree

6 files changed

+86
-85
lines changed

6 files changed

+86
-85
lines changed

src/pip/_internal/metadata/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
from .base import BaseDistribution, BaseEnvironment
44

5+
__all__ = [
6+
"BaseDistribution",
7+
"BaseEnvironment",
8+
"get_default_environment",
9+
"get_environment",
10+
"get_wheel_distribution",
11+
]
12+
513

614
def get_default_environment() -> BaseEnvironment:
715
"""Get the default representation for the current environment.

src/pip/_internal/metadata/base.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import email.message
2+
import json
23
import logging
34
import re
45
from typing import (
@@ -15,7 +16,11 @@
1516
from pip._vendor.packaging.requirements import Requirement
1617
from pip._vendor.packaging.version import LegacyVersion, Version
1718

18-
from pip._internal.models.direct_url import DirectUrl
19+
from pip._internal.models.direct_url import (
20+
DIRECT_URL_METADATA_NAME,
21+
DirectUrl,
22+
DirectUrlValidationError,
23+
)
1924
from pip._internal.utils.misc import stdlib_pkgs # TODO: Move definition here.
2025

2126
if TYPE_CHECKING:
@@ -63,7 +68,29 @@ def version(self) -> DistributionVersion:
6368

6469
@property
6570
def direct_url(self) -> Optional[DirectUrl]:
66-
raise NotImplementedError()
71+
"""Obtain a DirectUrl from this distribution.
72+
73+
Returns None if the distribution has no `direct_url.json` metadata,
74+
or if `direct_url.json` is invalid.
75+
"""
76+
try:
77+
content = self.read_text(DIRECT_URL_METADATA_NAME)
78+
except FileNotFoundError:
79+
return None
80+
try:
81+
return DirectUrl.from_json(content)
82+
except (
83+
UnicodeDecodeError,
84+
json.JSONDecodeError,
85+
DirectUrlValidationError,
86+
) as e:
87+
logger.warning(
88+
"Error parsing %s for %s: %s",
89+
DIRECT_URL_METADATA_NAME,
90+
self.canonical_name,
91+
e,
92+
)
93+
return None
6794

6895
@property
6996
def installer(self) -> str:
@@ -82,7 +109,11 @@ def in_usersite(self) -> bool:
82109
raise NotImplementedError()
83110

84111
def read_text(self, name: str) -> str:
85-
"""Read a file in the .dist-info (or .egg-info) directory."""
112+
"""Read a file in the .dist-info (or .egg-info) directory.
113+
114+
Should raise ``FileNotFoundError`` if ``name`` does not exist in the
115+
metadata directory.
116+
"""
86117
raise NotImplementedError()
87118

88119
def iter_entry_points(self) -> Iterable[BaseEntryPoint]:

src/pip/_internal/metadata/pkg_resources.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88
from pip._vendor.packaging.utils import canonicalize_name
99
from pip._vendor.packaging.version import parse as parse_version
1010

11-
from pip._internal.models.direct_url import DirectUrl
1211
from pip._internal.utils import misc # TODO: Move definition here.
13-
from pip._internal.utils.direct_url_helpers import dist_get_direct_url
1412
from pip._internal.utils.packaging import get_installer, get_metadata
1513
from pip._internal.utils.wheel import pkg_resources_distribution_for_wheel
1614

@@ -47,10 +45,6 @@ def canonical_name(self) -> str:
4745
def version(self) -> DistributionVersion:
4846
return parse_version(self._dist.version)
4947

50-
@property
51-
def direct_url(self) -> Optional[DirectUrl]:
52-
return dist_get_direct_url(self._dist)
53-
5448
@property
5549
def installer(self) -> str:
5650
return get_installer(self._dist)

src/pip/_internal/utils/direct_url_helpers.py

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
1-
import json
2-
import logging
31
from typing import Optional
42

5-
from pip._vendor.pkg_resources import Distribution
6-
7-
from pip._internal.models.direct_url import (
8-
DIRECT_URL_METADATA_NAME,
9-
ArchiveInfo,
10-
DirectUrl,
11-
DirectUrlValidationError,
12-
DirInfo,
13-
VcsInfo,
14-
)
3+
from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo
154
from pip._internal.models.link import Link
165
from pip._internal.vcs import vcs
176

18-
logger = logging.getLogger(__name__)
19-
207

218
def direct_url_as_pep440_direct_reference(direct_url, name):
229
# type: (DirectUrl, str) -> str
@@ -90,28 +77,3 @@ def direct_url_from_link(link, source_dir=None, link_is_in_wheel_cache=False):
9077
info=ArchiveInfo(hash=hash),
9178
subdirectory=link.subdirectory_fragment,
9279
)
93-
94-
95-
def dist_get_direct_url(dist):
96-
# type: (Distribution) -> Optional[DirectUrl]
97-
"""Obtain a DirectUrl from a pkg_resource.Distribution.
98-
99-
Returns None if the distribution has no `direct_url.json` metadata,
100-
or if `direct_url.json` is invalid.
101-
"""
102-
if not dist.has_metadata(DIRECT_URL_METADATA_NAME):
103-
return None
104-
try:
105-
return DirectUrl.from_json(dist.get_metadata(DIRECT_URL_METADATA_NAME))
106-
except (
107-
DirectUrlValidationError,
108-
json.JSONDecodeError,
109-
UnicodeDecodeError,
110-
) as e:
111-
logger.warning(
112-
"Error parsing %s for %s: %s",
113-
DIRECT_URL_METADATA_NAME,
114-
dist.project_name,
115-
e,
116-
)
117-
return None

tests/unit/test_direct_url_helpers.py

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
from functools import partial
2-
from unittest.mock import MagicMock, patch
3-
4-
from pip._internal.models.direct_url import (
5-
DIRECT_URL_METADATA_NAME,
6-
ArchiveInfo,
7-
DirectUrl,
8-
DirInfo,
9-
VcsInfo,
10-
)
2+
from unittest.mock import patch
3+
4+
from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo
115
from pip._internal.models.link import Link
126
from pip._internal.utils.direct_url_helpers import (
137
direct_url_as_pep440_direct_reference,
148
direct_url_from_link,
15-
dist_get_direct_url,
169
)
1710
from pip._internal.utils.urls import path_to_url
1811

@@ -181,30 +174,3 @@ def test_from_link_hide_user_password():
181174
link_is_in_wheel_cache=True,
182175
)
183176
assert direct_url.to_dict()["url"] == "ssh://[email protected]/u/p.git"
184-
185-
186-
def test_dist_get_direct_url_no_metadata():
187-
dist = MagicMock()
188-
dist.has_metadata.return_value = False
189-
assert dist_get_direct_url(dist) is None
190-
dist.has_metadata.assert_called()
191-
192-
193-
def test_dist_get_direct_url_bad_metadata():
194-
dist = MagicMock()
195-
dist.has_metadata.return_value = True
196-
dist.get_metadata.return_value = "{}" # invalid direct_url.json
197-
assert dist_get_direct_url(dist) is None
198-
dist.get_metadata.assert_called_with(DIRECT_URL_METADATA_NAME)
199-
200-
201-
def test_dist_get_direct_url_valid_metadata():
202-
dist = MagicMock()
203-
dist.has_metadata.return_value = True
204-
dist.get_metadata.return_value = (
205-
'{"url": "https://e.c/p.tgz", "archive_info": {}}'
206-
)
207-
direct_url = dist_get_direct_url(dist)
208-
dist.get_metadata.assert_called_with(DIRECT_URL_METADATA_NAME)
209-
assert direct_url.url == "https://e.c/p.tgz"
210-
assert isinstance(direct_url.info, ArchiveInfo)

tests/unit/test_metadata.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import logging
2+
from unittest.mock import patch
3+
4+
from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, ArchiveInfo
5+
from pip._internal.metadata import BaseDistribution
6+
7+
8+
@patch.object(BaseDistribution, "read_text", side_effect=FileNotFoundError)
9+
def test_dist_get_direct_url_no_metadata(mock_read_text):
10+
dist = BaseDistribution()
11+
assert dist.direct_url is None
12+
mock_read_text.assert_called_once_with(DIRECT_URL_METADATA_NAME)
13+
14+
15+
@patch.object(BaseDistribution, "read_text", return_value="{}")
16+
def test_dist_get_direct_url_invalid_json(mock_read_text, caplog):
17+
class FakeDistribution(BaseDistribution):
18+
canonical_name = "whatever" # Needed for error logging.
19+
20+
dist = FakeDistribution()
21+
with caplog.at_level(logging.WARNING):
22+
assert dist.direct_url is None
23+
24+
mock_read_text.assert_called_once_with(DIRECT_URL_METADATA_NAME)
25+
assert caplog.records[-1].getMessage().startswith(
26+
"Error parsing direct_url.json for whatever:",
27+
)
28+
29+
30+
@patch.object(
31+
BaseDistribution,
32+
"read_text",
33+
return_value='{"url": "https://e.c/p.tgz", "archive_info": {}}',
34+
)
35+
def test_dist_get_direct_url_valid_metadata(mock_read_text):
36+
dist = BaseDistribution()
37+
direct_url = dist.direct_url
38+
mock_read_text.assert_called_once_with(DIRECT_URL_METADATA_NAME)
39+
assert direct_url.url == "https://e.c/p.tgz"
40+
assert isinstance(direct_url.info, ArchiveInfo)

0 commit comments

Comments
 (0)