From c2663c0801ae1e2344f76033c50851256468d0c1 Mon Sep 17 00:00:00 2001 From: dschlueter Date: Mon, 22 Jun 2026 12:42:19 +0200 Subject: [PATCH] =?UTF-8?q?Refactor=20(Welle=20C):=20create=5Fapp()-Factor?= =?UTF-8?q?y=20einf=C3=BChren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - App-Aufbau in create_app(config) gekapselt; Blueprint-Importe und -Registrierung, CSRF, DB-Init, Pfade/Version dort. - app = create_app() am Modulende -> 'from app import app, db' und die bestehenden 80 Tests bleiben unverändert grün. - create_app(config) erlaubt isolierte Test-Apps (z.B. TESTING, In-Memory-DB) ohne den FLASK_TESTING-Env-Hack. - DB-Check/Lektion-Laden in _load_lessons_or_exit() ausgelagert, weiterhin nur im Normalbetrieb. - Obsoletes app.py-E402-Ignore aus ruff.toml entfernt (Importe jetzt funktionslokal). Co-Authored-By: Claude Sonnet 4.6 --- app.py | 108 +++++++++++++++++++++++++++++------------------------- ruff.toml | 2 - 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/app.py b/app.py index 1b380a1..5ac535f 100644 --- a/app.py +++ b/app.py @@ -17,54 +17,8 @@ logging.basicConfig( ) logger = logging.getLogger(__name__) -# Flask App erstellen -app = Flask(__name__) -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///typewriter.db' -app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -app.secret_key = helpers.get_or_create_secret_key() - -# CSRF-Schutz aktivieren -csrf = CSRFProtect(app) - -# Datenbank initialisieren -db.init_app(app) - -# Initialisiere Pfade für helpers -with app.app_context(): - helpers.initialize_paths(app.root_path) - -# VERSION aus helpers laden -VERSION = helpers.get_version() - -# Globale Template-Variablen -@app.context_processor -def inject_globals(): - """Stellt globale Variablen für alle Templates bereit""" - return {'APP_VERSION': VERSION} - - -# Blueprints importieren und registrieren -from routes.main import main_bp -from routes.training import training_bp -from routes.statistics import statistics_bp -from routes.settings import settings_bp -from routes.session import session_bp -from routes.utils import utils_bp, shutdown - -app.register_blueprint(main_bp) -app.register_blueprint(training_bp) -app.register_blueprint(statistics_bp) -app.register_blueprint(settings_bp) -app.register_blueprint(session_bp) -app.register_blueprint(utils_bp) - -# CSRF-Ausnahme gezielt nur für die Shutdown-Route. -# /repair_json und /recreate_lessons bleiben damit CSRF-geschützt. -csrf.exempt(shutdown) - - -# Datenbank-Status prüfen und Lektionen laden (nur wenn nicht im Test-Modus) -if not app.config.get('TESTING', False) and os.environ.get('FLASK_TESTING') != 'true': +def _load_lessons_or_exit(app: Flask) -> None: + """Prüft die Datenbank und lädt die Lektionen (nur im Normalbetrieb).""" with app.app_context(): db_ready = helpers.check_database_status(db.engine) @@ -85,10 +39,66 @@ if not app.config.get('TESTING', False) and os.environ.get('FLASK_TESTING') != ' logger.info(f"{len(helpers.lessons)} Lektionen geladen") +def create_app(config: dict | None = None) -> Flask: + """Application-Factory: erstellt und konfiguriert die Flask-App. + + Args: + config: Optionale Config-Overrides (z.B. für Tests). + """ + app = Flask(__name__) + app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///typewriter.db' + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + app.secret_key = helpers.get_or_create_secret_key() + + if config: + app.config.update(config) + + # CSRF-Schutz und Datenbank initialisieren + csrf = CSRFProtect(app) + db.init_app(app) + + # Pfade und Version + helpers.initialize_paths(app.root_path) + app.config['APP_VERSION'] = helpers.get_version() + + @app.context_processor + def inject_globals(): + """Stellt globale Variablen für alle Templates bereit""" + return {'APP_VERSION': app.config['APP_VERSION']} + + # Blueprints importieren und registrieren + from routes.main import main_bp + from routes.training import training_bp + from routes.statistics import statistics_bp + from routes.settings import settings_bp + from routes.session import session_bp + from routes.utils import utils_bp, shutdown + + app.register_blueprint(main_bp) + app.register_blueprint(training_bp) + app.register_blueprint(statistics_bp) + app.register_blueprint(settings_bp) + app.register_blueprint(session_bp) + app.register_blueprint(utils_bp) + + # CSRF-Ausnahme gezielt nur für die Shutdown-Route. + # /repair_json und /recreate_lessons bleiben damit CSRF-geschützt. + csrf.exempt(shutdown) + + # Datenbank-Status prüfen und Lektionen laden (nur im Normalbetrieb) + if not app.config.get('TESTING', False) and os.environ.get('FLASK_TESTING') != 'true': + _load_lessons_or_exit(app) + + return app + + +app = create_app() + + if __name__ == '__main__': logger.info("=" * 50) logger.info("Tipptrainer gestartet") - logger.info(f"Version: {VERSION}") + logger.info(f"Version: {app.config['APP_VERSION']}") logger.info(f"Lektionen geladen: {len(helpers.lessons)}") logger.info("Debug-Info verfügbar unter: http://127.0.0.1:5000/debug") logger.info("=" * 50) diff --git a/ruff.toml b/ruff.toml index d2a0937..b118e36 100644 --- a/ruff.toml +++ b/ruff.toml @@ -15,5 +15,3 @@ ignore = ["E501"] [lint.per-file-ignores] # Tests setzen FLASK_TESTING bewusst VOR dem App-Import (E402) "tests/*" = ["E402"] -# app.py registriert Blueprints bewusst nach App-/CSRF-Setup (E402) -"app.py" = ["E402"]