55"""
66from __future__ import annotations
77
8+ import base64
89import fnmatch
910import hashlib
1011import io
1819import shutil
1920import sys
2021import tarfile
22+ import zipfile
2123from types import ModuleType
2224from typing import IO , MutableMapping , Optional , Sequence , Union , TYPE_CHECKING
2325
2729 MissingDependencyError ,
2830 Version ,
2931 download_url ,
32+ extract_archive ,
3033 format_shebang ,
3134 runcmd ,
3235)
@@ -246,8 +249,18 @@ def update_ensurepip(directory: pathlib.Path) -> None:
246249
247250 # Detect existing whl. Later versions of python don't include setuptools. We
248251 # only want to update whl files that python expects to be there
249- pip_version = "25.2"
252+ pip_version = "25.3"
253+ pip_whl = f"pip-{ pip_version } -py3-none-any.whl"
254+ pip_whl_path = "44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068"
255+
250256 setuptools_version = "80.9.0"
257+ setuptools_whl = f"setuptools-{ setuptools_version } -py3-none-any.whl"
258+ setuptools_whl_path = "a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772"
259+
260+ urllib3_version = "2.6.2"
261+ urllib3_tarball = f"urllib3-{ urllib3_version } .tar.gz"
262+ urllib3_tarball_path = "1e/24/a2a2ed9addd907787d7aa0355ba36a6cadf1768b934c652ea78acbd59dcd"
263+
251264 update_pip = False
252265 update_setuptools = False
253266 for file in bundle_dir .glob ("*.whl" ):
@@ -275,11 +288,9 @@ def update_ensurepip(directory: pathlib.Path) -> None:
275288 # Download whl files and update __init__.py
276289 init_file = directory / "ensurepip" / "__init__.py"
277290 if update_pip :
278- whl = f"pip-{ pip_version } -py3-none-any.whl"
279- whl_path = "b7/3f/945ef7ab14dc4f9d7f40288d2df998d1837ee0888ec3659c813487572faa"
280- url = f"https://files.pythonhosted.org/packages/{ whl_path } /{ whl } "
291+ url = f"https://files.pythonhosted.org/packages/{ pip_whl_path } /{ pip_whl } "
281292 download_url (url = url , dest = bundle_dir )
282- assert (bundle_dir / whl ).exists ()
293+ assert (bundle_dir / pip_whl ).exists ()
283294
284295 # Update __init__.py
285296 old = "^_PIP_VERSION.*"
@@ -288,11 +299,9 @@ def update_ensurepip(directory: pathlib.Path) -> None:
288299
289300 # setuptools
290301 if update_setuptools :
291- whl = f"setuptools-{ setuptools_version } -py3-none-any.whl"
292- whl_path = "a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772"
293- url = f"https://files.pythonhosted.org/packages/{ whl_path } /{ whl } "
302+ url = f"https://files.pythonhosted.org/packages/{ setuptools_whl_path } /{ setuptools_whl } "
294303 download_url (url = url , dest = bundle_dir )
295- assert (bundle_dir / whl ).exists ()
304+ assert (bundle_dir / setuptools_whl ).exists ()
296305
297306 # setuptools
298307 old = "^_SETUPTOOLS_VERSION.*"
@@ -302,6 +311,83 @@ def update_ensurepip(directory: pathlib.Path) -> None:
302311 log .debug ("ensurepip __init__.py contents:" )
303312 log .debug (init_file .read_text ())
304313
314+ # TODO: unpack the pip whl using zipfile (wheel isn't installed yet)
315+ pip_whl_extracted = bundle_dir / "pip_whl_extracted"
316+ with zipfile .ZipFile (bundle_dir / pip_whl ) as whl_file :
317+ whl_file .extractall (path = pip_whl_extracted )
318+
319+ # TODO: pull down urllib3 tarball
320+ url = f"https://files.pythonhosted.org/packages/{ urllib3_tarball_path } /{ urllib3_tarball } "
321+ download_url (url = url , dest = bundle_dir )
322+ assert (bundle_dir / urllib3_tarball ).exists ()
323+
324+ # TODO: Extract the tarball
325+ urllib3_extracted = bundle_dir / "urllib3_extracted"
326+ extract_archive (to_dir = urllib3_extracted , archive = bundle_dir / urllib3_tarball )
327+
328+ # TODO: replace urllib3 in pip
329+ # Delete target urllib3
330+ urllib3_target_dir = bundle_dir / pip_whl_extracted / f"pip-{ pip_version } " / "pip" / "_vendor" / "urllib3"
331+ urllib3_source_dir = urllib3_extracted / "src" / "urllib3"
332+ try :
333+ shutil .rmtree (urllib3_target_dir )
334+ log .debug ("Removed urllib3 target directory: %s" , urllib3_target_dir )
335+ except OSError :
336+ log .debug ("Failed to remove urllib3 target directory: %s" , urllib3_target_dir )
337+
338+ # Move source urllib3 to target
339+ urllib3_source_dir .rename (urllib3_target_dir )
340+
341+ # Cleanup urllib3 source and tarball
342+ shutil .rmtree (urllib3_extracted )
343+ (bundle_dir / urllib3_tarball ).unlink (missing_ok = True )
344+
345+ # TODO: recompute the hashes and update dist-info\RECORD
346+ def get_record_entry (file_path , root_dir ):
347+ # 1. Calculate SHA256 and Size
348+ sha256 = hashlib .sha256 ()
349+ size = os .path .getsize (file_path )
350+
351+ with open (file_path , 'rb' ) as f :
352+ while chunk := f .read (8192 ):
353+ sha256 .update (chunk )
354+
355+ # 2. Encode to URL-safe Base64 and remove padding '='
356+ hash_base64 = base64 .urlsafe_b64encode (sha256 .digest ()).decode ('latin1' ).rstrip ('=' )
357+
358+ # 3. Create relative path for RECORD
359+ rel_path = os .path .relpath (file_path , root_dir ).replace (os .sep , '/' )
360+
361+ return f"{ rel_path } ,sha256={ hash_base64 } ,{ size } "
362+
363+ pip_src_dir = pip_whl_extracted / f"pip-{ pip_version } "
364+ # delete existing RECORD file
365+ records_file = pip_src_dir / f"pip-{ pip_version } .dist-info" / "RECORD"
366+ records_file .unlink (missing_ok = True )
367+ # create new RECORD file
368+ files_list = [f for f in pip_src_dir .rglob ("*" ) if f .is_file ()]
369+ with open (records_file , "w" ) as f :
370+ for file in files_list :
371+ f .write (get_record_entry (file , root_dir = pip_src_dir ) + "\n " )
372+ # This is the last line. It shouldn't be there because we removed the
373+ # RECORD file before we listed all files
374+ f .write (f"pip-{ pip_version } .dist-info/RECORD,," )
375+ assert records_file .exists ()
376+
377+ # TODO: pack the pip whl
378+ (bundle_dir / pip_whl ).unlink (missing_ok = True )
379+ # We need to do this again so we include the RECORD file
380+ files_list = [f for f in pip_src_dir .rglob ("*" ) if f .is_file ()]
381+ with zipfile .ZipFile (bundle_dir / pip_whl , "w" , zipfile .ZIP_DEFLATED ) as whl_file :
382+ for file in files_list :
383+ arc_name = file .relative_to (pip_src_dir )
384+ whl_file .write (file , arc_name )
385+ assert (bundle_dir / pip_whl ).exists ()
386+
387+ # TODO: Clean up extracted pip
388+ shutil .rmtree (pip_whl_extracted )
389+ assert not pip_whl_extracted .exists ()
390+
305391
306392def install_sysdata (
307393 mod : ModuleType ,
0 commit comments