codespine 0.9.1__tar.gz → 0.9.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {codespine-0.9.1 → codespine-0.9.2}/PKG-INFO +1 -1
- {codespine-0.9.1 → codespine-0.9.2}/codespine/__init__.py +1 -1
- {codespine-0.9.1 → codespine-0.9.2}/codespine/db/store.py +111 -61
- {codespine-0.9.1 → codespine-0.9.2}/codespine.egg-info/PKG-INFO +1 -1
- {codespine-0.9.1 → codespine-0.9.2}/pyproject.toml +1 -1
- {codespine-0.9.1 → codespine-0.9.2}/LICENSE +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/README.md +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/community.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/context.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/coupling.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/crossmodule.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/deadcode.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/flow.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/analysis/impact.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/cli.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/config.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/db/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/db/schema.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/diff/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/diff/branch_diff.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/guide.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/indexer/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/indexer/call_resolver.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/indexer/di_resolver.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/indexer/engine.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/indexer/java_parser.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/indexer/symbol_builder.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/mcp/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/mcp/server.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/noise/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/noise/blocklist.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/overlay/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/overlay/git_state.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/overlay/merge.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/overlay/store.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/search/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/search/bm25.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/search/fuzzy.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/search/hybrid.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/search/rrf.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/search/vector.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/watch/__init__.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/watch/git_hook.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine/watch/watcher.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine.egg-info/SOURCES.txt +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine.egg-info/dependency_links.txt +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine.egg-info/entry_points.txt +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine.egg-info/requires.txt +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/codespine.egg-info/top_level.txt +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/gindex.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/setup.cfg +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_branch_diff_normalize.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_call_resolver.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_community_detection.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_deadcode.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_index_and_hybrid.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_java_parser.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_multimodule_index.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_overlay.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_search_ranking.py +0 -0
- {codespine-0.9.1 → codespine-0.9.2}/tests/test_store_recovery.py +0 -0
|
@@ -297,14 +297,20 @@ class GraphStore:
|
|
|
297
297
|
)
|
|
298
298
|
|
|
299
299
|
def upsert_files_batch(self, records: list[dict[str, Any]]) -> None:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
300
|
+
if not records:
|
|
301
|
+
return
|
|
302
|
+
self.execute(
|
|
303
|
+
"""
|
|
304
|
+
UNWIND $rows AS row
|
|
305
|
+
MERGE (f:File {id: row.id})
|
|
306
|
+
SET f.path = row.path,
|
|
307
|
+
f.project_id = row.project_id,
|
|
308
|
+
f.is_test = row.is_test,
|
|
309
|
+
f.hash = row.hash
|
|
310
|
+
""",
|
|
311
|
+
{"rows": [{"id": r["id"], "path": r["path"], "project_id": r["project_id"],
|
|
312
|
+
"is_test": bool(r["is_test"]), "hash": r["hash"]} for r in records]},
|
|
313
|
+
)
|
|
308
314
|
|
|
309
315
|
def upsert_class(self, class_id: str, fqcn: str, name: str, package: str, file_id: str) -> None:
|
|
310
316
|
self.execute(
|
|
@@ -322,14 +328,20 @@ class GraphStore:
|
|
|
322
328
|
)
|
|
323
329
|
|
|
324
330
|
def upsert_classes_batch(self, records: list[dict[str, Any]]) -> None:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
331
|
+
if not records:
|
|
332
|
+
return
|
|
333
|
+
self.execute(
|
|
334
|
+
"""
|
|
335
|
+
UNWIND $rows AS row
|
|
336
|
+
MERGE (c:Class {id: row.id})
|
|
337
|
+
SET c.fqcn = row.fqcn,
|
|
338
|
+
c.name = row.name,
|
|
339
|
+
c.package = row.package,
|
|
340
|
+
c.file_id = row.file_id
|
|
341
|
+
""",
|
|
342
|
+
{"rows": [{"id": r["id"], "fqcn": r["fqcn"], "name": r["name"],
|
|
343
|
+
"package": r["package"], "file_id": r["file_id"]} for r in records]},
|
|
344
|
+
)
|
|
333
345
|
|
|
334
346
|
def upsert_method(
|
|
335
347
|
self,
|
|
@@ -370,17 +382,28 @@ class GraphStore:
|
|
|
370
382
|
)
|
|
371
383
|
|
|
372
384
|
def upsert_methods_batch(self, records: list[dict[str, Any]]) -> None:
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
385
|
+
if not records:
|
|
386
|
+
return
|
|
387
|
+
# Single UNWIND: upsert node + HAS_METHOD relationship in one round-trip.
|
|
388
|
+
self.execute(
|
|
389
|
+
"""
|
|
390
|
+
UNWIND $rows AS row
|
|
391
|
+
MATCH (c:Class {id: row.class_id})
|
|
392
|
+
MERGE (m:Method {id: row.id})
|
|
393
|
+
SET m.class_id = row.class_id,
|
|
394
|
+
m.name = row.name,
|
|
395
|
+
m.signature = row.signature,
|
|
396
|
+
m.return_type = row.return_type,
|
|
397
|
+
m.modifiers = row.modifiers,
|
|
398
|
+
m.is_constructor = row.is_constructor,
|
|
399
|
+
m.is_test = row.is_test
|
|
400
|
+
MERGE (c)-[:HAS_METHOD]->(m)
|
|
401
|
+
""",
|
|
402
|
+
{"rows": [{"id": r["id"], "class_id": r["class_id"], "name": r["name"],
|
|
403
|
+
"signature": r["signature"], "return_type": r["return_type"],
|
|
404
|
+
"modifiers": r["modifiers"], "is_constructor": bool(r["is_constructor"]),
|
|
405
|
+
"is_test": bool(r["is_test"])} for r in records]},
|
|
406
|
+
)
|
|
384
407
|
|
|
385
408
|
def upsert_symbol(
|
|
386
409
|
self,
|
|
@@ -421,17 +444,28 @@ class GraphStore:
|
|
|
421
444
|
)
|
|
422
445
|
|
|
423
446
|
def upsert_symbols_batch(self, records: list[dict[str, Any]]) -> None:
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
447
|
+
if not records:
|
|
448
|
+
return
|
|
449
|
+
# Single UNWIND: upsert node + DECLARES relationship in one round-trip.
|
|
450
|
+
self.execute(
|
|
451
|
+
"""
|
|
452
|
+
UNWIND $rows AS row
|
|
453
|
+
MATCH (f:File {id: row.file_id})
|
|
454
|
+
MERGE (s:Symbol {id: row.id})
|
|
455
|
+
SET s.kind = row.kind,
|
|
456
|
+
s.name = row.name,
|
|
457
|
+
s.fqname = row.fqname,
|
|
458
|
+
s.file_id = row.file_id,
|
|
459
|
+
s.line = row.line,
|
|
460
|
+
s.col = row.col,
|
|
461
|
+
s.embedding = row.embedding
|
|
462
|
+
MERGE (f)-[:DECLARES]->(s)
|
|
463
|
+
""",
|
|
464
|
+
{"rows": [{"id": r["id"], "kind": r["kind"], "name": r["name"],
|
|
465
|
+
"fqname": r["fqname"], "file_id": r["file_id"],
|
|
466
|
+
"line": int(r["line"]), "col": int(r["col"]),
|
|
467
|
+
"embedding": r.get("embedding")} for r in records]},
|
|
468
|
+
)
|
|
435
469
|
|
|
436
470
|
def add_call(self, source_id: str, target_id: str, confidence: float, reason: str) -> None:
|
|
437
471
|
self.execute(
|
|
@@ -448,13 +482,18 @@ class GraphStore:
|
|
|
448
482
|
)
|
|
449
483
|
|
|
450
484
|
def add_calls_batch(self, records: list[dict[str, Any]]) -> None:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
)
|
|
485
|
+
if not records:
|
|
486
|
+
return
|
|
487
|
+
self.execute(
|
|
488
|
+
"""
|
|
489
|
+
UNWIND $rows AS row
|
|
490
|
+
MATCH (src:Method {id: row.source_id}), (dst:Method {id: row.target_id})
|
|
491
|
+
MERGE (src)-[:CALLS {confidence: row.confidence, reason: row.reason}]->(dst)
|
|
492
|
+
""",
|
|
493
|
+
{"rows": [{"source_id": r["source_id"], "target_id": r["target_id"],
|
|
494
|
+
"confidence": float(r["confidence"]), "reason": r["reason"]}
|
|
495
|
+
for r in records]},
|
|
496
|
+
)
|
|
458
497
|
|
|
459
498
|
def add_reference(self, rel: str, src_label: str, src_id: str, dst_label: str, dst_id: str, confidence: float) -> None:
|
|
460
499
|
if rel not in {"REFERENCES_TYPE", "IMPLEMENTS", "OVERRIDES"}:
|
|
@@ -466,14 +505,25 @@ class GraphStore:
|
|
|
466
505
|
self.execute(query, {"src_id": src_id, "dst_id": dst_id, "confidence": confidence})
|
|
467
506
|
|
|
468
507
|
def add_references_batch(self, records: list[dict[str, Any]]) -> None:
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
508
|
+
if not records:
|
|
509
|
+
return
|
|
510
|
+
# Group by (rel, src_label, dst_label) so each group can use a single UNWIND.
|
|
511
|
+
from collections import defaultdict
|
|
512
|
+
groups: dict[tuple, list[dict]] = defaultdict(list)
|
|
513
|
+
for rec in records:
|
|
514
|
+
rel = rec.get("rel")
|
|
515
|
+
if rel not in {"REFERENCES_TYPE", "IMPLEMENTS", "OVERRIDES"}:
|
|
516
|
+
continue
|
|
517
|
+
groups[(rel, rec["src_label"], rec["dst_label"])].append(
|
|
518
|
+
{"src_id": rec["src_id"], "dst_id": rec["dst_id"],
|
|
519
|
+
"confidence": float(rec["confidence"])}
|
|
520
|
+
)
|
|
521
|
+
for (rel, src_label, dst_label), batch in groups.items():
|
|
522
|
+
self.execute(
|
|
523
|
+
f"UNWIND $rows AS row "
|
|
524
|
+
f"MATCH (s:{src_label} {{id: row.src_id}}), (d:{dst_label} {{id: row.dst_id}}) "
|
|
525
|
+
f"MERGE (s)-[:{rel} {{confidence: row.confidence}}]->(d)",
|
|
526
|
+
{"rows": batch},
|
|
477
527
|
)
|
|
478
528
|
|
|
479
529
|
def add_injection(
|
|
@@ -603,17 +653,17 @@ class GraphStore:
|
|
|
603
653
|
self.upsert_symbols_batch(batch)
|
|
604
654
|
self._recycle_conn()
|
|
605
655
|
|
|
606
|
-
# 6. Write call edges in sub-batches of 500
|
|
656
|
+
# 6. Write call edges in sub-batches of 500 (normalise key names to match add_calls_batch)
|
|
607
657
|
for i in range(0, len(calls), self._FILE_CALL_SUB_BATCH):
|
|
608
658
|
batch = calls[i: i + self._FILE_CALL_SUB_BATCH]
|
|
659
|
+
normalised = [
|
|
660
|
+
{"source_id": rec["src"], "target_id": rec["dst"],
|
|
661
|
+
"confidence": float(rec.get("confidence", 0.5)),
|
|
662
|
+
"reason": rec.get("reason", "unknown")}
|
|
663
|
+
for rec in batch
|
|
664
|
+
]
|
|
609
665
|
with self.transaction():
|
|
610
|
-
|
|
611
|
-
self.add_call(
|
|
612
|
-
source_id=rec["src"],
|
|
613
|
-
target_id=rec["dst"],
|
|
614
|
-
confidence=float(rec.get("confidence", 0.5)),
|
|
615
|
-
reason=rec.get("reason", "unknown"),
|
|
616
|
-
)
|
|
666
|
+
self.add_calls_batch(normalised)
|
|
617
667
|
self._recycle_conn()
|
|
618
668
|
|
|
619
669
|
# 7. Write type relations (IMPLEMENTS, OVERRIDES, REFERENCES_TYPE)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|