Drive the CLI and GUI from one pipeline function (#1) - #4
Merged
Conversation
GUI runs left no run_info.json or config.used.yaml. The cause was duplication,
not a broken function: cli.run() and gui.workers.run_pipeline_job() were
near-identical copies of detect -> tables -> figures -> write, and only the CLI
copy went on to call write_provenance().
That also meant `flypad stats` silently degraded on GUI-produced directories.
It reuses the run's config.used.yaml when present and falls back to a
default-constructed Config() when absent, so it recomputed per_condition and
comparisons with default ci_level / statistic / facet_by and overwrote those
tables with numbers that need not match what the GUI displayed.
Both entry points now call pipeline.run_experiment(), which owns the sequence
including provenance; run_info.json records which drove it in `command`
("run" / "gui"). gui.JobResult is now an alias of pipeline.ExperimentResult, so
the GUI keeps its vocabulary while sharing the shape.
Tested for CLI/GUI consistency: the two produce the same file set, byte-identical
config.used.yaml, frame-equal tables, and run_info.json differing only in
`command` and `timestamp`. Those live in test_cli.py rather than test_gui.py
because test_gui.py skips wholesale without qtpy, which CI does not install —
they exercise run_experiment directly, which is what run_pipeline_job delegates
to. Removing the provenance call again fails five of them.
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 #1.
A run started from the GUI wrote its tables and figures but no
run_info.json/config.used.yaml, so GUI results directories were not self-describing. Reproduced onaf3eeb4 by calling the GUI's job function directly (it needs no Qt to run):
Cause
Duplication, not a broken function.
cli.run()andgui.workers.run_pipeline_job()werenear-identical copies of the same sequence — detect → tables → figures → write — and only
the CLI copy went on to call
write_provenance().write_provenancewas imported incli/app.pyand nowhere else in the package.Also fixed by this
flypad statsreuses the run'sconfig.used.yamlwhen present and falls back to adefault-constructed
Config()when absent, then overwritesper_conditionandcomparisonsin that directory. On a GUI-produced directory it therefore silentlydiscarded the run's settings.
Worth scoping honestly:
Config()and both presets agree on every fieldstatsactuallyuses (
stats.ci_level,n_permutations,statistic,plotting.facet_by), so a run witha stock config recomputed to the same numbers. It diverged only when an
experiment.yamloverrode one of those fields — but silently, either way.
The fix
pipeline.run_experiment()now owns the sequence, provenance included, and both entrypoints call it.
run_info.jsonrecords which one did, in itscommandfield(
"run"/"gui"); everything else about the directory is identical.cli.run()drops to a call plus its summary line.run_pipeline_job()becomes a Qt-free delegation taggingcommand="gui".gui.JobResultis now an alias of the newpipeline.ExperimentResult, so the GUI keepsits vocabulary and
gui/app.pyis untouched.flypad detectkeeps writing its own provenance: it writes a different subset of tableswith different extras, so routing it through the shared function would fit worse than the
duplication it removes.
Tests
Four consistency tests assert the CLI and GUI paths produce:
events/per_fly/per_condition/comparisons;config.used.yamland matchingconfig_hash;run_info.jsondiffering only incommandandtimestamp.Plus one covering the
flypad statsconsequence, and one intest_gui.pyfor the realGUI entry point.
They live in
test_cli.pyrather thantest_gui.pyon purpose:test_gui.pyskipswholesale without qtpy, which CI does not install, so consistency tests there would never
run on the runner. They exercise
run_experimentdirectly — exactly whatrun_pipeline_jobdelegates to.Verified they have teeth by neutering the
write_provenancecall again: five of themfail, including three of the consistency ones.
guiextra).UV_PROJECT_ENVIRONMENT+uv sync --frozen):ruff, ruff format, mypy and pytest all pass — 232 passed,
test_guiskipped for theabsent qtpy.
Docs
docs/tutorial.md§5 now states that the GUI andflypad runproduce identical resultsdirectories and what the
commandfield means.CHANGELOG.mdgains the entry under[Unreleased].No placeholders in this one — Closes #1 is literal, so it's ready to use as-is. The # placeholder is only in the issue comment draft.