annator-command-center / scripts /migrate_print_to_logging.py
techprotrade's picture
Deploy ATOM FastAPI command center runtime (part 6)
383cb38 verified
Raw
History Blame Contribute Delete
6.7 kB
#!/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()