diffgenome 0.1.0__py3-none-any.whl

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.
Files changed (53) hide show
  1. diffgenome/__init__.py +7 -0
  2. diffgenome/__main__.py +240 -0
  3. diffgenome/_collectors/go/dg/dg.go +623 -0
  4. diffgenome/_collectors/go/go.mod +3 -0
  5. diffgenome/_collectors/go/instrument/facts.go +346 -0
  6. diffgenome/_collectors/go/instrument/main.go +484 -0
  7. diffgenome/_collectors/node/instrument.js +289 -0
  8. diffgenome/_collectors/node/jest-setup.js +40 -0
  9. diffgenome/_collectors/node/package-lock.json +35 -0
  10. diffgenome/_collectors/node/package.json +11 -0
  11. diffgenome/_collectors/node/runtime.js +426 -0
  12. diffgenome/ambiguity.py +122 -0
  13. diffgenome/api.py +67 -0
  14. diffgenome/change.py +86 -0
  15. diffgenome/change_artifact.py +310 -0
  16. diffgenome/collect/__init__.py +2 -0
  17. diffgenome/collect/go_test.py +271 -0
  18. diffgenome/collect/node_jest.py +319 -0
  19. diffgenome/collect/py_monitoring.py +985 -0
  20. diffgenome/collect/py_runtime.py +116 -0
  21. diffgenome/collect/py_symbols.py +238 -0
  22. diffgenome/collect/pytest_plugin.py +130 -0
  23. diffgenome/compose.py +469 -0
  24. diffgenome/dependence.py +264 -0
  25. diffgenome/evaluate.py +669 -0
  26. diffgenome/frontends/__init__.py +0 -0
  27. diffgenome/frontends/python_ir.py +335 -0
  28. diffgenome/genome.py +1016 -0
  29. diffgenome/genome_pipeline.py +674 -0
  30. diffgenome/genome_prompt.py +33 -0
  31. diffgenome/genome_state.py +2118 -0
  32. diffgenome/graph.py +426 -0
  33. diffgenome/llm.py +189 -0
  34. diffgenome/model.py +364 -0
  35. diffgenome/mvp.py +398 -0
  36. diffgenome/probe.py +509 -0
  37. diffgenome/projection.py +308 -0
  38. diffgenome/py.typed +0 -0
  39. diffgenome/render.py +118 -0
  40. diffgenome/report.py +363 -0
  41. diffgenome/resolve.py +37 -0
  42. diffgenome/runtime.py +74 -0
  43. diffgenome/runtime_evidence.py +261 -0
  44. diffgenome/sandbox.py +166 -0
  45. diffgenome/serialize.py +96 -0
  46. diffgenome/sites.py +19 -0
  47. diffgenome/static_types.py +69 -0
  48. diffgenome/structure.py +462 -0
  49. diffgenome-0.1.0.dist-info/METADATA +139 -0
  50. diffgenome-0.1.0.dist-info/RECORD +53 -0
  51. diffgenome-0.1.0.dist-info/WHEEL +4 -0
  52. diffgenome-0.1.0.dist-info/entry_points.txt +2 -0
  53. diffgenome-0.1.0.dist-info/licenses/LICENSE +202 -0
diffgenome/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """diffgenome: runtime evidence for code changes.
2
+
3
+ Runs a repository's existing tests against a change and reports what actually executed, as the
4
+ `runtime` section (`diffgenome-runtime/1`) of a `diffgenome-change/1` artifact.
5
+ """
6
+
7
+ __version__ = "0.1.0"
diffgenome/__main__.py ADDED
@@ -0,0 +1,240 @@
1
+ import argparse
2
+ import json
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from diffgenome import mvp
7
+
8
+
9
+ def inspect_main(argv: list[str]) -> int:
10
+ ap = argparse.ArgumentParser(prog="diffgenome inspect")
11
+ ap.add_argument("--graph", required=True, type=Path, help="graph.json written by an mvp run")
12
+ ap.add_argument("--symbol", action="append", default=[], help="symbol id, or a suffix of one")
13
+ ap.add_argument("--up", type=int, default=3)
14
+ ap.add_argument("--down", type=int, default=4)
15
+ ap.add_argument("--json", action="store_true", help="print the neighborhood as JSON")
16
+ ap.add_argument(
17
+ "--ambiguity", action="store_true", help="print the ambiguity/rejected-join report"
18
+ )
19
+ ap.add_argument(
20
+ "--behavior",
21
+ action="store_true",
22
+ help="print the behavioral projection instead of the evidence slice",
23
+ )
24
+ args = ap.parse_args(argv)
25
+ from diffgenome.graph import BehavioralGraph
26
+ from diffgenome.report import render_map_slice
27
+
28
+ graph = BehavioralGraph.from_json(json.loads(args.graph.read_text()))
29
+ if args.ambiguity:
30
+ from diffgenome.ambiguity import render_ambiguity
31
+
32
+ print(render_ambiguity(graph))
33
+ return 0
34
+ if not args.symbol:
35
+ print(f"{len(graph.symbols)} symbols, {len(graph.edges)} edges, {len(graph.gaps)} gaps")
36
+ return 0
37
+ seeds: list[str] = []
38
+ for q in args.symbol:
39
+ matches = [s for s in graph.symbols if s == q or s.endswith(q)]
40
+ if not matches:
41
+ print(f"no symbol matches {q!r}", file=sys.stderr)
42
+ return 1
43
+ seeds.extend(sorted(matches))
44
+ if args.json:
45
+ nb = graph.neighborhood(seeds, up=args.up, down=args.down)
46
+ print(
47
+ json.dumps(
48
+ {
49
+ "seeds": seeds,
50
+ "metrics": nb.metrics().as_dict(),
51
+ "edges": [
52
+ {
53
+ "distance": d,
54
+ "caller": e.caller,
55
+ "callee": e.callee,
56
+ "kind": e.kind.value,
57
+ "best_join": e.best_join.name if e.best_join else None,
58
+ "executions": sorted(e.executions),
59
+ "probe_derived": e.probe_derived,
60
+ }
61
+ for d, e in sorted(
62
+ nb.edges.values(), key=lambda x: (x[0], x[1].caller, x[1].callee)
63
+ )
64
+ ],
65
+ },
66
+ indent=1,
67
+ )
68
+ )
69
+ elif args.behavior:
70
+ from diffgenome.projection import render_behavior_map
71
+
72
+ print(render_behavior_map(graph, seeds, up=args.up, down=args.down))
73
+ else:
74
+ print(render_map_slice(graph, seeds, up=args.up, down=args.down))
75
+ return 0
76
+
77
+
78
+ def evaluate_main(argv: list[str]) -> int:
79
+ ap = argparse.ArgumentParser(prog="diffgenome evaluate")
80
+ ap.add_argument("--graph", required=True, type=Path, help="reconstructed graph.json")
81
+ ap.add_argument("--ground-truth", required=True, type=Path, help="directory of complete traces")
82
+ ap.add_argument("--entry", action="append", required=True, help="entry symbol (exact id)")
83
+ ap.add_argument("--depth", type=int, default=6)
84
+ ap.add_argument("--json", type=Path, default=None, help="also write the evaluation as JSON")
85
+ ap.add_argument(
86
+ "--traces",
87
+ action="append",
88
+ default=[],
89
+ type=Path,
90
+ help="directories of the executions the graph was built from (for seam grading)",
91
+ )
92
+ ap.add_argument(
93
+ "--no-state",
94
+ action="store_true",
95
+ help="rebuild the graph from --traces ignoring state facts (the VALUE-only baseline)",
96
+ )
97
+ args = ap.parse_args(argv)
98
+ from diffgenome.evaluate import (
99
+ evaluate,
100
+ ground_truth_from,
101
+ render_evaluation,
102
+ render_seams,
103
+ seam_precision,
104
+ )
105
+ from diffgenome.graph import BehavioralGraph
106
+ from diffgenome.model import Origin
107
+ from diffgenome.serialize import execution_from_json
108
+
109
+ graph = BehavioralGraph.from_json(json.loads(args.graph.read_text()))
110
+ if args.no_state:
111
+ from diffgenome.compose import build_corpus as _build_corpus
112
+ from diffgenome.graph import build_graph as _build_graph
113
+
114
+ runs = [
115
+ execution_from_json(f.read_text())
116
+ for d in args.traces
117
+ for f in sorted(d.glob("*.json"))
118
+ ]
119
+ graph = _build_graph(_build_corpus(runs), use_state=False)
120
+ print("(graph rebuilt from --traces with state facts ignored: VALUE-only baseline)\n")
121
+ truth_runs = [
122
+ execution_from_json(f.read_text()) for f in sorted(args.ground_truth.glob("*.json"))
123
+ ]
124
+ origins = {s.id: s.origin for e in truth_runs for s in e.symbols}
125
+ origins.update(
126
+ {sid: s.origin for sid, s in graph.symbols.items() if s.origin is not Origin.UNKNOWN}
127
+ )
128
+ gt = ground_truth_from(truth_runs, args.entry, origins)
129
+ ev = evaluate(graph, gt, args.entry, depth=args.depth)
130
+ print(render_evaluation(ev))
131
+ doc = ev.as_dict()
132
+ if args.traces:
133
+ # path level needs the corpus behind the graph
134
+ from diffgenome.compose import build_corpus
135
+ from diffgenome.evaluate import (
136
+ evaluate_paths,
137
+ explain_paths,
138
+ join_matrix,
139
+ render_matrix,
140
+ render_paths,
141
+ )
142
+ from diffgenome.graph import build_graph
143
+
144
+ corpus_runs = [
145
+ execution_from_json(f.read_text())
146
+ for d in args.traces
147
+ for f in sorted(d.glob("*.json"))
148
+ ]
149
+ full = build_graph(build_corpus(corpus_runs), use_state=not args.no_state)
150
+ path_docs: list[dict[str, object]] = []
151
+ for entry in args.entry:
152
+ pe = evaluate_paths(full, truth_runs, entry, origins)
153
+ print(render_paths(pe))
154
+ explanations = explain_paths(pe, full)
155
+ for line in explanations:
156
+ print(" " + line)
157
+ print()
158
+ path_docs.append(
159
+ {
160
+ "entry": entry,
161
+ "truth": pe.truth_paths,
162
+ "claimed": pe.claimed_paths,
163
+ "matched": pe.matched,
164
+ "extra": [list(p) for p in pe.extra],
165
+ "missed": [list(p) for p in pe.missed],
166
+ "outcome_mismatches": pe.outcome_mismatches,
167
+ "explanations": explanations,
168
+ }
169
+ )
170
+ doc["paths"] = path_docs
171
+ if args.traces:
172
+ from diffgenome.model import CallNode, Execution
173
+
174
+ def _repo_edges(ex: Execution) -> set[tuple[str, str]]:
175
+ by_id = {n.id: n for n in ex.nodes}
176
+ edges: set[tuple[str, str]] = set()
177
+ for n in ex.nodes:
178
+ if isinstance(n, CallNode) and n.parent is not None:
179
+ p = by_id[n.parent]
180
+ if (
181
+ isinstance(p, CallNode)
182
+ and origins.get(p.symbol) is Origin.REPO
183
+ and origins.get(n.symbol) is Origin.REPO
184
+ ):
185
+ edges.add((p.symbol, n.symbol))
186
+ return edges
187
+
188
+ per_exec = {ex.id: _repo_edges(ex) for ex in corpus_runs}
189
+ truth_per_exec = [_repo_edges(ex) for ex in truth_runs]
190
+ grades = seam_precision(graph, per_exec, gt)
191
+ print("## Join lattice: seam-level precision")
192
+ print(render_seams(grades))
193
+ doc["seam_grades"] = [g.__dict__ for g in grades]
194
+ rows = join_matrix(full, per_exec, gt, truth_per_exec)
195
+ print("## Join matrix (seams on ground-truth paths)")
196
+ print(render_matrix(rows))
197
+ doc["join_matrix"] = [r.__dict__ for r in rows]
198
+ if args.json:
199
+ args.json.write_text(json.dumps(doc, indent=1) + "\n")
200
+ return 0
201
+
202
+
203
+ def main(argv: list[str] | None = None) -> int:
204
+ argv = list(sys.argv[1:] if argv is None else argv)
205
+ if argv[:1] in (["--version"], ["-V"]):
206
+ from diffgenome import __version__
207
+
208
+ print(f"diffgenome {__version__}")
209
+ return 0
210
+ if argv and argv[0] == "inspect":
211
+ return inspect_main(argv[1:])
212
+ if argv and argv[0] == "evaluate":
213
+ return evaluate_main(argv[1:])
214
+ if argv and argv[0] == "change":
215
+ # The integrator's entry point: same pipeline as `mvp`, existing tests only by
216
+ # default (no LLM call), and the diffgenome-change/1 artifact as the product.
217
+ rest = argv[1:]
218
+ if "--writer" not in rest:
219
+ rest = ["--writer", "none", *rest]
220
+ if "--probes" not in rest:
221
+ rest = ["--probes", "0", *rest]
222
+ if "--up" not in rest:
223
+ # integrators ask "what reaches the change": follow callers far enough to meet
224
+ # an entry point even when the change sits deep below it
225
+ rest = ["--up", "5", *rest]
226
+ if "--probe-max-distance" not in rest:
227
+ # a probe is only worth its cost when the gap sits next to the change
228
+ rest = ["--probe-max-distance", "1", *rest]
229
+ return mvp.main(rest)
230
+ if argv and argv[0] == "genome":
231
+ from diffgenome import genome_pipeline
232
+
233
+ return genome_pipeline.main(argv[1:])
234
+ if argv and argv[0] == "mvp":
235
+ argv = argv[1:]
236
+ return mvp.main(argv)
237
+
238
+
239
+ if __name__ == "__main__":
240
+ sys.exit(main())