50
50
import logging
51
51
import os
52
52
import re
53
- from dataclasses import dataclass
53
+ from dataclasses import dataclass , field
54
54
from enum import StrEnum
55
55
from pathlib import Path
56
56
from typing import Generator , Iterable , NamedTuple , Self
@@ -120,6 +120,7 @@ class PythonDownload:
120
120
filename : str
121
121
url : str
122
122
sha256 : str | None = None
123
+ build_options : list [str ] = field (default_factory = list )
123
124
variant : Variant | None = None
124
125
125
126
def key (self ) -> str :
@@ -211,14 +212,14 @@ async def _fetch_downloads(self, pages: int = 100) -> list[PythonDownload]:
211
212
download
212
213
)
213
214
214
- # Collapse CPython variants to a single URL flavor per triple and variant
215
+ # Collapse CPython variants to a single flavor per triple and variant
215
216
downloads = []
216
217
for version_downloads in downloads_by_version .values ():
217
218
selected : dict [
218
219
tuple [PlatformTriple , Variant | None ], tuple [PythonDownload , int ]
219
220
] = {}
220
221
for download in version_downloads :
221
- priority = self ._get_flavor_priority (download . flavor )
222
+ priority = self ._get_priority (download )
222
223
existing = selected .get ((download .triple , download .variant ))
223
224
if existing :
224
225
existing_download , existing_priority = existing
@@ -301,10 +302,10 @@ def _parse_download_url(self, url: str) -> PythonDownload | None:
301
302
302
303
version , _date , triple , build_options , flavor = match .groups ()
303
304
304
- variants = build_options .split ("+" ) if build_options else []
305
+ build_options = build_options .split ("+" ) if build_options else []
305
306
variant : Variant | None
306
307
for variant in Variant :
307
- if variant in variants :
308
+ if variant in build_options :
308
309
break
309
310
else :
310
311
variant = None
@@ -322,6 +323,7 @@ def _parse_download_url(self, url: str) -> PythonDownload | None:
322
323
implementation = self .implementation ,
323
324
filename = filename ,
324
325
url = url ,
326
+ build_options = build_options ,
325
327
variant = variant ,
326
328
)
327
329
@@ -355,13 +357,29 @@ def _normalize_arch(self, arch: str) -> str:
355
357
def _normalize_os (self , os : str ) -> str :
356
358
return os
357
359
358
- def _get_flavor_priority (self , flavor : str ) -> int :
359
- """Returns the priority of a flavor. Lower is better."""
360
+ def _get_priority (self , download : PythonDownload ) -> tuple [int , int ]:
361
+ """
362
+ Returns the priority of a download, a lower score is better.
363
+ """
364
+ flavor_priority = self ._flavor_priority (download .flavor )
365
+ build_option_priority = self ._build_option_priority (download .build_options )
366
+ return (flavor_priority , build_option_priority )
367
+
368
+ def _flavor_priority (self , flavor : str ) -> int :
360
369
try :
361
- pref = self .FLAVOR_PREFERENCES .index (flavor )
370
+ priority = self .FLAVOR_PREFERENCES .index (flavor )
362
371
except ValueError :
363
- pref = len (self .FLAVOR_PREFERENCES ) + 1
364
- return pref
372
+ priority = len (self .FLAVOR_PREFERENCES ) + 1
373
+ return priority
374
+
375
+ def _build_option_priority (self , build_options : list [str ]) -> int :
376
+ # Prefer optimized builds
377
+ return - 1 * sum (
378
+ (
379
+ "lgo" in build_options ,
380
+ "pgo" in build_options ,
381
+ )
382
+ )
365
383
366
384
367
385
class PyPyFinder (Finder ):
0 commit comments