tracepatch 0.1.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.
@@ -0,0 +1,59 @@
1
+ # This workflow will upload a Python Package to PyPI when a release is created
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
3
+
4
+ # This workflow uses actions that are not certified by GitHub.
5
+ # They are provided by a third-party and are governed by
6
+ # separate terms of service, privacy policy, and support
7
+ # documentation.
8
+
9
+ name: Upload Python Package
10
+
11
+ on:
12
+ release:
13
+ types: [published]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ release-build:
20
+ runs-on: ubuntu-latest
21
+
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: "3.x"
28
+
29
+ - name: Build release distributions
30
+ run: |
31
+ # NOTE: put your own distribution build steps here.
32
+ python -m pip install build
33
+ python -m build
34
+
35
+ - name: Upload distributions
36
+ uses: actions/upload-artifact@v4
37
+ with:
38
+ name: release-dists
39
+ path: dist/
40
+
41
+ pypi-publish:
42
+ runs-on: ubuntu-latest
43
+ needs:
44
+ - release-build
45
+ permissions:
46
+ # IMPORTANT: this permission is mandatory for trusted publishing
47
+ id-token: write
48
+
49
+ steps:
50
+ - name: Retrieve release distributions
51
+ uses: actions/download-artifact@v4
52
+ with:
53
+ name: release-dists
54
+ path: dist/
55
+
56
+ - name: Publish release distributions to PyPI
57
+ uses: pypa/gh-action-pypi-publish@release/v1
58
+ with:
59
+ packages-dir: dist/
@@ -0,0 +1,26 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ *.egg
6
+ dist/
7
+ build/
8
+
9
+ # Virtual environments
10
+ .venv/
11
+
12
+ # pytest
13
+ .pytest_cache/
14
+
15
+ # tracepatch
16
+ .tracepatch_cache/
17
+
18
+ # IDE
19
+ .idea/
20
+ .vscode/
21
+ *.swp
22
+ *.swo
23
+
24
+ # OS
25
+ .DS_Store
26
+ Thumbs.db
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 levinismynameirl
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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.4
2
+ Name: tracepatch
3
+ Version: 0.1.0
4
+ Summary: Focused, opt-in runtime call tracing for a single execution context.
5
+ Project-URL: Homepage, https://github.com/tracepatch/tracepatch
6
+ Project-URL: Documentation, https://github.com/tracepatch/tracepatch#readme
7
+ Project-URL: Repository, https://github.com/tracepatch/tracepatch
8
+ Project-URL: Issues, https://github.com/tracepatch/tracepatch/issues
9
+ Author: tracepatch contributors
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: call-tree,debugging,profiling,tracing
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Debuggers
22
+ Classifier: Topic :: Software Development :: Testing
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.10
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
27
+ Requires-Dist: pytest>=7.0; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # tracepatch
31
+
32
+ Focused, opt-in runtime call tracing for a single execution context.
33
+
34
+ tracepatch is a debugging tool. It records function calls, arguments, return
35
+ values, and timing for one specific scope (a request handler, a CLI command,
36
+ a background task) and produces a readable call tree. It is not a replacement
37
+ for OpenTelemetry, structured logging, or APM dashboards. Think of it as a
38
+ scalpel: you point it at one execution path that is misbehaving, and it tells
39
+ you exactly what happened. It is recommended you leave tracepatch installed in production and have it ready to use when you need it, since it has zero overhead when not active.
40
+
41
+ ## Features
42
+
43
+ - Pure Python, no external dependencies.
44
+ - Zero overhead when inactive. Safe to leave installed in production; the
45
+ profiling hook is only active inside a `trace()` block.
46
+ - Works in synchronous and asynchronous code.
47
+ - Uses `contextvars` to isolate traces per async task. Concurrent requests
48
+ on the same event loop do not interfere with each other.
49
+ - Built-in safety limits (`max_depth`, `max_calls`) that automatically
50
+ disable tracing if exceeded, preventing runaway overhead.
51
+ - Human-readable ASCII call tree output with timing.
52
+ - JSON export for further processing.
53
+ - Recommended to be used in production, can help diagnose issues that only occur in production environments, or issues that weren't caught in staging, they will show up in the trace and can be analyzed. Leaves no overhead when not in use, so it's safe to have it installed and ready to go when you need it.
54
+
55
+ ## Installation
56
+
57
+ ```
58
+ pip install tracepatch
59
+ ```
60
+
61
+ Or install from source:
62
+
63
+ ```
64
+ pip install .
65
+ ```
66
+
67
+ ## Quick start
68
+
69
+ ### Synchronous
70
+
71
+ ```python
72
+ from tracepatch import trace
73
+
74
+ def fetch_user(user_id):
75
+ return {"id": user_id, "name": "Alice"}
76
+
77
+ def handle_request():
78
+ user = fetch_user(42)
79
+ return user
80
+
81
+ with trace() as t:
82
+ handle_request()
83
+
84
+ print(t.tree())
85
+ ```
86
+
87
+ Output:
88
+
89
+ ```
90
+ └── __main__.handle_request() [0.03ms]
91
+ └── __main__.fetch_user(user_id=42) -> {'id': 42, 'name': 'Alice'} [0.01ms]
92
+ ```
93
+
94
+ ### Asynchronous
95
+
96
+ ```python
97
+ import asyncio
98
+ from tracepatch import trace
99
+
100
+ async def fetch_user(user_id):
101
+ return {"id": user_id, "name": "Alice"}
102
+
103
+ async def handle_request():
104
+ user = await fetch_user(42)
105
+ return user
106
+
107
+ async def main():
108
+ async with trace() as t:
109
+ await handle_request()
110
+ print(t.tree())
111
+
112
+ asyncio.run(main())
113
+ ```
114
+
115
+ ### JSON export
116
+
117
+ ```python
118
+ with trace() as t:
119
+ handle_request()
120
+
121
+ t.to_json("trace.json")
122
+ ```
123
+
124
+ The JSON file contains a structured representation of the call tree with
125
+ timing in milliseconds, suitable for custom analysis scripts.
126
+
127
+ ## Configuration
128
+
129
+ The `trace()` constructor accepts the following keyword arguments:
130
+
131
+ | Parameter | Default | Description |
132
+ |------------------|---------|------------------------------------------------------------|
133
+ | `ignore_modules` | `[]` | List of module name prefixes to exclude from the trace. |
134
+ | `max_depth` | `30` | Maximum call nesting depth. Deeper calls are silently skipped. |
135
+ | `max_calls` | `10000` | Maximum total calls to record. Tracing freezes when exceeded. |
136
+ | `max_repr` | `120` | Maximum character length for `repr()` of arguments and return values. |
137
+
138
+ ### Filtering noise
139
+
140
+ ```python
141
+ with trace(ignore_modules=["logging", "urllib3", "ssl"]) as t:
142
+ handle_request()
143
+
144
+ print(t.tree())
145
+ ```
146
+
147
+ ### Limiting scope
148
+
149
+ ```python
150
+ with trace(max_depth=5, max_calls=500) as t:
151
+ handle_request()
152
+
153
+ if t.was_limited:
154
+ print("Warning: trace was truncated due to limits")
155
+
156
+ print(t.tree())
157
+ ```
158
+
159
+ ## API reference
160
+
161
+ ### `trace(**kwargs)`
162
+
163
+ Context manager (sync and async). Returns itself. Configuration via keyword
164
+ arguments listed above.
165
+
166
+ ### `t.tree() -> str`
167
+
168
+ Returns a human-readable ASCII call tree string with timing information for
169
+ each call.
170
+
171
+ ### `t.to_json(path) -> None`
172
+
173
+ Writes the trace to a JSON file. `path` can be a string, a `pathlib.Path`,
174
+ or a writable file object.
175
+
176
+ ### `t.call_count -> int`
177
+
178
+ Number of calls recorded.
179
+
180
+ ### `t.was_limited -> bool`
181
+
182
+ True if the trace was cut short because `max_calls` was exceeded.
183
+
184
+ ### `t.roots -> list[TraceNode]`
185
+
186
+ Direct access to the root `TraceNode` objects for programmatic traversal.
187
+
188
+ ## How it works
189
+
190
+ tracepatch uses `sys.settrace` to install a lightweight tracing callback
191
+ that fires on function call, return, and exception events in the current
192
+ thread. On a 'call' event, the global trace function checks a
193
+ `contextvars.ContextVar` to find the active collector for the current
194
+ execution context. If no collector is active (the common case in production),
195
+ the callback returns `None` immediately and Python does not trace that frame
196
+ further, so overhead is negligible.
197
+
198
+ When a `trace()` block is entered, a new collector is created and stored in
199
+ the ContextVar. The collector records call events into a tree of `TraceNode`
200
+ objects. When the block exits, the profiling hook is removed (reference
201
+ counted, so nested traces work correctly) and the ContextVar is reset.
202
+
203
+ Because isolation is done through `contextvars` rather than thread-locals,
204
+ concurrent `asyncio` tasks each get their own independent trace even though
205
+ they share a single thread.
206
+
207
+ ## Limitations
208
+
209
+ - `sys.settrace` captures Python-level calls in the thread while
210
+ active. C-level functions (builtins, C extensions) are not captured.
211
+ - There is measurable overhead while a trace block is active. This is a
212
+ debugging tool meant for targeted use, not always-on instrumentation.
213
+ - Traces are scoped to a single thread. If your code spawns threads, only
214
+ the originating thread is traced by default.
215
+
216
+ ## License
217
+
218
+ MIT. See LICENSE for the full text.
@@ -0,0 +1,189 @@
1
+ # tracepatch
2
+
3
+ Focused, opt-in runtime call tracing for a single execution context.
4
+
5
+ tracepatch is a debugging tool. It records function calls, arguments, return
6
+ values, and timing for one specific scope (a request handler, a CLI command,
7
+ a background task) and produces a readable call tree. It is not a replacement
8
+ for OpenTelemetry, structured logging, or APM dashboards. Think of it as a
9
+ scalpel: you point it at one execution path that is misbehaving, and it tells
10
+ you exactly what happened. It is recommended you leave tracepatch installed in production and have it ready to use when you need it, since it has zero overhead when not active.
11
+
12
+ ## Features
13
+
14
+ - Pure Python, no external dependencies.
15
+ - Zero overhead when inactive. Safe to leave installed in production; the
16
+ profiling hook is only active inside a `trace()` block.
17
+ - Works in synchronous and asynchronous code.
18
+ - Uses `contextvars` to isolate traces per async task. Concurrent requests
19
+ on the same event loop do not interfere with each other.
20
+ - Built-in safety limits (`max_depth`, `max_calls`) that automatically
21
+ disable tracing if exceeded, preventing runaway overhead.
22
+ - Human-readable ASCII call tree output with timing.
23
+ - JSON export for further processing.
24
+ - Recommended to be used in production, can help diagnose issues that only occur in production environments, or issues that weren't caught in staging, they will show up in the trace and can be analyzed. Leaves no overhead when not in use, so it's safe to have it installed and ready to go when you need it.
25
+
26
+ ## Installation
27
+
28
+ ```
29
+ pip install tracepatch
30
+ ```
31
+
32
+ Or install from source:
33
+
34
+ ```
35
+ pip install .
36
+ ```
37
+
38
+ ## Quick start
39
+
40
+ ### Synchronous
41
+
42
+ ```python
43
+ from tracepatch import trace
44
+
45
+ def fetch_user(user_id):
46
+ return {"id": user_id, "name": "Alice"}
47
+
48
+ def handle_request():
49
+ user = fetch_user(42)
50
+ return user
51
+
52
+ with trace() as t:
53
+ handle_request()
54
+
55
+ print(t.tree())
56
+ ```
57
+
58
+ Output:
59
+
60
+ ```
61
+ └── __main__.handle_request() [0.03ms]
62
+ └── __main__.fetch_user(user_id=42) -> {'id': 42, 'name': 'Alice'} [0.01ms]
63
+ ```
64
+
65
+ ### Asynchronous
66
+
67
+ ```python
68
+ import asyncio
69
+ from tracepatch import trace
70
+
71
+ async def fetch_user(user_id):
72
+ return {"id": user_id, "name": "Alice"}
73
+
74
+ async def handle_request():
75
+ user = await fetch_user(42)
76
+ return user
77
+
78
+ async def main():
79
+ async with trace() as t:
80
+ await handle_request()
81
+ print(t.tree())
82
+
83
+ asyncio.run(main())
84
+ ```
85
+
86
+ ### JSON export
87
+
88
+ ```python
89
+ with trace() as t:
90
+ handle_request()
91
+
92
+ t.to_json("trace.json")
93
+ ```
94
+
95
+ The JSON file contains a structured representation of the call tree with
96
+ timing in milliseconds, suitable for custom analysis scripts.
97
+
98
+ ## Configuration
99
+
100
+ The `trace()` constructor accepts the following keyword arguments:
101
+
102
+ | Parameter | Default | Description |
103
+ |------------------|---------|------------------------------------------------------------|
104
+ | `ignore_modules` | `[]` | List of module name prefixes to exclude from the trace. |
105
+ | `max_depth` | `30` | Maximum call nesting depth. Deeper calls are silently skipped. |
106
+ | `max_calls` | `10000` | Maximum total calls to record. Tracing freezes when exceeded. |
107
+ | `max_repr` | `120` | Maximum character length for `repr()` of arguments and return values. |
108
+
109
+ ### Filtering noise
110
+
111
+ ```python
112
+ with trace(ignore_modules=["logging", "urllib3", "ssl"]) as t:
113
+ handle_request()
114
+
115
+ print(t.tree())
116
+ ```
117
+
118
+ ### Limiting scope
119
+
120
+ ```python
121
+ with trace(max_depth=5, max_calls=500) as t:
122
+ handle_request()
123
+
124
+ if t.was_limited:
125
+ print("Warning: trace was truncated due to limits")
126
+
127
+ print(t.tree())
128
+ ```
129
+
130
+ ## API reference
131
+
132
+ ### `trace(**kwargs)`
133
+
134
+ Context manager (sync and async). Returns itself. Configuration via keyword
135
+ arguments listed above.
136
+
137
+ ### `t.tree() -> str`
138
+
139
+ Returns a human-readable ASCII call tree string with timing information for
140
+ each call.
141
+
142
+ ### `t.to_json(path) -> None`
143
+
144
+ Writes the trace to a JSON file. `path` can be a string, a `pathlib.Path`,
145
+ or a writable file object.
146
+
147
+ ### `t.call_count -> int`
148
+
149
+ Number of calls recorded.
150
+
151
+ ### `t.was_limited -> bool`
152
+
153
+ True if the trace was cut short because `max_calls` was exceeded.
154
+
155
+ ### `t.roots -> list[TraceNode]`
156
+
157
+ Direct access to the root `TraceNode` objects for programmatic traversal.
158
+
159
+ ## How it works
160
+
161
+ tracepatch uses `sys.settrace` to install a lightweight tracing callback
162
+ that fires on function call, return, and exception events in the current
163
+ thread. On a 'call' event, the global trace function checks a
164
+ `contextvars.ContextVar` to find the active collector for the current
165
+ execution context. If no collector is active (the common case in production),
166
+ the callback returns `None` immediately and Python does not trace that frame
167
+ further, so overhead is negligible.
168
+
169
+ When a `trace()` block is entered, a new collector is created and stored in
170
+ the ContextVar. The collector records call events into a tree of `TraceNode`
171
+ objects. When the block exits, the profiling hook is removed (reference
172
+ counted, so nested traces work correctly) and the ContextVar is reset.
173
+
174
+ Because isolation is done through `contextvars` rather than thread-locals,
175
+ concurrent `asyncio` tasks each get their own independent trace even though
176
+ they share a single thread.
177
+
178
+ ## Limitations
179
+
180
+ - `sys.settrace` captures Python-level calls in the thread while
181
+ active. C-level functions (builtins, C extensions) are not captured.
182
+ - There is measurable overhead while a trace block is active. This is a
183
+ debugging tool meant for targeted use, not always-on instrumentation.
184
+ - Traces are scoped to a single thread. If your code spawns threads, only
185
+ the originating thread is traced by default.
186
+
187
+ ## License
188
+
189
+ MIT. See LICENSE for the full text.