initial project structure

This commit is contained in:
hoffmeisterda104361 committed 2026-10-20 16:28:38 +02:00
1 parent b201730dfb
commit 252330ddc0
5 files changed
+161 -3

No files matched your search

+5 -1
View File
@@ -1,4 +1,8 @@
## Code Conventions ## Code Conventions
- max line length: 88 characters - max line length: 88 characters
- follow PEP 8 - follow PEP 8
- after creating code: execute "ruff check --fix" - after creating code: execute "ruff check --fix"
## Projectstructure
- source code lies in "src/matrixmania"
- test packages, files and functions should start with "test_"
- test code can be found in "test/test_matrixmania"
+146
View File
@@ -0,0 +1,146 @@
#!/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())
+8 -1
View File
@@ -1,3 +1,10 @@
[project] [project]
name = "matrixmania" name = "matrixmania"
version = "0.1.0" version = "0.1.0"
[project.optional-dependencies]
dev = ["pytest", "ruff", "pytest-cov"]
[tool.pytest.ini_options]
pythonpath = "src"
+1
View File
@@ -0,0 +1 @@
from .compute import matmul, transpose, rot_2d
+1 -1
View File
@@ -95,7 +95,7 @@ def transpose(matrix: Matrix) -> Matrix:
return [list(col) for col in zip(*matrix)] return [list(col) for col in zip(*matrix)]
def rotate(matrix: Matrix) -> Matrix: def rot_2d(matrix: Matrix) -> Matrix:
"""Return the matrix rotated 90 degrees clockwise. """Return the matrix rotated 90 degrees clockwise.
This is equivalent to transposing the matrix and then reversing This is equivalent to transposing the matrix and then reversing