10
10
from typing import Any
11
11
from typing import Iterator
12
12
13
+ import pkginfo
13
14
import requests
14
15
15
16
from poetry .core .constraints .version import parse_constraint
18
19
from poetry .core .utils .helpers import temporary_directory
19
20
from poetry .core .version .markers import parse_marker
20
21
22
+ from poetry .inspection .info import PackageInfo
21
23
from poetry .repositories .cached_repository import CachedRepository
22
24
from poetry .repositories .exceptions import PackageNotFound
23
25
from poetry .repositories .exceptions import RepositoryError
32
34
from packaging .utils import NormalizedName
33
35
34
36
from poetry .config .config import Config
35
- from poetry .inspection .info import PackageInfo
36
37
from poetry .repositories .link_sources .base import LinkSource
37
38
from poetry .utils .authenticator import RepositoryCertificateConfig
38
39
@@ -98,10 +99,29 @@ def _get_info_from_sdist(self, url: str) -> PackageInfo:
98
99
with self ._cached_or_downloaded_file (Link (url )) as filepath :
99
100
return PackageInfo .from_sdist (filepath )
100
101
101
- def _get_info_from_urls (self , urls : dict [str , list [str ]]) -> PackageInfo :
102
+ @staticmethod
103
+ def _get_info_from_metadata (
104
+ url : str , metadata : dict [str , pkginfo .Distribution ]
105
+ ) -> PackageInfo | None :
106
+ if url in metadata :
107
+ dist = metadata [url ]
108
+ return PackageInfo (
109
+ name = dist .name ,
110
+ version = dist .version ,
111
+ summary = dist .summary ,
112
+ requires_dist = list (dist .requires_dist ),
113
+ requires_python = dist .requires_python ,
114
+ )
115
+ return None
116
+
117
+ def _get_info_from_urls (
118
+ self ,
119
+ urls : dict [str , list [str ]],
120
+ metadata : dict [str , pkginfo .Distribution ] | None = None ,
121
+ ) -> PackageInfo :
122
+ metadata = metadata or {}
102
123
# Prefer to read data from wheels: this is faster and more reliable
103
- wheels = urls .get ("bdist_wheel" )
104
- if wheels :
124
+ if wheels := urls .get ("bdist_wheel" ):
105
125
# We ought just to be able to look at any of the available wheels to read
106
126
# metadata, they all should give the same answer.
107
127
#
@@ -137,13 +157,19 @@ def _get_info_from_urls(self, urls: dict[str, list[str]]) -> PackageInfo:
137
157
platform_specific_wheels .append (wheel )
138
158
139
159
if universal_wheel is not None :
140
- return self ._get_info_from_wheel (universal_wheel )
160
+ return self ._get_info_from_metadata (
161
+ universal_wheel , metadata
162
+ ) or self ._get_info_from_wheel (universal_wheel )
141
163
142
164
info = None
143
165
if universal_python2_wheel and universal_python3_wheel :
144
- info = self ._get_info_from_wheel (universal_python2_wheel )
166
+ info = self ._get_info_from_metadata (
167
+ universal_python2_wheel , metadata
168
+ ) or self ._get_info_from_wheel (universal_python2_wheel )
145
169
146
- py3_info = self ._get_info_from_wheel (universal_python3_wheel )
170
+ py3_info = self ._get_info_from_metadata (
171
+ universal_python3_wheel , metadata
172
+ ) or self ._get_info_from_wheel (universal_python3_wheel )
147
173
148
174
if info .requires_python or py3_info .requires_python :
149
175
info .requires_python = str (
@@ -193,16 +219,24 @@ def _get_info_from_urls(self, urls: dict[str, list[str]]) -> PackageInfo:
193
219
194
220
# Prefer non platform specific wheels
195
221
if universal_python3_wheel :
196
- return self ._get_info_from_wheel (universal_python3_wheel )
222
+ return self ._get_info_from_metadata (
223
+ universal_python3_wheel , metadata
224
+ ) or self ._get_info_from_wheel (universal_python3_wheel )
197
225
198
226
if universal_python2_wheel :
199
- return self ._get_info_from_wheel (universal_python2_wheel )
227
+ return self ._get_info_from_metadata (
228
+ universal_python2_wheel , metadata
229
+ ) or self ._get_info_from_wheel (universal_python2_wheel )
200
230
201
231
if platform_specific_wheels :
202
232
first_wheel = platform_specific_wheels [0 ]
203
- return self ._get_info_from_wheel (first_wheel )
233
+ return self ._get_info_from_metadata (
234
+ first_wheel , metadata
235
+ ) or self ._get_info_from_wheel (first_wheel )
204
236
205
- return self ._get_info_from_sdist (urls ["sdist" ][0 ])
237
+ return self ._get_info_from_metadata (
238
+ urls ["sdist" ][0 ], metadata
239
+ ) or self ._get_info_from_sdist (urls ["sdist" ][0 ])
206
240
207
241
def _links_to_data (self , links : list [Link ], data : PackageInfo ) -> dict [str , Any ]:
208
242
if not links :
@@ -211,11 +245,39 @@ def _links_to_data(self, links: list[Link], data: PackageInfo) -> dict[str, Any]
211
245
f' "{ data .version } "'
212
246
)
213
247
urls = defaultdict (list )
248
+ metadata = {}
214
249
files : list [dict [str , Any ]] = []
215
250
for link in links :
216
251
if link .yanked and not data .yanked :
217
252
# drop yanked files unless the entire release is yanked
218
253
continue
254
+ if link .has_metadata :
255
+ try :
256
+ assert link .metadata_url is not None
257
+ response = self .session .get (link .metadata_url )
258
+ distribution = pkginfo .Distribution ()
259
+ assert link .metadata_hash_name is not None
260
+ metadata_hash = getattr (hashlib , link .metadata_hash_name )(
261
+ response .text .encode ()
262
+ ).hexdigest ()
263
+
264
+ if metadata_hash != link .metadata_hash :
265
+ self ._log (
266
+ (
267
+ f"Metadata file hash ({ metadata_hash } ) does not match"
268
+ f" expected hash ({ link .metadata_hash } )."
269
+ ),
270
+ level = "warning" ,
271
+ )
272
+
273
+ distribution .parse (response .content )
274
+ metadata [link .url ] = distribution
275
+ except requests .HTTPError :
276
+ self ._log (
277
+ f"Failed to retrieve metadata at { link .metadata_url } " ,
278
+ level = "debug" ,
279
+ )
280
+
219
281
if link .is_wheel :
220
282
urls ["bdist_wheel" ].append (link .url )
221
283
elif link .filename .endswith (
@@ -253,7 +315,7 @@ def _links_to_data(self, links: list[Link], data: PackageInfo) -> dict[str, Any]
253
315
254
316
data .files = files
255
317
256
- info = self ._get_info_from_urls (urls )
318
+ info = self ._get_info_from_urls (urls , metadata )
257
319
258
320
data .summary = info .summary
259
321
data .requires_dist = info .requires_dist
0 commit comments