fix(state): serialise BoundWrapper.func by value when not importable by reference - #887
Open
pmrv wants to merge 2 commits into
Open
fix(state): serialise BoundWrapper.func by value when not importable by reference#887pmrv wants to merge 2 commits into
pmrv wants to merge 2 commits into
Conversation
…by reference BoundWrapper relied on default dataclass pickling, which references func by (module, qualname). That only resolves in a ProcessPoolExecutor worker under the 'fork' start method, where the worker inherits the parent's already- imported objects. Under 'spawn'/'forkserver' (default on macOS/Windows, and on Linux 3.14+) the worker is a fresh interpreter that must import func to find it, so anything defined in __main__, a notebook, or a local closure raised AttributeError/PicklingError and brought the pool down as BrokenProcessPool. __reduce__ now serialises func into a standalone bytes payload up front: plain pickle when func is importable by reference (unchanged, no cloudpickle needed), else cloudpickle by value with a clear error if cloudpickle isn't installed. Pre-serialising means the payload survives regardless of which pickler carries the outer BoundWrapper (stdlib pickle via ProcessPoolExecutor, or cloudpickle via SshCache/executorlib). Closes #840 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nhem4ftatpXhjwtxDnLLaV
…round-trip a closure test-minimum-deps CI resolves cloudpickle to its declared floor (2.0.0), which can't reconstruct even a bare closure's code object on this Python (TypeError: code() argument 13 must be str, not int) — the same class of cloudpickle/Python version-skew gap test_wrap_executor_cloudpickle_lock.py already skips around. Add the same defensive skip to the two new #840 tests so they report an environment limitation instead of failing outright. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nhem4ftatpXhjwtxDnLLaV
pmrv
added a commit
that referenced
this pull request
Aug 22, 2026
…for #840 (#888) Scheduled audit of `AGENTS.md` and its linked guides against the tracker state as of 2026-08-22. No code landed on `main` since the previous audit (#882), so the Quick Reference and Architecture claims are unchanged; the delta is issue-tracker state only. - Add the 2026-08-21 refactor cohort under open proposals: #883 (`_raw_sub_digests` hardcodes built-in `Digested` subtypes, breaking `gc()`/`count_reuses()` for `register_destructurer` extensions — the one correctness gap of the batch), #884 (sqlite URL handling hand-rolled twice and drifted on `~`-expansion), #885 (`XDG_CACHE_HOME` companion to #868), #886 (`benchmark_storage.py` five-op timing harness duplicated). - Record PR #887 in flight against bug #840 (`BoundWrapper.__reduce__` pre-serialises `func`: `pickle` by reference, `cloudpickle` by value). - Split the single Bugs/Feature-request paragraph into one-item-per-line sub-bullets so independent updates stop colliding in merges. Verified still open and accurately described: PRs #797, #837, #873-#878, #879, #881. --- _Generated by [Claude Code](https://claude.ai/code/session_01J6LqSMaWKWzDfF7hF51Syq)_ Co-authored-by: Claude <noreply@anthropic.com>
pmrv
added a commit
that referenced
this pull request
Aug 23, 2026
…theme (#889) Scheduled AGENTS.md audit, 2026-08-23. No code has landed on `main` since the 2026-08-21 audit (63a0584), and the issue/PR state recorded in `agents/DEVELOPING.md` is still current: in-flight PRs #873–#878, #881, #887, and #797 all remain open; no new issues since #883–#886. Spot-checks of the Quick Reference and Architecture claims against `src/` all pass (public `__all__`, `_lazy_default`/`_sticky_set`/`_hard_set`, `PreparedCall`/two-phase save, `register_storage`, `_CACHE_TEMPLATES`, `Runtime.cputime`/`systime`, module line counts quoted in #789/#832). `AGENTS.md` and `agents/USAGE.md` need no changes. One gap found: the performance theme names "pooled file handles in `bagofholding_file.py`" as a fix candidate without noting that draft PR #786 (open since 2026-07-23) already implements it, and PR #804 (open since 2026-07-31) — the benchmark-harness fix that removes the always-evicting `SizeLimitedCache(max_size=10)` config — was recorded nowhere. Both are now listed under the perf theme as check-before-duplicating entries, added as separate paragraphs to keep future edits conflict-free. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01H9uSJpX8SLJrtYbc2ZaSv5 --- _Generated by [Claude Code](https://claude.ai/code/session_01H9uSJpX8SLJrtYbc2ZaSv5)_ Co-authored-by: claude[bot] <claude[bot]@users.noreply.github.com>
pmrv
added a commit
that referenced
this pull request
Aug 28, 2026
…qlFile fsync cheap fix (#898) Weekly AGENTS.md/DEVELOPING.md audit. Only one change since the 2026-08-27 pass (#897): issue #625's perf audit was refreshed the same morning. - Update the perf-audit pointer in `agents/DEVELOPING.md` from "refreshed 2026-08-20" to 2026-08-27 and record the run's verdict (no new source-caused regressions; flagged rows are the chronic `BagOfHoldingH5File` per-op open cost or noise). - Record the still-open SQL-side hot spot the refreshed audit re-flags: `Sql` fsyncs once per key on `save`/`evict` because `_configure_sqlite_pragmas` (`src/fleche/storage/sql.py:190-227`) sets `journal_mode=WAL` but no `synchronous` pragma; the cheap fix (`PRAGMA synchronous=NORMAL`) has been flagged in every audit since 2026-05-07. Verified against the source — the function sets only `foreign_keys` and `journal_mode`. Everything else checked and current: no merges to `main` since #897; open PRs (#873, #874, #887, #892, #894, #896, #797, #786, #804) and issues (#893, #895, #625) are all already recorded. `eisenforschung/landau` was audited in the same pass and needs no update (nothing landed since its 2026-08-25 pass; in-flight PRs #391/#394/#395/#414/#422 unchanged; spot-checked claims hold). --- _Generated by [Claude Code](https://claude.ai/code/session_012SCLy9y7aUR7F44Q7UviGo)_ Co-authored-by: Claude <noreply@anthropic.com>
pmrv
added a commit
that referenced
this pull request
Aug 29, 2026
…racker items (#904) Scheduled AGENTS.md audit. Changes since the 2026-08-27 pass: - Release-state claims updated: PR #793 (two-phase save) and PR #843 (lock-free pickle-family backends) shipped in 0.22.0 on 2026-08-28 (release PR #837); the three "unreleased as of 2026-08-27" qualifiers are gone. Noted that `fix(query)` #894 is an ancestor of the 0.22.0 tag (verified via `git tag --contains c82c691`) even though the generated changelog entry omits it. - PR #894 (`latest()`/`oldest()` raise `ValueError` when no matching call carries `Runtime` metadata, `IndexError` on empty), PR #892 (gc mid-sweep guards + 441-case digest product collapse, suite 1738 → 1299), and docs-audit PRs #873/#874 moved from the in-flight section to decisions landed; the in-flight section is now a one-liner pointing at the five PRs still open (#887, #804, #797, #786, #523), each covered under its theme. - New tracker items recorded: the 2026-08-28 refactor cohort #899–#902, bug #903 (`put()` `filelock.Timeout` fatality on ≤0.21.2, dedup-concentrated lock contention; resolved for pickle-family by 0.22.0's atomic rename, bagofholding multi-bag still exposed per #893), and the #895 resolution (documented limitation per the 2026-08-28 maintainer comment). #900 cross-referenced at the `pypi-publish.yml` line in the workflows list. - The #893 bullet's `lock_timeout` mitigation sentence rephrased to the current state: the `FutureWarning` drop covers only pickle-family configs, a bagofholding `values.lock_timeout` still works on 0.22.0 (checked against `config.py:466`). - USAGE.md: query section states the `latest()`/`oldest()` `ValueError`/`IndexError` contract (verified against `query.py:149-188`). landau's AGENTS.md was audited in the same pass and left unchanged — no code or tracker movement since its 2026-08-24 update (only docs commits on main; issues #423–#425 and PRs #422/#414/#395/#394/#391 unchanged). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_014myhh6xKWC2VhQeB92mGSn --- _Generated by [Claude Code](https://claude.ai/code/session_014myhh6xKWC2VhQeB92mGSn)_ Co-authored-by: Claude <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.
Closes #840.
Problem
BoundWrapperrelied on default dataclass pickling forfunc, whichreferences it by
(module, qualname). That only resolves in aProcessPoolExecutorworker under theforkstart method, where the workerinherits the parent's already-imported objects. Under
spawn/forkserver(default on macOS/Windows, and on Linux 3.14+) the worker is a fresh
interpreter that must import
functo find it, so anything defined in__main__, a notebook, or a local closure raisedAttributeError/PicklingErrorat load time and brought the whole pool downas
BrokenProcessPool. There was no error at the submit site — the firstsign of trouble was a dead worker.
Fix
BoundWrapper.__reduce__now serialisesfuncinto a standalonebytespayload up front, instead of leaving it for the outer pickler to serialise:
funcis picklable by reference (the common case — a module-level or@fleche-decorated function), it's pickled with plainpickle, exactly asbefore. No
cloudpickledependency is introduced for this path.cloudpickle, serialisingfuncby value, witha clear
TypeErrorifcloudpickleisn't installed.Pre-serialising the payload means it survives regardless of which pickler
carries the outer
BoundWrapper— stdlibpickle(ProcessPoolExecutor,any start method) or
cloudpickle(SshCache,executorlib) alike.Testing
tests/unit/fleche/test_bound_wrapper.py: new cases for a locally-defined(non-importable) function pickling by value via
cloudpickle, the clearerror when
cloudpickleis missing, and confirming a module-level functionstill round-trips without
cloudpickleat all.tests/regression/test_issue_840.py: end-to-end reproduction — a localclosure shipped through a real
spawn-contextProcessPoolExecutor.Verified this fails with
AttributeError: Can't pickle local object ...on
mainand passes with this fix.pytest tests/— 1724 passed (the only failures are 10pre-existing notebook-execution tests that fail in this sandbox because the
jupyterconsole script isn't installed, unrelated to this change).ty check src/— clean.Generated by Claude Code