"Update template files and add windows fixes"
This commit is contained in:
194
backend/app/utils/windows_fixes.py
Normal file
194
backend/app/utils/windows_fixes.py
Normal file
@@ -0,0 +1,194 @@
|
||||
"""
|
||||
Windows-spezifische Fixes für Thread- und Socket-Probleme
|
||||
Behebt bekannte Issues mit Flask Auto-Reload auf Windows.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import threading
|
||||
import time
|
||||
import atexit
|
||||
from typing import List, Callable
|
||||
from utils.logging_config import get_logger
|
||||
|
||||
# Logger für Windows-Fixes
|
||||
windows_logger = get_logger("windows_fixes")
|
||||
|
||||
class WindowsThreadManager:
|
||||
"""
|
||||
Verwaltet Threads und deren ordnungsgemäße Beendigung auf Windows.
|
||||
Behebt Socket-Fehler beim Flask Auto-Reload.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.managed_threads: List[threading.Thread] = []
|
||||
self.cleanup_functions: List[Callable] = []
|
||||
self.shutdown_event = threading.Event()
|
||||
self._lock = threading.Lock()
|
||||
self._is_shutting_down = False
|
||||
|
||||
# Signal-Handler nur auf Windows registrieren
|
||||
if os.name == 'nt':
|
||||
self._register_signal_handlers()
|
||||
|
||||
def _register_signal_handlers(self):
|
||||
"""Registriert Windows-spezifische Signal-Handler."""
|
||||
try:
|
||||
signal.signal(signal.SIGINT, self._signal_handler)
|
||||
signal.signal(signal.SIGTERM, self._signal_handler)
|
||||
# Windows-spezifisches SIGBREAK
|
||||
if hasattr(signal, 'SIGBREAK'):
|
||||
signal.signal(signal.SIGBREAK, self._signal_handler)
|
||||
windows_logger.debug("✅ Windows Signal-Handler registriert")
|
||||
except Exception as e:
|
||||
windows_logger.warning(f"⚠️ Signal-Handler konnten nicht registriert werden: {str(e)}")
|
||||
|
||||
def _signal_handler(self, sig, frame):
|
||||
"""Signal-Handler für ordnungsgemäßes Shutdown."""
|
||||
if not self._is_shutting_down:
|
||||
windows_logger.warning(f"🛑 Windows Signal {sig} empfangen - initiiere Shutdown")
|
||||
self.shutdown_all()
|
||||
|
||||
def register_thread(self, thread: threading.Thread):
|
||||
"""Registriert einen Thread für ordnungsgemäße Beendigung."""
|
||||
with self._lock:
|
||||
if thread not in self.managed_threads:
|
||||
self.managed_threads.append(thread)
|
||||
windows_logger.debug(f"📝 Thread {thread.name} registriert")
|
||||
|
||||
def register_cleanup_function(self, func: Callable):
|
||||
"""Registriert eine Cleanup-Funktion."""
|
||||
with self._lock:
|
||||
if func not in self.cleanup_functions:
|
||||
self.cleanup_functions.append(func)
|
||||
windows_logger.debug(f"📝 Cleanup-Funktion registriert")
|
||||
|
||||
def shutdown_all(self):
|
||||
"""Beendet alle verwalteten Threads und führt Cleanup durch."""
|
||||
if self._is_shutting_down:
|
||||
return
|
||||
|
||||
with self._lock:
|
||||
self._is_shutting_down = True
|
||||
windows_logger.info("🔄 Starte Windows Thread-Shutdown...")
|
||||
|
||||
# Shutdown-Event setzen
|
||||
self.shutdown_event.set()
|
||||
|
||||
# Cleanup-Funktionen ausführen
|
||||
for func in self.cleanup_functions:
|
||||
try:
|
||||
windows_logger.debug(f"🧹 Führe Cleanup-Funktion aus: {func.__name__}")
|
||||
func()
|
||||
except Exception as e:
|
||||
windows_logger.error(f"❌ Fehler bei Cleanup-Funktion {func.__name__}: {str(e)}")
|
||||
|
||||
# Threads beenden
|
||||
active_threads = [t for t in self.managed_threads if t.is_alive()]
|
||||
if active_threads:
|
||||
windows_logger.info(f"⏳ Warte auf {len(active_threads)} aktive Threads...")
|
||||
|
||||
for thread in active_threads:
|
||||
try:
|
||||
windows_logger.debug(f"🔄 Beende Thread: {thread.name}")
|
||||
thread.join(timeout=5)
|
||||
|
||||
if thread.is_alive():
|
||||
windows_logger.warning(f"⚠️ Thread {thread.name} konnte nicht ordnungsgemäß beendet werden")
|
||||
else:
|
||||
windows_logger.debug(f"✅ Thread {thread.name} erfolgreich beendet")
|
||||
except Exception as e:
|
||||
windows_logger.error(f"❌ Fehler beim Beenden von Thread {thread.name}: {str(e)}")
|
||||
|
||||
windows_logger.info("✅ Windows Thread-Shutdown abgeschlossen")
|
||||
|
||||
# Globale Instanz
|
||||
_windows_thread_manager = None
|
||||
|
||||
def get_windows_thread_manager() -> WindowsThreadManager:
|
||||
"""Gibt die globale Instanz des Windows Thread-Managers zurück."""
|
||||
global _windows_thread_manager
|
||||
if _windows_thread_manager is None:
|
||||
_windows_thread_manager = WindowsThreadManager()
|
||||
return _windows_thread_manager
|
||||
|
||||
def fix_windows_socket_issues():
|
||||
"""
|
||||
Anwendung von Windows-spezifischen Socket-Fixes.
|
||||
Verhindert Socket-Fehler beim Flask Auto-Reload.
|
||||
"""
|
||||
if os.name != 'nt':
|
||||
return
|
||||
|
||||
try:
|
||||
# Socket-Wiederverwendung aktivieren
|
||||
import socket
|
||||
socket.socket._bind_orig = socket.socket.bind
|
||||
|
||||
def patched_bind(self, address):
|
||||
"""Gepatchte bind-Methode mit SO_REUSEADDR."""
|
||||
try:
|
||||
self.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
except:
|
||||
pass
|
||||
return self._bind_orig(address)
|
||||
|
||||
socket.socket.bind = patched_bind
|
||||
windows_logger.debug("✅ Windows Socket-Patches angewendet")
|
||||
|
||||
except Exception as e:
|
||||
windows_logger.warning(f"⚠️ Socket-Patches konnten nicht angewendet werden: {str(e)}")
|
||||
|
||||
def setup_windows_environment():
|
||||
"""
|
||||
Richtet die Windows-Umgebung für bessere Flask-Kompatibilität ein.
|
||||
"""
|
||||
if os.name != 'nt':
|
||||
return
|
||||
|
||||
try:
|
||||
# Umgebungsvariablen für bessere Windows-Kompatibilität
|
||||
os.environ['PYTHONIOENCODING'] = 'utf-8'
|
||||
os.environ['PYTHONUTF8'] = '1'
|
||||
|
||||
# Thread-Pool-Größe optimieren
|
||||
if 'WERKZEUG_SERVER_FD' not in os.environ:
|
||||
# Nur im Hauptprozess
|
||||
os.environ['WERKZEUG_RUN_MAIN'] = 'true'
|
||||
|
||||
windows_logger.debug("✅ Windows-Umgebung optimiert")
|
||||
|
||||
except Exception as e:
|
||||
windows_logger.warning(f"⚠️ Windows-Umgebung konnte nicht optimiert werden: {str(e)}")
|
||||
|
||||
def is_flask_reloader_process() -> bool:
|
||||
"""
|
||||
Prüft, ob der aktuelle Prozess der Flask-Reloader-Prozess ist.
|
||||
"""
|
||||
return os.environ.get('WERKZEUG_RUN_MAIN') != 'true'
|
||||
|
||||
def apply_all_windows_fixes():
|
||||
"""
|
||||
Wendet alle Windows-spezifischen Fixes an.
|
||||
"""
|
||||
if os.name != 'nt':
|
||||
windows_logger.debug("⏭️ Keine Windows-Fixes nötig (nicht Windows)")
|
||||
return
|
||||
|
||||
windows_logger.info("🔧 Wende Windows-spezifische Fixes an...")
|
||||
|
||||
setup_windows_environment()
|
||||
fix_windows_socket_issues()
|
||||
|
||||
# Thread-Manager initialisieren
|
||||
thread_manager = get_windows_thread_manager()
|
||||
|
||||
# Atexit-Handler registrieren
|
||||
atexit.register(thread_manager.shutdown_all)
|
||||
|
||||
windows_logger.info("✅ Alle Windows-Fixes erfolgreich angewendet")
|
||||
|
||||
# Automatisch Windows-Fixes beim Import anwenden
|
||||
if os.name == 'nt':
|
||||
apply_all_windows_fixes()
|
Reference in New Issue
Block a user