project-doctor 0.6.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 (37) hide show
  1. project_doctor-0.6.0/CHANGELOG.md +9 -0
  2. project_doctor-0.6.0/LICENSE +21 -0
  3. project_doctor-0.6.0/MANIFEST.in +7 -0
  4. project_doctor-0.6.0/PKG-INFO +398 -0
  5. project_doctor-0.6.0/README.md +362 -0
  6. project_doctor-0.6.0/docs/PYPI_RELEASE.md +51 -0
  7. project_doctor-0.6.0/examples/sample_project/app.py +21 -0
  8. project_doctor-0.6.0/examples/sample_project/pyproject.toml +9 -0
  9. project_doctor-0.6.0/examples/sample_project/uv.lock +17 -0
  10. project_doctor-0.6.0/examples/sample_project/worker.py +4 -0
  11. project_doctor-0.6.0/pyproject.toml +73 -0
  12. project_doctor-0.6.0/scripts/benchmark.py +118 -0
  13. project_doctor-0.6.0/setup.cfg +4 -0
  14. project_doctor-0.6.0/src/project_doctor/__init__.py +34 -0
  15. project_doctor-0.6.0/src/project_doctor/__main__.py +4 -0
  16. project_doctor-0.6.0/src/project_doctor/analyze.py +171 -0
  17. project_doctor-0.6.0/src/project_doctor/cli.py +381 -0
  18. project_doctor-0.6.0/src/project_doctor/context.py +25 -0
  19. project_doctor-0.6.0/src/project_doctor/dependencies.py +360 -0
  20. project_doctor-0.6.0/src/project_doctor/entrypoint.py +85 -0
  21. project_doctor-0.6.0/src/project_doctor/import_timing.py +150 -0
  22. project_doctor-0.6.0/src/project_doctor/models.py +126 -0
  23. project_doctor-0.6.0/src/project_doctor/py.typed +1 -0
  24. project_doctor-0.6.0/src/project_doctor/report.py +344 -0
  25. project_doctor-0.6.0/src/project_doctor/static_scan.py +301 -0
  26. project_doctor-0.6.0/src/project_doctor/utils.py +98 -0
  27. project_doctor-0.6.0/src/project_doctor/uv.py +103 -0
  28. project_doctor-0.6.0/src/project_doctor.egg-info/PKG-INFO +398 -0
  29. project_doctor-0.6.0/src/project_doctor.egg-info/SOURCES.txt +35 -0
  30. project_doctor-0.6.0/src/project_doctor.egg-info/dependency_links.txt +1 -0
  31. project_doctor-0.6.0/src/project_doctor.egg-info/entry_points.txt +2 -0
  32. project_doctor-0.6.0/src/project_doctor.egg-info/requires.txt +12 -0
  33. project_doctor-0.6.0/src/project_doctor.egg-info/top_level.txt +1 -0
  34. project_doctor-0.6.0/tests/test_basic.py +111 -0
  35. project_doctor-0.6.0/tests/test_performance_features.py +173 -0
  36. project_doctor-0.6.0/tests/test_release_branding.py +69 -0
  37. project_doctor-0.6.0/tests/test_wow_entrypoint_uv.py +285 -0
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.6.0
4
+
5
+ - Renamed the project to Project Doctor with the `project-doctor` distribution and CLI.
6
+ - Moved the import package to `project_doctor`.
7
+ - Updated README branding, install examples, CI snippets, badges, and development commands.
8
+ - Added release documentation for building, checking, and publishing PyPI artifacts without storing credentials in the repository.
9
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Project Doctor maintainers
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,7 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include scripts/benchmark.py
5
+ recursive-include docs *.md
6
+ recursive-include examples *.py *.toml *.lock
7
+ recursive-include tests *.py
@@ -0,0 +1,398 @@
1
+ Metadata-Version: 2.4
2
+ Name: project-doctor
3
+ Version: 0.6.0
4
+ Summary: A local-first health checker for Python imports, dependencies, startup time, and package bloat.
5
+ Author: Project Doctor maintainers
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/billjamesevans/project-doctor
8
+ Project-URL: Repository, https://github.com/billjamesevans/project-doctor
9
+ Project-URL: Issues, https://github.com/billjamesevans/project-doctor/issues
10
+ Project-URL: Changelog, https://github.com/billjamesevans/project-doctor/releases
11
+ Keywords: python,performance,imports,dependencies,optimizer,cli
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Classifier: Topic :: Software Development :: Build Tools
21
+ Classifier: Topic :: System :: Software Distribution
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Provides-Extra: dev
26
+ Requires-Dist: bandit>=1.9; extra == "dev"
27
+ Requires-Dist: build>=1.2; extra == "dev"
28
+ Requires-Dist: mypy>=2.1; extra == "dev"
29
+ Requires-Dist: pytest>=9; extra == "dev"
30
+ Requires-Dist: ruff>=0.14; extra == "dev"
31
+ Requires-Dist: twine>=6; extra == "dev"
32
+ Provides-Extra: security
33
+ Requires-Dist: detect-secrets>=1.5; extra == "security"
34
+ Requires-Dist: pip-audit>=2.10; extra == "security"
35
+ Dynamic: license-file
36
+
37
+ # Project Doctor
38
+
39
+ [![CI](https://github.com/billjamesevans/project-doctor/actions/workflows/ci.yml/badge.svg)](https://github.com/billjamesevans/project-doctor/actions/workflows/ci.yml)
40
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
41
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
42
+ ![Project Doctor](https://img.shields.io/badge/project--doctor-passing-brightgreen)
43
+
44
+ A local-first health checker for Python imports, dependencies, startup time, and package bloat.
45
+
46
+ Project Doctor is a zero-dependency analyzer for finding project optimization work that usually hides in plain sight:
47
+
48
+ - entrypoint startup drag
49
+ - slow third-party imports
50
+ - likely unused dependencies
51
+ - possible undeclared imports
52
+ - opt-in large installed package checks
53
+ - top-level imports that look safe to move into deferred code paths
54
+ - CI thresholds for dependency and import hygiene
55
+ - uv.lock sync status and package explanations
56
+
57
+ Project Doctor never edits your code. It produces reviewable reports and CI-friendly checks so teams can make deliberate changes.
58
+
59
+ ## Security and privacy
60
+
61
+ Project Doctor is local-first and safe by default. Static scans parse source files with Python's `ast` module and do not import your project code. Optional import timing must be enabled with `--import-time`; it imports third-party modules in child processes, so leave it off when reviewing sensitive projects or code with import-time side effects. Installed package size checks are also opt-in with `--package-sizes` because they walk installed distribution metadata.
62
+
63
+ ## Install
64
+
65
+ Current reliable install path:
66
+
67
+ From this folder:
68
+
69
+ ```bash
70
+ python3 -m pip install -e .
71
+ ```
72
+
73
+ Then run:
74
+
75
+ ```bash
76
+ project-doctor analyze /path/to/your/project
77
+ ```
78
+
79
+ Or without installing:
80
+
81
+ ```bash
82
+ PYTHONPATH=src python3 -m project_doctor analyze /path/to/your/project
83
+ ```
84
+
85
+ After the PyPI release is live:
86
+
87
+ ```bash
88
+ pip install project-doctor
89
+ uv tool install project-doctor
90
+ ```
91
+
92
+ ## Quick examples
93
+
94
+ ```bash
95
+ project-doctor doctor
96
+ project-doctor analyze examples/sample_project
97
+ project-doctor analyze examples/sample_project --json -o project-doctor-report.json
98
+ project-doctor analyze examples/sample_project --jobs auto --package-sizes
99
+ project-doctor analyze examples/sample_project --entrypoint "python app.py"
100
+ project-doctor analyze examples/sample_project --uv
101
+ project-doctor check examples/sample_project --max-unused 0
102
+ project-doctor check examples/sample_project --import-time --json --max-import-ms 150
103
+ project-doctor sync-check examples/sample_project/uv.lock
104
+ project-doctor explain-package pandas examples/sample_project --uv
105
+ ```
106
+
107
+ `analyze` writes a shareable "wow" report by default. Use `--report detailed` for the longer audit report. `check` prints a compact status report and exits nonzero when a configured threshold is exceeded.
108
+
109
+ ## Example report
110
+
111
+ ```text
112
+ Project Doctor Report
113
+
114
+ Startup time: 1.42s
115
+ Potential avoidable import cost: 630ms
116
+
117
+ Top startup contributors:
118
+ - pandas: 312ms
119
+ - boto3: 205ms
120
+ - matplotlib: 113ms
121
+
122
+ Likely unused dependencies:
123
+ - openpyxl
124
+ - beautifulsoup4
125
+ - python-dotenv
126
+
127
+ Possible undeclared imports:
128
+ - requests imported but not declared
129
+
130
+ Largest installed packages:
131
+ - torch: 742MB
132
+ - pandas: 78MB
133
+ - botocore: 71MB
134
+
135
+ Suggested quick wins:
136
+ 1. Move `pandas` import at `reports.py:3` inside the deferred function that uses it.
137
+ 2. Remove `openpyxl` if it is no longer used by active code paths.
138
+ 3. Add `requests` to `pyproject.toml`.
139
+ ```
140
+
141
+ ## Python API
142
+
143
+ ```python
144
+ from project_doctor import AnalysisContext, analyze_project
145
+
146
+ context = AnalysisContext.from_environment()
147
+ report = analyze_project(
148
+ "examples/sample_project",
149
+ context=context,
150
+ jobs="auto",
151
+ run_import_timing=False,
152
+ collect_package_sizes=False,
153
+ entrypoint="python app.py",
154
+ )
155
+ print(report.unused_dependencies)
156
+ ```
157
+
158
+ The returned `AnalysisReport` and nested report objects are dataclasses and can be converted to dictionaries with `report.to_dict()`. Reuse `AnalysisContext` when analyzing multiple projects in one process; it keeps installed package metadata and package size estimates cached.
159
+
160
+ ## What Project Doctor checks
161
+
162
+ ### Static import scan
163
+
164
+ Project Doctor parses Python source with `ast`, so it can read imports without importing your project code. Syntax errors are reported as warnings instead of aborting the full scan.
165
+
166
+ ### Dependency usage
167
+
168
+ Project Doctor reads dependencies from:
169
+
170
+ - `pyproject.toml` `[project.dependencies]`
171
+ - `pyproject.toml` `[project.optional-dependencies]`
172
+ - `pyproject.toml` `[dependency-groups]`, including `{ include-group = "..." }`
173
+ - common Poetry dependency sections
174
+ - `requirements*.txt`, including nested `-r` and `--requirement` includes
175
+
176
+ It compares declared dependencies against static imports. Results marked `unused` should be treated as a review queue, not an automatic delete list.
177
+
178
+ ### Import timings
179
+
180
+ When `--import-time` is enabled, Project Doctor runs a subprocess like this for each likely third-party top-level import:
181
+
182
+ ```bash
183
+ python -X importtime -c "import pandas"
184
+ ```
185
+
186
+ This keeps imports out of the analyzer process, but the imported library can still run import-time side effects in the child process. Leave import timing disabled when you want a purely static scan.
187
+
188
+ ### Entrypoint startup timing
189
+
190
+ Entrypoint mode measures the command users actually wait on:
191
+
192
+ ```bash
193
+ project-doctor analyze . --entrypoint "python app.py"
194
+ project-doctor analyze . --entrypoint "uvicorn app:app"
195
+ project-doctor analyze . --entrypoint "python -m my_cli"
196
+ ```
197
+
198
+ Project Doctor runs the command with Python import profiling enabled and `shell=False`, then folds the parsed startup import costs into the report. Server-style commands may time out by design; Project Doctor still reports import data captured before the timeout.
199
+
200
+ ### Lazy-import candidates
201
+
202
+ Project Doctor looks for imports that are defined at module load but only used inside deferred function or method bodies.
203
+
204
+ For example:
205
+
206
+ ```python
207
+ import pandas as pd
208
+
209
+
210
+ def make_report(rows):
211
+ return pd.DataFrame(rows)
212
+ ```
213
+
214
+ Project Doctor may suggest:
215
+
216
+ ```python
217
+ def make_report(rows):
218
+ import pandas as pd
219
+ return pd.DataFrame(rows)
220
+ ```
221
+
222
+ That kind of change can reduce startup time for CLIs, Lambdas, Flask/FastAPI apps, and agent scripts when a heavy dependency is only needed on a less-common path.
223
+
224
+ ## CLI
225
+
226
+ ```bash
227
+ project-doctor doctor [path] [options]
228
+
229
+ Runs the default project health report. It is an alias for `project-doctor analyze .` with the same analysis options.
230
+ ```
231
+
232
+ ```bash
233
+ project-doctor analyze [path] [options]
234
+
235
+ Options:
236
+ --json Emit JSON instead of Markdown
237
+ --report wow|detailed Human report style, default wow
238
+ --output FILE, -o FILE Write report to a file
239
+ --import-time Run subprocess import timing checks
240
+ --no-import-time Skip subprocess import timing checks, default
241
+ --import-time-limit N Max third-party modules to time, default 20
242
+ --import-time-timeout SECONDS Timeout per import, default 10
243
+ --package-sizes Collect installed package sizes
244
+ --no-package-sizes Skip installed package size checks, default
245
+ --jobs N|auto Static scan workers, default auto
246
+ --entrypoint COMMAND Measure startup for a real entrypoint command
247
+ --entrypoint-timeout SECONDS Timeout for entrypoint measurement, default 10
248
+ --uv Include uv.lock status
249
+ --max-files N Max Python files to scan, default 5000
250
+ --exclude DIR Extra directory name to exclude; repeatable
251
+ ```
252
+
253
+ ```bash
254
+ project-doctor check [path] [options]
255
+
256
+ Options:
257
+ --json Emit machine-readable check results
258
+ --max-unused N Max likely unused dependencies, default 0
259
+ --max-undeclared N Max possible undeclared imports, default 0
260
+ --max-lazy-imports N Max lazy-import candidates
261
+ --max-import-ms N Max cumulative import time for any measured module
262
+ --max-package-mb N Max installed package size; enables package size checks
263
+ --import-time Run subprocess import timing checks
264
+ --no-import-time Skip subprocess import timing checks, default
265
+ --import-time-limit N Max third-party modules to time, default 20
266
+ --import-time-timeout SECONDS Timeout per import, default 10
267
+ --package-sizes Collect installed package sizes
268
+ --no-package-sizes Skip installed package size checks, default
269
+ --jobs N|auto Static scan workers, default auto
270
+ --entrypoint COMMAND Measure startup for a real entrypoint command
271
+ --entrypoint-timeout SECONDS Timeout for entrypoint measurement, default 10
272
+ --uv Include uv.lock status
273
+ --max-files N Max Python files to scan, default 5000
274
+ --exclude DIR Extra directory name to exclude; repeatable
275
+ ```
276
+
277
+ ```bash
278
+ project-doctor sync-check [uv.lock] [options]
279
+
280
+ Options:
281
+ --json Emit machine-readable sync results
282
+ ```
283
+
284
+ ```bash
285
+ project-doctor explain-package PACKAGE [path] [options]
286
+
287
+ Options:
288
+ --uv Include uv.lock status
289
+ --json Emit machine-readable package explanation
290
+ ```
291
+
292
+ ## CI
293
+
294
+ Add Project Doctor to GitHub Actions as a dependency hygiene gate:
295
+
296
+ ```yaml
297
+ - name: Check Python dependency health
298
+ run: project-doctor check . --max-unused 0 --max-undeclared 0 --max-package-mb 100
299
+ ```
300
+
301
+ Intended full workflow after first package release and name/package ownership confirmation:
302
+
303
+ ```yaml
304
+ name: project-doctor
305
+
306
+ on: [push, pull_request]
307
+
308
+ jobs:
309
+ project-doctor:
310
+ runs-on: ubuntu-latest
311
+ steps:
312
+ - uses: actions/checkout@v4
313
+ - uses: actions/setup-python@v5
314
+ with:
315
+ python-version: "3.12"
316
+ - run: pip install project-doctor
317
+ - run: project-doctor check . --max-unused 0 --max-undeclared 0
318
+ ```
319
+
320
+ For projects that use uv:
321
+
322
+ ```yaml
323
+ - name: Check uv lock sync
324
+ run: project-doctor sync-check uv.lock
325
+ ```
326
+
327
+ Badge for project docs:
328
+
329
+ ```markdown
330
+ ![Project Doctor](https://img.shields.io/badge/project--doctor-passing-brightgreen)
331
+ ```
332
+
333
+ ## uv
334
+
335
+ Project Doctor understands the common `pyproject.toml` + `uv.lock` workflow:
336
+
337
+ ```bash
338
+ project-doctor analyze --uv
339
+ project-doctor sync-check uv.lock
340
+ project-doctor explain-package pandas --uv
341
+ ```
342
+
343
+ `sync-check` verifies that direct dependencies declared in `pyproject.toml` are represented in `uv.lock`. `explain-package` shows whether a package is declared, which import names map to it, whether it is installed locally, and whether uv has locked it.
344
+
345
+ ## Performance
346
+
347
+ Project Doctor keeps the default path fast:
348
+
349
+ - installed package metadata is indexed once per analysis context
350
+ - package size checks are skipped unless requested or needed by `--max-package-mb`
351
+ - static scans stream into aggregate results instead of retaining every file scan object
352
+ - `--jobs auto` uses serial scanning for small projects and bounded parallel scanning for larger projects
353
+
354
+ Run the benchmark helper against a synthetic project:
355
+
356
+ ```bash
357
+ PYTHONPATH=src python3 scripts/benchmark.py --files 1000 --runs 5 --jobs auto
358
+ ```
359
+
360
+ Or benchmark a real project:
361
+
362
+ ```bash
363
+ PYTHONPATH=src python3 scripts/benchmark.py /path/to/project --runs 5 --jobs auto
364
+ ```
365
+
366
+ ## Development
367
+
368
+ ```bash
369
+ python3 -m venv .venv
370
+ .venv/bin/python -m pip install -e ".[dev,security]"
371
+ .venv/bin/python -m pytest
372
+ .venv/bin/python -m ruff check .
373
+ .venv/bin/python -m mypy src/project_doctor tests
374
+ .venv/bin/python -m bandit -r src examples scripts -q
375
+ .venv/bin/python -m pip_audit
376
+ .venv/bin/python scripts/benchmark.py --files 200 --runs 2
377
+ .venv/bin/python -m build
378
+ ```
379
+
380
+ ## Current limitations
381
+
382
+ - Static analysis misses dynamic imports and plugin systems.
383
+ - Dependency names do not always match import names.
384
+ - Optional dependencies may be marked unused if their optional code path is not statically imported.
385
+ - Opt-in import timing imports third-party packages in a subprocess, which can still trigger child-process side effects.
386
+ - Entrypoint mode runs your command in a subprocess; use it for commands that are safe to execute locally.
387
+ - Package size checks are opt-in and only work for dependencies installed in the current environment.
388
+
389
+ ## Roadmap
390
+
391
+ The next serious versions should add:
392
+
393
+ 1. Deeper entrypoint startup benchmarks with clearer default-path waste attribution.
394
+ 2. `project-doctor fix --lazy-imports` with AST-safe rewrites and backups.
395
+ 3. Lockfile awareness for Poetry, PDM, and pip-tools.
396
+ 4. Docker/image-size analysis.
397
+ 5. Richer package-name/import-name mapping.
398
+ 6. Profiler integration for hot-loop acceleration suggestions.