pyossmtool 0.0.2__tar.gz → 0.0.4__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.
- pyossmtool-0.0.4/PKG-INFO +260 -0
- pyossmtool-0.0.4/README.md +230 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/pyproject.toml +7 -3
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/__init__.py +1 -1
- pyossmtool-0.0.4/src/pyossmtool/bundle/defaults/configs/ruff.toml +11 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/cli.py +24 -6
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/installer.py +6 -10
- pyossmtool-0.0.4/src/pyossmtool.egg-info/PKG-INFO +260 -0
- pyossmtool-0.0.4/tests/test_cli.py +49 -0
- pyossmtool-0.0.2/PKG-INFO +0 -351
- pyossmtool-0.0.2/README.md +0 -322
- pyossmtool-0.0.2/src/pyossmtool/bundle/defaults/configs/ruff.toml +0 -8
- pyossmtool-0.0.2/src/pyossmtool.egg-info/PKG-INFO +0 -351
- pyossmtool-0.0.2/tests/test_cli.py +0 -17
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/LICENSE +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/setup.cfg +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/bandit.scan.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/codespell.spelling.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/deadcode.scan.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/gate.acronym-allowlist.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/gate.folder-breadth.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/gate.module-private-vars.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/gate.module-size.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/gitleaks.secrets.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/hadolint.dockerfile.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/jscpd.duplication.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/markdownlint.check.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/mdformat.apply.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/mdformat.format.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/mutmut.run.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/pydeps.cycles.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/pytest.coverage.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/pytest.test.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/radon.cc.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/radon.mi.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/ruff.format.apply.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/ruff.format.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/ruff.lint.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/ruff.unused.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/semgrep.scan.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/shellcheck.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/shfmt.apply.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/shfmt.format.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/sourcery.review.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/ty.check.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/vulture.scan.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/yamlfmt.apply.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/yamlfmt.format.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/checks/yamllint.check.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/bandit.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/codespell.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/deadcode.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/gitleaks.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/hadolint.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/jscpd.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/markdownlint.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/mdformat.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/mutmut.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/pydeps.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/pytest.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/radon.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/ruff.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/script.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/semgrep.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/shellcheck.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/shfmt.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/sourcery.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/ty.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/vulture.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/yamlfmt.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/catalog/tools/yamllint.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/allowlists/acronyms.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/allowlists/folder-breadth.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/allowlists/module-private-vars.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/allowlists/module-size.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/bandit.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/gates/acronym-allowlist.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/gates/folder-breadth.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/gates/module-private-vars.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/gates/module-size.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/markdownlint.json +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/ty.toml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/yamlfmt.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/configs/yamllint.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/gates/README.md +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/gates/acronym-allowlist.sh +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/gates/folder-breadth.sh +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/gates/lib.sh +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/gates/module-private-vars.sh +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/gates/module-size.sh +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/defaults/semgrep/python-quality.yml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/all.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/demo.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/extended.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/format.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/formatting.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/policy.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/python-quality.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/reslab-parity.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/bundle/suites/standard.yaml +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/config_resolver.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/discovery.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/error_formatters.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/gate_config.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/gates.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/ignore.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/ignore_materialize.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/models.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/__init__.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/analysis.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/base.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/common.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/format_text.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/gates.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/patterns.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/prose.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/ruff.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/parsers/shell.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/policy/__init__.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/policy/acronym_allowlist.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/policy/folder_breadth.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/registry.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/reporter.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/resolver.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/runner.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/runner_script.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool/target_expand.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool.egg-info/SOURCES.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool.egg-info/dependency_links.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool.egg-info/entry_points.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool.egg-info/requires.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/src/pyossmtool.egg-info/top_level.txt +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_check_modes.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_config_and_targets.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_discovery.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_error_formatters.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_gate_config.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_ignore.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_npm_install.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_packaging.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_parser_registry.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_script_gates.py +0 -0
- {pyossmtool-0.0.2 → pyossmtool-0.0.4}/tests/test_wheel_install.py +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyossmtool
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: Quality-gate orchestrator for AI-assisted development
|
|
5
|
+
Author-email: Vishal Kumar Mishra <vishal.k.mishra2@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Changelog, https://github.com/inquilabee/pyossmtool/blob/main/CHANGELOG.md
|
|
8
|
+
Project-URL: Homepage, https://github.com/inquilabee/pyossmtool
|
|
9
|
+
Project-URL: Issues, https://github.com/inquilabee/pyossmtool/issues
|
|
10
|
+
Project-URL: Repository, https://github.com/inquilabee/pyossmtool
|
|
11
|
+
Keywords: quality,lint,format,pre-commit,python,static-analysis
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
22
|
+
Requires-Python: <3.15,>=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: pydantic>=2.0
|
|
26
|
+
Requires-Dist: pathspec>=0.12
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: typer>=0.12
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# pyossmtool
|
|
32
|
+
|
|
33
|
+
Quality gates for Python repos that developers can run locally, in CI, or hand to an AI agent without inventing a new workflow each time.
|
|
34
|
+
|
|
35
|
+
`pyossmtool` gives you one project config, a bundled catalog of checks, quiet success, and structured failure reports. It is inspired by Trunk and pre-commit, but keeps the surface area small:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pyossmtool install
|
|
39
|
+
pyossmtool format
|
|
40
|
+
pyossmtool check
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install pyossmtool
|
|
47
|
+
# or
|
|
48
|
+
uv add --dev pyossmtool
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Requires Python 3.11–3.14.
|
|
52
|
+
|
|
53
|
+
## 60-Second Setup
|
|
54
|
+
|
|
55
|
+
Create `pyossmtool.yaml`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
suite: all
|
|
59
|
+
env: auto
|
|
60
|
+
target: .
|
|
61
|
+
error-format: compact
|
|
62
|
+
configs:
|
|
63
|
+
mode: auto
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Then run:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pyossmtool install # install tools needed by suite:
|
|
70
|
+
pyossmtool format # apply formatter/autofix checks
|
|
71
|
+
pyossmtool check # report-only quality checks
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The `suite:` value is the project default. You do not need to repeat `--suite all`; the CLI reads it from `pyossmtool.yaml`.
|
|
75
|
+
|
|
76
|
+
Success is silent and exits `0`. Failures exit `1`, write a JSON report under `reports/failures/`, and print the same report through your configured `error-format`.
|
|
77
|
+
|
|
78
|
+
## Mental Model
|
|
79
|
+
|
|
80
|
+
- **Suite**: a named checklist, such as `python-quality`, `standard`, or `all`.
|
|
81
|
+
- **Check**: one rule runner inside a suite, such as `ruff.lint` or `ty.check`.
|
|
82
|
+
- **`check`**: report-only. It should not rewrite your files.
|
|
83
|
+
- **`format`**: applies write/fix checks, such as formatters.
|
|
84
|
+
- **`install`**: installs the tools needed by the selected suite.
|
|
85
|
+
|
|
86
|
+
Most teams pick a suite once in `pyossmtool.yaml` and run the same three commands everywhere.
|
|
87
|
+
|
|
88
|
+
## Config That Matters
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
suite: python-quality # default checklist for install/check/format
|
|
92
|
+
env: auto # auto | managed | project
|
|
93
|
+
target: . # scan root; defaults to .
|
|
94
|
+
error-format: compact # json | log | text | compact | github
|
|
95
|
+
|
|
96
|
+
configs:
|
|
97
|
+
mode: auto # repo configs first, bundled fallback
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use `--suite` only when you want a one-off override:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
pyossmtool check --suite extended
|
|
104
|
+
pyossmtool install --suite standard
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Copy [`pyossmtool.yaml.example`](pyossmtool.yaml.example) for an annotated config with ignores, custom checks, gates, and custom error formatters.
|
|
108
|
+
|
|
109
|
+
## Suites
|
|
110
|
+
|
|
111
|
+
Suites are bundled starting points. They choose which checks run; tools still discover their own files and `.gitignore` is always respected.
|
|
112
|
+
|
|
113
|
+
| Suite | Use it for |
|
|
114
|
+
| --- | --- |
|
|
115
|
+
| `all` | Run every bundled check |
|
|
116
|
+
| `python-quality` | Core Python lint, format-check, and type checks |
|
|
117
|
+
| `formatting` | Report-only formatting drift |
|
|
118
|
+
| `format` | Formatter/autofix checks that write files |
|
|
119
|
+
| `standard` | Broader portable quality baseline |
|
|
120
|
+
| `extended` | Slower optional checks on top of standard |
|
|
121
|
+
| `policy` | Bundled script gates |
|
|
122
|
+
| `demo` | Internal/sample fixture suite |
|
|
123
|
+
|
|
124
|
+
Examples:
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
# Local Python repo
|
|
128
|
+
suite: python-quality
|
|
129
|
+
error-format: text
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
# CI baseline with GitHub annotations
|
|
134
|
+
suite: standard
|
|
135
|
+
error-format: github
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
# Max coverage while developing pyossmtool itself
|
|
140
|
+
suite: all
|
|
141
|
+
error-format: compact
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Error Output
|
|
145
|
+
|
|
146
|
+
On failure, pyossmtool always writes the canonical JSON report to disk:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
reports/failures/ruff.lint-20260716T163000Z/report.json
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`error-format` only controls what is printed to stderr.
|
|
153
|
+
|
|
154
|
+
| Format | Example output |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| `json` | Pretty JSON report, including `report_path` |
|
|
157
|
+
| `log` | `2026-07-16T12:00:00+00:00 [error] ruff.lint/E501 src/app.py:42: Line too long` |
|
|
158
|
+
| `text` | `- [error] E501: Line too long (src/app.py:42)` |
|
|
159
|
+
| `compact` | `src/app.py:42: error: E501 Line too long` |
|
|
160
|
+
| `github` | `::error file=src/app.py,title=ruff.lint/E501,line=42::Line too long` |
|
|
161
|
+
|
|
162
|
+
Default is `json` when `error-format` is omitted.
|
|
163
|
+
|
|
164
|
+
Custom formatters live in `pyossmtool.yaml`:
|
|
165
|
+
|
|
166
|
+
```yaml
|
|
167
|
+
suite: all
|
|
168
|
+
error-format: short
|
|
169
|
+
error-formatters:
|
|
170
|
+
short:
|
|
171
|
+
kind: finding_line
|
|
172
|
+
template: "{severity}\t{rule_id}\t{file}:{line}\t{message}"
|
|
173
|
+
jq-summary:
|
|
174
|
+
kind: jq
|
|
175
|
+
program: >
|
|
176
|
+
.findings[] | "[\(.severity)] \(.rule_id) \(.message)"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`finding_line` placeholders: `severity`, `rule_id`, `message`, `file`, `line`, `check_id`, `report_path`.
|
|
180
|
+
|
|
181
|
+
`jq` formatters require `jq` on `PATH`.
|
|
182
|
+
|
|
183
|
+
## Daily Commands
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pyossmtool install
|
|
187
|
+
pyossmtool format
|
|
188
|
+
pyossmtool check
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Inspect what is available:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
pyossmtool list suites
|
|
195
|
+
pyossmtool list tools
|
|
196
|
+
pyossmtool list checks
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Run a single check:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
pyossmtool check --check ruff.lint --target .
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Export the failure report schema:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
pyossmtool schema > failure-report.schema.json
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Project-Local Gates
|
|
212
|
+
|
|
213
|
+
Use gates when a repo needs a policy that is not covered by the bundled catalog.
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
pyossmtool gate init module-size --description "Cap module line counts"
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
That creates a shell gate under `.pyossmtool/gates/` and a catalog entry under `.pyossmtool/catalog/checks/`. Enable it from `pyossmtool.yaml`:
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
checks:
|
|
223
|
+
- id: gate.module-size
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## CI
|
|
227
|
+
|
|
228
|
+
Minimal GitHub Actions job:
|
|
229
|
+
|
|
230
|
+
```yaml
|
|
231
|
+
name: quality
|
|
232
|
+
|
|
233
|
+
on:
|
|
234
|
+
pull_request:
|
|
235
|
+
push:
|
|
236
|
+
branches: [main]
|
|
237
|
+
|
|
238
|
+
jobs:
|
|
239
|
+
pyossmtool:
|
|
240
|
+
runs-on: ubuntu-latest
|
|
241
|
+
steps:
|
|
242
|
+
- uses: actions/checkout@v4
|
|
243
|
+
- uses: astral-sh/setup-uv@v6
|
|
244
|
+
- run: uvx pyossmtool install
|
|
245
|
+
- run: uvx pyossmtool check
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
For GitHub PR annotations, set:
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
error-format: github
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Contributing
|
|
255
|
+
|
|
256
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, adding tools/checks, and release notes.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# pyossmtool
|
|
2
|
+
|
|
3
|
+
Quality gates for Python repos that developers can run locally, in CI, or hand to an AI agent without inventing a new workflow each time.
|
|
4
|
+
|
|
5
|
+
`pyossmtool` gives you one project config, a bundled catalog of checks, quiet success, and structured failure reports. It is inspired by Trunk and pre-commit, but keeps the surface area small:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pyossmtool install
|
|
9
|
+
pyossmtool format
|
|
10
|
+
pyossmtool check
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install pyossmtool
|
|
17
|
+
# or
|
|
18
|
+
uv add --dev pyossmtool
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Requires Python 3.11–3.14.
|
|
22
|
+
|
|
23
|
+
## 60-Second Setup
|
|
24
|
+
|
|
25
|
+
Create `pyossmtool.yaml`:
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
suite: all
|
|
29
|
+
env: auto
|
|
30
|
+
target: .
|
|
31
|
+
error-format: compact
|
|
32
|
+
configs:
|
|
33
|
+
mode: auto
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then run:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pyossmtool install # install tools needed by suite:
|
|
40
|
+
pyossmtool format # apply formatter/autofix checks
|
|
41
|
+
pyossmtool check # report-only quality checks
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The `suite:` value is the project default. You do not need to repeat `--suite all`; the CLI reads it from `pyossmtool.yaml`.
|
|
45
|
+
|
|
46
|
+
Success is silent and exits `0`. Failures exit `1`, write a JSON report under `reports/failures/`, and print the same report through your configured `error-format`.
|
|
47
|
+
|
|
48
|
+
## Mental Model
|
|
49
|
+
|
|
50
|
+
- **Suite**: a named checklist, such as `python-quality`, `standard`, or `all`.
|
|
51
|
+
- **Check**: one rule runner inside a suite, such as `ruff.lint` or `ty.check`.
|
|
52
|
+
- **`check`**: report-only. It should not rewrite your files.
|
|
53
|
+
- **`format`**: applies write/fix checks, such as formatters.
|
|
54
|
+
- **`install`**: installs the tools needed by the selected suite.
|
|
55
|
+
|
|
56
|
+
Most teams pick a suite once in `pyossmtool.yaml` and run the same three commands everywhere.
|
|
57
|
+
|
|
58
|
+
## Config That Matters
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
suite: python-quality # default checklist for install/check/format
|
|
62
|
+
env: auto # auto | managed | project
|
|
63
|
+
target: . # scan root; defaults to .
|
|
64
|
+
error-format: compact # json | log | text | compact | github
|
|
65
|
+
|
|
66
|
+
configs:
|
|
67
|
+
mode: auto # repo configs first, bundled fallback
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Use `--suite` only when you want a one-off override:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pyossmtool check --suite extended
|
|
74
|
+
pyossmtool install --suite standard
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Copy [`pyossmtool.yaml.example`](pyossmtool.yaml.example) for an annotated config with ignores, custom checks, gates, and custom error formatters.
|
|
78
|
+
|
|
79
|
+
## Suites
|
|
80
|
+
|
|
81
|
+
Suites are bundled starting points. They choose which checks run; tools still discover their own files and `.gitignore` is always respected.
|
|
82
|
+
|
|
83
|
+
| Suite | Use it for |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `all` | Run every bundled check |
|
|
86
|
+
| `python-quality` | Core Python lint, format-check, and type checks |
|
|
87
|
+
| `formatting` | Report-only formatting drift |
|
|
88
|
+
| `format` | Formatter/autofix checks that write files |
|
|
89
|
+
| `standard` | Broader portable quality baseline |
|
|
90
|
+
| `extended` | Slower optional checks on top of standard |
|
|
91
|
+
| `policy` | Bundled script gates |
|
|
92
|
+
| `demo` | Internal/sample fixture suite |
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
# Local Python repo
|
|
98
|
+
suite: python-quality
|
|
99
|
+
error-format: text
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
# CI baseline with GitHub annotations
|
|
104
|
+
suite: standard
|
|
105
|
+
error-format: github
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
# Max coverage while developing pyossmtool itself
|
|
110
|
+
suite: all
|
|
111
|
+
error-format: compact
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Error Output
|
|
115
|
+
|
|
116
|
+
On failure, pyossmtool always writes the canonical JSON report to disk:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
reports/failures/ruff.lint-20260716T163000Z/report.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`error-format` only controls what is printed to stderr.
|
|
123
|
+
|
|
124
|
+
| Format | Example output |
|
|
125
|
+
| --- | --- |
|
|
126
|
+
| `json` | Pretty JSON report, including `report_path` |
|
|
127
|
+
| `log` | `2026-07-16T12:00:00+00:00 [error] ruff.lint/E501 src/app.py:42: Line too long` |
|
|
128
|
+
| `text` | `- [error] E501: Line too long (src/app.py:42)` |
|
|
129
|
+
| `compact` | `src/app.py:42: error: E501 Line too long` |
|
|
130
|
+
| `github` | `::error file=src/app.py,title=ruff.lint/E501,line=42::Line too long` |
|
|
131
|
+
|
|
132
|
+
Default is `json` when `error-format` is omitted.
|
|
133
|
+
|
|
134
|
+
Custom formatters live in `pyossmtool.yaml`:
|
|
135
|
+
|
|
136
|
+
```yaml
|
|
137
|
+
suite: all
|
|
138
|
+
error-format: short
|
|
139
|
+
error-formatters:
|
|
140
|
+
short:
|
|
141
|
+
kind: finding_line
|
|
142
|
+
template: "{severity}\t{rule_id}\t{file}:{line}\t{message}"
|
|
143
|
+
jq-summary:
|
|
144
|
+
kind: jq
|
|
145
|
+
program: >
|
|
146
|
+
.findings[] | "[\(.severity)] \(.rule_id) \(.message)"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`finding_line` placeholders: `severity`, `rule_id`, `message`, `file`, `line`, `check_id`, `report_path`.
|
|
150
|
+
|
|
151
|
+
`jq` formatters require `jq` on `PATH`.
|
|
152
|
+
|
|
153
|
+
## Daily Commands
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
pyossmtool install
|
|
157
|
+
pyossmtool format
|
|
158
|
+
pyossmtool check
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Inspect what is available:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
pyossmtool list suites
|
|
165
|
+
pyossmtool list tools
|
|
166
|
+
pyossmtool list checks
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Run a single check:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
pyossmtool check --check ruff.lint --target .
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Export the failure report schema:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pyossmtool schema > failure-report.schema.json
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Project-Local Gates
|
|
182
|
+
|
|
183
|
+
Use gates when a repo needs a policy that is not covered by the bundled catalog.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pyossmtool gate init module-size --description "Cap module line counts"
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
That creates a shell gate under `.pyossmtool/gates/` and a catalog entry under `.pyossmtool/catalog/checks/`. Enable it from `pyossmtool.yaml`:
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
checks:
|
|
193
|
+
- id: gate.module-size
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## CI
|
|
197
|
+
|
|
198
|
+
Minimal GitHub Actions job:
|
|
199
|
+
|
|
200
|
+
```yaml
|
|
201
|
+
name: quality
|
|
202
|
+
|
|
203
|
+
on:
|
|
204
|
+
pull_request:
|
|
205
|
+
push:
|
|
206
|
+
branches: [main]
|
|
207
|
+
|
|
208
|
+
jobs:
|
|
209
|
+
pyossmtool:
|
|
210
|
+
runs-on: ubuntu-latest
|
|
211
|
+
steps:
|
|
212
|
+
- uses: actions/checkout@v4
|
|
213
|
+
- uses: astral-sh/setup-uv@v6
|
|
214
|
+
- run: uvx pyossmtool install
|
|
215
|
+
- run: uvx pyossmtool check
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
For GitHub PR annotations, set:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
error-format: github
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Contributing
|
|
225
|
+
|
|
226
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local development, adding tools/checks, and release notes.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pyossmtool"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.4"
|
|
4
4
|
description = "Quality-gate orchestrator for AI-assisted development"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.11,<3.
|
|
6
|
+
requires-python = ">=3.11,<3.15"
|
|
7
7
|
license = "MIT"
|
|
8
8
|
license-files = ["LICENSE"]
|
|
9
9
|
authors = [
|
|
@@ -26,6 +26,7 @@ classifiers = [
|
|
|
26
26
|
"Programming Language :: Python :: 3.11",
|
|
27
27
|
"Programming Language :: Python :: 3.12",
|
|
28
28
|
"Programming Language :: Python :: 3.13",
|
|
29
|
+
"Programming Language :: Python :: 3.14",
|
|
29
30
|
"Topic :: Software Development :: Quality Assurance",
|
|
30
31
|
]
|
|
31
32
|
dependencies = [
|
|
@@ -83,7 +84,10 @@ line-length = 120
|
|
|
83
84
|
target-version = "py311"
|
|
84
85
|
|
|
85
86
|
[tool.ruff.lint]
|
|
86
|
-
select = ["B", "E", "F", "I", "UP"]
|
|
87
|
+
select = ["B", "C90", "E", "F", "I", "UP"]
|
|
88
|
+
|
|
89
|
+
[tool.ruff.lint.mccabe]
|
|
90
|
+
max-complexity = 10
|
|
87
91
|
|
|
88
92
|
[tool.ty]
|
|
89
93
|
# Repo-owned config; wins over bundled defaults when configs.mode=auto
|
|
@@ -89,12 +89,22 @@ def export_schema() -> None:
|
|
|
89
89
|
|
|
90
90
|
@app.command("install")
|
|
91
91
|
def install(
|
|
92
|
-
suite: str = typer.Option(
|
|
92
|
+
suite: str | None = typer.Option(
|
|
93
|
+
None,
|
|
94
|
+
"--suite",
|
|
95
|
+
help="Suite whose tools to install (default: suite from pyossmtool.yaml)",
|
|
96
|
+
),
|
|
93
97
|
) -> None:
|
|
94
98
|
registry = _registry()
|
|
95
|
-
|
|
99
|
+
project_root = _project_root()
|
|
100
|
+
project_config = registry.load_project_config(project_root)
|
|
101
|
+
suite_id = suite or (project_config.suite if project_config else None)
|
|
102
|
+
if not suite_id:
|
|
103
|
+
typer.echo("SETUP provide --suite or set suite: in pyossmtool.yaml", err=True)
|
|
104
|
+
raise typer.Exit(code=2)
|
|
105
|
+
installer = Installer(registry, project_root)
|
|
96
106
|
try:
|
|
97
|
-
installer.install_suite(
|
|
107
|
+
installer.install_suite(suite_id)
|
|
98
108
|
except Exception as exc:
|
|
99
109
|
typer.echo(f"SETUP {exc}", err=True)
|
|
100
110
|
raise typer.Exit(code=2) from exc
|
|
@@ -102,7 +112,11 @@ def install(
|
|
|
102
112
|
|
|
103
113
|
@app.command("check")
|
|
104
114
|
def check_cmd(
|
|
105
|
-
suite: str | None = typer.Option(
|
|
115
|
+
suite: str | None = typer.Option(
|
|
116
|
+
None,
|
|
117
|
+
"--suite",
|
|
118
|
+
help="Suite id to run (default: suite from pyossmtool.yaml)",
|
|
119
|
+
),
|
|
106
120
|
check: str | None = typer.Option(None, "--check", help="Single check id to run"),
|
|
107
121
|
target: str | None = typer.Option(None, "--target", help="Target path for single check"),
|
|
108
122
|
fail_fast: bool = typer.Option(False, "--fail-fast", help="Stop at first failure"),
|
|
@@ -114,7 +128,11 @@ def check_cmd(
|
|
|
114
128
|
|
|
115
129
|
@app.command("format")
|
|
116
130
|
def format_cmd(
|
|
117
|
-
suite: str | None = typer.Option(
|
|
131
|
+
suite: str | None = typer.Option(
|
|
132
|
+
None,
|
|
133
|
+
"--suite",
|
|
134
|
+
help="Suite id to run (default: suite from pyossmtool.yaml)",
|
|
135
|
+
),
|
|
118
136
|
check: str | None = typer.Option(None, "--check", help="Single check id to run"),
|
|
119
137
|
target: str | None = typer.Option(None, "--target", help="Target path for single check"),
|
|
120
138
|
fail_fast: bool = typer.Option(False, "--fail-fast", help="Stop at first failure"),
|
|
@@ -147,7 +165,7 @@ def _execute(
|
|
|
147
165
|
|
|
148
166
|
suite_id = suite or (project_config.suite if project_config else None)
|
|
149
167
|
if not suite_id:
|
|
150
|
-
typer.echo("SETUP provide --suite or
|
|
168
|
+
typer.echo("SETUP provide --suite or set suite: in pyossmtool.yaml", err=True)
|
|
151
169
|
return 2
|
|
152
170
|
|
|
153
171
|
try:
|
|
@@ -76,6 +76,12 @@ class Installer:
|
|
|
76
76
|
return False
|
|
77
77
|
|
|
78
78
|
def _managed_python(self) -> str:
|
|
79
|
+
"""Prefer a stable CPython for managed tool installs.
|
|
80
|
+
|
|
81
|
+
pyossmtool itself supports 3.11–3.14. Some third-party CLI tools still lag
|
|
82
|
+
on brand-new interpreters, so when the host is 3.14+, prefer 3.13/3.12/3.11
|
|
83
|
+
if available for the managed venv.
|
|
84
|
+
"""
|
|
79
85
|
if sys.version_info < (3, 14):
|
|
80
86
|
return sys.executable
|
|
81
87
|
for candidate in ("python3.13", "python3.12", "python3.11"):
|
|
@@ -85,16 +91,6 @@ class Installer:
|
|
|
85
91
|
return sys.executable
|
|
86
92
|
|
|
87
93
|
def _ensure_managed_venv(self) -> None:
|
|
88
|
-
python_bin = self.managed_venv / "bin" / "python"
|
|
89
|
-
if python_bin.exists():
|
|
90
|
-
version = subprocess.run(
|
|
91
|
-
[str(python_bin), "--version"],
|
|
92
|
-
capture_output=True,
|
|
93
|
-
text=True,
|
|
94
|
-
check=True,
|
|
95
|
-
).stdout
|
|
96
|
-
if "3.14" in version:
|
|
97
|
-
shutil.rmtree(self.managed_venv)
|
|
98
94
|
if (self.managed_venv / "bin" / "python").exists():
|
|
99
95
|
return
|
|
100
96
|
self.managed_venv.parent.mkdir(parents=True, exist_ok=True)
|