Skip to content

Commit a7cc7f0

Browse files
committed
Avoid the deprecated JSON API
1 parent 7bb0a87 commit a7cc7f0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3695
-6767
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
from typing import Any
5+
6+
from poetry.core.packages.utils.link import Link
7+
8+
from poetry.repositories.link_sources.base import LinkSource
9+
10+
11+
if TYPE_CHECKING:
12+
from collections.abc import Iterator
13+
14+
15+
class SimpleJsonPage(LinkSource):
16+
"""Links as returned by PEP 691 compatible JSON-based Simple API."""
17+
18+
def __init__(self, url: str, content: dict[str, Any]) -> None:
19+
super().__init__(url=url)
20+
self.content = content
21+
22+
@property
23+
def links(self) -> Iterator[Link]:
24+
for file in self.content["files"]:
25+
url = file["url"]
26+
requires_python = file.get("requires-python")
27+
link = Link(url, requires_python=requires_python)
28+
if link.ext not in self.SUPPORTED_FORMATS:
29+
continue
30+
31+
yield link

src/poetry/repositories/pypi_repository.py

+33-30
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
from html5lib.html5parser import parse
1313
from poetry.core.packages.package import Package
1414
from poetry.core.packages.utils.link import Link
15-
from poetry.core.semver.version import Version
1615
from poetry.core.version.exceptions import InvalidVersion
1716

1817
from poetry.repositories.exceptions import PackageNotFound
1918
from poetry.repositories.http import HTTPRepository
19+
from poetry.repositories.link_sources.json import SimpleJsonPage
2020
from poetry.utils._compat import to_str
2121
from poetry.utils.constants import REQUESTS_TIMEOUT
2222

@@ -27,6 +27,7 @@
2727

2828

2929
if TYPE_CHECKING:
30+
from poetry.core.semver.version import Version
3031
from poetry.core.semver.version_constraint import VersionConstraint
3132

3233

@@ -110,47 +111,40 @@ def _find_packages(self, name: str, constraint: VersionConstraint) -> list[Packa
110111
Find packages on the remote server.
111112
"""
112113
try:
113-
info = self.get_package_info(name)
114+
json_page = self.get_json_page(name)
114115
except PackageNotFound:
115116
self._log(
116-
f"No packages found for {name} {constraint!s}",
117+
f"No packages found for {name}",
117118
level="debug",
118119
)
119120
return []
120121

121-
packages = []
122+
versions: list[Version]
122123

123-
for version_string, release in info["releases"].items():
124-
if not release:
125-
# Bad release
126-
self._log(
127-
f"No release information found for {name}-{version_string},"
128-
" skipping",
129-
level="debug",
130-
)
131-
continue
124+
key = name
125+
if not constraint.is_any():
126+
key = f"{key}:{constraint!s}"
132127

133-
try:
134-
version = Version.parse(version_string)
135-
except InvalidVersion:
136-
self._log(
137-
f'Unable to parse version "{version_string}" for the'
138-
f" {name} package, skipping",
139-
level="debug",
140-
)
141-
continue
142-
143-
if constraint.allows(version):
144-
packages.append(Package(info["info"]["name"], version))
128+
if self._cache.store("matches").has(key):
129+
versions = self._cache.store("matches").get(key)
130+
else:
131+
versions = [
132+
version
133+
for version in json_page.versions(name)
134+
if constraint.allows(version)
135+
]
136+
self._cache.store("matches").put(key, versions, 5)
145137

146-
return packages
138+
pretty_name = json_page.content["name"]
139+
return [Package(pretty_name, version) for version in versions]
147140

148141
def _get_package_info(self, name: str) -> dict[str, Any]:
149-
data = self._get(f"pypi/{name}/json")
150-
if data is None:
142+
headers = {"Accept": "application/vnd.pypi.simple.v1+json"}
143+
info = self._get(f"simple/{name}/", headers=headers)
144+
if info is None:
151145
raise PackageNotFound(f"Package [{name}] not found.")
152146

153-
return data
147+
return info
154148

155149
def find_links_for_package(self, package: Package) -> list[Link]:
156150
json_data = self._get(f"pypi/{package.name}/{package.version}/json")
@@ -229,12 +223,20 @@ def _get_release_info(
229223

230224
return data.asdict()
231225

232-
def _get(self, endpoint: str) -> dict[str, Any] | None:
226+
def get_json_page(self, name: str) -> SimpleJsonPage:
227+
source = self._base_url + f"simple/{name}/"
228+
info = self.get_package_info(name)
229+
return SimpleJsonPage(source, info)
230+
231+
def _get(
232+
self, endpoint: str, headers: dict[str, str] | None = None
233+
) -> dict[str, Any] | None:
233234
try:
234235
json_response = self.session.get(
235236
self._base_url + endpoint,
236237
raise_for_status=False,
237238
timeout=REQUESTS_TIMEOUT,
239+
headers=headers,
238240
)
239241
except requests.exceptions.TooManyRedirects:
240242
# Cache control redirect loop.
@@ -244,6 +246,7 @@ def _get(self, endpoint: str) -> dict[str, Any] | None:
244246
self._base_url + endpoint,
245247
raise_for_status=False,
246248
timeout=REQUESTS_TIMEOUT,
249+
headers=headers,
247250
)
248251

249252
if json_response.status_code != 200:

src/poetry/utils/authenticator.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ def authenticated_url(self, url: str) -> str:
191191
def request(
192192
self, method: str, url: str, raise_for_status: bool = True, **kwargs: Any
193193
) -> requests.Response:
194-
request = requests.Request(method, url)
194+
headers = kwargs.get("headers")
195+
request = requests.Request(method, url, headers=headers)
195196
credential = self.get_credentials_for_url(url)
196197

197198
if credential.username is not None or credential.password is not None:

tests/installation/test_executor.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def test_execute_executes_a_batch_of_operations(
146146

147147
return_code = executor.execute(
148148
[
149-
Install(Package("pytest", "3.5.2")),
149+
Install(Package("pytest", "3.5.1")),
150150
Uninstall(Package("attrs", "17.4.0")),
151151
Update(Package("requests", "2.18.3"), Package("requests", "2.18.4")),
152152
Uninstall(Package("clikit", "0.2.3")).skip("Not currently installed"),
@@ -159,7 +159,7 @@ def test_execute_executes_a_batch_of_operations(
159159
expected = f"""
160160
Package operations: 4 installs, 1 update, 1 removal
161161
162-
• Installing pytest (3.5.2)
162+
• Installing pytest (3.5.1)
163163
• Removing attrs (17.4.0)
164164
• Updating requests (2.18.3 -> 2.18.4)
165165
• Installing demo (0.1.0 {file_package.source_url})
@@ -243,18 +243,18 @@ def test_execute_works_with_ansi_output(
243243
mocker.patch.object(env, "_run", return_value=install_output)
244244
return_code = executor.execute(
245245
[
246-
Install(Package("pytest", "3.5.2")),
246+
Install(Package("pytest", "3.5.1")),
247247
]
248248
)
249249
env._run.assert_called_once()
250250

251251
# fmt: off
252252
expected = [
253253
"\x1b[39;1mPackage operations\x1b[39;22m: \x1b[34m1\x1b[39m install, \x1b[34m0\x1b[39m updates, \x1b[34m0\x1b[39m removals", # noqa: E501
254-
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.2\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mPending...\x1b[39m", # noqa: E501
255-
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.2\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mDownloading...\x1b[39m", # noqa: E501
256-
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.2\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mInstalling...\x1b[39m", # noqa: E501
257-
"\x1b[32;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[32m3.5.2\x1b[39m\x1b[39m)\x1b[39m", # finished # noqa: E501
254+
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.1\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mPending...\x1b[39m", # noqa: E501
255+
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.1\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mDownloading...\x1b[39m", # noqa: E501
256+
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.1\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mInstalling...\x1b[39m", # noqa: E501
257+
"\x1b[32;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[32m3.5.1\x1b[39m\x1b[39m)\x1b[39m", # finished # noqa: E501
258258
]
259259
# fmt: on
260260

@@ -285,15 +285,15 @@ def test_execute_works_with_no_ansi_output(
285285
mocker.patch.object(env, "_run", return_value=install_output)
286286
return_code = executor.execute(
287287
[
288-
Install(Package("pytest", "3.5.2")),
288+
Install(Package("pytest", "3.5.1")),
289289
]
290290
)
291291
env._run.assert_called_once()
292292

293293
expected = """
294294
Package operations: 1 install, 0 updates, 0 removals
295295
296-
• Installing pytest (3.5.2)
296+
• Installing pytest (3.5.1)
297297
"""
298298
expected = set(expected.splitlines())
299299
output = set(io_not_decorated.fetch_output().splitlines())
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,25 @@
11
{
2-
"info": {
3-
"author": "Hynek Schlawack",
4-
"author_email": "[email protected]",
5-
"bugtrack_url": null,
6-
"classifiers": [
7-
"Development Status :: 5 - Production/Stable",
8-
"Intended Audience :: Developers",
9-
"License :: OSI Approved :: MIT License",
10-
"Natural Language :: English",
11-
"Operating System :: OS Independent",
12-
"Programming Language :: Python",
13-
"Programming Language :: Python :: 2",
14-
"Programming Language :: Python :: 2.7",
15-
"Programming Language :: Python :: 3",
16-
"Programming Language :: Python :: 3.4",
17-
"Programming Language :: Python :: 3.5",
18-
"Programming Language :: Python :: 3.6",
19-
"Programming Language :: Python :: Implementation :: CPython",
20-
"Programming Language :: Python :: Implementation :: PyPy",
21-
"Topic :: Software Development :: Libraries :: Python Modules"
22-
],
23-
"description": "",
24-
"docs_url": null,
25-
"download_url": "",
26-
"downloads": {
27-
"last_day": -1,
28-
"last_month": -1,
29-
"last_week": -1
30-
},
31-
"home_page": "http://www.attrs.org/",
32-
"keywords": "class,attribute,boilerplate",
33-
"license": "MIT",
34-
"maintainer": "",
35-
"maintainer_email": "",
36-
"name": "attrs",
37-
"package_url": "https://pypi.org/project/attrs/",
38-
"platform": "",
39-
"project_url": "https://pypi.org/project/attrs/",
40-
"release_url": "https://pypi.org/project/attrs/17.4.0/",
41-
"requires_dist": [
42-
"coverage; extra == 'dev'",
43-
"hypothesis; extra == 'dev'",
44-
"pympler; extra == 'dev'",
45-
"pytest; extra == 'dev'",
46-
"six; extra == 'dev'",
47-
"zope.interface; extra == 'dev'",
48-
"sphinx; extra == 'dev'",
49-
"zope.interface; extra == 'dev'",
50-
"sphinx; extra == 'docs'",
51-
"zope.interface; extra == 'docs'",
52-
"coverage; extra == 'tests'",
53-
"hypothesis; extra == 'tests'",
54-
"pympler; extra == 'tests'",
55-
"pytest; extra == 'tests'",
56-
"six; extra == 'tests'",
57-
"zope.interface; extra == 'tests'"
58-
],
59-
"requires_python": "",
60-
"summary": "Classes Without Boilerplate",
61-
"version": "17.4.0"
62-
},
63-
"last_serial": 3451237,
64-
"releases": {
65-
"17.4.0": [
66-
{
67-
"comment_text": "",
68-
"digests": {
69-
"md5": "5835a573b3f0316e1602dac3fd9c1daf",
70-
"sha256": "a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"
71-
},
72-
"downloads": -1,
73-
"filename": "attrs-17.4.0-py2.py3-none-any.whl",
74-
"has_sig": true,
75-
"md5_digest": "5835a573b3f0316e1602dac3fd9c1daf",
76-
"packagetype": "bdist_wheel",
77-
"python_version": "py2.py3",
78-
"size": 31658,
79-
"upload_time": "2017-12-30T08:20:05",
80-
"url": "https://files.pythonhosted.org/packages/b5/60/4e178c1e790fd60f1229a9b3cb2f8bc2f4cc6ff2c8838054c142c70b5adc/attrs-17.4.0-py2.py3-none-any.whl"
81-
},
82-
{
83-
"comment_text": "",
84-
"digests": {
85-
"md5": "d7a89063b2e0fd36bd82389c4d82821d",
86-
"sha256": "1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
87-
},
88-
"downloads": -1,
89-
"filename": "attrs-17.4.0.tar.gz",
90-
"has_sig": true,
91-
"md5_digest": "d7a89063b2e0fd36bd82389c4d82821d",
92-
"packagetype": "sdist",
93-
"python_version": "source",
94-
"size": 97071,
95-
"upload_time": "2017-12-30T08:20:08",
96-
"url": "https://files.pythonhosted.org/packages/8b/0b/a06cfcb69d0cb004fde8bc6f0fd192d96d565d1b8aa2829f0f20adb796e5/attrs-17.4.0.tar.gz"
97-
}
98-
]
99-
},
100-
"urls": [
2+
"name": "attrs",
3+
"files": [
1014
{
102-
"comment_text": "",
103-
"digests": {
5+
"filename": "attrs-17.4.0-py2.py3-none-any.whl",
6+
"url": "https://files.pythonhosted.org/packages/b5/60/4e178c1e790fd60f1229a9b3cb2f8bc2f4cc6ff2c8838054c142c70b5adc/attrs-17.4.0-py2.py3-none-any.whl",
7+
"hashes": {
1048
"md5": "5835a573b3f0316e1602dac3fd9c1daf",
1059
"sha256": "a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450"
106-
},
107-
"downloads": -1,
108-
"filename": "attrs-17.4.0-py2.py3-none-any.whl",
109-
"has_sig": true,
110-
"md5_digest": "5835a573b3f0316e1602dac3fd9c1daf",
111-
"packagetype": "bdist_wheel",
112-
"python_version": "py2.py3",
113-
"size": 31658,
114-
"upload_time": "2017-12-30T08:20:05",
115-
"url": "https://files.pythonhosted.org/packages/b5/60/4e178c1e790fd60f1229a9b3cb2f8bc2f4cc6ff2c8838054c142c70b5adc/attrs-17.4.0-py2.py3-none-any.whl"
10+
}
11611
},
11712
{
118-
"comment_text": "",
119-
"digests": {
13+
"filename": "attrs-17.4.0.tar.gz",
14+
"url": "https://files.pythonhosted.org/packages/8b/0b/a06cfcb69d0cb004fde8bc6f0fd192d96d565d1b8aa2829f0f20adb796e5/attrs-17.4.0.tar.gz",
15+
"hashes": {
12016
"md5": "d7a89063b2e0fd36bd82389c4d82821d",
12117
"sha256": "1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9"
122-
},
123-
"downloads": -1,
124-
"filename": "attrs-17.4.0.tar.gz",
125-
"has_sig": true,
126-
"md5_digest": "d7a89063b2e0fd36bd82389c4d82821d",
127-
"packagetype": "sdist",
128-
"python_version": "source",
129-
"size": 97071,
130-
"upload_time": "2017-12-30T08:20:08",
131-
"url": "https://files.pythonhosted.org/packages/8b/0b/a06cfcb69d0cb004fde8bc6f0fd192d96d565d1b8aa2829f0f20adb796e5/attrs-17.4.0.tar.gz"
18+
}
13219
}
133-
]
20+
],
21+
"meta": {
22+
"api-version": "1.0",
23+
"_last-serial": 3451237
24+
}
13425
}

0 commit comments

Comments
 (0)