refactor(store): polish the LocalStore rename retry from #4358 - #4359
Merged
Merged
Conversation
…ers#4358 Follow-up cleanup to the retry landed in zarr-developers#4358, no behaviour change: - drop the leading 0.0 sentinel, the bare `last_error` annotation, and the possibly-unbound re-raise from `_move_with_retry`; the final attempt now runs after the loop and propagates its own traceback - correct the comment on ERROR_ACCESS_DENIED: Windows also reports it for destinations that will never clear (directory, read-only, ACL), so those surface the same error after the bounded delay - align the changelog with the code: the exclusive path is routed through the retry, only FileExistsError is excluded; rename the fragment to the PR number and describe zarr-developers#3522 as mitigated, since a second process holding the destination open past the budget still fails - tests record `time.sleep` instead of sleeping (~1.2 s per run before, now instant) and assert the actual delay schedule; the closure test double becomes a small callable class, removing four type-ignores - add a regression test for `_atomic_write` onto an existing directory Assisted-by: ClaudeCode:claude-fable-5-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4359 +/- ##
==========================================
- Coverage 94.34% 94.34% -0.01%
==========================================
Files 92 92
Lines 12950 12948 -2
==========================================
- Hits 12218 12216 -2
Misses 732 732
🚀 New features to boost your workflow:
|
d-v-b
marked this pull request as ready for review
September 15, 2026 17:43
d-v-b
added a commit
to d-v-b/zarr-python
that referenced
this pull request
Sep 16, 2026
Resolves the import conflict in tests/test_store/test_local.py: main's LocalStore rename-retry tests (zarr-developers#4358, zarr-developers#4359) import `time`, this branch's event-loop detector imports `dataclass`/`field` and typing names. Both are kept. The retry helpers main added (`_move_with_retry`, `_atomic_write`) are only reached from `_put`, which this branch already runs under `asyncio.to_thread`, so their `time.sleep` blocks a worker thread rather than the event loop and the detector stays quiet. Main's new tests are module-level, so the class-scoped autouse detector does not apply to them. Assisted-by: ClaudeCode:claude-opus-5 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
AI-written polish on top of #4358. Fixes the changelog entry, removes some double backticks, removes a 0.0 delay, adds test improvements.
🤖 AI text below 🤖
Follow-up cleanup to #4358 (thanks @Tomatokeftes). No behaviour change on any platform; the retry set, attempt count, and delay schedule are identical.
What changed
_move_with_retry: dropped the leading0.0sentinel in_RETRY_DELAYS, the barelast_error: OSErrorannotation, and the post-loopraise last_error. The final attempt now runs after the loop and propagates its own traceback, which also removes a possibly-unbound path if the delay tuple were ever emptied.Comment on
ERROR_ACCESS_DENIED: the original said code 5 means "could not be superseded right now, as opposed to a permission problem that will not clear". MoveFileEx also returns 5 when the destination is an existing directory, a read-only file, or ACL-denied, so those cases go through the retry too and surface the samePermissionErrorafter the bounded delay. The comment now says so. The measurement narrative moved out of the source; it lives in the #4358 description.Changelog: the fragment said the retry "deliberately does not cover the
exclusivepath", but the code routes that path through the retry and only excludesFileExistsError(183). Reworded to match. Also renamed to4358.bugfix.mdperchanges/README.md, and #3522 is now described as mitigated rather than fixed, since a second process holding the destination open past the retry budget still fails.Tests:
time.sleepis recorded via asleepsfixture (thetest_latency.pyidiom) instead of sleeping for real, which cuts ~1.2 s per run and lets the tests assert the actual delay schedule. The closure test double is now a small callable class, removing fourtype: ignore[attr-defined]. Addedtest_atomic_write_onto_directoryas a regression guard for the directory-destination case, checking both the error and that no.partialfile is left behind.Checks
pytest tests/test_store/test_local.py: 109 passedTODO
docs/user-guide/*.md— n/achanges/(existing fragment edited)🤖 Generated with Claude Code