Skip to content

[Bulk index][Python] Wrap Pyright and scip-python with truthful dynamic limits #80

Description

@samchon

Outcome

Replace the stale batch scip-python core with a pinned current Pyright semantic
exporter that shares one Program/type-evaluator state, walks each source once,
and publishes exact facts plus Any/Unknown coverage. Keep scip-python as a
navigation compatibility fallback only.

Current master baseline

Current master registers scip-python with semantic-index authority and only
references. Its bundled Pyright core is historically pinned far behind
current Pyright, omits kinds/enclosing symbols/diagnostics, conflates
implementation relations, and cannot expose call-receiver typing coverage.

PR #147 measured Flask at 10.629 s through scip-python and 0.748 s
through the static fallback. The faster static cell does not have semantic
resolution. The strict cell is a useful cold navigation baseline; no resident
no-op/edit result exists.

Why Pyright is the semantic owner

Pyright is designed as a high-performance
type checker for large Python codebases. Its open-source service already owns:

  • configuration and execution environments;
  • import resolution and typeshed/custom stubs;
  • a Program of source files;
  • parse trees, symbol tables, declarations and scopes;
  • cached type evaluation and diagnostics; and
  • incremental source updates.

scip-python contains a modified historical Pyright tree. Updating and enriching
that batch artifact creates a permanent lag. A custom exporter in a pinned
current Pyright service can reuse the same program and evaluator used by the
language service and avoid one references request per symbol.

Decision: pinned Pyright command/service

Add an upstreamable samchon/graphSnapshot command or narrow maintained fork:

  1. load the same configuration/execution environments as Pyright;
  2. wait for analysis completion and capture one program generation;
  3. enumerate program source files and walk parse trees once;
  4. resolve declarations, symbols, calls and types through the existing evaluator;
  5. emit changed file/environment shards using [Bulk index] Replace symbol-by-symbol LSP scans with compiler-owned snapshots #63's protocol; and
  6. verify program/config/source generation before commit.

Use the resident language service for edit/no-op mode. A batch command may serve
CI, but it must use the same exporter and schema rather than generating SCIP
and reparsing it.

Pin the Pyright release and commit, record its Python language support and
typeshed revision, and reject an unknown internal API handshake.

Facts and dynamic limits

Export:

  • modules, classes, functions, methods, parameters, variables, properties,
    type aliases and nested/local declarations;
  • imports/aliases, syntactic exports and __all__ only where statically
    evaluable;
  • resolved function/method/constructor calls;
  • read/write accesses and type references;
  • direct base classes/protocol information and overrides where Pyright proves
    them;
  • syntactic decorators with resolved decorator expression and decorated target;
  • diagnostics from the same program; and
  • framework tests through a named pytest/unittest enricher.

Python's dynamic surface is not a provider failure. Emit unresolved records for:

  • Any/Unknown call receivers;
  • getattr/setattr, __getattr__, __getattribute__;
  • monkey-patching and metaclass-synthesized members;
  • importlib, __import__, dynamic __all__;
  • eval/exec;
  • dynamic *args/**kwargs forwarding;
  • C-extension/opaque stub boundaries; and
  • decorators whose runtime transformation changes identity/signature beyond
    static proof.

Publish both counts and denominators for call receivers typed concrete,
union/protocol, explicit Any, implicit Unknown, and unresolved. Do not merge
Any and Unknown: one is an explicit escape, the other missing information.

Potential override/duck-typing candidates are not executed dispatches.
Only a statically closed exact target may emit that edge.

Identity and environment

Use distribution/module identity, qualified owner path, declaration kind and
canonical signature/type-parameter structure. Include:

  • execution environment/root and --project-namespace equivalent;
  • source module versus stub module and stub-to-implementation relation;
  • decorated function's source identity distinct from the runtime wrapper;
  • document/owner-scoped locals;
  • nested definitions and comprehensions; and
  • generation-scoped identity for dynamic/synthetic entities that cannot be
    stable.

Python has no source-level overload definitions in the Java sense, but
@overload declarations, implementation bodies and stub/source pairs require
one canonical callable with all declaration spans and signatures.

The universe includes Pyright config/pyproject, execution environment,
interpreter version/platform, import paths, venv, installed distributions,
typeshed/custom stubs, Python version, define constants, strictness settings and
all imported source/stub digests.

Incremental and performance behavior

  • no-op: reuse the resident Program generation and graph manifest;
  • body edit: Pyright invalidates affected files/types; export their shards;
  • public type/import/stub/config change: re-export the reported dependent
    closure;
  • environment/package/typeshed change: select a new universe;
  • create/delete/rename: explicit shard update/removal; and
  • analysis error/cancel/process crash/generation movement: retain prior commit.

No call to find references per declaration is allowed. Cold overhead is
measured against the same Pyright analysis without graph export. Target no-op
p95 below 250 ms and ordinary body edit below 2 s on the pinned fixture.

Implementation map

  1. Add [Bulk index] Replace symbol-by-symbol LSP scans with compiler-owned snapshots #63's shard/coverage schema.
  2. Build a pinned Pyright exporter package/fork with a version handshake.
  3. Add a provider ahead of scip-python only after configuration parity passes.
  4. Add exact dynamic/stub/execution-environment fixtures and deterministic
    fake-protocol tests.
  5. Retain scip-python/static as separately identified fallbacks.
  6. Phase-separate Pyright load/analyze/export/merge in the real Flask benchmark.

Acceptance

  • Exact fixtures cover packages, multiple execution environments, namespace
    packages, stubs/source, overloads, decorators, protocols, generics,
    properties, async, comprehensions, dynamic imports, monkey-patching and
    tests.
  • Calls/accesses/types are extracted from the resident evaluator, not text
    or SCIP markdown.
  • Any/Unknown receiver coverage and every unresolved dynamic class are
    machine-readable.
  • All 15 fact families have explicit coverage/unresolved rows.
  • No-op, body/API edit, create/delete/rename, config/interpreter/package/
    stub change, error, cancel, crash and retry are atomic.
  • Cold overhead is compared with native Pyright; no-op/edit phases are
    measured separately.
  • Linux/macOS/Windows interpreter and path selection are proven.
  • scip-python and static remain truthful fallbacks.
  • Focused tests, Python experiment, pnpm build, pnpm test, and
    pnpm coverage pass.

Primary references

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions