Skip to content

Commit a963d26

Browse files
committed
Sample code for: How to Write an AGENTS.md File for a Python Project
1 parent 786ca25 commit a963d26

7 files changed

Lines changed: 301 additions & 0 deletions

File tree

agents-md/AGENTS.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
## Project Domain
2+
3+
This is a REST API for browsing and managing a collection of cars.
4+
Each car is a complete record with a unique, server-assigned `id`
5+
and the fields `make`, `model`, `year`, `horsepower`, `engine_cc`,
6+
and `transmission`.
7+
8+
The `engine_cc` field can be `0` for fully electric cars. Every
9+
car you return or store must include all fields.
10+
11+
## Project Setup and Management
12+
13+
- Python version: 3.14. Don't use newer syntax.
14+
- Dependency management: `uv` and `pyproject.toml`. Never use `pip`
15+
or a `requirements.txt` file.
16+
- Add a dependency with `uv add <package>`. Never use `uv pip`
17+
for dependencies.
18+
- Run the app with `uv run fastapi dev main.py`.
19+
- Branch from `main` as `feature/<name>` and use Conventional Commits.
20+
- Stage changes for review. Don't commit to `main` or push without
21+
being asked.
22+
23+
## Coding Conventions
24+
25+
- Type-hint public functions and methods, including their return types.
26+
- Use `pathlib` for path management. Don't use `os.path`.
27+
- Prefer f-strings over `str.format()` or `%` formatting.
28+
- Follow EAFP: handle exceptions rather than checking conditions up front.
29+
- Write Google-style docstrings for every public function and method.
30+
- Validate request bodies with Pydantic models.
31+
- Embrace idiomatic Python like comprehensions, generators, and decorators.
32+
33+
## Project Structure
34+
35+
- The project is flat: `main.py` holds the app and `cars.json` holds
36+
the data.
37+
- The `main.py` file is the only module to edit when adding features.
38+
- Put tests in `tests/test_main.py`.
39+
- Don't create new packages or new files without being asked.
40+
41+
## Quality Gates
42+
43+
A task is done only when all of these pass:
44+
45+
- `uv run ruff format` leaves the code unchanged.
46+
- `uv run ruff check` reports no errors.
47+
- `uv run mypy main.py` reports no errors.
48+
- `uv run pytest` passes, with a test added for every new endpoint.
49+
50+
## Constraints
51+
52+
- Ask before adding any external dependency.
53+
- Preserve the signature and response shape of existing endpoints.
54+
- Don't use blocking I/O inside `async` functions.
55+
- Keep existing tests intact, and fix the code to make them pass.
56+
- Declare a task done only after the gates pass and docstrings are
57+
updated.
58+
59+
## Ignore
60+
61+
Treat everything in `.gitignore` as off-limits to read or edit. On top of
62+
that, never open:
63+
64+
- Secrets and `.env` files
65+
- Large data files unrelated to the current task
66+
- Vendored or generated code

agents-md/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# How to Write an AGENTS.md File for a Python Project
2+
3+
This folder provides the code examples for the Real Python tutorial [How to Write an AGENTS.md File for a Python Project](https://realpython.com/agents-md/)

agents-md/cars.json

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
[
2+
{
3+
"id": 1,
4+
"make": "Ford",
5+
"model": "Mustang",
6+
"year": 1969,
7+
"horsepower": 290,
8+
"engine_cc": 5752,
9+
"transmission": "Manual"
10+
},
11+
{
12+
"id": 2,
13+
"make": "Chevrolet",
14+
"model": "Corvette",
15+
"year": 2020,
16+
"horsepower": 490,
17+
"engine_cc": 6162,
18+
"transmission": "Automatic"
19+
},
20+
{
21+
"id": 3,
22+
"make": "Dodge",
23+
"model": "Charger",
24+
"year": 2023,
25+
"horsepower": 370,
26+
"engine_cc": 5654,
27+
"transmission": "Automatic"
28+
},
29+
{
30+
"id": 4,
31+
"make": "Tesla",
32+
"model": "Model S",
33+
"year": 2022,
34+
"horsepower": 670,
35+
"engine_cc": 0,
36+
"transmission": "Automatic"
37+
},
38+
{
39+
"id": 5,
40+
"make": "Jeep",
41+
"model": "Wrangler",
42+
"year": 2021,
43+
"horsepower": 285,
44+
"engine_cc": 3604,
45+
"transmission": "Automatic"
46+
},
47+
{
48+
"id": 6,
49+
"make": "Ford",
50+
"model": "F-150",
51+
"year": 2024,
52+
"horsepower": 400,
53+
"engine_cc": 3496,
54+
"transmission": "Automatic"
55+
},
56+
{
57+
"id": 7,
58+
"make": "Cadillac",
59+
"model": "Escalade",
60+
"year": 2023,
61+
"horsepower": 420,
62+
"engine_cc": 6162,
63+
"transmission": "Automatic"
64+
},
65+
{
66+
"id": 8,
67+
"make": "Chevrolet",
68+
"model": "Camaro",
69+
"year": 2018,
70+
"horsepower": 455,
71+
"engine_cc": 6162,
72+
"transmission": "Manual"
73+
},
74+
{
75+
"id": 9,
76+
"make": "GMC",
77+
"model": "Sierra",
78+
"year": 2022,
79+
"horsepower": 355,
80+
"engine_cc": 5328,
81+
"transmission": "Automatic"
82+
},
83+
{
84+
"id": 10,
85+
"make": "Chrysler",
86+
"model": "300",
87+
"year": 2019,
88+
"horsepower": 292,
89+
"engine_cc": 3604,
90+
"transmission": "Automatic"
91+
}
92+
]

agents-md/original_main.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import json
2+
from pathlib import Path
3+
4+
from fastapi import FastAPI, HTTPException
5+
6+
app = FastAPI()
7+
cars: list[dict] = json.loads(Path("cars.json").read_text())
8+
9+
10+
@app.get("/cars")
11+
def list_cars() -> list[dict]:
12+
"""Return a list of all cars."""
13+
return cars
14+
15+
16+
@app.get("/cars/{car_id}")
17+
def get_car(car_id: int) -> dict:
18+
"""Return a single car by its id, or raise 404 if it doesn't exist."""
19+
for car in cars:
20+
if car["id"] == car_id:
21+
return car
22+
raise HTTPException(status_code=404, detail="Car not found")

agents-md/run1_main.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import json
2+
import os
3+
4+
from fastapi import FastAPI, HTTPException
5+
6+
app = FastAPI()
7+
8+
data_file = os.path.join(os.path.dirname(__file__), "cars.json")
9+
with open(data_file) as f:
10+
cars = json.load(f)
11+
12+
13+
@app.get("/cars")
14+
def list_cars() -> list[dict]:
15+
"""Return a list of all cars."""
16+
return cars
17+
18+
19+
@app.get("/cars/{car_id}")
20+
def get_car(car_id: int) -> dict:
21+
"""Return a single car by its id, or raise 404 if it doesn't exist."""
22+
for car in cars:
23+
if car["id"] == car_id:
24+
return car
25+
raise HTTPException(status_code=404, detail="Car not found")
26+
27+
28+
@app.post("/cars")
29+
def create_car(car: dict):
30+
car["id"] = len(cars) + 1
31+
cars.append(car)
32+
return {"message": "Car created successfully", "car": car}
33+
34+
35+
@app.delete("/cars/{car_id}")
36+
def delete_car(car_id: int):
37+
for i in range(len(cars)):
38+
if cars[i]["id"] == car_id:
39+
cars.pop(i)
40+
return {"message": "Car deleted"}
41+
return {"error": "Car not found"}

agents-md/run2_main.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import json
2+
from pathlib import Path
3+
4+
from fastapi import FastAPI, HTTPException
5+
from pydantic import BaseModel
6+
7+
app = FastAPI()
8+
cars: list[dict] = json.loads(Path("cars.json").read_text())
9+
10+
11+
class NewCar(BaseModel):
12+
make: str
13+
model: str
14+
year: int
15+
horsepower: int
16+
engine_cc: int
17+
transmission: str
18+
19+
20+
@app.get("/cars")
21+
def list_cars() -> list[dict]:
22+
"""Return a list of all cars."""
23+
return cars
24+
25+
26+
@app.get("/cars/{car_id}")
27+
def get_car(car_id: int) -> dict:
28+
"""Return a single car by its id, or raise 404 if it doesn't exist."""
29+
for car in cars:
30+
if car["id"] == car_id:
31+
return car
32+
raise HTTPException(status_code=404, detail="Car not found")
33+
34+
35+
@app.post("/cars", status_code=201)
36+
def create_car(new_car: NewCar) -> dict:
37+
"""Add a new car and return it with a server-assigned id."""
38+
car = new_car.model_dump()
39+
car["id"] = max((existing["id"] for existing in cars), default=0) + 1
40+
cars.append(car)
41+
return car
42+
43+
44+
@app.delete("/cars/{car_id}", status_code=204)
45+
def delete_car(car_id: int) -> None:
46+
"""Delete a car by its id, or raise 404 if it doesn't exist."""
47+
for index, car in enumerate(cars):
48+
if car["id"] == car_id:
49+
del cars[index]
50+
return
51+
raise HTTPException(status_code=404, detail="Car not found")

agents-md/test_main.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from fastapi.testclient import TestClient
2+
3+
from run2_main import app, cars
4+
5+
client = TestClient(app)
6+
7+
8+
def test_create_car_assigns_unique_id():
9+
new_car = {
10+
"make": "Honda",
11+
"model": "Civic",
12+
"year": 2021,
13+
"horsepower": 158,
14+
"engine_cc": 1996,
15+
"transmission": "Manual",
16+
}
17+
response = client.post("/cars", json=new_car)
18+
19+
assert response.status_code == 201
20+
assert response.json()["id"] not in {car["id"] for car in cars[:-1]}
21+
22+
23+
def test_delete_missing_car_returns_404():
24+
response = client.delete("/cars/999")
25+
26+
assert response.status_code == 404

0 commit comments

Comments
 (0)