api_service.py en Python
Un servicio FastAPI pequeño: modelos, store inyectado como dependencia, rutas y pruebas.
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
class TourIn(BaseModel):
slug: str = Field(pattern=r"^[a-z][a-z0-9_-]{1,31}$")
title: str = Field(max_length=60)
steps: int = Field(default=0, ge=0)
class TourOut(TourIn):
id: int
class Almacen:
"""Un sustituto en memoria de una base de datos."""
def __init__(self):
self._rows: dict[int, TourOut] = {}
self._next = 1
def add(self, tour: TourIn) -> TourOut:
if any(row.slug == tour.slug for row in self._rows.values()):
raise KeyError(tour.slug)
row = TourOut(id=self._next, **tour.model_dump())
self._rows[self._next] = row
self._next += 1
return row
def all(self) -> list[TourOut]:
return list(self._rows.values())
def get(self, tour_id: int) -> TourOut | None:
return self._rows.get(tour_id)
almacen = Almacen()
app = FastAPI(title="tours")
def get_store() -> Almacen:
return almacen
@app.get("/healthz")
def healthz():
return {"status": "ok"}
@app.get("/tours", response_model=list[TourOut])
def list_tours(shop: Almacen = Depends(get_store)):
return shop.all()
@app.post("/tours", response_model=TourOut,
status_code=status.HTTP_201_CREATED)
def create_tour(tour: TourIn, shop: Almacen = Depends(get_store)):
try:
return shop.add(tour)
except KeyError:
raise HTTPException(status.HTTP_409_CONFLICT, f"{tour.slug} ya existe")
@app.get("/tours/{tour_id}", response_model=TourOut)
def read_tour(tour_id: int, shop: Almacen = Depends(get_store)):
found = shop.get(tour_id)
if found is None:
raise HTTPException(status.HTTP_404_NOT_FOUND, "no existe ese tour")
return found
def principal():
cliente = TestClient(app)
print(cliente.get("/healthz").json())
creado = cliente.post("/tours", json={"slug": "basics",
"title": "Fundamentos del lenguaje",
"steps": 60})
print(creado.status_code, creado.json())
otra = cliente.post("/tours", json={"slug": "basics", "title": "Copia"})
print(otra.status_code, otra.json()["detail"])
malo = cliente.post("/tours", json={"slug": "Slug Malo", "title": "x"})
print(malo.status_code, malo.json()["detail"][0]["loc"])
print(cliente.get("/tours/1").json()["title"])
print(cliente.get("/tours/99").status_code)
print(len(cliente.get("/tours").json()))
if __name__ == "__main__":
principal()
Cómo funciona
- Los modelos de Pydantic definen las formas de petición y respuesta.
- El store llega vía Depends, así una prueba puede reemplazarlo.
- El TestClient ejercita cada ruta al final del archivo.
Palabras clave y builtins usados aquí
anyclassdefdictexceptforidifintlenlistprintraisereturnselfstrtry
El intento, en números
- Líneas
- 94
- Caracteres a escribir
- 2292
- Tokens
- 668
- Ritmo de tres estrellas
- 115 tpm
Al ritmo de tres estrellas de 115 tokens por minuto, este intento toma unos 349 segundos.
Paso 1 de 2 en Bis; paso 18 de 19 en Servicios web y acceso a datos.