Changelog¶
All notable changes to this project will be documented in this file. Please follow the Keep a Changelog standard.
[Unreleased]¶
Added¶
- Added
schema(Model).had(description=...)to give the older versions of a model their own description in OpenAPI instead of the docstring of the latest model, andhad(description=None)to remove it. Blank descriptions are rejected. InHeadVersion, it also replaces the description of the latest version, e.g. to keep internal notes in the docstring out of OpenAPI.cadwyn renderrenders the description of the version as the docstring, and the deprecated changelog does not describe these changes. Apps that do not use it are not affected (#168). - Documented how to serve endpoints to requests without an API version, such as the OAuth2 token endpoint that Swagger UI's "Authorize" button calls without the version header: keep the endpoint unversioned or set
api_version_default_value(#230). - Documented how to use fastapi-pagination with Cadwyn: call
add_pagination()on theVersionedAPIRouterinstead of the app, or pass the pagination params topaginate()explicitly (#191). - Documented that computed fields that return versioned models keep the latest versions of these models in older API versions, in both serialization and OpenAPI, and how to expose such data as a regular field instead.
Changed¶
- Breaking: parametrized generic models such as
Page[Item]now use the versioned copies of their type args in requests, responses, and OpenAPI (#416). Before, they kept the latest versions of their type args in every version. There is no option to keep the previous behavior. The first five changes below only apply to generics whose type args refer to models or enums that have schema or enum instructions in any version, includingHeadVersion. The last two also apply to generics with any other type args, such asBox[str]orclass Ints(RootModel[list[int]]), when they, their subclasses, or the parametrizations in their fields have instructions:- Older versions validate and serialize these args with their own versions of the models instead of the latest ones, so clients of older versions that relied on latest-shaped models inside generics may need converters for the generics (converters for
Itemdo not run for the items ofPage[Item]). This also applies to args that refer to self-referencing models: e.g. ifItem.coloris an enum thatenum(Color).didnt_have("blue")changes andItemalso hascategory: Categorywith a self-referencingCategory, older versions reject"blue"inPage[Item]request bodies with a 422, and responses that contain it fail. - Changes from
HeadVersionalso apply to these args in every version, including the latest one. - The OpenAPI names of these generics can change in every version because their quoted type args are evaluated, e.g.
Page[list["Item"]]is namedPage_list_Item__instead ofPage_list_str__. migrate_response_bodyno longer accepts instances of the latest models inside these generics, e.g.Page[Item](...)or{"items": [Item(...)]}forPage[Item], and raises aValidationErrorfor them in every version, including the latest one, as it already did for other versioned models nested in the response model. Pass plain data such as the result ofmodel_dump()instead.- The models in these args are copied for every version, so the ones that cannot be copied (e.g. with an
Annotatedvalidator that holds athreading.Lock) fail even when no route uses them directly. schema(...).field(...).had(...)and.didnt_have(...)for a field that a model inherits from a generic base through its type args keep the field's type in older versions. The most common case is aRootModelsubclass such asclass Ints(RootModel[list[int]])withschema(Ints).field("root"); others are subclasses of parametrized generics such asclass StrIntPair(Pair[str, int]), and, on Pydantic 2.13 and newer, the parametrizations themselves such asschema(Pair[str, int]).field("second"). Before, such a field became the bare type var (e.g.~RootModelRootTypeor~U) in older versions, so these versions accepted any value for it, and invalid input failed with a 500 (CadwynHeadRequestValidationError) instead of a 422. Now the field behaves in older versions like any other field of its type: its validators and coercion run there and again in the latest version, serializers that change its value also change the request body that reaches the latest version, models in its type args validate their older shape, and request migrations no longer see values that the field's type rejects. Also as for other fields,had()no longer overrides the constraints that the field's type declares throughAnnotated[..., Field(...)]: e.g. withclass Concrete(Box[Annotated[int, Field(ge=0)]]),schema(Concrete).field("value").had(ge=5)no longer applies, so older versions documentminimum: 0instead of 5 in OpenAPI and, on Pydantic 2.12 and newer, accept values such as1that they rejected with a 422 before.- Instructions for a specific parametrization of a generic model, such as
schema(Pair[str, int]), now also apply where that parametrization is reached through a field of another generic model, such aspair: Pair[T, int]inBox[T]used asBox[str], including throughOptional,list, and further generic models. Older versions of such models validate, serialize, and document the parametrization with its instructions, like the routes that use it directly, so request bodies with such models need the same request migrations as the parametrization itself. Before, these fields kept the parametrization without its instructions. Fields keep their previous behavior when the generic model, its bases, or the parametrization that has the field have instructions of their own, when pydantic refers to the parametrization recursively inside itself (e.g.children: "list[Union[Pair[T, int], Node[T]]]"inNode[T]), or when such parametrizations refer to each other (#420).
- Older versions validate and serialize these args with their own versions of the models instead of the latest ones, so clients of older versions that relied on latest-shaped models inside generics may need converters for the generics (converters for
- Models that refer to themselves, directly or through other models (e.g.
children: "list[Category]"), are no longer copied for every version if they have no schema instructions in any version, includingHeadVersion, do not refer to models or enums with instructions, and are not parents of models that need versioned copies. Every version uses the original model itself, sogenerate_versioned_models(versions)[version][Category] is Category. All other models are copied as before. Models defined after Cadwyn has generated the versioned models, e.g. a subclass of a self-referencing model with a field of a changed model, do not change which self-referencing models are reused.
Fixed¶
- Fixed older API versions using current request models when endpoints use postponed annotations, a regression introduced in 6.0.1.
- Upgrade note: this applies to endpoints whose request body annotations are postponed (
from __future__ import annotations) or quoted. Since 6.0.1, older API versions of such endpoints validated requests with the latest models, so schema instructions did not affect their request bodies. Now they validate them with their own versions of the models, as documented, so the instructions take effect for these requests: removed or renamed fields, changed types and constraints (e.g..had(strict=True)or a narrower enum), defaults removed with.didnt_have("default"), aliases, discriminators, and so on. Clients of older versions that kept sending the latest shape may now get a 422 or have unknown fields dropped, so check that each older version's instructions describe what its clients actually send. This includes the instructions that a subclass gives for a field it inherits: when the model that defines the field has instructions of its own, they also change the field of that model and of its other subclasses in older versions, as they always did for responses and for the request bodies of other endpoints. E.g. withschema(Invoice).field("amount").had(...)andschema(BusinessInvoice).field("code").had(max_length=5), older versions now also reject aPersonalInvoicerequest body whosecodeis longer. Instructions on fields that a model inherits from a parametrized generic base, such asschema(Ints).field("root")forclass Ints(RootModel[list[int]])or a field ofclass StrIntPair(Pair[str, int]), also take effect for these requests: these fields keep their real type in older versions (see the breaking change for generic models above). - Apps served with
uvicorn module:app(oruvicorn.run(app)) returning a 500 for every request since 7.1.1: uvicorn mistookCadwynfor an app factory becauseCadwyn.__call__accepted any arguments - Fixed OpenAPI documenting the API version header (
x-api-versionby default) as optional whenapi_version_default_valueis not set, although the server returns a 404 for requests without it (#277). The header is now required in OpenAPI in this case. It keeps the version of the document as its default and example, so clients generated from OpenAPI send it by default, and Swagger UI still fills it in for "Try it out". Only OpenAPI changes: requests are handled exactly as before. OpenAPI does not change whenapi_version_default_valueis set. - Fixed every request and the OpenAPI of an app failing with a 500 (
RecursionError, reported as anImportError) when a route uses a model that refers to itself, directly or through other models, such aschildren: "list[Category]", even with a single version (#419). - Fixed apps failing to start (
RecursionError, reported as anImportError) when models that refer to themselves, directly or through other models, need versioning, e.g. when a self-referencing model or a model that it refers to has instructions. Their copies now refer to each other, so older versions use their own shape at every depth of a tree, and generic models such asPage[Category]use these copies as their type args when the models with instructions or the routes refer to such models. This requires Pydantic 2.11.5 or newer. With older Pydantic versions, and for such models that Cadwyn cannot copy, Cadwyn now raises aSchemaGenerationErrorthat names the models instead. Cadwyn cannot copy them when they refer to each other in default factories,Annotatedmetadata, type aliases, or the type args of custom generics; when the copies of the version would keep the latest versions of such models, e.g. in the return types of computed fields or serializers, in names quoted inside other annotations such aslist["Book"], or in generics that keep the latest models; when the defaults of the fields that use them contain instances of latest models that need versioning or their default factories cannot be verified to create the versioned copies (e.g. lambdas); when their annotations refer to names that are not defined yet; or when a model with instructions refers to them only through the type args of a generic while a route uses them directly. When onlymigrate_response_bodyuses such models directly, the type args of generics keep the latest models, as before (#419). - Fixed
/changelogfailing with a 500 (KeyError) for changes of models that refer to themselves, including through generic fields such aschildren: "Page[Category]". - Fixed older API versions adding
"example": {}to the OpenAPI of path, query, header, cookie, and body parameters declared through default values such as= Query()(#417). - Restored type checking compatibility with Pydantic 2.14 and ty 0.0.86.
- Fixed a
TypeError("cannot be parametrized because it does not inherit from typing.Generic") in older API versions for generic models whose fields parametrize generic models with their own type parameters, such asitems: list[Wrapper[T]]ornext: Optional["LinkedPage[T]"]. - Fixed older API versions failing to start when a model uses
typing.Selfin its annotations. - Fixed older API versions failing to start when a model uses annotations that cannot be rebuilt from their arguments, such as a bare
typing.CallableorUnpack[Ts], or a default factory without an inspectable signature, such asfunctools.partial(dict, count=1). - Fixed older API versions failing on Python 3.14 when versioned models or their type aliases use names that are only imported under
TYPE_CHECKING, e.g. inClassVarannotations. - Fixed enums that have no enum instructions in any version being copied for every version. Their members were not the members of the original enum, so strict models rejected them in responses (a 500), and request bodies with such enums failed with
CadwynHeadRequestValidationErrorin every version as soon as any model had instructions. - Fixed
TypeError: unhashable typeon older API versions when an endpoint's request body or response model is an unhashable annotation such aslist[Annotated[int, {"description": "item"}]]. - Fixed
functools.partialdependencies losing the annotations of their remaining parameters, which turned their sub-dependencies and request bodies into required query parameters. Partials of async functions also returned unawaited coroutines. - Fixed dependencies whose parameters are only described by
__signature__losing their parameter types, which skipped type conversion, crashed constraints such asgewith a 500, and removed the types from OpenAPI. - Fixed async generator dependencies not receiving exceptions raised after their
yield, which skipped theirexceptblocks (e.g. a database rollback) and delayed their cleanup until garbage collection. - Fixed versioned dependencies losing the
scopeofDependsandSecurity. - Fixed
app.dependency_overridesgetting ignored in older API versions for dependencies that are class instances, bound methods, or partials. Older versions used copies of such dependencies, which also hid changes to their state. - Fixed parameter defaults such as
filters: Filters = Query()in endpoints and dependencies being shared between API versions and between apps built from the same router. FastAPI changes them while building routes, so their OpenAPI and validation could use the models of another version or app. - Fixed decorated dependencies, such as
functools.lru_cacheones, failing in older API versions when their annotations are postponed (from __future__ import annotations). - Fixed a
NameErrorat startup on Python 3.14 when a dependency's annotations use a name that only exists for type checkers (e.g. one imported underif TYPE_CHECKING:). - Fixed router generation failing for dependencies whose return annotation only type checkers can evaluate, such as
-> "pd.Series[int]"with postponed annotations, or whose returned model has a default factory without a signature, such astime.timebefore Python 3.13. Like FastAPI, Cadwyn no longer evaluates or versions the return annotations of dependencies. - Fixed
Annotateddependencies whose type is not versioned, such asAnnotated[list[int], Depends(get_values)], using the dependency of the head or another API version. This made versioned request constraints, such as amin_lengthor a removed enum member, get ignored in both validation and OpenAPI. Older versions now apply their own versions of such dependencies, so they can reject values that the latest version accepts (e.g. an enum member that an older version doesn't have), as they already did for dependencies declared withoutAnnotated. - Fixed
schema(...).field(...).didnt_have(...)having no effect in older API versions on the attributes that document a field (title,description,examples, anddeprecated) declared throughAnnotated[..., Field(...)]. It still has no effect there on other attributes, such as constraints, aliases,default,exclude, orjson_schema_extra(which validators can read), because removing them could make older versions reject or change the data that they have always accepted or returned. - Fixed
schema(...).field(...).didnt_have("title")failing withTypeErrorwhen the field's type isAnnotated, for example because it has anAfterValidatoror a documentation string. - Fixed
schema(...).field(...).didnt_have("min_length")failing withTypeErrorwhen the field's type hasannotated_types.Len. - Fixed
schema(...).field(...).didnt_have(...)failing withTypeErrorwhen the field's type uses theStrictorAllowInfNanclass itself, such asAnnotated[int, Strict]. - Fixed
schema(...).field(...).had(default=...)or.had(default_factory=...)that leave a field with both a default and a default factory in a version failing with pydantic'sTypeErrorat startup, e.g..had(default_factory=f)for a field declared with a default, including a default from theField()in itsAnnotatedtype. Cadwyn now raisesInvalidGenerationInstructionErrorinstead. It names the field and the model of the last instruction that changed the field's default or default factory, which may be a parent or a sibling of the model whose field it is. Like in pydantic,default=...anddefault_factory=Nonedo not count, so to replace one of them with the other, unset it in the same instruction (e.g..had(default=..., default_factory=f)) or in an earlier one. - Fixed instructions that replace a field's default with a default factory or vice versa failing with pydantic's
TypeErrorat startup when theField()in the field'sAnnotatedtype still sets the replaced one, e.g..had(default=..., default_factory=f)forx: Annotated[int, Field(default=0)], or.didnt_have("default")followed by.had(default_factory=f)in an older version. The one from the instructions now replaces the one from the annotation in older versions. Instructions that leave the field itself with both still raiseInvalidGenerationInstructionError, e.g..had(default_factory=f)alone for that field. - Fixed responses failing with a 500 (
ResponseValidationError) in every API version (including the latest one), andmigrate_response_bodyraising aValidationError, when a required field of the response model validates a different plain key than it serializes to, such asField(alias="x", validation_alias="v"),Field(validation_alias="v"),Field(serialization_alias="s"),AliasChoiceswith a plain key, anAliasGeneratorwith different aliases (#370), orvalidate_by_alias=False, or when the response model forbids extra keys. Responses are still validated once, as before: only when a response would surely fail as it is, because a required field or a model that forbids extra keys gets a dumped key, Cadwyn validates it with the keys that the response model validates instead, and uses the result if it passes. Otherwise, FastAPI validates the response as it is, so responses that passed validation before don't change at all, even if such a field got its default instead of its value, and failing responses get their original errors, also when the rescue raises an exception. This also covers such fields in nested models, in lists and dicts of models (also as the response model itself), inOptionalandRootModelresponse models, and withresponse_model_by_alias=False. Response migrations still see the same keys as before. Such responses fail as before in the same cases as request bodies (see below), when the response model itself has "before", "wrap" or "plain" validators, and when the endpoint doesn't return instances of the route's response models wherever the route's response model expects one, e.g. when it returns dicts, which FastAPI validates as they are,None, other models, or an unparametrized generic model such asPage(items=...)forPage[Item]. Instances of subclasses count only if they dump the fields of the response model the same way, i.e. without their own serializers,model_dump()overrides, fields redeclared with another type or otherAnnotatedmetadata,extra="allow", or fields and computed fields that use the keys of the response model's fields. Cadwyn doesn't check what the returned models contain, so dicts in their fields, and whatever response migrations put into the body, are restored like the rest of the body. - Fixed request bodies failing in every API version except head (including the latest one) with
CadwynHeadRequestValidationErrorwhen a required field of the body validates a different plain key than it serializes to, such asField(alias="x", validation_alias="v"),Field(validation_alias="v"),Field(serialization_alias="s"),AliasChoiceswith a plain key, anAliasGeneratorwith different aliases, orvalidate_by_alias=False, or when the body model forbids extra keys. Cadwyn now gives the head model the keys that it validates, but only when the head would surely reject the body otherwise, i.e. when such a required field, or a model that forbids extra keys, gets a dumped key. Requests that worked before don't change at all: optional fields with such aliases still get their defaults as before, and the head still validates each body once. If the head rejects the restored body too, the request fails with the same error as before. This also covers such fields in nested models, in lists and dicts of models, inRootModelbodies, and in form models. Request migrations still see the same keys as before. Such bodies fail as before when Cadwyn can't be sure that their dumped values are valid input, e.g. with custom serializers (also on nested models, such asAnnotated[Model, PlainSerializer(...)]),Json,OnErrorOmit, "before", "wrap" or "plain" validators (also on the body parameter), a custom__init__ormodel_dump(), computed fields that share keys with fields,AliasPath-only aliases,extra="allow", or keys shared by several fields. - Fixed requests failing with
CadwynHeadRequestValidationError(a 500), or optional bodies silently arriving asNone, in every API version except head, including the latest one, when the request body is embedded (Body(embed=True), a single non-modelForm()field, or a body parameter next to the body parameter of a dependency) and the endpoint has a parameter namedbody, such asbody: Optional[Item] = Body(None, embed=True), or when a single body parameter has an alias that is the name of another non-dependency parameter, such asitem: Item = Body(alias="filter")next tofilter: str = Query(). Request migrations of such endpoints now see the request body as the client sent it, like the ones of endpoints whose parameters have other names. File uploads keep failing as before. - Fixed uploaded files declared as
bytesin endpoint parameters silently arriving empty (b"", with a 200) in every API version except head, including the latest one, e.g.upload: bytes = File(),uploads: list[bytes] = File(), orupload: bytes = File()next toname: str = Form(). FastAPI had already read these files for the versioned route, and Cadwyn passed them on to the head route as they were. Cadwyn now passes such files to the head route from their start, unless a request migration replaced, moved, closed, or swapped the stream of them. So dependencies that read the same upload, e.g. as anUploadFile, now see its contents in these versions too, as in FastAPI itself. Note that thesebytesuploads are now held in memory twice per request in these versions. - Fixed uploaded files declared as
bytesin the parameters of dependencies, including nested ones, silently arriving empty (b"", with a 200) in every API version except head, including the latest one, e.g.async def get_data(data: bytes = File())used asDepends(get_data). They now arrive like the ones in endpoint parameters, as in FastAPI itself: when several params read the same file, the first one gets its contents. Overridden dependencies and files that a dependency moved, replaced, or closed after they were read keep working as before. In API versions that add dependencies to an endpoint (endpoint(...).had(dependencies=...)), thebytesfiles of dependencies keep working as before too, i.e. they still arrive empty, because the added dependencies don't run again for the head and might have changed the files. A dependency that moves an uploaded file only whencurrent_dependency_solveris"fastapi"(e.g. to skip a header) can now have the file moved back to its start if FastAPI has read it to its end for abytesparam. Move it in the run that gives the endpoint its arguments instead, as the docs on dependency re-execution now describe. The docs also describeRequestInfo.formand that request migrations reading uploaded files must seek them back.
[7.4.0]¶
Fixed¶
- Preserve SQLAlchemy and SQLModel classes during API version generation, fixing startup failures for annotated ORM dependencies. Reject migrations targeting these classes with guidance to use separate Pydantic models, and document this restriction in the schema migrations guide (#410).
[7.3.0]¶
Added¶
- Added native View Transition API animations to documentation navigation, with reduced-motion and unsupported-browser fallbacks.
Changed¶
- Updated Starlette documentation links to the project's current official domain.
Fixed¶
- Restored compatibility with ty 0.0.78 while preserving FastAPI's coroutine detection on every supported Python version.
- Preserved the
classmethodbinding of@field_serializerdecorators during model generation. Previously, such serializers were re-registered as instance methods, shifting their arguments by one and raisingPydanticSerializationErroron every serialization.
[7.2.0]¶
Deprecated¶
- Deprecated changelog generation, including the default
GET /changelogendpoint,Cadwyn.generate_changelog(),cadwyn.hidden(), and thechangelog_urlandinclude_changelog_url_in_schemaconstructor arguments. The feature remains available for backwards compatibility.
[7.1.2]¶
Changed¶
- Simplified contributor setup and development commands (#387)
- Standardized test names around the
what,when, andexpectedconvention and documented test organization (#397) - Expanded Ruff coverage, including return annotations, preview correctness checks, and explicit tracking of reviewed rules
Fixed¶
- Restored compatibility with FastAPI 0.140.2+ after it stopped retaining flattened dependency trees
[7.1.1]¶
Changed¶
- Migrated static type checking from Pyright to ty
Fixed¶
- Return the matched API version in the response header after waterfalling, by @Sanjays2402 (#384)
[7.1.0]¶
Added¶
- Support FastAPI 0.137.1+ included-router trees, including routes added to included routers after inclusion, for Cadwyn's versioned and unversioned routing
[7.0.0]¶
- Breaking change if you relied on lifespan running multiple times: Fixed duplicated lifecycle callbacks in Cadwyn routers, see this issue for more details
- Fixed
Cadwyn.mount()routes, includingStaticFiles, returning 404 instead of being served as unversioned routes (#374)
[6.2.2]¶
[6.2.1]¶
Fixed¶
- Fixed
openapi_tagsto only include tags used by served routes (#368) - Fixed
response_modelnot being respected on routes (#365)
[6.2.0]¶
Added¶
- Added
swagger_js_url,swagger_css_url,swagger_favicon_url,redoc_js_url, andredoc_favicon_urlparameters toCadwynfor customizing docs static asset URLs (#361)
[6.1.1]¶
Fixed¶
- Fixed compatibility with Starlette 1.0.0 which removed the deprecated
TemplateResponse(name, context)calling convention
[6.1.0]¶
Added¶
- Added
formproperty toRequestInfofor reading and modifying multipart/form-encoded fields (strings and file uploads) insideconvert_request_to_next_version_forhandlers (#359)
Fixed¶
- Fixed false
RouterPathParamsModifiedErrorwhen renaming path-versioned endpoints (#357)
[6.0.4]¶
Added¶
- Python 3.14 support (#345)
Fixed¶
- Fixed missing type imports with future annotations in
Dependleading toRecursionErroringenerate_versioned_routersby @Shigerman (#329) - Resolved
'__getitem__' requires a 'typing.Union'error by @ulgens (#336) - Adopted Python 3.14 related union handling changes by @ulgens (#337)
- Adopted Python 3.14 related
http.cookieschanges by @ulgens (#338)
Removed¶
- Python 3.9 support by @ulgens (#342)
[6.0.3]¶
Fixed¶
- Tolerate FastAPI 0.128+ utils removal, restrict fastapi >= 0.128.6 by @shahar1 (#327)
[6.0.2]¶
Fixed¶
- Resolved
...has no attribute '__annotations__'error by @ulgens (#334)
[6.0.1]¶
Fixed¶
- Fixed AttributeError when using callable class dependencies with
from __future__ import annotations. Note that this introduces another problem in python 3.9 in the same case that is currently not yet solved (#321)
[6.0.0]¶
Fixed¶
- Support for FastAPI>=0.128.0+
Removed¶
- Support for FastAPI < 0.128.0 and Pydantic v1
[5.6.2]¶
Fixed¶
- Support for FastAPI 0.126.0+ where
_prepare_response_contentmay return Pydantic model instances instead of dicts, breaking response migrations (#314)
[5.6.1]¶
Fixed¶
- Support for FastAPI 0.121.0
[5.6.0]¶
Added¶
schema(...).had(json_schema_extra=...)support by @csemanish12
[5.5.0]¶
- Fix the rest of the issues from fastapi==0.119.0
[5.4.5]¶
Fixed¶
- Many issues after GenerateJsonSchema getting removed in fastapi==0.119.0
Changed¶
- A lot of documentation improvements by @Shigerman
[5.4.4]¶
Fixed¶
- Fixed KeyError when versioning Pydantic models containing ClassVar annotations
[5.4.3]¶
Fixed¶
- XSS vulnerability in
/docsand/redocendpoints where theversionparameter was not properly sanitized, allowing potential cross-site scripting attacks
[5.4.2]¶
Fixed¶
- Added support for computed fields
[5.4.1]¶
Fixed¶
- Fixed import error in python 3.9 when using typing_extensions==3.14.0
[5.4.0]¶
Added¶
typing_inspectiondependency from pydantic team for complex isinstance checks that must be the same betweentypingandtyping_extensions- Support for
pydantic>=2.12.0FieldInfo refactoring from https://github.com/pydantic/pydantic/pull/11898
[5.3.3]¶
Fixed¶
- Fixed pypi badge link in README
[5.3.2]¶
Fixed¶
- Add support for
pydantic_settings.BaseSettingsin dependencies
[5.3.1]¶
Fixed¶
- Fix invalid content-length header with custom exception handlers (#273)
[5.3.0]¶
Added¶
api_version_titleandapi_version_descriptionarguments toCadwynto allow customizing the API version parameter in the OpenAPI schema
[5.2.2]¶
Fixed¶
- Whenever a route was migrated by path, we used the path and methods of the request. So if the request wanted "/v1/webhook_settings" and we renamed them to "/v1/webhook_subscriptions" -- all migrations for "/v1/webhook_subscriptions" would not get applied to any requests that wanted "/v1/webhook_settings". This effectively means that previously route renamings were incompatible with by path converters. Now we use the head route id instead of the path and methods of the request for matching so we always know which migrations to apply.
[5.2.1]¶
Fixed¶
- #268 A bug where we received 404 for all unversioned routes when a default api version was passed to Cadwyn at initialization
[5.2.0]¶
Removed¶
issubclassdependency
[5.1.4]¶
Fixed¶
- a bug where a custom HTTP exception was not being copied correctly so its unique properties are ignored
[5.1.3]¶
Fixed¶
__doc__attribute is now copied from the original enum to the generated enum- Python 3.12 type aliases and their typing_extensions backports are now supported (including
pydantic.JsonValueand othertyping_extensions.TypeAliasTypeinstances) - The bug when solve_dependencies error on the migration of a request to the latest version responds with a non-json serializable error and cadwyn showed a failed to serialize error instead of the actual error
- Updated minimum FastAPI version to 0.112.4 because embed_body_fields was added in 0.112.4
[5.1.2]¶
Fixed¶
- Generators not being called when FastAPI validates the initial request
Added¶
cadwyn.current_dependency_solverfunction to check whether cadwyn or FastAPI is currently solving dependencies. It can be used as a dependency for versioned endpoints like so:current_dependency_solver: Annotated[Literal["fastapi", "cadwyn"], Depends(current_dependency_solver)]. If your dependency has side effects, you would likely want to only run it once per request. If you want to run it once we migrated the request to the latest version, you should only run it whencurrent_dependency_solveris"cadwyn". If you want your dependency to run at the very beginning of handling the request, you should only run it whencurrent_dependency_solveris"fastapi".
[5.1.1]¶
Fixed¶
- Support for
WithJsonSchemain schema generation
[5.1.0]¶
Added¶
- Support for python 3.9
[5.0.0]¶
Added¶
- Support for URL path version prefixes instead of version headers. You can control it with the
api_version_locationargument ofcadwyn.Cadwyn. - Support for arbitrary strings as versions. You can control the format of the version with the
api_version_formatargument ofcadwyn.Cadwyn. - Extensibility of version picking logic with a new
VersionPickingMiddlewareclass that you can pass to theversioning_middleware_classargument ofcadwyn.Cadwyn. api_version_default_valueargument tocadwyn.Cadwynto set a default version for unversioned requests. It can be a string or an async callable that returns a string.
Changed¶
cadwyn.Version,cadwyn.VersionBundle, andcadwyn.VersionBundle.api_version_varnow store versions as strings instead of dates. Date types can still be passed tocadwyn.Versionbut there is no guarantee that they will be supported in the future. However, ISO dates with a string type are guaranteed to be supported.- Cadwyn's
api_version_header_nameargument is now deprecated in favor ofapi_version_parameter_name cadwyn.Cadwyn.add_header_versioned_routersmethod is now deprecated in favor ofcadwyn.Cadwyn.generate_and_include_versioned_routers. It will be removed in version 6.0.0
Removed¶
HeaderVersioningMiddlewarein favor ofVersionPickingMiddlewarebecause we now support more than just headers
[4.6.0]¶
Added¶
- Support for more field attributes in
schema.had()andschema.didnt_have():field_title_generator,fail_fast,coerce_numbers_to_str,union_mode,allow_mutation,pattern,discriminator - Support for forwardrefs in body fields (for example, when you use
from __future__ import annotationsin the file with your routes) - Support for forwardrefs in route dependencies
[4.5.0]¶
Added¶
check_usageargument to request/response by schema converters. Cadwyn always checks whether a schema mentioned in a converter applies to one or more endpoints to guarantee that the converter will apply to at least one endpoint. Sometimes, however, you do not need this validation. For example, when you use these converters for converting webhook bodies. Settingcheck_usage=Falsemakes it possible to skip the validation
[4.4.5]¶
Fixed¶
- Fix invalid migration of pydantic v1 style root validators when pydantic erases information about their "skip_on_failure" attribute
[4.4.4]¶
Fixed¶
- Type hints for newest pydantic versions
[4.4.3]¶
Changed¶
- Bumped package versions
[4.4.2]¶
Fixed¶
- Non-function (object instances) dependencies not supporting dependency overrides in testing
[4.4.1]¶
Added¶
- Python 3.13 to CI
[4.4.0]¶
Added¶
- Support for webhooks in swagger
- Automatic generation of versioned routes and webhooks upon the first request to Cadwyn. Notice that if you were using some of cadwyn's internal interfaces, this might break your code. If it did, make an issue and let's discuss your use case
[4.3.1]¶
Fixed¶
- Removed typer from main dependencies
[4.3.0]¶
Changed¶
- Migrated from poetry to uv (Contributed by @bastienpo)
- Removed CLI and Uvicorn dependencies from Cadwyn installations by default. Added a
standardextras group to mirror FastAPI (Contributed by @bastienpo)
[4.2.4]¶
Fixed¶
- Background tasks not functioning in versioned endpoints
[4.2.3]¶
Fixed¶
- Added support for
embed_body_fieldsinsolve_dependenciesandcreate_model_fieldin FastAPI. FastAPI has made a breaking change for these interfaces which is why we had to fix it - Fixed invalid imports in quickstart docs
- Fixed default dependencies not including the CLI for FastAPI, thus causing the quickstart docs to be invalid
[4.2.2]¶
Fixed¶
- FastAPI Servers list did not include root path when mounted as a sub-app unless specified directly within the user defined server list (Contributed by @OD-tpeko)
- OpenAPI spec did not include the
summaryfield (Contributed by @OD-tpeko)
[4.2.1]¶
Fixed¶
- Previously the docs were showing wrong versioned doc paths when cadwyn was mounted as a sub-app (Contributed by @OD-tpeko)
[4.2.0]¶
Added¶
- Automatic changelog generation from version changes using
Cadwyn.generate_changelogmethod andGET /changelogendpoint. - Automatic creation of versioned_routers based on the
VersionBundlepassed toCadwynwhich means that all versions mentioned in the version bundle will already be available for routing even without the use ofgenerate_and_include_versioned_routers
Changed¶
- Renamed
Version.version_changestoVersion.changes
Removed¶
regex,include,min_items,max_items, andunique_itemsarguments were removed fromschema(...).field(...).had. Notice that it's not a breaking change for most cases because passing these arguments caused exceptions
[4.1.0]¶
Added¶
- Exposed
cadwyn.generate_versioned_modelsthat allow you to generate pydantic models and enums even without a FastAPI app
[4.0.0]¶
Versions 3.x.x are still supported in terms of bug and security fixes but all the new features will go into versions 4.x.x.
Added¶
- Runtime schema/enum generation
- Support for versions as ISO date strings instead of dates in
cadwyn.migrate_response_bodyandcadwyn.Version
Removed¶
- Pydantic 1 support
- Code generation from everywhere. It is now completely replaced by runtime generation (so schemas/enums are generated in the same manner as endpoints). This allows Cadwyn to version things outside of your project and allows you to pick any project structure unlike codegen that required a single "head" directory with all the versioned modules.
- CLI commands for codegen
cadwyn.mainbecause it is replaced bycadwyn.__init__cadwyn.structure.moduleas it was only necessary in codegencadwyn.VersionBundle.latest_schemas_packageandcadwyn.VersionBundle.head_schemas_package,cadwyn.VersionBundle.versioned_modules,cadwyn.VersionBundle.versioned_directories_with_head,cadwyn.VersionBundle.versioned_directories_without_head, because they were only necessary in code generationcadwyn.Cadwyn.add_unversioned_routersas you can now simply use FastAPI'sinclude_routercadwyn.Cadwyn.add_unversioned_routesas you can now simply use any of FastAPI's methods for adding routes directly to the appcadwyn.Cadwyn.enrich_swaggeras its functionality has been automatedcadwyn.InternalRepresentationOfas it was deprecated previously and is now replaced with HeadVersion migrations
Changed¶
VersionBundle.migrate_response_bodyis no longer a method ofVersionBundleand is now importable directly fromcadwynas a functioncadwyn.structureis no longer recommended to be used directly because everything from it is now available incadwyndirectly
[3.15.8]¶
Fixed¶
- Invalid unparseable JSON response without quotes when the response was a raw string JSONResponse
- An exception raised during codegen if a pydantic model or its parent were created within some indent such as classes defined under if statements
[3.15.7]¶
Fixed¶
- Wrong globals being used for wrapped endpoints in older versions, sometimes causing FastAPI to fail to resolve forward references on endpoint generation (see #192 for more details)
- dependency_overrides not working for old versions of the endpoints because they were wrapped and wraps did not have the same
__hash__and__eq__as the original dependencies
[3.15.6]¶
Added¶
HeadVersionandVersionintocadwyn.__init__so now they are directly importable fromcadwyn
Fixed¶
- Fix dependencies from other libraries not resolving if they use fastapi.Request or fastapi.Response (added svcs-specific test)
- Now a proper exception is used when no dated version was passed into
VersionBundle
[3.15.5]¶
Fixed¶
- Fix dependency overrides not working for versioned routes
[3.15.4]¶
Changed¶
Cadwyn.enrich_swaggeris now completely unnecessary: OpenAPI is now generated at runtime. It also now does not do anything, is deprecated, and will be removed in a future version
Fixed¶
- fastapi-pagination now does not require an explicit call to
Cadwyn.enrich_swagger
[3.15.3]¶
Changed¶
Cadwyn.router.routesnow includes all existing routers within the application instead of just unversioned routes
Fixed¶
- Compatibility with fastapi-pagination
- High cardinality of metrics for routers with path variables in starlette-exporter
[3.15.2]¶
Fixed¶
- OpenAPI not being generated when lifespan was used
[3.15.1]¶
Fixed¶
- Badge links in the readme
[3.15.0]¶
Changed¶
- Optimized the calls to enrich_swagger which now happen on the startup event, once for the whole application
Fixed¶
- Oauth2 authentication parameters did not get passed to swagger
[3.14.0]¶
Added¶
- Current API version to per-version openapi.json
[3.13.0]¶
Added¶
- Validation for path converters to make sure that impossible HTTP methods cannot be used
- Validation for both path and schema converters to make sure that they are used at some point. Otherwise, router generation will raise an error
[3.12.1]¶
Fixed¶
fastapi.Responsesubclasses with non-null bodies and 500 response causing the response to not get returnedfastapi.Responsesubclasses had invalid content length if migration affected it
[3.12.0]¶
Changed¶
- Rewritten header routing logic and structure to support the full feature set of FastAPI
[3.11.1]¶
Fixed¶
- Modules and enums from head versions not being detected and thus causing errors
[3.11.0]¶
Changed¶
- Header router is no longer reliant on the API version header -- now it simply takes the API version from the
VersionBundle.api_version_var, thus making it easy for someone to extend header routing and set their own rules for how the default version is chosen
[3.10.1]¶
Fixed¶
- Previous version introduced a minor breaking change: if any old users depended on the pure
generate_versioned_routersinterface, their work would receive a minor yet simple breaking change.
[3.10.0]¶
Yanked due to a minor breaking change that we fixed in 3.10.1.
Added¶
- The new approach to internal schemas: instead of having them duplicate certain fields from
latest, we introduced a newHEADversion -- the only one the user maintains by hand. All requests get migrated toHEADand latest schemas are generated fromHEAD.cadwyn.structure.HeadVersionwas added to give us the ability to have migrations betweenHEADand latest, thus eliminating the need forInternalRepresentationOfbecause all the used schemas are now the internal representations
Changed¶
latestis now namedheadbecause it no longer represents the newest version. Instead, it is the theinternally usedversion and the version that is used for generating all other versions.- the newest version is not aliased from
latestanymore. Instead, it is generated like all the rest - deprecated
InternalRepresentationOfand the concept ofinternal schemasin favor ofHeadVersionmigrations
[3.9.1]¶
Fixed¶
- A broken link to docs in README.md
[3.9.0]¶
Added¶
- Support for getting openapi.json routes using API version headers instead of path query params
[3.8.0]¶
Added¶
- Discord status badge in README
- Logos to existing status badges in README
- An ability to specify multiple schemas when using
convert_request_to_next_version_forandconvert_response_to_next_version_forto be able to migrate multiple types of schemas using the same converter - Redoc support
Removed¶
- Dependency from verselect. Now it is included as a part of Cadwyn
Fixed¶
h11._util.LocalProtocolErrorwhen raisingHTTPException(status_code=500)
[3.7.1]¶
Fixed¶
- Error message for changing path params of an endpoint in an incompatible manner which listed methods instead of path params
Changed¶
- Deprecated
cadwyn generate-code-for-versioned-packagesand addedcadwyn codegeninstead. It doesn't requiretemplate_packageargument anymore and does not have theignore_coverage_for_latest_aliasesargument as we plan to remove this feature in the future. So it only requiresversion_bundle.
[3.7.0]¶
Changed¶
- Deprecated
cadwyn generate-code-for-versioned-packagesand addedcadwyn codegeninstead. It doesn't requiretemplate_packageargument anymore and does not have theignore_coverage_for_latest_aliasesargument as we plan to remove this feature in the future. So it only requiresversion_bundle.
[3.6.6]¶
Fixed¶
- When a class-based dependency from fastapi was used (anything security related), FastAPI had hardcoded
isinstancechecks for it which it used to enrich swagger with functionality. But when the dependencies were wrapped into our function wrappers, these checks stopped passing, thus breaking this functionality in swagger. Now we ignore all dependencies that FastAPI creates. This also introduces a hard-to-solve bug: if FastAPI's class-based security dependency was subclassed and then__call__was overridden with new dependencies that are versioned -- we will not migrate them from version to version. I hope this is an extremely rare use case though. In fact, such use case breaks Liskov Substitution Principle and doesn't make much sense because security classes already includerequestparameter which means that no extra dependencies or parameters are necessary.
[3.6.5]¶
Fixed¶
- When a class-based dependency was used, its dependant was incorrectly generated, causing all affected endpoints to completely stop functioning
[3.6.4] ¶
[3.6.3]¶
Fixed¶
- A rare pydantic 2 bug that caused
BaseModelannotations to be corrupted when new fields were added to the schema
[3.6.2]¶
Fixed¶
- Removed exception when creating
cadwyn.Cadwynwithoutlatest_schemas_packageas it was a minor breaking change
[3.6.0]¶
Added¶
- Add
cadwyn.VersionBundle.migrate_response_bodythat allows us to migrate response bodies outside of routing and FastAPI latest_schemas_packageargument tocadwyn.VersionBundleto support the migration above
Removed¶
Changed¶
- We now raise a 5xx error (
cadwyn.exceptions.CadwynLatestRequestValidationError) whenever a request migration caused our payload to be incompatible with latest request schemas - Deprecated
cadwyn.mainand usecadwyn.applicationsinstead - Deprecated
latest_schemas_packageargument incadwyn.Cadwyn
[3.5.0]¶
Fixed¶
- Previously, Cadwyn did not set the default status code for ResponseInfo
Added¶
- HTTP status error handling in response converters using
convert_response_to_previous_version_for(..., migrate_http_errors=True)
[3.4.4]¶
Fixed¶
- Request and response converters were not applied when path params were present
[3.4.3]¶
Added¶
RouterPathParamsModifiedErroris now raised ifendpoint(...).had(path=...)has different path params than the original route
[3.4.2]¶
Fixed¶
- Fix import aliases in nested
__init__.pyfiles generating incorrectly for latest version
[3.4.1]¶
Fixed¶
- If the endpoint specified a single non-pydantic (list/dict) body parameter, Cadwyn failed to serialize the body
[3.4.0]¶
Added¶
schema(...).validator(...).existedandschema(...).validator(...).didnt_existinstructions for simplistic manipulation of validators- Automatic deletion of validators when the fields they validate get deleted
schema(...).field(...).didnt_havefor unsetting field attributes- Improved support for
typing.Annotatedin schemas - Full preservation of original abstract syntax trees for all field values and annotations
Fixed¶
- If the user wrote a wrong signature in a transformer decorated by
convert_request_to_next_version_fororconvert_response_to_previous_version_for, the text of the error suggested the wrong argument count and names
[3.3.4]¶
Fixed¶
- Added backwards compatibility for FastAPI < 0.106.0
[3.3.3]¶
Fixed¶
- Guaranteed that it is impossible to release cadwyn with the wrong pydantic dependency
[3.3.2]¶
Fixed¶
- Downgrade required version of verselect for backwards compatibility
[3.3.1]¶
Fixed¶
- Removed lazy migrations as they were producing incorrect results when there were no migrations but when there were schema changes
- Added compatibility with fastapi>=0.109.0
[3.3.0]¶
Fixed¶
- If a user used a FastAPI/Starlette
StreamingResponseorFileResponse, we still tried to access itsbodyattribute which caused anAttributeError
[3.2.0]¶
Added¶
- Sponsors section to README and docs, along with Monite as our main and only current sponsor ✨
[3.1.3]¶
Fixed¶
- Switched to
better-ast-commentsbecauseast-commentshad no license listed on pypi (even though its actual license was MIT) which caused some dependency checking tools to report it as unlicensed
[3.1.2]¶
Changed¶
- Migrate from black to ruff-format
Fixed¶
- A rare Pydantic 2 bug in internal body schema handling when it was applied too early, causing partially incomplete data to arrive to the handler
[3.1.1]¶
Fixed¶
- Previously we did not pass
dependency_overrides_provider,response_model_exclude_unsetresponse_model_exclude_defaults, andresponse_model_exclude_nonetofastapiwhich could cause erroneous behaviour during serialization in rare cases.
[3.1.0]¶
Added¶
module(...).had(import_=...)construct for adding imports in older versions- Codegen plugin system that allows easily customizing code generation for any purpose. It also significantly simplifies the core code of code generation
[3.0.2]¶
Fixed¶
- If a user returned a FastAPI/Starlette
Responsewith an empty body, we still tried to serialize it which caused an invalid response body
[Unreleased]¶
[3.0.0]¶
Added¶
- Pydantic 2 support
- Expanded reference section to docs
- Contributor docs
- Expanded makefile commands
Changed¶
- internal request representation is now done using an annotation
latest_schemas_modulewas renamed tolatest_schemas_packageeverywhereapi_version_varinVersionBundleis now an optional argument instead of a required one
Removed¶
cadwyn.internal_body_representation_ofbecause it is now done using an annotation
[2.3.4]¶
Fixed¶
schema(...).field(...).had(ge=...)for union fields previously raised anAttributeErroron code generation
[2.3.3]¶
Fixed¶
- Field ASTs not preserving the original structure when constrained fields were changed
Added¶
- Support for synchronous endpoints
[2.3.2]¶
Fixed¶
- The bug where fields from parent schemas also appeared in child schemas
[2.3.1]¶
Changed¶
- Migrate from external verselect to stable verselect
[2.3.0]¶
Fixed¶
- Previously whenever we generated routes, we wrapped all endpoints and callbacks in decorators for every version which caused stacktraces to be unnecessarily huge. Now there is only one wrapper for all versions
Added¶
cadwyn.Cadwynclass similar tofastapi.FastAPIthat provides header routing and encapsulates all information about versioned routes- Migrated from
fastapi-header-routingtoverselect cadwyn.routing.VERSION_HEADER_FORMATfromverselect.routing
Changed¶
*versionsargument incadwyn.VersionBundleis now split into a required positional-onlylatest_versionand*other_versionsto make it possible to see an invalid versionless definition statically. Note that it is not a breaking change because the presence of at least one version was also implicitly required before and would produce a failure at runtime
Removed¶
cadwyn.get_cadwyn_dependencyandcadwyn.headerbecause it is fully replaced withverselect
[2.2.0]¶
Added¶
- Validation for the spelling of HTTP methods in
cadwyn.structure.endpoint.
[2.1.0]¶
Added¶
- A prototype of AST-based code generation where we try to keep as much of the old field/annotation structure as possible
[2.0.5]¶
Fixed¶
UploadFileand forms have previously caused exceptions on request migration
[2.0.4]¶
Fixed¶
ContextVar[datetime.date]being incompatible withVersionBundle
[2.0.3]¶
Added¶
- A note into reference docs about the paths specification in CLI
Fixed¶
- Custom body fields created by FastAPI caused an exception. Now they are ignored
[2.0.2]¶
Added¶
- A link to OpenAPI discussion on enum expansion into docs/recipes
- A link to intercom's API versioning article into docs/theory
[2.0.1]¶
Fixed¶
generate_versioned_routersdid not alterAPIRoute.dependant.call,APIRoute.response_field, andAPIRoute.secure_cloned_response_fieldbefore which caused these fields to represent latest version on all generated versions. However, this was only a bug if the routes were later added into the app/router by hand instead of usinginherit_routesoradd_api_route.
[2.0.0]¶
Changed¶
generate_versioned_routersnow accepts only one router instead of a sequence of routers to give us the ability to guarantee that the type of generated routers is the same as the type of the passed router.
[1.4.0]¶
Added¶
- Theory section to docs
[1.3.0]¶
Added¶
- Recipes documentation section
schema(...).field(...).had(name=...)functionality to rename fields
Changed¶
- Tutorial example structure in tests
[1.2.0] - 2023-10-16¶
Added¶
cadwyn.main._Cadwynexperimental private class for automatically adding the header dependency and managing all objects related to versioning
Removed¶
cadwyn.header_routingwhich only had experimental private functions
Fixed¶
- Route callbacks didn't get migrated to versions with their parent routes
[1.1.0] - 2023-10-13¶
Added¶
ignore_coverage_for_latest_aliasesargument togenerate_code_for_versioned_packageswhich controls whether we add "a pragma: no cover" comment to the star imports in the generated version of the latest module. It is True by default.
[1.0.3] - 2023-10-10¶
Fixed¶
- Add back the approach where the first version being an alias to latest in codegen
[1.0.2] - 2023-10-09¶
Fixed¶
- Add current working dir to
sys.pathwhen running code generation through CLI - Use
exclude_unsetwhen migrating the body of a request to make sure that users'exclude_unsetlogic gets preserved
[1.0.1] - 2023-10-09¶
Fixed¶
- Pass first argument in
typer.Argumentto prevent errors on older typer versions
[1.0.0] - 2023-10-09¶
Added¶
- Command-line interface capable of running code-generation and outputting version info
- Internal request schema which gives us all the functionality we could ever need to migrate request bodies forward without any complexity of the prior solution
_get_versioned_routerand experimental header routing with it (by @tikon93). Note that the interface for this feature will change in the future
Changed¶
- Renamed
cadwyn.regenerate_dir_to_all_versionstocadwyn.generate_code_for_versioned_packages - Renamed
cadwyn.generate_all_router_versionstocadwyn.generate_versioned_routers
Removed¶
unionsdirectory and all logic around it (replaced by internal request schema)FillablePrivateAttrand all logic around it (replaced by internal request schema)schema(...).propertyconstructor and all logic around it (replaced by internal request schema)- Special-casing for code generation of package with latest version using star imports from latest (replaced by internal request schema)