Skip to content

Refresh the library, builder, printing, and how-it-works docs - #145

Merged
ESultanik merged 4 commits into
masterfrom
docs-library-refresh
Sep 9, 2026
Merged

ESultanik merged 4 commits into
masterfrom
docs-library-refresh

Conversation

@ESultanik

Copy link
Copy Markdown
Collaborator

Refreshes the four prose pages that document Graphtage as a library. The last new prose in docs/ landed 2024-01-07, and docs/howitworks.rst and docs/printing.rst had not changed since May 2020.

What was broken

docs/library.rst

  • Every output block was stale. PR Select optimal Levenshtein paths in EditDistance #132 changed the edit tie-break order to put removals before insertions, so get_all_edits now yields the StringEdit before the Remove, the JSON key renders as ~~foo~~++bar++, and the YAML key likewise. The last element of the pydiff example is now a single Replace (4 -> "four") rather than an insert and remove pair, and the custom-class example renders ba~~z~~++k++.
  • A stray >>> from_node.diff(to_node) line used two names the session never defines. The session binds from_tree and to_tree, and the real call was the next line. Pasting the block raised NameError.
  • :func:instanceof`` is not a Python builtin; it is isinstance.
  • The printing examples used printer.DEFAULT_PRINTER directly. graphtage/printer.py says to read it through get_default_printer(), because set_default_printer rebinds the module attribute. The examples also bound as p and then ignored it.

docs/builders.rst

Five separate defects made the examples fail:

  • graphtage.dataclasses.DataClass does not exist. The class is DataClassNode, so the prose cross reference was dangling and the code sample raised NameError.
  • @Build.builder(Bar) is a typo for @Builder.builder(Bar).
  • The last two example methods were missing self. Builder.expand calls expander(self, node) and Builder.build calls builder(self, node, children), so both raised TypeError. The earlier examples on the same page already took self.
  • graphtage.Builder.builder, graphtage.Builder.expander, and graphtage.SequenceNode were dangling cross references. Builder and SequenceNode are not hoisted into the top-level namespace. They are also methods, so they now use :meth:.
  • StringNode, ListNode, and BasicBuilder were used but never imported, and two reprs were wrong: ListNode stores a tuple, so it prints ListNode((...)), and StringNode's repr uses single quotes.

docs/printing.rst

The quoted condition for choosing an edit over a node was missing the isinstance(node_or_edit, EditedTreeNode) guard, and the raw bounds comparison it showed was replaced by Edit.has_non_zero_cost(), which tightens bounds in a loop before comparing. The rest of the protocol description checked out against graphtage/tree.py and is unchanged.

docs/howitworks.rst

"Dicts are matched by solving the minimum weight matching problem on the complete bipartite graph" describes --dict-strategy match, which stopped being the default in PR #51. The default is auto.

What was added

  • docs/library.rst: sections on BuildOptions (used in docs/filetypes.rst signatures but never explained), pydiff.diff(), pydiff.build_tree(), and diffing Python source through pydiff.ast_to_tree and ASTBuilder.
  • docs/builders.rst: sections on data class slots (type enforcement, the TypeError on redefining an ancestor's slot, subscripted generics being skipped) and post_init().
  • docs/printing.rst: sections on Printer(quiet=...), NULL_PRINTER, StatusWriter, and the PR Keep forced color when stdout is redirected #130 enable_ansi_support() split. Importing graphtage no longer calls colorama.init() or replaces sys.stdout.
  • docs/howitworks.rst: all three dict strategies, and a section on --ignore-list-order and UnorderedListNode.

Verification

  • Every code example on these pages was executed against this tree and its real output pasted in, including the library.rst session end to end.
  • sphinx-build -W --keep-going -E -b html docs docs/_build/html produces no new warnings. Two warnings remain on this branch, both pre-existing on master and both outside these files: duplicate object descriptions for graphtage.Edit.from_node and graphtage.Edit.initial_bounds, caused by graphtage/tree.py carrying both an Attributes: docstring section and inline attribute docstrings for the same two names.
  • ruff check graphtage test docs bindist passes.

Code defects found, not fixed

No graphtage/*.py source file was touched. These are reported for separate issues:

  1. BuildOptions.__init__ takes the keyword check_for_cyces while the attribute is check_for_cycles (graphtage/graphtage.py:1017 and :1051). Already known.
  2. DataClassNode.post_init() never runs for the class being instantiated. __init__ iterates self._DATA_CLASS_ANCESTORS, which excludes cls itself, so a post_init defined on a class only runs when a subclass of it is instantiated. The page documents the actual behavior rather than the intended one.
  3. DataClassNode.print uses Fore.Yellow, which does not exist (colorama spells it Fore.YELLOW). Any call raises AttributeError.
  4. graphtage/ast.py Subscript.print calls self.slice.write(printer) instead of self.slice.print(printer).
  5. graphtage/pydiff.py PyDiffFormatter.print_Subscript writes "[" twice; the closing bracket should be "]".
  6. graphtage/pydiff.py PyObjAttribute.__init__ tests isinstance(object, StringNode) against the builtin object rather than self.object, so the branch never fires.
  7. ASTBuilder has no builder for ast.Import, so ast_to_tree(ast.parse("import os")) raises NotImplementedError. Only ast.ImportFrom is handled.
  8. There is no formatter registered for DataClassEdit, so an edited Assignment, Call, or Import falls through to the generic compound-edit printer. x = foo(1, 2) against x = foo(1, 2, 3) renders as [x]foo[1,2,++3++] instead of Python source. Unedited AST trees print correctly.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GypKU5KdLfs2Cf8kS2TzJa

ESultanik and others added 4 commits September 9, 2026 08:46
The bipartite matching description covered only `--dict-strategy match`,
which stopped being the default in PR #51. Describe `auto`, `match`, and
`none`, and say which one is the default.

Add a section on `--ignore-list-order`, which builds `UnorderedListNode`
rather than `ListNode`, and give the complexity trade-off from the
`BuildOptions.ignore_list_order` docstring.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GypKU5KdLfs2Cf8kS2TzJa
The quoted condition for choosing an edit over a node was missing the
`isinstance(node_or_edit, EditedTreeNode)` guard, and the raw bounds
comparison it showed has been replaced by `Edit.has_non_zero_cost()`,
which tightens bounds in a loop rather than doing one cheap check.

Add sections on `Printer(quiet=...)`, `NULL_PRINTER`, `StatusWriter`, and
`enable_ansi_support()`: importing graphtage no longer calls
`colorama.init()` or replaces `sys.stdout`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GypKU5KdLfs2Cf8kS2TzJa
Every code sample on this page failed as written:

- `graphtage.dataclasses.DataClass` does not exist; the class is
  `DataClassNode`.
- `@Build.builder(Bar)` is a typo for `@Builder.builder(Bar)`.
- The last two methods were missing `self`, so `Builder.expand` and
  `Builder.build` raised `TypeError` when calling them.
- `StringNode`, `ListNode`, and `BasicBuilder` were used but never
  imported.
- `ListNode` stores a tuple, so its repr is `ListNode((...))`, and
  `StringNode`'s repr uses single quotes.

Repoint the dangling `graphtage.Builder.builder`,
`graphtage.Builder.expander`, and `graphtage.SequenceNode` cross
references at their real modules, use `:meth:` for the two methods, make
the inline-only `CustomNode` reference a literal, and correct
`instanceof` to `isinstance`.

Add sections on data class slots and `post_init()`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GypKU5KdLfs2Cf8kS2TzJa
PR #132 put removals before insertions in the edit tie-break order, so
every output block on this page was stale. Rerun the whole session and
paste the real output. The last element of the pydiff example is now a
`Replace` rather than an insert and remove pair.

Delete the stray `from_node.diff(to_node)` line, which used two names the
session never defines, and correct `instanceof` to `isinstance`.

Read the default printer through `printer.get_default_printer()` rather
than the `printer.DEFAULT_PRINTER` module attribute, and use the `p` the
examples already bind.

Add sections on `BuildOptions`, `pydiff.diff()`, `pydiff.build_tree()`,
and diffing Python source through `pydiff.ast_to_tree`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GypKU5KdLfs2Cf8kS2TzJa
@ESultanik

Copy link
Copy Markdown
Collaborator Author

The code defects noted in the PR description are now tracked:

@ESultanik

Copy link
Copy Markdown
Collaborator Author

Correction to my earlier comment: #150 is not a missing formatter. DataClassEdit is missing a print() override equivalent to the one SequenceEdit already has (graphtage/sequences.py:53), which hands control back to the node formatter. The issue has been retitled and rewritten with the verified two-line fix.

That does not change anything in this PR: the AST example still needs to avoid an edited call until #150 lands.

@ESultanik
ESultanik merged commit 2e10eee into master Sep 9, 2026
11 checks passed
@ESultanik
ESultanik deleted the docs-library-refresh branch September 9, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant