Build a Self-Aware Code Complexity Analyzer That Rates Your Own Shame Level

devdotdev.dev May 22, 2026
Source

Create a tool that analyzes Python source code and assigns it a 'shame score' based on various code smell metrics, then generates brutally honest feedback about the developer's choices. The tool should parse code, detect anti-patterns, and deliver verdicts wrapped in passive-aggressive commentary. from typing import List, Dict, Tuple, Optional, Union, Any from abc import ABC, abstractmethod from dataclasses import dataclass from enum import Enum import re # This class represents the severity of a code smell. It is a severity level. class SeverityLevel(Enum): MINOR = 1 MODERATE = 2 SEVERE = 3 CATASTROPHIC = 4 # This dataclass holds information about a code smell detection result. @dataclass class CodeSmellDetection: smell_type: str line_number: int severity: SeverityLevel message: str shame_points: int # This is an abstract base class for code smell detectors. class CodeSmellDetectorBase(ABC): def init(self, code: str) -> None: self.code = code self.lines = code.split('n') self.detections: List[CodeSmellDetection] = [] @abstractmethod def detect(self) -> List[CodeSmellDetection]: pass # This detector finds overly long functions in your code. class LongFunctionDetector(CodeSmellDetectorBase): MAX_FUNCTION_LENGTH = 20 def detect(self) -> List[CodeSmellDetection]: function_pattern = r'^sdefs+(w+)s(' current_function_start = None current_function_name = None for idx, line in enumerate(self.lines): if re.match(function_pattern, line): if current_function_start is not None: length = idx - current_function_start if length > self.MAX_FUNCTION_LENGTH: self.detections.append(CodeSmellDetection( smell_type='LONG_FUNCTION', line_number=current_function_start + 1, severity=SeverityLevel.MODERATE, message=f'Function "{current_function_name}" is {length} lines long. Consider breaking it up.', shame_points=15 )) current_function_start = idx match = re.match(function_pattern, line) current_function_name = match.group(1) if match else 'unknown' return self.detections # This detector identifies deeply nested control structures in code. class NestingDepthDetector(CodeSmellDetectorBase): MAX_NESTING_DEPTH = 3 def detect(self) -> List[CodeSmellDetection]: for idx, line in enumerate(self.lines): indent_level = len(line) - len(line.lstrip()) nesting_depth = indent_level // 4 if nesting_depth > self.MAX_NESTING_DEPTH: self.detections.append(CodeSmellDetection( smell_type='DEEP_NESTING', line_number=idx + 1, severity=SeverityLevel.MODERATE, message=f'Nesting depth of {nesting_depth} detected. Your code is experiencing vertigo.', shame_points=20 )) return self.detections # This detector flags variable names that are too cryptic or single-letter. class BadNamingDetector(CodeSmellDetectorBase): def detect(self) -> List[CodeSmellDetection]: variable_pattern = r'b([a-z])s*=' for idx, line in enumerate(self.lines): matches = re.finditer(variable_pattern, line) for match in matches: var_name = match.group(1) if var_name not in ['x', 'y', 'z', 'i', 'j', 'k']: self.detections.append(CodeSmellDetection( smell_type='BAD_NAMING', line_number=idx + 1, severity=SeverityLevel.MINOR, message=f'Variable "{var_name}" is too vague. Future you will hate current you.', shame_points=5 )) return self.detections # This is the main orchestrator class that coordinates all detectors. class ShameScoreCalculator: def init(self, source_code: str) -> None: self.source_code = source_code self.detectors: List[CodeSmellDetectorBase] = [ LongFunctionDetector(source_code), NestingDepthDetector(source_code), BadNamingDetector(source_code) ] self.all_detections: List[CodeSmellDetection] = [] def calculate(self) -> Tuple[int, List[CodeSmellDetection]]: # Execute all detectors and aggregate results. for detector in self.detectors: self.all_detections.extend(detector.detect()) # Calculate total shame score by summing shame points. total_shame = sum(detection.shame_points for detection in self.all_detections) return total_shame, self.all_detections def generate_report(self) -> str: # Generate a human-readable shame report. shame_score, detections = self.calculate() report = f"=== CODE SHAME REPORT ===nTotal Shame Score: {shame_score}nn" if shame_score == 0: report += "Somehow, miraculously, your code is acceptable.n" elif shame_score < 50: report += "Your code is a little rough around the edges.n" elif shame_score < 100: report += "Your code makes reviewers weep quietly at their desks.n" else: report += "Please consider a career change. Perhaps agriculture.n" report += "nDetailed Findings:n" for detection in detections: report += f" [{detection.severity.name}] Line {detection.line_number}: {detection.message}n" return report # Main execution block if __name__ == "__main__": sample_code = ''' def process_data(d): for x in range(10): if x > 5: for y in range(10): if y % 2 == 0: for z in range(10): if z < 5: print(x, y, z) return d ''' calculator = ShameScoreCalculator(sample_code) print(calculator.generate_report()) Code Review 1. Lines 1-6. We're importing ABC and abstractmethod for a single abstract method, then defining SeverityLevel as an Enum with numeric values. The Enum is reasonable, but we could've just used strings and avoided the import overhead. Also, why does SeverityLevel have numeric values when we never do numeric comparisons on it? 2. Lines 14-19. The @dataclass decorator is perfectly fine, but the comment above it saying 'This dataclass holds information about a code smell detection result' restates what the code already makes obvious. Comments should explain 'why', not 'what'. 3. Lines 21-28. The entire CodeSmellDetectorBase abstract class with type hints like List[CodeSmellDetection] feels like enterprise architecture theater here. We could just have plain detector functions or a simple base class without the abstract method forcing subclasses to implement detect(). This violates YAGNI principle, or at minimum over-applies the strategy pattern where composition would suffice. 4. Lines 30-55. The LongFunctionDetector regex logic is close but not actually correct for real Python parsing. It would match function definitions but loses state and doesn't handle nested functions or decorators properly. We're also calling .group(1) on match that might be None after the conditional check, which could raise an AttributeError. Plus, iterating line-by-line won't catch if a function spans multiple lines with a line break after def foo(. 5. Lines 76-79. The BadNamingDetector regex b([a-z])s*= will match single lowercase letters, but then the condition if var_name not in ['x', 'y', 'z', 'i', 'j', 'k'] excludes the most common single-letter variables used in loops. This detector basically never fires for legitimate use cases. 6. Lines 85-99. We're storing all_detections as instance state, then calling self.calculate() inside generate_report() which recalculates everything. This means if someone calls calculate() twice, the second call appends to already-populated all_detections (no clearing), so shame scores stack. Classic mutable state bug hiding in plain sight. 7. Lines 103-110. The shame score thresholds (0, 50, 100) and accompanying messages are hardcoded strings that should probably live in a config or constants file. More importantly, this branching logic screams 'I should be a lookup table or strategy', but we already over-engineered with that abstract base class, so now we're under-engineering this part instead. Inconsistent architectural choices throughout. 8. Lines 119-128. The sample code in main has a deeply nested loop situation (4 levels of indentation / nesting depth 4), but our NestingDepthDetector uses indent_level // 4 which will detect this correctly. However, we never actually test or validate the regex patterns work on this sample, so dead code paths in the detectors might never surface.

Discussion in the ATmosphere

Loading comments...