qx-devtools 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
.Python
|
|
7
|
+
*.so
|
|
8
|
+
*.egg
|
|
9
|
+
*.egg-info/
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
sdist/
|
|
15
|
+
wheels/
|
|
16
|
+
*.egg-link
|
|
17
|
+
|
|
18
|
+
# Virtual environments
|
|
19
|
+
.venv/
|
|
20
|
+
venv/
|
|
21
|
+
env/
|
|
22
|
+
ENV/
|
|
23
|
+
|
|
24
|
+
# uv
|
|
25
|
+
.uv/
|
|
26
|
+
|
|
27
|
+
# Testing
|
|
28
|
+
.pytest_cache/
|
|
29
|
+
.coverage
|
|
30
|
+
htmlcov/
|
|
31
|
+
.tox/
|
|
32
|
+
|
|
33
|
+
# Type checking
|
|
34
|
+
.mypy_cache/
|
|
35
|
+
.ruff_cache/
|
|
36
|
+
|
|
37
|
+
# IDE
|
|
38
|
+
.idea/
|
|
39
|
+
.vscode/
|
|
40
|
+
*.swp
|
|
41
|
+
*.swo
|
|
42
|
+
|
|
43
|
+
# OS
|
|
44
|
+
.DS_Store
|
|
45
|
+
Thumbs.db
|
|
46
|
+
|
|
47
|
+
# Docker
|
|
48
|
+
*.env.local
|
|
49
|
+
|
|
50
|
+
# Dist artifacts
|
|
51
|
+
dist/
|
|
52
|
+
|
|
53
|
+
# VS Code extension build artifacts
|
|
54
|
+
extensions/vscode/node_modules/
|
|
55
|
+
extensions/vscode/dist/
|
|
56
|
+
extensions/vscode/*.vsix
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: qx-devtools
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Qx devtools: ruff/mypy/pre-commit presets, code quality helpers
|
|
5
|
+
Author: Qx Engineering
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.14
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
# qx-devtools
|
|
11
|
+
|
|
12
|
+
Shared code-quality configuration presets for Qx services — ruff, mypy, pre-commit, and editorconfig in one place.
|
|
13
|
+
|
|
14
|
+
## What lives here
|
|
15
|
+
|
|
16
|
+
- **`qx.devtools.RUFF_CONFIG`** — string constant with the standard ruff configuration (line length 100, full lint rule set, per-file ignores for tests and Alembic).
|
|
17
|
+
- **`qx.devtools.MYPY_CONFIG`** — string constant with strict mypy configuration (`strict = True`, pydantic plugin, test/alembic exclusions).
|
|
18
|
+
- **`qx.devtools.PRE_COMMIT_CONFIG`** — string constant with a pre-commit config that runs ruff, ruff-format, mypy, and standard file-hygiene hooks.
|
|
19
|
+
- **`qx.devtools.EDITORCONFIG`** — string constant with the standard `.editorconfig` (UTF-8, LF, 4-space indent).
|
|
20
|
+
- **`qx.devtools.write_configs`** — writes any combination of the above files into a target directory. Skips existing files unless `overwrite=True`.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Write the standard config files into a new service's root directory:
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from qx.devtools import write_configs
|
|
29
|
+
|
|
30
|
+
written = write_configs(Path("my-service/"), ruff=True, mypy=True, pre_commit=True)
|
|
31
|
+
print(f"Written: {[str(p) for p in written]}")
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or call it from the CLI during service scaffolding:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv run python -c "from qx.devtools import write_configs; from pathlib import Path; write_configs(Path('.'))"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Design rules
|
|
41
|
+
|
|
42
|
+
- The config strings are plain text constants so services can read, extend, or compose from them without parsing TOML/YAML.
|
|
43
|
+
- `write_configs` is intentionally conservative: it never overwrites by default. Run with `overwrite=True` only when you want to pull in updated framework defaults.
|
|
44
|
+
- Services that want to diverge from a specific rule should do so in their own config file rather than forking the preset — extend, don't override wholesale.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# qx-devtools
|
|
2
|
+
|
|
3
|
+
Shared code-quality configuration presets for Qx services — ruff, mypy, pre-commit, and editorconfig in one place.
|
|
4
|
+
|
|
5
|
+
## What lives here
|
|
6
|
+
|
|
7
|
+
- **`qx.devtools.RUFF_CONFIG`** — string constant with the standard ruff configuration (line length 100, full lint rule set, per-file ignores for tests and Alembic).
|
|
8
|
+
- **`qx.devtools.MYPY_CONFIG`** — string constant with strict mypy configuration (`strict = True`, pydantic plugin, test/alembic exclusions).
|
|
9
|
+
- **`qx.devtools.PRE_COMMIT_CONFIG`** — string constant with a pre-commit config that runs ruff, ruff-format, mypy, and standard file-hygiene hooks.
|
|
10
|
+
- **`qx.devtools.EDITORCONFIG`** — string constant with the standard `.editorconfig` (UTF-8, LF, 4-space indent).
|
|
11
|
+
- **`qx.devtools.write_configs`** — writes any combination of the above files into a target directory. Skips existing files unless `overwrite=True`.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Write the standard config files into a new service's root directory:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from qx.devtools import write_configs
|
|
20
|
+
|
|
21
|
+
written = write_configs(Path("my-service/"), ruff=True, mypy=True, pre_commit=True)
|
|
22
|
+
print(f"Written: {[str(p) for p in written]}")
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or call it from the CLI during service scaffolding:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv run python -c "from qx.devtools import write_configs; from pathlib import Path; write_configs(Path('.'))"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Design rules
|
|
32
|
+
|
|
33
|
+
- The config strings are plain text constants so services can read, extend, or compose from them without parsing TOML/YAML.
|
|
34
|
+
- `write_configs` is intentionally conservative: it never overwrites by default. Run with `overwrite=True` only when you want to pull in updated framework defaults.
|
|
35
|
+
- Services that want to diverge from a specific rule should do so in their own config file rather than forking the preset — extend, don't override wholesale.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "qx-devtools"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Qx devtools: ruff/mypy/pre-commit presets, code quality helpers"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
license = { text = "MIT" }
|
|
8
|
+
authors = [{ name = "Qx Engineering" }]
|
|
9
|
+
dependencies = []
|
|
10
|
+
|
|
11
|
+
[build-system]
|
|
12
|
+
requires = ["hatchling"]
|
|
13
|
+
build-backend = "hatchling.build"
|
|
14
|
+
|
|
15
|
+
[tool.hatch.build.targets.wheel]
|
|
16
|
+
packages = ["src/qx"]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Qx devtools: shared code-quality configuration.
|
|
2
|
+
|
|
3
|
+
Services pin to the same lint/type/format rules by extending the framework
|
|
4
|
+
config rather than re-declaring it. The text constants here let a service's
|
|
5
|
+
pyproject.toml or pre-commit config compose from a single source of truth.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
__version__ = "0.2.0"
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"EDITORCONFIG",
|
|
16
|
+
"MYPY_CONFIG",
|
|
17
|
+
"PRE_COMMIT_CONFIG",
|
|
18
|
+
"RUFF_CONFIG",
|
|
19
|
+
"__version__",
|
|
20
|
+
"write_configs",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
RUFF_CONFIG = """\
|
|
25
|
+
# Generated by qx-devtools — pin to framework conventions
|
|
26
|
+
line-length = 100
|
|
27
|
+
target-version = "py312"
|
|
28
|
+
|
|
29
|
+
[lint]
|
|
30
|
+
select = [
|
|
31
|
+
"E", "W", "F", "I", "B", "C4", "UP", "N", "S", "RUF", "PL", "ASYNC", "PERF",
|
|
32
|
+
]
|
|
33
|
+
ignore = ["E501", "S101", "PLR0913"]
|
|
34
|
+
|
|
35
|
+
[lint.per-file-ignores]
|
|
36
|
+
"tests/**" = ["S", "PLR2004"]
|
|
37
|
+
"alembic/versions/**" = ["F401", "E402"]
|
|
38
|
+
|
|
39
|
+
[format]
|
|
40
|
+
quote-style = "double"
|
|
41
|
+
docstring-code-format = true
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
MYPY_CONFIG = """\
|
|
45
|
+
# Generated by qx-devtools
|
|
46
|
+
[mypy]
|
|
47
|
+
python_version = 3.12
|
|
48
|
+
strict = True
|
|
49
|
+
warn_redundant_casts = True
|
|
50
|
+
warn_unused_ignores = True
|
|
51
|
+
warn_return_any = True
|
|
52
|
+
warn_unreachable = True
|
|
53
|
+
plugins = pydantic.mypy
|
|
54
|
+
|
|
55
|
+
[mypy-tests.*]
|
|
56
|
+
disallow_untyped_defs = False
|
|
57
|
+
|
|
58
|
+
[mypy-alembic.versions.*]
|
|
59
|
+
ignore_errors = True
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
PRE_COMMIT_CONFIG = """\
|
|
64
|
+
# Generated by qx-devtools
|
|
65
|
+
repos:
|
|
66
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
67
|
+
rev: v0.6.9
|
|
68
|
+
hooks:
|
|
69
|
+
- id: ruff
|
|
70
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
71
|
+
- id: ruff-format
|
|
72
|
+
|
|
73
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
74
|
+
rev: v1.11.2
|
|
75
|
+
hooks:
|
|
76
|
+
- id: mypy
|
|
77
|
+
additional_dependencies: [pydantic, types-PyYAML]
|
|
78
|
+
args: [--strict]
|
|
79
|
+
exclude: ^(tests/|alembic/)
|
|
80
|
+
|
|
81
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
82
|
+
rev: v5.0.0
|
|
83
|
+
hooks:
|
|
84
|
+
- id: check-yaml
|
|
85
|
+
- id: check-toml
|
|
86
|
+
- id: end-of-file-fixer
|
|
87
|
+
- id: trailing-whitespace
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
EDITORCONFIG = """\
|
|
92
|
+
root = true
|
|
93
|
+
|
|
94
|
+
[*]
|
|
95
|
+
charset = utf-8
|
|
96
|
+
end_of_line = lf
|
|
97
|
+
insert_final_newline = true
|
|
98
|
+
trim_trailing_whitespace = true
|
|
99
|
+
|
|
100
|
+
[*.{py,toml,yaml,yml,json}]
|
|
101
|
+
indent_style = space
|
|
102
|
+
indent_size = 4
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def write_configs(
|
|
107
|
+
target: Path,
|
|
108
|
+
*,
|
|
109
|
+
ruff: bool = True,
|
|
110
|
+
mypy: bool = True,
|
|
111
|
+
pre_commit: bool = True,
|
|
112
|
+
editorconfig: bool = True,
|
|
113
|
+
overwrite: bool = False,
|
|
114
|
+
) -> list[Path]:
|
|
115
|
+
"""Write the standard config files into a project directory.
|
|
116
|
+
|
|
117
|
+
Returns the list of files written. Files that already exist are skipped
|
|
118
|
+
unless ``overwrite=True``.
|
|
119
|
+
"""
|
|
120
|
+
target = Path(target)
|
|
121
|
+
target.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
written: list[Path] = []
|
|
123
|
+
|
|
124
|
+
files: list[tuple[str, str]] = []
|
|
125
|
+
if ruff:
|
|
126
|
+
files.append(("ruff.toml", RUFF_CONFIG))
|
|
127
|
+
if mypy:
|
|
128
|
+
files.append(("mypy.ini", MYPY_CONFIG))
|
|
129
|
+
if pre_commit:
|
|
130
|
+
files.append((".pre-commit-config.yaml", PRE_COMMIT_CONFIG))
|
|
131
|
+
if editorconfig:
|
|
132
|
+
files.append((".editorconfig", EDITORCONFIG))
|
|
133
|
+
|
|
134
|
+
for name, content in files:
|
|
135
|
+
path = target / name
|
|
136
|
+
if path.exists() and not overwrite:
|
|
137
|
+
continue
|
|
138
|
+
path.write_text(content, encoding="utf-8")
|
|
139
|
+
written.append(path)
|
|
140
|
+
return written
|
|
File without changes
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""qx-devtools tests — config writers produce expected files."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from qx.devtools import (
|
|
8
|
+
EDITORCONFIG,
|
|
9
|
+
MYPY_CONFIG,
|
|
10
|
+
PRE_COMMIT_CONFIG,
|
|
11
|
+
RUFF_CONFIG,
|
|
12
|
+
write_configs,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_constants_present() -> None:
|
|
20
|
+
assert "line-length" in RUFF_CONFIG
|
|
21
|
+
assert "python_version" in MYPY_CONFIG
|
|
22
|
+
assert "pre-commit" in PRE_COMMIT_CONFIG or "ruff" in PRE_COMMIT_CONFIG
|
|
23
|
+
assert "root = true" in EDITORCONFIG
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_write_configs_creates_all_four(tmp_path: Path) -> None:
|
|
27
|
+
written = write_configs(tmp_path)
|
|
28
|
+
names = {p.name for p in written}
|
|
29
|
+
assert names == {"ruff.toml", "mypy.ini", ".pre-commit-config.yaml", ".editorconfig"}
|
|
30
|
+
for p in written:
|
|
31
|
+
assert p.read_text().strip()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_write_configs_respects_existing(tmp_path: Path) -> None:
|
|
35
|
+
(tmp_path / "ruff.toml").write_text("# user-customized")
|
|
36
|
+
written = write_configs(tmp_path)
|
|
37
|
+
assert not any(p.name == "ruff.toml" for p in written)
|
|
38
|
+
assert (tmp_path / "mypy.ini").exists()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_write_configs_overwrite_flag(tmp_path: Path) -> None:
|
|
42
|
+
(tmp_path / "ruff.toml").write_text("# user-customized")
|
|
43
|
+
written = write_configs(tmp_path, overwrite=True)
|
|
44
|
+
assert any(p.name == "ruff.toml" for p in written)
|
|
45
|
+
assert "line-length" in (tmp_path / "ruff.toml").read_text()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_write_configs_selective(tmp_path: Path) -> None:
|
|
49
|
+
written = write_configs(tmp_path, ruff=True, mypy=False, pre_commit=False, editorconfig=False)
|
|
50
|
+
assert {p.name for p in written} == {"ruff.toml"}
|