33import argparse
44import json
55import re
6+ import time
67from urllib import request
8+ from urllib .error import HTTPError
79
810import pandas as pd
911from docutils import nodes
@@ -24,11 +26,22 @@ def get_all_python_versions():
2426
2527 Note that all versions are returned in descending order
2628 """
29+ today = pd .Timestamp ("today" )
30+ today = today .normalize ()
31+
2732 python_versions = (
28- pd .read_html (
29- "https://devguide.python.org/versions/" ,
30- storage_options = HEADERS ,
31- )[0 ]
33+ pd .read_json (
34+ "https://peps.python.org/api/release-cycle.json" ,
35+ orient = "index" ,
36+ convert_axes = False ,
37+ )
38+ .rename (columns = {"branch" : "Branch" })
39+ .pipe (
40+ lambda df : df .assign (
41+ end_of_life = pd .to_datetime (df .end_of_life , format = "mixed" )
42+ )
43+ )
44+ .query ("end_of_life >= @today" )
3245 .query ('Branch != "main"' )
3346 .dropna ()
3447 .Branch .to_list ()
@@ -67,40 +80,50 @@ def get_min_numba_numpy_version(min_python):
6780 Find the minimum versions of Numba and NumPy that supports the specified
6881 `min_python` version
6982 """
70- df = (
71- pd .read_html (
72- "https://numba.readthedocs.io/en/stable/user/installing.html#version-support-information" , # noqa
73- storage_options = HEADERS ,
74- )[0 ]
75- .dropna ()
76- .drop (columns = ["Numba.1" , "llvmlite" , "LLVM" , "TBB" ])
77- .query ('`Python`.str.contains("2.7") == False' )
78- .query ('`Numba`.str.contains(".x") == False' )
79- .query ('`Numba`.str.contains("{") == False' )
80- .pipe (
81- lambda df : df .assign (
82- MIN_PYTHON_SPEC = (
83- df .Python .str .split ().str [1 ].replace ({"<" : "=" }, regex = True )
84- + df .Python .str .split ().str [0 ].replace ({".x" : "" }, regex = True )
85- ).apply (SpecifierSet )
83+ url = "https://github.com/numba/numba/blob/main/docs/source/user/installing.rst"
84+ try :
85+ time .sleep (2 ) # Avoid 429 error
86+ df = (
87+ pd .read_html (
88+ url ,
89+ storage_options = HEADERS ,
90+ )[0 ]
91+ .dropna ()
92+ .drop (columns = ["Numba.1" , "llvmlite" , "LLVM" , "TBB" ])
93+ .query ('`Python`.str.contains("2.7") == False' )
94+ .query ('`Numba`.str.contains(".x") == False' )
95+ .query ('`Numba`.str.contains("{") == False' )
96+ .pipe (
97+ lambda df : df .assign (
98+ MIN_PYTHON_SPEC = (
99+ df .Python .str .split ().str [1 ].replace ({"<" : "=" }, regex = True )
100+ + df .Python .str .split ().str [0 ].replace ({".x" : "" }, regex = True )
101+ ).apply (SpecifierSet )
102+ )
86103 )
87- )
88- .pipe (
89- lambda df : df .assign (
90- MIN_NUMPY = (df .NumPy .str .split ().str [0 ].replace ({".x" : "" }, regex = True ))
104+ .pipe (
105+ lambda df : df .assign (
106+ MIN_NUMPY = (
107+ df .NumPy .str .split ().str [0 ].replace ({".x" : "" }, regex = True )
108+ )
109+ )
91110 )
92- )
93- . assign (
94- COMPATIBLE = lambda row : row . apply (
95- check_python_compatibility , axis = 1 , args = ( Version ( min_python ), )
111+ . assign (
112+ COMPATIBLE = lambda row : row . apply (
113+ check_python_compatibility , axis = 1 , args = ( Version ( min_python ),)
114+ )
96115 )
116+ .query ("COMPATIBLE == True" )
117+ .pipe (lambda df : df .assign (MINOR = df .Numba .str .split ("." ).str [1 ]))
118+ .pipe (lambda df : df .assign (PATCH = df .Numba .str .split ("." ).str [2 ]))
119+ .sort_values (["MINOR" , "PATCH" ], ascending = [False , True ])
120+ .iloc [- 1 ]
97121 )
98- .query ("COMPATIBLE == True" )
99- .pipe (lambda df : df .assign (MINOR = df .Numba .str .split ("." ).str [1 ]))
100- .pipe (lambda df : df .assign (PATCH = df .Numba .str .split ("." ).str [2 ]))
101- .sort_values (["MINOR" , "PATCH" ], ascending = [False , True ])
102- .iloc [- 1 ]
103- )
122+ except HTTPError as e :
123+ if e .code == 429 :
124+ time .sleep (2 ) # Avoid 429 error
125+ return get_min_numba_numpy_version (min_python )
126+
104127 return df .Numba , df .MIN_NUMPY
105128
106129
@@ -435,7 +458,7 @@ def match_pkg_version(line, pkg_name):
435458 rf"""
436459 { pkg_name } # Package name
437460 [\s=><:"\'\[\]]* # Zero or more spaces or special characters
438- (\d+\.\d+[\.0-9]*) # Capture "version" in `matches`
461+ (\d+\.\d+[\.0-9\* ]*) # Capture "version" in `matches`
439462 """ ,
440463 line ,
441464 re .VERBOSE | re .IGNORECASE , # Ignores all whitespace and case in pattern
@@ -457,6 +480,7 @@ def find_pkg_mismatches(pkg_name, pkg_version, fnames):
457480 matches = match_pkg_version (l , pkg_name )
458481 if matches is not None :
459482 version = matches .groups ()[0 ]
483+ version = version .removesuffix (".*" )
460484 if version != pkg_version :
461485 pkg_mismatches .append ((pkg_name , version , fname , line_num ))
462486
@@ -537,12 +561,19 @@ def get_all_min_versions(MIN_PYTHON):
537561 ):
538562 if fname == "pyproject.toml" :
539563 line = pyproject_lines [line_num - 1 ]
564+ prev_line = pyproject_lines [line_num - 2 ]
540565 if "Programming Language :: Python" in line and Version (
541566 pkg_version
542567 ) <= Version (version ):
543568 # Skip lines in `pyproject.toml` where the "Programming Language"
544569 # version may be higher than the minimum Python version
545570 continue
571+ elif "tool.pixi.feature." in prev_line and Version (
572+ pkg_version
573+ ) < Version (version ):
574+ # Skip lines in `pyproject.toml` where the "tool.pixi.feature"
575+ # version may be higher than the minimum Python version
576+ continue
546577
547578 print (
548579 f"{ pkg_name } { pkg_version } Mismatch: Version { version } "
0 commit comments