python-qv 0.1.0__py3-none-any.whl → 0.1.2__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.
- python_qv-0.1.2.dist-info/METADATA +492 -0
- python_qv-0.1.2.dist-info/RECORD +46 -0
- qv/__init__.py +1 -1
- qv/__main__.py +6 -0
- qv/analyzers/dependencies/analyzer.py +65 -4
- qv/analyzers/environment/drift.py +75 -6
- qv/analyzers/imports/analyzer.py +218 -3
- qv/analyzers/security/__init__.py +13 -1
- qv/analyzers/security/analyzer.py +113 -0
- qv/analyzers/security/lockfile_parser.py +173 -0
- qv/analyzers/security/osv_client.py +137 -0
- qv/cli/main.py +332 -2
- qv/core/config.py +4 -1
- qv/core/engine.py +2 -0
- qv/core/models.py +16 -0
- qv/core/project.py +111 -6
- qv/remediation/__init__.py +6 -0
- qv/remediation/engine.py +302 -0
- qv/remediation/models.py +62 -0
- qv/reporters/__init__.py +2 -0
- qv/reporters/github_annotator.py +88 -0
- qv/reporters/html_reporter.py +511 -0
- qv/rules/registry.py +18 -0
- qv/tui/__init__.py +5 -0
- qv/tui/app.py +315 -0
- qv/visualizers/__init__.py +5 -0
- qv/visualizers/tree.py +209 -0
- python_qv-0.1.0.dist-info/METADATA +0 -281
- python_qv-0.1.0.dist-info/RECORD +0 -33
- {python_qv-0.1.0.dist-info → python_qv-0.1.2.dist-info}/WHEEL +0 -0
- {python_qv-0.1.0.dist-info → python_qv-0.1.2.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,492 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: python-qv
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Diagnose why a Python project is unhealthy — explain root cause and suggest safe fixes.
|
|
5
|
+
Project-URL: Homepage, https://github.com/inzamol/qv
|
|
6
|
+
Project-URL: Repository, https://github.com/inzamol/qv
|
|
7
|
+
Project-URL: Documentation, https://inzamol.github.io/qv/
|
|
8
|
+
Project-URL: Issues, https://github.com/inzamol/qv/issues
|
|
9
|
+
Author: Inzamul Hoque
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
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 :: Bug Tracking
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: click>=8.1.0
|
|
23
|
+
Requires-Dist: packaging>=23.2
|
|
24
|
+
Requires-Dist: pydantic>=2.5.0
|
|
25
|
+
Requires-Dist: rich>=13.7.0
|
|
26
|
+
Requires-Dist: tomli>=2.0.1; python_version < '3.11'
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pyright>=1.1.350; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: tox-uv>=1.7.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: tox>=4.15.0; extra == 'dev'
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
|
|
37
|
+
Requires-Dist: mkdocs<2.0.0,>=1.6.0; extra == 'docs'
|
|
38
|
+
Requires-Dist: pymdown-extensions>=10.7.0; extra == 'docs'
|
|
39
|
+
Description-Content-Type: text/markdown
|
|
40
|
+
|
|
41
|
+
# 🔍 qv
|
|
42
|
+
|
|
43
|
+
<div align="center">
|
|
44
|
+
|
|
45
|
+
**Diagnose why your Python project is unhealthy — understand the root cause and get safe, actionable fixes.**
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/python-qv/)
|
|
48
|
+
[](https://pypi.org/project/python-qv/)
|
|
49
|
+
[](https://github.com/inzamol/qv/actions/workflows/ci.yml)
|
|
50
|
+
[](https://opensource.org/licenses/MIT)
|
|
51
|
+
|
|
52
|
+
[Installation](#-installation) • [Quick Start](#-quick-start) • [Interactive TUI](#-interactive-tui-explorer) • [Features](#-what-it-detects) • [CLI Commands](#-cli-commands) • [CI/CD Integration](#-cicd-integration) • [Documentation](docs/getting_started.md)
|
|
53
|
+
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 💡 Why qv?
|
|
59
|
+
|
|
60
|
+
Python projects rarely fail because of Python syntax. They fail because of **ecosystem friction**:
|
|
61
|
+
- Incompatible transitive dependency constraints that break your resolver.
|
|
62
|
+
- Missing dependencies you forgot to add to `pyproject.toml`.
|
|
63
|
+
- Docker containers running Python 3.10 while your team develops on 3.12.
|
|
64
|
+
- Silent circular imports that only crash at runtime when certain modules load.
|
|
65
|
+
|
|
66
|
+
Instead of parsing hundreds of lines of cryptic resolver logs, **`qv`** scans your project in milliseconds, pinpoints the root cause, shows the exact evidence, and gives you a copy-paste command to fix it.
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
🔍 qv
|
|
70
|
+
Project: payment-service
|
|
71
|
+
Python: 3.12.7
|
|
72
|
+
Package Manager: uv
|
|
73
|
+
|
|
74
|
+
🔴 1 Errors 🟡 1 Warnings 🟢 48 Checks Passed
|
|
75
|
+
|
|
76
|
+
┌────────────────── 🔴 DEP-001 Dependency constraint conflict ─────────────────┐
|
|
77
|
+
│ celery 5.4.0 requires kombu<5.4.0,>=5.3.0, but installed is kombu 5.5.2. │
|
|
78
|
+
│ │
|
|
79
|
+
│ Evidence: │
|
|
80
|
+
│ • celery declared requirement: kombu<5.4.0,>=5.3.0 │
|
|
81
|
+
│ • Installed kombu version: 5.5.2 in active environment │
|
|
82
|
+
│ │
|
|
83
|
+
│ Suggested fix: │
|
|
84
|
+
│ Upgrade celery or pin kombu to <5.4.0,>=5.3.0. │
|
|
85
|
+
│ $ uv add 'kombu<5.4.0,>=5.3.0' │
|
|
86
|
+
└──────────────────────────────────────────────────────────────────────────────┘
|
|
87
|
+
|
|
88
|
+
Health Score: 85/100
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 📦 Installation
|
|
94
|
+
|
|
95
|
+
Install `python-qv` into your virtual environment (provides the `qv` CLI):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Using pip
|
|
99
|
+
pip install python-qv
|
|
100
|
+
|
|
101
|
+
# Using uv
|
|
102
|
+
uv add python-qv --dev
|
|
103
|
+
|
|
104
|
+
# Run directly without installing (via uvx or pipx)
|
|
105
|
+
uvx python-qv scan
|
|
106
|
+
# or
|
|
107
|
+
pipx run python-qv scan
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🚀 Quick Start
|
|
113
|
+
|
|
114
|
+
### 1. Run a Health Scan
|
|
115
|
+
|
|
116
|
+
Run `qv scan` inside any Python repository:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
qv scan
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 2. Launch the Interactive TUI Explorer
|
|
123
|
+
|
|
124
|
+
Explore findings interactively with keyboard navigation, live details, dependency tree view, and 1-key remediation:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
qv inspect
|
|
128
|
+
# or
|
|
129
|
+
qv ui
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 3. Generate a Self-Contained HTML Report
|
|
133
|
+
|
|
134
|
+
Export a standalone, interactive HTML report with score meters, search filters, and dark/light modes:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
qv scan --html report.html
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 4. Understand Any Flagged Issue
|
|
141
|
+
|
|
142
|
+
Need more context on why a rule triggered? Run `explain`:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
qv explain DEP-001
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 5. Add Project Configuration
|
|
149
|
+
|
|
150
|
+
To add default configuration to your `pyproject.toml`:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
qv init
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 🖥️ Interactive TUI Explorer
|
|
159
|
+
|
|
160
|
+
Run `qv inspect` (or `qv ui`) for a full terminal dashboard:
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
┌────────────────────────────────────────────── qv Explorer ──────────────────────────────────────────────┐
|
|
164
|
+
│ 🔍 qv Explorer • Project: all-in-one-demo • Health Score: 40/100 │
|
|
165
|
+
│ Python: 3.12.7 | Package Manager: PIP | Errors: 3 | Warnings: 3 | Checks Passed: 49 │
|
|
166
|
+
├────────────────────────── Findings (1/6) ──────────────────────────┬──────────────── Details: DEP-001 ──┤
|
|
167
|
+
│ Sev Rule Title │ [ERROR] DEP-001: Constraint conflict│
|
|
168
|
+
│ 👉 ERR DEP-001 Dependency constraint conflict │ celery requires kombu<5.4.0,>=5.3.0│
|
|
169
|
+
│ ERR DEP-002 Missing dependency: httpx │ Location: pyproject.toml │
|
|
170
|
+
│ ERR DEP-002 Missing dependency: pydantic │ │
|
|
171
|
+
│ WARN DEP-003 Unused declared dependency: requests │ Remediation: │
|
|
172
|
+
│ WARN DEP-003 Unused declared dependency: pyyaml │ 👉 Pin kombu to <5.4.0,>=5.3.0 │
|
|
173
|
+
│ ... 1 more below ... │ $ pip install 'kombu<5.4.0' │
|
|
174
|
+
├────────────────────────────────────────────────────────────────────┴────────────────────────────────────┤
|
|
175
|
+
│ [↑/k, ↓/j] Navigate • [Enter] Expand • [f] Apply Fix • [t] Tree View • [q] Quit │
|
|
176
|
+
└─────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- **`↑ / k` & `↓ / j`**: Scroll smoothly through findings.
|
|
180
|
+
- **`f`**: Apply automated remediation fix for the active finding.
|
|
181
|
+
- **`t`**: Toggle between Findings view and live Dependency Tree.
|
|
182
|
+
- **`Enter`**: Expand details panel.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 🔍 What It Detects
|
|
187
|
+
|
|
188
|
+
| Category | Rule ID | Description | Default Severity |
|
|
189
|
+
|---|---|---|---|
|
|
190
|
+
| **Dependencies** | `DEP-001` | Incompatible package version constraints across dependency tree | `ERROR` |
|
|
191
|
+
| | `DEP-002` | Third-party packages imported in code but missing from `pyproject.toml` | `ERROR` |
|
|
192
|
+
| | `DEP-003` | Declared dependencies that are never imported anywhere in project | `WARNING` |
|
|
193
|
+
| | `DEP-004` | Package requires a Python version incompatible with target runtime | `WARNING` |
|
|
194
|
+
| | `DEP-005` | Installed virtualenv version does not match declared manifest pin | `WARNING` |
|
|
195
|
+
| **Environment** | `ENV-001` | Active interpreter version differs from project `requires-python` | `WARNING` |
|
|
196
|
+
| | `ENV-002` | Dockerfile base image Python version differs from project runtime | `WARNING` |
|
|
197
|
+
| | `ENV-003` | CI matrix does not cover the Python versions declared in project | `WARNING` |
|
|
198
|
+
| **Architecture** | `IMP-001` | Circular import cycles across local modules | `ERROR` |
|
|
199
|
+
| | `IMP-002` | Unresolved relative or internal module imports | `ERROR` |
|
|
200
|
+
| **Packaging** | `PKG-001` | Missing PEP 621 metadata (name, version, etc.) | `WARNING` |
|
|
201
|
+
| | `PKG-002` | Invalid syntax or malformed keys in `pyproject.toml` | `ERROR` |
|
|
202
|
+
|
|
203
|
+
👉 *See full explanations and remediation steps in the [Rules Catalog](docs/rules.md).*
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 🛠️ CLI Commands & Examples
|
|
208
|
+
|
|
209
|
+
### 1. `qv scan` — Full Project Diagnostics
|
|
210
|
+
Run a comprehensive health audit scanning dependencies, runtime environment, imports, and packaging.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
# Scan current repository
|
|
214
|
+
qv scan
|
|
215
|
+
|
|
216
|
+
# Scan a specific directory or microservice
|
|
217
|
+
qv scan ./services/payment
|
|
218
|
+
|
|
219
|
+
# Strict mode: fail CI if any warnings exist (exit code 1)
|
|
220
|
+
qv scan --strict
|
|
221
|
+
|
|
222
|
+
# Generate a self-contained interactive HTML dashboard
|
|
223
|
+
qv scan --html report.html
|
|
224
|
+
|
|
225
|
+
# Output SARIF format for GitHub Code Scanning / IDE integration
|
|
226
|
+
qv scan --sarif -o results.sarif
|
|
227
|
+
|
|
228
|
+
# Output machine-readable JSON
|
|
229
|
+
qv scan --json -o results.json
|
|
230
|
+
|
|
231
|
+
# Offline / Airgapped mode (skips remote package index checks)
|
|
232
|
+
qv scan --offline
|
|
233
|
+
|
|
234
|
+
# Emit GitHub Actions workflow command annotations (::error:: and ::warning::)
|
|
235
|
+
qv scan --ci --github-annotations
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### 2. `qv inspect` (or `qv ui`) — Interactive Terminal Dashboard
|
|
241
|
+
Explore diagnostic findings, view evidence, inspect circular import trees, and apply fixes interactively with keyboard shortcuts.
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
# Launch interactive dashboard for current repository
|
|
245
|
+
qv inspect
|
|
246
|
+
|
|
247
|
+
# Inspect another project
|
|
248
|
+
qv inspect ../another-service
|
|
249
|
+
|
|
250
|
+
# Airgapped / offline TUI
|
|
251
|
+
qv inspect --offline
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
*Controls: `↑`/`k` and `↓`/`j` to navigate, `Enter` to expand details, `f` to apply fix, `t` to toggle dependency tree, `q` to quit.*
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### 3. `qv fix` — Safe Automated Remediation
|
|
259
|
+
Automatically generate and apply deterministic fixes to your project manifest and configuration.
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# Interactive wizard (prompts before applying each fix)
|
|
263
|
+
qv fix
|
|
264
|
+
|
|
265
|
+
# Preview proposed file diffs and commands without modifying disk
|
|
266
|
+
qv fix --dry-run
|
|
267
|
+
|
|
268
|
+
# Automatically apply all safe fixes without prompting
|
|
269
|
+
qv fix -y
|
|
270
|
+
|
|
271
|
+
# Fix only a specific rule (e.g. missing dependencies)
|
|
272
|
+
qv fix --rule DEP-002 -y
|
|
273
|
+
|
|
274
|
+
# Apply fixes and execute package manager sync commands
|
|
275
|
+
qv fix --sync -y
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
### 4. `qv tree` (or `qv graph`) — Dependency & Architecture Visualizer
|
|
281
|
+
Render color-coded visual trees of package dependencies and source module import graphs.
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Render complete overview (dependency tree + import architecture)
|
|
285
|
+
qv tree
|
|
286
|
+
|
|
287
|
+
# Visualize direct & transitive dependencies up to depth 2
|
|
288
|
+
qv tree --dependencies --depth 2
|
|
289
|
+
# or shorthand:
|
|
290
|
+
qv tree -d -L 2
|
|
291
|
+
|
|
292
|
+
# Visualize internal module import graph and circular import cycles
|
|
293
|
+
qv tree --imports
|
|
294
|
+
# or shorthand:
|
|
295
|
+
qv tree -i
|
|
296
|
+
|
|
297
|
+
# Export tree hierarchy and cycle statistics as JSON
|
|
298
|
+
qv tree --json > tree.json
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
### 5. `qv explain` — Rule Catalog & Fix Advice
|
|
304
|
+
Look up detailed explanations, common causes, evidence criteria, and remediation advice for any diagnostic rule.
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Explain dependency constraint conflicts
|
|
308
|
+
qv explain DEP-001
|
|
309
|
+
|
|
310
|
+
# Explain missing undeclared imports
|
|
311
|
+
qv explain DEP-002
|
|
312
|
+
|
|
313
|
+
# Explain circular import loops
|
|
314
|
+
qv explain IMP-001
|
|
315
|
+
|
|
316
|
+
# Explain Python/Docker environment drift
|
|
317
|
+
qv explain ENV-002
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### 6. Subsystem-Focused Scans
|
|
323
|
+
Run focused audits on specific components when troubleshooting or in modular CI pipelines:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# Check dependencies only (conflicts, missing, unused, incompatible Python)
|
|
327
|
+
qv dependency
|
|
328
|
+
|
|
329
|
+
# Check environment drift only (interpreter version, Dockerfile, CI matrix)
|
|
330
|
+
qv environment
|
|
331
|
+
|
|
332
|
+
# Check AST & imports only (circular import loops, unresolvable modules)
|
|
333
|
+
qv architecture
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### 7. `qv init` — Project Configuration Setup
|
|
339
|
+
Initialize or update `pyproject.toml` with default `[tool.qv]` configuration rules and path exclusions without overwriting existing settings.
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# Initialize [tool.qv] in pyproject.toml
|
|
343
|
+
qv init
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
### 8. `qv version` — Version Information
|
|
349
|
+
```bash
|
|
350
|
+
qv version
|
|
351
|
+
# or
|
|
352
|
+
qv --version
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
👉 *See full option matrices in the [CLI Reference](docs/cli_reference.md).*
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## ⚙️ Configuration
|
|
360
|
+
|
|
361
|
+
Configure `qv` in your `pyproject.toml`:
|
|
362
|
+
|
|
363
|
+
```toml
|
|
364
|
+
[tool.qv]
|
|
365
|
+
|
|
366
|
+
# Override severity for any rule (error, warning, info, off)
|
|
367
|
+
[tool.qv.rules]
|
|
368
|
+
DEP-001 = "error"
|
|
369
|
+
DEP-003 = "warning"
|
|
370
|
+
ENV-002 = "info"
|
|
371
|
+
|
|
372
|
+
# Ignore specific rules
|
|
373
|
+
[tool.qv.ignore]
|
|
374
|
+
rules = ["DEP-004"]
|
|
375
|
+
|
|
376
|
+
# Exclude directories from scanning
|
|
377
|
+
[tool.qv.paths]
|
|
378
|
+
exclude = [
|
|
379
|
+
".venv",
|
|
380
|
+
"build",
|
|
381
|
+
"dist",
|
|
382
|
+
"legacy_scripts",
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
# Set expected target Python version
|
|
386
|
+
[tool.qv.runtime]
|
|
387
|
+
python = "3.12"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
👉 *Learn more in the [Configuration Guide](docs/configuration.md).*
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## 🤖 CI/CD Integration
|
|
395
|
+
|
|
396
|
+
### Official GitHub Action
|
|
397
|
+
|
|
398
|
+
You can use the official `qv` GitHub Action directly in `.github/workflows/ci.yml`:
|
|
399
|
+
|
|
400
|
+
```yaml
|
|
401
|
+
name: Health & Dependency Scan
|
|
402
|
+
|
|
403
|
+
on: [push, pull_request]
|
|
404
|
+
|
|
405
|
+
jobs:
|
|
406
|
+
qv:
|
|
407
|
+
runs-on: ubuntu-latest
|
|
408
|
+
steps:
|
|
409
|
+
- uses: actions/checkout@v4
|
|
410
|
+
- name: Run qv Health Scan
|
|
411
|
+
uses: inzamol/qv@v1
|
|
412
|
+
with:
|
|
413
|
+
strict: true
|
|
414
|
+
html_report: report.html
|
|
415
|
+
sarif_report: qv.sarif
|
|
416
|
+
github_annotations: true
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### GitHub Actions (Manual setup with SARIF code scanning)
|
|
420
|
+
|
|
421
|
+
```yaml
|
|
422
|
+
name: Health & Dependency Scan
|
|
423
|
+
|
|
424
|
+
on: [push, pull_request]
|
|
425
|
+
|
|
426
|
+
jobs:
|
|
427
|
+
qv:
|
|
428
|
+
runs-on: ubuntu-latest
|
|
429
|
+
steps:
|
|
430
|
+
- uses: actions/checkout@v4
|
|
431
|
+
- uses: astral-sh/setup-uv@v3
|
|
432
|
+
|
|
433
|
+
# Run scan with PR annotations
|
|
434
|
+
- name: Run qv
|
|
435
|
+
run: uv run qv scan --ci --github-annotations
|
|
436
|
+
|
|
437
|
+
# Generate SARIF report for GitHub Code Scanning
|
|
438
|
+
- name: Generate SARIF report
|
|
439
|
+
run: uv run qv scan --sarif -o qv.sarif
|
|
440
|
+
if: always()
|
|
441
|
+
|
|
442
|
+
- name: Upload SARIF to GitHub Security
|
|
443
|
+
uses: github/codeql-action/upload-sarif@v3
|
|
444
|
+
with:
|
|
445
|
+
sarif_file: qv.sarif
|
|
446
|
+
if: always()
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Pre-commit Hook Integration
|
|
450
|
+
|
|
451
|
+
Add `qv` directly to your `.pre-commit-config.yaml` to catch dependency drift and circular imports before committing:
|
|
452
|
+
|
|
453
|
+
```yaml
|
|
454
|
+
repos:
|
|
455
|
+
- repo: https://github.com/inzamol/qv
|
|
456
|
+
rev: v0.1.0
|
|
457
|
+
hooks:
|
|
458
|
+
# Diagnose health before committing
|
|
459
|
+
- id: qv-scan
|
|
460
|
+
args: [--strict, --offline]
|
|
461
|
+
|
|
462
|
+
# Optional: Auto-remediate safe issues on commit
|
|
463
|
+
# - id: qv-fix
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
👉 *See full details in [CI/CD & Pre-commit Integration](docs/ci_integration.md).*
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## 🎯 Design Principles
|
|
471
|
+
|
|
472
|
+
- **Diagnose first. Explain second. Fix safely.**
|
|
473
|
+
- **No destructive auto-mutations**: Remediations provide the exact commands/diffs for you to review and apply.
|
|
474
|
+
- **Local-first & Blazing fast**: Zero network calls required; scans complete in under a second.
|
|
475
|
+
- **Package-manager agnostic**: Works out of the box with `uv`, Poetry, `pip`, PDM, and Pipenv.
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
## 📚 Complete Documentation
|
|
480
|
+
|
|
481
|
+
- 🚀 [Getting Started Guide](docs/getting_started.md)
|
|
482
|
+
- 📖 [CLI Reference](docs/cli_reference.md)
|
|
483
|
+
- 📋 [Diagnostic Rules Catalog](docs/rules.md)
|
|
484
|
+
- ⚙️ [Configuration Guide](docs/configuration.md)
|
|
485
|
+
- 🤖 [CI/CD & SARIF Integration](docs/ci_integration.md)
|
|
486
|
+
- 🤝 [Contributing Guidelines](docs/contributing.md)
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## 📄 License
|
|
491
|
+
|
|
492
|
+
Distributed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
qv/__init__.py,sha256=ytQrd73EtfTFFFPArsLrgOJNI9NNyUwp_B8_rlbd-Fg,100
|
|
2
|
+
qv/__main__.py,sha256=tG1eQDuox_m4xSLYLEC8KXwxKhL-IJno7CsCgdrw9Bs,112
|
|
3
|
+
qv/analyzers/__init__.py,sha256=_d-1_NpNFMDu1cT-SqTKRBBeipboWZlRt5zZ0tsVPk0,25
|
|
4
|
+
qv/analyzers/ci/__init__.py,sha256=W0Kr4IJBwjysEJPY-D3wNeDMWVFOFQzZt71LIzRDGfY,20
|
|
5
|
+
qv/analyzers/dependencies/__init__.py,sha256=AfzPubOgDrmbfPVe8I5egJ6tswolYzWsDwXjW-Maqhg,129
|
|
6
|
+
qv/analyzers/dependencies/analyzer.py,sha256=ENQPbqhzb7YxNqqz9rStoii2TMTLEEB1BXK9uc9ghAc,15022
|
|
7
|
+
qv/analyzers/docker/__init__.py,sha256=zQsRHV9IiRbjpb9AKmKzaMPtlXp1iSQCBSvyKDo-2q8,24
|
|
8
|
+
qv/analyzers/environment/__init__.py,sha256=5EtSq3sQIJJxKAfpwWiCul3VHWMA94y3-QBi5pjHVb8,128
|
|
9
|
+
qv/analyzers/environment/drift.py,sha256=ZPWCsGMr-vLjIoOnt97fCSgQfwsL_C8z36OfxGiyw-g,10267
|
|
10
|
+
qv/analyzers/imports/__init__.py,sha256=bShRWSwJunl5mbkHI738-jTut0RGStVQhtdzCae-fuo,112
|
|
11
|
+
qv/analyzers/imports/analyzer.py,sha256=a6pOHZ35SK6fpZwiSg0oup60yObPk-Tk80CP_KRA_tA,14580
|
|
12
|
+
qv/analyzers/packaging/__init__.py,sha256=owgIeiZUNwhn-gPLbho1jCqA72cg1f8u_6wsqDqsgdk,123
|
|
13
|
+
qv/analyzers/packaging/analyzer.py,sha256=-57OYQPsS6uIN8u_1LR2CQRT8sOcDmYYCtGkgFo4jo0,4776
|
|
14
|
+
qv/analyzers/security/__init__.py,sha256=VOpkxOxoC_7HpyuSKJi35y88ErKMtQ9w31HXDkaxhi4,384
|
|
15
|
+
qv/analyzers/security/analyzer.py,sha256=DQAm3kex4v63ta8mFeI0gs4OCDvQYPfECLv1vzaujIQ,4629
|
|
16
|
+
qv/analyzers/security/lockfile_parser.py,sha256=XkrJ-bwkv82wIojFsyzkqe4flCJWIliS1DEJNj9E59M,6565
|
|
17
|
+
qv/analyzers/security/osv_client.py,sha256=Y9s8VeY3dSpUd-VBqOvdSSB8KgBzeti1K8niJY2G8IM,4961
|
|
18
|
+
qv/cli/__init__.py,sha256=EUnPKm0L_oVbU1jAO_Icle4TXMCVGpd_aFTQ4iaueik,88
|
|
19
|
+
qv/cli/main.py,sha256=aiDn3sjPVijPrNrW5YqyFBsO2KEL5h-V4rXhnNzzNP0,18806
|
|
20
|
+
qv/core/analyzer.py,sha256=FPiEjghPbi-RVCOxODE_BRlkvtMsnrgbakhq7-YORuc,503
|
|
21
|
+
qv/core/config.py,sha256=0ARPWbLCV_RJhJZOVomldnuztxeLhkQPPkoiOrTO9Jw,3792
|
|
22
|
+
qv/core/context.py,sha256=kAI_QENd6c9kRiIE5Ejt2Um4MZsQ3TL9ubkl3w5AJF0,2575
|
|
23
|
+
qv/core/engine.py,sha256=foFddOmcSFuLWqmqYdcXWC-6vh_27IsgUF2l7XJrBJ4,4496
|
|
24
|
+
qv/core/models.py,sha256=aQQTmyaBJsdof7oh8Onu2C2DLqQA6YbpLxQ0zB5utXw,4096
|
|
25
|
+
qv/core/project.py,sha256=pHpDbjhYs_8iZiIluXUcj_zwTZPwbKHyXDIWfTxgCFM,18218
|
|
26
|
+
qv/frameworks/__init__.py,sha256=5XnOOPd2Kyb9uG3Z7MjqZO1emDZs5F1sCAmI4yjWMPI,25
|
|
27
|
+
qv/integrations/__init__.py,sha256=xBWG_BBOAn4l43MnjXmhbu4qjiMdYq32pVjmDO0Q8iM,37
|
|
28
|
+
qv/remediation/__init__.py,sha256=oHIxQraTgQVQUZ_kIorPR4t-YDVe7ygpn_dqXq_dmIU,252
|
|
29
|
+
qv/remediation/engine.py,sha256=QkmrHqAyV7Zf1Z0OtWvxySJlzjUxKAmz63bJPiWHlsE,12741
|
|
30
|
+
qv/remediation/models.py,sha256=y8zWfbbhjWCjiBks1cpcvLODNavvnj2sR_uqR1TbjYw,1600
|
|
31
|
+
qv/reporters/__init__.py,sha256=c5HE52PVODdUwtCb_06bugj4aZ9gHqqJ0QaOCt1R1es,381
|
|
32
|
+
qv/reporters/base.py,sha256=LvtGAy5xocwB8gqYuAvFEIO73ov7BwXa06Azj_Pnuu4,329
|
|
33
|
+
qv/reporters/github_annotator.py,sha256=o8Msz9hIhXYtIrqdhCk9aq64sgZjZyz6Yr1cda7ruCI,3480
|
|
34
|
+
qv/reporters/html_reporter.py,sha256=WZHYu_ND1GVNUHjgUK4ts97KPp1DqbkoYp3_-eOgnoU,18142
|
|
35
|
+
qv/reporters/json_reporter.py,sha256=RRzEZAMpOw8HGEVbYV8n_PCJ0PrIvFlOgAGptUxvtvY,446
|
|
36
|
+
qv/reporters/sarif.py,sha256=CZEtjmqB7sNCc4qZJBLtm_7cT_gYa8frI1jl9JHzuyU,3062
|
|
37
|
+
qv/reporters/terminal.py,sha256=H5c5-HMKSZ2PIHx-ZE4XA5bhMH826JHzBav0EKFqU44,4549
|
|
38
|
+
qv/rules/registry.py,sha256=sz-S59_AAE0dzTe4wb77q2xymwQra0mXmEDrXf-FuMo,8596
|
|
39
|
+
qv/tui/__init__.py,sha256=otEuHr1bZ6rRMF2WkgPmv6z2Ws1cmnvFqr9Semhe_6A,100
|
|
40
|
+
qv/tui/app.py,sha256=Db08g5SMYsLZZ4ncrh_9ElIw_TyiRoh5gDiGkoF6mPI,11841
|
|
41
|
+
qv/visualizers/__init__.py,sha256=0kMeoXfdUJalMeZms5EwrFEYMpXmx30dJxITr9ahLCU,132
|
|
42
|
+
qv/visualizers/tree.py,sha256=Jf7w4HZzvxvmCIiGea1PzaUoCKN2ByAVdM8vXLHId-c,8043
|
|
43
|
+
python_qv-0.1.2.dist-info/METADATA,sha256=P7c1fy7JlOGs60A338evg0PvW6EkUwBxhJAcwW_cuPU,16415
|
|
44
|
+
python_qv-0.1.2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
45
|
+
python_qv-0.1.2.dist-info/entry_points.txt,sha256=y0aZqfDPndkJ2yXxBM2RLL1qriH72zWEKSj4nJtc2q4,94
|
|
46
|
+
python_qv-0.1.2.dist-info/RECORD,,
|
qv/__init__.py
CHANGED
qv/__main__.py
ADDED
|
@@ -12,11 +12,70 @@ from qv.core.context import ProjectContext
|
|
|
12
12
|
from qv.core.models import Diagnostic, Evidence, Suggestion
|
|
13
13
|
from qv.rules.registry import get_rule_definition
|
|
14
14
|
|
|
15
|
+
PEP594_REMOVED_MODULES = {
|
|
16
|
+
"distutils",
|
|
17
|
+
"imp",
|
|
18
|
+
"cgi",
|
|
19
|
+
"pipes",
|
|
20
|
+
"asyncore",
|
|
21
|
+
"asynchat",
|
|
22
|
+
"smtpd",
|
|
23
|
+
"crypt",
|
|
24
|
+
"chunk",
|
|
25
|
+
"telnetlib",
|
|
26
|
+
"mailcap",
|
|
27
|
+
"nntplib",
|
|
28
|
+
"audioop",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ALL_PYTHON_3_STDLIB = {
|
|
32
|
+
"tomllib", # Added in Python 3.11
|
|
33
|
+
"zoneinfo", # Added in Python 3.9
|
|
34
|
+
"wsgiref",
|
|
35
|
+
"concurrent",
|
|
36
|
+
"ctypes",
|
|
37
|
+
"curses",
|
|
38
|
+
"email",
|
|
39
|
+
"html",
|
|
40
|
+
"mimetypes",
|
|
41
|
+
"tarfile",
|
|
42
|
+
"zipfile",
|
|
43
|
+
"gzip",
|
|
44
|
+
"bz2",
|
|
45
|
+
"lzma",
|
|
46
|
+
"venv",
|
|
47
|
+
"ensurepip",
|
|
48
|
+
"trace",
|
|
49
|
+
"tracemalloc",
|
|
50
|
+
"secrets",
|
|
51
|
+
"token",
|
|
52
|
+
"tokenize",
|
|
53
|
+
"profile",
|
|
54
|
+
"cProfile",
|
|
55
|
+
"pstats",
|
|
56
|
+
"timeit",
|
|
57
|
+
"gc",
|
|
58
|
+
"signal",
|
|
59
|
+
"errno",
|
|
60
|
+
"selectors",
|
|
61
|
+
"sched",
|
|
62
|
+
"queue",
|
|
63
|
+
"site",
|
|
64
|
+
"stat",
|
|
65
|
+
"reprlib",
|
|
66
|
+
"copy",
|
|
67
|
+
"copyreg",
|
|
68
|
+
"fnmatch",
|
|
69
|
+
"linecache",
|
|
70
|
+
"posixpath",
|
|
71
|
+
"ntpath",
|
|
72
|
+
"genericpath",
|
|
73
|
+
}
|
|
74
|
+
|
|
15
75
|
# Known Python standard library modules (Python 3.10+)
|
|
16
76
|
STDLIB_MODULES = (
|
|
17
|
-
set(sys.stdlib_module_names)
|
|
18
|
-
|
|
19
|
-
else {
|
|
77
|
+
(set(sys.stdlib_module_names) if hasattr(sys, "stdlib_module_names") else set())
|
|
78
|
+
| {
|
|
20
79
|
"abc",
|
|
21
80
|
"argparse",
|
|
22
81
|
"ast",
|
|
@@ -70,6 +129,8 @@ STDLIB_MODULES = (
|
|
|
70
129
|
"zipfile",
|
|
71
130
|
"__future__",
|
|
72
131
|
}
|
|
132
|
+
| ALL_PYTHON_3_STDLIB
|
|
133
|
+
| PEP594_REMOVED_MODULES
|
|
73
134
|
)
|
|
74
135
|
|
|
75
136
|
# Known tools/packages that may not be imported directly in application code
|
|
@@ -185,7 +246,7 @@ class DependencyAnalyzer:
|
|
|
185
246
|
context.project_name.lower(),
|
|
186
247
|
}
|
|
187
248
|
for sf in context.source_files:
|
|
188
|
-
parts = sf.relative_path.parts
|
|
249
|
+
parts = list(sf.relative_path.parts)
|
|
189
250
|
if parts:
|
|
190
251
|
if parts[0] in ("src", "lib") and len(parts) > 1:
|
|
191
252
|
local_modules.add(parts[1].replace(".py", "").lower().replace("-", "_"))
|