methodgraph 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.
- methodgraph/__init__.py +28 -0
- methodgraph/cli.py +79 -0
- methodgraph/collector.py +307 -0
- methodgraph/renderer.py +42 -0
- methodgraph/server.py +33 -0
- methodgraph/templates/report.html +2350 -0
- methodgraph/tracer.py +297 -0
- methodgraph-0.1.0.dist-info/METADATA +154 -0
- methodgraph-0.1.0.dist-info/RECORD +13 -0
- methodgraph-0.1.0.dist-info/WHEEL +5 -0
- methodgraph-0.1.0.dist-info/entry_points.txt +2 -0
- methodgraph-0.1.0.dist-info/licenses/LICENSE +20 -0
- methodgraph-0.1.0.dist-info/top_level.txt +1 -0
methodgraph/tracer.py
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import inspect
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import threading
|
|
6
|
+
import atexit
|
|
7
|
+
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
|
8
|
+
|
|
9
|
+
from .collector import ExecutionTraceCollector, safe_serialize_val
|
|
10
|
+
from .renderer import render_and_save, open_in_browser
|
|
11
|
+
|
|
12
|
+
_report_scheduled = False
|
|
13
|
+
|
|
14
|
+
def _generate_atexit_report(report_path: str):
|
|
15
|
+
global _report_scheduled
|
|
16
|
+
if not _report_scheduled:
|
|
17
|
+
return
|
|
18
|
+
collector = get_active_collector()
|
|
19
|
+
summary = collector.build_summary()
|
|
20
|
+
html_path = render_and_save(summary, report_path)
|
|
21
|
+
open_in_browser(html_path)
|
|
22
|
+
|
|
23
|
+
# Thread-local storage for managing nested call stacks per thread
|
|
24
|
+
_thread_local = threading.local()
|
|
25
|
+
|
|
26
|
+
def _get_call_stack() -> List[int]:
|
|
27
|
+
if not hasattr(_thread_local, "call_stack"):
|
|
28
|
+
_thread_local.call_stack = []
|
|
29
|
+
return _thread_local.call_stack
|
|
30
|
+
|
|
31
|
+
_global_collector: Optional[ExecutionTraceCollector] = None
|
|
32
|
+
_global_collector_lock = threading.Lock()
|
|
33
|
+
|
|
34
|
+
def get_active_collector() -> ExecutionTraceCollector:
|
|
35
|
+
global _global_collector
|
|
36
|
+
with _global_collector_lock:
|
|
37
|
+
if _global_collector is None:
|
|
38
|
+
_global_collector = ExecutionTraceCollector()
|
|
39
|
+
return _global_collector
|
|
40
|
+
|
|
41
|
+
def reset_active_collector() -> ExecutionTraceCollector:
|
|
42
|
+
global _global_collector
|
|
43
|
+
with _global_collector_lock:
|
|
44
|
+
_global_collector = ExecutionTraceCollector()
|
|
45
|
+
return _global_collector
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _capture_function_args(func: Callable, args: tuple, kwargs: dict) -> tuple:
|
|
49
|
+
"""Parses positional args and kwargs into structured data list and dict."""
|
|
50
|
+
args_data = []
|
|
51
|
+
kwargs_data = {}
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
sig = inspect.signature(func)
|
|
55
|
+
bound_args = sig.bind(*args, **kwargs)
|
|
56
|
+
bound_args.apply_defaults()
|
|
57
|
+
|
|
58
|
+
for name, val in bound_args.arguments.items():
|
|
59
|
+
param_kind = sig.parameters[name].kind if name in sig.parameters else None
|
|
60
|
+
|
|
61
|
+
if param_kind == inspect.Parameter.VAR_POSITIONAL: # *args
|
|
62
|
+
for idx, item in enumerate(val):
|
|
63
|
+
args_data.append({
|
|
64
|
+
"name": f"*{name}[{idx}]",
|
|
65
|
+
"val_info": safe_serialize_val(item)
|
|
66
|
+
})
|
|
67
|
+
elif param_kind == inspect.Parameter.VAR_KEYWORD: # **kwargs
|
|
68
|
+
for k, item in val.items():
|
|
69
|
+
kwargs_data[k] = safe_serialize_val(item)
|
|
70
|
+
else:
|
|
71
|
+
# Regular parameter or self/cls
|
|
72
|
+
if name in ("self", "cls"):
|
|
73
|
+
# Record self/cls cleanly
|
|
74
|
+
args_data.append({
|
|
75
|
+
"name": name,
|
|
76
|
+
"val_info": safe_serialize_val(val, max_depth=1)
|
|
77
|
+
})
|
|
78
|
+
else:
|
|
79
|
+
kwargs_data[name] = safe_serialize_val(val)
|
|
80
|
+
except Exception:
|
|
81
|
+
# Fallback if signature binding fails (C extensions or dynamic builtins)
|
|
82
|
+
for idx, item in enumerate(args):
|
|
83
|
+
args_data.append({
|
|
84
|
+
"name": f"arg_{idx}",
|
|
85
|
+
"val_info": safe_serialize_val(item)
|
|
86
|
+
})
|
|
87
|
+
for k, v in kwargs.items():
|
|
88
|
+
kwargs_data[k] = safe_serialize_val(v)
|
|
89
|
+
|
|
90
|
+
return args_data, kwargs_data
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class trace:
|
|
94
|
+
"""Decorator to trace a function or method execution."""
|
|
95
|
+
|
|
96
|
+
def __init__(self, func: Optional[Callable] = None, *, show_on_exit: bool = False, report_path: str = "methodgraph_report.html"):
|
|
97
|
+
self.func = func
|
|
98
|
+
self.show_on_exit = show_on_exit
|
|
99
|
+
self.report_path = report_path
|
|
100
|
+
|
|
101
|
+
if func is not None:
|
|
102
|
+
functools.update_wrapper(self, func)
|
|
103
|
+
|
|
104
|
+
def __call__(self, *args, **kwargs):
|
|
105
|
+
if self.func is None:
|
|
106
|
+
# Used as @trace(...) with arguments
|
|
107
|
+
func_to_wrap = args[0]
|
|
108
|
+
@functools.wraps(func_to_wrap)
|
|
109
|
+
def wrapper(*w_args, **w_kwargs):
|
|
110
|
+
return self._invoke(func_to_wrap, w_args, w_kwargs)
|
|
111
|
+
return wrapper
|
|
112
|
+
else:
|
|
113
|
+
# Used as @trace without parentheses
|
|
114
|
+
return self._invoke(self.func, args, kwargs)
|
|
115
|
+
|
|
116
|
+
def __get__(self, instance, owner):
|
|
117
|
+
"""Supports binding to instance methods."""
|
|
118
|
+
if self.func is None:
|
|
119
|
+
return self
|
|
120
|
+
return functools.partial(self.__call__, instance)
|
|
121
|
+
|
|
122
|
+
def _invoke(self, func: Callable, args: tuple, kwargs: dict) -> Any:
|
|
123
|
+
collector = get_active_collector()
|
|
124
|
+
call_stack = _get_call_stack()
|
|
125
|
+
|
|
126
|
+
parent_id = call_stack[-1] if call_stack else None
|
|
127
|
+
depth = len(call_stack)
|
|
128
|
+
|
|
129
|
+
module = getattr(func, "__module__", "") or ""
|
|
130
|
+
qualname = getattr(func, "__qualname__", func.__name__)
|
|
131
|
+
func_name = getattr(func, "__name__", "function")
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
file_path = inspect.getfile(func)
|
|
135
|
+
line_no = inspect.getsourcelines(func)[1]
|
|
136
|
+
except Exception:
|
|
137
|
+
file_path = "unknown"
|
|
138
|
+
line_no = 0
|
|
139
|
+
|
|
140
|
+
args_data, kwargs_data = _capture_function_args(func, args, kwargs)
|
|
141
|
+
|
|
142
|
+
record = collector.add_call(
|
|
143
|
+
parent_id=parent_id,
|
|
144
|
+
depth=depth,
|
|
145
|
+
module=module,
|
|
146
|
+
qualname=qualname,
|
|
147
|
+
func_name=func_name,
|
|
148
|
+
file_path=file_path,
|
|
149
|
+
line_no=line_no,
|
|
150
|
+
args_data=args_data,
|
|
151
|
+
kwargs_data=kwargs_data,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
call_stack.append(record.call_id)
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
result = func(*args, **kwargs)
|
|
158
|
+
record.complete(return_val=result)
|
|
159
|
+
return result
|
|
160
|
+
except BaseException as exc:
|
|
161
|
+
record.complete(exception=exc)
|
|
162
|
+
raise
|
|
163
|
+
finally:
|
|
164
|
+
call_stack.pop()
|
|
165
|
+
if self.show_on_exit:
|
|
166
|
+
global _report_scheduled
|
|
167
|
+
if not _report_scheduled:
|
|
168
|
+
_report_scheduled = True
|
|
169
|
+
atexit.register(_generate_atexit_report, self.report_path)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def trace_class(cls: Type) -> Type:
|
|
173
|
+
"""Class decorator to apply @trace to all callable methods in a class."""
|
|
174
|
+
for attr_name, attr_val in list(cls.__dict__.items()):
|
|
175
|
+
if attr_name.startswith("__") and attr_name.endswith("__"):
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
if inspect.isfunction(attr_val) or inspect.ismethod(attr_val):
|
|
179
|
+
setattr(cls, attr_name, trace(attr_val))
|
|
180
|
+
elif isinstance(attr_val, classmethod):
|
|
181
|
+
original_fn = attr_val.__func__
|
|
182
|
+
setattr(cls, attr_name, classmethod(trace(original_fn)))
|
|
183
|
+
elif isinstance(attr_val, staticmethod):
|
|
184
|
+
original_fn = attr_val.__func__
|
|
185
|
+
setattr(cls, attr_name, staticmethod(trace(original_fn)))
|
|
186
|
+
|
|
187
|
+
return cls
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class TraceSession:
|
|
191
|
+
"""Context manager for recording execution blocks."""
|
|
192
|
+
|
|
193
|
+
def __init__(self, report_path: str = "methodgraph_report.html", auto_open: bool = False):
|
|
194
|
+
self.report_path = report_path
|
|
195
|
+
self.auto_open = auto_open
|
|
196
|
+
self.collector: Optional[ExecutionTraceCollector] = None
|
|
197
|
+
|
|
198
|
+
def __enter__(self):
|
|
199
|
+
self.collector = reset_active_collector()
|
|
200
|
+
return self
|
|
201
|
+
|
|
202
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
203
|
+
if self.collector:
|
|
204
|
+
summary = self.collector.build_summary()
|
|
205
|
+
saved_file = render_and_save(summary, self.report_path)
|
|
206
|
+
if self.auto_open:
|
|
207
|
+
open_in_browser(saved_file)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class SystemTracer:
|
|
211
|
+
"""sys.settrace hook for dynamic tracing of scripts without decorators."""
|
|
212
|
+
|
|
213
|
+
def __init__(self, include_stdlib: bool = False):
|
|
214
|
+
self.include_stdlib = include_stdlib
|
|
215
|
+
self.collector = get_active_collector()
|
|
216
|
+
self.frame_to_record: Dict[int, Any] = {}
|
|
217
|
+
self.py_stdlib_dir = os.path.dirname(os.path.dirname(inspect.__file__)).lower()
|
|
218
|
+
|
|
219
|
+
def _should_trace_file(self, file_path: str) -> bool:
|
|
220
|
+
if not file_path or file_path.startswith("<"):
|
|
221
|
+
return False
|
|
222
|
+
fp_lower = os.path.abspath(file_path).lower()
|
|
223
|
+
|
|
224
|
+
# Prevent tracing methodgraph's own internal source files
|
|
225
|
+
tracer_dir = os.path.dirname(os.path.abspath(__file__)).lower()
|
|
226
|
+
if fp_lower.startswith(tracer_dir):
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
if not self.include_stdlib:
|
|
230
|
+
if "site-packages" in fp_lower or "dist-packages" in fp_lower:
|
|
231
|
+
return False
|
|
232
|
+
if fp_lower.startswith(self.py_stdlib_dir):
|
|
233
|
+
return False
|
|
234
|
+
return True
|
|
235
|
+
|
|
236
|
+
def trace_dispatch(self, frame, event, arg):
|
|
237
|
+
code = frame.f_code
|
|
238
|
+
file_path = code.co_filename
|
|
239
|
+
|
|
240
|
+
if not self._should_trace_file(file_path):
|
|
241
|
+
return self.trace_dispatch
|
|
242
|
+
|
|
243
|
+
frame_id = id(frame)
|
|
244
|
+
call_stack = _get_call_stack()
|
|
245
|
+
|
|
246
|
+
if event == "call":
|
|
247
|
+
func_name = code.co_name
|
|
248
|
+
module = frame.f_globals.get("__name__", "")
|
|
249
|
+
qualname = func_name
|
|
250
|
+
line_no = code.co_firstlineno
|
|
251
|
+
|
|
252
|
+
# Extract local arguments from frame
|
|
253
|
+
args_data = []
|
|
254
|
+
kwargs_data = {}
|
|
255
|
+
argcount = code.co_argcount + code.co_kwonlyargcount
|
|
256
|
+
var_names = code.co_varnames
|
|
257
|
+
|
|
258
|
+
for i in range(argcount):
|
|
259
|
+
if i < len(var_names):
|
|
260
|
+
var_name = var_names[i]
|
|
261
|
+
val = frame.f_locals.get(var_name)
|
|
262
|
+
if var_name in ("self", "cls"):
|
|
263
|
+
args_data.append({"name": var_name, "val_info": safe_serialize_val(val, max_depth=1)})
|
|
264
|
+
else:
|
|
265
|
+
kwargs_data[var_name] = safe_serialize_val(val)
|
|
266
|
+
|
|
267
|
+
parent_id = call_stack[-1] if call_stack else None
|
|
268
|
+
depth = len(call_stack)
|
|
269
|
+
|
|
270
|
+
record = self.collector.add_call(
|
|
271
|
+
parent_id=parent_id,
|
|
272
|
+
depth=depth,
|
|
273
|
+
module=module,
|
|
274
|
+
qualname=qualname,
|
|
275
|
+
func_name=func_name,
|
|
276
|
+
file_path=file_path,
|
|
277
|
+
line_no=line_no,
|
|
278
|
+
args_data=args_data,
|
|
279
|
+
kwargs_data=kwargs_data,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
self.frame_to_record[frame_id] = record
|
|
283
|
+
call_stack.append(record.call_id)
|
|
284
|
+
|
|
285
|
+
elif event == "return":
|
|
286
|
+
record = self.frame_to_record.pop(frame_id, None)
|
|
287
|
+
if record:
|
|
288
|
+
record.complete(return_val=arg)
|
|
289
|
+
if call_stack and call_stack[-1] == record.call_id:
|
|
290
|
+
call_stack.pop()
|
|
291
|
+
|
|
292
|
+
elif event == "exception":
|
|
293
|
+
record = self.frame_to_record.get(frame_id)
|
|
294
|
+
if record and isinstance(arg[1], BaseException):
|
|
295
|
+
record.complete(exception=arg[1])
|
|
296
|
+
|
|
297
|
+
return self.trace_dispatch
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: methodgraph
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Graphical flow debugger for Python method calls, arguments, return values, and execution flow.
|
|
5
|
+
Author-email: Sagar Kariya <sbkariya99@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# 🔍 methodgraph
|
|
18
|
+
|
|
19
|
+
**Graphical Style Flow Visual Debugger for Python Method Calls, Passed Values, and Execution Flow**
|
|
20
|
+
|
|
21
|
+
- ⚡ **Graphical Style Flow Canvas**: Interactive node-edge workflow DAG with `__start__` and `__end__` boundary capsules, smooth curved Bezier connectors, card-style nodes, pan/zoom, and minimap.
|
|
22
|
+
- 🎬 **Step-by-Step Playback & Time-Travel Scrubber**: Animate function execution flows with live pulsing halos, edge particle streams, and synchronized state inspection.
|
|
23
|
+
- 🔬 **State & Run Inspector**: Split-screen drawer featuring collapsible syntax-highlighted object trees for inputs, outputs, exceptions, execution metadata, and run history.
|
|
24
|
+
- 🌓 **Light & Dark Theme Toggle**: Built-in dark and light mode themes with persistent preferences and auto OS detection.
|
|
25
|
+
- ⏱️ **Trace Waterfall Timeline**: Execution timing visualization broken down by function call duration and concurrency spans.
|
|
26
|
+
- 🌳 **Interactive Call Tree**: Nested collapsible hierarchy of function invocations with inline parameter chips and return status.
|
|
27
|
+
- 📊 **Searchable Data Matrix**: Filter, search, and inspect argument values, types, return values, and error tracebacks.
|
|
28
|
+
- 🖥️ **CLI Runner & Auto Browser Launcher**: Trace scripts automatically without changing source code.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🚀 Quick Start
|
|
33
|
+
|
|
34
|
+
### 1. Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install methodgraph
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or install locally in editable mode:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/example/methodgraph.git
|
|
44
|
+
cd methodgraph
|
|
45
|
+
pip install -e .
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 💡 Usage Modes
|
|
51
|
+
|
|
52
|
+
### Option A: Function Decorator `@trace`
|
|
53
|
+
|
|
54
|
+
Trace specific functions and open the graphical visualization when executed:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from methodgraph import trace, show
|
|
58
|
+
|
|
59
|
+
@trace(show_on_exit=True)
|
|
60
|
+
def calculate_tax(amount, rate=0.2):
|
|
61
|
+
return amount * rate
|
|
62
|
+
|
|
63
|
+
@trace
|
|
64
|
+
def process_order(item_id, price, quantity):
|
|
65
|
+
tax = calculate_tax(price * quantity)
|
|
66
|
+
total = (price * quantity) + tax
|
|
67
|
+
return {"item": item_id, "total": total}
|
|
68
|
+
|
|
69
|
+
# Execute methods
|
|
70
|
+
process_order("ITEM-102", price=49.99, quantity=3)
|
|
71
|
+
|
|
72
|
+
# Generates 'methodgraph_report.html' and opens browser
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### Option B: Class Decorator `@trace_class`
|
|
78
|
+
|
|
79
|
+
Trace all methods within a class automatically:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from methodgraph import trace_class, save_report
|
|
83
|
+
|
|
84
|
+
@trace_class
|
|
85
|
+
class DataPipeline:
|
|
86
|
+
def fetch_data(self, source):
|
|
87
|
+
return [10, 20, 30, 40]
|
|
88
|
+
|
|
89
|
+
def transform(self, data, multiplier=2):
|
|
90
|
+
return [x * multiplier for x in data]
|
|
91
|
+
|
|
92
|
+
def run(self):
|
|
93
|
+
raw = self.fetch_data("database")
|
|
94
|
+
return self.transform(raw, multiplier=3)
|
|
95
|
+
|
|
96
|
+
pipeline = DataPipeline()
|
|
97
|
+
pipeline.run()
|
|
98
|
+
|
|
99
|
+
# Save interactive visual report
|
|
100
|
+
save_report("pipeline_report.html")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Option C: Context Manager `TraceSession`
|
|
106
|
+
|
|
107
|
+
Trace a specific block of code:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
from methodgraph import TraceSession
|
|
111
|
+
|
|
112
|
+
with TraceSession(report_path="session_report.html", auto_open=True) as session:
|
|
113
|
+
data = [5, 12, 18, 24]
|
|
114
|
+
avg = sum(data) / len(data)
|
|
115
|
+
print(f"Average: {avg}")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Option D: CLI Script Tracer (`methodgraph run`)
|
|
121
|
+
|
|
122
|
+
Trace any existing Python script without modifying a single line of code!
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
methodgraph run my_script.py --open
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Additional CLI options:
|
|
129
|
+
- `--open`: Open generated HTML report in browser automatically.
|
|
130
|
+
- `--output report.html`: Specify custom report file path.
|
|
131
|
+
- `--include-stdlib`: Include standard library modules in tracing (disabled by default for clean graphs).
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 🎨 Interactive Features in Graphical Presentation
|
|
136
|
+
|
|
137
|
+
1. **Parameter Inspection**: Click any method node to view exact positional `args` and keyword `kwargs`, object types, formatted values, and line numbers.
|
|
138
|
+
2. **Return & Exception Inspector**: Clear visual distinction between successful returns and unhandled exceptions (highlighted in crimson red with traceback stack).
|
|
139
|
+
3. **Execution Bottleneck Finder**: Identify slowest methods visually on the Gantt timeline or graph heatmap.
|
|
140
|
+
4. **Live Search**: Filter method calls in real-time by method name, argument name, or argument value substring.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 🛠️ Requirements
|
|
145
|
+
|
|
146
|
+
- Python >= 3.8
|
|
147
|
+
- No heavy third-party dependencies required! Generates self-contained HTML/CSS/JS visualizers.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 📜 License
|
|
152
|
+
|
|
153
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
|
154
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
methodgraph/__init__.py,sha256=xlR9DNAITQ4pEg7WpMEoy-c03fqgspxFU7YTG_pzolc,871
|
|
2
|
+
methodgraph/cli.py,sha256=OvZOPxuq6v2J4JTQ78v0zDhXH_DWLCMeNkz2DI192zc,3241
|
|
3
|
+
methodgraph/collector.py,sha256=v8K1h_fgV2BcRnmJ89KQelUv14k9Jm0K5p_dCLplCKU,12246
|
|
4
|
+
methodgraph/renderer.py,sha256=r_iFicA_hHXkzC9SLyoH3fPM2OTmEAsTYKGSyAm-EOU,1539
|
|
5
|
+
methodgraph/server.py,sha256=wo00ZfaCvTmL2tlUvnvWAkLdxbKVH6uiDfQAJGMKIuc,1096
|
|
6
|
+
methodgraph/tracer.py,sha256=YhYXXRLb-Y9jzzQNGCUC9bvrvpPl0I7CVfKqfGLJTyM,10710
|
|
7
|
+
methodgraph/templates/report.html,sha256=WsckjiJw29Idkp2-5jZXw-AkPVABXFkfKBx7W2tbOS8,88402
|
|
8
|
+
methodgraph-0.1.0.dist-info/licenses/LICENSE,sha256=wfJJ1UL-WsyIlxt2aTqCOIvRIBO4Qf_OLMZkOs6kRSw,1048
|
|
9
|
+
methodgraph-0.1.0.dist-info/METADATA,sha256=zfWOz_aLwLlUi8dMKM1obcgcEDBQEceQ-_0NEWket4g,4978
|
|
10
|
+
methodgraph-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
11
|
+
methodgraph-0.1.0.dist-info/entry_points.txt,sha256=7x-v0Xna1UqlasiF3YgHps7MJc0HNqb0TpsOXCyHaMM,53
|
|
12
|
+
methodgraph-0.1.0.dist-info/top_level.txt,sha256=gi84YkdFhN2ckhnBgE6l1gY3Fv9s3rM91W1nbpcG3EM,12
|
|
13
|
+
methodgraph-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Antigravity
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS IN Schnitt ODER OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
methodgraph
|