Download app.py from dejun-huang/Code-Complexity-Analyzer: direct link, hf CLI and curl.
- Browser
- Download file 8.7 kB
-
https://huggingface.co/spaces/dejun-huang/Code-Complexity-Analyzer/resolve/main/app.py
- Command line
-
hf download hf://spaces/dejun-huang/Code-Complexity-Analyzer/app.py
-
curl -L -o app.py https://huggingface.co/spaces/dejun-huang/Code-Complexity-Analyzer/resolve/main/app.py
8.7 kB
| """ | |
| Code Complexity Analyzer MCP Server | |
| This MCP server analyzes Python code complexity and provides insights | |
| about code quality, maintainability, and potential refactoring opportunities. | |
| Tags: building-mcp-track-enterprise | |
| """ | |
| import ast | |
| import re | |
| from typing import Dict, List, Tuple | |
| import gradio as gr | |
| def calculate_cyclomatic_complexity(code: str) -> Dict[str, any]: | |
| """ | |
| Calculate cyclomatic complexity of Python code. | |
| Args: | |
| code: Python source code as a string | |
| Returns: | |
| Dictionary with complexity metrics | |
| """ | |
| try: | |
| tree = ast.parse(code) | |
| except SyntaxError as e: | |
| return {"error": f"Syntax error in code: {str(e)}"} | |
| complexity = 1 # Base complexity | |
| # Count decision points that increase complexity | |
| for node in ast.walk(tree): | |
| if isinstance(node, (ast.If, ast.While, ast.For, ast.ExceptHandler)): | |
| complexity += 1 | |
| elif isinstance(node, ast.BoolOp): | |
| complexity += len(node.values) - 1 | |
| elif isinstance(node, (ast.ListComp, ast.DictComp, ast.SetComp, ast.GeneratorExp)): | |
| complexity += 1 | |
| # Determine complexity level | |
| if complexity <= 10: | |
| level = "Low (Simple)" | |
| recommendation = "Code is easy to understand and maintain." | |
| elif complexity <= 20: | |
| level = "Moderate" | |
| recommendation = "Consider breaking into smaller functions." | |
| elif complexity <= 50: | |
| level = "High" | |
| recommendation = "Refactoring strongly recommended. Break into smaller, focused functions." | |
| else: | |
| level = "Very High (Critical)" | |
| recommendation = "Immediate refactoring required. Code is difficult to test and maintain." | |
| return { | |
| "cyclomatic_complexity": complexity, | |
| "complexity_level": level, | |
| "recommendation": recommendation | |
| } | |
| def analyze_code_metrics(code: str) -> Dict[str, any]: | |
| """ | |
| Analyze various code metrics including LOC, functions, classes, etc. | |
| Args: | |
| code: Python source code as a string | |
| Returns: | |
| Dictionary with code metrics | |
| """ | |
| try: | |
| tree = ast.parse(code) | |
| except SyntaxError as e: | |
| return {"error": f"Syntax error in code: {str(e)}"} | |
| lines = code.split('\n') | |
| loc = len(lines) | |
| # Count non-empty, non-comment lines | |
| sloc = sum(1 for line in lines if line.strip() and not line.strip().startswith('#')) | |
| # Count comments | |
| comments = sum(1 for line in lines if line.strip().startswith('#')) | |
| # Count functions and classes | |
| functions = sum(1 for node in ast.walk(tree) if isinstance(node, ast.FunctionDef)) | |
| classes = sum(1 for node in ast.walk(tree) if isinstance(node, ast.ClassDef)) | |
| # Calculate comment ratio | |
| comment_ratio = (comments / sloc * 100) if sloc > 0 else 0 | |
| return { | |
| "lines_of_code": loc, | |
| "source_lines_of_code": sloc, | |
| "comment_lines": comments, | |
| "comment_ratio_percent": round(comment_ratio, 2), | |
| "functions": functions, | |
| "classes": classes | |
| } | |
| def detect_code_smells(code: str) -> List[str]: | |
| """ | |
| Detect common code smells in Python code. | |
| Args: | |
| code: Python source code as a string | |
| Returns: | |
| List of detected code smells | |
| """ | |
| smells = [] | |
| try: | |
| tree = ast.parse(code) | |
| except SyntaxError: | |
| return ["Syntax error prevents analysis"] | |
| # Check for long functions (>50 lines) | |
| for node in ast.walk(tree): | |
| if isinstance(node, ast.FunctionDef): | |
| if hasattr(node, 'end_lineno') and hasattr(node, 'lineno'): | |
| func_lines = node.end_lineno - node.lineno | |
| if func_lines > 50: | |
| smells.append(f"Long function '{node.name}' ({func_lines} lines)") | |
| # Check for too many parameters | |
| if isinstance(node, ast.FunctionDef): | |
| param_count = len(node.args.args) | |
| if param_count > 5: | |
| smells.append(f"Function '{node.name}' has too many parameters ({param_count})") | |
| # Check for deeply nested code (>4 levels) | |
| if isinstance(node, (ast.If, ast.For, ast.While)): | |
| depth = sum(1 for parent in ast.walk(tree) | |
| if isinstance(parent, (ast.If, ast.For, ast.While))) | |
| if depth > 4: | |
| smells.append("Deeply nested code blocks detected") | |
| break | |
| # Check for duplicate code patterns (simple check) | |
| lines = [line.strip() for line in code.split('\n') if line.strip()] | |
| if len(lines) != len(set(lines)): | |
| duplicate_count = len(lines) - len(set(lines)) | |
| if duplicate_count > 3: | |
| smells.append(f"Possible duplicate code: {duplicate_count} duplicate lines") | |
| if not smells: | |
| smells.append("No obvious code smells detected!") | |
| return smells | |
| def full_code_analysis(code: str) -> str: | |
| """ | |
| Perform complete code analysis combining all metrics. | |
| Args: | |
| code: Python source code as a string | |
| Returns: | |
| Formatted analysis report | |
| """ | |
| if not code.strip(): | |
| return "Please provide Python code to analyze." | |
| complexity = calculate_cyclomatic_complexity(code) | |
| metrics = analyze_code_metrics(code) | |
| smells = detect_code_smells(code) | |
| # Build report | |
| report = "# Code Analysis Report\n\n" | |
| # Complexity section | |
| report += "## Complexity Analysis\n" | |
| if "error" in complexity: | |
| report += f"Error: {complexity['error']}\n\n" | |
| else: | |
| report += f"- **Cyclomatic Complexity**: {complexity['cyclomatic_complexity']}\n" | |
| report += f"- **Complexity Level**: {complexity['complexity_level']}\n" | |
| report += f"- **Recommendation**: {complexity['recommendation']}\n\n" | |
| # Metrics section | |
| report += "## Code Metrics\n" | |
| if "error" in metrics: | |
| report += f"Error: {metrics['error']}\n\n" | |
| else: | |
| report += f"- **Total Lines**: {metrics['lines_of_code']}\n" | |
| report += f"- **Source Lines**: {metrics['source_lines_of_code']}\n" | |
| report += f"- **Comment Lines**: {metrics['comment_lines']}\n" | |
| report += f"- **Comment Ratio**: {metrics['comment_ratio_percent']}%\n" | |
| report += f"- **Functions**: {metrics['functions']}\n" | |
| report += f"- **Classes**: {metrics['classes']}\n\n" | |
| # Code smells section | |
| report += "## Code Smells Detected\n" | |
| for smell in smells: | |
| report += f"- {smell}\n" | |
| return report | |
| # Create Gradio interface | |
| with gr.Blocks(title="Code Complexity Analyzer MCP Server") as demo: | |
| gr.Markdown(""" | |
| # Code Complexity Analyzer MCP Server | |
| Analyze Python code complexity and quality metrics. This MCP server provides: | |
| - Cyclomatic complexity calculation | |
| - Code metrics (LOC, comments, functions, classes) | |
| - Code smell detection | |
| - Refactoring recommendations | |
| **Category**: Enterprise MCP Server | |
| **Tags**: building-mcp-track-enterprise | |
| """) | |
| with gr.Tab("Full Analysis"): | |
| code_input = gr.Code( | |
| label="Python Code", | |
| language="python", | |
| lines=20, | |
| value="# Paste your Python code here\ndef example():\n pass" | |
| ) | |
| analyze_btn = gr.Button("Analyze Code", variant="primary") | |
| analysis_output = gr.Markdown(label="Analysis Report") | |
| analyze_btn.click( | |
| fn=full_code_analysis, | |
| inputs=code_input, | |
| outputs=analysis_output | |
| ) | |
| with gr.Tab("Complexity Only"): | |
| complexity_input = gr.Code(label="Python Code", language="python", lines=15) | |
| complexity_btn = gr.Button("Calculate Complexity") | |
| complexity_output = gr.JSON(label="Complexity Metrics") | |
| complexity_btn.click( | |
| fn=calculate_cyclomatic_complexity, | |
| inputs=complexity_input, | |
| outputs=complexity_output | |
| ) | |
| with gr.Tab("Code Metrics"): | |
| metrics_input = gr.Code(label="Python Code", language="python", lines=15) | |
| metrics_btn = gr.Button("Analyze Metrics") | |
| metrics_output = gr.JSON(label="Code Metrics") | |
| metrics_btn.click( | |
| fn=analyze_code_metrics, | |
| inputs=metrics_input, | |
| outputs=metrics_output | |
| ) | |
| with gr.Tab("Code Smells"): | |
| smells_input = gr.Code(label="Python Code", language="python", lines=15) | |
| smells_btn = gr.Button("Detect Code Smells") | |
| smells_output = gr.JSON(label="Detected Code Smells") | |
| smells_btn.click( | |
| fn=detect_code_smells, | |
| inputs=smells_input, | |
| outputs=smells_output | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(mcp_server=True, server_name="0.0.0.0", server_port=7860) | |