@@ -136,7 +136,7 @@ def _update_reg_values(key, data, install, exclude=set()):
136136 winreg .SetValueEx (key , k , None , v_kind , v )
137137
138138
139- def _is_tag_managed (company_key , tag_name ):
139+ def _is_tag_managed (company_key , tag_name , * , creating = False ):
140140 try :
141141 tag = winreg .OpenKey (company_key , tag_name )
142142 except FileNotFoundError :
@@ -147,7 +147,70 @@ def _is_tag_managed(company_key, tag_name):
147147 return True
148148 except FileNotFoundError :
149149 pass
150- return False
150+ if not creating :
151+ return False
152+
153+ # gh-11: Clean up invalid entries from other installers
154+ # It's highly likely that our old MSI installer wouldn't properly remove
155+ # its registry keys on uninstall, so we'll check for the InstallPath
156+ # subkey and if it's missing, back up the key and then use it ourselves.
157+ try :
158+ with _reg_open (tag , "InstallPath" ) as subkey :
159+ if subkey :
160+ # if InstallPath refers to a directory that exists,
161+ # leave it alone.
162+ p = winreg .QueryValueEx (subkey , None )[0 ]
163+ if p and Path (p ).exists ():
164+ return False
165+ except FileNotFoundError :
166+ pass
167+ except OSError :
168+ # If we couldn't access it for some reason, leave it alone.
169+ return False
170+
171+ # Existing key is almost certainly not valid, so let's rename it,
172+ # warn the user, and then continue.
173+ LOGGER .debug ("Key %s appears invalid, so moving it and taking it for this "
174+ "new install" , tag_name )
175+ try :
176+ from _native import reg_rename_key
177+ except ImportError :
178+ LOGGER .debug ("Failed to import reg_rename_key" , exc_info = True )
179+ return False
180+
181+ parent_name , _ , orig_name = tag_name .replace ("/" , "\\ " ).rpartition ("\\ " )
182+ with _reg_open (company_key , parent_name , writable = True ) as tag :
183+ if not tag :
184+ # Key is no longer there, so we can use it
185+ return True
186+ for i in range (1000 ):
187+ try :
188+ new_name = f"{ orig_name } .{ i } "
189+ # Raises standard PermissionError (5) if new_name exists
190+ reg_rename_key (tag .handle , orig_name , new_name )
191+ LOGGER .warn ("An existing registry key for %s was renamed to %s "
192+ "because it appeared to be invalid. If this is "
193+ "correct, the registry key can be safely deleted. "
194+ "To avoid this in future, ensure that the "
195+ "InstallPath key refers to a valid path." ,
196+ tag_name , new_name )
197+ break
198+ except FileNotFoundError :
199+ LOGGER .debug ("Original key disappeared, so we will claim it" )
200+ return True
201+ except PermissionError :
202+ LOGGER .debug ("Failed to rename %s to %s" , orig_name , new_name ,
203+ exc_info = True )
204+ # Continue, hopefully the next new_name is available
205+ except OSError :
206+ LOGGER .debug ("Unexpected error while renaming %s to %s" ,
207+ orig_name , new_name , exc_info = True )
208+ raise
209+ else :
210+ LOGGER .warn ("Attempted to clean up invalid registry key %s but "
211+ "failed after too many attempts." , tag_name );
212+ return False
213+ return True
151214
152215
153216def _split_root (root_name ):
@@ -166,7 +229,7 @@ def _split_root(root_name):
166229def update_registry (root_name , install , data ):
167230 hive , name = _split_root (root_name )
168231 with winreg .CreateKey (hive , name ) as root :
169- if _is_tag_managed (root , data ["Key" ]):
232+ if _is_tag_managed (root , data ["Key" ], creating = True ):
170233 with winreg .CreateKey (root , data ["Key" ]) as tag :
171234 LOGGER .debug ("Creating/updating %s\\ %s" , root_name , data ["Key" ])
172235 winreg .SetValueEx (tag , "ManagedByPyManager" , None , winreg .REG_DWORD , 1 )
0 commit comments