0

I'm dynamically creating endpoints in in a FastAPI project based on models loaded from another file. When I loop over these I expected to be able to get the value of the variable in the loop inside the async function. However I get only the last value the variable held. This will probably be more clear with code:

my_models = [
    ("FooModel", FooModel),
    ("BarModel", BarModel),
]

app = FastAPI()

for name, model in my_models:
    snake_name = utils.camel_to_snake(name)
    title_name = utils.camel_to_title(name)

    @app.post(
        f"/{snake_name}",
        response_model=model,
        name=f"{title_name} Create",
    )
    async def validate_item(item: model, request: Request):
        logger.info(
            f"[Valid] {request.method} {title_name} ID: {item.identifier()}"
        )
        return item

The FastAPI endpoints get created correctly, but wether I hit the /foo_model endpoint or the /bar_model endpoint the logger will print "Bar Model" for title_name. I expected the async def to act like a closure with a temporary local scope which holds what was in the outer variable when defined. It seems like it is actually waiting to evaluate it until the time it is called and it holds whatever was the last in the for loop.

The values are what I expect inside the @app.post decorator but not inside the validate_item itself.

How can I get title_name to behave like how I want and have it hold the proper value when defining it? Also does model in the signature behave the same way?

I'm using:

  • Python 3.10.12
  • fastapi 0.95.1
  • and running it using a UvicornWorker
  • uvicorn 0.21.1
Robbie
  • 892
  • 2
  • 7
  • 19
  • Closures capture *variables*, not values. – user2357112 Aug 17 '23 at 13:16
  • The linked duplicate question helped me. The way I solved this for my particular case was to add title_name to the arg list with a default of title_name: async def validate_item(item: model, request: Request, title_name=title_name): – Robbie Aug 24 '23 at 15:54

0 Answers0