parent
540099f2b7
commit
c24ab48704
@ -0,0 +1 @@ |
|||||||
|
pytest>=9.0.2 |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
from pathlib import Path |
||||||
|
import re |
||||||
|
|
||||||
|
|
||||||
|
def _extract_project_dependencies(text: str): |
||||||
|
"""Return the dependencies block text from a [project] section, or None.""" |
||||||
|
lines = text.splitlines() |
||||||
|
cur = None |
||||||
|
deps_block = [] |
||||||
|
in_dependencies = False |
||||||
|
for line in lines: |
||||||
|
s = line.strip() |
||||||
|
if s.startswith("[") and s.endswith("]"): |
||||||
|
cur = s |
||||||
|
in_dependencies = False |
||||||
|
continue |
||||||
|
if cur == "[project]": |
||||||
|
if ( |
||||||
|
s.startswith("dependencies") |
||||||
|
and "=[" in s |
||||||
|
or s.startswith("dependencies") |
||||||
|
and s.endswith("=[") |
||||||
|
): |
||||||
|
# start of block on same line or next |
||||||
|
# capture from the first '[' |
||||||
|
idx = line.find("[") |
||||||
|
if idx != -1 and line.rstrip().endswith("]"): |
||||||
|
# single-line |
||||||
|
deps_block.append(line[idx:]) |
||||||
|
in_dependencies = False |
||||||
|
else: |
||||||
|
deps_block.append(line[idx:]) |
||||||
|
in_dependencies = True |
||||||
|
continue |
||||||
|
if in_dependencies: |
||||||
|
deps_block.append(line) |
||||||
|
if "]" in line: |
||||||
|
in_dependencies = False |
||||||
|
if deps_block: |
||||||
|
return "\n".join(deps_block) |
||||||
|
return None |
||||||
|
|
||||||
|
|
||||||
|
def _extract_poetry_dependencies(text: str): |
||||||
|
"""Return the lines under [tool.poetry.dependencies] as a single string, or None.""" |
||||||
|
lines = text.splitlines() |
||||||
|
cur = None |
||||||
|
collected = [] |
||||||
|
for line in lines: |
||||||
|
s = line.strip() |
||||||
|
if s.startswith("[") and s.endswith("]"): |
||||||
|
cur = s |
||||||
|
continue |
||||||
|
if cur == "[tool.poetry.dependencies]": |
||||||
|
if s == "" or s.startswith("#"): |
||||||
|
continue |
||||||
|
collected.append(s) |
||||||
|
if collected: |
||||||
|
return "\n".join(collected) |
||||||
|
return None |
||||||
|
|
||||||
|
|
||||||
|
def test_pytest_not_in_production_deps(): |
||||||
|
p = Path("pyproject.toml") |
||||||
|
assert p.exists(), "pyproject.toml must exist for this project" |
||||||
|
text = p.read_text() |
||||||
|
|
||||||
|
# Check PEP 621 [project] dependencies = [ ... ] |
||||||
|
proj_deps = _extract_project_dependencies(text) |
||||||
|
if proj_deps: |
||||||
|
assert "pytest" not in proj_deps.lower(), ( |
||||||
|
"pytest must not be listed in [project] production dependencies" |
||||||
|
) |
||||||
|
|
||||||
|
# Check poetry style [tool.poetry.dependencies] |
||||||
|
poetry_deps = _extract_poetry_dependencies(text) |
||||||
|
if poetry_deps: |
||||||
|
# lines like pytest = "^..." or pytest = "..." |
||||||
|
for line in poetry_deps.splitlines(): |
||||||
|
# split on = and check key |
||||||
|
key = line.split("=", 1)[0].strip() |
||||||
|
assert key.lower() != "pytest", ( |
||||||
|
"pytest must not be listed in [tool.poetry.dependencies]" |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
def test_requirements_dev_contains_pytest_if_present(): |
||||||
|
# If pytest was removed from production deps, ensure it's present in requirements-dev.txt |
||||||
|
dev = Path("requirements-dev.txt") |
||||||
|
if dev.exists(): |
||||||
|
text = dev.read_text().lower() |
||||||
|
assert "pytest" in text, ( |
||||||
|
"requirements-dev.txt exists but does not contain pytest" |
||||||
|
) |
||||||
|
else: |
||||||
|
# If requirements-dev.txt does not exist, that's acceptable as long as pytest isn't in prod deps |
||||||
|
p = Path("pyproject.toml") |
||||||
|
text = p.read_text() |
||||||
|
assert "pytest" not in text.lower(), ( |
||||||
|
"pytest found in pyproject.toml and requirements-dev.txt is missing" |
||||||
|
) |
||||||
Loading…
Reference in new issue