Developer's Note
Regarding Path Parameter idiosyncrasies¶
If you decide to specify your path parameters using kwargs, only two options are there. - Using kwargs as path parameters - Embedding path parameters as f-strings
The second option is less error-prone as the full URL path is being constructed, while the first option is preferable if you don't like working with f-strings. It is an experimental feature which I thought would be beneficial but only time will tell how useful it really is.
Pydantic V1 Issues¶
If you are using pydantic v1, it is highly recommended to switch to v2 which offers stricter type-safety, validation and faster parsing. However, if you are still using it, here are a few caveats I observed that you might want to take into consideration:
-
In v1,
intandfloatare implicitly coerced tostr, whereas in v2 this raises aValidationError, because v2 by default disables type coercion fromintandfloattostr. This might be a problem when you are using pydantic request models in v1. For example, consider the following:from pydantic import BaseModel from arrest.params import Query class UserRequest(BaseModel): id: str await service.user.post("/profile", request=UserRequest(id=15)) # request body = {"id": "15"}
The above will throw a ValidationError in pydantic v2 unless you have coerce_numbers_to_str=True in ConfigDict
-
In pydantic v1,
parse_obj_ashelper function is used to convert objects of one type to another arbitrarty python type, as opposed to usingTypeAdapterfrom Pydantic v2. Theparse_obj_asfunction is inconsistent with the type conversions in comparison toTypeAdapter, because it does not throw aValidationErrorif an object of class B is attempted to be parsed as class A. For example, consider this:from pydantic import BaseModel from arrest import Resource from .service import service class UserRequest(BaseModel): id: Optional[int] = None name: Optional[str] = None class FooBar(BaseModel): foo: str bar: str service.add_resource( Resource( name="user", path="/user", handlers=[ ("POST", "/profile", UserRequest), ] ) ) await service.user.post("/profile", request=FooBar(foo="foo", bar="bar"))
The above will fail in v2 with ValidationError during attempting to parse the supplied request instance of FooBar as UserRequest.
But it will succeed and a request will be sent but the body will be an empty UserRequest instance, i.e. {"id": None, "name": None}.
Note: this can still raise ValidationError in v1, if, let's say, the UserRequest fields are all required, and non-overlapping with the fields in FooBar.