I have tried the solution of using IntEnum
instead (source), and I can confirm that it works, but still - why does it have to be this way?
The enum.py source code defines IntEnum
as:
class IntEnum(int, Enum):
"""Enum where members are also (and must be) ints"""
CodePudding user response:
Did a little digging, based on the comments below OP. To start of with why this is not working as expected: it is how Starlette handles path parameters. When a request is handled by Starlette (or to be precise, when the Router
object is called from which the APIRouter
inherits), it determines the path_params
as a dictionary with keys are parameter names and values as its value. However, when this happens and you haven't specified a type in the path string, it automatically treats the path parameters as string. It then adds this to the Request
, basically as a dict {"a":"1", "b":"2"}
. Then, further up the call stack, FastAPI is then trying to find the enum value corresponding to "1", which throws throws the validation error because "1" is not 1
. Note that your MyNumber
class does not play any role here yet.
This behaviour is by design and can be influenced like so:
@app.get("/add/{a:int}/{b:int}")
This will make sure that Starlette will create a path_params dictionary like {"a":1, "b":2}
. Note that 1
and 2
are now integers and will be handled as such by FastAPI.
As to why MyNumber(IntEnum)
works out of the box while MyNumber(int, Enum)
does not, that is a Pydantic implementation. I can't really seem to pinpoint how exactly, but when the ModelField
is created for both parameters (which happens in utils.py -> create_response_field()
upon application startup), the list of validators
is including an int
validator when using IntEnum
.
But because ModelField
is created using functools.partial()
I cannot follow the call stack into Pydantic. So, I am unsure why this behaves like this.
So, in short, there are two options to fix this: either force Starlette to parse the path paramaters a
and b
as int
(using {a:int}
) or inherit from IntEnum
in your own enum class.