Using Python reserved keyword for parameter alias causes error on start up
I have requirements to implement pagination with parameters size and from. I am forming my custom parameter class like:
class FromSizeParams(BaseModel, AbstractParams):
size: int = Query(50, ge=1, le=100)
# use from_ since from is reserved, set alias to "from"
from_: int = Query(0, ge=0, alias="from")
def to_raw_params(self) -> RawParams:
return RawParams(limit=self.size, offset=self.from_)
Pydantic does allow using a reserved keywords as an alias. However, add_pagination(app) fails on start up.
Traceback (most recent call last):
File "/usr/local/lib/python3.10/multiprocessing/process.py", line 314, in _bootstrap
self.run()
File "/usr/local/lib/python3.10/multiprocessing/process.py", line 108, in run
self._target(*self._args, **self._kwargs)
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/uvicorn/_subprocess.py", line 76, in subprocess_started
target(sockets=sockets)
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/uvicorn/server.py", line 60, in run
return asyncio.run(self.serve(sockets=sockets))
File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "uvloop/loop.pyx", line 1517, in uvloop.loop.Loop.run_until_complete
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/uvicorn/server.py", line 67, in serve
config.load()
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/uvicorn/config.py", line 479, in load
self.loaded_app = import_from_string(self.app)
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/uvicorn/importer.py", line 21, in import_from_string
module = importlib.import_module(module_str)
File "/usr/local/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 883, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/opt/fp/api-project/./api_project/main.py", line 86, in <module>
add_pagination(app)
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/fastapi_pagination/api.py", line 373, in add_pagination
_add_pagination(parent)
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/fastapi_pagination/api.py", line 369, in _add_pagination
_update_route(route)
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/fastapi_pagination/api.py", line 348, in _update_route
dep = Depends(pagination_ctx(cls, __page_ctx_dep__=True))
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/fastapi_pagination/api.py", line 311, in pagination_ctx
params_dep: Any = _create_params_dependency(params) if params is not None else _noop_dep
File "/root/.cache/pypoetry/virtualenvs/api-project-8bpWA_BU-py3.10/lib/python3.10/site-packages/fastapi_pagination/api.py", line 289, in _create_params_dependency
sign_params[name] = sign_params[name].replace(default=field)
KeyError: 'from'
It appears the signature of FromSizeParams still has from_ instead of the alias, causing the key error.
(*, size: typing.Annotated[int, Ge(ge=1), Le(le=100)] = 50, from_: typing.Annotated[int, Ge(ge=0)] = 0) -> None
Aliases of words that are not reserved work fine.
Keywords other than from like: import and def also fail.
Is there a good fix or work around to use a reserved python keyword as an alias?
@cwtfp Looks like it's fastapi-pagination bug, I will take a look
Hi @cwtfp,
Looks like it's not fastapi-pagination issue, also I have found workaround. You just need to replace alias with validation_alias:
class FromSizeParams(BaseModel, AbstractParams):
size: int = Query(50, ge=1, le=100)
from_: int = Query(0, ge=0, validation_alias="from")
def to_raw_params(self) -> RawParams:
return RawParams(limit=self.size, offset=self.from_)
Apologies for the delay in response.
I did try that as a work around before posting the issue. However, it didn't pick up the from query parameter, when making the request.
# picks up size but not from
curl localhost:8000/api/v1/things?size=1&from=1
# does still work
curl localhost:8000/api/v1/things?size=1&from_=1
I did, however, find a work around that worked. Only drawback is that from_ remains visible in the OpenAPI spec.
class FromSizeParams(BaseModel, AbstractParams):
size: int = Query(50, ge=1, le=100)
# labeled as depcreated and unsupported to discourage use
from_: int | None = Query(
None, ge=0, deprecated=True, description="Unsupported. Use `from` instead"
)
def to_raw_params(self) -> RawParams:
return RawParams(limit=self.size, offset=self.from_)
async def set_pagination_params(
from_workaround: Annotated[int, Query(ge=0, alias="from")] = 0,
params: FromSizeParams = Depends(),
) -> FromSizeParams:
params.from_ = from_workaround
return params
class FromSizePage(AbstractPage[T], Generic[T]):
# page stuff
__params_type__ = FromSizeParams
@router.get("/things", response_model=FromSizePage[Thing])
def get_queries(
params=Depends(set_pagination_params),
):
return paginate(..., params)
Also needed some extra custom code in FromSizePage to generate links with this as well.
Hi @cwtfp
New version 0.12.22 has been released, this issue should be fixed.
Hi @cwtfp,
I'm closing this issue, please reopen it in case issue still exists on your side.