Summary
Add a new pythonic_check tool that detects non-idiomatic Python patterns and suggests Pythonic alternatives. This validates the shared core architecture and expands the code quality suite.
Parent Epic
Part of: Evolve into Python Code Quality MCP
Depends On
- Refactor shared Astroid utilities into core module
Patterns to Detect
Loop Anti-patterns
| Anti-pattern |
Pythonic Alternative |
for i in range(len(items)): |
for i, item in enumerate(items): |
for key in d.keys(): |
for key in d: |
for i in range(len(a)): ... a[i] ... b[i] |
for x, y in zip(a, b): |
Comparison Anti-patterns
| Anti-pattern |
Pythonic Alternative |
if x == True: / if x == False: |
if x: / if not x: |
if x == None: |
if x is None: |
type(x) == SomeClass |
isinstance(x, SomeClass) |
if len(items) == 0: |
if not items: |
Resource Management
| Anti-pattern |
Pythonic Alternative |
f = open(); ...; f.close() |
with open() as f: |
| Manual lock acquire/release |
with lock: |
Collection Building
| Anti-pattern |
Pythonic Alternative |
l = []; for x in y: l.append(f(x)) |
[f(x) for x in y] |
d = {}; for x in y: d[k] = v |
{k: v for x in y} |
| Loop with string concatenation |
"".join(...) |
Mutable Defaults
| Anti-pattern |
Pythonic Alternative |
def foo(items=[]): |
def foo(items=None): items = items or [] |
def foo(d={}): |
def foo(d=None): d = d or {} |
Other
| Anti-pattern |
Pythonic Alternative |
if x: return True else: return False |
return x / return bool(x) |
Manual __enter__/__exit__ in simple cases |
@contextmanager |
lambda x: func(x) |
Just func |
MCP Tool Interface
{
"name": "pythonic_check",
"description": "Analyze Python code for non-idiomatic patterns and suggest Pythonic alternatives",
"inputSchema": {
"type": "object",
"properties": {
"file_path": {"type": "string"},
"source_code": {"type": "string"}
}
}
}
Example Output
{
"issues": [
{
"tool": "pythonic",
"category": "non_idiomatic_loop",
"severity": "warning",
"message": "Use enumerate() instead of range(len())",
"line": 42,
"column": 4,
"suggestion": "for i, item in enumerate(items):",
"code_snippet": "for i in range(len(items)):"
}
],
"summary": {
"total_issues": 5,
"by_category": {
"non_idiomatic_loop": 2,
"mutable_default": 1,
"comparison_to_none": 2
}
}
}
Implementation
- Create
src/workshop_mcp/tools/pythonic_check/
- Define patterns in
patterns.py
- Implement checker using core Astroid utilities
- Register tool in MCP server
- Add tests in
tests/test_pythonic_checker.py
Acceptance Criteria
Summary
Add a new
pythonic_checktool that detects non-idiomatic Python patterns and suggests Pythonic alternatives. This validates the shared core architecture and expands the code quality suite.Parent Epic
Part of: Evolve into Python Code Quality MCP
Depends On
Patterns to Detect
Loop Anti-patterns
for i in range(len(items)):for i, item in enumerate(items):for key in d.keys():for key in d:for i in range(len(a)): ... a[i] ... b[i]for x, y in zip(a, b):Comparison Anti-patterns
if x == True:/if x == False:if x:/if not x:if x == None:if x is None:type(x) == SomeClassisinstance(x, SomeClass)if len(items) == 0:if not items:Resource Management
f = open(); ...; f.close()with open() as f:with lock:Collection Building
l = []; for x in y: l.append(f(x))[f(x) for x in y]d = {}; for x in y: d[k] = v{k: v for x in y}"".join(...)Mutable Defaults
def foo(items=[]):def foo(items=None): items = items or []def foo(d={}):def foo(d=None): d = d or {}Other
if x: return True else: return Falsereturn x/return bool(x)__enter__/__exit__in simple cases@contextmanagerlambda x: func(x)funcMCP Tool Interface
{ "name": "pythonic_check", "description": "Analyze Python code for non-idiomatic patterns and suggest Pythonic alternatives", "inputSchema": { "type": "object", "properties": { "file_path": {"type": "string"}, "source_code": {"type": "string"} } } }Example Output
{ "issues": [ { "tool": "pythonic", "category": "non_idiomatic_loop", "severity": "warning", "message": "Use enumerate() instead of range(len())", "line": 42, "column": 4, "suggestion": "for i, item in enumerate(items):", "code_snippet": "for i in range(len(items)):" } ], "summary": { "total_issues": 5, "by_category": { "non_idiomatic_loop": 2, "mutable_default": 1, "comparison_to_none": 2 } } }Implementation
src/workshop_mcp/tools/pythonic_check/patterns.pytests/test_pythonic_checker.pyAcceptance Criteria