openverify-code 0.1.1__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.
@@ -0,0 +1,6 @@
1
+ """Code check facade for the local OpenVerify product."""
2
+ # pyright: basic
3
+
4
+ from openverify_code.local import check
5
+
6
+ __all__ = ["check"]
@@ -0,0 +1,134 @@
1
+ """Python code rules owned by openverify-code."""
2
+ # pyright: basic
3
+
4
+ import json
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ from openverify.relay.local import finding, run_rules
12
+
13
+ RULES = {
14
+ "ruff": "Ruff lint with project configuration",
15
+ "ruff-format": "Ruff format check with project configuration",
16
+ "pytest": "Configured project tests",
17
+ "ast-grep": "Configured structural code pattern",
18
+ }
19
+
20
+
21
+ def tool(name: str) -> str | None:
22
+ sibling = Path(sys.executable).with_name(name + (".exe" if os.name == "nt" else ""))
23
+ return str(sibling) if sibling.is_file() else shutil.which(name)
24
+
25
+
26
+ def run_command(argv: list[str], root: Path) -> subprocess.CompletedProcess[str]:
27
+ return subprocess.run(argv, cwd=root, text=True, capture_output=True, encoding="utf-8")
28
+
29
+
30
+ def code_rule(rule: str, root: Path, files: list[str], settings: dict) -> list[dict]:
31
+ for name in files:
32
+ if b"\0" in (root / name).read_bytes():
33
+ raise ValueError(f"Binary selected file: {name}")
34
+ if rule == "pytest":
35
+ tests = settings.get("tests")
36
+ if not tests:
37
+ return []
38
+ if any(not isinstance(t, str) or t.startswith("-") for t in tests):
39
+ raise ValueError("pytest tests must be explicit paths")
40
+ executable = settings.get("executable") or tool("pytest")
41
+ if not executable:
42
+ raise FileNotFoundError("pytest is required")
43
+ pytest_config = []
44
+ if settings.get("_staged"):
45
+ candidate = next(
46
+ (
47
+ root / name
48
+ for name in ("pytest.ini", "pyproject.toml", "tox.ini", "setup.cfg")
49
+ if (root / name).is_file()
50
+ ),
51
+ None,
52
+ )
53
+ if candidate is None:
54
+ candidate = root / ".openverify-empty-pytest.ini"
55
+ candidate.write_text("[pytest]\n", encoding="utf-8")
56
+ pytest_config = ["-c", str(candidate)]
57
+ result = run_command([executable, "-q", *pytest_config, "--", *tests], root)
58
+ if result.returncode not in (0, 1):
59
+ raise RuntimeError(f"pytest exit {result.returncode}: {result.stderr}")
60
+ return (
61
+ []
62
+ if result.returncode == 0
63
+ else [finding(rule, "", 0, 0, "Tests failed", stdout=result.stdout[-8000:])]
64
+ )
65
+ if not files:
66
+ return []
67
+ if rule in ("ruff", "ruff-format"):
68
+ executable = settings.get("executable") or tool("ruff")
69
+ if not executable:
70
+ raise FileNotFoundError("Ruff is required")
71
+ ruff_config = []
72
+ if settings.get("_staged"):
73
+ candidate = next(
74
+ (
75
+ root / name
76
+ for name in ("ruff.toml", ".ruff.toml", "pyproject.toml")
77
+ if (root / name).is_file()
78
+ ),
79
+ None,
80
+ )
81
+ ruff_config = ["--config", str(candidate)] if candidate else ["--isolated"]
82
+ if rule == "ruff":
83
+ argv = [executable, "check", *ruff_config, "--output-format", "json"]
84
+ if settings.get("select"):
85
+ argv += ["--select", ",".join(settings["select"])]
86
+ result = run_command([*argv, "--", *files], root)
87
+ if result.returncode not in (0, 1):
88
+ raise RuntimeError(f"Ruff exit {result.returncode}: {result.stderr}")
89
+ return [
90
+ finding(
91
+ rule,
92
+ Path(item["filename"]).relative_to(root).as_posix(),
93
+ item["location"]["row"],
94
+ item["location"]["column"],
95
+ item["message"],
96
+ code=item["code"],
97
+ )
98
+ for item in json.loads(result.stdout)
99
+ ]
100
+ findings = []
101
+ for name in files:
102
+ result = run_command([executable, "format", *ruff_config, "--check", "--", name], root)
103
+ if result.returncode not in (0, 1):
104
+ raise RuntimeError(f"Ruff format exit {result.returncode}: {result.stderr}")
105
+ if result.returncode == 1:
106
+ findings.append(finding(rule, name, 1, 1, "Formatting differs"))
107
+ return findings
108
+ executable = settings.get("executable") or tool("ast-grep")
109
+ if not executable:
110
+ raise FileNotFoundError("ast-grep is required")
111
+ if "rule_file" in settings:
112
+ argv = [executable, "scan", "--rule", settings["rule_file"]]
113
+ elif settings.get("pattern") and settings.get("language"):
114
+ argv = [executable, "run", "--lang", settings["language"], "--pattern", settings["pattern"]]
115
+ else:
116
+ return []
117
+ result = run_command([*argv, "--json=compact", "--", *files], root)
118
+ if result.returncode not in (0, 1):
119
+ raise RuntimeError(f"ast-grep exit {result.returncode}: {result.stderr}")
120
+ return [
121
+ finding(
122
+ rule,
123
+ item["file"],
124
+ item["range"]["start"]["line"] + 1,
125
+ item["range"]["start"]["column"] + 1,
126
+ "Pattern matched",
127
+ text=item["text"],
128
+ )
129
+ for item in json.loads(result.stdout or "[]")
130
+ ]
131
+
132
+
133
+ def check(root: Path, names: list[str], staged: bool = False) -> dict:
134
+ return run_rules("code", root, names, staged, RULES, code_rule)
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: openverify-code
3
+ Version: 0.1.1
4
+ Summary: Local Python code checks for OpenVerify
5
+ Requires-Python: <3.14,>=3.13
6
+ Requires-Dist: openverify-relay==0.1.1
7
+ Provides-Extra: tools
8
+ Requires-Dist: ruff>=0.17; extra == "tools"
9
+ Requires-Dist: pytest>=8.3; extra == "tools"
10
+ Requires-Dist: ast-grep-cli>=0.50; extra == "tools"
@@ -0,0 +1,6 @@
1
+ openverify_code/__init__.py,sha256=Q3EAQpz_nmAZl5395qdQisrAR36sgXoUB-1p2kVcm_4,137
2
+ openverify_code/local.py,sha256=D7z0cSUwbS8XSIiIKMnDlUja9IQHjUxLp8yPnCXNzus,5323
3
+ openverify_code-0.1.1.dist-info/METADATA,sha256=XxrHswmwRY99zUj9qJJbxyLRwwbnvnAuyuqAM2JHiDk,340
4
+ openverify_code-0.1.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
5
+ openverify_code-0.1.1.dist-info/top_level.txt,sha256=t09MnzjD_Qcx2adx3POypDzEFWD6U1-f0c6XqLqZ_UA,16
6
+ openverify_code-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ openverify_code