fix: preserve_in_zip replaces a stale archive member (#1414) - #1427
Merged
Conversation
`preserve_in_zip` only wrote a member that was absent from the search's zip, so a post-completion artifact that was rewritten on disk (an adapt-image cache invalidated by a changed dataset mask, PyAutoGalaxy#516) left the stale bytes in the archive. `restore()` deletes the output directory and re-extracts the zip, so the stale copy came back and the search missed its cache on every run rather than once. An existing member is now compared against the file on disk via the size and CRC in the zip's central directory — no decompression, and the file is read once in chunks — and replaced when it differs. `zipfile` cannot overwrite in place, so the replacement streams the other members into a temporary archive beside the original and swaps it in with `os.replace`; a failure part-way leaves the original intact. Byte-identical content stays a no-op, so the common resume path never pays for a rewrite. Rebuilding the archive from the output directory (as the search does at completion) is not usable here: with `remove_files` the directory holds only the file just written.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #1414.
AbstractPaths.preserve_in_zipadded a file to a completed search's.ziponly when that member was absent, so a post-completion artifact rewritten on disk left the stale bytes in the archive. Becauserestore()deletes the output directory and re-extracts the zip, the stale copy came back on the next resume — the case PyAutoGalaxy#516 creates, where a changed dataset mask invalidatesfiles/galaxy_images_snr.fitsand the images are recomputed. Correctness was never at risk (the stale cache is rejected on load), but such a search missed its cache on every run instead of once, repaying the max-log-likelihood-fit rebuild each time.An existing member is now compared against the file on disk and replaced when it differs:
file_sizeandCRCalready in the zip's central directory, with a chunk-streamedzlib.crc32over the file — the archived bytes are never decompressed and a large.fitscache is never read fully into memory.os.replaceis atomic) and swaps it in only once complete; a failure part-way leaves the original archive untouched. The replaced member'scompress_typeis carried over.Rebuilding the whole archive from the output directory (what the search itself does at completion via
zip_directory) is not usable here: withremove_files: truethe completed search's directory is deleted after zipping, so a later post-completion write recreates onlyfiles/<cache>and a full re-zip would truncate the archive to that one file.The issue's other-callers check:
autolens/analysis/result.pywrites its multiple-image-positions cache only when the loose file is absent, so it never depended on replacement — no change needed. The related config knobsforce_pickle_overwrite/force_visualize_overwritedo not overlap: they act on the search whosefit()is re-invoked, which always brackets the run withpaths.restore()(which deletes the zip) andpost_fit_output→zip_remove()(which rebuilds it wholesale).preserve_in_zipexists for the opposite seam — a write into an upstream completed search that is not fitted this run, whose zip is never rebuilt.API Changes
None — no signature changes.
AbstractPaths.preserve_in_zipgains behaviour: it now replaces an archived member whose content differs from the file on disk, where it previously left the archive untouched. Callers that write a changed artifact into a completed search'sfiles/folder get the refreshed copy afterrestore().See full details below.
Test Plan
pytest test_autofit/non_linear/paths/test_paths.py— 18 passedpytest test_autofit— 1559 passed, 1 skippedtest__preserve_in_zip__replaces_stale_memberfails on the pre-fix source (regression pinned), passes afterFull API Changes (for automation & release notes)
Changed Behaviour
autofit.non_linear.paths.abstract.AbstractPaths.preserve_in_zip(file_path)— an existing archive member whose content differs fromfile_pathis now replaced (archive rewritten atomically) instead of being silently skipped. Unchanged: no-op when the zip does not exist, plain append when the member is absent, and no-op when the member is byte-identical.Added (private)
autofit.non_linear.paths.abstract._matches_archived(info, file_path)— size + CRC comparison against the central directory.autofit.non_linear.paths.abstract._replace_zip_member(zip_path, arcname, file_path)— atomic member replacement via a temporary archive.Migration
Generated by the PyAutoLabs agent workflow.