failtrace 0.0.8__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 (54) hide show
  1. failtrace-0.0.8/PKG-INFO +89 -0
  2. failtrace-0.0.8/README.md +62 -0
  3. failtrace-0.0.8/failtrace/__init__.py +0 -0
  4. failtrace-0.0.8/failtrace/__main__.py +4 -0
  5. failtrace-0.0.8/failtrace/analysis/__init__.py +0 -0
  6. failtrace-0.0.8/failtrace/analysis/critical_path_extractor.py +180 -0
  7. failtrace-0.0.8/failtrace/analysis/function_extractor.py +506 -0
  8. failtrace-0.0.8/failtrace/analysis/locator.py +124 -0
  9. failtrace-0.0.8/failtrace/analysis/mapper.py +118 -0
  10. failtrace-0.0.8/failtrace/analysis/summarizer.py +44 -0
  11. failtrace-0.0.8/failtrace/cli/__init__.py +0 -0
  12. failtrace-0.0.8/failtrace/cli/main.py +312 -0
  13. failtrace-0.0.8/failtrace/graph/__init__.py +0 -0
  14. failtrace-0.0.8/failtrace/graph/builder/__init__.py +10 -0
  15. failtrace-0.0.8/failtrace/graph/builder/base.py +9 -0
  16. failtrace-0.0.8/failtrace/graph/builder/csharp_graph.py +547 -0
  17. failtrace-0.0.8/failtrace/graph/builder/csharp_graph_builder.py +10 -0
  18. failtrace-0.0.8/failtrace/graph/builder/detector.py +18 -0
  19. failtrace-0.0.8/failtrace/graph/builder/java_graph.py +164 -0
  20. failtrace-0.0.8/failtrace/graph/builder/java_graph_builder.py +10 -0
  21. failtrace-0.0.8/failtrace/graph/builder/plugins.py +26 -0
  22. failtrace-0.0.8/failtrace/graph/builder/python_graph.py +305 -0
  23. failtrace-0.0.8/failtrace/graph/builder/python_graph_builder.py +10 -0
  24. failtrace-0.0.8/failtrace/graph/utils/__init__.py +0 -0
  25. failtrace-0.0.8/failtrace/graph/utils/locator_ranker.py +62 -0
  26. failtrace-0.0.8/failtrace/graph/utils/node_summarizer.py +76 -0
  27. failtrace-0.0.8/failtrace/graph/utils/path_enricher.py +100 -0
  28. failtrace-0.0.8/failtrace/llm/__init__.py +0 -0
  29. failtrace-0.0.8/failtrace/llm/avalai_client.py +105 -0
  30. failtrace-0.0.8/failtrace/llm/few_shots/__init__.py +0 -0
  31. failtrace-0.0.8/failtrace/llm/few_shots/csharp.json +10 -0
  32. failtrace-0.0.8/failtrace/llm/few_shots/java.json +10 -0
  33. failtrace-0.0.8/failtrace/llm/few_shots/python.json +10 -0
  34. failtrace-0.0.8/failtrace/llm/few_shots_loader.py +53 -0
  35. failtrace-0.0.8/failtrace/llm/prompt_generator.py +160 -0
  36. failtrace-0.0.8/failtrace/llm/structured_prompt_builder.py +141 -0
  37. failtrace-0.0.8/failtrace/output/analysis_report.txt +113 -0
  38. failtrace-0.0.8/failtrace/output/cache/graph.pkl +0 -0
  39. failtrace-0.0.8/failtrace/output/final_prompt.txt +399 -0
  40. failtrace-0.0.8/failtrace/output/function_summaries.json +8 -0
  41. failtrace-0.0.8/failtrace/output/graph.html +164 -0
  42. failtrace-0.0.8/failtrace/output/llm_prompt.json +247 -0
  43. failtrace-0.0.8/failtrace/output/summary.json +38 -0
  44. failtrace-0.0.8/failtrace/report/__init__.py +0 -0
  45. failtrace-0.0.8/failtrace/report/app.css +285 -0
  46. failtrace-0.0.8/failtrace/report/app.js +399 -0
  47. failtrace-0.0.8/failtrace/report/report_template.html +118 -0
  48. failtrace-0.0.8/failtrace/utils/__init__.py +0 -0
  49. failtrace-0.0.8/failtrace/utils/file_ops.py +5 -0
  50. failtrace-0.0.8/failtrace/utils/logs_parser.py +511 -0
  51. failtrace-0.0.8/failtrace/utils/normalize.py +98 -0
  52. failtrace-0.0.8/failtrace/utils/report_renderer.py +457 -0
  53. failtrace-0.0.8/failtrace/utils/visualizer.py +51 -0
  54. failtrace-0.0.8/pyproject.toml +64 -0
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.3
2
+ Name: failtrace
3
+ Version: 0.0.8
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: networkx (>=3.5,<4.0)
17
+ Requires-Dist: openai (>=1.99.9,<2.0.0)
18
+ Requires-Dist: platformdirs (>=4.2,<5.0)
19
+ Requires-Dist: pycg (>=0.0.8,<0.0.9)
20
+ Requires-Dist: pyvis (>=0.3.2,<0.4.0)
21
+ Requires-Dist: requests (>=2.32.4,<3.0.0)
22
+ Requires-Dist: rich (>=14.1.0,<15.0.0)
23
+ Requires-Dist: tree-sitter (>=0.25.1,<0.26.0)
24
+ Requires-Dist: tree-sitter-c-sharp (==0.23.1)
25
+ Description-Content-Type: text/markdown
26
+
27
+ # FailTrace
28
+
29
+ **Leveraging Large Language Models for Automated Test Results Analysis**
30
+
31
+ [![PyPI version](https://badge.fury.io/py/failtrace.svg)](https://pypi.org/project/failtrace/)
32
+ [![Python Version](https://img.shields.io/pypi/pyversions/failtrace)](https://www.python.org/)
33
+
34
+ ---
35
+
36
+ ## 🌍 Overview
37
+
38
+ FailTrace helps developers and QA engineers **analyze automated test results** with **Large Language Models (LLMs)**.
39
+ It builds **dependency graphs**, maps them with test execution logs, and generates **interactive HTML reports** with insights into failures.
40
+
41
+ ---
42
+
43
+ ## ✨ Features
44
+
45
+ - Supports **Python, Java, C#**
46
+ - Dependency graph visualization (PyVis + NetworkX)
47
+ - Test log parsing (Pytest, JSON, TRX, …)
48
+ - Interactive reports (`HTML + CSS + JS`)
49
+ - Dry-run mode (no API calls)
50
+ - CLI-first design for CI/CD
51
+
52
+ ---
53
+
54
+ ## 📦 Installation
55
+
56
+ ```bash
57
+ pip install failtrace
58
+ ````
59
+
60
+ ---
61
+
62
+ ## ⚡ Quickstart
63
+
64
+ Run the **full pipeline**:
65
+
66
+ ```bash
67
+ python -m failtrace full -p ./your-source-code -l ./your-test-results.xml --open-report
68
+ ```
69
+
70
+ Run in **quick mode** (reuse cached graph):
71
+
72
+ ```bash
73
+ failtrace quick -p ./src -l ./results.json
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 📜 License
79
+
80
+ Licensed under the [MIT License](https://opensource.org/licenses/MIT).
81
+
82
+ ---
83
+
84
+ ## 👩‍💻 Author
85
+
86
+ [**Mohadese Akhoondy**](mailto:m.akhoondy1381@gmail.com)
87
+
88
+ ```
89
+
@@ -0,0 +1,62 @@
1
+ # FailTrace
2
+
3
+ **Leveraging Large Language Models for Automated Test Results Analysis**
4
+
5
+ [![PyPI version](https://badge.fury.io/py/failtrace.svg)](https://pypi.org/project/failtrace/)
6
+ [![Python Version](https://img.shields.io/pypi/pyversions/failtrace)](https://www.python.org/)
7
+
8
+ ---
9
+
10
+ ## 🌍 Overview
11
+
12
+ FailTrace helps developers and QA engineers **analyze automated test results** with **Large Language Models (LLMs)**.
13
+ It builds **dependency graphs**, maps them with test execution logs, and generates **interactive HTML reports** with insights into failures.
14
+
15
+ ---
16
+
17
+ ## ✨ Features
18
+
19
+ - Supports **Python, Java, C#**
20
+ - Dependency graph visualization (PyVis + NetworkX)
21
+ - Test log parsing (Pytest, JSON, TRX, …)
22
+ - Interactive reports (`HTML + CSS + JS`)
23
+ - Dry-run mode (no API calls)
24
+ - CLI-first design for CI/CD
25
+
26
+ ---
27
+
28
+ ## 📦 Installation
29
+
30
+ ```bash
31
+ pip install failtrace
32
+ ````
33
+
34
+ ---
35
+
36
+ ## ⚡ Quickstart
37
+
38
+ Run the **full pipeline**:
39
+
40
+ ```bash
41
+ python -m failtrace full -p ./your-source-code -l ./your-test-results.xml --open-report
42
+ ```
43
+
44
+ Run in **quick mode** (reuse cached graph):
45
+
46
+ ```bash
47
+ failtrace quick -p ./src -l ./results.json
48
+ ```
49
+
50
+ ---
51
+
52
+ ## 📜 License
53
+
54
+ Licensed under the [MIT License](https://opensource.org/licenses/MIT).
55
+
56
+ ---
57
+
58
+ ## 👩‍💻 Author
59
+
60
+ [**Mohadese Akhoondy**](mailto:m.akhoondy1381@gmail.com)
61
+
62
+ ```
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,180 @@
1
+ import networkx as nx
2
+ from typing import Dict, List, Any, Tuple, Iterable
3
+ from ..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
+ if not isinstance(step, dict):
22
+ return False
23
+
24
+ f = (step.get("file") or "").replace("\\", "/").lower()
25
+ if not f:
26
+ return True
27
+
28
+ path = f if f.startswith("/") else f"/{f}"
29
+ for bad in _EXCLUDED_SUBSTRS:
30
+ if bad in path:
31
+ return False
32
+
33
+ return True
34
+
35
+
36
+ def _unique(seq: Iterable[str]) -> List[str]:
37
+ seen = set()
38
+ out: List[str] = []
39
+ for x in seq:
40
+ if x not in seen:
41
+ out.append(x)
42
+ seen.add(x)
43
+ return out
44
+
45
+
46
+ def _attach_origin_for_external_step(graph: nx.DiGraph, step: Dict[str, Any]) -> None:
47
+ if step.get("type") != "external":
48
+ return
49
+
50
+ node_id = step.get("node")
51
+ if not node_id or node_id not in graph:
52
+ return
53
+
54
+ callers_files: List[str] = []
55
+ for u in graph.predecessors(node_id):
56
+ f = (graph.nodes.get(u, {}) or {}).get("file")
57
+ if f:
58
+ callers_files.append(str(f).replace("\\", "/"))
59
+
60
+ if not callers_files:
61
+ for v in graph.successors(node_id):
62
+ f = (graph.nodes.get(v, {}) or {}).get("file")
63
+ if f:
64
+ callers_files.append(str(f).replace("\\", "/"))
65
+
66
+ callers_files = _unique(callers_files)
67
+ if callers_files:
68
+ step["origin"] = {"callers": callers_files[:5]}
69
+ else:
70
+ step["origin"] = {"callers": []}
71
+
72
+
73
+ def _augment_externals_with_origin(
74
+ graph: nx.DiGraph, path: List[Dict[str, Any]]
75
+ ) -> None:
76
+ for step in path:
77
+ if (
78
+ isinstance(step, dict)
79
+ and step.get("type") == "external"
80
+ and "origin" not in step
81
+ ):
82
+ _attach_origin_for_external_step(graph, step)
83
+
84
+
85
+ def _path_signature(path: List[Dict[str, Any]]) -> Tuple[str, ...]:
86
+ return tuple(
87
+ step["node"] for step in path if isinstance(step, dict) and "node" in step
88
+ )
89
+
90
+
91
+ def _informativeness_score(path: List[Dict[str, Any]]) -> Tuple[int, int]:
92
+ non_test = sum(1 for s in path if not s.get("is_test", False))
93
+ return (non_test, len(path))
94
+
95
+
96
+ def _postprocess_paths(
97
+ paths: List[List[Dict[str, Any]]],
98
+ *,
99
+ drop_short_downstream: bool,
100
+ max_paths: int,
101
+ ) -> List[List[Dict[str, Any]]]:
102
+
103
+ uniq: Dict[Tuple[str, ...], List[Dict[str, Any]]] = {}
104
+
105
+ for p in paths:
106
+ if drop_short_downstream and len(p) < 2:
107
+ continue
108
+ sig = _path_signature(p)
109
+ if not sig:
110
+ continue
111
+ old = uniq.get(sig)
112
+ if old is None or _informativeness_score(p) > _informativeness_score(old):
113
+ uniq[sig] = p
114
+
115
+ sorted_paths = sorted(uniq.values(), key=_informativeness_score, reverse=True)
116
+ return sorted_paths[:max_paths]
117
+
118
+
119
+ def find_critical_paths(
120
+ graph: nx.DiGraph,
121
+ *,
122
+ cutoff: int = _DEFAULT_CUTOFF,
123
+ max_paths_per_direction: int = _DEFAULT_MAX_PATHS_PER_DIR,
124
+ ) -> Dict[str, Dict[str, List[List[Dict[str, Any]]]]]:
125
+
126
+ critical_paths: Dict[str, Dict[str, List[List[Dict[str, Any]]]]] = {}
127
+
128
+ failed_tests = [
129
+ node
130
+ for node, data in graph.nodes(data=True)
131
+ if data.get("is_test") and data.get("test_status") == "failed"
132
+ ]
133
+
134
+ if not failed_tests:
135
+ return {}
136
+
137
+ for failed_node in failed_tests:
138
+ upstream_raw: List[List[Dict[str, Any]]] = []
139
+ downstream_raw: List[List[Dict[str, Any]]] = []
140
+
141
+ for node in graph.nodes:
142
+ if node == failed_node:
143
+ continue
144
+
145
+ try:
146
+ for path in nx.all_simple_paths(
147
+ graph, source=node, target=failed_node, cutoff=cutoff
148
+ ):
149
+ enriched = enrich_path_with_metadata(graph, path)
150
+ _augment_externals_with_origin(graph, enriched)
151
+ filtered = [step for step in enriched if _keep_step(step)]
152
+ if filtered:
153
+ upstream_raw.append(filtered)
154
+ except (nx.NetworkXNoPath, nx.NodeNotFound):
155
+ pass
156
+
157
+ try:
158
+ for path in nx.all_simple_paths(
159
+ graph, source=failed_node, target=node, cutoff=cutoff
160
+ ):
161
+ enriched = enrich_path_with_metadata(graph, path)
162
+ _augment_externals_with_origin(graph, enriched)
163
+ filtered = [step for step in enriched if _keep_step(step)]
164
+ if filtered:
165
+ downstream_raw.append(filtered)
166
+ except (nx.NetworkXNoPath, nx.NodeNotFound):
167
+ pass
168
+
169
+ upstream = _postprocess_paths(
170
+ upstream_raw, drop_short_downstream=False, max_paths=max_paths_per_direction
171
+ )
172
+ downstream = _postprocess_paths(
173
+ downstream_raw,
174
+ drop_short_downstream=True,
175
+ max_paths=max_paths_per_direction,
176
+ )
177
+
178
+ critical_paths[failed_node] = {"upstream": upstream, "downstream": downstream}
179
+
180
+ return critical_paths