Make --no-status and --quiet suppress the progress bar - #135
Merged
Merged
Conversation
`main()` replaced `printer.DEFAULT_PRINTER` with a printer configured from `--no-status` and `--quiet`, but `tree.py`, `json.py`, and `levenshtein.py` bound the name at import time and never saw the replacement. Those modules kept drawing progress bars from the original printer, whose `quiet` is `False`. Add `get_default_printer()` and `set_default_printer()` to `printer.py`, and have the three modules resolve the printer at call time so the result no longer depends on import order. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> # Conflicts: # graphtage/json.py
The bug lives in an import-time name binding, so an in-process test would not reach it. Run the command line in a subprocess and assert that `--no-status` and `--quiet` leave stderr empty while the default run still draws its bar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 9, 2026
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 #133
Problem
--no-statusand--quietdo not suppress the "Diffing" progress bar, contrary to whatREADME.mddocuments.Root cause
main()builds a printer from the command line options and replaces the module attribute:Three modules bind the name at import time instead of reading it through the module:
graphtage/tree.py—from .printer import DEFAULT_PRINTER, Printergraphtage/json.py—from .printer import DEFAULT_PRINTER, Fore, Printergraphtage/levenshtein.py—from .printer import DEFAULT_PRINTERRebinding
printermodule.DEFAULT_PRINTERleaves those names pointing at the printer that was current when each module was first imported, whosequietisFalse. Their sixtqdmcall sites therefore draw progress bars whatever the flags say. The bar in the issue's reproducer comes fromtree.py.Approach
printer.pygainsget_default_printer()andset_default_printer(), and the three modules call the accessor at the point of use rather than binding the name.main()installs its printer throughset_default_printer().The accessor resolves the printer when the progress bar is created, so the result no longer depends on which module Python imported first. That is what makes this different from a fix that only works today: any future module can call
get_default_printer()without having to reason about import order, and adding anotherfrom .printer import DEFAULT_PRINTERis now a visible departure from the convention rather than an invisible one.DEFAULT_PRINTERstays as the module attribute and remains the single source of truth, soprinter.DEFAULT_PRINTER(used indocs/library.rst) keeps working.set_default_printer()gives the mutation a documented home instead of leaving it as a bare assignment inmain().I considered plumbing the printer through explicitly.
BuildOptionsalready carries aprinterattribute, which would cover the two build-time bars injson.py, but the diff-time bars intree.pyandlevenshtein.pysit onTreeNode.diff()and insideEditDistance.tighten_bounds(). Threading a printer to those would change theTreeNodeandEditsignatures across the package, which is out of proportion to the bug.levenshtein.pyreads the printer once into a local and uses it for both thequietguard and thetqdmcall, so the guard and the bar can no longer disagree.Verification
The reproducer from the issue, on merged master (06e0417) and on this branch, with stderr redirected to a file:
graphtage big1.json big2.jsongraphtage --no-status big1.json big2.jsongraphtage --quiet big1.json big2.jsonDefault output is byte-for-byte unchanged.
Because tqdm is created with
disable=Falserather thandisable=None, the bars are drawn whether or not stderr is a terminal, so a redirected run is not enough on its own to show that the default still works. I also ran the three cases with stdout and stderr each attached to a real pty sized 100x40:--no-status--quietstdout was identical (23302 bytes) in all six runs.
Tests
test/test_progress.pyadds four tests. Three run the command line in a subprocess, which is what the bug requires: the stale binding is established while the module is imported, so an in-process test would reuse whichever printer an earlier test had installed. The fourth asserts that all three modules observeset_default_printer(), which fails if anyone reintroduces an import-time binding.Against merged master with the source changes reverted, three of the four fail:
The one that passes is
test_progress_is_shown_by_default, which is correct: this change does not alter default behavior.With the fix applied, all four pass. Full suite: 123 passed on Python 3.14 and on Python 3.8.
ruff checkreports the same 27 pre-existing findings on the touched files before and after, and none on the new test file.flake8 --select=E9,F63,F7,F82is clean andcd docs && make htmlsucceeds with the same five pre-existing warnings.Interaction with #130
This branch is rebased on master after #130, #129, and #131 merged. #130's changes are orthogonal: it moved
colorama.init()intoenable_ansi_support()and changedNullWriter.isatty()to returnFalse, neither of which touches howDEFAULT_PRINTERis looked up. I re-confirmed the reproducer against merged master before finishing the fix; #130 did not incidentally address it. The rebase produced one conflict, injson.py, where #131 restructured the sameListNode/UnorderedListNodebranch that holds one of thetqdmcalls; it is resolved to keep both changes.🤖 Generated with Claude Code