Skip to content

Analysis output depends on the interpreter the analyzer runs under (resolution spellings differ across Python versions) #190

Description

@rahlk

Observation

The same checkout (odoo-slim-19, 2,364 files) analysed by codeanalyzer-python 1.4.1 under two different Python interpreters produces materially different graphs. Emitting into Neo4j from two runs:

run A run B
PY_CALLS baseline +2,256 (3,125 new / 869 gone)
PY_RESOLVES_TO baseline −40
PyBodyNode, L4 kinds only (actual_in −1,193, actual_out −568, formal_in −678, formal_out −285) baseline −2,724
PY_DDG baseline −5,360 (reaching-defs −6,993, points-to +1,633)
every level-1 body kind (call 77,332, statement 55,169, …) identical identical

The differences are systematic re-spellings of resolved targets:

  • werkzeug.exceptions/NotFoundwerkzeug.exceptions.NotFound/__init__
  • _thread/allocate_lock_thread.LockType/__init__
  • super().writeBaseModel.write in one run, builtins.super/write (a ghost) in the other

with downstream consequences: one call site in StockWarehouse.write loses 100 L4 vertices because its chain no longer reaches odoo.tools.translate.

Isolation

A controlled run over a small fixture shows this is not a 1.4.0→1.4.1 emitter change:

  • 1.4.0 and 1.4.1 on the same interpreter give byte-identical call targets.
  • The same fixture under Python 3.13 flips Lock() to _thread.LockType/__init__ for both versions.
  • v1.4.0..v1.4.1 touches only entrypoints/, neo4j/, schema/; jedi is 0.19.2 in both.

The variable is the interpreter (and by implication its stdlib typeshed and installed packages) that Jedi inspects at analysis time.

Why it matters

  • Two analyses of the same commit are not comparable across machines; a Neo4j graph's PY_CALLS depends on who emitted it.
  • The defuse linker's fan-out amplifies each unresolved call (companion issue): one changed resolution added 219 edges to a single hub in a consumer's bounded call graph.
  • :PyApplication records analyzer_version but not the interpreter or package set, so a consumer cannot detect the difference.

Suggestions

  • Record the analysis interpreter (sys.version_info, platform) and ideally a hash of the resolved environment on :PyApplication and in analysis.json metadata.
  • Document that resolution is environment-sensitive and recommend pinning the analysis interpreter for reproducible graphs.
  • Consider spelling stdlib targets canonically regardless of interpreter (always the class, never the factory) so the stable cases agree.

Definition of done

  • A consumer reading analysis.json or the graph can determine which interpreter produced it.
  • The docs state the sensitivity and the recommended practice.

Found while migrating the python-sdk Neo4j backend to 1.4.1 (python-sdk#327).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions