Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |