Skip to content

Commit 2f77d57

Browse files
committed
test: scan every readable wheel surface
1 parent 71dab1e commit 2f77d57

2 files changed

Lines changed: 95 additions & 2 deletions

File tree

scripts/validate_storefront_claims.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,24 @@
22
"""Reject stale mutable claims from authored, generated, and packaged surfaces."""
33

44
import argparse
5+
import csv
56
import re
67
from pathlib import Path
78
from typing import Iterable, List, Pattern, Sequence, Tuple
89

910
ROOT = Path(__file__).resolve().parents[1]
1011
CONTRACT = "https://api.oilpriceapi.com/product-facts.json"
12+
BINARY_SUFFIXES = {
13+
".a",
14+
".class",
15+
".dll",
16+
".dylib",
17+
".o",
18+
".pyd",
19+
".pyc",
20+
".pyo",
21+
".so",
22+
}
1123
BLOCKED: Sequence[Tuple[str, Pattern[str]]] = (
1224
("real-time claim", re.compile(r"\breal[ -]?time\b", re.IGNORECASE)),
1325
(
@@ -74,10 +86,37 @@ def discover_public_surfaces(root: Path = ROOT) -> List[Path]:
7486
return sorted(surfaces)
7587

7688

89+
def discover_installed_surfaces(package_root: Path) -> List[Path]:
90+
"""Return every UTF-8 customer-readable file recorded in the wheel manifest."""
91+
package_root = package_root.resolve()
92+
record_files = sorted(package_root.glob("oilpriceapi-*.dist-info/RECORD"))
93+
if len(record_files) != 1:
94+
return []
95+
96+
surfaces: List[Path] = []
97+
with record_files[0].open(encoding="utf-8", newline="") as record:
98+
for row in csv.reader(record):
99+
if not row:
100+
continue
101+
path = (package_root / row[0]).resolve()
102+
try:
103+
path.relative_to(package_root)
104+
except ValueError:
105+
continue
106+
if not path.is_file() or path.suffix.lower() in BINARY_SUFFIXES:
107+
continue
108+
try:
109+
path.read_text(encoding="utf-8")
110+
except UnicodeDecodeError:
111+
continue
112+
surfaces.append(path)
113+
return sorted(set(surfaces))
114+
115+
77116
def _claim_failures(root: Path, surfaces: Iterable[Path]) -> List[str]:
78117
failures: List[str] = []
79118
for path in surfaces:
80-
text = path.read_text()
119+
text = path.read_text(encoding="utf-8")
81120
for label, pattern in BLOCKED:
82121
match = pattern.search(text)
83122
if match:
@@ -109,12 +148,16 @@ def validate_package(package_root: Path) -> List[str]:
109148
package_root = package_root.resolve()
110149
package_dir = package_root / "oilpriceapi"
111150
metadata_files = sorted(package_root.glob("oilpriceapi-*.dist-info/METADATA"))
112-
surfaces = sorted(package_dir.rglob("*.py")) + metadata_files
151+
record_files = sorted(package_root.glob("oilpriceapi-*.dist-info/RECORD"))
152+
surfaces = discover_installed_surfaces(package_root)
113153
failures = _claim_failures(package_root, surfaces)
114154

115155
if len(metadata_files) != 1:
116156
failures.append("installed artifact must contain exactly one oilpriceapi METADATA file")
117157
return failures
158+
if len(record_files) != 1:
159+
failures.append("installed artifact must contain exactly one oilpriceapi RECORD file")
160+
return failures
118161

119162
metadata = metadata_files[0].read_text()
120163
if CONTRACT not in metadata:

tests/test_storefront_claims.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from pathlib import Path
22

33
from scripts.validate_storefront_claims import (
4+
discover_installed_surfaces,
45
discover_public_surfaces,
56
validate,
67
validate_package,
@@ -34,5 +35,54 @@ def test_rejects_claim_introduced_only_in_installed_wheel(tmp_path: Path) -> Non
3435
"Version: 9.9.9\n\n"
3536
"https://api.oilpriceapi.com/product-facts.json\n"
3637
)
38+
(dist_info / "RECORD").write_text(
39+
"oilpriceapi/version.py,,\n"
40+
"oilpriceapi/future.py,,\n"
41+
"oilpriceapi-9.9.9.dist-info/METADATA,,\n"
42+
"oilpriceapi-9.9.9.dist-info/RECORD,,\n"
43+
)
3744

3845
assert any("oilpriceapi/future.py" in failure for failure in validate_package(tmp_path))
46+
47+
48+
def test_rejects_claim_in_future_installed_package_data(tmp_path: Path) -> None:
49+
package = tmp_path / "oilpriceapi"
50+
dist_info = tmp_path / "oilpriceapi-9.9.9.dist-info"
51+
package.mkdir()
52+
(package / "docs").mkdir()
53+
(package / "__pycache__").mkdir()
54+
dist_info.mkdir()
55+
(package / "version.py").write_text('__version__ = "9.9.9"\n')
56+
(package / "py.typed").write_text("")
57+
(package / "types.pyi").write_text('"""Real-time prices."""\n')
58+
(package / "docs" / "catalog.json").write_text(
59+
'{"allowance": "1,000 API requests/month"}\n'
60+
)
61+
(package / "__pycache__" / "version.cpython-312.pyc").write_bytes(b"\x00\xff")
62+
(dist_info / "METADATA").write_text(
63+
"Metadata-Version: 2.1\n"
64+
"Name: oilpriceapi\n"
65+
"Version: 9.9.9\n\n"
66+
"https://api.oilpriceapi.com/product-facts.json\n"
67+
)
68+
(dist_info / "RECORD").write_text(
69+
"oilpriceapi/version.py,,\n"
70+
"oilpriceapi/py.typed,,\n"
71+
"oilpriceapi/types.pyi,,\n"
72+
"oilpriceapi/docs/catalog.json,,\n"
73+
"oilpriceapi/__pycache__/version.cpython-312.pyc,,\n"
74+
"oilpriceapi-9.9.9.dist-info/METADATA,,\n"
75+
"oilpriceapi-9.9.9.dist-info/RECORD,,\n"
76+
)
77+
78+
surfaces = {
79+
path.relative_to(tmp_path).as_posix()
80+
for path in discover_installed_surfaces(tmp_path)
81+
}
82+
failures = validate_package(tmp_path)
83+
84+
assert "oilpriceapi/types.pyi" in surfaces
85+
assert "oilpriceapi/docs/catalog.json" in surfaces
86+
assert not any("__pycache__" in surface for surface in surfaces)
87+
assert any("oilpriceapi/types.pyi" in failure for failure in failures)
88+
assert any("oilpriceapi/docs/catalog.json" in failure for failure in failures)

0 commit comments

Comments
 (0)