Eventos de Lifespan¶
🌐 Traducción por IA y humanos
Esta traducción fue hecha por IA guiada por humanos. 🤝
Podría tener errores al interpretar el significado original, o sonar poco natural, etc. 🤖
Puedes mejorar esta traducciónayudándonos a guiar mejor al LLM de IA.
Puedes definir lógica (código) que debería ser ejecutada antes de que la aplicacióninicie. Esto significa que este código será ejecutadouna vez,antes de que la aplicacióncomience a recibir requests.
De la misma manera, puedes definir lógica (código) que debería ser ejecutada cuando la aplicación estécerrándose. En este caso, este código será ejecutadouna vez,después de haber manejado posiblementemuchos requests.
Debido a que este código se ejecuta antes de que la aplicacióncomience a tomar requests, y justo después de quetermine de manejarlos, cubre todo ellifespan de la aplicación (la palabra "lifespan" será importante en un momento 😉).
Esto puede ser muy útil para configurarrecursos que necesitas usar para toda la app, y que soncompartidos entre requests, y/o que necesitaslimpiar después. Por ejemplo, un pool de conexiones a una base de datos, o cargando un modelo de machine learning compartido.
Caso de Uso¶
Empecemos con un ejemplo decaso de uso y luego veamos cómo resolverlo con esto.
Imaginemos que tienes algunosmodelos de machine learning que quieres usar para manejar requests. 🤖
Los mismos modelos son compartidos entre requests, por lo que no es un modelo por request, o uno por usuario o algo similar.
Imaginemos que cargar el modelo puedetomar bastante tiempo, porque tiene que leer muchosdatos del disco. Entonces no quieres hacerlo para cada request.
Podrías cargarlo en el nivel superior del módulo/archivo, pero eso también significaría quecargaría el modelo incluso si solo estás ejecutando una simple prueba automatizada, entonces esa prueba seríalenta porque tendría que esperar a que el modelo se cargue antes de poder ejecutar una parte independiente del código.
Eso es lo que resolveremos, vamos a cargar el modelo antes de que los requests sean manejados, pero solo justo antes de que la aplicación comience a recibir requests, no mientras el código se está cargando.
Lifespan¶
Puedes definir esta lógica destartup yshutdown usando el parámetrolifespan de la app deFastAPI, y un "context manager" (te mostraré lo que es en un momento).
Comencemos con un ejemplo y luego veámoslo en detalle.
Creamos una función asíncronalifespan() conyield así:
fromcontextlibimportasynccontextmanagerfromfastapiimportFastAPIdeffake_answer_to_everything_ml_model(x:float):returnx*42ml_models={}@asynccontextmanagerasyncdeflifespan(app:FastAPI):# Load the ML modelml_models["answer_to_everything"]=fake_answer_to_everything_ml_modelyield# Clean up the ML models and release the resourcesml_models.clear()app=FastAPI(lifespan=lifespan)@app.get("/predict")asyncdefpredict(x:float):result=ml_models["answer_to_everything"](x)return{"result":result}Aquí estamos simulando la operación costosa destartup de cargar el modelo poniendo la función del (falso) modelo en el diccionario con modelos de machine learning antes delyield. Este código será ejecutadoantes de que la aplicacióncomience a tomar requests, durante elstartup.
Y luego, justo después delyield, quitaremos el modelo de memoria. Este código será ejecutadodespués de que la aplicacióntermine de manejar requests, justo antes delshutdown. Esto podría, por ejemplo, liberar recursos como la memoria o una GPU.
Consejo
Elshutdown ocurriría cuando estásdeteniendo la aplicación.
Quizás necesites iniciar una nueva versión, o simplemente te cansaste de ejecutarla. 🤷
Función de Lifespan¶
Lo primero que hay que notar es que estamos definiendo una función asíncrona conyield. Esto es muy similar a las Dependencias conyield.
fromcontextlibimportasynccontextmanagerfromfastapiimportFastAPIdeffake_answer_to_everything_ml_model(x:float):returnx*42ml_models={}@asynccontextmanagerasyncdeflifespan(app:FastAPI):# Load the ML modelml_models["answer_to_everything"]=fake_answer_to_everything_ml_modelyield# Clean up the ML models and release the resourcesml_models.clear()app=FastAPI(lifespan=lifespan)@app.get("/predict")asyncdefpredict(x:float):result=ml_models["answer_to_everything"](x)return{"result":result}La primera parte de la función, antes delyield, será ejecutadaantes de que la aplicación comience.
Y la parte después delyield será ejecutadadespués de que la aplicación haya terminado.
Async Context Manager¶
Si revisas, la función está decorada con un@asynccontextmanager.
Eso convierte a la función en algo llamado un "async context manager".
fromcontextlibimportasynccontextmanagerfromfastapiimportFastAPIdeffake_answer_to_everything_ml_model(x:float):returnx*42ml_models={}@asynccontextmanagerasyncdeflifespan(app:FastAPI):# Load the ML modelml_models["answer_to_everything"]=fake_answer_to_everything_ml_modelyield# Clean up the ML models and release the resourcesml_models.clear()app=FastAPI(lifespan=lifespan)@app.get("/predict")asyncdefpredict(x:float):result=ml_models["answer_to_everything"](x)return{"result":result}Uncontext manager en Python es algo que puedes usar en un statementwith, por ejemplo,open() puede ser usado como un context manager:
withopen("file.txt")asfile:file.read()En versiones recientes de Python, también hay unasync context manager. Lo usarías conasync with:
asyncwithlifespan(app):awaitdo_stuff()Cuando creas un context manager o un async context manager como arriba, lo que hace es que, antes de entrar al bloquewith, ejecutará el código antes delyield, y al salir del bloquewith, ejecutará el código después delyield.
En nuestro ejemplo de código arriba, no lo usamos directamente, pero se lo pasamos a FastAPI para que lo use.
El parámetrolifespan de la app deFastAPI toma unasync context manager, por lo que podemos pasar nuestro nuevolifespan async context manager a él.
fromcontextlibimportasynccontextmanagerfromfastapiimportFastAPIdeffake_answer_to_everything_ml_model(x:float):returnx*42ml_models={}@asynccontextmanagerasyncdeflifespan(app:FastAPI):# Load the ML modelml_models["answer_to_everything"]=fake_answer_to_everything_ml_modelyield# Clean up the ML models and release the resourcesml_models.clear()app=FastAPI(lifespan=lifespan)@app.get("/predict")asyncdefpredict(x:float):result=ml_models["answer_to_everything"](x)return{"result":result}Eventos Alternativos (obsoleto)¶
Advertencia
La forma recomendada de manejar elstartup y elshutdown es usando el parámetrolifespan de la app deFastAPI como se describió arriba. Si proporcionas un parámetrolifespan, los manejadores de eventosstartup yshutdown ya no serán llamados. Es sololifespan o solo los eventos, no ambos.
Probablemente puedas saltarte esta parte.
Hay una forma alternativa de definir esta lógica para ser ejecutada durante elstartup y durante elshutdown.
Puedes definir manejadores de eventos (funciones) que necesitan ser ejecutadas antes de que la aplicación se inicie, o cuando la aplicación se está cerrando.
Estas funciones pueden ser declaradas conasync def odef normal.
Eventostartup¶
Para añadir una función que debería ejecutarse antes de que la aplicación inicie, declárala con el evento"startup":
fromfastapiimportFastAPIapp=FastAPI()items={}@app.on_event("startup")asyncdefstartup_event():items["foo"]={"name":"Fighters"}items["bar"]={"name":"Tenders"}@app.get("/items/{item_id}")asyncdefread_items(item_id:str):returnitems[item_id]En este caso, la función manejadora del eventostartup inicializará los ítems de la "base de datos" (solo undict) con algunos valores.
Puedes añadir más de un manejador de eventos.
Y tu aplicación no comenzará a recibir requests hasta que todos los manejadores de eventosstartup hayan completado.
Eventoshutdown¶
Para añadir una función que debería ejecutarse cuando la aplicación se esté cerrando, declárala con el evento"shutdown":
fromfastapiimportFastAPIapp=FastAPI()@app.on_event("shutdown")defshutdown_event():withopen("log.txt",mode="a")aslog:log.write("Application shutdown")@app.get("/items/")asyncdefread_items():return[{"name":"Foo"}]Aquí, la función manejadora del eventoshutdown escribirá una línea de texto"Application shutdown" a un archivolog.txt.
Información
En la funciónopen(), elmode="a" significa "añadir", por lo tanto, la línea será añadida después de lo que sea que esté en ese archivo, sin sobrescribir el contenido anterior.
Consejo
Nota que en este caso estamos usando una función estándar de Pythonopen() que interactúa con un archivo.
Entonces, involucra I/O (entrada/salida), que requiere "esperar" para que las cosas se escriban en el disco.
Peroopen() no usaasync yawait.
Por eso, declaramos la función manejadora del evento condef estándar en vez deasync def.
startup yshutdown juntos¶
Hay una gran posibilidad de que la lógica para tustartup yshutdown esté conectada, podrías querer iniciar algo y luego finalizarlo, adquirir un recurso y luego liberarlo, etc.
Hacer eso en funciones separadas que no comparten lógica o variables juntas es más difícil ya que necesitarías almacenar valores en variables globales o trucos similares.
Debido a eso, ahora se recomienda en su lugar usar ellifespan como se explicó arriba.
Detalles Técnicos¶
Solo un detalle técnico para los nerds curiosos. 🤓
Por debajo, en la especificación técnica ASGI, esto es parte delProtocolo de Lifespan, y define eventos llamadosstartup yshutdown.
Información
Puedes leer más sobre los manejadoreslifespan de Starlette enla documentación deLifespan de Starlette.
Incluyendo cómo manejar el estado de lifespan que puede ser usado en otras áreas de tu código.
Sub Aplicaciones¶
🚨 Ten en cuenta que estos eventos de lifespan (startup y shutdown) solo serán ejecutados para la aplicación principal, no paraSub Aplicaciones - Mounts.







