88from urllib .parse import quote
99from pathlib import Path
1010from github import Github
11- from typing import List , Dict
11+ from typing import List , Dict , Set
12+
13+ # Define yanked versions - modify this dictionary as needed
14+ yanked_versions = {
15+ "confluent-kafka" : {
16+ "2.11.0+gr" ,
17+ "2.11.0+gr.1" ,
18+ },
19+ }
1220
1321HTML_TEMPLATE = """<!DOCTYPE html>
1422 <html>
2129 </body>
2230 </html>
2331"""
24-
32+
2533def normalize (name ):
2634 """Normalize package name according to PEP 503."""
2735 return re .sub (r"[-_.]+" , "-" , name ).lower ()
@@ -32,12 +40,25 @@ def calculate_sha256(file_path):
3240
3341 return digest .hexdigest ()
3442
43+ def extract_version_from_filename (filename : str ) -> str :
44+ """Extract version from wheel or sdist filename."""
45+ # Remove extension
46+ name = filename .replace ('.tar.gz' , '' ).replace ('.whl' , '' )
47+
48+ # For wheels: package-version-python-abi-platform
49+ # For sdist: package-version
50+ parts = name .split ('-' )
51+ if len (parts ) >= 2 :
52+ return parts [1 ]
53+ return ""
54+
3555class PackageIndexBuilder :
36- def __init__ (self , token : str , repo_name : str , output_dir : str ):
56+ def __init__ (self , token : str , repo_name : str , output_dir : str , yanked_versions : Dict [ str , Set [ str ]] = None ):
3757 self .github = Github (token )
3858 self .repo = self .github .get_repo (repo_name )
3959 self .output_dir = Path (output_dir )
4060 self .packages : Dict [str , List [Dict ]] = {}
61+ self .yanked_versions = yanked_versions or {}
4162
4263 # Set up authenticated session
4364 self .session = requests .Session ()
@@ -46,9 +67,13 @@ def __init__(self, token: str, repo_name: str, output_dir: str):
4667 "Accept" : "application/octet-stream" ,
4768 })
4869
49- def collect_packages (self ):
70+ def is_version_yanked (self , package_name : str , version : str ) -> bool :
71+ """Check if a specific version of a package is yanked."""
72+ normalized_package = normalize (package_name )
73+ return normalized_package in self .yanked_versions and version in self .yanked_versions [normalized_package ]
5074
51- print ("Query release assets" )
75+ def collect_packages (self ):
76+ print ("Query release assets" )
5277
5378 for release in self .repo .get_releases ():
5479 for asset in release .get_assets ():
@@ -57,11 +82,13 @@ def collect_packages(self):
5782 if package_name not in self .packages :
5883 self .packages [package_name ] = []
5984
85+ version = extract_version_from_filename (asset .name )
6086 self .packages [package_name ].append ({
6187 'filename' : asset .name ,
6288 'url' : asset .url ,
6389 'size' : asset .size ,
6490 'upload_time' : asset .created_at .strftime ('%Y-%m-%d %H:%M:%S' ),
91+ 'version' : version ,
6592 })
6693
6794 def generate_index_html (self ):
@@ -84,7 +111,9 @@ def generate_index_html(self):
84111 file_links = []
85112 assets = sorted (assets , key = lambda x : x ["filename" ])
86113 for filename , items in itertools .groupby (assets , key = lambda x : x ["filename" ]):
87- url = next (items )['url' ]
114+ asset_info = next (items )
115+ url = asset_info ['url' ]
116+ version = asset_info ['version' ]
88117
89118 # Download the file
90119 with open (package_dir / filename , 'wb' ) as f :
@@ -96,7 +125,15 @@ def generate_index_html(self):
96125 f .write (chunk )
97126
98127 sha256_hash = calculate_sha256 (package_dir / filename )
99- file_links .append (f'<a href="{ quote (filename )} #sha256={ sha256_hash } ">{ filename } </a><br/>' )
128+
129+ # Check if this version is yanked
130+ yanked_attr = ""
131+ if self .is_version_yanked (package , version ):
132+ yanked_attr = ' data-yanked="true"'
133+
134+ file_links .append (
135+ f'<a href="{ quote (filename )} #sha256={ sha256_hash } "{ yanked_attr } >{ filename } </a><br/>'
136+ )
100137
101138 package_index = HTML_TEMPLATE .format (
102139 package_name = f"Links for { package } " ,
@@ -126,7 +163,7 @@ def main():
126163 print ("Missing required environment variables" )
127164 sys .exit (1 )
128165
129- builder = PackageIndexBuilder (token , repo , output_dir )
166+ builder = PackageIndexBuilder (token , repo , output_dir , yanked_versions )
130167 builder .build ()
131168
132169if __name__ == "__main__" :
0 commit comments