Repository navigation
Preserve types of functions decorated with jwt_required and loaders - #581
Conversation
|
It looks like there was an issue on a couple of the CI tasks. Let me know if you need any help getting those resolved! I also left one comment, if you don't mind giving that a look. Thank you for the PR! |
I believe I've addressed the comments + extended the test suite to cover for more cases. Note: I'll follow up with another PR as the |
Ah sorry about that. I remember getting that notice, then completely forgetting to do anything about it. |
Description
jwt_required()is annotated-> Any, and theJWTManagerloader decorators (user_identity_loader,user_lookup_loader,token_in_blocklist_loader, …) are annotated(callback: Callable) -> Callable. Because the package shipspy.typed, type checkers trust these annotations, so every protected view and every registered loader loses its signature: parameters and return types are no longer checked, and the loss carries over to callers, tests that call views directly, and decorators stacked on top.Changes
jwt_required()now returnsCallable[[_F], _F], where_F = TypeVar("_F", bound=Callable[..., Any]). The inner wrapper is annotated, and acastis needed becausefunctools.wrapsdoesn't carry the type over.JWTManager.*_loaderdecorators are now typed(callback: _F) -> _F. They already return the callback unchanged, so no cast is needed there.TypeVaris private (_F), so it doesn't add to the public API.tests/test_typing.py:assert_typechecks run by the existingmypytox env. With the old annotations, mypy fails on this test, so a regression toAnywould be caught in CI.Compatibility
cast, which does nothing at runtime. The full test suite passes unchanged.TypeVarbound is as loose as the old bareCallable. Checked withmypy --strictand pyright against sync andasyncviews, everyjwt_requiredkeyword argument,MethodView.decorators, decorated view methods,jwt_required()(lambda: ...), and loaders registered with lambdas,functools.partialand untyped functions. Neither checker reported an error from the library.Anywas hiding, for example calling a view with the wrong argument types, a loader with the wrong signature, or@jwt_requiredwritten without parentheses.async defviews keep their declared coroutine type, the same convention Flask's own decorators follow. (At runtimeensure_syncmakes the wrapper synchronous. A more preciseParamSpec/overload typing was considered but rejected: it would change the inferred type of every async view and add new errors for currently valid code.)Testing
mypy flask_jwt_extended/ tests/: no issuespytest tests/: 181 passed, 1 skippedflake8andblackclean on the changed files