2222os .environ ["HOME" ] = os .environ ["USERPROFILE" ]
2323
2424class Package :
25- "standardize a Package from filename or pip list"
26- def __init__ (self , fname , suggested_summary = None ):
25+ """Standardize a Package from filename or pip list."" "
26+ def __init__ (self , fname : str , suggested_summary : str = None ):
2727 self .fname = fname
2828 self .description = piptree .sum_up (suggested_summary ) if suggested_summary else ""
2929 self .name , self .version = None , None
@@ -40,33 +40,32 @@ def __str__(self):
4040
4141
4242class Distribution :
43- def __init__ ( self , target = None , verbose = False ):
44- # if no target path given, take the current python interpreter one
45- self .target = target or os .path .dirname (sys .executable )
43+ """Handles operations on a WinPython distribution."""
44+ def __init__ ( self , target : str = None , verbose : bool = False ):
45+ self .target = target or os .path .dirname (sys .executable ) # Default target more explicit
4646 self .verbose = verbose
4747 self .pip = None
48- self .to_be_removed = [] # list of directories to be removed later
49- self .version , self .architecture = utils .get_python_infos (target )
50- # name of the exe (python.exe or pypy3.exe)
48+ self .to_be_removed = []
49+ self .version , self .architecture = utils .get_python_infos (self .target )
5150 self .short_exe = Path (utils .get_python_executable (self .target )).name
5251
5352 def clean_up (self ):
54- """Remove directories which couldn't be removed when building """
53+ """Remove directories that were marked for removal. """
5554 for path in self .to_be_removed :
5655 try :
5756 shutil .rmtree (path , onexc = utils .onerror )
58- except WindowsError :
59- print (f"Directory { path } could not be removed " , file = sys .stderr )
57+ except OSError as e :
58+ print (f"Error: Could not remove directory { path } : { e } " , file = sys .stderr )
6059
61- def remove_directory (self , path ):
62- """Try to remove directory -- on WindowsError, remove it later """
60+ def remove_directory (self , path : str ):
61+ """Try to remove a directory, add to removal list on failure. """
6362 try :
6463 shutil .rmtree (path )
65- except WindowsError :
64+ except OSError :
6665 self .to_be_removed .append (path )
6766
68- def copy_files (self , package , targetdir , srcdir , dstdir , create_bat_files = False ):
69- """Add copy task """
67+ def copy_files (self , package : Package , targetdir : str , srcdir : str , dstdir : str , create_bat_files : bool = False ):
68+ """Copy files from srcdir to dstdir within the target distribution. """
7069 srcdir = str (Path (targetdir ) / srcdir )
7170 if not Path (srcdir ).is_dir ():
7271 return
@@ -112,8 +111,8 @@ def create_file(self, package, name, dstdir, contents):
112111 fd .write (contents )
113112 package .files .append (dst )
114113
115- def get_installed_packages (self , update = False ):
116- """Return installed packages"""
114+ def get_installed_packages (self , update : bool = False ) -> list [ Package ] :
115+ """Return installed packages. """
117116
118117 # Include package installed via pip (not via WPPM)
119118 wppm = []
@@ -133,14 +132,14 @@ def get_installed_packages(self, update=False):
133132 ]
134133 return sorted (wppm , key = lambda tup : tup .name .lower ())
135134
136- def find_package (self , name ) :
137- """Find installed package"""
135+ def find_package (self , name : str ) -> Package | None :
136+ """Find installed package by name. """
138137 for pack in self .get_installed_packages ():
139138 if utils .normalize (pack .name ) == utils .normalize (name ):
140139 return pack
141140
142- def patch_all_shebang (self , to_movable = True , max_exe_size = 999999 , targetdir = "" ):
143- """make all python launchers relatives """
141+ def patch_all_shebang (self , to_movable : bool = True , max_exe_size : int = 999999 , targetdir : str = "" ):
142+ """Make all python launchers relative. """
144143 import glob
145144
146145 for ffname in glob .glob (r"%s\Scripts\*.exe" % self .target ):
@@ -150,10 +149,9 @@ def patch_all_shebang(self, to_movable=True, max_exe_size=999999, targetdir=""):
150149 for ffname in glob .glob (r"%s\Scripts\*.py" % self .target ):
151150 utils .patch_shebang_line_py (ffname , to_movable = to_movable , targetdir = targetdir )
152151
153- def install (self , package , install_options = None ):
154- """Install package in distribution"""
155- # wheel addition
156- if package .fname .endswith ((".whl" , ".tar.gz" , ".zip" )):
152+ def install (self , package : Package , install_options : list [str ] = None ): # Type hint install_options
153+ """Install package in distribution."""
154+ if package .fname .endswith ((".whl" , ".tar.gz" , ".zip" )): # Check extension with tuple
157155 self .install_bdist_direct (package , install_options = install_options )
158156 self .handle_specific_packages (package )
159157 # minimal post-install actions
@@ -206,9 +204,7 @@ def patch_standard_packages(self, package_name="", to_movable=True):
206204 # sheb_mov2 = tried way, but doesn't work for pip (at least)
207205 sheb_fix = " executable = get_executable()"
208206 sheb_mov1 = " executable = os.path.join(os.path.basename(get_executable()))"
209- sheb_mov2 = (
210- " executable = os.path.join('..',os.path.basename(get_executable()))"
211- )
207+ sheb_mov2 = " executable = os.path.join('..',os.path.basename(get_executable()))"
212208
213209 # Adpating to PyPy
214210 the_place = site_package_place + r"pip\_vendor\distlib\scripts.py"
@@ -240,30 +236,25 @@ def patch_standard_packages(self, package_name="", to_movable=True):
240236 else :
241237 self .create_pybat (package_name .lower ())
242238
243- def create_pybat (
244- self ,
245- names = "" ,
246- contents = r"""@echo off
239+
240+ def create_pybat (self , names = "" , contents = r"""@echo off
247241..\python "%~dpn0" %*""" ,
248242 ):
249243 """Create launcher batch script when missing"""
250244
251- scriptpy = str (Path (self .target ) / "Scripts" ) # std Scripts of python
252-
253- # PyPy has no initial Scipts directory
254- if not Path (scriptpy ).is_dir ():
255- os .mkdir (scriptpy )
245+ scriptpy = Path (self .target ) / "Scripts" # std Scripts of python
246+ os .makedirs (scriptpy , exist_ok = True )
256247 if not list (names ) == names :
257248 my_list = [f for f in os .listdir (scriptpy ) if "." not in f and f .startswith (names )]
258249 else :
259250 my_list = names
260251 for name in my_list :
261- if Path ( scriptpy ) .is_dir () and (Path ( scriptpy ) / name ).is_file ():
252+ if scriptpy .is_dir () and (scriptpy / name ).is_file ():
262253 if (
263- not (Path ( scriptpy ) / (name + ".exe" )).is_file ()
264- and not (Path ( scriptpy ) / (name + ".bat" )).is_file ()
254+ not (scriptpy / (name + ".exe" )).is_file ()
255+ and not (scriptpy / (name + ".bat" )).is_file ()
265256 ):
266- with open (Path ( scriptpy ) / (name + ".bat" ), "w" ) as fd :
257+ with open (scriptpy / (name + ".bat" ), "w" ) as fd :
267258 fd .write (contents )
268259 fd .close ()
269260
@@ -272,9 +263,7 @@ def handle_specific_packages(self, package):
272263 if package .name .lower () in ("pyqt4" , "pyqt5" , "pyside2" ):
273264 # Qt configuration file (where to find Qt)
274265 name = "qt.conf"
275- contents = """[Paths]
276- Prefix = .
277- Binaries = ."""
266+ contents = """[Paths]\n Prefix = .\n Binaries = ."""
278267 self .create_file (package , name , str (Path ("Lib" ) / "site-packages" / package .name ), contents )
279268 self .create_file (package , name , "." , contents .replace ("." , f"./Lib/site-packages/{ package .name } " ))
280269 # pyuic script
@@ -296,13 +285,14 @@ def handle_specific_packages(self, package):
296285 for dirname in ("Loader" , "port_v2" , "port_v3" ):
297286 self .create_file (package , "__init__.py" , str (Path (uic_path ) / dirname ), "" )
298287
299- def _print (self , package , action ):
300- """Print package-related action text (e.g. 'Installing')"""
288+
289+ def _print (self , package : Package , action : str ):
290+ """Print package-related action text."""
301291 text = f"{ action } { package .name } { package .version } "
302292 if self .verbose :
303293 utils .print_box (text )
304294 else :
305- print (" " + text + " ..." , end = " " )
295+ print (f " { text } ..." , end = " " )
306296
307297 def _print_done (self ):
308298 """Print OK at the end of a process"""
@@ -318,6 +308,7 @@ def uninstall(self, package):
318308 subprocess .call ([this_exec , "-m" , "pip" , "uninstall" , package .name , "-y" ], cwd = self .target )
319309 self ._print_done ()
320310
311+
321312 def install_bdist_direct (self , package , install_options = None ):
322313 """Install a package directly !"""
323314 self ._print (package ,f"Installing { package .fname .split ('.' )[- 1 ]} " )
@@ -335,18 +326,22 @@ def install_bdist_direct(self, package, install_options=None):
335326 package = Package (fname )
336327 self ._print_done ()
337328
338- def install_script (self , script , install_options = None ):
329+
330+ def install_script (self , script : str , install_options : list [str ] = None ): # Type hint install_options
331+ """Install a script using pip."""
339332 try :
340333 fname = utils .do_script (
341334 script ,
342335 python_exe = utils .get_python_executable (self .target ), # PyPy3 !
343336 verbose = self .verbose ,
344337 install_options = install_options ,
345338 )
346- except RuntimeError :
339+ except RuntimeError as e : # Catch specific RuntimeError
347340 if not self .verbose :
348341 print ("Failed!" )
349- raise
342+ raise # Re-raise if not verbose
343+ else :
344+ print (f"Script installation failed: { e } " ) # Print error if verbose
350345
351346
352347def main (test = False ):
@@ -415,7 +410,7 @@ def main(test=False):
415410 const = True ,
416411 default = False ,
417412 help = f"list packages matching the given [optionnal] package expression: wppm -ls, wppm -ls pand" ,
418- )
413+ )
419414 parser .add_argument (
420415 "-p" ,
421416 dest = "pipdown" ,
@@ -473,9 +468,8 @@ def main(test=False):
473468 )
474469 args = parser .parse_args ()
475470 targetpython = None
476- if args .target and not args .target == sys .prefix :
477- targetpython = args .target if args .target [- 4 :] == '.exe' else str (Path (args .target ) / 'python.exe' )
478- # print(targetpython.resolve() to check)
471+ if args .target and args .target != sys .prefix :
472+ targetpython = args .target if args .target .lower ().endswith ('.exe' ) else str (Path (args .target ) / 'python.exe' )
479473 if args .install and args .uninstall :
480474 raise RuntimeError ("Incompatible arguments: --install and --uninstall" )
481475 if args .registerWinPython and args .unregisterWinPython :
@@ -492,51 +486,49 @@ def main(test=False):
492486 sys .exit ()
493487 elif args .list :
494488 pip = piptree .PipData (targetpython )
495- todo = [l for l in pip .pip_list (full = True ) if bool (re .search (args .fname , l [0 ])) ]
496- titles = [['Package' , 'Version' , 'Summary' ],['_' * max (x , 6 ) for x in utils .columns_width (todo )]]
489+ todo = [l for l in pip .pip_list (full = True ) if bool (re .search (args .fname , l [0 ]))]
490+ titles = [['Package' , 'Version' , 'Summary' ], ['_' * max (x , 6 ) for x in utils .columns_width (todo )]]
497491 listed = utils .formatted_list (titles + todo , max_width = 70 )
498492 for p in listed :
499493 print (* p )
500494 sys .exit ()
501495 elif args .all :
502496 pip = piptree .PipData (targetpython )
503- todo = [l for l in pip .pip_list (full = True ) if bool (re .search (args .fname , l [0 ])) ]
497+ todo = [l for l in pip .pip_list (full = True ) if bool (re .search (args .fname , l [0 ]))]
504498 for l in todo :
505499 # print(pip.distro[l[0]])
506500 title = f"** Package: { l [0 ]} **"
507- print ("\n " + "*" * len (title ), f"\n { title } " , "\n " + "*" * len (title ) )
501+ print ("\n " + "*" * len (title ), f"\n { title } " , "\n " + "*" * len (title ))
508502 for key , value in pip .raw [l [0 ]].items ():
509503 rawtext = json .dumps (value , indent = 2 , ensure_ascii = False )
510504 lines = [l for l in rawtext .split (r"\n" ) if len (l .strip ()) > 2 ]
511- if key .lower () != 'description' or args .verbose == True :
505+ if key .lower () != 'description' or args .verbose :
512506 print (f"{ key } : " , "\n " .join (lines ).replace ('"' , "" ))
513- sys .exit ()
507+ sys .exit ()
514508 if args .registerWinPython :
515509 print (registerWinPythonHelp )
516510 if utils .is_python_distribution (args .target ):
517511 dist = Distribution (args .target )
518512 else :
519- raise WindowsError (f"Invalid Python distribution { args .target } " )
513+ raise OSError (f"Invalid Python distribution { args .target } " )
520514 print (f"registering { args .target } " )
521515 print ("continue ? Y/N" )
522516 theAnswer = input ()
523517 if theAnswer == "Y" :
524518 from winpython import associate
525-
526519 associate .register (dist .target , verbose = args .verbose )
527520 sys .exit ()
528521 if args .unregisterWinPython :
529522 print (unregisterWinPythonHelp )
530523 if utils .is_python_distribution (args .target ):
531524 dist = Distribution (args .target )
532525 else :
533- raise WindowsError (f"Invalid Python distribution { args .target } " )
526+ raise OSError (f"Invalid Python distribution { args .target } " )
534527 print (f"unregistering { args .target } " )
535528 print ("continue ? Y/N" )
536529 theAnswer = input ()
537530 if theAnswer == "Y" :
538531 from winpython import associate
539-
540532 associate .unregister (dist .target , verbose = args .verbose )
541533 sys .exit ()
542534 elif not args .install and not args .uninstall :
@@ -546,7 +538,7 @@ def main(test=False):
546538 parser .print_help ()
547539 sys .exit ()
548540 else :
549- raise IOError (f"File not found: { args .fname } " )
541+ raise FileNotFoundError (f"File not found: { args .fname } " )
550542 if utils .is_python_distribution (args .target ):
551543 dist = Distribution (args .target , verbose = True )
552544 try :
@@ -560,7 +552,7 @@ def main(test=False):
560552 except NotImplementedError :
561553 raise RuntimeError ("Package is not (yet) supported by WPPM" )
562554 else :
563- raise WindowsError (f"Invalid Python distribution { args .target } " )
555+ raise OSError (f"Invalid Python distribution { args .target } " )
564556
565557
566558if __name__ == "__main__" :
0 commit comments