124 lines
4.2 KiB
Python
124 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Fix missing slot imports in class files - Version 2.
|
|
|
|
This script properly handles YAML indentation when adding imports.
|
|
"""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
import yaml
|
|
|
|
SCHEMA_DIR = Path('/Users/kempersc/apps/glam/schemas/20251121/linkml')
|
|
CLASSES_DIR = SCHEMA_DIR / 'modules' / 'classes'
|
|
SLOTS_DIR = SCHEMA_DIR / 'modules' / 'slots'
|
|
|
|
# Get all available slot files
|
|
available_slots = {f.stem for f in SLOTS_DIR.glob('*.yaml')}
|
|
print(f"Available slot files: {len(available_slots)}")
|
|
|
|
def get_class_slots(content: str) -> set:
|
|
"""Extract slot names from a class file's slots: list."""
|
|
slots = set()
|
|
try:
|
|
data = yaml.safe_load(content)
|
|
if data and 'classes' in data:
|
|
for class_name, class_def in data['classes'].items():
|
|
if class_def and 'slots' in class_def:
|
|
for slot in class_def['slots']:
|
|
slots.add(slot)
|
|
except yaml.YAMLError:
|
|
return set()
|
|
return slots
|
|
|
|
def get_imported_slots(content: str) -> set:
|
|
"""Extract slot imports from a class file."""
|
|
imported = set()
|
|
# Match imports like: - ../slots/slot_name
|
|
pattern = r'-\s*\.\./slots/(\w+)'
|
|
for match in re.finditer(pattern, content):
|
|
imported.add(match.group(1))
|
|
return imported
|
|
|
|
def add_slot_imports(content: str, slots_to_add: set) -> str:
|
|
"""Add missing slot imports to content, preserving YAML structure."""
|
|
lines = content.split('\n')
|
|
result_lines = []
|
|
|
|
in_imports = False
|
|
imports_indent = ''
|
|
last_import_line_idx = -1
|
|
|
|
for i, line in enumerate(lines):
|
|
result_lines.append(line)
|
|
|
|
# Detect start of imports section
|
|
if re.match(r'^imports:\s*$', line):
|
|
in_imports = True
|
|
continue
|
|
|
|
# Inside imports section
|
|
if in_imports:
|
|
# Check if this is an import line (starts with - after whitespace)
|
|
match = re.match(r'^(\s*)-\s*', line)
|
|
if match:
|
|
imports_indent = match.group(1)
|
|
last_import_line_idx = len(result_lines)
|
|
elif line.strip() and not line.strip().startswith('#'):
|
|
# Non-empty, non-comment line that's not an import - we've left imports section
|
|
in_imports = False
|
|
|
|
# Insert new imports after the last import line
|
|
# Note: imports_indent can be '' (empty string) which is falsy but valid
|
|
if last_import_line_idx > 0:
|
|
# Create import lines with correct indentation
|
|
new_imports = [f"{imports_indent}- ../slots/{slot}" for slot in sorted(slots_to_add)]
|
|
# Insert after last import
|
|
for new_import in reversed(new_imports):
|
|
result_lines.insert(last_import_line_idx, new_import)
|
|
|
|
return '\n'.join(result_lines)
|
|
|
|
def main():
|
|
total_fixed = 0
|
|
files_modified = 0
|
|
errors = 0
|
|
|
|
for class_file in sorted(CLASSES_DIR.glob('*.yaml')):
|
|
content = class_file.read_text()
|
|
|
|
# Get slots used by this class
|
|
used_slots = get_class_slots(content)
|
|
if not used_slots:
|
|
continue
|
|
|
|
# Get already imported slots
|
|
imported_slots = get_imported_slots(content)
|
|
|
|
# Find slots that need to be imported
|
|
missing_imports = used_slots - imported_slots
|
|
|
|
# Filter to only slots that have files
|
|
valid_missing = missing_imports & available_slots
|
|
|
|
if valid_missing:
|
|
new_content = add_slot_imports(content, valid_missing)
|
|
|
|
# Verify the new content is valid YAML
|
|
try:
|
|
yaml.safe_load(new_content)
|
|
class_file.write_text(new_content)
|
|
print(f"OK: {class_file.name}: Added {len(valid_missing)} imports")
|
|
total_fixed += len(valid_missing)
|
|
files_modified += 1
|
|
except yaml.YAMLError as e:
|
|
print(f"ERROR: {class_file.name}: YAML error after modification: {e}")
|
|
errors += 1
|
|
|
|
print(f"\n=== Summary ===")
|
|
print(f"Files modified: {files_modified}")
|
|
print(f"Total imports added: {total_fixed}")
|
|
print(f"Errors: {errors}")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|