Repository navigation
refactor(#358): move Python import edges to shared captures - #382
Conversation
|
@LaimaWu thank you for this, and for the very thorough validation write-up. It's a careful slice, and holding off on rebasing onto an unmerged branch was the right call. Short answer: keep the Python-specific binding helper in #382. Just adapt it to the new shared interfaces (details below). Please don't move binding emission into the shared API in this PR; we'll do that as a separate follow-up. Why:
Merge orderOur JS/TS and Go slices go into What the rebase looks likeI merged #382 locally on top of both slices to check this. There are 8 conflict hunks in two files: 1. const ImportReach reach = importContainerReach( stmt, Lang::Python );
if( reach.depth > kMaxImportContainerDepth || !reach.reachable )
{
return;
}2. case DepDialect::Python: return { std::string( raw ) };
3. case DepDialect::CFamily:
case DepDialect::Web:
case DepDialect::Go:
case DepDialect::Python:
return true;With that in place, drop the 4. 5. The first-name filter can live in the query. The (import_statement . name: (_) @import.path)
(import_from_statement module_name: (_) @import.path)This is optional but cleaner. I checked it locally: the anchored query gives the same 6. 7. Please keep the vocabulary comment in 8. CHANGELOG. The sentence about the bounded Python walk remaining for disclosure needs to change to match the next section. Disclosure:
|
| file | main / #382 |
rebased with walk off |
|---|---|---|
import a at the bottom |
announced | announced |
pass at the bottom |
announced | not announced |
import b at the top, pass at the bottom |
announced | not announced |
C-family already behaves the capture-path way on main: 300 nested #ifdef around int x; is not announced. The JS/TS slice states the same rule ("announce only when a captured import is actually cut"). So we'd prefer Python to follow it rather than keep a second walk alive just for disclosure.
The only gate change is the deep-nesting fixture in test/pyimportprecisecheck.sh:
assert {f.get('p') for f in skipped.findall('f') if f.get('why') == 'extract-partial'} == {
'depth257.py'}, 'cut at 257 is disclosed; an import-free deep container is not (nothing was cut)'Keep the 255/256 kept and 257 cut-and-announced rows as they are.
Review notes on #382 itself
- No bugs found. I compared these forms against
mainon--deps,--skipped, callees, uses and the map. All are byte-identical both on refactor(#358): move Python import edges to shared captures #382 alone and on the rebased stack:import a.b as c,import a . b,import a, b as bb, pkg.mod as pmfrom . import a as x, b,from .. import up,from .pkg import mod,from x import (a, b as c,\n c,),from m import *if TYPE_CHECKING:,try/except ImportError/else/finally,with,for,while,match/caseasync def, decorated defs, class bodiesfrom __future__ import …,__import__("x"),importlib.import_module("x")(no edge before or after)- broken/ERROR statements and py2
print/exec
- Missing negatives in the gate (cheap to add):
from __future__ import annotations,__import__("x")andimportlib.import_module("x")should produce no Include and no binding.import a.b as cshould bindc → a.bwith the module marker. Today only the undotted alias is pinned.from . import a as bshould give Include.and bindb.
- Small cost note, not blocking. Each
@import.namesmatch does its own root descent inimportContainerReach, plus one or twots_node_parentcalls. Sofrom m import (a, b, …)with N names costs N descents. That's fine for real code. If you want, compute the reach once per statement.
Verifying byte identity after the rebase
I ran this locally on the rebased stack with steps 1–6 applied and the gate change above. For your rerun:
- Gates. Build
mainand your head, then runRIPWIRE_BASE_BIN=<main build> test/pyimportprecisecheck.shandtest/importcapcheck.sh. Also run the resolve/deps gates you already use (depsprecisecheck,resolvecheck,nestedimportcheck,importnarrowcheck,externalvetocheck,builtinbindcheck,pymodulealiascheck,cacheidentitycheck). They all passed for me, and both import gates ran clean under ASan (no sanitizer reports). - Diffs. Diff
--deps --limit=100000 --no-cache, cached--deps,--skipped --no-cache, the map, and--callers/--callees/--impact/--usesover a few Python repos. I got 49/49 identical over 8 repos and the Python fixtures. As a sanity check against silent drops, the number of Python<inc>rows was unchanged on textual (4479), aiohttp (1496), conan (4924), meson (3378) and synapse (4776). - Expected difference. The only one should be the import-free deep-nesting
--skippedrows above. Please call it out in the CHANGELOG entry.
Thanks again! Ping us here if anything in the rebase is unclear, or just say the word and we'll take the rebase.
|
@joyful-ii-V-I Thanks for the detailed review and integration guidance. I’ll keep the Python-specific binding helper scoped to #382, as suggested. I’ll wait for the Go and JS/TS slices to land on main, then handle the rebase and rerun the differential validation. I’ll also add the missing negative regression cases and explicitly document the deep-nesting disclosure change. Appreciate you checking the combined implementation. |
|
@LaimaWu a short status so you can plan. The Go and JS/TS import slices that #382 waits for are in the The shared binds channel (renames for the other languages) comes after v0.6.7. It does not have a date yet, v0.6.7 also brings a new CONTRIBUTING section, Adding a language: common stays common. |
fe4e681 to
38607f4
Compare
|
@joyful-ii-V-I Thank you for the detailed integration guidance. I have rebased #382 onto main The six-file Python slice follows the eight review points, retains the Python-specific binding helper, and removes the superseded walked extractor. The gate now checks future annotations, dynamic import calls, dotted module aliases (including the module marker), and relative aliases with real Include/RawBind assertions and fault-injection controls. No shared binding API or Go/JS/TS feature change is included. Clean builds of the new head: 78 relevant gates, 3,084 PASS / 0 FAIL / 16 SKIP; five G1 gates, 269 PASS / 0 FAIL / 10 SKIP, with no sanitizer reports. The 117 successful raw baseline/head command pairs differ only on the documented deep fixture; Go/JS/TS comparisons and 12 non-deep main-cache reuse cases pass. The full repository suite was not run, and the old Linux results remain historical. The approved disclosure exception is explicit in CHANGELOG and tests: imports at depths 255/256 stay, a real import cut at 257 is disclosed, and import-free deep containers no longer produce extract-partial (including a top-level import followed by deep pass). Ready for your re-review; thanks again for checking the combined implementation. |
|
@joyful-ii-V-I Thanks also for pointing me to the new CONTRIBUTING guide and the slice 1a opportunities. As noted in my update above, #382 has now been rebased, validated, and pushed for re-review. I'd be interested in taking on another small 1a slice once #382 is settled. I'll check what's still available and coordinate on #358 before starting. Appreciate your guidance and the opportunity to keep contributing! |
|
@LaimaWu thank you for this. The rebase follows all eight hints closely, and the slice is what we hoped for: Python's import edges now come from We're approving this and merging it once CI is green; it ships in the next release. The notes below are small and none of them blocks the merge. Please don't push for them now (a push would restart CI); we'll fold them into our next integration batch, or you're welcome to send them as a small follow-up PR. What we checked (base
|
|
@coderabbitai review |
|
Merged to |
Hygiene after v0.6.7: cppqualcheck reports timeouts as timeouts, #382 review notes, CONTRIBUTING map rows
…iew) - src/ingest_importcap.h, src/graph.h: comments named the deleted capturePythonImportBinds; they now name emitCapturedPythonImportBind. Comment-only. - test/pyimportprecisecheck.sh: the "vocabulary is compiled in" arm grepped the bare capture names, which the Go tags.scm comment already carried, so it passed on a binary without the Python captures. It now greps the Python patterns themselves. - CHANGELOG: the redhat-et#382 entry states that record content and the extraction version are unchanged while the order of a file's bind records differs; drop a doubled blank line. - queries/python/tags.scm: "C++ imposes" reads as the language; name the C++ side (ingest_importcap.h). - CONTRIBUTING: family table row 1 lists Python as reading @import.path; row 2 names the Python bind producer that replaced the deleted one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI run 38864273049 (pyimportprecisecheck #382, and headbinstagecheck, which runs it end to end): the gate's exact-EOF walk of the lean cache pinned kCacheVersion 31, which the train moved to 32 without changing any record's shape. The walk itself is unchanged and still passes to exact EOF on the version-32 blob, which is the evidence for "no record changes shape". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
attribmultidefcheck: the ninja-26 CanonicalizePath call is now proven (the 3-parameter overload cannot take 2 arguments), so the split premise of (B), [S] and [S2] moves to a new same-arity Normalize overload pair in the fixture; new [N] arms pin CanonicalizePath as proven (red on base). cachefuzzcheck Part 3 and pyimportprecisecheck #382 walk the cache record layout: the def record gains a u32 (paramsMin), cache version 33. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Python import targets now use the shared
@import.pathcapture path on current main. Python-specific@import.namesand paired@import.aliasmatches retain the existing RawBind semantics; the old Python walked extractor is removed. Plain comma-separated imports still contribute only their first dependency target, and an aliased first clause keeps its writtenasspelling in--deps. No cross-language binding API or Go/JS/TS behavior change is included.Rebased onto
0e0f67c73ca332ed58bb8478677f916f7ccc1e84after the Go and JS/TS slices landed through #385/v0.6.7. Published head:38607f401a5b0497cff18f131239f208065a2877. Author/committer metadata was corrected to the verified GitHub noreply identity; the tree is identical to the reviewed integration candidate. All validation counts below were rerun on clean builds of this head.The integration follows the eight review points: the struct-returning ImportReach, Python normalizer and importsFromCapture arm, the single existing two-argument importDirectiveOf, first-name query anchor, removal of both Python directiveTargetOf arms and the old bind helper, preserved Full-capture guard, updated shared vocabulary comment, and an explicit CHANGELOG entry.
The approved disclosure change is intentional: real imports at container depths 255/256 remain; a real import at 257 is cut and disclosed. A deeper container with no import below the cut, including a top-level import followed by a deep pass, no longer produces extract-partial and can be cached. This is not a claim of complete byte identity with the previous walk.
The Python gate now pins future annotations and dynamic import calls against extra Include/RawBind facts, dotted module alias
c -> a.bwith its module marker, and relative Include.with bindingb. It checks complete binding records and counts, including the existing NameAlias fact, without coupling to the relative order of two extraction passes. Sixteen built-in corrupt-fact controls and sixteen external serialized-fact injections all reject their planted defects; the transparent wrapper control passes.Validation on macOS 14.3.1 x86_64, 16 GiB, CMake 4.4.4 / Clang 23.1.1, clean dev builds:
The 16 normal arm SKIPs are ten historical red-first module-alias controls whose fixes are already on HEAD, three macroreparse historical-baseline arms, the Apple Silicon auto-detection arm on this Intel host, and the qualnew/objcfield historical-binary controls. Binoverride's failing-sentinel harness reports zero false-greens: 636 nonzero children and seven timeout children; those timeout children are not counted as functional passes. The seven sentinel timeouts are compactlegendcheck, emitescapecheck, regexguardcheck, selfcheckcheck, shapingflagcheck, type3clonecheck and vendorpatchcheck. No prior Linux/Release/full-suite results are inherited by these counts.
Persisted record format and main's extraction/quality version numbers remain unchanged; the existing facts are preserved, the approved disclosure/cacheability exception is explicit, and producer-source identity checks pass. Current-main G1, a separate Release functional run, third-party large Python repository matrix and other native platforms were not run in this round.
Ready for maintainer re-review. This updates the existing Python slice only.
Historical validation: the previous PR head
fe4e681b7575c5472ee9a3e4db5cc5f6ed6ca2fcwas tested on Linux against pre-integration main60dd3b3f2246ce0284cfa4d4ed48694eafa69c2b. Those Linux, Release and full-suite results belong to the old head only and are not results for this rebased head. In particular, the historical full suite was not entirely green; no claim of a completed or green full suite is made here.Refs #358