victor-codegraph 0.10.0__tar.gz → 0.10.1__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.
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/PKG-INFO +29 -3
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/README.md +28 -2
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/pyproject.toml +1 -1
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_adapter.py +9 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_adapter_validation_v2.py +27 -0
- victor_codegraph-0.10.1/tests/test_diagnostics_v2.py +46 -0
- victor_codegraph-0.10.1/tests/test_repository_delta_v2.py +128 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_treesitter_jsts.py +35 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/__init__.py +1 -1
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/adapter.py +19 -4
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/parser.py +18 -10
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/repository_index.py +59 -8
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/treesitter_parser.py +3 -1
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph.egg-info/PKG-INFO +29 -3
- victor_codegraph-0.10.0/tests/test_diagnostics_v2.py +0 -26
- victor_codegraph-0.10.0/tests/test_repository_delta_v2.py +0 -46
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/setup.cfg +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_chunk_contract_v2.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_embedding_contract.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_identity_v2.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_line_base_contract.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_manifest.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_python_parser.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_relations_v2.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_repo.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_sizing_and_chunk.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_sizing_overlap.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_symbol_oid_contract.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_symbol_oid_parity.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_treesitter_languages.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/tests/test_treesitter_relations.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/config.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/embeddings.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/javascript_repository.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/languages.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/manifest.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/model.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/py.typed +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/python_parser.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/repo.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/resolution.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/rust_repository.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/sizing.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/treesitter_tables.py +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph.egg-info/SOURCES.txt +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph.egg-info/dependency_links.txt +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph.egg-info/requires.txt +0 -0
- {victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: victor-codegraph
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Repository-to-Code-Semantic-Graph compiler with stable identity, bounded chunks, incremental deltas, and ProximaRecord projection.
|
|
5
5
|
Author-email: Vijaykumar Singh <vijay@anvaiops.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -56,7 +56,7 @@ gaps:
|
|
|
56
56
|
## Install
|
|
57
57
|
|
|
58
58
|
Published on PyPI as [`victor-codegraph`](https://pypi.org/project/victor-codegraph/).
|
|
59
|
-
This source tree is version **0.10.
|
|
59
|
+
This source tree is version **0.10.1**; consumers should use a compatible `<1.0` pin.
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
# from PyPI
|
|
@@ -128,10 +128,36 @@ records = to_proxima_records(repository, repo_graph_id="myrepo")
|
|
|
128
128
|
|
|
129
129
|
## Status
|
|
130
130
|
|
|
131
|
-
`0.10.
|
|
131
|
+
`0.10.1` source version. Python (`ast`) is the full, offline frontend. JS/TS, Go, Rust,
|
|
132
132
|
Java, C, and C++ have conformance fixtures; other mapped tree-sitter languages report
|
|
133
133
|
symbols-tier or fallback status rather than implying full fidelity.
|
|
134
134
|
|
|
135
|
+
### Ambiguous declarations
|
|
136
|
+
|
|
137
|
+
Duplicate structural identities retain raw declarations in `ParsedRepository.files`
|
|
138
|
+
and report each conflicting source location. Repository projection emits only the
|
|
139
|
+
canonical FILE marker for that file, with typed diagnostics, declaration spans and
|
|
140
|
+
omitted symbol/relation counts in metadata. Calls into its declarations remain
|
|
141
|
+
unresolved; healthy imports can still point to its diagnostic marker. Repairing the
|
|
142
|
+
source restores normal projection through deletion-complete repository deltas.
|
|
143
|
+
Direct record projection rejects duplicate identities before invoking an embedder,
|
|
144
|
+
including when legacy OIDs are selected.
|
|
145
|
+
|
|
146
|
+
`identity_collisions` groups use the existing `symbol_id` with `resolution: ambiguous`.
|
|
147
|
+
Each declaration retains `name`, `qualified_name`, `symbol_type` and its full
|
|
148
|
+
`source_span`; these are inspectable evidence, not separately resolved graph nodes.
|
|
149
|
+
The record adapter forwards these groups, projection status and omitted counts only
|
|
150
|
+
when present, without copying arbitrary symbol metadata into exported properties.
|
|
151
|
+
|
|
152
|
+
### TypeScript grammar limitation
|
|
153
|
+
|
|
154
|
+
The current tree-sitter grammar reports a partial parse for inline import types
|
|
155
|
+
in generic calls, for example `load<typeof import('child_process')>()`, even though
|
|
156
|
+
TypeScript accepts that syntax. A named type alias (`type ChildProcessModule =
|
|
157
|
+
typeof import('child_process')`, then `load<ChildProcessModule>()`) is supported.
|
|
158
|
+
Partial status and syntax diagnostics remain visible; the parser does not rewrite
|
|
159
|
+
source or infer missing relationships.
|
|
160
|
+
|
|
135
161
|
### Rust graph fidelity
|
|
136
162
|
|
|
137
163
|
Rust indexing retains inline module scopes and discriminates same-named trait
|
|
@@ -25,7 +25,7 @@ gaps:
|
|
|
25
25
|
## Install
|
|
26
26
|
|
|
27
27
|
Published on PyPI as [`victor-codegraph`](https://pypi.org/project/victor-codegraph/).
|
|
28
|
-
This source tree is version **0.10.
|
|
28
|
+
This source tree is version **0.10.1**; consumers should use a compatible `<1.0` pin.
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
31
|
# from PyPI
|
|
@@ -97,10 +97,36 @@ records = to_proxima_records(repository, repo_graph_id="myrepo")
|
|
|
97
97
|
|
|
98
98
|
## Status
|
|
99
99
|
|
|
100
|
-
`0.10.
|
|
100
|
+
`0.10.1` source version. Python (`ast`) is the full, offline frontend. JS/TS, Go, Rust,
|
|
101
101
|
Java, C, and C++ have conformance fixtures; other mapped tree-sitter languages report
|
|
102
102
|
symbols-tier or fallback status rather than implying full fidelity.
|
|
103
103
|
|
|
104
|
+
### Ambiguous declarations
|
|
105
|
+
|
|
106
|
+
Duplicate structural identities retain raw declarations in `ParsedRepository.files`
|
|
107
|
+
and report each conflicting source location. Repository projection emits only the
|
|
108
|
+
canonical FILE marker for that file, with typed diagnostics, declaration spans and
|
|
109
|
+
omitted symbol/relation counts in metadata. Calls into its declarations remain
|
|
110
|
+
unresolved; healthy imports can still point to its diagnostic marker. Repairing the
|
|
111
|
+
source restores normal projection through deletion-complete repository deltas.
|
|
112
|
+
Direct record projection rejects duplicate identities before invoking an embedder,
|
|
113
|
+
including when legacy OIDs are selected.
|
|
114
|
+
|
|
115
|
+
`identity_collisions` groups use the existing `symbol_id` with `resolution: ambiguous`.
|
|
116
|
+
Each declaration retains `name`, `qualified_name`, `symbol_type` and its full
|
|
117
|
+
`source_span`; these are inspectable evidence, not separately resolved graph nodes.
|
|
118
|
+
The record adapter forwards these groups, projection status and omitted counts only
|
|
119
|
+
when present, without copying arbitrary symbol metadata into exported properties.
|
|
120
|
+
|
|
121
|
+
### TypeScript grammar limitation
|
|
122
|
+
|
|
123
|
+
The current tree-sitter grammar reports a partial parse for inline import types
|
|
124
|
+
in generic calls, for example `load<typeof import('child_process')>()`, even though
|
|
125
|
+
TypeScript accepts that syntax. A named type alias (`type ChildProcessModule =
|
|
126
|
+
typeof import('child_process')`, then `load<ChildProcessModule>()`) is supported.
|
|
127
|
+
Partial status and syntax diagnostics remain visible; the parser does not rewrite
|
|
128
|
+
source or infer missing relationships.
|
|
129
|
+
|
|
104
130
|
### Rust graph fidelity
|
|
105
131
|
|
|
106
132
|
Rust indexing retains inline module scopes and discriminates same-named trait
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "victor-codegraph"
|
|
7
|
-
version = "0.10.
|
|
7
|
+
version = "0.10.1"
|
|
8
8
|
description = "Repository-to-Code-Semantic-Graph compiler with stable identity, bounded chunks, incremental deltas, and ProximaRecord projection."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -26,6 +26,15 @@ def test_symbol_records_shape():
|
|
|
26
26
|
assert n["props"]["lang"] == "python"
|
|
27
27
|
assert n["props"]["ast_kind"] in ("FUNCTION", "METHOD", "CONSTRUCTOR")
|
|
28
28
|
assert n["embeddings"] == [] # no embedder supplied
|
|
29
|
+
assert (
|
|
30
|
+
not {
|
|
31
|
+
"identity_collisions",
|
|
32
|
+
"projection_status",
|
|
33
|
+
"omitted_symbol_count",
|
|
34
|
+
"omitted_relation_count",
|
|
35
|
+
}
|
|
36
|
+
& n["props"].keys()
|
|
37
|
+
)
|
|
29
38
|
|
|
30
39
|
|
|
31
40
|
def test_edge_records_reference_node_oids():
|
|
@@ -40,3 +40,30 @@ def test_legacy_oid_mode_keeps_edge_endpoints_attached():
|
|
|
40
40
|
edge = next(r for r in records if "graph_edge" in r["labels"])
|
|
41
41
|
assert edge["edge"]["from_oid"] in nodes
|
|
42
42
|
assert edge["edge"]["to_oid"] in nodes
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@pytest.mark.parametrize("stable_oid", [True, False])
|
|
46
|
+
@pytest.mark.parametrize("embedding_mode", ["embedder", "batch_embedder"])
|
|
47
|
+
def test_duplicate_symbol_ids_fail_before_embedding(stable_oid, embedding_mode):
|
|
48
|
+
from unittest.mock import Mock
|
|
49
|
+
|
|
50
|
+
parsed = parse("def f(): return 1\ndef f(): return 2\n", file_path="duplicate.py")
|
|
51
|
+
embedder = Mock(
|
|
52
|
+
return_value=[0.0, 0.0] if embedding_mode == "embedder" else [[0.0, 0.0], [0.0, 0.0]]
|
|
53
|
+
)
|
|
54
|
+
with pytest.raises(ValueError, match="identity collision"):
|
|
55
|
+
to_proxima_records(
|
|
56
|
+
parsed, "repo", stable_oid=stable_oid, dim=2, **{embedding_mode: embedder}
|
|
57
|
+
)
|
|
58
|
+
embedder.assert_not_called()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_symbol_alias_cannot_rebind_another_canonical_identity():
|
|
62
|
+
from unittest.mock import Mock
|
|
63
|
+
|
|
64
|
+
parsed = parse("def a(): pass\ndef b(): pass\n", file_path="aliases.py")
|
|
65
|
+
parsed.symbols[1].legacy_id = parsed.symbols[0].id
|
|
66
|
+
embedder = Mock(return_value=[0.0, 0.0])
|
|
67
|
+
with pytest.raises(ValueError, match="alias identity collision"):
|
|
68
|
+
to_proxima_records(parsed, "repo", embedder=embedder, dim=2)
|
|
69
|
+
embedder.assert_not_called()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from victor_codegraph import CapabilityTier, ChunkConfig, ParseStatus, parse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_syntax_error_reports_fallback_diagnostic():
|
|
7
|
+
parsed = parse("def broken(:\n", file_path="bad.py")
|
|
8
|
+
assert parsed.status == ParseStatus.FALLBACK
|
|
9
|
+
assert parsed.diagnostics
|
|
10
|
+
assert parsed.diagnostics[0].code == "syntax_error"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_unknown_language_reports_fallback_instead_of_silent_success():
|
|
14
|
+
parsed = parse("plain text", language="unknown", file_path="x.unknown")
|
|
15
|
+
assert parsed.status == ParseStatus.FALLBACK
|
|
16
|
+
assert parsed.capability_tier == CapabilityTier.FALLBACK
|
|
17
|
+
assert parsed.diagnostics
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_extract_relations_config_is_effective():
|
|
21
|
+
parsed = parse(
|
|
22
|
+
"def f():\n return g()\n",
|
|
23
|
+
file_path="m.py",
|
|
24
|
+
config=ChunkConfig(extract_relations=False),
|
|
25
|
+
)
|
|
26
|
+
assert parsed.relations == []
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_shadowed_python_declarations_remain_explicit_identity_collisions():
|
|
30
|
+
parsed = parse(
|
|
31
|
+
"class C:\n @property\n def value(self): return 1\n"
|
|
32
|
+
" @property\n def value(self): return 2\n",
|
|
33
|
+
file_path="duplicate.py",
|
|
34
|
+
)
|
|
35
|
+
assert parsed.status == ParseStatus.PARTIAL
|
|
36
|
+
assert [(item.code, item.severity) for item in parsed.diagnostics] == [
|
|
37
|
+
("identity_collision", "error"),
|
|
38
|
+
("identity_collision", "error"),
|
|
39
|
+
]
|
|
40
|
+
assert [(item.line, item.column) for item in parsed.diagnostics] == [(2, 4), (4, 4)]
|
|
41
|
+
definitions = [symbol for symbol in parsed.symbols if symbol.simple_name == "value"]
|
|
42
|
+
assert len(definitions) == 2
|
|
43
|
+
assert definitions[0].id == definitions[1].id
|
|
44
|
+
assert definitions[0].legacy_id != definitions[1].legacy_id
|
|
45
|
+
assert all("duplicate.py::C::value" in item.message for item in parsed.diagnostics)
|
|
46
|
+
assert all(definitions[0].id in item.message for item in parsed.diagnostics)
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import replace
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from victor_codegraph import apply_index_delta, diff_repository, parse_repo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_repository_delta_deletes_removed_and_replaced_symbols(tmp_path):
|
|
11
|
+
a = tmp_path / "a.py"
|
|
12
|
+
b = tmp_path / "b.py"
|
|
13
|
+
a.write_text("def old_name():\n return 1\n")
|
|
14
|
+
b.write_text("def removed():\n return 2\n")
|
|
15
|
+
previous = parse_repo(tmp_path, repo_id="repo")
|
|
16
|
+
|
|
17
|
+
a.write_text("def new_name():\n return 1\n")
|
|
18
|
+
b.unlink()
|
|
19
|
+
current = parse_repo(tmp_path, repo_id="repo")
|
|
20
|
+
delta = diff_repository(current, previous)
|
|
21
|
+
|
|
22
|
+
old_function_ids = {s.id for s in previous.symbols if s.symbol_type.name == "FUNCTION"}
|
|
23
|
+
assert old_function_ids <= set(delta.delete_symbol_ids)
|
|
24
|
+
assert "new_name" in {s.simple_name for s in delta.upsert_symbols}
|
|
25
|
+
|
|
26
|
+
rebuilt = apply_index_delta(previous, delta)
|
|
27
|
+
assert {s.id for s in rebuilt.symbols} == {s.id for s in current.symbols}
|
|
28
|
+
assert rebuilt.manifest == current.manifest
|
|
29
|
+
assert delta.base_generation_id == previous.generation_id
|
|
30
|
+
assert delta.target_generation_id == current.generation_id
|
|
31
|
+
|
|
32
|
+
with pytest.raises(ValueError, match="base generation"):
|
|
33
|
+
apply_index_delta(replace(previous, generation_id="stale"), delta)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_repository_delta_refreshes_relation_evidence_on_line_shift(tmp_path):
|
|
37
|
+
path = tmp_path / "m.py"
|
|
38
|
+
path.write_text("def a():\n return b()\n\ndef b(): return 1\n")
|
|
39
|
+
previous = parse_repo(tmp_path, repo_id="repo")
|
|
40
|
+
path.write_text("\ndef a():\n return b()\n\ndef b(): return 1\n")
|
|
41
|
+
current = parse_repo(tmp_path, repo_id="repo")
|
|
42
|
+
|
|
43
|
+
delta = diff_repository(current, previous)
|
|
44
|
+
assert delta.upsert_relations
|
|
45
|
+
rebuilt = apply_index_delta(previous, delta)
|
|
46
|
+
assert rebuilt.relations[0].call_site.start_line == current.relations[0].call_site.start_line
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_repository_quarantines_identity_collisions_and_delta_recovers(tmp_path):
|
|
50
|
+
from victor_codegraph import CodeRelationType, CodeSymbolType, ParseStatus
|
|
51
|
+
|
|
52
|
+
ambiguous = tmp_path / "duplicate.py"
|
|
53
|
+
ambiguous.write_text("def helper(): return 1\n")
|
|
54
|
+
(tmp_path / "caller.py").write_text(
|
|
55
|
+
"from duplicate import helper\ndef caller(): return helper()\n"
|
|
56
|
+
)
|
|
57
|
+
previous = parse_repo(tmp_path, repo_id="repo")
|
|
58
|
+
ambiguous.write_text(
|
|
59
|
+
"def f(): return helper()\ndef f(): return helper()\ndef helper(): return 1\n"
|
|
60
|
+
)
|
|
61
|
+
current = parse_repo(tmp_path, repo_id="repo")
|
|
62
|
+
raw = current.files["duplicate.py"]
|
|
63
|
+
assert raw.status == ParseStatus.PARTIAL
|
|
64
|
+
assert len(raw.symbols) == 3 # Keep original evidence, including both definitions.
|
|
65
|
+
projected = [s for s in current.symbols if s.location.file_path == "duplicate.py"]
|
|
66
|
+
assert len(projected) == 1
|
|
67
|
+
marker = projected[0]
|
|
68
|
+
assert marker.symbol_type == CodeSymbolType.FILE
|
|
69
|
+
assert marker.metadata["parse_status"] == "partial"
|
|
70
|
+
assert marker.metadata["projection_status"] == "file_marker_only"
|
|
71
|
+
assert marker.metadata["omitted_symbol_count"] == 3
|
|
72
|
+
assert marker.metadata["omitted_relation_count"] == len(raw.relations)
|
|
73
|
+
assert [d["line"] for d in marker.metadata["diagnostics"]] == [1, 2]
|
|
74
|
+
collisions = marker.metadata["identity_collisions"]
|
|
75
|
+
assert len(collisions) == 1 and collisions[0]["symbol_id"] == raw.symbols[0].id
|
|
76
|
+
assert collisions[0]["resolution"] == "ambiguous"
|
|
77
|
+
assert [d["qualified_name"] for d in collisions[0]["declarations"]] == [
|
|
78
|
+
"duplicate.py::f",
|
|
79
|
+
"duplicate.py::f",
|
|
80
|
+
]
|
|
81
|
+
assert [d["symbol_type"] for d in collisions[0]["declarations"]] == ["function", "function"]
|
|
82
|
+
assert [d["source_span"]["start_line"] for d in collisions[0]["declarations"]] == [1, 2]
|
|
83
|
+
from victor_codegraph import to_proxima_records
|
|
84
|
+
|
|
85
|
+
marker.metadata["private_source_metadata"] = "must not be exported"
|
|
86
|
+
records = to_proxima_records(current, repo_graph_id="repo")
|
|
87
|
+
record = next(
|
|
88
|
+
r
|
|
89
|
+
for r in records
|
|
90
|
+
if r.get("props", {}).get("file") == "duplicate.py"
|
|
91
|
+
and r.get("props", {}).get("ast_kind") == "FILE"
|
|
92
|
+
)
|
|
93
|
+
for key in (
|
|
94
|
+
"identity_collisions",
|
|
95
|
+
"projection_status",
|
|
96
|
+
"omitted_symbol_count",
|
|
97
|
+
"omitted_relation_count",
|
|
98
|
+
):
|
|
99
|
+
assert record["props"][key] == marker.metadata[key]
|
|
100
|
+
assert "private_source_metadata" not in record["props"]
|
|
101
|
+
assert not any(
|
|
102
|
+
"/external/" not in r["edge"]["to_oid"]
|
|
103
|
+
for r in records
|
|
104
|
+
if "graph_edge" in r["labels"] and r["edge"]["edge_type"] == "CALLS"
|
|
105
|
+
)
|
|
106
|
+
assert len({s.id for s in current.symbols}) == len(current.symbols)
|
|
107
|
+
assert not any(r.from_symbol_id == marker.id for r in current.relations)
|
|
108
|
+
calls = [r for r in current.relations if r.relation_type == CodeRelationType.CALLS]
|
|
109
|
+
assert len(calls) == 1 and calls[0].target_ref.name == "helper"
|
|
110
|
+
assert any(
|
|
111
|
+
r.relation_type == CodeRelationType.IMPORTS and r.to_symbol_id == marker.id
|
|
112
|
+
for r in current.relations
|
|
113
|
+
)
|
|
114
|
+
delta = diff_repository(current, previous)
|
|
115
|
+
assert delta.delete_symbol_ids and delta.delete_relation_keys
|
|
116
|
+
rebuilt = apply_index_delta(previous, delta)
|
|
117
|
+
assert {s.id for s in rebuilt.symbols} == {s.id for s in current.symbols}
|
|
118
|
+
|
|
119
|
+
ambiguous.write_text("def helper(): return 2\n")
|
|
120
|
+
repaired = parse_repo(tmp_path, repo_id="repo")
|
|
121
|
+
rebuilt = apply_index_delta(current, diff_repository(repaired, current))
|
|
122
|
+
assert {s.id for s in rebuilt.symbols} == {s.id for s in repaired.symbols}
|
|
123
|
+
calls = [r for r in rebuilt.relations if r.relation_type == CodeRelationType.CALLS]
|
|
124
|
+
assert len(calls) == 1 and calls[0].target_ref is None
|
|
125
|
+
assert (
|
|
126
|
+
next(s for s in repaired.symbols if s.id == marker.id).metadata["projection_status"]
|
|
127
|
+
== "symbols"
|
|
128
|
+
)
|
|
@@ -75,3 +75,38 @@ def test_chunk_jsts_is_size_capped():
|
|
|
75
75
|
if not parsed.symbols:
|
|
76
76
|
pytest.skip("javascript grammar not installed")
|
|
77
77
|
assert parsed.symbols # routed through tree-sitter, not the empty stub
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.parametrize(
|
|
81
|
+
"annotation",
|
|
82
|
+
[
|
|
83
|
+
"export type X<T> = T extends object ? T : never;",
|
|
84
|
+
"type Options = Record<string, object>;",
|
|
85
|
+
"function accept(value: object): object { return value; }",
|
|
86
|
+
],
|
|
87
|
+
)
|
|
88
|
+
def test_typescript_object_keyword_is_not_an_object_literal_scope(annotation):
|
|
89
|
+
source = annotation + "\nconst handlers = { run() { return 1; } };"
|
|
90
|
+
parsed = parse(source, file_path="case.ts")
|
|
91
|
+
shifted = parse("// heading\n\n" + source, file_path="case.ts")
|
|
92
|
+
assert parsed.status.value == "success"
|
|
93
|
+
assert not parsed.diagnostics
|
|
94
|
+
methods = [symbol for symbol in parsed.symbols if symbol.simple_name == "run"]
|
|
95
|
+
assert len(methods) == 1
|
|
96
|
+
assert methods[0].scope_chain
|
|
97
|
+
assert {symbol.id for symbol in parsed.symbols} == {symbol.id for symbol in shifted.symbols}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_inline_import_generic_grammar_limitation_stays_explicit():
|
|
101
|
+
# The pinned grammar rejects this valid TypeScript import type in a generic
|
|
102
|
+
# call. Do not hide the partial parse; an equivalent named alias is supported.
|
|
103
|
+
inline = parse("const value = load<typeof import('child_process')>();", file_path="case.ts")
|
|
104
|
+
assert inline.status.value == "partial"
|
|
105
|
+
assert "syntax_error" in {diagnostic.code for diagnostic in inline.diagnostics}
|
|
106
|
+
aliased = parse(
|
|
107
|
+
"type ChildProcessModule = typeof import('child_process');"
|
|
108
|
+
"const value = load<ChildProcessModule>();",
|
|
109
|
+
file_path="case.ts",
|
|
110
|
+
)
|
|
111
|
+
assert aliased.status.value == "success"
|
|
112
|
+
assert not aliased.diagnostics
|
|
@@ -145,6 +145,15 @@ def symbol_to_record(
|
|
|
145
145
|
},
|
|
146
146
|
"embeddings": [],
|
|
147
147
|
}
|
|
148
|
+
# Preserve compiler-owned ambiguity evidence without exposing arbitrary metadata.
|
|
149
|
+
for key in (
|
|
150
|
+
"identity_collisions",
|
|
151
|
+
"projection_status",
|
|
152
|
+
"omitted_symbol_count",
|
|
153
|
+
"omitted_relation_count",
|
|
154
|
+
):
|
|
155
|
+
if key in symbol.metadata:
|
|
156
|
+
record["props"][key] = symbol.metadata[key]
|
|
148
157
|
if embedder is not None and not symbol.metadata.get("embedding_excluded"):
|
|
149
158
|
record["embeddings"].append(
|
|
150
159
|
{
|
|
@@ -241,14 +250,24 @@ def to_proxima_records(
|
|
|
241
250
|
|
|
242
251
|
id_map: dict[str, str] = {}
|
|
243
252
|
use_stable = _stable_oid_enabled(stable_oid)
|
|
253
|
+
seen_symbols: set[str] = set()
|
|
254
|
+
seen_oids: set[str] = set()
|
|
244
255
|
for symbol in parsed.symbols:
|
|
245
256
|
primary = (
|
|
246
257
|
_canonical_symbol_oid(repo_graph_id, symbol)
|
|
247
258
|
if use_stable
|
|
248
259
|
else _legacy_symbol_oid(repo_graph_id, symbol)
|
|
249
260
|
)
|
|
261
|
+
# Validate before invoking either embedding callback. A legacy OID
|
|
262
|
+
# preference must not hide ambiguous canonical symbol identities.
|
|
263
|
+
if symbol.id in seen_symbols or primary in seen_oids:
|
|
264
|
+
raise ValueError("canonical symbol identity collision in adapter input")
|
|
265
|
+
seen_symbols.add(symbol.id)
|
|
266
|
+
seen_oids.add(primary)
|
|
250
267
|
for key in (symbol.id, symbol.legacy_id):
|
|
251
268
|
if key:
|
|
269
|
+
if key in id_map and id_map[key] != primary:
|
|
270
|
+
raise ValueError("symbol alias identity collision in adapter input")
|
|
252
271
|
id_map[key] = primary
|
|
253
272
|
records = [
|
|
254
273
|
symbol_to_record(
|
|
@@ -286,10 +305,6 @@ def to_proxima_records(
|
|
|
286
305
|
"values": _validate_vector(values, dim),
|
|
287
306
|
}
|
|
288
307
|
)
|
|
289
|
-
by_oid = [r["oid"] for r in records]
|
|
290
|
-
if len(by_oid) != len(set(by_oid)):
|
|
291
|
-
raise ValueError("canonical symbol identity collision in adapter input")
|
|
292
|
-
|
|
293
308
|
external_records: dict[str, dict[str, Any]] = {}
|
|
294
309
|
for relation in parsed.relations:
|
|
295
310
|
if relation.target_ref is None:
|
|
@@ -16,6 +16,7 @@ from .model import (
|
|
|
16
16
|
CapabilityTier,
|
|
17
17
|
CodeChunk,
|
|
18
18
|
CodeRelationType,
|
|
19
|
+
CodeSymbol,
|
|
19
20
|
CodeSymbolType,
|
|
20
21
|
ParseDiagnostic,
|
|
21
22
|
ParsedCode,
|
|
@@ -52,7 +53,7 @@ def _finalize_identity(parsed: ParsedCode, content: str) -> ParsedCode:
|
|
|
52
53
|
"""Promote parser-local line IDs to the v2 structural identity contract."""
|
|
53
54
|
|
|
54
55
|
id_map: dict[str, str] = {}
|
|
55
|
-
|
|
56
|
+
groups: dict[str, list[CodeSymbol]] = {}
|
|
56
57
|
for symbol in parsed.symbols:
|
|
57
58
|
legacy = symbol.legacy_id or symbol.id
|
|
58
59
|
stable = stable_symbol_key(
|
|
@@ -62,22 +63,29 @@ def _finalize_identity(parsed: ParsedCode, content: str) -> ParsedCode:
|
|
|
62
63
|
symbol.signature,
|
|
63
64
|
symbol.location.file_path,
|
|
64
65
|
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
groups.setdefault(stable, []).append(symbol)
|
|
67
|
+
id_map[legacy] = stable
|
|
68
|
+
symbol.legacy_id = legacy
|
|
69
|
+
symbol.id = stable
|
|
70
|
+
symbol.identity_version = "v2"
|
|
71
|
+
|
|
72
|
+
for stable, definitions in groups.items():
|
|
73
|
+
if len(definitions) < 2:
|
|
74
|
+
continue
|
|
75
|
+
parsed.status = ParseStatus.PARTIAL
|
|
76
|
+
for symbol in definitions:
|
|
68
77
|
parsed.diagnostics.append(
|
|
69
78
|
ParseDiagnostic(
|
|
70
79
|
code="identity_collision",
|
|
71
|
-
message=
|
|
80
|
+
message=(
|
|
81
|
+
f"multiple declarations of {symbol.fully_qualified_name!r} "
|
|
82
|
+
f"share structural key {stable}"
|
|
83
|
+
),
|
|
72
84
|
severity="error",
|
|
73
85
|
line=symbol.location.start_line,
|
|
86
|
+
column=symbol.location.start_column,
|
|
74
87
|
)
|
|
75
88
|
)
|
|
76
|
-
seen[stable] = legacy
|
|
77
|
-
id_map[legacy] = stable
|
|
78
|
-
symbol.legacy_id = legacy
|
|
79
|
-
symbol.id = stable
|
|
80
|
-
symbol.identity_version = "v2"
|
|
81
89
|
|
|
82
90
|
for relation in parsed.relations:
|
|
83
91
|
relation.from_symbol_id = id_map.get(relation.from_symbol_id, relation.from_symbol_id)
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import ast
|
|
6
6
|
import hashlib
|
|
7
7
|
import os
|
|
8
|
-
from dataclasses import dataclass, field, replace
|
|
8
|
+
from dataclasses import asdict, dataclass, field, replace
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
11
|
from .model import (
|
|
@@ -245,6 +245,40 @@ def _module_candidates(module: str, importer: str) -> tuple[str, ...]:
|
|
|
245
245
|
return f"{stem}.py", f"{stem}/__init__.py"
|
|
246
246
|
|
|
247
247
|
|
|
248
|
+
def _file_projection_metadata(parsed: ParsedCode) -> dict[str, object]:
|
|
249
|
+
"""Keep diagnostic evidence while quarantining ambiguous symbol identities."""
|
|
250
|
+
quarantined = any(item.code == "identity_collision" for item in parsed.diagnostics)
|
|
251
|
+
groups: dict[str, list[CodeSymbol]] = {}
|
|
252
|
+
if quarantined:
|
|
253
|
+
for symbol in parsed.symbols:
|
|
254
|
+
groups.setdefault(symbol.id, []).append(symbol)
|
|
255
|
+
return {
|
|
256
|
+
"parse_status": parsed.status.value,
|
|
257
|
+
"capability_tier": parsed.capability_tier.value,
|
|
258
|
+
"diagnostics": [asdict(item) for item in parsed.diagnostics],
|
|
259
|
+
"projection_status": "file_marker_only" if quarantined else "symbols",
|
|
260
|
+
"omitted_symbol_count": len(parsed.symbols) if quarantined else 0,
|
|
261
|
+
"omitted_relation_count": len(parsed.relations) if quarantined else 0,
|
|
262
|
+
"identity_collisions": [
|
|
263
|
+
{
|
|
264
|
+
"symbol_id": symbol_id,
|
|
265
|
+
"resolution": "ambiguous",
|
|
266
|
+
"declarations": [
|
|
267
|
+
{
|
|
268
|
+
"name": symbol.simple_name,
|
|
269
|
+
"qualified_name": symbol.fully_qualified_name,
|
|
270
|
+
"symbol_type": symbol.symbol_type.name.lower(),
|
|
271
|
+
"source_span": asdict(symbol.location),
|
|
272
|
+
}
|
|
273
|
+
for symbol in declarations
|
|
274
|
+
],
|
|
275
|
+
}
|
|
276
|
+
for symbol_id, declarations in groups.items()
|
|
277
|
+
if len(declarations) > 1
|
|
278
|
+
],
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
248
282
|
def parse_repo(
|
|
249
283
|
root: str | os.PathLike[str],
|
|
250
284
|
*,
|
|
@@ -288,18 +322,33 @@ def parse_repo(
|
|
|
288
322
|
),
|
|
289
323
|
source_code="",
|
|
290
324
|
language=parsed.language,
|
|
291
|
-
metadata={
|
|
325
|
+
metadata={
|
|
326
|
+
"embedding_excluded": True,
|
|
327
|
+
"content_version": parsed.content_hash,
|
|
328
|
+
**_file_projection_metadata(parsed),
|
|
329
|
+
},
|
|
292
330
|
)
|
|
293
331
|
file_symbols[relative] = file_symbol
|
|
294
332
|
|
|
295
|
-
|
|
296
|
-
|
|
333
|
+
# Retain raw ParsedCode in `files` for diagnostics, but never feed ambiguous
|
|
334
|
+
# declarations into symbol lookup or cross-file resolution. The canonical
|
|
335
|
+
# FILE node stays visible, including to healthy files importing this module.
|
|
336
|
+
resolution_files = {
|
|
337
|
+
path: parsed
|
|
338
|
+
for path, parsed in files.items()
|
|
339
|
+
if file_symbols[path].metadata["projection_status"] != "file_marker_only"
|
|
340
|
+
}
|
|
341
|
+
symbols = [
|
|
342
|
+
*file_symbols.values(),
|
|
343
|
+
*(symbol for parsed in resolution_files.values() for symbol in parsed.symbols),
|
|
344
|
+
]
|
|
345
|
+
relations = [relation for parsed in resolution_files.values() for relation in parsed.relations]
|
|
297
346
|
from .rust_repository import RustRepository, use_paths
|
|
298
347
|
|
|
299
|
-
rust = RustRepository(root_path,
|
|
300
|
-
javascript = JavaScriptRepository(
|
|
348
|
+
rust = RustRepository(root_path, resolution_files)
|
|
349
|
+
javascript = JavaScriptRepository(resolution_files)
|
|
301
350
|
rust_bindings: dict[str, list] = {}
|
|
302
|
-
for relative, parsed in
|
|
351
|
+
for relative, parsed in resolution_files.items():
|
|
303
352
|
owner = file_symbols[relative]
|
|
304
353
|
for symbol in parsed.symbols:
|
|
305
354
|
if not symbol.scope_chain:
|
|
@@ -379,7 +428,9 @@ def parse_repo(
|
|
|
379
428
|
)
|
|
380
429
|
# Re-run the scope resolver over the full stable table only for already textual
|
|
381
430
|
# targets, then apply conservative unique-name cross-file linking.
|
|
382
|
-
relations = _resolve_across_files(
|
|
431
|
+
relations = _resolve_across_files(
|
|
432
|
+
symbols, relations, resolution_files, rust_bindings, javascript
|
|
433
|
+
)
|
|
383
434
|
generation_raw = "\n".join(f"{path}\x1f{digest}" for path, digest in sorted(manifest.items()))
|
|
384
435
|
generation_id = hashlib.blake2b(generation_raw.encode(), digest_size=16).hexdigest()
|
|
385
436
|
return ParsedRepository(
|
|
@@ -426,7 +426,9 @@ def _go_receiver_type(node, src: bytes) -> str | None:
|
|
|
426
426
|
def _walk_collect(node, src, file_path, language, frames, symbols, relations, impl_traits):
|
|
427
427
|
for child in _children(node):
|
|
428
428
|
t = child.type
|
|
429
|
-
if t == "object" and language in {"javascript", "typescript", "tsx"}:
|
|
429
|
+
if t == "object" and child.is_named and language in {"javascript", "typescript", "tsx"}:
|
|
430
|
+
# TypeScript also uses an unnamed `object` keyword in type positions.
|
|
431
|
+
# Only named object expressions introduce a literal scope.
|
|
430
432
|
# Anonymous object literals have a structural AST path, not a line ID.
|
|
431
433
|
# Comments/whitespace do not participate; inserting a sibling object
|
|
432
434
|
# can change the ordinal and requires a fresh graph generation.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: victor-codegraph
|
|
3
|
-
Version: 0.10.
|
|
3
|
+
Version: 0.10.1
|
|
4
4
|
Summary: Repository-to-Code-Semantic-Graph compiler with stable identity, bounded chunks, incremental deltas, and ProximaRecord projection.
|
|
5
5
|
Author-email: Vijaykumar Singh <vijay@anvaiops.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -56,7 +56,7 @@ gaps:
|
|
|
56
56
|
## Install
|
|
57
57
|
|
|
58
58
|
Published on PyPI as [`victor-codegraph`](https://pypi.org/project/victor-codegraph/).
|
|
59
|
-
This source tree is version **0.10.
|
|
59
|
+
This source tree is version **0.10.1**; consumers should use a compatible `<1.0` pin.
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
# from PyPI
|
|
@@ -128,10 +128,36 @@ records = to_proxima_records(repository, repo_graph_id="myrepo")
|
|
|
128
128
|
|
|
129
129
|
## Status
|
|
130
130
|
|
|
131
|
-
`0.10.
|
|
131
|
+
`0.10.1` source version. Python (`ast`) is the full, offline frontend. JS/TS, Go, Rust,
|
|
132
132
|
Java, C, and C++ have conformance fixtures; other mapped tree-sitter languages report
|
|
133
133
|
symbols-tier or fallback status rather than implying full fidelity.
|
|
134
134
|
|
|
135
|
+
### Ambiguous declarations
|
|
136
|
+
|
|
137
|
+
Duplicate structural identities retain raw declarations in `ParsedRepository.files`
|
|
138
|
+
and report each conflicting source location. Repository projection emits only the
|
|
139
|
+
canonical FILE marker for that file, with typed diagnostics, declaration spans and
|
|
140
|
+
omitted symbol/relation counts in metadata. Calls into its declarations remain
|
|
141
|
+
unresolved; healthy imports can still point to its diagnostic marker. Repairing the
|
|
142
|
+
source restores normal projection through deletion-complete repository deltas.
|
|
143
|
+
Direct record projection rejects duplicate identities before invoking an embedder,
|
|
144
|
+
including when legacy OIDs are selected.
|
|
145
|
+
|
|
146
|
+
`identity_collisions` groups use the existing `symbol_id` with `resolution: ambiguous`.
|
|
147
|
+
Each declaration retains `name`, `qualified_name`, `symbol_type` and its full
|
|
148
|
+
`source_span`; these are inspectable evidence, not separately resolved graph nodes.
|
|
149
|
+
The record adapter forwards these groups, projection status and omitted counts only
|
|
150
|
+
when present, without copying arbitrary symbol metadata into exported properties.
|
|
151
|
+
|
|
152
|
+
### TypeScript grammar limitation
|
|
153
|
+
|
|
154
|
+
The current tree-sitter grammar reports a partial parse for inline import types
|
|
155
|
+
in generic calls, for example `load<typeof import('child_process')>()`, even though
|
|
156
|
+
TypeScript accepts that syntax. A named type alias (`type ChildProcessModule =
|
|
157
|
+
typeof import('child_process')`, then `load<ChildProcessModule>()`) is supported.
|
|
158
|
+
Partial status and syntax diagnostics remain visible; the parser does not rewrite
|
|
159
|
+
source or infer missing relationships.
|
|
160
|
+
|
|
135
161
|
### Rust graph fidelity
|
|
136
162
|
|
|
137
163
|
Rust indexing retains inline module scopes and discriminates same-named trait
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from victor_codegraph import CapabilityTier, ChunkConfig, ParseStatus, parse
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_syntax_error_reports_fallback_diagnostic():
|
|
7
|
-
parsed = parse("def broken(:\n", file_path="bad.py")
|
|
8
|
-
assert parsed.status == ParseStatus.FALLBACK
|
|
9
|
-
assert parsed.diagnostics
|
|
10
|
-
assert parsed.diagnostics[0].code == "syntax_error"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_unknown_language_reports_fallback_instead_of_silent_success():
|
|
14
|
-
parsed = parse("plain text", language="unknown", file_path="x.unknown")
|
|
15
|
-
assert parsed.status == ParseStatus.FALLBACK
|
|
16
|
-
assert parsed.capability_tier == CapabilityTier.FALLBACK
|
|
17
|
-
assert parsed.diagnostics
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def test_extract_relations_config_is_effective():
|
|
21
|
-
parsed = parse(
|
|
22
|
-
"def f():\n return g()\n",
|
|
23
|
-
file_path="m.py",
|
|
24
|
-
config=ChunkConfig(extract_relations=False),
|
|
25
|
-
)
|
|
26
|
-
assert parsed.relations == []
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import replace
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
|
|
7
|
-
from victor_codegraph import apply_index_delta, diff_repository, parse_repo
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def test_repository_delta_deletes_removed_and_replaced_symbols(tmp_path):
|
|
11
|
-
a = tmp_path / "a.py"
|
|
12
|
-
b = tmp_path / "b.py"
|
|
13
|
-
a.write_text("def old_name():\n return 1\n")
|
|
14
|
-
b.write_text("def removed():\n return 2\n")
|
|
15
|
-
previous = parse_repo(tmp_path, repo_id="repo")
|
|
16
|
-
|
|
17
|
-
a.write_text("def new_name():\n return 1\n")
|
|
18
|
-
b.unlink()
|
|
19
|
-
current = parse_repo(tmp_path, repo_id="repo")
|
|
20
|
-
delta = diff_repository(current, previous)
|
|
21
|
-
|
|
22
|
-
old_function_ids = {s.id for s in previous.symbols if s.symbol_type.name == "FUNCTION"}
|
|
23
|
-
assert old_function_ids <= set(delta.delete_symbol_ids)
|
|
24
|
-
assert "new_name" in {s.simple_name for s in delta.upsert_symbols}
|
|
25
|
-
|
|
26
|
-
rebuilt = apply_index_delta(previous, delta)
|
|
27
|
-
assert {s.id for s in rebuilt.symbols} == {s.id for s in current.symbols}
|
|
28
|
-
assert rebuilt.manifest == current.manifest
|
|
29
|
-
assert delta.base_generation_id == previous.generation_id
|
|
30
|
-
assert delta.target_generation_id == current.generation_id
|
|
31
|
-
|
|
32
|
-
with pytest.raises(ValueError, match="base generation"):
|
|
33
|
-
apply_index_delta(replace(previous, generation_id="stale"), delta)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def test_repository_delta_refreshes_relation_evidence_on_line_shift(tmp_path):
|
|
37
|
-
path = tmp_path / "m.py"
|
|
38
|
-
path.write_text("def a():\n return b()\n\ndef b(): return 1\n")
|
|
39
|
-
previous = parse_repo(tmp_path, repo_id="repo")
|
|
40
|
-
path.write_text("\ndef a():\n return b()\n\ndef b(): return 1\n")
|
|
41
|
-
current = parse_repo(tmp_path, repo_id="repo")
|
|
42
|
-
|
|
43
|
-
delta = diff_repository(current, previous)
|
|
44
|
-
assert delta.upsert_relations
|
|
45
|
-
rebuilt = apply_index_delta(previous, delta)
|
|
46
|
-
assert rebuilt.relations[0].call_site.start_line == current.relations[0].call_site.start_line
|
|
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
|
{victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph/javascript_repository.py
RENAMED
|
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
|
{victor_codegraph-0.10.0 → victor_codegraph-0.10.1}/victor_codegraph.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|