Skip to content

Neo4j projection emits code: None on every :PyCallable / :PyClass node (schema 2.0.0) #104

Description

@rahlk

Summary

In 1.0.1, the Neo4j projection writes code: None for every :PyCallable and :PyClass node. Schema v2 removed the code field from PyCallable/PyClass (source now lives once on PyModule.source, sliced by each node's Span), but neo4j/project.py still reads the old field:

# neo4j/project.py:468 (classes) and :487 (callables)
"code": getattr(cl, "code", None),   # field no longer exists → always None

Since the pydantic models neither define code nor allow extras, the getattr fallback always fires.

Repro (1.0.1)

from codeanalyzer.core import Codeanalyzer
from codeanalyzer.options import AnalysisOptions
from codeanalyzer.config import OutputFormat
from codeanalyzer.neo4j.project import project

opts = AnalysisOptions(input=Path("tinyproj"), output=None, format=OutputFormat.JSON,
                       skip_tests=True, no_venv=True)
with Codeanalyzer(opts) as a:
    analysis = a.analyze()

rows = project(analysis.application, "tinyproj", {})
for node in rows.nodes:
    if node.props.get("signature") in ("pkg.mod.f", "pkg.mod.A.m"):
        print(node.props["signature"], node.props["code"])
# pkg.mod.A.m None
# pkg.mod.f None

(tinyproj/pkg/mod.py = a two-callable module; both emit code: None while the in-memory models carry correct span.bytes and module.source.)

Impact

  • neo4j/schema.py still declares the code property and builds the fulltext index py_code_fts over c.code / c.docstring — the index is now over nulls, so code search in the graph is dead.
  • The CLDK python-sdk's Neo4j backend (PyNeo4jBackend.get_method_bodies, RETURN c.code AS code) returns nothing, breaking local ↔ Neo4j backend parity (the in-process backend recovers source by slicing module.source with span.bytes).

Suggested fix

Derive the property at projection time the same way schema.l1_body handles spans — slice the module source by the callable's byte span:

start, end = c.span.bytes
code = mod.source.encode("utf-8")[start:end].decode("utf-8")

(needs the owning module's source in scope at the _project_class / callable projection sites; byte_offsets/Span.bytes are already populated at L1+).

Alternatively, if code is intentionally dropped from the graph, remove the property from neo4j/schema.py and the py_code_fts index and bump SCHEMA_VERSION — consumers fail fast on the version stamp either way. Keeping the declared property but always writing null is the one option that breaks consumers silently.

Found while bumping python-sdk to codeanalyzer-python==1.0.1 for cldk 2.0.0-rc.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions