Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Print to Logger Migration Script | |
| Automatically replaces print() statements with logger.info/error/debug calls. | |
| Usage: | |
| python scripts/migrate_print_to_logging.py [--dry-run] [--file path/to/file.py] | |
| Options: | |
| --dry-run: Show changes without applying them | |
| --file: Migrate a single file (default: migrate all non-test files) | |
| """ | |
| import argparse | |
| import ast | |
| from pathlib import Path | |
| import re | |
| from typing import List, Tuple | |
| class PrintToLoggerMigrator: | |
| """Migrate print() statements to logger calls""" | |
| def __init__(self, dry_run: bool = False): | |
| self.dry_run = dry_run | |
| self.changes_made = 0 | |
| def migrate_file(self, filepath: str) -> Tuple[bool, List[str]]: | |
| """Migrate a single Python file""" | |
| try: | |
| with open(filepath, 'r') as f: | |
| content = f.read() | |
| changes = [] | |
| original_content = content | |
| # Check if file already has logging | |
| has_logger = 'logger = logging.getLogger(__name__)' in content | |
| # Add logger import if not present | |
| if not has_logger: | |
| content, import_changes = self._add_logger_import(content) | |
| changes.extend(import_changes) | |
| # Replace print() statements | |
| content, print_changes = self._replace_print_statements(content) | |
| changes.extend(print_changes) | |
| # Write changes | |
| if content != original_content and changes: | |
| if self.dry_run: | |
| print(f"\n{'='*60}") | |
| print(f"File: {filepath}") | |
| print(f"{'='*60}") | |
| for change in changes: | |
| print(f" - {change}") | |
| else: | |
| with open(filepath, 'w') as f: | |
| f.write(content) | |
| self.changes_made += 1 | |
| return True, changes | |
| return False, ["No print() statements found"] | |
| except Exception as e: | |
| print(f"Error migrating {filepath}: {e}") | |
| return False, [f"Error: {e}"] | |
| def _add_logger_import(self, content: str) -> Tuple[str, List[str]]: | |
| """Add logging import and logger initialization""" | |
| changes = [] | |
| # Check if logging is already imported | |
| if 'import logging' in content: | |
| # Just add logger initialization after imports | |
| if 'logger = logging.getLogger(__name__)' not in content: | |
| # Find the end of imports | |
| import_end = content.find('\n\n') | |
| if import_end == -1: | |
| import_end = 0 | |
| # Insert logger initialization | |
| content = content[:import_end] + '\n\nlogger = logging.getLogger(__name__)' + content[import_end:] | |
| changes.append("Added logger initialization") | |
| else: | |
| # Add import logging at the top | |
| content = 'import logging\n\n' + content | |
| # Add logger initialization | |
| if 'logger = logging.getLogger(__name__)' not in content: | |
| content = 'import logging\n\nlogger = logging.getLogger(__name__)\\n\\n' + content[content.find('\\n')+1:] | |
| changes.append("Added logging import and logger initialization") | |
| return content, changes | |
| def _replace_print_statements(self, content: str) -> Tuple[str, List[str]]: | |
| """Replace print() statements with logger calls""" | |
| changes = [] | |
| # Pattern 1: print(string) -> logger.info(string) | |
| # Pattern 2: print(f"...") -> logger.info(f"...") | |
| # Pattern 3: print("error:", e) -> logger.error(f"error: {e}") | |
| # Simple print statements | |
| patterns = [ | |
| # print("message") -> logger.info("message") | |
| (r'print\("([^"]+)"\)', r'logger.info("\1")'), | |
| # print('message') -> logger.info('message') | |
| (r"print\('([^']+)'\)", r"logger.info('\1')"), | |
| # print(f"...") -> logger.info(f"...") | |
| (r'print\(f"([^"]+)"\)', r'logger.info(f"\1")'), | |
| (r"print\(f'([^']+)'\)", r"logger.info(f'\1')"), | |
| # print(variable) -> logger.info(str(variable)) | |
| (r'print\((\w+)\)', r'logger.info(str(\1))'), | |
| ] | |
| for pattern, replacement in patterns: | |
| matches = re.findall(pattern, content) | |
| if matches: | |
| content = re.sub(pattern, replacement, content) | |
| changes.append(f"Replaced {len(matches)} print() statement(s)") | |
| return content, changes | |
| def find_python_files() -> List[str]: | |
| """Find all Python files (excluding tests)""" | |
| base_dir = Path(__file__).parent.parent | |
| python_files = [] | |
| # Find all Python files | |
| for py_file in base_dir.rglob('*.py'): | |
| # Exclude test files, __init__.py, and venv | |
| if ( | |
| not py_file.name.startswith('test_') | |
| and py_file.name != '__init__.py' | |
| and 'venv' not in py_file.parts | |
| and '.venv' not in py_file.parts | |
| and 'site-packages' not in py_file.parts | |
| ): | |
| python_files.append(str(py_file)) | |
| return sorted(python_files) | |
| def main(): | |
| parser = argparse.ArgumentParser(description='Migrate print() to logger') | |
| parser.add_argument('--dry-run', action='store_true', help='Show changes without applying') | |
| parser.add_argument('--file', type=str, help='Migrate a single file') | |
| args = parser.parse_args() | |
| migrator = PrintToLoggerMigrator(dry_run=args.dry_run) | |
| if args.file: | |
| files = [args.file] | |
| else: | |
| files = find_python_files() | |
| print(f"Found {len(files)} Python files to check\n") | |
| results = { | |
| 'success': 0, | |
| 'no_changes': 0, | |
| 'errors': 0 | |
| } | |
| for filepath in files: | |
| success, changes = migrator.migrate_file(filepath) | |
| if success: | |
| results['success'] += 1 | |
| if not args.dry_run: | |
| print(f"✓ {filepath}") | |
| for change in changes: | |
| print(f" - {change}") | |
| elif 'No print()' in changes[0]: | |
| results['no_changes'] += 1 | |
| else: | |
| results['errors'] += 1 | |
| print(f"✗ {filepath}: {changes[0]}") | |
| print(f"\n{'='*60}") | |
| print("Migration Summary:") | |
| print(f" Files migrated: {results['success']}") | |
| print(f" Files unchanged: {results['no_changes']}") | |
| print(f" Files with errors: {results['errors']}") | |
| print(f"{'='*60}") | |
| if args.dry_run: | |
| print("\nDRY RUN MODE - No changes were applied") | |
| print("Run without --dry-run to apply changes") | |
| if __name__ == '__main__': | |
| main() | |