import os models_dir = r"c:\dev\git\python\homelab-automation-api-v2\app\models" def fix_file(filepath): with open(filepath, 'r', encoding='utf-8') as f: content = f.read() # Enable future annotations content = content.replace("# # from __future__", "from __future__") content = content.replace("# from __future__", "from __future__") if "from __future__ import annotations" not in content: content = "from __future__ import annotations\n" + content # Massive replacement of Union/Optional style types inside Mapped for Python 3.14 workaround # We remove the '| None' and ensure nullable=True in mapped_column lines = content.split('\n') new_lines = [] for line in lines: if "Mapped[" in line and ("| None" in line or "Optional[" in line): # Remove | None new_line = line.replace(" | None", "").replace("| None", "").replace("Optional[", "").replace("]]", "]") # If mapped_column exists, ensure nullable=True if "mapped_column(" in new_line and "nullable=" not in new_line: if "mapped_column()" in new_line: new_line = new_line.replace("mapped_column()", "mapped_column(nullable=True)") else: # Find the closing parenthesis of mapped_column # Example: mapped_column(String) or mapped_column(DateTime(timezone=True)) # We replace the last ')' with ', nullable=True)' idx = new_line.rfind(')') if idx != -1: new_line = new_line[:idx] + ", nullable=True" + new_line[idx:] new_lines.append(new_line) else: new_lines.append(line) with open(filepath, 'w', encoding='utf-8') as f: f.write('\n'.join(new_lines)) for filename in os.listdir(models_dir): if filename.endswith(".py") and filename != "__init__.py": print(f"Fixing {filename}...") fix_file(os.path.join(models_dir, filename))