From 3c6d64ce2aa730f8e908f2cd52c26006088ae495 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Mon, 3 Aug 2026 07:25:03 -0700 Subject: [PATCH] Omit None-code callables from Python get_method_bodies The TypeScript bulk accessors (#298) ship get_method_bodies with a mechanical omission rule: a callable whose `code` is None is left out, so every value in the returned Dict[str, str] is a real str. The Python implementations passed `code` straight through, so a PyCallable with no source text yielded a None value -- a violation of the declared Dict[str, str] contract. Align Python upward to the TS rule in both backends: - in-memory (codeanalyzer.py): filter on `c.code is not None` - Neo4j (neo4j_backend.py): add `AND c.code IS NOT NULL` to the MATCH and index the row directly instead of `r.get("code")`, mirroring the TS Cypher exactly Document the rule on the Python ABC and the facade docstring, as the TS side already does -- the promise that every value is a real str is part of the contract, not an implementation detail. Refs #301 --- cldk/analysis/python/backend.py | 3 ++- cldk/analysis/python/codeanalyzer/codeanalyzer.py | 5 +++-- cldk/analysis/python/neo4j/neo4j_backend.py | 4 ++-- cldk/analysis/python/python_analysis.py | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cldk/analysis/python/backend.py b/cldk/analysis/python/backend.py index c0c64ad..8911e5e 100644 --- a/cldk/analysis/python/backend.py +++ b/cldk/analysis/python/backend.py @@ -156,7 +156,8 @@ def get_callables_overview(self) -> List[PyCallableOverview]: @abstractmethod def get_method_bodies(self, signatures: List[str]) -> Dict[str, str]: """Source bodies for the given callable signatures, keyed by signature. Signatures with no - matching callable are omitted.""" + matching callable are omitted, as are callables whose ``code`` is ``None`` (e.g. synthesized + callables the analyzer emits with no source text) — every returned value is a real ``str``.""" @abstractmethod def get_decorated_callables(self, markers: List[str]) -> List[PyCallableOverview]: diff --git a/cldk/analysis/python/codeanalyzer/codeanalyzer.py b/cldk/analysis/python/codeanalyzer/codeanalyzer.py index 8ddaced..fead2f5 100644 --- a/cldk/analysis/python/codeanalyzer/codeanalyzer.py +++ b/cldk/analysis/python/codeanalyzer/codeanalyzer.py @@ -583,9 +583,10 @@ def get_callables_overview(self) -> List[PyCallableOverview]: return [_overview(c, class_sig, kind) for c, class_sig, kind in self._iter_callables()] def get_method_bodies(self, signatures: List[str]) -> Dict[str, str]: - """Return ``{signature: code}`` for the requested signatures that exist.""" + """Return ``{signature: code}`` for the requested signatures that exist and have a body + (omits callables whose ``code`` is ``None``).""" wanted = set(signatures) - return {c.signature: c.code for c, _, _ in self._iter_callables() if c.signature in wanted} + return {c.signature: c.code for c, _, _ in self._iter_callables() if c.signature in wanted and c.code is not None} def get_decorated_callables(self, markers: List[str]) -> List[PyCallableOverview]: """Return overviews of callables decorated with any of ``markers``.""" diff --git a/cldk/analysis/python/neo4j/neo4j_backend.py b/cldk/analysis/python/neo4j/neo4j_backend.py index e01e817..455ba36 100644 --- a/cldk/analysis/python/neo4j/neo4j_backend.py +++ b/cldk/analysis/python/neo4j/neo4j_backend.py @@ -498,12 +498,12 @@ def get_callables_overview(self) -> List[PyCallableOverview]: def get_method_bodies(self, signatures: List[str]) -> Dict[str, str]: rows = self._run( - "MATCH (c:PyCallable) WHERE c._module IN $mods AND c.signature IN $sigs " + "MATCH (c:PyCallable) WHERE c._module IN $mods AND c.signature IN $sigs AND c.code IS NOT NULL " "RETURN c.signature AS signature, c.code AS code", mods=self._modules, sigs=list(signatures), ) - return {r["signature"]: r.get("code") for r in rows} + return {r["signature"]: r["code"] for r in rows} def get_decorated_callables(self, markers: List[str]) -> List[PyCallableOverview]: rows = self._run( diff --git a/cldk/analysis/python/python_analysis.py b/cldk/analysis/python/python_analysis.py index 3262309..233ad2a 100644 --- a/cldk/analysis/python/python_analysis.py +++ b/cldk/analysis/python/python_analysis.py @@ -552,7 +552,8 @@ def get_method_bodies(self, signatures: List[str]) -> Dict[str, str]: Returns: A dict mapping each signature to its source body. Signatures with no matching callable - are omitted. + are omitted, as are callables whose ``code`` is ``None`` — every returned value is a + real ``str``. """ return self.backend.get_method_bodies(signatures)