""" PYTHON CODE ANALYZER & IMPROVER Analyzes Python code, learns patterns, generates better Python code """ import ast import re import logging from typing import Dict, List, Optional, Tuple, Any import json import os logger = logging.getLogger(__name__) class PythonAnalyzer: """Analyzes and learns from Python code""" def __init__(self): self.learned_patterns = {} self.code_quality_metrics = {} self.best_practices = self._initialize_best_practices() self.load_learned_patterns() def _initialize_best_practices(self) -> Dict: """Initialize Python best practices database""" return { 'naming_conventions': { 'functions': r'^[a-z_][a-z0-9_]*$', 'classes': r'^[A-Z][a-zA-Z0-9]*$', 'constants': r'^[A-Z_][A-Z0-9_]*$', 'variables': r'^[a-z_][a-z0-9_]*$', }, 'anti_patterns': [ {'pattern': r'import \*', 'message': 'Avoid wildcard imports'}, {'pattern': r'except:', 'message': 'Avoid bare except clauses'}, {'pattern': r'== True', 'message': 'Use "if value:" instead of "== True"'}, {'pattern': r'== False', 'message': 'Use "if not value:" instead of "== False"'}, {'pattern': r'== None', 'message': 'Use "if value is None:" instead'}, ], 'style_checks': [ 'Use 4 spaces for indentation', 'Use list comprehensions instead of loops', 'Use context managers (with statements)', 'Add docstrings to all functions', 'Use type hints for function parameters', ] } def analyze_python_code(self, code: str) -> Dict: """Comprehensive analysis of Python code""" analysis = { 'syntax_valid': False, 'structure': {}, 'quality_score': 0.0, 'issues': [], 'suggestions': [], 'complexity': 0, } try: tree = ast.parse(code) analysis['syntax_valid'] = True # Extract structure analysis['structure'] = self._extract_structure(tree) # Check for issues analysis['issues'] = self._check_code_issues(code) # Generate suggestions analysis['suggestions'] = self._generate_suggestions(code, tree) # Calculate complexity analysis['complexity'] = self._calculate_complexity(tree) # Quality score analysis['quality_score'] = self._calculate_quality_score(analysis) # Learn from this code self._learn_from_code(code, analysis) except SyntaxError as e: analysis['syntax_valid'] = False analysis['issues'].append(f"Syntax Error: {str(e)}") except Exception as e: analysis['issues'].append(f"Analysis Error: {str(e)}") return analysis def _extract_structure(self, tree: ast.AST) -> Dict: """Extract code structure""" structure = { 'functions': [], 'classes': [], 'imports': [], 'global_vars': [] } for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): structure['functions'].append({ 'name': node.name, 'args': len(node.args.args), 'lines': node.end_lineno - node.lineno if node.end_lineno else 0, 'docstring': ast.get_docstring(node) is not None }) elif isinstance(node, ast.ClassDef): structure['classes'].append({ 'name': node.name, 'methods': len([n for n in node.body if isinstance(n, ast.FunctionDef)]), 'has_init': any(isinstance(n, ast.FunctionDef) and n.name == '__init__' for n in node.body) }) elif isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom): structure['imports'].append(self._extract_import(node)) return structure def _extract_import(self, node) -> str: """Extract import statement""" if isinstance(node, ast.Import): return ', '.join(alias.name for alias in node.names) elif isinstance(node, ast.ImportFrom): module = node.module or '' names = ', '.join(alias.name for alias in node.names) return f"from {module} import {names}" return "" def _check_code_issues(self, code: str) -> List[str]: """Check for code issues""" issues = [] # Check anti-patterns for anti_pattern in self.best_practices['anti_patterns']: if re.search(anti_pattern['pattern'], code): issues.append(anti_pattern['message']) # Check naming conventions functions = re.findall(r'def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(', code) for func_name in functions: if not re.match(self.best_practices['naming_conventions']['functions'], func_name): issues.append(f"Function '{func_name}' doesn't follow naming convention") # Check for missing docstrings if 'def ' in code and '"""' not in code and "'''" not in code: issues.append("Missing docstrings in functions") return issues def _generate_suggestions(self, code: str, tree: ast.AST) -> List[str]: """Generate improvement suggestions""" suggestions = [] # Check for loop patterns that could be list comprehensions for node in ast.walk(tree): if isinstance(node, ast.For): suggestions.append("Consider using list comprehension instead of for loop") # Check for proper exception handling for node in ast.walk(tree): if isinstance(node, ast.ExceptHandler) and node.type is None: suggestions.append("Specify exception type instead of bare except") # Check for type hints (Python 3.5+) for node in ast.walk(tree): if isinstance(node, ast.FunctionDef) and not node.returns: suggestions.append(f"Add return type hint to function '{node.name}'") return suggestions def _calculate_complexity(self, tree: ast.AST) -> int: """Calculate cyclomatic complexity""" complexity = 1 for node in ast.walk(tree): if isinstance(node, (ast.If, ast.While, ast.For, ast.ExceptHandler)): complexity += 1 return complexity def _calculate_quality_score(self, analysis: Dict) -> float: """Calculate overall code quality score (0-100)""" score = 100.0 # Subtract points for issues score -= len(analysis['issues']) * 10 # Subtract points for complexity if analysis['complexity'] > 5: score -= (analysis['complexity'] - 5) * 2 # Add bonus for structure structure = analysis['structure'] if structure['functions']: score += min(5, len(structure['functions'])) if structure['classes']: score += min(5, len(structure['classes'])) return max(0, min(100, score)) def _learn_from_code(self, code: str, analysis: Dict): """Learn patterns from Python code""" # Store patterns for future reference key = f"pattern_{hash(code) % 10000}" self.learned_patterns[key] = { 'code_snippet': code[:500], 'quality': analysis['quality_score'], 'complexity': analysis['complexity'], 'structure': analysis['structure'], 'issues': analysis['issues'] } def improve_python_code(self, code: str) -> Dict: """Generate improved version of Python code""" analysis = self.analyze_python_code(code) if not analysis['syntax_valid']: return { 'success': False, 'error': 'Code has syntax errors', 'original': code } improved = code improvements = [] # Apply improvements based on analysis # 1. Fix common patterns if 'Avoid bare except clauses' in analysis['issues']: improved = improved.replace('except:', 'except Exception:') improvements.append('Fixed bare except clause') # 2. Add docstrings if missing if 'Missing docstrings in functions' in analysis['issues']: improved = self._add_docstrings(improved) improvements.append('Added docstrings') # 3. Suggest list comprehensions if any('list comprehension' in s for s in analysis['suggestions']): improved = self._suggest_list_comprehensions(improved) improvements.append('Suggested list comprehensions') # 4. Add type hints improved = self._add_type_hints(improved) improvements.append('Added type hints') return { 'success': True, 'original': code, 'improved': improved, 'analysis': analysis, 'improvements': improvements, 'quality_before': 0, 'quality_after': analysis['quality_score'] } def _add_docstrings(self, code: str) -> str: """Add docstrings to functions""" improved = code functions = re.findall(r'def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\((.*?)\):', code) for func_name, args in functions: if '"""' not in code: docstring = f'\n """{func_name}.\n \n Args:\n {args}\n """\n ' improved = improved.replace( f'def {func_name}({args}):', f'def {func_name}({args}):{docstring}' ) return improved def _add_type_hints(self, code: str) -> str: """Add type hints to functions""" improved = code # Simple type hint addition (can be enhanced) improved = re.sub( r'def\s+(\w+)\s*\(([^)]*)\)\s*:', r'def \1(\2) -> Any:', improved ) return improved def _suggest_list_comprehensions(self, code: str) -> str: """Suggest list comprehensions instead of loops""" # This would require more complex parsing return code def generate_python_from_requirement(self, requirement: str) -> Dict: """Generate Python code from natural language requirement""" templates = { 'function': '''def {name}({args}): """{description}""" # TODO: Implement function pass''', 'class': '''class {name}: """Class for {description}""" def __init__(self): """Initialize {name}""" pass def method(self): """Class method""" pass''', 'loop': '''for item in items: # Process item print(item)''', 'request': '''import requests def fetch_data(url): """Fetch data from URL""" try: response = requests.get(url) response.raise_for_status() return response.json() except requests.RequestException as e: print(f"Error: {e}") return None''', 'file_operation': '''def read_file(filepath): """Read file safely""" try: with open(filepath, 'r', encoding='utf-8') as f: return f.read() except FileNotFoundError: print(f"File not found: {filepath}") return None''', } # Select appropriate template requirement_lower = requirement.lower() selected_template = 'function' # Default if 'class' in requirement_lower: selected_template = 'class' elif 'loop' in requirement_lower or 'iterate' in requirement_lower: selected_template = 'loop' elif 'request' in requirement_lower or 'fetch' in requirement_lower or 'api' in requirement_lower: selected_template = 'request' elif 'file' in requirement_lower or 'read' in requirement_lower or 'write' in requirement_lower: selected_template = 'file_operation' code_template = templates[selected_template] return { 'success': True, 'code': code_template, 'template_used': selected_template, 'requirement': requirement, 'quality': 'template', 'needs_customization': True } def load_learned_patterns(self): """Load previously learned patterns""" try: pattern_file = 'noahski_data/python_patterns.json' if os.path.exists(pattern_file): with open(pattern_file, 'r', encoding='utf-8') as f: self.learned_patterns = json.load(f) logger.info(f"Loaded {len(self.learned_patterns)} Python patterns") except Exception as e: logger.error(f"Error loading patterns: {e}") def save_learned_patterns(self): """Save learned patterns for future use""" try: os.makedirs('noahski_data', exist_ok=True) pattern_file = 'noahski_data/python_patterns.json' with open(pattern_file, 'w', encoding='utf-8') as f: json.dump(self.learned_patterns, f, indent=2) logger.info(f"Saved {len(self.learned_patterns)} Python patterns") except Exception as e: logger.error(f"Error saving patterns: {e}") # Global instance _python_analyzer = None def get_python_analyzer() -> PythonAnalyzer: """Get or create global Python analyzer""" global _python_analyzer if _python_analyzer is None: _python_analyzer = PythonAnalyzer() return _python_analyzer