Files
ss-tools/backend/_batch_convert_defs.py
busya 4205618ee6 chore: eliminate all deprecation warnings from tests and linter
Warnings fixed:
- datetime.utcnow() → datetime.now(UTC) across 48+ files (src/ + tests/)
- datetime.utcnow (callback ref) → lambda: datetime.now(UTC) in model fields (18 files)
- Pydantic class Config → model_config = ConfigDict(...) (16 files)
- Pydantic .dict() → .model_dump() (8 files)
- ConfigDict(allow_population_by_field_name=True) → validate_by_name=True
- SQLAlchemy declarative_base() import path updated
- FastAPI on_event → lifespan context manager (app.py)
- Import sorting (ruff I001) auto-fixed across all files
- Fixed broken re-export chains that ruff F401 cleanup broke:
  _validate_bcp47: service.py now imports from dictionary_validation directly
  job_to_response: _job_routes.py and test imports from service_utils directly
  fetch_datasource_metadata: restored re-export in service.py
- Added missing TranslateJobService import in _job_routes.py (was deleted by F401)
- Added ConfigDict(protected_namespaces=()) for DashboardDatasetItem schema field
- pytest.ini: replaced deprecated importmode with asyncio_mode

All 440 tests pass with zero deprecation warnings.
2026-05-26 19:18:28 +03:00

107 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""
Batch convert legacy [DEF:...] / [/DEF:...] annotations to #region/#endregion format.
Handles Python, JS, and Svelte files. Removes [SECTION:...] / [/SECTION] markers.
"""
from pathlib import Path
import re
ROOT = Path(__file__).resolve().parent.parent
def process_file(filepath: Path) -> int:
"""Process a single file. Returns number of DEF blocks converted."""
with open(filepath, encoding='utf-8') as f:
text = f.read()
lines = text.split('\n')
result = []
blocks_converted = 0
i = 0
n = len(lines)
while i < n:
line = lines[i]
stripped = line.rstrip()
# Extract leading whitespace for indentation preservation
indent = re.match(r'^(\s*)', line).group(1)
# Check for [/DEF:Name] or [/DEF:Name:Type]
m = re.match(r'^\s*(#|//)\s*\[/DEF:(\w+(?:\.\w+)*)(?::\w+)?\]\s*$', stripped)
if m:
name = m.group(2)
result.append(f'{indent}# #endregion {name}')
blocks_converted += 1
i += 1
continue
# Check for [DEF:Name:Type]
m = re.match(r'^\s*(#|//)\s*\[DEF:(\w+(?:\.\w+)*):(\w+)\]\s*$', stripped)
if m:
name = m.group(2)
typ = m.group(3)
result.append(f'{indent}# #region {name} [C:2] [TYPE {typ}]')
blocks_converted += 1
i += 1
continue
# Skip [SECTION: ...] and [/SECTION] lines entirely
if re.match(r'^\s*(#|//)\s*\[SECTION:', stripped) or re.match(r'^\s*(#|//)\s*\[/SECTION\]', stripped):
i += 1
continue
# Fix @RELATION BELONGS_TO -> @RELATION BINDS_TO (not handled by earlier passes)
if re.match(r'^\s*(#|//)\s*@RELATION\s+BELONGS_TO\b', stripped):
line = re.sub(r'(@RELATION\s+)BELONGS_TO\b', r'\1BINDS_TO', line)
# Fix @RELATION CONTAINS -> @RELATION DEPENDS_ON (not handled by earlier passes)
if re.match(r'^\s*(#|//)\s*@RELATION\s+CONTAINS\b', stripped):
line = re.sub(r'(@RELATION\s+)CONTAINS\b', r'\1DEPENDS_ON', line)
# Pass through everything else
result.append(line)
i += 1
new_text = '\n'.join(result)
if new_text == text:
return 0
with open(filepath, 'w', encoding='utf-8') as f:
f.write(new_text)
return blocks_converted
def main():
total_blocks = 0
total_files = 0
# Process all test files and other files with DEF contracts
patterns = [
'backend/tests/**/*.py',
'frontend/src/**/__tests__/*.js',
'frontend/src/**/*.test.js',
'merge_spec.py',
]
for pattern in patterns:
for filepath in sorted(ROOT.glob(pattern)):
if '.venv' in str(filepath) or '__pycache__' in str(filepath):
continue
try:
blocks = process_file(filepath)
if blocks > 0:
total_files += 1
total_blocks += blocks
rel = filepath.relative_to(ROOT)
print(f" {rel}: {blocks} blocks converted")
except Exception as e:
print(f" ERROR {filepath}: {e}")
print(f"\nTotal: {total_files} files, {total_blocks} DEF blocks converted")
if __name__ == "__main__":
main()