Repository navigation
Conversation
Review inject-r4 found two minors that #591 stated in the report instead of fixing. Both fixes land in the scan here. The scan now parses a statement suite an annotation's code call holds, with the same memo, depth limit and unexpanded handling as a suite in a statement. Expression literals in hints stay with the hint checks. module_path now unwinds sys.modules subscripts and attributes in one loop, so a long chain of either cannot exhaust recursion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sweep showed that no test reached _resolve from module_path, the empty path arm or the strip in _spelled. Compare module_path with a recursive reference on every chain of up to three links with invented re-exports, and add working cases for a padded literal and a re-exported sys. Count levels through every mix of exec and eval, and make the second sys.modules chain one whose hit depends on the path. State the new behaviour in section 6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sweep two left one survivor: dropping the argument guard crashes the scan on exec() in a hint, and no test used that spelling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review scanfix-r1 found that the hint walk had no bound: a hint that spells itself ran the folder past its budget, and a 1002-level chain of forward references ended in a swallowed RecursionError and no hit. The walk now marks a sixth level of hint text as dynamic code, follows each text once per hint, as typing's recursive guard does, and skips the pieces a folded string is built from. Tests pin the quine, the countdown at 6, 50 and 1002 levels, the level boundary for strings and string hints, the data strings the scan reads on purpose, the pieces it skips, a name-bound hint it misses, and async return hints. The parity test now checks each lost hit's replacement on its line, and the mix test runs both scans. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review scanfix-r2 found two major gaps. Skipping a folded subtree lost a working check under a rebound name, so the walk now reads every string and piece in a hint. A wide hint stopped the scan with the module's budget error, so a module's hints now share a second budget, and a hint the walk can't finish reports dynamic-code. A star text now reads as typing reads it. The report answers the minor finding and the nit, and states what stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The run from 12:36 to 13:19 UTC on 6 November, its 66 mutants, and the three newer pending entries. This commit changes only the report. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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.
WIP. Follow-up to #591. Review inject-r4 approved the OP1 stream scan with two minor findings, and #591 answered them by wording in §6 of
docs/design/open_population_evidence/op1_report.md. This PR fixes both intests/open_population/test_stream_injection.pyand drops the exceptions from §6. Review scanfix-r1 asked for a bound on the new hint walk, and review scanfix-r2 found two gaps in the first bound. This PR answers both.What changes
Literal statement suites in annotations. At
6413d842,_embeddedskips annotations and the hint checks parse only expressions, sov: exec('global f\ndef f(x):\n return isinstance(x, R)\n')runs a working check when something reads the annotation, and the scan gives no hit._embeddednow passes each hint to_hint_code:ForwardRef("ProjectionRNGRegistry")stays trusted._expanded, with the memo, depth andunexpandedset a code call outside an annotation uses.typing.get_type_hintsevaluates it, and_hint_codelooks inside it for code calls and further strings. That includes the pieces a folded or spelled string is built from, because a name the module rebinds can run one of them. A string that starts with*reads astypingcompiles it,(*x,)[0].unexpandedinstead of following it, follows each text once per hint, astyping's recursive guard does, and spends from a second budget that all of a module's hints share. A hint the walk can't finish, past that budget or on a text too deep for the parser, reportsdynamic-codein place of all its walk found. When the failure comes inside what a hint runs, the outermost hint takes it.unexpanded, and_kindtests that set first, because a string hint is a constant.walked_hintsneeded no change, becausehits()already walks annotation calls.module_pathrecursion. One loop unwinds attributes andsys.modules[...]subscripts from the outermost link inward, and a second replays them from the base. A chain that raisedRecursionErrorat 1000 links now passes at 3000.Two adjacent changes the first fix needs.
_prunekeeps each hint whole. With the old pruning, a pruned scan missed a suite that an unpruned scan reported, in a literal under a call the folder does not fold through.def g(v: 'exec(S)'), reportsdynamic-codeat the hint. Without that, limit class 4 would need a new exception.Three over-approximations, stated in §6 and pinned by tests. The scan never asks whether Python evaluates an annotation, so a local variable's hint, which Python never runs, gives the same hit. It reads every string in a hint that parses as an expression, so a string Python keeps as data (a
Literalvalue,Annotatedmetadata, a list item, a piece of a folded string) gives the hit its code would give. And a hint past the walk's budget leaves that budget spent, so each later hint in the module that holds an expression reports too.Review scanfix-r1
Verdict REVISE at
f8d64f8b, with one major, three minor and two nit findings. Each fix:RecursionErrorthat_applyabsorbed, and the scan gave no hit. Fixed by the level bound and the per-hint guard. Both sources are now tests.execliteral, and review scanfix-r2 showed that skip loses a working check, so it has gone. I kept reading every other string rather than only typing's places. A conditional, anandor a tuple index with a run-time index each pick a string thatget_type_hintsruns, and the folder cannot tell which one, so a typing-places walk misses all three silently. A false hit fails the pin loudly, so §6 states the over-approximation and three cases pin it._FORCED_HINTSgains the place, and the sweep's two async mutants now die.or. It now checks each lost hit's replacement on its line.Review scanfix-r2
Verdict REVISE at
7a830f39, with two major, one minor and one nit finding, plus two surviving mutants. Each answer:strto mean the builtin. Afterstr = eval, the hint(str("exec(...)"), "int")[1]folds to"int", Python runs the piece, and the skip left that piece unread. r2 proposed that the folder refuse any name the module rebinds. I removed the skip instead, so the walk reads every string and piece.builtins.str = eval,globals()['str'] = eval,exec('str = eval')and a star import each rebind a name without an assignment a list of bindings reads directly, and that folder change would reach every other check in such a module. A false hit fails the pin loudly, so the two piece cases go back toisinstance, pinned and stated.ScanError, where the base gave no error. r2 proposed one budget per_hint_codecall. I built that first, and a probe showed that a hint inside what a hint runs got a fresh budget, so the work multiplied at each of five levels. Each module now has one second budget, which all its hints share, nested ones included, and a hint the walk can't finish reportsdynamic-code.typing's guard. The report and docstring now give the scan's own argument, nottyping's.I found two more while answering these. A string that starts with
*ran its check and gave no hit at every revision, and_spellednow reads it astypingcompiles it. A callee known only by name, such asrun = exec, gave no hit at every revision, and limit class 1 now states it, with four pinned misses.Invariants
test_pruning_leaves_every_invented_scan_unchanged,test_pruning_leaves_scans_unchanged_for_any_literal,test_pruning_keeps_each_hint_whole, and every hint test runs both).module_pathagrees with a recursive reference on every chain the recursion survives: all 26,180 chains up to three links, and 200 Hypothesis chains of four to eight.dynamic-codepast them, for all 127 mixes ofexecandevalat the four places, and for strings and string hints.execstatement gives, except name-string hits, andannotation-readin place of a namespace read orvarsof a module on the same line.test_scan_bounds_hints_inside_what_a_hint_runs,test_scan_shares_the_walk_budget_across_a_module).dynamic-code, whatever the module binds, unless the walk has already followed that text in this hint.test_a_hint_past_the_walk_budget_marks_only_itself,test_a_failure_inside_what_a_hint_runs_goes_to_the_outermost_hint).test_scan_keeps_a_hint_within_the_walk_budget_off_the_module_budget).Evidence
Host run at
181f40ca(6 Nov, 12:36 to 13:19 UTC; the first steps started at a one-minute load under 100, the rest under 130, the mark the coordinator set that day). The next commit changes only the report.tests/open_population: 812 passed and 1 strict xfail in 169.14 s, 118 more than base (all intest_stream_injection.py). The other four modules: 104 passed. Track B holdout-registry modules: 230 passed and 2 skipped. All of them in one process: 1,146 passed, 2 skipped and 1 strict xfail. Peak RSS 1.33 GB.--collect-only -m unit tests/open_population: 813. Black, Ruff andgit diff --check 6413d842come back clean; the sealed-pathgit diff --quietexits 0.repository_sources(): 302 modules, exactly the pinned hit (mortality_observer.py:381 type-compare), the same at6413d842,f8d64f8b,7a830f39and181f40ca.7a830f39, pruned and unpruned: the same hits on all 178 base-corpus sources. Of the 617 added sources, 23 differ in both modes, each as intended: a string that starts with*, six pieces a rebound name runs, the pieces of a folded and a spelled literal, andbuiltinsrebound now giveisinstance; six wide sources and three branching ones now reportdynamic-codeinstead ofScanError; four sources too deep for the parser now givedynamic-codeinstead ofMemoryErrororRecursionError. Pruned and unpruned agree on all 795.test_a_failure_inside_what_a_hint_runs_goes_to_the_outermost_hintat the committed module. The module came back byte for byte each time.unit(+118 at base); the recount happens in the coordinator's slot after merging master.Plan
unexpandedhandling.sys.modules[...]inmodule_path, in the same loop as attributes.LONG_CHAINS.🤖 Generated with Claude Code