2
2
import importlib .metadata
3
3
import logging
4
4
import warnings
5
+ from collections .abc import Iterable
5
6
from dataclasses import dataclass , field
6
7
from importlib .metadata import PackageNotFoundError
7
8
from urllib .parse import urlparse
19
20
Requirement ,
20
21
)
21
22
from ._vendored .packaging .src .packaging .utils import canonicalize_name
22
- from .constants import FAQ_URLS
23
+ from .constants import FAQ_URLS , YANKED_WARNING_MESSAGE
23
24
from .package import PackageMetadata
24
25
from .package_index import ProjectInfo
25
26
from .wheelinfo import WheelInfo
@@ -238,6 +239,16 @@ async def _add_requirement_from_package_index(self, req: Requirement):
238
239
239
240
logger .debug ("Transaction: Selected wheel: %r" , wheel )
240
241
242
+ if wheel .yanked :
243
+ yanked_reason = wheel .yanked_reason if wheel .yanked_reason else "None"
244
+ logger .info (
245
+ YANKED_WARNING_MESSAGE ,
246
+ wheel .name ,
247
+ str (wheel .version ),
248
+ wheel .url ,
249
+ yanked_reason ,
250
+ )
251
+
241
252
# Maybe while we were downloading pypi_json some other branch
242
253
# installed the wheel?
243
254
satisfied , ver = self .check_version_satisfied (req )
@@ -327,6 +338,8 @@ def find_wheel(metadata: ProjectInfo, req: Requirement) -> WheelInfo:
327
338
reverse = True ,
328
339
)
329
340
341
+ yanked_versions : list [list [WheelInfo ]] = []
342
+
330
343
for ver in candidate_versions :
331
344
if ver not in releases :
332
345
warnings .warn (
@@ -335,22 +348,43 @@ def find_wheel(metadata: ProjectInfo, req: Requirement) -> WheelInfo:
335
348
)
336
349
continue
337
350
338
- best_wheel = None
339
- best_tag_index = float ("infinity" )
351
+ wheels = list (releases [ver ])
340
352
341
- wheels = releases [ver ]
342
- for wheel in wheels :
343
- tag_index = best_compatible_tag_index (wheel .tags )
344
- if tag_index is not None and tag_index < best_tag_index :
345
- best_wheel = wheel
346
- best_tag_index = tag_index
353
+ # If the version is yanked, put it in the end of the candidate list.
354
+ # If we can't find a wheel that satisfies the requirement,
355
+ # install the yanked version as a last resort.
356
+ # when the version is yanked, all wheels are yanked, so we can check only the first wheel.
357
+ yanked = wheels and wheels [0 ].yanked
358
+ if yanked :
359
+ yanked_versions .append (wheels )
360
+ continue
361
+
362
+ best_wheel = _find_best_wheel (wheels )
347
363
348
364
if best_wheel is not None :
349
- return wheel
365
+ return best_wheel
366
+
367
+ for wheels in yanked_versions :
368
+ best_wheel = _find_best_wheel (wheels )
369
+
370
+ if best_wheel is not None :
371
+ return best_wheel
350
372
351
373
raise ValueError (
352
374
f"Can't find a pure Python 3 wheel for '{ req } '.\n "
353
375
f"See: { FAQ_URLS ['cant_find_wheel' ]} \n "
354
376
"You can use `await micropip.install(..., keep_going=True)` "
355
377
"to get a list of all packages with missing wheels."
356
378
)
379
+
380
+
381
+ def _find_best_wheel (wheels : Iterable [WheelInfo ]) -> WheelInfo | None :
382
+ best_wheel = None
383
+ best_tag_index = float ("infinity" )
384
+ for wheel in wheels :
385
+ tag_index = best_compatible_tag_index (wheel .tags )
386
+ if tag_index is not None and tag_index < best_tag_index :
387
+ best_wheel = wheel
388
+ best_tag_index = tag_index
389
+
390
+ return best_wheel
0 commit comments