Repository navigation
Port the pileup, cut-site tables and the memory planner — Closes #3 - #8
Draft
conradbzura wants to merge 3 commits into
Draft
conradbzura wants to merge 3 commits into
conradbzura wants to merge 3 commits into
Conversation
Somewhere for the ported data path to land. No logic yet, so that a later failure is attributable to the port rather than to the scaffolding it lands on. Follows abdenlab/giql, the nearest sibling and quadrat's own dependency: src layout, hatchling, ruff at line-length 89, four-space indentation, PEP 735 dependency groups. Python floor is 3.11 rather than cherimoya's 3.10, because polars-bio, polars and giql are all gated there and the pileup is not an optional part of what this package is for. Runtime dependencies shear from cherimoya's sixteen to six, with the pileup engine behind a `pileup` extra. Every one of those imports is already lazy inside a function body upstream, so a caller that streams windows over tables it already has never pays for them. Two defects in cherimoya's manifest are corrected rather than carried: pyfaidx is imported by production code but declared only in a dev group, and cherimoya.workers is missing from the explicit package list, so it never ships in a built distribution. Hatchling's src discovery makes the second unrepeatable. The ruff select is pinned rather than left to the default. Ruff 0.17 enables UP, SIM, FURB and RUF by default, and UP032 alone fires on cherimoya's `str.format` style throughout -- so the default would demand a style migration inside the change that moves the code, which is the one thing that makes a port unreviewable. giql resolves from the public commit at the tip of its cherimoya-mvp branch. The datafusion-bio target is not on PyPI and cherimoya pins a path outside its own tree, which a published package cannot inherit. That commit's library code is identical to what cherimoya builds against; the only delta is benchmark material outside src. The fixture layer is the substantive half. Cherimoya has no data-path fixtures in conftest -- every one is module-local, and the FASTA, BAM and BED quintet is duplicated five ways differing only in chromosome sizes and window geometry. Porting that duplication would multiply it, so the builders take the geometry as arguments and the five sets collapse onto one factory. Cut-site builders arrive with the writer they need, in the pileup slice. The cache directory is pointed at a fresh temporary directory at import time, before anything imports the package, because default_cache_dir reads it per call: set later, the first reader would still see the user's own cache, and a surviving entry would satisfy a test written to exercise a cold build.
Review roles, authoring guides and accumulated review documents are local working state, untracked here as they are in the sibling projects. The review history is versioned by its own repository under .sdlc; the guides are config rather than a project artifact.
Ports cherimoya's pileup across eight modules, split along seams that already existed in the 2,652-line module it came from. Output is byte-identical: the same two alignments through cherimoya and through quadrat produce the same Parquet, per contig, at one contig batch and at three, manifests included. The split resolves a cycle rather than imposing one. Upstream, the planner and the SQL layer import from each other inside function bodies because the module graph would not otherwise admit them. Here plan.py is a leaf, sql.py and alignments.py depend on it, run.py depends on plan, and the four lazy imports that existed to tolerate the cycle are gone -- which is the test of whether the seam was real. Three names had to change because the polars-bio session is process-global. Its table names carry a quadrat_ prefix rather than a cherimoya_ one, and the cache directory variable follows, so both packages can be imported into one process during the transition. That same globalness is why a worker runs one pileup at a time, and the reasoning travels with the names. Two things deliberately do not change. CUT_SITE_SUFFIX and MANIFEST_VERSION are cherimoya's, so a table either package writes is readable by the other; the alternative is re-piling every corpus to move between them. And `import quadrat` still pulls in none of polars, polars-bio, torch, giql or pyfaidx -- every heavy import stayed inside the function body that needs it, which is what makes the pileup extra optional rather than nominal. The wool backend is deferred rather than ported. It dispatches to workers on other machines and is the only part of the pileup needing a dependency outside this package. BACKENDS ships as serial and process, and asking for wool raises NotImplementedError naming the alternative, because a caller migrating a working configuration needs to know it is deferred and not that it never existed. Tests: 34 of cherimoya's 38 pileup tests and 8 of its 30 giql_io tests. The four dropped from each drive the cherimoya CLI. The other 18 giql_io tests cover locus loading and extraction and belong to a later slice, not here -- the earlier estimate of 26 counted tests leaving cherimoya rather than tests arriving in this module. Two ported tests needed adjusting, both for the two-backend contract rather than for the port: one asserted on an error message listing three backends, and one iterated a hardcoded tuple of them, which now reads BACKENDS so the wool backend is picked up when it lands. Also fixes a defect in the test builders from the previous commit. write_random_bam placed every read on the first contig, because the branch meant to spread them indexed the contig list by a value that was zero whenever that branch was taken. It survived review because the builder's own test only checked that reads came back sorted, so a corpus confined to one contig looked correct. The parity run found it: the pileup wrote one per-contig table where it should have written three. There is now a test asserting reads reach every contig, which is the assertion that was missing.
conradbzura
force-pushed
the
quadrat-2-package-skeleton
branch
from
October 23, 2026 21:36
b7dd054 to
f8edaff
Compare
conradbzura
force-pushed
the
quadrat-3-pileup
branch
from
October 23, 2026 21:36
30a3345 to
2b53c89
Compare
conradbzura
force-pushed
the
quadrat-2-package-skeleton
branch
6 times, most recently
from
October 28, 2026 18:25
142c822 to
f00a79e
Compare
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
Port cherimoya's pileup across eight modules, split along seams that already existed in the 2,652-line module it came from. Output is byte-identical: the same two alignments through cherimoya and through quadrat produce the same Parquet, per contig, at one contig batch and at three, manifests included.
The split resolves a cycle rather than imposing one. Upstream, the planner and the SQL layer import from each other inside function bodies because the module graph would not otherwise admit them. Here
plan.pyis a leaf,sql.pyandalignments.pydepend on it,run.pydepends onplan, and the four lazy imports that existed to tolerate the cycle are gone — which is the test of whether the seam was real.Based on
quadrat-2-package-skeletonrather thanmain, since it builds on the packaging in #7. GitHub retargets this tomainwhen that merges.Closes #3
Proposed changes
Module split
alignments.pyreads contigs, indexes and contig batches;tables.pyowns the cut-site format and its atomic writer;pileup/sql.pyholds the aggregate;pileup/plan.pythe memory model;pileup/run.pythe backends;pileup/__init__.pythe entry point._engine.pyandreference.pycarry the session and the reference reader.Renames that are behaviour
Rename the polars-bio table names and the cache variable to a
quadrat_prefix. The session is process-global, so both packages must be importable into one process during the transition. That same globalness is why a worker runs one pileup at a time, and the reasoning travels with the names.What deliberately does not change
Keep
CUT_SITE_SUFFIXandMANIFEST_VERSIONas cherimoya's, so a table either package writes is readable by the other. Keep every heavy import inside the function body that needs it:import quadratpulls in none of polars, polars-bio, torch, giql or pyfaidx.Deferred backend
Ship
BACKENDSas serial and process. The wool backend dispatches to workers on other machines and is the only part of the pileup needing a dependency outside this package; asking for it raisesNotImplementedErrornaming the alternative, because a caller migrating a working configuration needs to know it is deferred rather than that it never existed.Fix carried from #7
write_random_bamplaced every read on the first contig — the branch meant to spread them indexed the contig list by a value that was zero whenever that branch was taken. The builder's own test only checked sortedness, so a one-contig corpus looked correct; the parity run exposed it. Add the assertion that was missing.Test cases
43 tests: 34 ported from cherimoya's
test_pileup.py, 8 from itstest_giql_io.py, 1 new. Summarized by area rather than per test.test_pileupplan_pileupis called across input sizestest_pileuptest_pileuppileup_manyassigns batch countstest_pileuptest_pileuppileup_manyis calledtest_pileuppileup_manyis calledtest_pileup_iotest_pileup_iopileup_alignmentsrunstest_pileup_iotest_pileup_iocut_site_contigslists ittest_pileup_ioEXPLAINis inspectedtest_builderswrite_random_bamplaces them