VidSimplify / manimator /utils /code_validator.py
Adityahulk
Restoring repo state for deployment
6fc3143
raw
history blame
5.31 kB
"""
Code Validator - Validates generated Manim code before rendering
"""
import ast
import re
from typing import List, Tuple, Optional
class CodeValidator:
"""Validates Manim code for common issues"""
def validate(self, code: str) -> Tuple[bool, List[str]]:
"""
Validate code and return validation result.
Args:
code: Python code to validate
Returns:
Tuple of (is_valid, list_of_errors)
"""
errors = []
# 1. Syntax validation
syntax_valid, syntax_errors = self._validate_syntax(code)
if not syntax_valid:
errors.extend(syntax_errors)
return (False, errors) # Can't continue if syntax is invalid
# 2. Import validation
import_errors = self._validate_imports(code)
errors.extend(import_errors)
# 3. Structure validation
structure_errors = self._validate_structure(code)
errors.extend(structure_errors)
# 4. Variable/color validation
variable_errors = self._validate_variables(code)
errors.extend(variable_errors)
# 5. Voiceover validation
voiceover_errors = self._validate_voiceover(code)
errors.extend(voiceover_errors)
is_valid = len(errors) == 0
return (is_valid, errors)
def _validate_syntax(self, code: str) -> Tuple[bool, List[str]]:
"""Validate Python syntax"""
try:
ast.parse(code)
return (True, [])
except SyntaxError as e:
return (False, [f"Syntax error at line {e.lineno}: {e.msg}"])
except Exception as e:
return (False, [f"Parse error: {str(e)}"])
def _validate_imports(self, code: str) -> List[str]:
"""Check for required imports"""
errors = []
# Check for manim import
if 'from manim import *' not in code and 'import manim' not in code:
errors.append("Missing required import: from manim import *")
# Check for VoiceoverScene import (from our custom implementation)
if 'from manimator.scene.voiceover_scene import VoiceoverScene' not in code:
errors.append("Missing required import: from manimator.scene.voiceover_scene import VoiceoverScene")
# Check for voiceover service import
if 'from manimator.services.voiceover import SimpleElevenLabsService' not in code:
errors.append("Missing required import: from manimator.services.voiceover import SimpleElevenLabsService")
# Check for pathlib import (needed for cache_dir)
if 'from pathlib import Path' not in code:
errors.append("Missing required import: from pathlib import Path")
return errors
def _validate_structure(self, code: str) -> List[str]:
"""Validate code structure (class, construct method, etc.)"""
errors = []
# Check for Scene class
if 'class' not in code:
errors.append("No class definition found")
return errors
# Check for VoiceoverScene inheritance
if 'VoiceoverScene' not in code:
errors.append("Scene class must inherit from VoiceoverScene")
# Check for construct method
if 'def construct(self):' not in code and 'def construct(self):' not in code:
errors.append("No construct() method found")
return errors
def _validate_variables(self, code: str) -> List[str]:
"""Check for undefined variables and colors"""
errors = []
# Check for undefined color constants
undefined_colors = re.findall(r'\b(ORANGE|RED|BLUE|GREEN|YELLOW|PURPLE|PINK|TEAL|GRAY)_[A-Z]\b', code)
if undefined_colors:
errors.append(f"Undefined color constants found: {', '.join(set(undefined_colors))}")
return errors
def _validate_voiceover(self, code: str) -> List[str]:
"""Validate voiceover setup"""
errors = []
# Check for voiceover service setup
if 'set_speech_service' not in code:
errors.append("Voiceover service not initialized (missing set_speech_service)")
# Check for SimpleElevenLabsService (the actual service we use)
if 'SimpleElevenLabsService' not in code:
errors.append("SimpleElevenLabsService not imported or used")
# Check for voiceover blocks
if 'with self.voiceover' not in code:
errors.append("No voiceover blocks found (use 'with self.voiceover(text=...)' for narration)")
return errors
def get_fixable_errors(self, errors: List[str]) -> List[str]:
"""Identify errors that can be auto-fixed"""
fixable_patterns = [
'Missing required import',
'Undefined color constants',
'Voiceover service not initialized',
]
fixable = []
for error in errors:
for pattern in fixable_patterns:
if pattern in error:
fixable.append(error)
break
return fixable