warntrace 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,286 @@
1
+ Metadata-Version: 2.3
2
+ Name: warntrace
3
+ Version: 0.1.0
4
+ Summary: Find which package caused a Python warning and where your code triggered it.
5
+ Author: Kunal Kaul
6
+ Author-email: Kunal Kaul <54585925+kunalkaul@users.noreply.github.com>
7
+ Requires-Dist: packaging>=24
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+
11
+ # Warntrace
12
+
13
+ Trace the origin of Python warnings — classify them by ownership (application, direct
14
+ dependency, transitive, stdlib), suppress noise, and fail CI when policy is violated.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ uv add --dev warntrace
20
+ # or
21
+ pip install warntrace
22
+ ```
23
+
24
+ Requires Python >= 3.10.
25
+
26
+ ## Quick start
27
+
28
+ ```bash
29
+ # Capture warnings from any Python command
30
+ warntrace run python your_script.py
31
+
32
+ # Capture warnings from your test suite
33
+ warntrace run pytest
34
+ ```
35
+
36
+ Warntrace intercepts every `warnings.warn()` call in the child process, classifies each
37
+ warning by origin, and writes a JSON report to stdout after the child exits.
38
+
39
+ ## CLI reference
40
+
41
+ ```
42
+ warntrace run [options] -- <command> [args...]
43
+ ```
44
+
45
+ | Flag | Default | Description |
46
+ |---|---|---|
47
+ | `--format` | `json` | Output format: `json`, `detailed`, or `summary` |
48
+ | `--output FILE` | stdout | Write report to a file instead of stdout |
49
+ | `--max-frames N` | unlimited | Limit stack frames per warning in terminal output |
50
+ | `--show-all` | off | Capture normally-suppressed warnings (e.g. `BytesWarning`) |
51
+ | `--no-passthrough` | off | Suppress original warning output from the child process |
52
+ | `--fail-on POLICY` | off | Exit code 3 when a warning matches the policy (see below) |
53
+ | `--root PATH` | child's CWD | Application root directory for classification |
54
+ | `--no-color` | off | Disable ANSI color in terminal output |
55
+
56
+ ### Examples
57
+
58
+ ```bash
59
+ # Basic — run a script and see the default JSON report
60
+ warntrace run python -c "import warnings; warnings.warn('hello')"
61
+
62
+ # Detailed terminal output with limited stack frames
63
+ warntrace run --format detailed --max-frames 5 pytest
64
+
65
+ # Summary — one line per warning with total counts
66
+ warntrace run --format summary pytest
67
+
68
+ # Write report to a file (no ANSI codes in file output)
69
+ warntrace run --output report.json pytest
70
+
71
+ # Collect all warnings silently, even normally-suppressed ones
72
+ warntrace run --no-passthrough --show-all pytest
73
+
74
+ # Fail CI when application-origin warnings are found
75
+ warntrace run --fail-on application pytest
76
+
77
+ # Treat triggered dependency warnings as application-level too
78
+ warntrace run --fail-on application pytest
79
+
80
+ # Fail on any warning (application + deps + stdlib + unknown)
81
+ warntrace run --fail-on all pytest
82
+
83
+ # Opt out of policy failure explicitly
84
+ warntrace run --fail-on none pytest
85
+
86
+ # Specify granular origins to fail on
87
+ warntrace run --fail-on direct_dependency transitive_dependency pytest
88
+
89
+ # Combined: silent, fail on app warnings, save report
90
+ warntrace run --no-passthrough --fail-on application --output ci-report.json pytest
91
+ ```
92
+
93
+ ## Python API
94
+
95
+ ```python
96
+ from warntrace import capture_warnings
97
+
98
+ with capture_warnings() as tracer:
99
+ import warnings
100
+ warnings.warn("something is deprecated", DeprecationWarning)
101
+
102
+ report = tracer.stop()
103
+ print(report.to_dict())
104
+ ```
105
+
106
+ The context manager installs the capture hook, runs your code, restores the
107
+ original warning state, and returns a classified `WarningReport`. The report
108
+ contains the full warning list with origin, stack, dependency paths, and
109
+ occurrence counts.
110
+
111
+ ### Lower-level API
112
+
113
+ ```python
114
+ from warntrace import WarningTracer
115
+
116
+ tracer = WarningTracer(show_all=True, passthrough=False)
117
+ tracer.start()
118
+ # ... your code ...
119
+ report = tracer.stop()
120
+ print(report.to_dict())
121
+ ```
122
+
123
+ ### Configuration
124
+
125
+ | Parameter | Default | Description |
126
+ |---|---|---|
127
+ | `root` | `Path.cwd()` | Application root for origin classification |
128
+ | `show_all` | `False` | Capture normally-suppressed warning types |
129
+ | `passthrough` | `True` | Forward warnings to the original handler |
130
+
131
+ ## Output formats
132
+
133
+ ### JSON (default)
134
+
135
+ ```json
136
+ {
137
+ "schema_version": "0.1",
138
+ "summary": {
139
+ "unique_warnings": 2,
140
+ "total_occurrences": 5,
141
+ "application": 1,
142
+ "direct_dependency": 1,
143
+ "transitive_dependency": 0,
144
+ "standard_library": 0,
145
+ "unknown": 0
146
+ },
147
+ "warnings": [
148
+ {
149
+ "category": "DeprecationWarning",
150
+ "message": "use foobar() instead of deprecated_func()",
151
+ "origin": "direct_dependency",
152
+ "triggered_directly_by_application": true,
153
+ "occurrences": 3,
154
+ "emitted_from": {
155
+ "filename": "/path/to/site-packages/oldpkg/utils.py",
156
+ "lineno": 42,
157
+ "distribution": {
158
+ "name": "oldpkg",
159
+ "normalized_name": "oldpkg",
160
+ "version": "1.2.3"
161
+ }
162
+ },
163
+ "application_frame": {
164
+ "filename": "/home/user/project/app.py",
165
+ "lineno": 15,
166
+ "function": "run",
167
+ "source_line": "oldpkg.deprecated_func()"
168
+ },
169
+ "dependency_path": [
170
+ {"name": "myproject", "normalized_name": "myproject", "version": null},
171
+ {"name": "oldpkg", "normalized_name": "oldpkg", "version": "1.2.3"}
172
+ ],
173
+ "stack": [
174
+ {"filename": "/home/user/project/app.py", "lineno": 15, "function": "run", "source_line": "oldpkg.deprecated_func()"},
175
+ {"filename": "/path/to/site-packages/oldpkg/utils.py", "lineno": 42, "function": "deprecated_func", "source_line": "warnings.warn(...)"}
176
+ ]
177
+ }
178
+ ]
179
+ }
180
+ ```
181
+
182
+ ### Detailed terminal output
183
+
184
+ ```
185
+ warntrace report — 1 unique warning, 3 total occurrences
186
+
187
+ ─── Warning 1 of 1 ───────────────────────────────────────────────────────────
188
+ Origin: ! direct_dependency (triggered by your code)
189
+ Category: DeprecationWarning
190
+ Message: use foobar() instead of deprecated_func()
191
+ Location: oldpkg/utils.py:42
192
+ Occurrences: 3
193
+ Application:
194
+ myproject/app.py:15 run()
195
+ -> oldpkg.deprecated_func()
196
+ Dependency path:
197
+ myproject → oldpkg 1.2.3
198
+ Stack:
199
+ 1 myproject/app.py:15 run()
200
+ 2 oldpkg/utils.py:42 deprecated_func()
201
+ ```
202
+
203
+ ### Summary output
204
+
205
+ ```
206
+ application 1 DeprecationWarning: use foobar() instead...
207
+ direct_dependency 3 DeprecationWarning: use foobar() instead...
208
+ ────────────────────────────────────────────────────────
209
+ warntrace report — 2 unique warnings, 4 total occurrences
210
+ ```
211
+
212
+ ## Classification
213
+
214
+ Warntrace assigns an **origin** to every captured warning by walking the stack and
215
+ mapping each frame to a package or to the Python standard library. There are five
216
+ origin levels:
217
+
218
+ | Origin | Meaning |
219
+ |---|---|
220
+ | `application` | The warning was emitted from your own code (under the project root) |
221
+ | `direct_dependency` | The warning was emitted by a package listed in your dependencies |
222
+ | `transitive_dependency` | The warning was emitted by a dependency of one of your dependencies |
223
+ | `standard_library` | The warning was emitted from CPython stdlib |
224
+ | `unknown` | The file could not be mapped to any known package |
225
+
226
+ Additionally, each warning has a `triggered_directly_by_application` boolean.
227
+ This is `true` when an application frame appears in the call stack below the
228
+ warning emission — meaning your code called the code that warned, even if the
229
+ warning itself comes from a dependency.
230
+
231
+ See `docs/CLASSIFICATION.md` for the detailed pipeline documentation.
232
+
233
+ ## CI integration
234
+
235
+ Policies are expressed via `--fail-on`:
236
+
237
+ | Policy | Warnings that trigger failure |
238
+ |---|---|
239
+ | `none` | Never (explicit opt-out) |
240
+ | `application` | Origin is `application` **or** `triggered_directly_by_application` is true |
241
+ | `direct` | Origin is `application` or `direct_dependency` |
242
+ | `all` | Any captured warning |
243
+ | *(individual origins)* | `direct_dependency`, `transitive_dependency`, `standard_library`, `unknown` |
244
+
245
+ Exit codes:
246
+
247
+ | Code | Meaning |
248
+ |---|---|
249
+ | 0 | Success — no policy failure |
250
+ | 2 | Warntrace usage error (e.g. missing command) |
251
+ | 3 | Policy matched — warnings exceed the configured threshold |
252
+
253
+ > Child-process failure (e.g. pytest test failures) takes priority over policy:
254
+ > if the child exits non-zero, that exit code is returned even when a policy
255
+ > would have matched.
256
+
257
+ ### Example: CI workflow
258
+
259
+ ```yaml
260
+ # .github/workflows/ci.yml
261
+ - run: warntrace run --no-passthrough --fail-on application --output warnings.json pytest
262
+ ```
263
+
264
+ The test suite runs normally. If any application-origin or application-triggered
265
+ warnings are found, the step exits with code 3. The full report is saved to
266
+ `warnings.json` for inspection.
267
+
268
+ ## Privacy
269
+
270
+ Warntrace makes **no network requests**. All analysis is local. It reads:
271
+ - Your project's dependency metadata (`importlib.metadata`)
272
+ - The filesystem path of each stack frame
273
+ - Environment variables (for configuration only)
274
+
275
+ No data is collected, logged, or transmitted.
276
+
277
+ ## Known limitations
278
+
279
+ - **Pytest stack frames.** Under pytest, captured stacks include ~30 frames of
280
+ pytest/pluggy internals. Use `--max-frames` to limit terminal output.
281
+ - **Self-testing.** Running `warntrace run pytest` on warntrace's own test suite
282
+ reports 0 warnings because the test files manipulate capture state at import
283
+ time. This does not affect normal projects.
284
+ - **Dependency graph accuracy.** The graph is built from installed package
285
+ metadata (`Requires-Dist`). Optional/extra dependencies are excluded.
286
+ Namespace packages with ambiguous ownership return `unknown`.
@@ -0,0 +1,276 @@
1
+ # Warntrace
2
+
3
+ Trace the origin of Python warnings — classify them by ownership (application, direct
4
+ dependency, transitive, stdlib), suppress noise, and fail CI when policy is violated.
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ uv add --dev warntrace
10
+ # or
11
+ pip install warntrace
12
+ ```
13
+
14
+ Requires Python >= 3.10.
15
+
16
+ ## Quick start
17
+
18
+ ```bash
19
+ # Capture warnings from any Python command
20
+ warntrace run python your_script.py
21
+
22
+ # Capture warnings from your test suite
23
+ warntrace run pytest
24
+ ```
25
+
26
+ Warntrace intercepts every `warnings.warn()` call in the child process, classifies each
27
+ warning by origin, and writes a JSON report to stdout after the child exits.
28
+
29
+ ## CLI reference
30
+
31
+ ```
32
+ warntrace run [options] -- <command> [args...]
33
+ ```
34
+
35
+ | Flag | Default | Description |
36
+ |---|---|---|
37
+ | `--format` | `json` | Output format: `json`, `detailed`, or `summary` |
38
+ | `--output FILE` | stdout | Write report to a file instead of stdout |
39
+ | `--max-frames N` | unlimited | Limit stack frames per warning in terminal output |
40
+ | `--show-all` | off | Capture normally-suppressed warnings (e.g. `BytesWarning`) |
41
+ | `--no-passthrough` | off | Suppress original warning output from the child process |
42
+ | `--fail-on POLICY` | off | Exit code 3 when a warning matches the policy (see below) |
43
+ | `--root PATH` | child's CWD | Application root directory for classification |
44
+ | `--no-color` | off | Disable ANSI color in terminal output |
45
+
46
+ ### Examples
47
+
48
+ ```bash
49
+ # Basic — run a script and see the default JSON report
50
+ warntrace run python -c "import warnings; warnings.warn('hello')"
51
+
52
+ # Detailed terminal output with limited stack frames
53
+ warntrace run --format detailed --max-frames 5 pytest
54
+
55
+ # Summary — one line per warning with total counts
56
+ warntrace run --format summary pytest
57
+
58
+ # Write report to a file (no ANSI codes in file output)
59
+ warntrace run --output report.json pytest
60
+
61
+ # Collect all warnings silently, even normally-suppressed ones
62
+ warntrace run --no-passthrough --show-all pytest
63
+
64
+ # Fail CI when application-origin warnings are found
65
+ warntrace run --fail-on application pytest
66
+
67
+ # Treat triggered dependency warnings as application-level too
68
+ warntrace run --fail-on application pytest
69
+
70
+ # Fail on any warning (application + deps + stdlib + unknown)
71
+ warntrace run --fail-on all pytest
72
+
73
+ # Opt out of policy failure explicitly
74
+ warntrace run --fail-on none pytest
75
+
76
+ # Specify granular origins to fail on
77
+ warntrace run --fail-on direct_dependency transitive_dependency pytest
78
+
79
+ # Combined: silent, fail on app warnings, save report
80
+ warntrace run --no-passthrough --fail-on application --output ci-report.json pytest
81
+ ```
82
+
83
+ ## Python API
84
+
85
+ ```python
86
+ from warntrace import capture_warnings
87
+
88
+ with capture_warnings() as tracer:
89
+ import warnings
90
+ warnings.warn("something is deprecated", DeprecationWarning)
91
+
92
+ report = tracer.stop()
93
+ print(report.to_dict())
94
+ ```
95
+
96
+ The context manager installs the capture hook, runs your code, restores the
97
+ original warning state, and returns a classified `WarningReport`. The report
98
+ contains the full warning list with origin, stack, dependency paths, and
99
+ occurrence counts.
100
+
101
+ ### Lower-level API
102
+
103
+ ```python
104
+ from warntrace import WarningTracer
105
+
106
+ tracer = WarningTracer(show_all=True, passthrough=False)
107
+ tracer.start()
108
+ # ... your code ...
109
+ report = tracer.stop()
110
+ print(report.to_dict())
111
+ ```
112
+
113
+ ### Configuration
114
+
115
+ | Parameter | Default | Description |
116
+ |---|---|---|
117
+ | `root` | `Path.cwd()` | Application root for origin classification |
118
+ | `show_all` | `False` | Capture normally-suppressed warning types |
119
+ | `passthrough` | `True` | Forward warnings to the original handler |
120
+
121
+ ## Output formats
122
+
123
+ ### JSON (default)
124
+
125
+ ```json
126
+ {
127
+ "schema_version": "0.1",
128
+ "summary": {
129
+ "unique_warnings": 2,
130
+ "total_occurrences": 5,
131
+ "application": 1,
132
+ "direct_dependency": 1,
133
+ "transitive_dependency": 0,
134
+ "standard_library": 0,
135
+ "unknown": 0
136
+ },
137
+ "warnings": [
138
+ {
139
+ "category": "DeprecationWarning",
140
+ "message": "use foobar() instead of deprecated_func()",
141
+ "origin": "direct_dependency",
142
+ "triggered_directly_by_application": true,
143
+ "occurrences": 3,
144
+ "emitted_from": {
145
+ "filename": "/path/to/site-packages/oldpkg/utils.py",
146
+ "lineno": 42,
147
+ "distribution": {
148
+ "name": "oldpkg",
149
+ "normalized_name": "oldpkg",
150
+ "version": "1.2.3"
151
+ }
152
+ },
153
+ "application_frame": {
154
+ "filename": "/home/user/project/app.py",
155
+ "lineno": 15,
156
+ "function": "run",
157
+ "source_line": "oldpkg.deprecated_func()"
158
+ },
159
+ "dependency_path": [
160
+ {"name": "myproject", "normalized_name": "myproject", "version": null},
161
+ {"name": "oldpkg", "normalized_name": "oldpkg", "version": "1.2.3"}
162
+ ],
163
+ "stack": [
164
+ {"filename": "/home/user/project/app.py", "lineno": 15, "function": "run", "source_line": "oldpkg.deprecated_func()"},
165
+ {"filename": "/path/to/site-packages/oldpkg/utils.py", "lineno": 42, "function": "deprecated_func", "source_line": "warnings.warn(...)"}
166
+ ]
167
+ }
168
+ ]
169
+ }
170
+ ```
171
+
172
+ ### Detailed terminal output
173
+
174
+ ```
175
+ warntrace report — 1 unique warning, 3 total occurrences
176
+
177
+ ─── Warning 1 of 1 ───────────────────────────────────────────────────────────
178
+ Origin: ! direct_dependency (triggered by your code)
179
+ Category: DeprecationWarning
180
+ Message: use foobar() instead of deprecated_func()
181
+ Location: oldpkg/utils.py:42
182
+ Occurrences: 3
183
+ Application:
184
+ myproject/app.py:15 run()
185
+ -> oldpkg.deprecated_func()
186
+ Dependency path:
187
+ myproject → oldpkg 1.2.3
188
+ Stack:
189
+ 1 myproject/app.py:15 run()
190
+ 2 oldpkg/utils.py:42 deprecated_func()
191
+ ```
192
+
193
+ ### Summary output
194
+
195
+ ```
196
+ application 1 DeprecationWarning: use foobar() instead...
197
+ direct_dependency 3 DeprecationWarning: use foobar() instead...
198
+ ────────────────────────────────────────────────────────
199
+ warntrace report — 2 unique warnings, 4 total occurrences
200
+ ```
201
+
202
+ ## Classification
203
+
204
+ Warntrace assigns an **origin** to every captured warning by walking the stack and
205
+ mapping each frame to a package or to the Python standard library. There are five
206
+ origin levels:
207
+
208
+ | Origin | Meaning |
209
+ |---|---|
210
+ | `application` | The warning was emitted from your own code (under the project root) |
211
+ | `direct_dependency` | The warning was emitted by a package listed in your dependencies |
212
+ | `transitive_dependency` | The warning was emitted by a dependency of one of your dependencies |
213
+ | `standard_library` | The warning was emitted from CPython stdlib |
214
+ | `unknown` | The file could not be mapped to any known package |
215
+
216
+ Additionally, each warning has a `triggered_directly_by_application` boolean.
217
+ This is `true` when an application frame appears in the call stack below the
218
+ warning emission — meaning your code called the code that warned, even if the
219
+ warning itself comes from a dependency.
220
+
221
+ See `docs/CLASSIFICATION.md` for the detailed pipeline documentation.
222
+
223
+ ## CI integration
224
+
225
+ Policies are expressed via `--fail-on`:
226
+
227
+ | Policy | Warnings that trigger failure |
228
+ |---|---|
229
+ | `none` | Never (explicit opt-out) |
230
+ | `application` | Origin is `application` **or** `triggered_directly_by_application` is true |
231
+ | `direct` | Origin is `application` or `direct_dependency` |
232
+ | `all` | Any captured warning |
233
+ | *(individual origins)* | `direct_dependency`, `transitive_dependency`, `standard_library`, `unknown` |
234
+
235
+ Exit codes:
236
+
237
+ | Code | Meaning |
238
+ |---|---|
239
+ | 0 | Success — no policy failure |
240
+ | 2 | Warntrace usage error (e.g. missing command) |
241
+ | 3 | Policy matched — warnings exceed the configured threshold |
242
+
243
+ > Child-process failure (e.g. pytest test failures) takes priority over policy:
244
+ > if the child exits non-zero, that exit code is returned even when a policy
245
+ > would have matched.
246
+
247
+ ### Example: CI workflow
248
+
249
+ ```yaml
250
+ # .github/workflows/ci.yml
251
+ - run: warntrace run --no-passthrough --fail-on application --output warnings.json pytest
252
+ ```
253
+
254
+ The test suite runs normally. If any application-origin or application-triggered
255
+ warnings are found, the step exits with code 3. The full report is saved to
256
+ `warnings.json` for inspection.
257
+
258
+ ## Privacy
259
+
260
+ Warntrace makes **no network requests**. All analysis is local. It reads:
261
+ - Your project's dependency metadata (`importlib.metadata`)
262
+ - The filesystem path of each stack frame
263
+ - Environment variables (for configuration only)
264
+
265
+ No data is collected, logged, or transmitted.
266
+
267
+ ## Known limitations
268
+
269
+ - **Pytest stack frames.** Under pytest, captured stacks include ~30 frames of
270
+ pytest/pluggy internals. Use `--max-frames` to limit terminal output.
271
+ - **Self-testing.** Running `warntrace run pytest` on warntrace's own test suite
272
+ reports 0 warnings because the test files manipulate capture state at import
273
+ time. This does not affect normal projects.
274
+ - **Dependency graph accuracy.** The graph is built from installed package
275
+ metadata (`Requires-Dist`). Optional/extra dependencies are excluded.
276
+ Namespace packages with ambiguous ownership return `unknown`.
@@ -0,0 +1,48 @@
1
+ [project]
2
+ name = "warntrace"
3
+ version = "0.1.0"
4
+ description = "Find which package caused a Python warning and where your code triggered it."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Kunal Kaul", email = "54585925+kunalkaul@users.noreply.github.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = ["packaging>=24"]
11
+
12
+ [project.scripts]
13
+ warntrace = "warntrace.cli:main"
14
+
15
+ [build-system]
16
+ requires = ["uv_build>=0.11.22,<0.12.0"]
17
+ build-backend = "uv_build"
18
+
19
+ [tool.ruff]
20
+ line-length = 100
21
+ target-version = "py310"
22
+
23
+ [tool.ruff.lint]
24
+ select = ["E", "F", "I", "B", "UP", "SIM"]
25
+
26
+ [tool.mypy]
27
+ python_version = "3.10"
28
+ strict = true
29
+
30
+ [tool.pytest.ini_options]
31
+ testpaths = ["tests"]
32
+ addopts = "-ra"
33
+
34
+ [tool.coverage.run]
35
+ branch = true
36
+ source = ["warntrace"]
37
+
38
+ [tool.coverage.report]
39
+ fail_under = 85
40
+ show_missing = true
41
+
42
+ [dependency-groups]
43
+ dev = [
44
+ "mypy>=2.1.0",
45
+ "pytest>=9.1.0",
46
+ "pytest-cov>=7.1.0",
47
+ "ruff>=0.15.18",
48
+ ]
@@ -0,0 +1,34 @@
1
+ """Warntrace - Find which package caused a Python warning and where your code triggered it."""
2
+
3
+ from warntrace.api import WarningTracer, capture_warnings
4
+ from warntrace.capture import (
5
+ clear_captured_warnings,
6
+ get_aggregator,
7
+ get_captured_warnings,
8
+ install_hook,
9
+ is_hook_installed,
10
+ uninstall_hook,
11
+ )
12
+ from warntrace.models import (
13
+ CapturedWarning,
14
+ DistributionInfo,
15
+ FrameInfo,
16
+ WarningOrigin,
17
+ WarningReport,
18
+ )
19
+
20
+ __all__ = [
21
+ "CapturedWarning",
22
+ "capture_warnings",
23
+ "clear_captured_warnings",
24
+ "DistributionInfo",
25
+ "FrameInfo",
26
+ "get_aggregator",
27
+ "get_captured_warnings",
28
+ "install_hook",
29
+ "is_hook_installed",
30
+ "uninstall_hook",
31
+ "WarningOrigin",
32
+ "WarningReport",
33
+ "WarningTracer",
34
+ ]
@@ -0,0 +1,5 @@
1
+ """Entry point for ``python -m warntrace``."""
2
+
3
+ from warntrace.cli import main
4
+
5
+ main()