GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Add drift guards for the generated expression surface by eriknw · Pull Request #601 · python-graphblas/python-graphblas

Conversation

@eriknw

@eriknw eriknw commented Aug 4, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Nothing failed when the generated expression surface drifted: a method added
to Matrix/Vector/Scalar without updating the automethods name sets, or a
hand-edit inside a generated block, sailed through CI. Two complementary
guards close this.

  • test_automethods.py (b32a58a): every public attribute of
    Scalar/Vector/Matrix/TransposedMatrix must be auto-computed on the
    expression classes or listed in an explicit OPT_OUT table with a reason;
    every generated name must still exist on the concrete type; OPT_OUT
    entries must stay live so the table cannot rot. A class's own dunders are
    checked too, except interpreter and library machinery such as
    __slotnames__ and __array_priority__ (the class constant numpy reads,
    which Fix Scalar infix expressions with a literal operand #654 sets; 7ffe93e).
  • Infix coverage (0040675): coverage is the intersection across plain,
    index, and infix expression classes rather than the union, so a name
    missing only from the infix classes is caught too.
  • autogenerate.py --check and a drift-guard test (4222524):
    regenerates every generated file into a scratch tree and compares parsed
    ASTs (so the result does not depend on whether black is installed),
    naming the files that drifted. The script pins sys.path to its own repo
    root and prints which package it validated, so a worktree cannot
    green-light the wrong checkout.

The two guards cover different failure modes: the test guards the name sets
against the classes; --check guards generated file content against the
generator.


Stack created with GitHub Stacks CLI • Give Feedback 💬

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@eriknw eriknw changed the title 21 autogenerate drift guard Add drift guards for the generated expression surface Aug 4, 2026
@eriknw
eriknw marked this pull request as ready for review August 4, 2026 16:07
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 6ead79d to 2968622 Compare August 4, 2026 16:12
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 2968622 to 4405133 Compare August 5, 2026 00:06
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch 2 times, most recently from 3a77564 to d202125 Compare August 5, 2026 17:44
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from d202125 to 7edb8b1 Compare August 5, 2026 18:03
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch 2 times, most recently from 4183028 to c11cd07 Compare August 6, 2026 07:59
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from c11cd07 to c3933ff Compare August 6, 2026 15:39
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from c3933ff to 76c9d16 Compare August 6, 2026 15:41
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch 2 times, most recently from 2450829 to 8e098e0 Compare August 6, 2026 20:41
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 8e098e0 to 8788937 Compare August 7, 2026 02:48
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 8788937 to 32893f7 Compare August 7, 2026 05:09
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 32893f7 to 1ece03c Compare August 26, 2026 17:30
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch 2 times, most recently from bc838b6 to 590c59d Compare September 18, 2026 03:46
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch 4 times, most recently from e6a1e9f to 9c05db9 Compare October 1, 2026 17:10
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch 3 times, most recently from f774476 to 13a1aca Compare October 3, 2026 04:57
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 13a1aca to 12d47f2 Compare October 3, 2026 04:57
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 12d47f2 to 5472623 Compare October 4, 2026 06:21
@eriknw
eriknw removed this pull request from stack #634 October 8, 2026 21:02
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 5472623 to 625009c Compare October 8, 2026 21:40
@eriknw
eriknw added this pull request to stack #635 October 8, 2026 21:46
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 625009c to 6b1d4aa Compare October 9, 2026 03:06
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 6b1d4aa to 1ff2bf4 Compare October 9, 2026 05:25
@eriknw
eriknw force-pushed the 21-autogenerate-drift-guard branch from 1ff2bf4 to 065d257 Compare October 14, 2026 18:45
@codecov

codecov Bot commented Oct 19, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.43%. Comparing base (31fa1bc) to head (7211597).

Additional details and impacted files
@@                   Coverage Diff                    @@
##           24-operator-resolution     #601    +/-   ##
========================================================
  Coverage                   98.43%   98.43%            
========================================================
  Files                         106      108     +2     
  Lines                       33178    33301   +123     
  Branches                     3863     3872     +9     
========================================================
+ Hits                        32658    32781   +123     
+ Misses                        373      372     -1     
- Partials                      147      148     +1     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

eriknw added 7 commits November 12, 2026 11:25
@eriknw
Add test_automethods.py: guard the generated expression surface
c275ce0
Adding a method to Matrix/Vector/Scalar without updating the automethods
name sets left the expression classes silently missing it, and no test
failed. The new introspection test closes that:

- Forward: every public attribute (and value-forwarding dunder) of
  Scalar/Vector/Matrix/TransposedMatrix is either auto-computed on the
  expression classes or listed in an explicit OPT_OUT table with a reason
  (52 entries: mutators, constructors, native metadata, storage flags).
- Reverse: every generated name still exists on the concrete type.
- Hygiene: OPT_OUT entries must be live and not redundantly covered, so
  the table cannot rot.

Coverage is derived at runtime from what the generator emitted (getter
__module__ is graphblas.core.automethods), not from a second copy of the
name sets, so reorganizing the sets does not break the test. Failure
messages name the attribute and point to the sets and
scripts/autogenerate.py.

Teeth: adding a fake public method to Vector makes the test fail with
that message.
@eriknw
Cover the infix expression classes in the automethods guard
3dc2098
core/infixmethods.py copies the same generated automethods surface onto
the infix expression classes (VectorInfixExpr and friends, minus the
private _get_value), so a name missing only from the infix classes was a
gap test_automethods.py could not see: coverage was the UNION across
expression classes, so a name present on the plain expression class
masked its absence elsewhere. Coverage is now the INTERSECTION across
plain, index, and infix classes (identical sets today: Scalar 42/42/41,
Vector 49/49/48, Matrix 58/58/57, differing only by _get_value).

Teeth: hiding to_coo on MatrixInfixExpr alone makes the Matrix forward
test fail, naming the attribute, all three expression classes, and
scripts/autogenerate.py.
@eriknw
Add --check mode to autogenerate and a drift-guard test
5e9d3c1
test_automethods.py guards the NAMES of the generated expression surface
but not the generated file content: hand-edit a block between the
auto-generated code markers, or edit the name sets in automethods._main
without rerunning the generator, and the on-disk blocks go stale while CI
stays green. scripts/autogenerate.py --check now regenerates every
generated file into a scratch tree and compares, naming the files that
drifted. The generator mains gained internal base-dir and callblack
parameters to support this.

The two guards are easy to confuse, so the scope is worth stating. The
generator emits from the literal name sets in automethods._main rather
than by introspecting the classes, so adding a method to Matrix does not
change its output and cannot surface here; test_automethods.py covers
that side. This covers the other one, generated files that no longer
match the generator claiming to produce them.

The check compares parsed syntax, not bytes. The generators shell out to
black when it is on PATH and skip it when it is not, so a byte comparison
reports drift on automethods.py and infixmethods.py in every environment
lacking black, and black is in no test extra. Guarding that with
skipif(black is None) meant the test never ran in the pytest_normal CI
jobs, which is exactly where it needs teeth. Comparing ASTs makes the
result independent of whether black is installed, so the check runs
everywhere, and layout is already enforced repo-wide by black in
pre-commit and the lint job. The residual gap is a layout-only edit
inside a generated block, which black --check catches and this does not.

The script also puts its own repo root on sys.path before importing
graphblas. For a script sys.path[0] is the script's own directory, so a
bare import resolves to whatever is installed; that coincides with the
checkout in an ordinary dev setup and diverges in a git worktree, where
--check would validate a tree nobody asked about while reporting green.
The check now prints the package it validated and the test asserts on
that line rather than inferring correctness from an exit code.

Scratch files go to a TemporaryDirectory outside the repo. Comparing
parsed syntax removed the reason to keep them inside it (letting black
discover the project pyproject.toml), and an interrupted run no longer
leaves .autogen_check_* directories in the working tree.
@eriknw
Treat __slotnames__ as machinery in the dunder drift guard
c18b819
@eriknw
Test the generators' base_dir and callblack=False in the test process
8bcb4f7
scripts/autogenerate.py --check regenerates into a scratch copy with
_main(base_dir=..., callblack=False), but the drift test runs it in a
subprocess, and CI's auto-generated code check runs the generators with
the defaults, so neither argument was ever covered. Copy the generated
files, make every generated block in the copies stale, run both
generators on the copies, and check that the copies are regenerated to
the same code, the package's files are untouched, and black never runs.
If a generator ignores base_dir and rewrites a package file, the test
fails and puts the file back byte for byte, so the checkout is left as
it was.
@eriknw
Exclude the guard tests' failure message builder from coverage
32d5887
_fix_hint builds the assertion message of the expression-surface
guards, so it runs only when one of them fails.
@eriknw
Treat __array_priority__ as machinery in the dunder drift guard
7211597
"Let numpy's operators defer to graphblas objects" sets __array_priority__
on BaseType, BaseExpression and TransposedMatrix. The guard counts only a
class's own dunders, so TransposedMatrix is the one it flagged. The name is
a class constant numpy reads, like the networkx dispatch attributes already
in the machinery set, not a value of the result that could auto-compute,
and the expression classes define it themselves. In the machinery set it
also holds below that commit, where an OPT_OUT entry would fail the
liveness check for naming an attribute TransposedMatrix does not have.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@eriknw
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.