@@ -290,9 +290,11 @@ def _project_module_body(
290290 externals : dict , sig_to_id : dict , module_id_by_key : dict ,
291291) -> None :
292292 for fn in (mod .functions or {}).values ():
293- _project_callable (b , file_key , mod_ref , "PY_DECLARES" , fn , externals , sig_to_id )
293+ _project_callable (b , file_key , mod_ref , "PY_DECLARES" , fn , externals , sig_to_id ,
294+ mod .source )
294295 for cl in (mod .types or {}).values ():
295- _project_class (b , file_key , mod_ref , "PY_DECLARES" , cl , externals , sig_to_id )
296+ _project_class (b , file_key , mod_ref , "PY_DECLARES" , cl , externals , sig_to_id ,
297+ mod .source )
296298 for v in mod .variables or []:
297299 _project_variable (b , file_key , mod_ref , file_key , v )
298300 _project_imports (b , mod_ref , mod , module_id_by_key )
@@ -360,10 +362,10 @@ def _project_imports(b: RowBuilder, mod_ref: NodeRef, mod: PyModule,
360362
361363def _project_class (
362364 b : RowBuilder , file_key : str , parent : NodeRef , parent_rel : str , cl : PyClass ,
363- externals : dict , sig_to_id : dict ,
365+ externals : dict , sig_to_id : dict , source : str ,
364366) -> None :
365367 ref = b .node (
366- ["PySymbol" , "PyClass" ], "id" , cl .id , _class_props (cl , file_key )
368+ ["PySymbol" , "PyClass" ], "id" , cl .id , _class_props (cl , file_key , source )
367369 )
368370 b .edge (parent_rel , parent , ref )
369371
@@ -372,22 +374,23 @@ def _project_class(
372374 b .edge_to_symbol ("PY_EXTENDS" , ref , _symbol_ref (base , externals , sig_to_id ))
373375
374376 for m in (cl .callables or {}).values ():
375- _project_callable (b , file_key , ref , "PY_HAS_METHOD" , m , externals , sig_to_id )
377+ _project_callable (b , file_key , ref , "PY_HAS_METHOD" , m , externals , sig_to_id ,
378+ source )
376379 for a in (cl .attributes or {}).values ():
377380 _project_attribute (b , file_key , ref , cl .signature , a )
378381 for ic in (cl .types or {}).values ():
379- _project_class (b , file_key , ref , "PY_DECLARES" , ic , externals , sig_to_id )
382+ _project_class (b , file_key , ref , "PY_DECLARES" , ic , externals , sig_to_id , source )
380383
381384
382385def _project_callable (
383386 b : RowBuilder , file_key : str , owner : NodeRef , owner_rel : str , c : PyCallable ,
384- externals : dict , sig_to_id : dict ,
387+ externals : dict , sig_to_id : dict , source : str ,
385388) -> None :
386389 ref = b .node (
387390 ["PySymbol" , "PyCallable" ],
388391 "id" ,
389392 c .id ,
390- _callable_props (c , file_key ),
393+ _callable_props (c , file_key , source ),
391394 )
392395 b .edge (owner_rel , owner , ref )
393396
@@ -410,9 +413,10 @@ def _project_callable(
410413 for v in c .local_variables or []:
411414 _project_variable (b , file_key , ref , c .signature , v )
412415 for ic in (c .callables or {}).values ():
413- _project_callable (b , file_key , ref , "PY_DECLARES" , ic , externals , sig_to_id )
416+ _project_callable (b , file_key , ref , "PY_DECLARES" , ic , externals , sig_to_id ,
417+ source )
414418 for cl in (c .types or {}).values ():
415- _project_class (b , file_key , ref , "PY_DECLARES" , cl , externals , sig_to_id )
419+ _project_class (b , file_key , ref , "PY_DECLARES" , cl , externals , sig_to_id , source )
416420
417421
418422def _project_attribute (
@@ -459,13 +463,24 @@ def _module_props(mod: PyModule, file_key: str) -> Props:
459463 )
460464
461465
462- def _class_props (cl : PyClass , file_key : str ) -> Props :
466+ def _span_code (source : str , span ) -> str | None :
467+ """A declaration's text: the owning module's ``source`` sliced by the node's
468+ utf-8 byte span. Schema v2 stores source once per module, so the graph's
469+ ``code`` property (declared on :PyClass/:PyCallable and indexed by
470+ ``py_code_fts``) is derived here at projection time (#104)."""
471+ if span is None or not source :
472+ return None
473+ lo , hi = span .bytes
474+ return source .encode ("utf-8" )[lo :hi ].decode ("utf-8" )
475+
476+
477+ def _class_props (cl : PyClass , file_key : str , source : str ) -> Props :
463478 return prune (
464479 {
465480 "id" : cl .id ,
466481 "signature" : cl .signature ,
467482 "name" : cl .name ,
468- "code" : getattr ( cl , "code" , None ),
483+ "code" : _span_code ( source , cl . span ),
469484 "base_classes" : list (cl .base_classes or []),
470485 "docstring" : _docstring_of (cl .comments ),
471486 "start_line" : cl .start_line ,
@@ -475,7 +490,7 @@ def _class_props(cl: PyClass, file_key: str) -> Props:
475490 )
476491
477492
478- def _callable_props (c : PyCallable , file_key : str ) -> Props :
493+ def _callable_props (c : PyCallable , file_key : str , source : str ) -> Props :
479494 return prune (
480495 {
481496 "id" : c .id ,
@@ -484,7 +499,7 @@ def _callable_props(c: PyCallable, file_key: str) -> Props:
484499 "path" : c .path ,
485500 "return_type" : c .return_type ,
486501 "cyclomatic_complexity" : c .cyclomatic_complexity ,
487- "code" : getattr ( c , "code" , None ),
502+ "code" : _span_code ( source , c . span ),
488503 "code_start_line" : c .code_start_line ,
489504 "start_line" : c .start_line ,
490505 "end_line" : c .end_line ,
0 commit comments