-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Sample code for: How to Write an AGENTS.md File for a Python Project #781
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
lpozo
wants to merge
1
commit into
master
Choose a base branch
from
agents-md
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| ## Project Domain | ||
|
|
||
| This is a REST API for browsing and managing a collection of cars. | ||
| Each car is a complete record with a unique, server-assigned `id` | ||
| and the fields `make`, `model`, `year`, `horsepower`, `engine_cc`, | ||
| and `transmission`. | ||
|
|
||
| The `engine_cc` field can be `0` for fully electric cars. Every | ||
| car you return or store must include all fields. | ||
|
|
||
| ## Project Setup and Management | ||
|
|
||
| - Python version: 3.14. Don't use newer syntax. | ||
| - Dependency management: `uv` and `pyproject.toml`. Never use `pip` | ||
| or a `requirements.txt` file. | ||
| - Add a dependency with `uv add <package>`. Never use `uv pip` | ||
| for dependencies. | ||
| - Run the app with `uv run fastapi dev main.py`. | ||
| - Branch from `main` as `feature/<name>` and use Conventional Commits. | ||
| - Stage changes for review. Don't commit to `main` or push without | ||
| being asked. | ||
|
|
||
| ## Coding Conventions | ||
|
|
||
| - Type-hint public functions and methods, including their return types. | ||
| - Use `pathlib` for path management. Don't use `os.path`. | ||
| - Prefer f-strings over `str.format()` or `%` formatting. | ||
| - Follow EAFP: handle exceptions rather than checking conditions up front. | ||
| - Write Google-style docstrings for every public function and method. | ||
| - Validate request bodies with Pydantic models. | ||
| - Embrace idiomatic Python like comprehensions, generators, and decorators. | ||
|
|
||
| ## Project Structure | ||
|
|
||
| - The project is flat: `main.py` holds the app and `cars.json` holds | ||
| the data. | ||
| - The `main.py` file is the only module to edit when adding features. | ||
| - Put tests in `tests/test_main.py`. | ||
| - Don't create new packages or new files without being asked. | ||
|
|
||
| ## Quality Gates | ||
|
|
||
| A task is done only when all of these pass: | ||
|
|
||
| - `uv run ruff format` leaves the code unchanged. | ||
| - `uv run ruff check` reports no errors. | ||
| - `uv run mypy main.py` reports no errors. | ||
| - `uv run pytest` passes, with a test added for every new endpoint. | ||
|
|
||
| ## Constraints | ||
|
|
||
| - Ask before adding any external dependency. | ||
| - Preserve the signature and response shape of existing endpoints. | ||
| - Don't use blocking I/O inside `async` functions. | ||
| - Keep existing tests intact, and fix the code to make them pass. | ||
| - Declare a task done only after the gates pass and docstrings are | ||
| updated. | ||
|
|
||
| ## Ignore | ||
|
|
||
| Treat everything in `.gitignore` as off-limits to read or edit. On top of | ||
| that, never open: | ||
|
|
||
| - Secrets and `.env` files | ||
| - Large data files unrelated to the current task | ||
| - Vendored or generated code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| # How to Write an AGENTS.md File for a Python Project | ||
|
|
||
| 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/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| [ | ||
| { | ||
| "id": 1, | ||
| "make": "Ford", | ||
| "model": "Mustang", | ||
| "year": 1969, | ||
| "horsepower": 290, | ||
| "engine_cc": 5752, | ||
| "transmission": "Manual" | ||
| }, | ||
| { | ||
| "id": 2, | ||
| "make": "Chevrolet", | ||
| "model": "Corvette", | ||
| "year": 2020, | ||
| "horsepower": 490, | ||
| "engine_cc": 6162, | ||
| "transmission": "Automatic" | ||
| }, | ||
| { | ||
| "id": 3, | ||
| "make": "Dodge", | ||
| "model": "Charger", | ||
| "year": 2023, | ||
| "horsepower": 370, | ||
| "engine_cc": 5654, | ||
| "transmission": "Automatic" | ||
| }, | ||
| { | ||
| "id": 4, | ||
| "make": "Tesla", | ||
| "model": "Model S", | ||
| "year": 2022, | ||
| "horsepower": 670, | ||
| "engine_cc": 0, | ||
| "transmission": "Automatic" | ||
| }, | ||
| { | ||
| "id": 5, | ||
| "make": "Jeep", | ||
| "model": "Wrangler", | ||
| "year": 2021, | ||
| "horsepower": 285, | ||
| "engine_cc": 3604, | ||
| "transmission": "Automatic" | ||
| }, | ||
| { | ||
| "id": 6, | ||
| "make": "Ford", | ||
| "model": "F-150", | ||
| "year": 2024, | ||
| "horsepower": 400, | ||
| "engine_cc": 3496, | ||
| "transmission": "Automatic" | ||
| }, | ||
| { | ||
| "id": 7, | ||
| "make": "Cadillac", | ||
| "model": "Escalade", | ||
| "year": 2023, | ||
| "horsepower": 420, | ||
| "engine_cc": 6162, | ||
| "transmission": "Automatic" | ||
| }, | ||
| { | ||
| "id": 8, | ||
| "make": "Chevrolet", | ||
| "model": "Camaro", | ||
| "year": 2018, | ||
| "horsepower": 455, | ||
| "engine_cc": 6162, | ||
| "transmission": "Manual" | ||
| }, | ||
| { | ||
| "id": 9, | ||
| "make": "GMC", | ||
| "model": "Sierra", | ||
| "year": 2022, | ||
| "horsepower": 355, | ||
| "engine_cc": 5328, | ||
| "transmission": "Automatic" | ||
| }, | ||
| { | ||
| "id": 10, | ||
| "make": "Chrysler", | ||
| "model": "300", | ||
| "year": 2019, | ||
| "horsepower": 292, | ||
| "engine_cc": 3604, | ||
| "transmission": "Automatic" | ||
| } | ||
| ] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import json | ||
| from pathlib import Path | ||
|
|
||
| from fastapi import FastAPI, HTTPException | ||
|
|
||
| app = FastAPI() | ||
| cars: list[dict] = json.loads(Path("cars.json").read_text()) | ||
|
|
||
|
|
||
| @app.get("/cars") | ||
| def list_cars() -> list[dict]: | ||
| """Return a list of all cars.""" | ||
| return cars | ||
|
|
||
|
|
||
| @app.get("/cars/{car_id}") | ||
| def get_car(car_id: int) -> dict: | ||
| """Return a single car by its id, or raise 404 if it doesn't exist.""" | ||
| for car in cars: | ||
| if car["id"] == car_id: | ||
| return car | ||
| raise HTTPException(status_code=404, detail="Car not found") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import json | ||
| import os | ||
|
|
||
| from fastapi import FastAPI, HTTPException | ||
|
|
||
| app = FastAPI() | ||
|
|
||
| data_file = os.path.join(os.path.dirname(__file__), "cars.json") | ||
| with open(data_file) as f: | ||
| cars = json.load(f) | ||
|
|
||
|
|
||
| @app.get("/cars") | ||
| def list_cars() -> list[dict]: | ||
| """Return a list of all cars.""" | ||
| return cars | ||
|
|
||
|
|
||
| @app.get("/cars/{car_id}") | ||
| def get_car(car_id: int) -> dict: | ||
| """Return a single car by its id, or raise 404 if it doesn't exist.""" | ||
| for car in cars: | ||
| if car["id"] == car_id: | ||
| return car | ||
| raise HTTPException(status_code=404, detail="Car not found") | ||
|
|
||
|
|
||
| @app.post("/cars") | ||
| def create_car(car: dict): | ||
| car["id"] = len(cars) + 1 | ||
| cars.append(car) | ||
| return {"message": "Car created successfully", "car": car} | ||
|
|
||
|
|
||
| @app.delete("/cars/{car_id}") | ||
| def delete_car(car_id: int): | ||
| for i in range(len(cars)): | ||
| if cars[i]["id"] == car_id: | ||
| cars.pop(i) | ||
| return {"message": "Car deleted"} | ||
| return {"error": "Car not found"} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import json | ||
| from pathlib import Path | ||
|
|
||
| from fastapi import FastAPI, HTTPException | ||
| from pydantic import BaseModel | ||
|
|
||
| app = FastAPI() | ||
| cars: list[dict] = json.loads(Path("cars.json").read_text()) | ||
|
|
||
|
|
||
| class NewCar(BaseModel): | ||
| make: str | ||
| model: str | ||
| year: int | ||
| horsepower: int | ||
| engine_cc: int | ||
| transmission: str | ||
|
|
||
|
|
||
| @app.get("/cars") | ||
| def list_cars() -> list[dict]: | ||
| """Return a list of all cars.""" | ||
| return cars | ||
|
|
||
|
|
||
| @app.get("/cars/{car_id}") | ||
| def get_car(car_id: int) -> dict: | ||
| """Return a single car by its id, or raise 404 if it doesn't exist.""" | ||
| for car in cars: | ||
| if car["id"] == car_id: | ||
| return car | ||
| raise HTTPException(status_code=404, detail="Car not found") | ||
|
|
||
|
|
||
| @app.post("/cars", status_code=201) | ||
| def create_car(new_car: NewCar) -> dict: | ||
| """Add a new car and return it with a server-assigned id.""" | ||
| car = new_car.model_dump() | ||
| car["id"] = max((existing["id"] for existing in cars), default=0) + 1 | ||
| cars.append(car) | ||
| return car | ||
|
|
||
|
|
||
| @app.delete("/cars/{car_id}", status_code=204) | ||
| def delete_car(car_id: int) -> None: | ||
| """Delete a car by its id, or raise 404 if it doesn't exist.""" | ||
| for index, car in enumerate(cars): | ||
| if car["id"] == car_id: | ||
| del cars[index] | ||
| return | ||
| raise HTTPException(status_code=404, detail="Car not found") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| from fastapi.testclient import TestClient | ||
|
|
||
| from run2_main import app, cars | ||
|
|
||
| client = TestClient(app) | ||
|
|
||
|
|
||
| def test_create_car_assigns_unique_id(): | ||
| new_car = { | ||
| "make": "Honda", | ||
| "model": "Civic", | ||
| "year": 2021, | ||
| "horsepower": 158, | ||
| "engine_cc": 1996, | ||
| "transmission": "Manual", | ||
| } | ||
| response = client.post("/cars", json=new_car) | ||
|
|
||
| assert response.status_code == 201 | ||
| assert response.json()["id"] not in {car["id"] for car in cars[:-1]} | ||
|
|
||
|
|
||
| def test_delete_missing_car_returns_404(): | ||
| response = client.delete("/cars/999") | ||
|
|
||
| assert response.status_code == 404 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original code in the tutorial uses single quotes for the strings in the dicts and for
'id', and this is one of the "flaws" you discuss from the first run. I suggest breaking our convention for proper formatting in the materials and use the original single quotes here, even if CI complains!