A3.0 What This Teaches
- Using a system prompt to enforce structured output across specific review categories
- Reviewing for bugs, security, error handling, type hints, and style
- Writing review results to a
.review.txtfile with pathlib - Reviewing multiple files in one run by looping over
sys.argv
A3.1 Review Categories
- Bugs / logic errors - off-by-one, wrong operator, unreachable code
- Security issues - shell injection, unsafe
eval(), hardcoded credentials - Missing error handling - uncaught exceptions that will crash in production
- Missing type hints - function signatures without
int,str, etc. - Style / readability - PEP 8 violations, unclear names, overly long lines
- Performance - quadratic loops, unnecessary copies, repeated lookups
A3.2 The System Prompt
# review_system.py - the review format.
SYSTEM = (
"You are a strict Python code reviewer. "
"Review the code for:\n"
"BUGS (logic errors)\n"
"SECURITY (injection, unsafe operations)\n"
"ERROR HANDLING (missing try/except)\n"
"TYPE HINTS (missing annotations)\n"
"STYLE (PEP 8 violations)\n"
"For each category, list findings as bullet points. "
"If no issues, write 'None found.'"
)
A3.3 Complete Implementation
# review.py - automated code review using Claude.
# Usage: python review.py <file1.py> [file2.py ...]
import sys
from pathlib import Path
import anthropic
SYSTEM = (
"You are a strict Python code reviewer. "
"Review the code for:\n"
"BUGS (logic errors)\n"
"SECURITY (injection, unsafe operations)\n"
"ERROR HANDLING (missing try/except)\n"
"TYPE HINTS (missing annotations)\n"
"STYLE (PEP 8 violations)\n"
"For each category, list findings as bullet points. "
"If no issues, write 'None found.'"
)
def review_file(client: anthropic.Anthropic, path: str) -> str:
try:
code = Path(path).read_text(encoding="utf-8")
except FileNotFoundError:
print(f"Error: file not found: {path}")
sys.exit(1)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=SYSTEM,
messages=[{"role": "user", "content": f"Review this Python code:\n\n```python\n{code}\n```"}],
)
return response.content[0].text
def main():
if len(sys.argv) < 2:
print("Usage: python review.py <file1.py> [file2.py ...]")
sys.exit(1)
client = anthropic.Anthropic()
for path in sys.argv[1:]:
print(f"\n=== Review: {path} ===")
review = review_file(client, path)
print(review)
out_path = Path(path).with_suffix(".review.txt")
out_path.write_text(review, encoding="utf-8")
print(f"\nSaved to {out_path}")
if __name__ == "__main__":
main()
A3.4 Writing the Review to a File
pathlib.Path.with_suffix() replaces the extension cleanly without
string manipulation. Writing immediately after each review means you lose nothing
if the script crashes partway through a multi-file run.
# save_review.py - write review output alongside the source file.
from pathlib import Path
def save_review(source_path: str, review_text: str) -> Path:
out = Path(source_path).stem + ".review.txt"
out_path = Path(source_path).parent / out
out_path.write_text(review_text, encoding="utf-8")
return out_path
.stem gives just the filename without any extension, so
utils.py becomes utils.review.txt.
A3.5 Sample Review Output
$ python review.py loader.py
=== Review: loader.py ===
BUGS
- None found.
SECURITY
- None found.
ERROR HANDLING
- load_data() calls open() without a try/except; a missing file will crash the caller
with an unhandled FileNotFoundError.
TYPE HINTS
- load_data(filename) has no parameter type annotation (expected str).
- Return type is not annotated (expected list[str]).
STYLE
- Line 14 exceeds 79 characters (PEP 8 E501).
Saved to loader.review.txt
A3.6 Reviewing Multiple Files
sys.argv[1:] is already a list, iterating over it handles
one file or ten files with no extra logic.
# multi_review.py - loop over every argument.
for path in sys.argv[1:]:
review = review_file(client, path)
print(f"\n=== {path} ===\n{review}")
Path(path).with_suffix(".review.txt").write_text(review, encoding="utf-8")
A3.7 Exercise
Exercise
Add a
--summary flag using argparse. When present, after
reviewing all files make one more API call with all the review texts concatenated
and ask Claude to write a one-paragraph executive summary of the most critical
issues across all files.
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("files", nargs="+")
parser.add_argument("--summary", action="store_true")
args = parser.parse_args()
reviews = []
for path in args.files:
review = review_file(client, path)
reviews.append(f"--- {path} ---\n{review}")
print(review)
if args.summary:
combined = "\n\n".join(reviews)
summary_response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=256,
messages=[{
"role": "user",
"content": (
"Write a one-paragraph executive summary of the most critical issues "
f"found across these code reviews:\n\n{combined}"
),
}],
)
print("\n=== Executive Summary ===")
print(summary_response.content[0].text)
A3.8 Common Mistakes
Trusting the review blindly
Reviewing minified or auto-generated code
Not saving results
.review.txt file - do not remove that step to "simplify" the script.
Key Terms
| Term | Meaning |
|---|---|
| PEP 8 | Python's official style guide; defines naming, spacing, and line length conventions |
| type hint | Optional annotation on a function parameter or return value, e.g. def f(x: int) -> str |
| pathlib.Path.with_suffix() | Returns a new Path with the file extension replaced; avoids manual string slicing |
| argparse | Standard library module for parsing command-line flags and positional arguments |
| sys.argv[1:] | All command-line arguments after the script name, as a list of strings |
| executive summary | A short high-level overview aimed at someone who needs the key points without details |