Conversation
There was a problem hiding this comment.
Pull request overview
Addresses issue #322 by improving install/upgrade unwind behavior so preserved site-packages directories are restored even when an install fails mid-flight.
Changes:
- Wrap
_install_one()’s remove/extract/metadata steps in atry/finallyto restore preserved site directories during failure unwind. - Refactor metadata preparation from
_sanitise_install()into_finalize_metadata()(and update tests accordingly). - Minor formatting cleanup.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/manage/install_command.py |
Adds failure-unwind handling around install steps; introduces _remove_existing() + _finalize_metadata() refactor. |
tests/test_install_command.py |
Renames/update tests to call the new _finalize_metadata() helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try: | ||
| if not cmd.repair: | ||
| _remove_existing(dest) | ||
|
|
||
| with ProgressPrinter("Extracting", maxwidth=CONSOLE_MAX_WIDTH) as on_progress: | ||
| extract_package(package, dest, on_progress=on_progress, repair=cmd.repair) | ||
|
|
||
| if target: | ||
| unlink( | ||
| metadata_dest, | ||
| "Removing metadata from the install is taking some time. Please " + | ||
| "continue to wait, or press Ctrl+C to abort." | ||
| ) | ||
| raise | ||
| else: | ||
| _finalize_metadata(cmd, install, metadata_dest) | ||
|
|
||
| with ProgressPrinter("Extracting", maxwidth=CONSOLE_MAX_WIDTH) as on_progress: | ||
| extract_package(package, dest, on_progress=on_progress, repair=cmd.repair) | ||
| LOGGER.debug("Write __install__.json to %s", dest) | ||
| with open(metadata_dest, "w", encoding="utf-8") as f: | ||
| json.dump(install, f, default=str) | ||
|
|
||
| if target: | ||
| unlink( | ||
| dest / "__install__.json", | ||
| "Removing metadata from the install is taking some time. Please " + | ||
| "continue to wait, or press Ctrl+C to abort." | ||
| finally: | ||
| # May be letting an exception bubble out here, so we'll handle and log | ||
| # here rather than letting any new ones leave. | ||
| try: | ||
| if dest.is_dir(): | ||
| # Install may be broken at this point, but we'll put site back anyway | ||
| _restore_site(cmd, preserved_site) | ||
| else: | ||
| # Install is certainly broken, but we don't want to delete user files | ||
| # Just warn, until we come up with a better idea | ||
| LOGGER.warn("This runtime has been lost due to an error, you will " | ||
| "need to reinstall.") | ||
| except Exception: | ||
| LOGGER.warn("Unexpected failure finalizing install. See log file for details") | ||
| LOGGER.verbose("TRACEBACK", exc_info=True) | ||
|
|
There was a problem hiding this comment.
The new unwind logic that restores preserved site-packages on install failure (the try/finally around _remove_existing/extract_package) isn't covered by tests. Consider adding a regression test that monkeypatches extract_package() (or rmtree()/atomic_unlink()) to raise after _preserve_site() has moved directories, and then asserts _restore_site() ran (e.g., preserved dir removed and files restored).
Fixes #322