Skip to content

Commit 6414df9

Browse files
authored
Merge pull request #4042 from pallets/update-typing
Update typing
2 parents c121e8c + 25884c4 commit 6414df9

File tree

7 files changed

+77
-49
lines changed

7 files changed

+77
-49
lines changed

CHANGES.rst

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Unreleased
88
- Re-add the ``filename`` parameter in ``send_from_directory``. The
99
``filename`` parameter has been renamed to ``path``, the old name
1010
is deprecated. :pr:`4019`
11+
- Mark top-level names as exported so type checking understands
12+
imports in user projects. :issue:`4024`
13+
- Fix type annotation for ``g`` and inform mypy that it is a namespace
14+
object that has arbitrary attributes. :issue:`4020`
15+
- Fix some types that weren't available in Python 3.6.0. :issue:`4040`
1116

1217

1318
Version 2.0.0

src/flask/__init__.py

+41-41
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
11
from markupsafe import escape
22
from markupsafe import Markup
3-
from werkzeug.exceptions import abort
4-
from werkzeug.utils import redirect
3+
from werkzeug.exceptions import abort as abort
4+
from werkzeug.utils import redirect as redirect
55

6-
from . import json
7-
from .app import Flask
8-
from .app import Request
9-
from .app import Response
10-
from .blueprints import Blueprint
11-
from .config import Config
12-
from .ctx import after_this_request
13-
from .ctx import copy_current_request_context
14-
from .ctx import has_app_context
15-
from .ctx import has_request_context
16-
from .globals import _app_ctx_stack
17-
from .globals import _request_ctx_stack
18-
from .globals import current_app
19-
from .globals import g
20-
from .globals import request
21-
from .globals import session
22-
from .helpers import flash
23-
from .helpers import get_flashed_messages
24-
from .helpers import get_template_attribute
25-
from .helpers import make_response
26-
from .helpers import safe_join
27-
from .helpers import send_file
28-
from .helpers import send_from_directory
29-
from .helpers import stream_with_context
30-
from .helpers import url_for
31-
from .json import jsonify
32-
from .signals import appcontext_popped
33-
from .signals import appcontext_pushed
34-
from .signals import appcontext_tearing_down
35-
from .signals import before_render_template
36-
from .signals import got_request_exception
37-
from .signals import message_flashed
38-
from .signals import request_finished
39-
from .signals import request_started
40-
from .signals import request_tearing_down
41-
from .signals import signals_available
42-
from .signals import template_rendered
43-
from .templating import render_template
44-
from .templating import render_template_string
6+
from . import json as json
7+
from .app import Flask as Flask
8+
from .app import Request as Request
9+
from .app import Response as Response
10+
from .blueprints import Blueprint as Blueprint
11+
from .config import Config as Config
12+
from .ctx import after_this_request as after_this_request
13+
from .ctx import copy_current_request_context as copy_current_request_context
14+
from .ctx import has_app_context as has_app_context
15+
from .ctx import has_request_context as has_request_context
16+
from .globals import _app_ctx_stack as _app_ctx_stack
17+
from .globals import _request_ctx_stack as _request_ctx_stack
18+
from .globals import current_app as current_app
19+
from .globals import g as g
20+
from .globals import request as request
21+
from .globals import session as session
22+
from .helpers import flash as flash
23+
from .helpers import get_flashed_messages as get_flashed_messages
24+
from .helpers import get_template_attribute as get_template_attribute
25+
from .helpers import make_response as make_response
26+
from .helpers import safe_join as safe_join
27+
from .helpers import send_file as send_file
28+
from .helpers import send_from_directory as send_from_directory
29+
from .helpers import stream_with_context as stream_with_context
30+
from .helpers import url_for as url_for
31+
from .json import jsonify as jsonify
32+
from .signals import appcontext_popped as appcontext_popped
33+
from .signals import appcontext_pushed as appcontext_pushed
34+
from .signals import appcontext_tearing_down as appcontext_tearing_down
35+
from .signals import before_render_template as before_render_template
36+
from .signals import got_request_exception as got_request_exception
37+
from .signals import message_flashed as message_flashed
38+
from .signals import request_finished as request_finished
39+
from .signals import request_started as request_started
40+
from .signals import request_tearing_down as request_tearing_down
41+
from .signals import signals_available as signals_available
42+
from .signals import template_rendered as template_rendered
43+
from .templating import render_template as render_template
44+
from .templating import render_template_string as render_template_string
4545

4646
__version__ = "2.0.1.dev0"

src/flask/app.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
from .wrappers import Response
7373

7474
if t.TYPE_CHECKING:
75+
import typing_extensions as te
7576
from .blueprints import Blueprint
7677
from .testing import FlaskClient
7778
from .testing import FlaskCliRunner
@@ -1441,7 +1442,7 @@ def log_exception(
14411442
f"Exception on {request.path} [{request.method}]", exc_info=exc_info
14421443
)
14431444

1444-
def raise_routing_exception(self, request: Request) -> t.NoReturn:
1445+
def raise_routing_exception(self, request: Request) -> "te.NoReturn":
14451446
"""Exceptions that are recording during routing are reraised with
14461447
this method. During debug we are not reraising redirect requests
14471448
for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising

src/flask/ctx.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ class _AppCtxGlobals:
4141
.. versionadded:: 0.10
4242
"""
4343

44+
# Define attr methods to let mypy know this is a namespace object
45+
# that has arbitrary attributes.
46+
47+
def __getattr__(self, name: str) -> t.Any:
48+
try:
49+
return self.__dict__[name]
50+
except KeyError:
51+
raise AttributeError(name) from None
52+
53+
def __setattr__(self, name: str, value: t.Any) -> None:
54+
self.__dict__[name] = value
55+
56+
def __delattr__(self, name: str) -> None:
57+
try:
58+
del self.__dict__[name]
59+
except KeyError:
60+
raise AttributeError(name) from None
61+
4462
def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any:
4563
"""Get an attribute by name, or a default value. Like
4664
:meth:`dict.get`.
@@ -78,10 +96,10 @@ def setdefault(self, name: str, default: t.Any = None) -> t.Any:
7896
"""
7997
return self.__dict__.setdefault(name, default)
8098

81-
def __contains__(self, item: t.Any) -> bool:
99+
def __contains__(self, item: str) -> bool:
82100
return item in self.__dict__
83101

84-
def __iter__(self) -> t.Iterator:
102+
def __iter__(self) -> t.Iterator[str]:
85103
return iter(self.__dict__)
86104

87105
def __repr__(self) -> str:

src/flask/globals.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
if t.TYPE_CHECKING:
88
from .app import Flask
9-
from .ctx import AppContext
9+
from .ctx import _AppCtxGlobals
1010
from .sessions import SessionMixin
1111
from .wrappers import Request
1212

@@ -53,5 +53,7 @@ def _find_app():
5353
_app_ctx_stack = LocalStack()
5454
current_app: "Flask" = LocalProxy(_find_app) # type: ignore
5555
request: "Request" = LocalProxy(partial(_lookup_req_object, "request")) # type: ignore
56-
session: "SessionMixin" = LocalProxy(partial(_lookup_req_object, "session")) # type: ignore # noqa: B950
57-
g: "AppContext" = LocalProxy(partial(_lookup_app_object, "g")) # type: ignore
56+
session: "SessionMixin" = LocalProxy( # type: ignore
57+
partial(_lookup_req_object, "session")
58+
)
59+
g: "_AppCtxGlobals" = LocalProxy(partial(_lookup_app_object, "g")) # type: ignore

src/flask/sessions.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .json.tag import TaggedJSONSerializer
1313

1414
if t.TYPE_CHECKING:
15+
import typing_extensions as te
1516
from .app import Flask
1617
from .wrappers import Request, Response
1718

@@ -92,7 +93,7 @@ class NullSession(SecureCookieSession):
9293
but fail on setting.
9394
"""
9495

95-
def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
96+
def _fail(self, *args: t.Any, **kwargs: t.Any) -> "te.NoReturn":
9697
raise RuntimeError(
9798
"The session is unavailable because no secret "
9899
"key was set. Set the secret_key on the "

src/flask/wrappers.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from .globals import current_app
99

1010
if t.TYPE_CHECKING:
11+
import typing_extensions as te
1112
from werkzeug.routing import Rule
1213

1314

@@ -91,7 +92,7 @@ def _load_form_data(self) -> None:
9192

9293
attach_enctype_error_multidict(self)
9394

94-
def on_json_loading_failed(self, e: Exception) -> t.NoReturn:
95+
def on_json_loading_failed(self, e: Exception) -> "te.NoReturn":
9596
if current_app and current_app.debug:
9697
raise BadRequest(f"Failed to decode JSON object: {e}")
9798

0 commit comments

Comments
 (0)