Erste Fassung: Claude-Werkzeuge aus dem Vault
.vault-tools (ohne trading-plattform-pruefung und watermarks-remover), .claude/agents, .claude/skills, .claude/workflows, .claude/settings.json. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
fa1c26b407
137 files changed
+43926
No files matched your search
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Gleichwertigkeitsprobe fuer die Konfigurationssuche von mutanten_lauf.py.
|
||||
|
||||
Zweck: Je Aufbau zwei Vergleiche. Erstens pytest_konfig_bestimmen aus
|
||||
mutanten_lauf.py gegen determine_setup aus pytest selbst (Konfigurationsdatei
|
||||
und rootdir). Zweitens ein echtes "pytest --co" ohne -c in einem sauberen
|
||||
Rahmen gegen einen Lauf mit den Argumenten des Laeufers in einem Rahmen, ueber
|
||||
dem eine fremde pyproject.toml, ein fremdes conftest.py und ein fremdes
|
||||
setup.py liegen. Verglichen werden Rueckgabewert, Knotennamen und die Liste
|
||||
der geladenen conftest.py-Dateien.
|
||||
|
||||
Aufruf (in WSL, mit dem Python eines Venvs, in dem pytest liegt):
|
||||
python mutanten_lauf_probe_konfig.py <mutanten_lauf.py> <arbeitsordner>
|
||||
Exit 0 ohne Abweichung, 1 mit Abweichung, 2 bei falschem Aufruf.
|
||||
|
||||
Geltungsbereich: ein Fangnetz fuer die Aufbauten unten, kein Nachweis fuer
|
||||
jeden denkbaren Baum. Ein Knoten mit ".." fehlt, weil mutanten_lauf.py ihn
|
||||
schon beim Lesen der mutanten.toml abweist. Eine Wurzel ueber einen Symlink
|
||||
prueft Treiber-Fall 41.
|
||||
|
||||
Die Probe prueft ihre eigene Voraussetzung mit: Ohne -c im sauberen Rahmen
|
||||
muss pytest mit 0 enden (ausser bei den Aufbauten knoten_fehl*), und wo der
|
||||
Aufbau ein conftest.py hat, muss eines geladen sein. Sonst endeten beide
|
||||
Laeufe gleich kaputt, und "gleich" hiesse nichts.
|
||||
|
||||
Grundlage: Probe des Pruefers vom 2026-10-06 (25 Aufbauten). Das conftest.py
|
||||
der Probe schreibt den Zeilenumbruch hier mit chr(10), damit kein Backslash
|
||||
durch die Zeichenkette muss.
|
||||
"""
|
||||
|
||||
import importlib.util
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
TEST = "def test_a():\n pass\n\n\ndef test_b():\n pass\n"
|
||||
CONFTEST = "import os\nopen(os.environ['CFLOG'], 'a').write(__file__ + chr(10))\n"
|
||||
INI = "[pytest]\n"
|
||||
|
||||
AUFBAUTEN = {
|
||||
"wurzel_pytest_ini": ({"pytest.ini": INI, "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"unterordner_ini": (
|
||||
{"tests/pytest.ini": INI, "tests/test_a.py": TEST, "conftest.py": CONFTEST, "tests/conftest.py": CONFTEST},
|
||||
["tests/test_a.py::test_a"],
|
||||
),
|
||||
"zwei_ordner_ini_im_ersten": (
|
||||
{"tests/a/pytest.ini": INI, "tests/a/test_a.py": TEST, "tests/b/test_b.py": TEST},
|
||||
["tests/a/test_a.py::test_a", "tests/b/test_b.py::test_b"],
|
||||
),
|
||||
"zwei_ordner_ini_im_zweiten": (
|
||||
{"tests/b/pytest.ini": INI, "tests/a/test_a.py": TEST, "tests/b/test_b.py": TEST},
|
||||
["tests/a/test_a.py::test_a", "tests/b/test_b.py::test_b"],
|
||||
),
|
||||
"setup_py_wurzel": ({"setup.py": "", "tests/test_a.py": TEST, "conftest.py": CONFTEST}, ["tests/test_a.py::test_a"]),
|
||||
"setup_py_unterordner": (
|
||||
{"sub/setup.py": "", "sub/tests/test_a.py": TEST, "conftest.py": CONFTEST, "sub/conftest.py": CONFTEST},
|
||||
["sub/tests/test_a.py::test_a"],
|
||||
),
|
||||
"setup_py_neben_ini": (
|
||||
{"tests/a/setup.py": "", "tests/b/pytest.ini": INI, "tests/a/test_a.py": TEST, "tests/b/test_b.py": TEST},
|
||||
["tests/a/test_a.py::test_a", "tests/b/test_b.py::test_b"],
|
||||
),
|
||||
"pyproject_ohne_abschnitt": (
|
||||
{"pyproject.toml": "[project]\nname = 'x'\n", "tests/test_a.py": TEST, "conftest.py": CONFTEST},
|
||||
["tests/test_a.py::test_a"],
|
||||
),
|
||||
"pyproject_ohne_unten_ini_oben": (
|
||||
{"tests/pyproject.toml": "[project]\nname = 'x'\n", "pytest.ini": INI, "tests/test_a.py": TEST},
|
||||
["tests/test_a.py::test_a"],
|
||||
),
|
||||
"pyproject_ohne_oben_ini_unten": (
|
||||
{"pyproject.toml": "[project]\nname = 'x'\n", "tests/pytest.ini": INI, "tests/test_a.py": TEST},
|
||||
["tests/test_a.py::test_a"],
|
||||
),
|
||||
"pyproject_ini_options_leer": ({"pyproject.toml": "[tool.pytest.ini_options]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"pyproject_tool_pytest_leer": ({"pyproject.toml": "[tool.pytest]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"pyproject_toml_modus": ({"pyproject.toml": "[tool.pytest]\nminversion = '8'\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"tox_mit_abschnitt": ({"tox.ini": "[tox]\n[pytest]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"tox_ohne_abschnitt": ({"tox.ini": "[tox]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"tox_abschnitt_mit_kommentar": ({"tox.ini": "[pytest] # k\nmarkers = x\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"tox_abschnitt_mit_semikolon": ({"tox.ini": "[pytest] ; k\nmarkers = x\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"tox_seitenvorschub_vor_abschnitt": ({"tox.ini": "[tox]" + chr(12) + "[pytest]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"setup_cfg_tool_pytest": ({"setup.cfg": "[tool:pytest]\n", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"pytest_toml_leer": ({"pytest.toml": "", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"punkt_pytest_ini_unterordner": ({"tests/.pytest.ini": "", "tests/test_a.py": TEST}, ["tests/test_a.py::test_a"]),
|
||||
"knoten_fehlt_teilweise": (
|
||||
{"tests/pytest.ini": INI, "tests/test_a.py": TEST},
|
||||
["tests/test_a.py::test_a", "gibtsnicht/test_x.py::test_a"],
|
||||
),
|
||||
"knoten_fehlen_alle": ({"tests/pytest.ini": INI, "tests/test_a.py": TEST}, ["gibtsnicht/test_x.py::test_a"]),
|
||||
"ordner_als_knoten": ({"tests/pytest.ini": INI, "tests/test_a.py": TEST}, ["tests"]),
|
||||
"testdatei_in_der_wurzel": ({"test_a.py": TEST, "conftest.py": CONFTEST}, ["test_a.py::test_a"]),
|
||||
"ohne_konfiguration": (
|
||||
{"tests/test_a.py": TEST, "conftest.py": CONFTEST, "tests/conftest.py": CONFTEST},
|
||||
["tests/test_a.py::test_a"],
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def baue(rahmen, dateien, fremd):
|
||||
wurzel = rahmen / "k"
|
||||
for rel, inhalt in dateien.items():
|
||||
pfad = wurzel / rel
|
||||
pfad.parent.mkdir(parents=True, exist_ok=True)
|
||||
pfad.write_text(inhalt, encoding="utf-8")
|
||||
if fremd:
|
||||
(rahmen / "pyproject.toml").write_text("[tool.pytest.ini_options]\naddopts = '--new-first'\n", encoding="utf-8")
|
||||
(rahmen / "conftest.py").write_text(CONFTEST, encoding="utf-8")
|
||||
(rahmen / "setup.py").write_text("", encoding="utf-8")
|
||||
return wurzel
|
||||
|
||||
|
||||
def sammeln(wurzel, zusatz, knoten, cflog):
|
||||
umgebung = dict(os.environ, CFLOG=str(cflog), PYTHONDONTWRITEBYTECODE="1")
|
||||
lauf = subprocess.run(
|
||||
[sys.executable, "-m", "pytest", "--co", "-q", "-p", "no:cacheprovider"] + zusatz + knoten,
|
||||
cwd=str(wurzel), env=umgebung, capture_output=True, text=True, timeout=120,
|
||||
)
|
||||
knotennamen = sorted(zeile for zeile in lauf.stdout.splitlines() if "::" in zeile)
|
||||
geladen = []
|
||||
if cflog.exists():
|
||||
geladen = sorted(os.path.relpath(z, str(wurzel)) for z in cflog.read_text(encoding="utf-8").split("\n") if z)
|
||||
cflog.unlink()
|
||||
return lauf.returncode, knotennamen, geladen
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 3:
|
||||
print("Aufruf: mutanten_lauf_probe_konfig.py <mutanten_lauf.py> <arbeitsordner>")
|
||||
return 2
|
||||
spec = importlib.util.spec_from_file_location("mutanten_lauf_unter_probe", sys.argv[1])
|
||||
laeufer = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(laeufer)
|
||||
from _pytest.config.findpaths import determine_setup
|
||||
|
||||
arbeit = Path(sys.argv[2])
|
||||
leere_konfig = arbeit / "plugin" / "pytest.ini"
|
||||
leere_konfig.parent.mkdir(parents=True, exist_ok=True)
|
||||
leere_konfig.write_text("", encoding="utf-8")
|
||||
cflog = arbeit / "cflog"
|
||||
abweichungen = 0
|
||||
for name, (dateien, knoten) in AUFBAUTEN.items():
|
||||
sauber = baue(arbeit / name / "sauber", dateien, False)
|
||||
fremd = baue(arbeit / name / "fremd", dateien, True)
|
||||
vorher = os.getcwd()
|
||||
os.chdir(sauber)
|
||||
try:
|
||||
rootdir, inipath, _, _ = determine_setup(
|
||||
inifile=None, override_ini=None, args=knoten, rootdir_cmd_arg=None, invocation_dir=sauber,
|
||||
)
|
||||
erwartet = (str(inipath) if inipath else None, str(rootdir))
|
||||
except Exception as exc:
|
||||
erwartet = ("Ausnahme " + type(exc).__name__, None)
|
||||
finally:
|
||||
os.chdir(vorher)
|
||||
konfig, rootdir_l = laeufer.pytest_konfig_bestimmen(sauber, knoten)
|
||||
bekommen = (str(konfig) if konfig else None, str(rootdir_l))
|
||||
ohne_c = sammeln(sauber, [], knoten, cflog)
|
||||
mit_c = sammeln(fremd, laeufer.pytest_konfig_argumente(fremd, knoten, leere_konfig), knoten, cflog)
|
||||
gleich_suche = erwartet == bekommen
|
||||
gleich_lauf = ohne_c == mit_c
|
||||
voraussetzung = name.startswith("knoten_fehl") or ohne_c[0] == 0
|
||||
if any(rel.endswith("conftest.py") for rel in dateien) and not ohne_c[2]:
|
||||
voraussetzung = False
|
||||
if not (gleich_suche and gleich_lauf and voraussetzung):
|
||||
abweichungen += 1
|
||||
print("{0:34s} suche={1} lauf={2} voraussetzung={3}".format(
|
||||
name, "gleich" if gleich_suche else "ABWEICHUNG", "gleich" if gleich_lauf else "ABWEICHUNG",
|
||||
"ok" if voraussetzung else "VERLETZT"))
|
||||
if not gleich_suche:
|
||||
print(" pytest: {0}".format(erwartet))
|
||||
print(" laeufer: {0}".format(bekommen))
|
||||
if not gleich_lauf:
|
||||
print(" ohne -c, sauber: {0}".format(ohne_c))
|
||||
print(" laeufer, fremd: {0}".format(mit_c))
|
||||
print("aufbauten: {0}, abweichungen: {1}".format(len(AUFBAUTEN), abweichungen))
|
||||
return 0 if abweichungen == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user