Hello, I come to you with an annoying little bug which cannot reproduce. 🤷
When trying to browse to http://127.0.0.1:8080/schema
I get this wonderful error:
AttributeError on GET /schema
type object 'MyInputModel' has no attribute '__parameters__'
.venv/lib/python3.12/site-packages/litestar/utils/typing.py</span> in get_type_hints_with_generics_resolved at line 263:
origin = get_origin(annotation)
if origin is None:
# Implies the generic types have not been specified in the annotation
if type_hints is None: # pragma: no cover
type_hints = get_type_hints(annotation, globalns=globalns, localns=localns, include_extras=include_extras)
typevar_map = {p: p for p in annotation.__parameters__}
else:
if type_hints is None: # pragma: no cover
type_hints = get_type_hints(origin, globalns=globalns, localns=localns, include_extras=include_extras)
# the __parameters__ is only available on the origin itself and not the annotation
typevar_map = dict(zip(origin.__parameters__, get_args(annotation)))
return {n: _substitute_typevars(type_, typevar_map) for n, type_ in type_hints.items()}
The cause seems to be a generic class (see MyInputModel
in MCVE). When debugging into the litestar code that model does indeed not have a __parameters__
attribute. If I understand this and this correctly, __parameters__
might not always exist. Read: "Note that generics with ParamSpec may not have correct parameters after substitution in some cases because they are intended primarily for static type checking."
Unfortunately, I cannot reproduce this (the model in the MCVE has the same structure, but always has __parameters__
when I test it). Nonetheless, it seems that get_type_hints_with_generics_resolved
should account for that.
My non-reproducible example model works, if I replace line 263 in litestar/utils/typing.py
:
- typevar_map = {p: p for p in annotation.__parameters__}
+ typevar_map = {p: p for p in annotation.__type_params__}
I assume it is not as straight forward as that, as __type_params__
seems not to be available before python 3.12 PEP 695. But I am no expert so, maybe it is. ;)
Cheers
No response
import litestar
import msgspec
import uvicorn
class Base(msgspec.Struct, frozen=True):
field: int = 0
class A(Base, frozen=True, kw_only=True):
x: int
class B(Base, frozen=True, kw_only=True):
x: str
class OtherModel(Base, frozen=True, kw_only=True):
x: float
class MyInputModel[T: A | B](Base, frozen=True, kw_only=True):
items: list[T]
@litestar.post()
async def handler(data: MyInputModel | OtherModel) -> None:
if isinstance(data, OtherModel):
data = MyInputModel(items=[A(x=0, field=0)], field=0)
print(data)
app = litestar.Litestar(route_handlers=[handler])
uvicorn.run(app, host="127.0.0.1", port=8000)
Unknown
No response
No response
2.10.0
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too