diff --git a/AGENTS.md b/AGENTS.md index 6ed4f9d..c0fd994 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,6 @@ # Regeln für dieses Projekt - Code nach PEP 8, Zeilenlänge maximal 88 Zeichen. - Jede Funktion und Methode hat einen Docstring und Type Hints. -- Vor jedem Commit: `ruff check` und `ruff format`. \ No newline at end of file +- Vor jedem Commit: `ruff check` und `ruff format`. +- Quellcode liegt unter "src/matrixmania/", Tests unter "test/test_matrixmania/" +- Testpakete, Testdateien und Testfunktionen beginnen mit "test_" \ No newline at end of file diff --git a/install_dependencies.py b/install_dependencies.py deleted file mode 100644 index 5a20e39..0000000 --- a/install_dependencies.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python3 -"""Installiert die Abhängigkeiten aus pyproject.toml in die aktuelle Python-Umgebung. - -Ersatz für "Install requirements" aus PyCharm, wenn das Projekt eine pyproject.toml -statt einer requirements.txt hat. Kein Build, keine besondere Projektstruktur nötig, -nur Standardbibliothek (Python >= 3.11). - -Benutzung: - In PyCharm: Rechtsklick auf diese Datei -> Run 'install_dependencies' - Im Terminal (venv aktiv): python install_dependencies.py - -Ohne Parameter wird ALLES installiert: - [project].dependencies + alle [project.optional-dependencies] + alle [dependency-groups] - -Abweichendes Verhalten: - python install_dependencies.py --extra dev nur Basis + Extra "dev" - python install_dependencies.py --group test nur Basis + Gruppe "test" - python install_dependencies.py --no-main --group dev - python install_dependencies.py --list nur anzeigen, nichts installieren - python install_dependencies.py --write requirements.txt schreiben statt installieren - python install_dependencies.py --write -o reqs.txt -""" -from __future__ import annotations - -import sys - -if sys.version_info < (3, 11): - sys.exit("Dieses Skript braucht Python 3.11 oder neuer (tomllib). " - f"Aktuell: {sys.version.split()[0]}") - -import argparse -import subprocess -import tempfile -import tomllib -from pathlib import Path - -PROJECT_DIR = Path(__file__).resolve().parent - - -def resolve_group(groups: dict, name: str, seen: set[str] | None = None) -> list[str]: - """Löst eine Dependency-Group inkl. {include-group = "..."} rekursiv auf.""" - seen = set() if seen is None else seen - if name in seen: - raise ValueError(f"Zyklische include-group: {name}") - if name not in groups: - raise KeyError(f"Dependency-Group '{name}' nicht gefunden") - seen.add(name) - result: list[str] = [] - for entry in groups[name]: - if isinstance(entry, str): - result.append(entry) - elif isinstance(entry, dict) and "include-group" in entry: - result.extend(resolve_group(groups, entry["include-group"], seen)) - return result - - -def collect(data: dict, extras: list[str], groups: list[str], no_main: bool) -> list[str]: - project = data.get("project", {}) - optional = project.get("optional-dependencies", {}) - dep_groups = data.get("dependency-groups", {}) - deps: list[str] = [] - - if not no_main: - deps.extend(project.get("dependencies", [])) - for extra in extras: - if extra not in optional: - raise KeyError(f"Optional-Dependency '{extra}' nicht gefunden") - deps.extend(optional[extra]) - for group in groups: - deps.extend(resolve_group(dep_groups, group)) - - # Duplikate entfernen, Reihenfolge behalten - return list(dict.fromkeys(d.strip() for d in deps if d.strip())) - - -def main() -> int: - p = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) - p.add_argument("-p", "--pyproject", type=Path, default=PROJECT_DIR / "pyproject.toml", - help="Pfad zur pyproject.toml (Standard: neben diesem Skript)") - p.add_argument("--extra", action="append", default=None, - help="nur diese optional-dependencies (mehrfach möglich)") - p.add_argument("--group", action="append", default=None, - help="nur diese dependency-groups (mehrfach möglich)") - p.add_argument("--no-main", action="store_true", help="[project].dependencies weglassen") - p.add_argument("--list", action="store_true", help="Abhängigkeiten nur anzeigen") - p.add_argument("--write", action="store_true", help="requirements-Datei schreiben statt installieren") - p.add_argument("-o", "--output", type=Path, default=PROJECT_DIR / "requirements.txt", - help="Zieldatei für --write") - args = p.parse_args() - - if not args.pyproject.exists(): - print(f"{args.pyproject} nicht gefunden", file=sys.stderr) - return 1 - data = tomllib.loads(args.pyproject.read_text(encoding="utf-8")) - all_extras = list(data.get("project", {}).get("optional-dependencies", {})) - all_groups = list(data.get("dependency-groups", {})) - - # Ohne --extra/--group: alles. Mit einem davon: nur das Gewählte. - selective = args.extra is not None or args.group is not None - extras = (args.extra or []) if selective else all_extras - groups = (args.group or []) if selective else all_groups - - try: - deps = collect(data, extras, groups, args.no_main) - except (KeyError, ValueError) as e: - print(f"Fehler: {e.args[0]}", file=sys.stderr) - print(f" Vorhandene --extra: {', '.join(all_extras) or '(keine)'}", file=sys.stderr) - print(f" Vorhandene --group: {', '.join(all_groups) or '(keine)'}", file=sys.stderr) - return 1 - - parts = ([] if args.no_main else ["Basis"]) + [f"extra:{e}" for e in extras] + [f"group:{g}" for g in groups] - print(f"Auswahl: {', '.join(parts) or '(nichts)'}") - - if args.list: - print("\n".join(deps) or "(keine Abhängigkeiten)") - return 0 - - content = f"# Automatisch erzeugt aus {args.pyproject.name} – nicht von Hand bearbeiten\n" + "\n".join(deps) + "\n" - - if args.write: - args.output.write_text(content, encoding="utf-8") - print(f"{len(deps)} Abhängigkeiten -> {args.output}") - return 0 - - if not deps: - print("Keine Abhängigkeiten gefunden – nichts zu tun.") - return 0 - - if sys.prefix == sys.base_prefix: - print("WARNUNG: Es ist kein virtuelles Environment aktiv – es wird in die globale " - f"Python-Installation installiert:\n {sys.prefix}", file=sys.stderr) - if sys.stdin.isatty() and input("Trotzdem fortfahren? [j/N] ").strip().lower() not in ("j", "ja", "y", "yes"): - return 1 - - with tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False, encoding="utf-8") as tmp: - tmp.write(content) - tmp_path = Path(tmp.name) - try: - print(f"Installiere {len(deps)} Abhängigkeiten in {sys.prefix}\n") - return subprocess.call([sys.executable, "-m", "pip", "install", "-r", str(tmp_path)]) - finally: - tmp_path.unlink(missing_ok=True) - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/ pyproject.toml b/pyproject.toml similarity index 60% rename from pyproject.toml rename to pyproject.toml index 6d9b5ed..be2cc56 100644 --- a/ pyproject.toml +++ b/pyproject.toml @@ -4,4 +4,7 @@ version = "0.1.0" dependencies = ["tabulate", "ruff", "pygame"] [project.optional-dependencies] -dev = ["pytest", "ruff", "pytest-cov"] \ No newline at end of file +dev = ["pytest", "ruff", "pytest-cov"] + +[tool.pytest.ini_options] +pythonpath = "src" \ No newline at end of file diff --git a/Moon.py b/src/matrixmania/Moon.py similarity index 93% rename from Moon.py rename to src/matrixmania/Moon.py index 563105b..66c2b05 100644 --- a/Moon.py +++ b/src/matrixmania/Moon.py @@ -1,7 +1,7 @@ import pygame -from compute import matmul, rot_2D, transpose -from game import Game +from src.matrixmania.compute import matmul, rot_2D, transpose +from src.matrixmania.game import Game EARTH_RADIUS = 30 MOON_RADIUS = 10 diff --git a/src/matrixmania/__init__.py b/src/matrixmania/__init__.py new file mode 100644 index 0000000..2dbdec6 --- /dev/null +++ b/src/matrixmania/__init__.py @@ -0,0 +1 @@ +from .compute import matmul, transpose, rot_2D diff --git a/apollo.py b/src/matrixmania/apollo.py similarity index 96% rename from apollo.py rename to src/matrixmania/apollo.py index c52ee23..4b2556a 100644 --- a/apollo.py +++ b/src/matrixmania/apollo.py @@ -1,6 +1,6 @@ import pygame -from compute import matmul, rot_2D, transpose +from src.matrixmania.compute import matmul, rot_2D, transpose from Moon import Moon RED = (255, 0, 0) diff --git a/compute.py b/src/matrixmania/compute.py similarity index 100% rename from compute.py rename to src/matrixmania/compute.py diff --git a/game.py b/src/matrixmania/game.py similarity index 100% rename from game.py rename to src/matrixmania/game.py