testscan 0.1.0__py3-none-win_amd64.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.
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.5
2
+ Name: testscan
3
+ Version: 0.1.0
4
+ Summary: Static analysis for AI-generated Python tests — thin launcher for the Go testscan CLI
5
+ Project-URL: Homepage, https://github.com/Nikita527/testscan
6
+ Project-URL: Repository, https://github.com/Nikita527/testscan
7
+ Project-URL: Issues, https://github.com/Nikita527/testscan/issues
8
+ Author: Nikita527
9
+ License-Expression: MIT
10
+ Keywords: ai,linter,pytest,static-analysis,test-smells,testing
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Software Development :: Quality Assurance
25
+ Classifier: Topic :: Software Development :: Testing
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+
29
+ # testscan (Python launcher)
30
+
31
+ English | [Русский](README.ru.md)
32
+
33
+ Thin wrapper: finds the Go binary and forwards `argv` / exit code.
34
+ **Source of truth is the Go CLI** (`cmd/testscan`). Python has no rules, parsing, or flag handling.
35
+
36
+ Package name on PyPI: **`testscan`**. Version comes from git tags (`v*`) via hatch-vcs.
37
+
38
+ ## Install (PyPI)
39
+
40
+ ```bash
41
+ uvx testscan@latest tests/
42
+ # or: pipx run testscan tests/
43
+ # or: pip install testscan && testscan tests/
44
+ ```
45
+
46
+ Release CI builds platform wheels (`linux/amd64`, `darwin/arm64`, `windows/amd64`) with the Go binary under `testscan_py/bin/`.
47
+
48
+ ## Local embed (contributors)
49
+
50
+ Binaries under `src/testscan_py/bin/` are **not committed** (`.gitignore`). Embed before `uvx --from ./python`. `pyproject.toml` sets `ignore-vcs = true` and `force-include` for `bin/*`, otherwise hatchling can drop the binary because of gitignore.
51
+
52
+ ```powershell
53
+ powershell -ExecutionPolicy Bypass -File python/scripts/embed_bin.ps1
54
+ ```
55
+
56
+ ```bash
57
+ bash python/scripts/embed_bin.sh
58
+ ```
59
+
60
+ Manual equivalent:
61
+
62
+ ```bash
63
+ go build -o python/src/testscan_py/bin/testscan ./cmd/testscan
64
+ # Windows: .../bin/testscan.exe
65
+ ```
66
+
67
+ Then:
68
+
69
+ ```bash
70
+ uvx --from ./python testscan --help
71
+ uvx --from ./python testscan rules/testdata/empty --fail-on never
72
+ ```
73
+
74
+ Without embed — via env (handy for debugging the wrapper):
75
+
76
+ ```bash
77
+ go build -o /tmp/testscan ./cmd/testscan
78
+ TESTSCAN_BIN=/tmp/testscan uvx --from ./python testscan --help
79
+ ```
80
+
81
+ ### Platform wheel (release / maintainers)
82
+
83
+ ```bash
84
+ # from repo root; needs git tag history for hatch-vcs
85
+ bash python/scripts/build_platform_wheel.sh linux-amd64 manylinux_2_17_x86_64
86
+ ```
87
+
88
+ Tag a release (`v0.1.0`) to trigger `.github/workflows/release.yml` → multi-OS wheels → PyPI + GitHub Release.
89
+
90
+ Before the first publish: create a PyPI project + [Trusted Publisher](https://docs.pypi.org/trusted-publishers/) for this repo (workflow `Release`, environment `pypi`), or the publish job will fail on OIDC.
91
+
92
+ ## Binary lookup (priority)
93
+
94
+ 1. `TESTSCAN_BIN`
95
+ 2. bundled: `testscan_py/bin/testscan.exe` (Windows) / `testscan` (unix)
96
+ 3. `PATH` (`shutil.which("testscan")`)
97
+
98
+ If not found → message on stderr, exit `2`.
99
+
100
+ ## Manual check list
101
+
102
+ - [x] `uvx testscan@latest --help` (after PyPI publish) → usage (exit 0)
103
+ - [x] `uvx --from ./python testscan --help` after embed → usage (exit 0)
104
+ - [x] `uvx --from ./python testscan rules/testdata/empty/hit --rule empty-test --fail-on error` → exit 1
105
+ - [x] `uvx --from ./python testscan rules/testdata/empty/clean --fail-on error` → exit 0
106
+ - [x] bundled binary in wheel after embed + `ignore-vcs` / `force-include`
107
+ - [x] `go test ./...` green
@@ -0,0 +1,9 @@
1
+ testscan_py/__init__.py,sha256=JmfAvCrIymZlazY3cDkZBtkCxb1FgUd4dpvIVyCo_O0,208
2
+ testscan_py/_version.py,sha256=ptILGutmCA6QX4OPt03bkYZX2VUTr2ovOOy8vb2k2ZU,544
3
+ testscan_py/cli.py,sha256=8q1-JprvPWDTeAEfhCxCOm-jZPnpqnEYd3_6fBcLBvM,1478
4
+ testscan_py/bin/.gitkeep,sha256=Zou8IZ3GE4d1_ImZSH2x9-hiGPsg6bg2TcMsL9Sl4XY,178
5
+ testscan_py/bin/testscan.exe,sha256=_WJCtW1MIz9mt0lxzLqsYldNO7mySG_EIrDfQOq4ld4,2958848
6
+ testscan-0.1.0.dist-info/METADATA,sha256=thduCBOfCOnhpUw-xQDU_cAjWiammBbI2eMM86cDwsY,3878
7
+ testscan-0.1.0.dist-info/WHEEL,sha256=DqRKTdcjqcnA9JhnVHem7iyI7zn0B1vfHx5zVCEXDZY,95
8
+ testscan-0.1.0.dist-info/entry_points.txt,sha256=ORpinBgXEk20x8X-dzXLTyeF66kD2f0OCCV2rFPdjTU,50
9
+ testscan-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-win_amd64
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ testscan = testscan_py.cli:main
@@ -0,0 +1,6 @@
1
+ """Launcher package for the Go testscan CLI."""
2
+
3
+ try:
4
+ from testscan_py._version import __version__
5
+ except ImportError: # pragma: no cover - missing only in bare checkouts
6
+ __version__ = "0.0.0"
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '0.1.0'
22
+ __version_tuple__ = version_tuple = (0, 1, 0)
23
+
24
+ __commit_id__ = commit_id = None
@@ -0,0 +1,4 @@
1
+ # Go binary is not committed (see repo .gitignore *.exe).
2
+ # Build into this directory:
3
+ # powershell python/scripts/embed_bin.ps1
4
+ # # or: bash python/scripts/embed_bin.sh
Binary file
testscan_py/cli.py ADDED
@@ -0,0 +1,51 @@
1
+ """Thin launcher for the Go testscan binary. No parsing or rules here."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+
12
+ def resolve_bin() -> Path:
13
+ if env := os.environ.get("TESTSCAN_BIN"):
14
+ p = Path(env)
15
+ if not p.is_file():
16
+ raise FileNotFoundError(f"TESTSCAN_BIN not a file: {env}")
17
+ return p
18
+
19
+ name = "testscan.exe" if os.name == "nt" else "testscan"
20
+ bundled = Path(__file__).resolve().parent / "bin" / name
21
+ if bundled.is_file():
22
+ return bundled
23
+
24
+ which = shutil.which("testscan")
25
+ if which:
26
+ p = Path(which).resolve()
27
+ # не выбирать сам uvx/pip entry-point (рекурсия)
28
+ argv0 = Path(sys.argv[0]).resolve()
29
+ if p != argv0 and p.is_file():
30
+ return p
31
+
32
+ raise FileNotFoundError(
33
+ "testscan binary not found. Install from PyPI (uvx testscan@latest), "
34
+ "set TESTSCAN_BIN, run python/scripts/embed_bin.ps1 (or .sh), "
35
+ "or put the Go binary on PATH."
36
+ )
37
+
38
+
39
+ def main() -> None:
40
+ try:
41
+ bin_path = resolve_bin()
42
+ except FileNotFoundError as exc:
43
+ print(f"testscan: {exc}", file=sys.stderr)
44
+ raise SystemExit(2) from exc
45
+
46
+ # Windows + uvx: subprocess надёжнее os.execv
47
+ raise SystemExit(subprocess.call([str(bin_path), *sys.argv[1:]]))
48
+
49
+
50
+ if __name__ == "__main__":
51
+ main()