Skip to content

Prune dead code and collapse single-implementation abstractions - #759

Merged
d-chambers merged 4 commits into
devfrom
dev-simplify-prune
Jul 20, 2026
Merged

Prune dead code and collapse single-implementation abstractions#759
d-chambers merged 4 commits into
devfrom
dev-simplify-prune

Conversation

@d-chambers

@d-chambers d-chambers commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Description

Follow-up cleanup on dev from a review pass over the branch. No behavior changes intended — this removes production-dead code and two abstraction layers that were left with a single implementation after the DuckDB/Parquet backends were removed.

Dead code

  • PatchFileSummary — superseded by PatchSummary; referenced only by its own export in dascore/io/__init__.py and its tests. Removing it also retires twelve now-unused imports in io/core.py.
  • _load_patch_summary — only reachable from tests. FileResolver._read / _resolve_read_spool is the path actually used for summary-driven reloads.
  • coord_summary_from_data and _normalize_coord_summary_dtype — neither is called anywhere in dascore. The docs note that mentioned them (along with an already-stale coord_summary_from_full_data) now points at get_coord(...).to_summary().
  • index_query_buffer config option — lost its only reader when the PyTables indexer was replaced by SQLite. The numpy import and arbitrary_types_allowed in config.py existed solely to support its timedelta64 field and go with it.
  • A stale TODO in PatchSummary.flat_dump — the indexer has since been redone, and the flat path/file_format/file_version names are the contract, so the comment is now a statement of intent rather than pending work.

Duplication in spool.py

  • The viz property duplicated the _namespace_attr_errors["viz"] message and shadowed it, making the dict entry unreachable. NamespaceOwner.__getattr__ now serves the identical message (verified).
  • get_patch_names was bound on both BaseSpool and Spool; the second binding is redundant.

Abstractions with one implementation

  • AbstractIndexBackend merged into SQLIndexBackend. The ABC declared twelve methods that SQLIndexBackend already implements in full, so it was pure redeclaration. SQLIndexBackend is now the base; close() joins its per-engine abstract hooks (it was the one method the ABC declared that SQLIndexBackend does not implement). The package now exports SQLIndexBackend in place of AbstractIndexBackend.
  • BaseDialect/SQLiteDialect collapse into one concrete SQLiteDialect. Note the dialect deliberately stays its own module rather than folding into lite.py as originally sketched: query imports the dialect and backend imports query, so a dialect living in lite (or backend) would close a query → lite → backend → query import cycle. That constraint is now recorded in the module docstring.
  • dascore.io.indexer merged into dascore.io.index.indexer, resolving the near-identical module names. AbstractIndexer was a self-described placeholder with a single subclass; Spool.from_directory now tests for DBDirectoryIndexer directly, and the index-map helpers move alongside their only caller.

Net: −483 / +92 lines, one module deleted.

Testing

  • Full suite: 7719 passed, 214 skipped, 2 xfailed.
  • Doctests (pytest dascore --doctest-modules, run separately in CI): 142 passed.
  • pre-commit clean on all changed files.
  • Spot-verified the three behavioral edges by hand: spool.viz still raises AttributeError with the same message, get_patch_names still resolves on a concrete Spool, and Spool.from_directory(indexer) still takes the indexer branch.

Changelog

  • removed breaking: dascore.io.PatchFileSummary; use PatchSummary and get_coord(...).to_summary().
  • removed breaking: the dascore.io.indexer module with its AbstractIndexer and DirectoryIndexer; use dascore.io.index.indexer.DBDirectoryIndexer.
  • changed: AbstractIndexBackend is gone — SQLIndexBackend is the base every backend implements.

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes.
  • documented the new feature with docstrings and/or appropriate doc page.
  • included tests. See testing guidelines.
  • added the "ready_for_review" tag once the PR is ready to be reviewed.

Summary by CodeRabbit

  • API Changes
    • Removed the public PatchFileSummary model (and corresponding IO re-export); PatchSummary is the replacement.
    • Removed legacy coordinate-summary helper(s) and the index_query_buffer config option.
    • Dropped the Spool.viz accessor and updated directory indexing behavior for SQL-backed indexers.
    • Consolidated index backend/dialect exposure around SQLIndexBackend/SQLite dialect.
  • Bug Fixes
    • Made read-only index filenames deterministic across sessions.
    • Hardened index-map caching for atomic updates and safer filesystem/index-cache handling.
    • Empty spools now raise MissingPatchError consistently.
  • Documentation
    • Updated changelog and coordinate-summary docs to emphasize to_summary().
  • Tests
    • Expanded indexer regression coverage for stability/atomicity and adjusted removed internal tests.

@d-chambers d-chambers added the ready_for_review PR is ready for review label Jul 19, 2026
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@d-chambers, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 seconds

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8b8a254c-82b9-4713-aa3d-15d342146f24

📥 Commits

Reviewing files that changed from the base of the PR and between 56dce5b and c6092da.

📒 Files selected for processing (17)
  • dascore/config.py
  • dascore/core/spool.py
  • dascore/core/summary.py
  • dascore/io/__init__.py
  • dascore/io/core.py
  • dascore/io/index/__init__.py
  • dascore/io/index/backend.py
  • dascore/io/index/dialect.py
  • dascore/io/index/indexer.py
  • dascore/io/index/query.py
  • dascore/io/indexer.py
  • docs/changelog.qmd
  • docs/notes/coordinate_internals.qmd
  • tests/test_core/test_patch.py
  • tests/test_io/test_index/test_index_edge_cases.py
  • tests/test_io/test_indexer.py
  • tests/test_io/test_io_core.py
📝 Walkthrough

Walkthrough

This PR removes deprecated summary, configuration, and indexer abstractions; consolidates SQLite dialect and backend interfaces; updates directory indexer integration; and revises exports, documentation, and affected tests.

Changes

Public API and summary cleanup

Layer / File(s) Summary
Summary and IO API removal
dascore/core/summary.py, dascore/io/core.py, dascore/io/__init__.py, docs/notes/..., tests/test_core/..., tests/test_io/...
Removes PatchFileSummary and legacy coordinate-summary helpers, documents coord.to_summary() as canonical, and updates related tests.
Configuration and spool cleanup
dascore/config.py, dascore/core/spool.py
Removes the NumPy-based index_query_buffer, obsolete spool symbols, and updates directory construction for DBDirectoryIndexer.

SQLite index consolidation

Layer / File(s) Summary
SQLite backend and dialect contracts
dascore/io/index/__init__.py, dascore/io/index/backend.py, dascore/io/index/dialect.py
Replaces the abstract backend hierarchy with SQLIndexBackend, centralizes SQLite SQL generation in SQLiteDialect, and updates exports and typing.
Query and directory indexer integration
dascore/io/index/query.py, dascore/io/index/indexer.py, docs/changelog.qmd, tests/test_io/test_indexer.py
Updates query annotations to SQLiteDialect, adds local JSON-backed index-map handling, stable index naming, resilient directory scanning, and documents the index API consolidation.

Possibly related PRs

  • DASDAE/dascore#645: Directly relates to removal of index_query_buffer and its indexer behavior.
  • DASDAE/dascore#751: Updates the same SQLite index backend, dialect, query, and spool integration areas.

Suggested labels: IO, spool

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the cleanup and abstraction-collapse changes in the PR.
Description check ✅ Passed The description matches the template with Description, Testing, and Checklist sections; only the issue reference is missing.
Docstring Coverage ✅ Passed Docstring coverage is 92.86% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev-simplify-prune

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added bug Something isn't working CI continuous integration documentation Improvements or additions to documentation IO Work for reading/writing different formats patch related to Patch class spool related to Spool class transform Related to transform operations labels Jul 19, 2026
@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.28571% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.96%. Comparing base (8cc0a07) to head (c6092da).

Files with missing lines Patch % Lines
dascore/io/index/indexer.py 85.00% 6 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##              dev     #759       +/-   ##
===========================================
+ Coverage   48.92%   99.96%   +51.04%     
===========================================
  Files         164      163        -1     
  Lines       17191    17087      -104     
===========================================
+ Hits         8410    17081     +8671     
+ Misses       8781        6     -8775     
Flag Coverage Δ
network 48.76% <35.71%> (-0.16%) ⬇️
unittests 99.95% <89.28%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@d-chambers
d-chambers force-pushed the dev-simplify-prune branch from 74815c4 to a272837 Compare July 19, 2026 19:02
@coderabbitai coderabbitai Bot added the proc Related to processing module label Jul 19, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
dascore/io/index/indexer.py (2)

252-253: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Handle potential file deletions during directory traversal.

Files yielded by _iter_filesystem might be deleted before path.stat() is called (a Time-of-Check to Time-of-Use race), which would raise a FileNotFoundError and crash the entire index update.

Wrap the stat() call in a try-except block to gracefully ignore files that disappear during the scan.

🛡️ Proposed fix to handle concurrent deletions
-            stat = path.stat()
-            files[self._rel(path)] = (stat.st_mtime_ns, stat.st_size, path)
+            try:
+                stat = path.stat()
+            except OSError:
+                continue
+            files[self._rel(path)] = (stat.st_mtime_ns, stat.st_size, path)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dascore/io/index/indexer.py` around lines 252 - 253, Update the file-statting
logic in the filesystem indexing flow around `_iter_filesystem` to catch
`FileNotFoundError` from `path.stat()`, skip the disappeared file, and continue
scanning the remaining paths without adding an entry to `files`.

155-157: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a stable hash for persistent file names.

In Python 3, hash() is randomized per process for strings and Path objects. Consequently, abs(hash(self.path)) will generate a completely different file name in every Python session. If the index map is ever cleared—or checked concurrently by two processes before being updated—a new SQLite file will be created, permanently orphaning the old one.

Use a stable hash like hashlib.sha256 to ensure the file name remains consistent across processes.

🛠️ Proposed fix using a stable hash
         if not directory_writable(self.path):
-            name = f"_dascore_index_{abs(hash(self.path))}.sqlite3"
+            digest = hashlib.sha256(str(self.path).encode()).hexdigest()[:16]
+            name = f"_dascore_index_{digest}.sqlite3"
             index_path = self.index_map_path.parent / name
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dascore/io/index/indexer.py` around lines 155 - 157, Replace the
process-randomized hash used in the Indexer path fallback with a deterministic
hashlib.sha256 digest of a stable path representation, and use that digest in
the SQLite filename. Update the relevant imports and preserve the existing
directory_writable and index_map_path behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dascore/io/index/indexer.py`:
- Around line 58-65: Update _update_index_map to write the serialized index data
to a temporary file in the same directory as cache_path, then atomically replace
cache_path with that file using Path.replace(). Preserve the existing directory
creation, merge behavior, and returned data while ensuring readers never observe
a partially written cache.
- Around line 34-40: Remove the `@cache` decorator from _get_index_map so each
invocation reads a fresh index map from disk before updates. After removing it,
delete the functools.cache import if no other code uses it, while preserving
_get_index_map’s existing return behavior and mutable-dictionary contract.

---

Outside diff comments:
In `@dascore/io/index/indexer.py`:
- Around line 252-253: Update the file-statting logic in the filesystem indexing
flow around `_iter_filesystem` to catch `FileNotFoundError` from `path.stat()`,
skip the disappeared file, and continue scanning the remaining paths without
adding an entry to `files`.
- Around line 155-157: Replace the process-randomized hash used in the Indexer
path fallback with a deterministic hashlib.sha256 digest of a stable path
representation, and use that digest in the SQLite filename. Update the relevant
imports and preserve the existing directory_writable and index_map_path
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e94b887c-7f22-49ae-aafe-2b97a67552c3

📥 Commits

Reviewing files that changed from the base of the PR and between 74815c4 and a272837.

📒 Files selected for processing (15)
  • dascore/config.py
  • dascore/core/spool.py
  • dascore/core/summary.py
  • dascore/io/__init__.py
  • dascore/io/core.py
  • dascore/io/index/__init__.py
  • dascore/io/index/backend.py
  • dascore/io/index/dialect.py
  • dascore/io/index/indexer.py
  • dascore/io/index/query.py
  • dascore/io/indexer.py
  • docs/changelog.qmd
  • docs/notes/coordinate_internals.qmd
  • tests/test_core/test_patch.py
  • tests/test_io/test_io_core.py
💤 Files with no reviewable changes (3)
  • dascore/io/init.py
  • dascore/io/indexer.py
  • dascore/config.py
🚧 Files skipped from review as they are similar to previous changes (10)
  • dascore/io/index/init.py
  • docs/notes/coordinate_internals.qmd
  • docs/changelog.qmd
  • tests/test_io/test_io_core.py
  • dascore/core/summary.py
  • dascore/core/spool.py
  • dascore/io/index/dialect.py
  • dascore/io/index/backend.py
  • dascore/io/core.py
  • dascore/io/index/query.py

Comment thread dascore/io/index/indexer.py Outdated
Comment thread dascore/io/index/indexer.py
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown

✅ Documentation built:
👉 Download
Note: You must be logged in to github and a DASDAE member to access the link.

@d-chambers
d-chambers force-pushed the dev-simplify-prune branch from a272837 to e51b6cb Compare July 19, 2026 19:47
@coderabbitai coderabbitai Bot added codex and removed documentation Improvements or additions to documentation patch related to Patch class transform Related to transform operations CI continuous integration bug Something isn't working proc Related to processing module labels Jul 19, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
dascore/io/index/indexer.py (2)

96-101: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Prevent FileNotFoundError in concurrent index rebuilds.

If multiple processes encounter an invalid index version simultaneously, one may execute the unlink before another, causing a FileNotFoundError when the trailing process attempts to unlink the already deleted file.

Adding missing_ok=True closes this race window and improves stability under concurrent access.

🔒️ Proposed fix
         except InvalidIndexVersionError:
             # The index is a disposable cache and the file already
             # identified itself as a dascore spool index of another
             # schema version; rebuild it rather than asking the user to.
-            self.index_path.unlink()
+            self.index_path.unlink(missing_ok=True)
             self._backend = get_backend(self.index_path)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dascore/io/index/indexer.py` around lines 96 - 101, Update the
InvalidIndexVersionError recovery block in the index initialization flow to call
self.index_path.unlink with missing_ok=True, allowing concurrent rebuilds to
proceed when another process has already removed the cache file. Keep the
existing backend recreation behavior unchanged.

155-157: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use a stable hash instead of Python's built-in hash() for filenames.

Python's built-in hash() for strings and paths uses a randomized seed per interpreter session (introduced in Python 3.3). If two processes initialize the same read-only directory concurrently, they will generate completely different filenames. This leads to orphaned SQLite databases that permanently leak disk space and fails to reliably reuse the cache across process restarts.

Use a deterministic hash function like hashlib.sha256 to ensure the same directory always maps to the exact same filename.

🐛 Proposed fix
         if not directory_writable(self.path):
-            name = f"_dascore_index_{abs(hash(self.path))}.sqlite3"
+            hash_hex = hashlib.sha256(str(self.path).encode()).hexdigest()[:16]
+            name = f"_dascore_index_{hash_hex}.sqlite3"
             index_path = self.index_map_path.parent / name
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dascore/io/index/indexer.py` around lines 155 - 157, Replace the built-in
hash used in the directory_writable fallback within the index path construction
with a deterministic hashlib-based digest of self.path. Preserve the existing
filename pattern and SQLite suffix while ensuring identical paths produce the
same filename across processes and interpreter restarts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@dascore/io/index/indexer.py`:
- Around line 96-101: Update the InvalidIndexVersionError recovery block in the
index initialization flow to call self.index_path.unlink with missing_ok=True,
allowing concurrent rebuilds to proceed when another process has already removed
the cache file. Keep the existing backend recreation behavior unchanged.
- Around line 155-157: Replace the built-in hash used in the directory_writable
fallback within the index path construction with a deterministic hashlib-based
digest of self.path. Preserve the existing filename pattern and SQLite suffix
while ensuring identical paths produce the same filename across processes and
interpreter restarts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 199c4c65-6d0a-486e-b180-8df068303e59

📥 Commits

Reviewing files that changed from the base of the PR and between a272837 and e51b6cb.

📒 Files selected for processing (15)
  • dascore/config.py
  • dascore/core/spool.py
  • dascore/core/summary.py
  • dascore/io/__init__.py
  • dascore/io/core.py
  • dascore/io/index/__init__.py
  • dascore/io/index/backend.py
  • dascore/io/index/dialect.py
  • dascore/io/index/indexer.py
  • dascore/io/index/query.py
  • dascore/io/indexer.py
  • docs/changelog.qmd
  • docs/notes/coordinate_internals.qmd
  • tests/test_core/test_patch.py
  • tests/test_io/test_io_core.py
💤 Files with no reviewable changes (3)
  • dascore/io/indexer.py
  • dascore/config.py
  • dascore/io/init.py
🚧 Files skipped from review as they are similar to previous changes (10)
  • dascore/io/index/init.py
  • docs/notes/coordinate_internals.qmd
  • docs/changelog.qmd
  • dascore/io/index/dialect.py
  • dascore/core/spool.py
  • dascore/io/core.py
  • dascore/io/index/backend.py
  • tests/test_io/test_io_core.py
  • dascore/core/summary.py
  • tests/test_core/test_patch.py

@coderabbitai coderabbitai Bot removed the codex label Jul 20, 2026
Removes production-dead code and two abstraction layers left over from
the DuckDB/Parquet backend removal.

Dead code:
- PatchFileSummary, superseded by PatchSummary and referenced only by
  its own export and tests.
- _load_patch_summary, only reachable from tests; FileResolver._read is
  the path actually used for summary-driven reloads.
- coord_summary_from_data and _normalize_coord_summary_dtype, neither
  called anywhere in dascore.
- The index_query_buffer config option, which lost its only reader when
  the PyTables indexer was replaced. The numpy import and
  arbitrary_types_allowed in config.py existed solely for its
  timedelta64 field and go with it.
- A stale TODO in PatchSummary.flat_dump; the indexer has been redone
  and the flat names are the contract.

Duplication in spool.py:
- The viz property duplicated the _namespace_attr_errors entry and
  shadowed it; the mixin now serves the same message.
- get_patch_names was bound on both BaseSpool and Spool.

Abstractions with one implementation:
- AbstractIndexBackend declared the interface SQLIndexBackend already
  implements in full; SQLIndexBackend is now the base, keeping close()
  among its per-engine abstract hooks.
- BaseDialect/SQLiteDialect collapse into one concrete SQLiteDialect.
  The dialect stays its own module: query needs it and backend needs
  query, so moving it into either would close an import cycle.
- dascore.io.indexer merges into dascore.io.index.indexer, resolving the
  confusing near-identical module names. AbstractIndexer was a
  self-described placeholder with one subclass; Spool.from_directory now
  tests for DBDirectoryIndexer directly.
Removing _load_patch_summary also removed the only test reaching the
empty-spool MissingPatchError guard in _select_patch_from_spool, which
that helper had been exercising indirectly. The guard is retained
production code, so cover it directly instead.

Parametrized over a present and absent source id: the guard must fire
before any identity matching in both cases.
Fixes concurrency and correctness issues in the index-map helpers, all
pre-existing but surfaced by moving them into io/index/indexer.py.

- Drop @cache from _get_index_map. Caching the map per process meant a
  later _update_index_map would dump this process's stale copy over
  entries another process had added. Reads now hit disk fresh.
- Write _update_index_map atomically (temp file + os.replace). A direct
  write is not atomic; a concurrent reader hitting a half-written file
  raises JSONDecodeError, which _get_index_map treats as corruption and
  deletes the whole map (#508). replace() means readers only ever see a
  complete file.
- Name the read-only-directory fallback index from a sha256 digest of
  the path, not abs(hash(path)). str/Path hashing is randomized per
  process (PYTHONHASHSEED), so hash() named a new index every session
  and orphaned the previous one.
- Skip files that disappear between the directory walk and their stat()
  instead of letting a concurrent deletion crash the whole update.

Regression tests pin all four; each was confirmed to fail when its fix
is reverted.
@d-chambers
d-chambers force-pushed the dev-simplify-prune branch from 56dce5b to 4244f04 Compare July 20, 2026 05:27
This test asserts no ResourceWarning fires while it garbage-collects its
own spool. But gc.collect() also reaps collectable garbage left by
earlier tests, and a leaked open SQLite connection emits a
ResourceWarning when collected -- which the recording window then caught
and blamed on this test. The result was a full-suite-order failure
(reproducible on dev independent of this branch; #754 merged with this
same test_code job red) that passed in isolation and within its own
file.

Drain pending garbage with a gc.collect() before opening the recording
window, so only the spool under test is collected inside it. The test's
intent -- this spool's backend connection closes silently on GC -- is
unchanged.
@d-chambers
d-chambers merged commit d3d6520 into dev Jul 20, 2026
27 checks passed
@d-chambers
d-chambers deleted the dev-simplify-prune branch July 20, 2026 06:35
d-chambers added a commit that referenced this pull request Jul 20, 2026
Bring the branch up to date with dev (#749-#759) so CI runs against the
current tip before landing the master integration.
@coderabbitai coderabbitai Bot mentioned this pull request Aug 7, 2026
4 tasks
@d-chambers d-chambers removed the ready_for_review PR is ready for review label Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

IO Work for reading/writing different formats spool related to Spool class

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant