failtrace 0.2.0__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.
Files changed (51) hide show
  1. failtrace-0.2.0/PKG-INFO +30 -0
  2. failtrace-0.2.0/README.md +0 -0
  3. failtrace-0.2.0/failtrace/__init__.py +0 -0
  4. failtrace-0.2.0/failtrace/__main__.py +4 -0
  5. failtrace-0.2.0/failtrace/analysis/__init__.py +0 -0
  6. failtrace-0.2.0/failtrace/analysis/critical_path.py +53 -0
  7. failtrace-0.2.0/failtrace/analysis/critical_path_extractor.py +205 -0
  8. failtrace-0.2.0/failtrace/analysis/function_extractor.py +530 -0
  9. failtrace-0.2.0/failtrace/analysis/locator.py +157 -0
  10. failtrace-0.2.0/failtrace/analysis/mapper.py +130 -0
  11. failtrace-0.2.0/failtrace/analysis/summarizer.py +47 -0
  12. failtrace-0.2.0/failtrace/cli/__init__.py +0 -0
  13. failtrace-0.2.0/failtrace/cli/main.py +189 -0
  14. failtrace-0.2.0/failtrace/graph/__init__.py +0 -0
  15. failtrace-0.2.0/failtrace/graph/graph_builder/__init__.py +11 -0
  16. failtrace-0.2.0/failtrace/graph/graph_builder/base.py +12 -0
  17. failtrace-0.2.0/failtrace/graph/graph_builder/csharp_graph.py +497 -0
  18. failtrace-0.2.0/failtrace/graph/graph_builder/csharp_graph_builder.py +13 -0
  19. failtrace-0.2.0/failtrace/graph/graph_builder/detector.py +18 -0
  20. failtrace-0.2.0/failtrace/graph/graph_builder/java_graph.py +164 -0
  21. failtrace-0.2.0/failtrace/graph/graph_builder/java_graph_builder.py +13 -0
  22. failtrace-0.2.0/failtrace/graph/graph_builder/plugins.py +28 -0
  23. failtrace-0.2.0/failtrace/graph/graph_builder/python_graph.py +311 -0
  24. failtrace-0.2.0/failtrace/graph/graph_builder/python_graph_builder.py +15 -0
  25. failtrace-0.2.0/failtrace/graph/graph_utils/__init__.py +0 -0
  26. failtrace-0.2.0/failtrace/graph/graph_utils/locator_ranker.py +77 -0
  27. failtrace-0.2.0/failtrace/graph/graph_utils/node_summarizer.py +84 -0
  28. failtrace-0.2.0/failtrace/graph/graph_utils/path_enricher.py +127 -0
  29. failtrace-0.2.0/failtrace/lib/bindings/utils.js +189 -0
  30. failtrace-0.2.0/failtrace/lib/tom-select/tom-select.complete.min.js +356 -0
  31. failtrace-0.2.0/failtrace/lib/tom-select/tom-select.css +334 -0
  32. failtrace-0.2.0/failtrace/lib/vis-9.1.2/vis-network.css +1 -0
  33. failtrace-0.2.0/failtrace/lib/vis-9.1.2/vis-network.min.js +27 -0
  34. failtrace-0.2.0/failtrace/llm/__init__.py +0 -0
  35. failtrace-0.2.0/failtrace/llm/avalai_client.py +105 -0
  36. failtrace-0.2.0/failtrace/llm/few_shots/csharp_async_race.json +10 -0
  37. failtrace-0.2.0/failtrace/llm/few_shots/java_nullpointer.json +10 -0
  38. failtrace-0.2.0/failtrace/llm/few_shots/python_unit_fail.json +10 -0
  39. failtrace-0.2.0/failtrace/llm/few_shots_loader.py +62 -0
  40. failtrace-0.2.0/failtrace/llm/function_extractor.py +100 -0
  41. failtrace-0.2.0/failtrace/llm/prompt_generator.py +170 -0
  42. failtrace-0.2.0/failtrace/llm/structured_prompt_builder.py +141 -0
  43. failtrace-0.2.0/failtrace/tests/__init__.py +0 -0
  44. failtrace-0.2.0/failtrace/tests/test_main.py +0 -0
  45. failtrace-0.2.0/failtrace/utils/__init__.py +0 -0
  46. failtrace-0.2.0/failtrace/utils/file_ops.py +5 -0
  47. failtrace-0.2.0/failtrace/utils/logs_parser.py +578 -0
  48. failtrace-0.2.0/failtrace/utils/normalize.py +128 -0
  49. failtrace-0.2.0/failtrace/utils/report_renderer.py +487 -0
  50. failtrace-0.2.0/failtrace/utils/visualizer.py +36 -0
  51. failtrace-0.2.0/pyproject.toml +43 -0
@@ -0,0 +1,30 @@
1
+ Metadata-Version: 2.3
2
+ Name: failtrace
3
+ Version: 0.2.0
4
+ Summary: Leveraging Large Language Models for Automated Test Results Analysis
5
+ License: MIT
6
+ Keywords: cli,testing,llm,analysis
7
+ Author: Mohadese Akhoondy
8
+ Author-email: m.akhoondy1381@gmail.com
9
+ Requires-Python: >=3.12
10
+ Classifier: Environment :: Console
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Operating System :: OS Independent
14
+ Requires-Dist: javalang (>=0.13.0,<0.14.0)
15
+ Requires-Dist: libcst (>=1.8.2,<2.0.0)
16
+ Requires-Dist: mypy (>=1.17.1,<2.0.0)
17
+ Requires-Dist: networkx (>=3.5,<4.0)
18
+ Requires-Dist: openai (>=1.99.9,<2.0.0)
19
+ Requires-Dist: pycg (>=0.0.8,<0.0.9)
20
+ Requires-Dist: pylint (>=3.3.8,<4.0.0)
21
+ Requires-Dist: pytest (>=8.4.1,<9.0.0)
22
+ Requires-Dist: pytest-json-report (>=1.5.0,<2.0.0)
23
+ Requires-Dist: pyvis (>=0.3.2,<0.4.0)
24
+ Requires-Dist: requests (>=2.32.4,<3.0.0)
25
+ Requires-Dist: rich (>=14.1.0,<15.0.0)
26
+ Requires-Dist: tree-sitter (>=0.25.1,<0.26.0)
27
+ Requires-Dist: tree-sitter-c-sharp (==0.23.1)
28
+ Description-Content-Type: text/markdown
29
+
30
+
File without changes
File without changes
@@ -0,0 +1,4 @@
1
+ from .cli.main import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
File without changes
@@ -0,0 +1,53 @@
1
+ import networkx as nx
2
+ from typing import Dict, List, Any
3
+ from ..graph.graph_utils.path_enricher import enrich_path_with_metadata
4
+
5
+
6
+ def find_critical_paths(graph: nx.DiGraph) -> Dict[str, Dict[str, List[List[Dict[str, Any]]]]]:
7
+ """
8
+ مسیرهای بحرانی برای تست‌های شکست‌خورده را استخراج می‌کند (upstream/downstream)
9
+ و خروجی enriched برمی‌گرداند.
10
+ """
11
+ critical_paths: Dict[str, Dict[str, List[List[Dict[str, Any]]]]] = {}
12
+
13
+ failed_tests = [
14
+ node for node, data in graph.nodes(data=True)
15
+ if data.get("is_test") and data.get("test_status") == "failed"
16
+ ]
17
+
18
+ if not failed_tests:
19
+ # Debugging output
20
+ """ print("[critical] No failed tests found.") """
21
+ return {}
22
+
23
+ for failed_node in failed_tests:
24
+ upstream = []
25
+ downstream = []
26
+
27
+ for node in graph.nodes:
28
+ if node == failed_node:
29
+ continue
30
+
31
+ try:
32
+ paths = nx.all_simple_paths(graph, source=node, target=failed_node, cutoff=6)
33
+ for path in paths:
34
+ enriched_path = enrich_path_with_metadata(graph, path)
35
+ upstream.append(enriched_path)
36
+ except (nx.NetworkXNoPath, nx.NodeNotFound):
37
+ continue
38
+
39
+ try:
40
+ paths = nx.all_simple_paths(graph, source=failed_node, target=node, cutoff=6)
41
+ for path in paths:
42
+ enriched_path = enrich_path_with_metadata(graph, path)
43
+ downstream.append(enriched_path)
44
+ except (nx.NetworkXNoPath, nx.NodeNotFound):
45
+ continue
46
+
47
+ critical_paths[failed_node] = {
48
+ "upstream": upstream,
49
+ "downstream": downstream
50
+ }
51
+ # Debugging output
52
+ """ print(f"[critical] Found {len(critical_paths)} critical test nodes.") """
53
+ return critical_paths
@@ -0,0 +1,205 @@
1
+ import networkx as nx
2
+ from typing import Dict, List, Any, Tuple, Iterable
3
+ from ..graph.graph_utils.path_enricher import enrich_path_with_metadata
4
+
5
+ _EXCLUDED_SUBSTRS = (
6
+ "/.venv/",
7
+ "/venv/",
8
+ "/env/",
9
+ "/.tox/",
10
+ "/site-packages/",
11
+ "/dist-packages/",
12
+ "/.pytest_cache/",
13
+ "/__pycache__/",
14
+ )
15
+
16
+ _DEFAULT_MAX_PATHS_PER_DIR = 5
17
+ _DEFAULT_CUTOFF = 6
18
+
19
+
20
+ def _keep_step(step: Dict[str, Any]) -> bool:
21
+ """
22
+ آیا این گام نگه داشته شود؟
23
+ - نودهای external حذف نمی‌شوند (برای تحلیل LLM لازم‌اند).
24
+ - هر گرهی که file آن داخل مسیرهای محیط/غیرسورس باشد حذف می‌شود.
25
+ """
26
+ if not isinstance(step, dict):
27
+ return False
28
+
29
+ f = (step.get("file") or "").replace("\\", "/").lower()
30
+ if not f:
31
+ return True
32
+
33
+ path = f if f.startswith("/") else f"/{f}"
34
+ for bad in _EXCLUDED_SUBSTRS:
35
+ if bad in path:
36
+ return False
37
+
38
+ return True
39
+
40
+
41
+ def _unique(seq: Iterable[str]) -> List[str]:
42
+ seen = set()
43
+ out: List[str] = []
44
+ for x in seq:
45
+ if x not in seen:
46
+ out.append(x)
47
+ seen.add(x)
48
+ return out
49
+
50
+
51
+ def _attach_origin_for_external_step(graph: nx.DiGraph, step: Dict[str, Any]) -> None:
52
+ """
53
+ برای نود external یک «origin» اضافه می‌کند:
54
+ - callers: فهرست فایل‌هایی که این نماد از آن‌ها فراخوانی شده (سرنخ import/alias).
55
+ - اگر caller فایلی نداشته باشد، حذف می‌شود.
56
+ """
57
+ if step.get("type") != "external":
58
+ return
59
+
60
+ node_id = step.get("node")
61
+ if not node_id or node_id not in graph:
62
+ return
63
+
64
+ callers_files: List[str] = []
65
+ for u in graph.predecessors(node_id):
66
+ f = (graph.nodes.get(u, {}) or {}).get("file")
67
+ if f:
68
+ callers_files.append(str(f).replace("\\", "/"))
69
+
70
+ if not callers_files:
71
+ for v in graph.successors(node_id):
72
+ f = (graph.nodes.get(v, {}) or {}).get("file")
73
+ if f:
74
+ callers_files.append(str(f).replace("\\", "/"))
75
+
76
+ callers_files = _unique(callers_files)
77
+ if callers_files:
78
+ step["origin"] = {"callers": callers_files[:5]}
79
+ else:
80
+ step["origin"] = {"callers": []}
81
+
82
+
83
+ def _augment_externals_with_origin(
84
+ graph: nx.DiGraph, path: List[Dict[str, Any]]
85
+ ) -> None:
86
+ for step in path:
87
+ if (
88
+ isinstance(step, dict)
89
+ and step.get("type") == "external"
90
+ and "origin" not in step
91
+ ):
92
+ _attach_origin_for_external_step(graph, step)
93
+
94
+
95
+ def _path_signature(path: List[Dict[str, Any]]) -> Tuple[str, ...]:
96
+ """امضای یکتا برای مسیر بر مبنای توالی node-id ها."""
97
+ return tuple(
98
+ step["node"] for step in path if isinstance(step, dict) and "node" in step
99
+ )
100
+
101
+
102
+ def _informativeness_score(path: List[Dict[str, Any]]) -> Tuple[int, int]:
103
+ """
104
+ نمره‌ی مفید بودن مسیر:
105
+ - تعداد گره‌های غیرتستی (بیشتر بهتر)
106
+ - طول مسیر (بیشتر بهتر)
107
+ """
108
+ non_test = sum(1 for s in path if not s.get("is_test", False))
109
+ return (non_test, len(path))
110
+
111
+
112
+ def _postprocess_paths(
113
+ paths: List[List[Dict[str, Any]]],
114
+ *,
115
+ drop_short_downstream: bool,
116
+ max_paths: int,
117
+ ) -> List[List[Dict[str, Any]]]:
118
+ """
119
+ Dedup + Filter + Sort + Cap
120
+ drop_short_downstream: اگر True باشد، مسیرهای با طول < 2 حذف می‌شوند.
121
+ """
122
+ uniq: Dict[Tuple[str, ...], List[Dict[str, Any]]] = {}
123
+
124
+ for p in paths:
125
+ if drop_short_downstream and len(p) < 2:
126
+ continue
127
+ sig = _path_signature(p)
128
+ if not sig:
129
+ continue
130
+ old = uniq.get(sig)
131
+ if old is None or _informativeness_score(p) > _informativeness_score(old):
132
+ uniq[sig] = p
133
+
134
+ sorted_paths = sorted(uniq.values(), key=_informativeness_score, reverse=True)
135
+ return sorted_paths[:max_paths]
136
+
137
+
138
+ def find_critical_paths(
139
+ graph: nx.DiGraph,
140
+ *,
141
+ cutoff: int = _DEFAULT_CUTOFF,
142
+ max_paths_per_direction: int = _DEFAULT_MAX_PATHS_PER_DIR,
143
+ ) -> Dict[str, Dict[str, List[List[Dict[str, Any]]]]]:
144
+ """
145
+ مسیرهای بحرانی برای تست‌های شکست‌خورده را استخراج می‌کند (upstream/downstream)
146
+ و خروجی enriched برمی‌گرداند. این نسخه:
147
+ - external ها را نگه می‌دارد و «origin» برایشان اضافه می‌کند.
148
+ - مسیرهای تکراری را حذف و بهترین‌ها را نگه می‌دارد.
149
+ - cutoff و سقف مسیرها قابل‌پیکربندی‌اند.
150
+ """
151
+ critical_paths: Dict[str, Dict[str, List[List[Dict[str, Any]]]]] = {}
152
+
153
+ failed_tests = [
154
+ node
155
+ for node, data in graph.nodes(data=True)
156
+ if data.get("is_test") and data.get("test_status") == "failed"
157
+ ]
158
+
159
+ if not failed_tests:
160
+ return {}
161
+
162
+ for failed_node in failed_tests:
163
+ upstream_raw: List[List[Dict[str, Any]]] = []
164
+ downstream_raw: List[List[Dict[str, Any]]] = []
165
+
166
+ for node in graph.nodes:
167
+ if node == failed_node:
168
+ continue
169
+
170
+ try:
171
+ for path in nx.all_simple_paths(
172
+ graph, source=node, target=failed_node, cutoff=cutoff
173
+ ):
174
+ enriched = enrich_path_with_metadata(graph, path)
175
+ _augment_externals_with_origin(graph, enriched)
176
+ filtered = [step for step in enriched if _keep_step(step)]
177
+ if filtered:
178
+ upstream_raw.append(filtered)
179
+ except (nx.NetworkXNoPath, nx.NodeNotFound):
180
+ pass
181
+
182
+ try:
183
+ for path in nx.all_simple_paths(
184
+ graph, source=failed_node, target=node, cutoff=cutoff
185
+ ):
186
+ enriched = enrich_path_with_metadata(graph, path)
187
+ _augment_externals_with_origin(graph, enriched)
188
+ filtered = [step for step in enriched if _keep_step(step)]
189
+ if filtered:
190
+ downstream_raw.append(filtered)
191
+ except (nx.NetworkXNoPath, nx.NodeNotFound):
192
+ pass
193
+
194
+ upstream = _postprocess_paths(
195
+ upstream_raw, drop_short_downstream=False, max_paths=max_paths_per_direction
196
+ )
197
+ downstream = _postprocess_paths(
198
+ downstream_raw,
199
+ drop_short_downstream=True,
200
+ max_paths=max_paths_per_direction,
201
+ )
202
+
203
+ critical_paths[failed_node] = {"upstream": upstream, "downstream": downstream}
204
+
205
+ return critical_paths