33from .installs import get_matching_install_tags
44from .install_command import SHORTCUT_HANDLERS , update_all_shortcuts
55from .logging import LOGGER
6- from .pathutils import PurePath
6+ from .pathutils import Path , PurePath
77from .tagutils import tag_or_range
88
99
1010def _iterdir (p , only_files = False ):
1111 try :
1212 if only_files :
13- return [f for f in p .iterdir () if p .is_file ()]
14- return list (p .iterdir ())
13+ return [f for f in Path ( p ) .iterdir () if f .is_file ()]
14+ return list (Path ( p ) .iterdir ())
1515 except FileNotFoundError :
1616 LOGGER .debug ("Skipping %s because it does not exist" , p )
1717 return []
1818
1919
20+ def _do_purge_global_dir (global_dir , warn_msg , * , hive = None , subkey = "Environment" ):
21+ import os
22+ import winreg
23+
24+ if hive is None :
25+ hive = winreg .HKEY_CURRENT_USER
26+ try :
27+ with winreg .OpenKeyEx (hive , subkey ) as key :
28+ path , kind = winreg .QueryValueEx (key , "Path" )
29+ if kind not in (winreg .REG_SZ , winreg .REG_EXPAND_SZ ):
30+ raise ValueError ("Value kind is not a string" )
31+ except (OSError , ValueError ):
32+ LOGGER .debug ("Not removing global commands directory from PATH" , exc_info = True )
33+ else :
34+ LOGGER .debug ("Current PATH contains %s" , path )
35+ paths = path .split (";" )
36+ newpaths = []
37+ for p in paths :
38+ # We should expand entries here, but we only want to remove those
39+ # that we added ourselves (during firstrun), and we never use
40+ # environment variables. So even if the kind is REG_EXPAND_SZ, we
41+ # don't need to expand to find our own entry.
42+ #ep = os.path.expandvars(p) if kind == winreg.REG_EXPAND_SZ else p
43+ ep = p
44+ if PurePath (ep ).match (global_dir ):
45+ LOGGER .debug ("Removing from PATH: %s" , p )
46+ else :
47+ newpaths .append (p )
48+ if len (newpaths ) < len (paths ):
49+ newpath = ";" .join (newpaths )
50+ with winreg .CreateKeyEx (hive , subkey , access = winreg .KEY_READ | winreg .KEY_WRITE ) as key :
51+ path2 , kind2 = winreg .QueryValueEx (key , "Path" )
52+ if path2 == path and kind2 == kind :
53+ LOGGER .info ("Removing global commands directory from PATH" )
54+ LOGGER .debug ("New PATH contains %s" , newpath )
55+ winreg .SetValueEx (key , "Path" , 0 , kind , newpath )
56+ else :
57+ LOGGER .debug ("Not removing global commands directory from PATH "
58+ "because the registry changed while processing." )
59+
60+ try :
61+ from _native import broadcast_settings_change
62+ broadcast_settings_change ()
63+ except (ImportError , OSError ):
64+ LOGGER .debug ("Did not broadcast settings change notification" ,
65+ exc_info = True )
66+
67+ if not global_dir .is_dir ():
68+ return
69+ LOGGER .info ("Purging global commands from %s" , global_dir )
70+ for f in _iterdir (global_dir ):
71+ LOGGER .debug ("Purging %s" , f )
72+ rmtree (f , after_5s_warning = warn_msg )
73+
74+
2075def execute (cmd ):
2176 LOGGER .debug ("BEGIN uninstall_command.execute: %r" , cmd .args )
2277
@@ -31,28 +86,28 @@ def execute(cmd):
3186 cmd .tags = []
3287
3388 if cmd .purge :
34- if cmd .ask_yn ("Uninstall all runtimes?" ):
35- for i in installed :
36- LOGGER . info ( "Purging %s from %s" , i [ "display-name" ], i [ "prefix" ])
37- try :
38- rmtree (
39- i [ "prefix" ],
40- after_5s_warning = warn_msg . format ( i [ "display-name" ]),
41- remove_ext_first = ( "exe" , "dll" , "json" )
42- )
43- except FilesInUseError :
44- LOGGER . warn ( "Unable to purge %s because it is still in use." ,
45- i [ "display-name" ])
46- continue
47- LOGGER . info ( "Purging saved downloads from %s" , cmd . download_dir )
48- rmtree ( cmd . download_dir , after_5s_warning = warn_msg . format ( "cached downloads" ))
49- LOGGER .info ("Purging global commands from %s" , cmd .global_dir )
50- for f in _iterdir (cmd .global_dir ):
51- LOGGER . debug ( "Purging %s" , f )
52- rmtree ( f , after_5s_warning = warn_msg .format ("global commands" ))
53- LOGGER .info ("Purging all shortcuts" )
54- for _ , cleanup in SHORTCUT_HANDLERS .values ():
55- cleanup (cmd , [])
89+ if not cmd .ask_yn ("Uninstall all runtimes?" ):
90+ LOGGER . debug ( "END uninstall_command.execute" )
91+ return
92+ for i in installed :
93+ LOGGER . info ( "Purging %s from %s" , i [ "display-name" ], i [ "prefix" ])
94+ try :
95+ rmtree (
96+ i [ "prefix" ],
97+ after_5s_warning = warn_msg . format ( i [ "display-name" ]),
98+ remove_ext_first = ( "exe" , "dll" , "json" )
99+ )
100+ except FilesInUseError :
101+ LOGGER . warn ( "Unable to purge %s because it is still in use." ,
102+ i [ "display-name" ] )
103+ continue
104+ LOGGER .info ("Purging saved downloads from %s" , cmd .download_dir )
105+ rmtree (cmd .download_dir , after_5s_warning = warn_msg . format ( "cached downloads" ))
106+ # Purge global commands directory
107+ _do_purge_global_dir ( cmd . global_dir , warn_msg .format ("global commands" ))
108+ LOGGER .info ("Purging all shortcuts" )
109+ for _ , cleanup in SHORTCUT_HANDLERS .values ():
110+ cleanup (cmd , [])
56111 LOGGER .debug ("END uninstall_command.execute" )
57112 return
58113
0 commit comments