From e1fcad9bfcc35f2f9a866b7e90ec27028dc88b79 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 23 Sep 2025 14:33:50 +0100 Subject: [PATCH 1/2] Also preserve '/Scripts' when upgrading an install. Fixes #170 --- src/manage/install_command.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/manage/install_command.py b/src/manage/install_command.py index eae2cc7..5f953cd 100644 --- a/src/manage/install_command.py +++ b/src/manage/install_command.py @@ -458,6 +458,14 @@ def _download_one(cmd, source, install, download_dir, *, must_copy=False): return package +def _should_preserve_on_upgrade(cmd, root, path): + if path.match("site-packages"): + return True + if path.parent == root and path.match("Scripts"): + return True + return False + + def _preserve_site(cmd, root): if not root.is_dir(): return None @@ -473,12 +481,12 @@ def _preserve_site(cmd, root): state = [] i = 0 dirs = [root] - root = root.with_name(f"_{root.name}") - root.mkdir(parents=True, exist_ok=True) + target_root = root.with_name(f"_{root.name}") + target_root.mkdir(parents=True, exist_ok=True) while dirs: - if dirs[0].match("site-packages"): + if _should_preserve_on_upgrade(cmd, root, dirs[0]): while True: - target = root / str(i) + target = target_root / str(i) i += 1 try: unlink(target) @@ -487,7 +495,8 @@ def _preserve_site(cmd, root): break except OSError: LOGGER.verbose("Failed to remove %s.", target) - LOGGER.info("Preserving %s during update as %s.", dirs[0], target) + LOGGER.info("Preserving %s during update.", dirs[0].relative_to(root)) + LOGGER.verbose("Moving %s to %s", dirs[0], target) try: dirs[0].rename(target) except OSError: @@ -498,8 +507,8 @@ def _preserve_site(cmd, root): else: dirs.extend(d for d in dirs[0].iterdir() if d.is_dir()) dirs.pop(0) - # Append None, root last so that root gets cleaned up after restore is done - state.append((None, root)) + # Append None, target_root last to clean up after restore is done + state.append((None, target_root)) return state @@ -520,7 +529,7 @@ def _restore_site(cmd, state): except KeyboardInterrupt: break continue - LOGGER.info("Restoring %s from %s after update.", dest, src) + LOGGER.verbose("Restoring %s from %s after update.", dest, src) try: for i in src.iterdir(): if not i.is_dir() and not i.is_file(): @@ -534,6 +543,7 @@ def _restore_site(cmd, state): LOGGER.verbose("Restoring %s to %s", i, d) d.parent.mkdir(parents=True, exist_ok=True) i.rename(d) + LOGGER.info("Restored %s", dest.name) except OSError: LOGGER.warn("Failed to restore %s during update.", dest) LOGGER.verbose("TRACEBACK", exc_info=True) From bf7004d6b08288cfcd5d02fdc8fb93b6f032b041 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 23 Sep 2025 19:43:19 +0100 Subject: [PATCH 2/2] Handle stray value errors --- src/manage/install_command.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/manage/install_command.py b/src/manage/install_command.py index 5f953cd..eaff783 100644 --- a/src/manage/install_command.py +++ b/src/manage/install_command.py @@ -495,7 +495,11 @@ def _preserve_site(cmd, root): break except OSError: LOGGER.verbose("Failed to remove %s.", target) - LOGGER.info("Preserving %s during update.", dirs[0].relative_to(root)) + try: + LOGGER.info("Preserving %s during update.", dirs[0].relative_to(root)) + except ValueError: + # Just in case a directory goes weird, so we don't break + LOGGER.verbose(exc_info=True) LOGGER.verbose("Moving %s to %s", dirs[0], target) try: dirs[0].rename(target)