Files
KI-Cluster-Roadmap/00_Globale_Richtlinien/Entworfener_Code/app/start.py
2025-11-12 11:49:38 +01:00

161 lines
4.7 KiB
Python

from typing import Any, Dict, Optional, List
import argparse
import logging
import logging.config
import os
from pathlib import Path
import sys
import uvicorn
import yaml
from fastapi import FastAPI
# Basisverzeichnis: .../Entworfener_Code/app
BASE_DIR: Path = Path(__file__).resolve().parent
CODE_DIR: Path = BASE_DIR / "code" # .../app/code
CONFIG_DIR: Path = BASE_DIR / "config" # .../app/config
# Sicherstellen, dass .../app/code importierbar ist (bevorzugt neue Struktur)
if str(CODE_DIR) not in sys.path:
sys.path.insert(0, str(CODE_DIR))
# Jetzt Import aus neuer Struktur (app/code/app/main.py)
from app.main import create_app # noqa: E402
APP_CONFIG_ENV = "APP_CONFIG_PATH"
def load_yaml(path: Path) -> Dict[str, Any]:
"""
Lädt eine YAML-Datei und gibt den Inhalt als Dictionary zurück.
"""
with path.open("r", encoding="utf-8") as f:
return yaml.safe_load(f) or {}
def setup_logging(cfg: Dict[str, Any]) -> None:
"""
Initialisiert das Logging anhand der in app/config/logging.yaml definierten Konfiguration.
Stellt sicher, dass das Log-Verzeichnis existiert.
"""
logging_cfg = cfg.get("logging", {}) or {}
paths_cfg = cfg.get("paths", {}) or {}
log_dir = Path(paths_cfg.get("log_dir", "logs"))
if not log_dir.is_absolute():
log_dir = BASE_DIR / log_dir
log_dir.mkdir(parents=True, exist_ok=True)
logging_config_file = logging_cfg.get("config_file", "config/logging.yaml")
logging_path = Path(logging_config_file)
if not logging_path.is_absolute():
logging_path = BASE_DIR / logging_path
if logging_path.exists():
with logging_path.open("r", encoding="utf-8") as f:
config_dict = yaml.safe_load(f) or {}
logging.config.dictConfig(config_dict)
else:
logging.basicConfig(
level=getattr(logging, (logging_cfg.get("level") or "INFO").upper(), logging.INFO),
format="%(asctime)s | %(levelname)s | %(name)s | %(filename)s:%(lineno)d | %(message)s",
)
logging.getLogger(__name__).debug("Logging initialisiert. Log-Verzeichnis: %s", str(log_dir))
def load_config_from_env() -> Dict[str, Any]:
"""
Lädt die Anwendungskonfiguration aus der durch APP_CONFIG_PATH angegebenen Datei.
Fällt zurück auf app/config/config.yaml.
"""
env_path = os.environ.get(APP_CONFIG_ENV)
if env_path:
cfg_path = Path(env_path)
else:
cfg_path = CONFIG_DIR / "config.yaml"
if not cfg_path.exists():
print(f"Konfigurationsdatei nicht gefunden: {cfg_path}", file=sys.stderr)
return {}
return load_yaml(cfg_path)
def app_factory() -> FastAPI:
"""
Uvicorn-Factory für Reload-Betrieb (importierbare App-Fabrik).
Liest Konfiguration, initialisiert Logging und erzeugt die FastAPI-App.
"""
cfg = load_config_from_env()
setup_logging(cfg)
app = create_app(cfg)
return app
def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
"""
CLI-Argumente parsen.
"""
parser = argparse.ArgumentParser(description="Startet den FastAPI-Server (app/code-Struktur).")
parser.add_argument(
"--config",
type=str,
default=str(CONFIG_DIR / "config.yaml"),
help="Pfad zur Konfigurationsdatei (YAML).",
)
return parser.parse_args(argv)
def main(argv: Optional[List[str]] = None) -> None:
"""
Einstiegspunkt: lädt Konfiguration, initialisiert Logging, startet Uvicorn.
"""
args = parse_args(argv)
# Resolve config path
config_path = Path(args.config)
if not config_path.is_absolute():
config_path = (BASE_DIR / args.config).resolve()
if not config_path.exists():
print(f"Konfigurationsdatei nicht gefunden: {config_path}", file=sys.stderr)
sys.exit(2)
# Set env var für Factory
os.environ[APP_CONFIG_ENV] = str(config_path)
cfg = load_yaml(config_path)
setup_logging(cfg)
app_cfg = cfg.get("app", {}) or {}
host = str(app_cfg.get("host", "0.0.0.0"))
port = int(app_cfg.get("port", 8000))
reload_enabled = bool(app_cfg.get("reload", False))
logger = logging.getLogger("start")
if reload_enabled:
# Für Reload muss eine importierbare App-Factory übergeben werden
logger.info("Starte Uvicorn mit Reload (Factory-Modus, app/code).")
module_name = Path(__file__).stem # "start"
uvicorn.run(
f"{module_name}:app_factory",
factory=True,
host=host,
port=port,
reload=True,
)
else:
logger.info("Starte Uvicorn ohne Reload (app/code).")
app = create_app(cfg)
uvicorn.run(
app,
host=host,
port=port,
reload=False,
)
if __name__ == "__main__":
main()