py-code-quality-guard 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.
Files changed (35) hide show
  1. py_code_quality_guard-0.1.0/.github/workflows/ci.yml +20 -0
  2. py_code_quality_guard-0.1.0/.github/workflows/publish.yml +34 -0
  3. py_code_quality_guard-0.1.0/.gitignore +9 -0
  4. py_code_quality_guard-0.1.0/.pre-commit-config.yaml +21 -0
  5. py_code_quality_guard-0.1.0/.pre-commit-hooks.yaml +19 -0
  6. py_code_quality_guard-0.1.0/.python-version +1 -0
  7. py_code_quality_guard-0.1.0/PKG-INFO +145 -0
  8. py_code_quality_guard-0.1.0/README.md +133 -0
  9. py_code_quality_guard-0.1.0/pyproject.toml +41 -0
  10. py_code_quality_guard-0.1.0/src/code_quality_guard/__init__.py +3 -0
  11. py_code_quality_guard-0.1.0/src/code_quality_guard/__main__.py +4 -0
  12. py_code_quality_guard-0.1.0/src/code_quality_guard/application/__init__.py +1 -0
  13. py_code_quality_guard-0.1.0/src/code_quality_guard/application/service.py +9 -0
  14. py_code_quality_guard-0.1.0/src/code_quality_guard/cli.py +58 -0
  15. py_code_quality_guard-0.1.0/src/code_quality_guard/config/__init__.py +3 -0
  16. py_code_quality_guard-0.1.0/src/code_quality_guard/config/settings.py +15 -0
  17. py_code_quality_guard-0.1.0/src/code_quality_guard/reporter/__init__.py +1 -0
  18. py_code_quality_guard-0.1.0/src/code_quality_guard/reporter/console.py +20 -0
  19. py_code_quality_guard-0.1.0/src/code_quality_guard/reporter/json.py +15 -0
  20. py_code_quality_guard-0.1.0/src/code_quality_guard/runner.py +40 -0
  21. py_code_quality_guard-0.1.0/src/code_quality_guard/tools/__init__.py +1 -0
  22. py_code_quality_guard-0.1.0/src/code_quality_guard/tools/base.py +13 -0
  23. py_code_quality_guard-0.1.0/src/code_quality_guard/tools/command.py +12 -0
  24. py_code_quality_guard-0.1.0/src/code_quality_guard/tools/mypy.py +18 -0
  25. py_code_quality_guard-0.1.0/src/code_quality_guard/tools/registry.py +20 -0
  26. py_code_quality_guard-0.1.0/src/code_quality_guard/tools/ruff.py +20 -0
  27. py_code_quality_guard-0.1.0/src/code_quality_guard/tools/ruff_format.py +20 -0
  28. py_code_quality_guard-0.1.0/tests/__init__.py +0 -0
  29. py_code_quality_guard-0.1.0/tests/conftest.py +8 -0
  30. py_code_quality_guard-0.1.0/tests/test_checkers/__init__.py +0 -0
  31. py_code_quality_guard-0.1.0/tests/test_checkers/test_runner.py +10 -0
  32. py_code_quality_guard-0.1.0/tests/test_cli.py +33 -0
  33. py_code_quality_guard-0.1.0/tests/test_reporter.py +20 -0
  34. py_code_quality_guard-0.1.0/tests/test_tools.py +56 -0
  35. py_code_quality_guard-0.1.0/uv.lock +784 -0
@@ -0,0 +1,20 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ quality:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v6
13
+ with:
14
+ enable-cache: true
15
+ - name: Set up Python
16
+ run: uv python install 3.12
17
+ - name: Install dependencies
18
+ run: uv sync --dev
19
+ - name: Run Linting, Formatting Checks, and Type Checking, Testing
20
+ run: uv run pre-commit run --all-files
@@ -0,0 +1,34 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ pypi-publish:
9
+ name: Upload release to PyPI
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+ env:
14
+ PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
15
+ steps:
16
+ - name: Checkout code
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.x"
23
+
24
+ - name: Install build tools
25
+ run: python -m pip install --upgrade build
26
+
27
+ - name: Build package distributions
28
+ run: python -m build
29
+
30
+ - name: Publish package to PyPI
31
+ uses: pypa/gh-action-pypi-publish@release/v1
32
+ with:
33
+ user: __token__
34
+ password: ${{ env.PYPI_API_TOKEN }}
@@ -0,0 +1,9 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .coverage
7
+ htmlcov/
8
+ dist/
9
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-check
5
+ name: Ruff lint
6
+ entry: uv run ruff check
7
+ language: system
8
+ pass_filenames: false
9
+ always_run: true
10
+ - id: ruff-format
11
+ name: Ruff format
12
+ entry: uv run ruff format --check
13
+ language: system
14
+ pass_filenames: false
15
+ always_run: true
16
+ - id: pytest
17
+ name: Pytest
18
+ entry: uv run pytest
19
+ language: system
20
+ pass_filenames: false
21
+ always_run: true
@@ -0,0 +1,19 @@
1
+ - id: cqg
2
+ name: Code Quality Guard
3
+ entry: code-quality-guard
4
+ language: python
5
+ language_version: python3.12
6
+ pass_filenames: false
7
+ require_serial: true
8
+ types: [python]
9
+ description: |
10
+ Run Ruff linting, Ruff formatting checks, and Mypy over the project.
11
+ args:
12
+ - --tool
13
+ - ruff
14
+ - --tool
15
+ - ruff-format
16
+ - --tool
17
+ - mypy
18
+ - --check-only
19
+ - .
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,145 @@
1
+ Metadata-Version: 2.5
2
+ Name: py-code-quality-guard
3
+ Version: 0.1.0
4
+ Summary: Automated Code Quality Guard for Python
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: click>=8.1
7
+ Requires-Dist: mypy>=1.11
8
+ Requires-Dist: pydantic>=2.8
9
+ Requires-Dist: rich>=13.7
10
+ Requires-Dist: ruff>=0.6
11
+ Description-Content-Type: text/markdown
12
+
13
+ # Code Quality Guard
14
+
15
+ [![CI](https://github.com/karma369-labs/code-quality-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/karma369-labs/code-quality-guard/actions/workflows/ci.yml)
16
+ [![PyPI](https://img.shields.io/pypi/v/py-code-quality-guard)](https://pypi.org/project/py-code-quality-guard/)
17
+ [![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)
18
+ [![Ruff](https://img.shields.io/badge/linter-Ruff-D7FF64?logo=ruff&logoColor=black)](https://docs.astral.sh/ruff/)
19
+ [![Mypy](https://img.shields.io/badge/type%20checker-Mypy-4B8BBE?logo=mypy&logoColor=white)](https://mypy-lang.org/)
20
+
21
+ `py-code-quality-guard` is a CI-friendly command-line quality gate for Python
22
+ projects. It runs Ruff linting, Ruff formatting, and Mypy through one
23
+ consistent interface, with human-readable or JSON output for local workflows
24
+ and automation.
25
+
26
+ ## Features
27
+
28
+ - Run Ruff, Ruff format, and Mypy independently or together.
29
+ - Check a project directory or a single file.
30
+ - Apply Ruff fixes when supported.
31
+ - Forward additional arguments to each selected tool.
32
+ - Emit text for developers or JSON for scripts and CI integrations.
33
+ - Return a non-zero exit code when any selected check fails.
34
+
35
+ ## Requirements
36
+
37
+ - Python 3.12 or newer
38
+ - Ruff and Mypy are installed automatically with the package
39
+
40
+ ## Installation
41
+
42
+ Install the latest release from PyPI in an active virtual environment:
43
+
44
+ ```bash
45
+ python -m pip install py-code-quality-guard
46
+ ```
47
+
48
+ For local development with `uv`:
49
+
50
+ ```bash
51
+ uv sync --dev
52
+ ```
53
+
54
+ ## Pre-commit Hook
55
+
56
+ Add `py-code-quality-guard` to a project-level `.pre-commit-config.yaml`:
57
+
58
+ ```yaml
59
+ repos:
60
+ - repo: https://github.com/karma369-labs/code-quality-guard
61
+ rev: v0.1.0
62
+ hooks:
63
+ - id: cqg
64
+ ```
65
+
66
+ Install the hook and run it against the repository:
67
+
68
+ ```bash
69
+ python -m pip install pre-commit
70
+ pre-commit install
71
+ pre-commit run --all-files
72
+ ```
73
+
74
+ The `cqg` hook runs Ruff linting, Ruff formatting checks, and Mypy over the
75
+ project. To use a newer release, update `rev` to the corresponding package
76
+ release tag.
77
+
78
+ ## Usage
79
+
80
+ Run all checks against the current directory:
81
+
82
+ ```bash
83
+ pyqguard .
84
+ ```
85
+
86
+ Run selected tools:
87
+
88
+ ```bash
89
+ pyqguard --tool ruff --tool mypy src/
90
+ ```
91
+
92
+ Checks report the tools' diagnostics without changing files by default:
93
+
94
+ ```bash
95
+ pyqguard --tool ruff-format .
96
+ ```
97
+
98
+ Apply supported fixes:
99
+
100
+ ```bash
101
+ pyqguard --tool ruff --tool ruff-format --fix .
102
+ ```
103
+
104
+ Produce machine-readable output:
105
+
106
+ ```bash
107
+ pyqguard --output json .
108
+ ```
109
+
110
+ Pass an additional argument to a selected tool. Repeat `--tool-arg` as
111
+ needed:
112
+
113
+ ```bash
114
+ pyqguard \
115
+ --tool ruff \
116
+ --tool-arg ruff --select \
117
+ --tool-arg ruff E,F \
118
+ .
119
+ ```
120
+
121
+ Run `pyqguard --help` for the complete option reference.
122
+
123
+ ## Development
124
+
125
+ Install the development dependencies and run the test suite:
126
+
127
+ ```bash
128
+ uv sync --dev
129
+ uv run pytest
130
+ ```
131
+
132
+ The repository uses pre-commit for linting, formatting, type checking, and
133
+ other repository checks:
134
+
135
+ ```bash
136
+ uv run pre-commit install
137
+ uv run pre-commit run --all-files
138
+ ```
139
+
140
+ Pull requests targeting `main` run the same pre-commit checks in GitHub Actions.
141
+
142
+
143
+ ## License
144
+
145
+ License information has not yet been published for this repository.
@@ -0,0 +1,133 @@
1
+ # Code Quality Guard
2
+
3
+ [![CI](https://github.com/karma369-labs/code-quality-guard/actions/workflows/ci.yml/badge.svg)](https://github.com/karma369-labs/code-quality-guard/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/py-code-quality-guard)](https://pypi.org/project/py-code-quality-guard/)
5
+ [![Python](https://img.shields.io/badge/python-3.12%2B-3776AB?logo=python&logoColor=white)](https://www.python.org/)
6
+ [![Ruff](https://img.shields.io/badge/linter-Ruff-D7FF64?logo=ruff&logoColor=black)](https://docs.astral.sh/ruff/)
7
+ [![Mypy](https://img.shields.io/badge/type%20checker-Mypy-4B8BBE?logo=mypy&logoColor=white)](https://mypy-lang.org/)
8
+
9
+ `py-code-quality-guard` is a CI-friendly command-line quality gate for Python
10
+ projects. It runs Ruff linting, Ruff formatting, and Mypy through one
11
+ consistent interface, with human-readable or JSON output for local workflows
12
+ and automation.
13
+
14
+ ## Features
15
+
16
+ - Run Ruff, Ruff format, and Mypy independently or together.
17
+ - Check a project directory or a single file.
18
+ - Apply Ruff fixes when supported.
19
+ - Forward additional arguments to each selected tool.
20
+ - Emit text for developers or JSON for scripts and CI integrations.
21
+ - Return a non-zero exit code when any selected check fails.
22
+
23
+ ## Requirements
24
+
25
+ - Python 3.12 or newer
26
+ - Ruff and Mypy are installed automatically with the package
27
+
28
+ ## Installation
29
+
30
+ Install the latest release from PyPI in an active virtual environment:
31
+
32
+ ```bash
33
+ python -m pip install py-code-quality-guard
34
+ ```
35
+
36
+ For local development with `uv`:
37
+
38
+ ```bash
39
+ uv sync --dev
40
+ ```
41
+
42
+ ## Pre-commit Hook
43
+
44
+ Add `py-code-quality-guard` to a project-level `.pre-commit-config.yaml`:
45
+
46
+ ```yaml
47
+ repos:
48
+ - repo: https://github.com/karma369-labs/code-quality-guard
49
+ rev: v0.1.0
50
+ hooks:
51
+ - id: cqg
52
+ ```
53
+
54
+ Install the hook and run it against the repository:
55
+
56
+ ```bash
57
+ python -m pip install pre-commit
58
+ pre-commit install
59
+ pre-commit run --all-files
60
+ ```
61
+
62
+ The `cqg` hook runs Ruff linting, Ruff formatting checks, and Mypy over the
63
+ project. To use a newer release, update `rev` to the corresponding package
64
+ release tag.
65
+
66
+ ## Usage
67
+
68
+ Run all checks against the current directory:
69
+
70
+ ```bash
71
+ pyqguard .
72
+ ```
73
+
74
+ Run selected tools:
75
+
76
+ ```bash
77
+ pyqguard --tool ruff --tool mypy src/
78
+ ```
79
+
80
+ Checks report the tools' diagnostics without changing files by default:
81
+
82
+ ```bash
83
+ pyqguard --tool ruff-format .
84
+ ```
85
+
86
+ Apply supported fixes:
87
+
88
+ ```bash
89
+ pyqguard --tool ruff --tool ruff-format --fix .
90
+ ```
91
+
92
+ Produce machine-readable output:
93
+
94
+ ```bash
95
+ pyqguard --output json .
96
+ ```
97
+
98
+ Pass an additional argument to a selected tool. Repeat `--tool-arg` as
99
+ needed:
100
+
101
+ ```bash
102
+ pyqguard \
103
+ --tool ruff \
104
+ --tool-arg ruff --select \
105
+ --tool-arg ruff E,F \
106
+ .
107
+ ```
108
+
109
+ Run `pyqguard --help` for the complete option reference.
110
+
111
+ ## Development
112
+
113
+ Install the development dependencies and run the test suite:
114
+
115
+ ```bash
116
+ uv sync --dev
117
+ uv run pytest
118
+ ```
119
+
120
+ The repository uses pre-commit for linting, formatting, type checking, and
121
+ other repository checks:
122
+
123
+ ```bash
124
+ uv run pre-commit install
125
+ uv run pre-commit run --all-files
126
+ ```
127
+
128
+ Pull requests targeting `main` run the same pre-commit checks in GitHub Actions.
129
+
130
+
131
+ ## License
132
+
133
+ License information has not yet been published for this repository.
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "py-code-quality-guard"
7
+ version = "0.1.0"
8
+ description = "Automated Code Quality Guard for Python"
9
+ readme = "README.md"
10
+ requires-python = ">=3.12"
11
+ dependencies = [
12
+ "click>=8.1",
13
+ "mypy>=1.11",
14
+ "pydantic>=2.8",
15
+ "rich>=13.7",
16
+ "ruff>=0.6",
17
+ ]
18
+
19
+ [project.scripts]
20
+ pyqguard = "code_quality_guard.cli:main"
21
+
22
+ [dependency-groups]
23
+ dev = [
24
+ "pre-commit>=3.8",
25
+ "pytest>=8.3",
26
+ "pytest-cov>=5.0",
27
+ ]
28
+
29
+ [tool.hatch.build.targets.wheel]
30
+ packages = ["src/code_quality_guard"]
31
+
32
+ [tool.pytest.ini_options]
33
+ testpaths = ["tests"]
34
+ pythonpath = ["src"]
35
+
36
+ [tool.ruff]
37
+ line-length = 100
38
+ target-version = "py312"
39
+
40
+ [tool.ruff.lint]
41
+ select = ["E", "F", "I"]
@@ -0,0 +1,3 @@
1
+ """Automated quality gates for Python projects."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from code_quality_guard.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1 @@
1
+ """Application orchestration for quality checks."""
@@ -0,0 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from code_quality_guard.config.settings import Settings
4
+ from code_quality_guard.runner import CommandResult
5
+ from code_quality_guard.tools.registry import build_tools
6
+
7
+
8
+ def run_quality_checks(settings: Settings) -> dict[str, CommandResult]:
9
+ return {tool.name: tool.run(settings) for tool in build_tools(settings.tools)}
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import click
6
+
7
+ from code_quality_guard.application.service import run_quality_checks
8
+ from code_quality_guard.config.settings import Settings
9
+ from code_quality_guard.reporter.console import render_console
10
+ from code_quality_guard.reporter.json import render_json
11
+
12
+ TOOL_NAMES = ["ruff", "ruff-format", "mypy"]
13
+
14
+
15
+ @click.command()
16
+ @click.argument("target", type=click.Path(path_type=Path, exists=True), default=Path.cwd())
17
+ @click.option("--tool", "tools", multiple=True, type=click.Choice(TOOL_NAMES))
18
+ @click.option("--fix", is_flag=True, help="Apply supported fixes.")
19
+ @click.option("--check-only", is_flag=True, help="Check formatting without changing files.")
20
+ @click.option("--output", type=click.Choice(["text", "json"]), default="text", show_default=True)
21
+ @click.option(
22
+ "--tool-arg",
23
+ "tool_args",
24
+ nargs=2,
25
+ multiple=True,
26
+ metavar="TOOL ARG",
27
+ help="Pass an additional argument to a tool; repeat as needed.",
28
+ )
29
+ def main(
30
+ target: Path,
31
+ tools: tuple[str, ...],
32
+ fix: bool,
33
+ check_only: bool,
34
+ output: str,
35
+ tool_args: tuple[tuple[str, str], ...],
36
+ ) -> None:
37
+ """Run selected code quality tools against TARGET."""
38
+ selected_tools = tools or tuple(TOOL_NAMES)
39
+ args_by_tool: dict[str, tuple[str, ...]] = {}
40
+ for tool_name, argument in tool_args:
41
+ if tool_name not in selected_tools:
42
+ raise click.BadParameter(f"{tool_name!r} is not selected with --tool")
43
+ args_by_tool[tool_name] = (*args_by_tool.get(tool_name, ()), argument)
44
+ settings = Settings(
45
+ target=target.resolve(),
46
+ tools=selected_tools,
47
+ fix=fix,
48
+ check_only=check_only,
49
+ output=output,
50
+ tool_args=args_by_tool,
51
+ )
52
+ results = run_quality_checks(settings)
53
+ if settings.output == "json":
54
+ click.echo(render_json(results))
55
+ else:
56
+ render_console(results)
57
+ if not all(result.passed for result in results.values()):
58
+ raise click.exceptions.Exit(1)
@@ -0,0 +1,3 @@
1
+ from .settings import Settings
2
+
3
+ __all__ = ["Settings"]
@@ -0,0 +1,15 @@
1
+ from pathlib import Path
2
+ from typing import Literal
3
+
4
+ from pydantic import BaseModel, ConfigDict, Field
5
+
6
+
7
+ class Settings(BaseModel):
8
+ model_config = ConfigDict(arbitrary_types_allowed=True)
9
+
10
+ target: Path = Field(default_factory=Path.cwd)
11
+ tools: tuple[str, ...] = ("ruff", "mypy")
12
+ fix: bool = False
13
+ check_only: bool = False
14
+ output: Literal["text", "json"] = "text"
15
+ tool_args: dict[str, tuple[str, ...]] = Field(default_factory=dict)
@@ -0,0 +1 @@
1
+ """Human and machine-readable reporting."""
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from rich.console import Console
4
+
5
+ from code_quality_guard.runner import CommandResult
6
+
7
+
8
+ def render_console(results: dict[str, CommandResult]) -> None:
9
+ console = Console()
10
+ for name, result in results.items():
11
+ status = "PASS" if result.passed else "FAIL"
12
+ color = "green" if result.passed else "red"
13
+ console.print(
14
+ f"[{color}][{status}][/{color}] {name} ({result.duration_seconds:.2f}s)"
15
+ )
16
+ diagnostics = "\n".join(
17
+ output.strip() for output in (result.stdout, result.stderr) if output.strip()
18
+ )
19
+ if diagnostics:
20
+ console.print(diagnostics, markup=False)
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import asdict
5
+ from typing import Any
6
+
7
+ from code_quality_guard.runner import CommandResult
8
+
9
+
10
+ def render_json(results: dict[str, CommandResult]) -> str:
11
+ payload: dict[str, Any] = {
12
+ "checks": {name: asdict(result) for name, result in results.items()},
13
+ "passed": all(result.passed for result in results.values()),
14
+ }
15
+ return json.dumps(payload, indent=2, sort_keys=True)
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ import time
5
+ from dataclasses import dataclass
6
+ from pathlib import Path
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class CommandResult:
11
+ command: list[str]
12
+ passed: bool
13
+ exit_code: int
14
+ stdout: str
15
+ stderr: str
16
+ duration_seconds: float
17
+
18
+
19
+ class SubprocessRunner:
20
+ def run(self, command: list[str], cwd: Path | None = None) -> CommandResult:
21
+ started = time.perf_counter()
22
+ try:
23
+ completed = subprocess.run(
24
+ command, cwd=cwd, capture_output=True, text=True, check=False
25
+ )
26
+ stdout, stderr, exit_code = completed.stdout, completed.stderr, completed.returncode
27
+ except FileNotFoundError as error:
28
+ stdout, stderr, exit_code = (
29
+ "",
30
+ f"Command not found: {error.filename or command[0]}",
31
+ 127,
32
+ )
33
+ return CommandResult(
34
+ command=command,
35
+ passed=exit_code == 0,
36
+ exit_code=exit_code,
37
+ stdout=stdout,
38
+ stderr=stderr,
39
+ duration_seconds=time.perf_counter() - started,
40
+ )
@@ -0,0 +1 @@
1
+ """Adapters for external quality tools."""
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Protocol
4
+
5
+ from code_quality_guard.config.settings import Settings
6
+ from code_quality_guard.runner import CommandResult
7
+
8
+
9
+ class Tool(Protocol):
10
+ name: str
11
+
12
+ def run(self, settings: Settings) -> CommandResult:
13
+ """Run the tool for the configured target."""
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ import sys
5
+ from pathlib import Path
6
+
7
+
8
+ def resolve_executable(name: str) -> str:
9
+ environment_executable = Path(sys.executable).with_name(name)
10
+ if environment_executable.exists():
11
+ return str(environment_executable)
12
+ return shutil.which(name) or name
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ from code_quality_guard.config.settings import Settings
4
+ from code_quality_guard.runner import CommandResult, SubprocessRunner
5
+ from code_quality_guard.tools.command import resolve_executable
6
+
7
+
8
+ class MypyTool:
9
+ name = "mypy"
10
+
11
+ def __init__(self, runner: SubprocessRunner | None = None) -> None:
12
+ self.runner = runner or SubprocessRunner()
13
+
14
+ def run(self, settings: Settings) -> CommandResult:
15
+ command = [resolve_executable("mypy"), str(settings.target)]
16
+ command.extend(settings.tool_args.get(self.name, ()))
17
+ cwd = settings.target if settings.target.is_dir() else settings.target.parent
18
+ return self.runner.run(command, cwd=cwd)