@@ -35,9 +35,8 @@ def check_app_alias(cmd):
3535 LOGGER .debug ("Failed to get current package name." , exc_info = True )
3636 pkg = None
3737 if not pkg :
38- pkg = ""
39- #LOGGER.debug("Check skipped: MSI install can't do this check")
40- #return True
38+ LOGGER .debug ("Check skipped: MSI install can't do this check" )
39+ return "skip"
4140 LOGGER .debug ("Checking for %s" , pkg )
4241 root = Path (os .environ ["LocalAppData" ]) / "Microsoft/WindowsApps"
4342 for name in ["py.exe" , "pyw.exe" , "python.exe" , "pythonw.exe" , "python3.exe" , "pymanager.exe" ]:
@@ -75,7 +74,7 @@ def check_py_on_path(cmd):
7574 from _native import get_current_package , read_alias_package
7675 if not get_current_package ():
7776 LOGGER .debug ("Check skipped: MSI install can't do this check" )
78- return True
77+ return "skip"
7978 for p in os .environ ["PATH" ].split (";" ):
8079 if not p :
8180 continue
@@ -96,23 +95,89 @@ def check_py_on_path(cmd):
9695def check_global_dir (cmd ):
9796 LOGGER .debug ("Checking for global dir on PATH" )
9897 if not cmd .global_dir :
99- LOGGER .debug ("Check passed : global dir is not configured" )
100- return True
98+ LOGGER .debug ("Check skipped : global dir is not configured" )
99+ return "skip"
101100 for p in os .environ ["PATH" ].split (";" ):
102101 if not p :
103102 continue
104103 if Path (p ).absolute ().match (cmd .global_dir ):
105104 LOGGER .debug ("Check passed: %s is on PATH" , p )
106105 return True
106+ # In case user has updated their registry but not the terminal
107+ import winreg
108+ try :
109+ with winreg .OpenKeyEx (winreg .HKEY_CURRENT_USER , "Environment" ) as key :
110+ path , kind = winreg .QueryValueEx (key , "Path" )
111+ LOGGER .debug ("Current registry path: %s" , path )
112+ if kind == winreg .REG_EXPAND_SZ :
113+ path = os .path .expandvars (path )
114+ elif kind != winreg .REG_SZ :
115+ LOGGER .debug ("Check skipped: PATH registry key is not a string." )
116+ return "skip"
117+ for p in path .split (";" ):
118+ if not p :
119+ continue
120+ if Path (p ).absolute ().match (cmd .global_dir ):
121+ LOGGER .debug ("Check skipped: %s will be on PATH after restart" , p )
122+ return True
123+ except Exception :
124+ LOGGER .debug ("Failed to read PATH setting from registry" , exc_info = True )
107125 LOGGER .debug ("Check failed: %s not found in PATH" , cmd .global_dir )
108126 return False
109127
110128
111129def do_global_dir_on_path (cmd ):
112130 import winreg
113- LOGGER .debug ("Adding %s to PATH" , cmd .global_dir )
114- # TODO: Add to PATH (correctly!)
115- # TODO: Send notification
131+ added = notified = False
132+ try :
133+ LOGGER .debug ("Adding %s to PATH" , cmd .global_dir )
134+ with winreg .OpenKeyEx (winreg .HKEY_CURRENT_USER , "Environment" ) as key :
135+ initial , kind = winreg .QueryValueEx (key , "Path" )
136+ LOGGER .debug ("Initial path: %s" , initial )
137+ if kind not in (winreg .REG_SZ , winreg .REG_EXPAND_SZ ) or not isinstance (initial , str ):
138+ LOGGER .debug ("Value kind is %s and not REG_[EXPAND_]SZ. Aborting." )
139+ return
140+ for p in initial .split (";" ):
141+ if not p :
142+ continue
143+ if p .casefold () == str (cmd .global_dir ).casefold ():
144+ LOGGER .debug ("Path is already found." )
145+ return
146+ newpath = initial + (";" if initial else "" ) + str (Path (cmd .global_dir ).absolute ())
147+ LOGGER .debug ("New path: %s" , newpath )
148+ # Expand the value and ensure we are found
149+ for p in os .path .expandvars (newpath ).split (";" ):
150+ if not p :
151+ continue
152+ if p .casefold () == str (cmd .global_dir ).casefold ():
153+ LOGGER .debug ("Path is added successfully" )
154+ break
155+ else :
156+ return
157+
158+ with winreg .CreateKeyEx (winreg .HKEY_CURRENT_USER , "Environment" ,
159+ access = winreg .KEY_READ | winreg .KEY_WRITE ) as key :
160+ initial2 , kind2 = winreg .QueryValueEx (key , "Path" )
161+ if initial2 != initial or kind2 != kind :
162+ LOGGER .debug ("PATH has changed while we were working. Aborting." )
163+ return
164+ winreg .SetValueEx (key , "Path" , 0 , kind , newpath )
165+ added = True
166+
167+ from _native import broadcast_settings_change
168+ broadcast_settings_change ()
169+ notified = True
170+ except Exception :
171+ LOGGER .debug ("Failed to update PATH environment variable" , exc_info = True )
172+ finally :
173+ if added and not notified :
174+ LOGGER .warn ("Failed to notify of PATH environment variable change." )
175+ LOGGER .info ("You may need to sign out or restart to see the changes." )
176+ elif not added :
177+ LOGGER .warn ("Failed to update PATH environment variable successfully." )
178+ LOGGER .info ("You may add it yourself by opening 'Edit environment "
179+ "variables' and adding this directory to 'PATH': !B!%s!W!" ,
180+ cmd .global_dir )
116181
117182
118183def check_any_install (cmd ):
@@ -160,7 +225,8 @@ def first_run(cmd):
160225 welcome ()
161226
162227 if cmd .check_app_alias :
163- if not check_app_alias (cmd ):
228+ r = check_app_alias (cmd )
229+ if not r :
164230 welcome ()
165231 LOGGER .warn ("Your app execution alias settings are configured to launch "
166232 "other commands besides 'py' and 'python'." )
@@ -173,7 +239,10 @@ def first_run(cmd):
173239 ):
174240 os .startfile ("ms-settings:advanced-apps" )
175241 elif cmd .explicit :
176- LOGGER .info ("Checked app execution aliases" )
242+ if r == "skip" :
243+ LOGGER .info ("Skipped app execution aliases check" )
244+ else :
245+ LOGGER .info ("Checked app execution aliases" )
177246
178247 if cmd .check_long_paths :
179248 if not check_long_paths (cmd ):
@@ -194,7 +263,8 @@ def first_run(cmd):
194263 LOGGER .info ("Checked system long paths setting" )
195264
196265 if cmd .check_py_on_path :
197- if not check_py_on_path (cmd ):
266+ r = check_py_on_path (cmd )
267+ if not r :
198268 welcome ()
199269 LOGGER .warn ("The legacy 'py' command is still installed." )
200270 LOGGER .info ("This may interfere with launching the new 'py' command, "
@@ -205,26 +275,33 @@ def first_run(cmd):
205275 ):
206276 os .startfile ("ms-settings:appsfeatures" )
207277 elif cmd .explicit :
208- LOGGER .info ("Checked PATH for legacy 'py' command" )
278+ if r == "skip" :
279+ LOGGER .info ("Skipped check for legacy 'py' command" )
280+ else :
281+ LOGGER .info ("Checked PATH for legacy 'py' command" )
209282
210283 if cmd .check_global_dir :
211- if not check_global_dir (cmd ):
284+ r = check_global_dir (cmd )
285+ if not r :
212286 welcome ()
213287 LOGGER .warn ("The directory for versioned Python commands is not configured." )
214288 LOGGER .info ("This will prevent commands like !B!python3.14.exe!W! "
215289 "working, but will not affect the !B!python!W! or "
216290 "!B!py!W! commands (for example, !B!py -V:3.14!W!)." )
217291 LOGGER .info ("We can add the directory to PATH now, but you will need "
218- "to restart your terminal to see the change, and may need "
219- "to manually edit your environment variables if you later "
220- "decide to remove the entry." )
292+ "to restart your terminal to see the change, and must "
293+ "manually edit environment variables to later remove the "
294+ "entry." )
221295 if (
222296 cmd .confirm and
223297 not cmd .ask_ny ("Add commands directory to your PATH now?" )
224298 ):
225299 do_global_dir_on_path (cmd )
226300 elif cmd .explicit :
227- LOGGER .info ("Checked PATH for versioned commands directory" )
301+ if r == "skip" :
302+ LOGGER .info ("Skipped check for commands directory on PATH" )
303+ else :
304+ LOGGER .info ("Checked PATH for versioned commands directory" )
228305
229306 # This check must be last, because 'do_install' will exit the program.
230307 if cmd .check_any_install :
0 commit comments