From df36410c3dbba1954d5170c826b265365ec111ad Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Mon, 20 Apr 2026 17:47:12 +0100 Subject: [PATCH] docs(python): known-limitation block on scope-resolution public API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unit 10 — document what the Python registry-primary path intentionally does not resolve, so reviewers and future maintainers can distinguish conscious trade-offs from latent bugs: - Dynamic attribute access (getattr / setattr) - Dynamic imports (importlib, __import__) - Metaclass-driven dispatch - Union / Optional branch-picking behavior - Arbitrary signature-rewriting decorators - typing.TYPE_CHECKING-guarded imports - *args / **kwargs type flow-through - super() outside a directly-bound method Each item names the file that owns the relevant hook so a future follow-up knows where to start. Shadow-harness corpus parity + the CI parity gate remain the authoritative signal for which of these matter at fleet scale. --- .../core/ingestion/languages/python/index.ts | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/gitnexus/src/core/ingestion/languages/python/index.ts b/gitnexus/src/core/ingestion/languages/python/index.ts index 102295eff..9643e781c 100644 --- a/gitnexus/src/core/ingestion/languages/python/index.ts +++ b/gitnexus/src/core/ingestion/languages/python/index.ts @@ -12,12 +12,64 @@ * - `ast-utils.ts` — generic `SyntaxNode` helpers * - `import-decomposer.ts` — `import a, b` / `from m import x, y` → one match per name * - `receiver-binding.ts` — synthesize `self`/`cls` type bindings on methods - * - `captures.ts` — `emitPythonScopeCaptures` (top-level orchestrator) + * - `captures.ts` — `emitPythonScopeCaptures` (top-level orchestrator) + * - `cache-stats.ts` — PROF_SCOPE_RESOLUTION cache hit/miss counters * - `interpret.ts` — capture-match → `ParsedImport` / `ParsedTypeBinding` * - `merge-bindings.ts` — Python LEGB precedence * - `arity.ts` — Python arity check (`*args`, `**kwargs`, defaults) * - `import-target.ts` — `(ParsedImport, WorkspaceIndex) → file path` adapter * - `simple-hooks.ts` — small/no-op hooks made explicit + * + * ## Known limitations + * + * The Python registry-primary path intentionally does NOT resolve the + * following. Each is a conscious trade-off at migration time; lifting any + * of them is tracked as a separate follow-up rather than silently + * "maybe-resolving" and emitting low-confidence edges. + * + * 1. **Dynamic attribute access** — `getattr(obj, 'name')` and + * `setattr` bind at runtime. We emit no edge; the call site + * surfaces as an unresolved reference. + * 2. **Dynamic imports** — `importlib.import_module(...)` and + * `__import__(...)` are not followed. Static `import x` and + * `from m import x` are fully resolved. + * 3. **Metaclass-driven dispatch** — C3 linearization drives MRO + * (see `mro-processor.ts`), but method resolution that depends + * on `__getattribute__` overrides or metaclass `__call__` + * remains unresolved. + * 4. **Union / Optional type hints** — `def f(x: Union[A, B])` or + * `x: Optional[A]`: `arity.ts` validates parameter count only; + * receiver-binding and field-type resolution pick the first arm + * and emit a single edge rather than branching. `List[T]` / + * `Dict[K, V]` strip the outer generic for receiver typing (see + * `interpret.ts`). + * 5. **Decorators that rewrite signatures** — `@dataclass`, + * `@property`, `@classmethod`, `@staticmethod` are recognized + * by `receiver-binding.ts`. Arbitrary decorators (e.g. + * `functools.wraps`, custom retry wrappers) preserve the wrapped + * function's declared signature; a decorator that returns a + * different callable is followed only through the declared + * return type. + * 6. **`typing.TYPE_CHECKING`-guarded imports** — treated like any + * other `import` for reference resolution. We do not distinguish + * runtime-visible from type-checker-only imports; this is + * intentional (type-only imports are still structurally valid + * type references). + * 7. **`*args` / `**kwargs` type flow-through** — `arity.ts` + * accepts any call count when a variadic is present, but no + * type information flows through the variadic into the callee + * body. Receiver-binding still works for explicit parameters. + * 8. **`super()` outside a method with a literal class binding** — + * resolved for the standard `class Child(Parent): def m(self): + * super().m()` pattern. Zero-arg `super()` inside a nested + * function, a `functools.wraps`-rewrapped method, or a call + * site where the enclosing class can't be statically determined + * is left unresolved. + * + * Shadow-harness corpus parity is the authoritative signal for which + * of these matter in practice. The CI parity gate blocks any PR that + * regresses either the legacy or registry-primary run of + * `test/integration/resolvers/python.test.ts`. */ export { PYTHON_SCOPE_QUERY } from './query.js';