Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Untyped decorator makes function untyped #4060

Closed
aphedges opened this issue May 15, 2021 · 2 comments
Closed

Untyped decorator makes function untyped #4060

aphedges opened this issue May 15, 2021 · 2 comments
Labels
Milestone

Comments

@aphedges
Copy link
Contributor

When I run mypy --strict on the following file (named flask_types.py),

from typing import Tuple

from flask import Flask

app = Flask(__name__)

@app.errorhandler(404)
def page_not_found(error: Exception) -> Tuple[str, int]:
    return str(error), 404

I get the following error:

flask_types.py:7: error: Untyped decorator makes function "page_not_found" untyped

Instead, mypy should return no errors.

Using --strict (or the equivalent option in setup.cfg) in your tests might have caught an error like this, but given how new your type annotations are, I understand why you aren't using the setting yet.

The root cause appears to be that the implemented decorators often use typing.Callable in type annotations without any type parameters, which causes them to lose type information.

The fix for this specific issue is only a couple of lines (I have a prototype implemented locally), but I will try to add appropriate type parameters for Callable in as many places as possible. I expect to have a PR out for this in a couple of days.
Once I got a fix working for errorhandler(), mypy was able to find an error in my type annotations of page_not_found() as well as some internal issues in Flask itself, which I feel demonstrates how better typing would be useful here.

Environment:

  • Python version: 3.7.9
  • Flask version: 2.0.0
  • mypy version: 0.812

PS: I really appreciate the new typing annotations in Flask! They seem like they took a lot of effort, and I have already found them useful. Thank you very much!

@pgjones
Copy link
Member

pgjones commented May 16, 2021

Does it work with this patch applied?

diff --git a/src/flask/scaffold.py b/src/flask/scaffold.py
index f50c9b1b..46d216f6 100644
--- a/src/flask/scaffold.py
+++ b/src/flask/scaffold.py
@@ -642,7 +642,7 @@ class Scaffold:
     @setupmethod
     def errorhandler(
         self, code_or_exception: t.Union[t.Type[Exception], int]
-    ) -> t.Callable:
+    ) -> t.Callable[[ErrorHandlerCallable], ErrorHandlerCallable]:
         """Register a function to handle errors by code or exception class.
 
         A decorator that is used to register a function given an

@aphedges
Copy link
Contributor Author

That was my initial thought, too, but unfortunately, setupmethod() erases any type signatures it is called on. The minimal patch to stop the error is the following:

diff --git a/src/flask/scaffold.py b/src/flask/scaffold.py
index 20654b6b..f8f94af1 100644
--- a/src/flask/scaffold.py
+++ b/src/flask/scaffold.py
@@ -33,8 +33,10 @@ if t.TYPE_CHECKING:
 # a singleton sentinel value for parameter defaults
 _sentinel = object()
 
+F = t.TypeVar("F", bound=t.Callable[..., t.Any])
 
-def setupmethod(f: t.Callable) -> t.Callable:
+
+def setupmethod(f: F) -> F:
     """Wraps a method so that it performs a check in debug mode if the
     first request was already handled.
     """
@@ -53,7 +55,7 @@ def setupmethod(f: t.Callable) -> t.Callable:
             )
         return f(self, *args, **kwargs)
 
-    return update_wrapper(wrapper_func, f)
+    return t.cast(F, update_wrapper(wrapper_func, f))
 
 
 class Scaffold:

It seems the mypy error was caused by the decorator not having any type, so changing t.Callable to t.Callable[[ErrorHandlerCallable], ErrorHandlerCallable]: is not needed. However, it's still useful for catching errors in user code.

I have a PR with my complete fix: #4071.

@davidism davidism added this to the 2.0.1 milestone May 21, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 5, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants