mirror of https://github.com/pallets/flask.git
blinker is required, signals are always available
This commit is contained in:
parent
e1e4e82096
commit
9cb1a7a52d
|
@ -28,6 +28,8 @@ Unreleased
|
|||
- The ``app.got_first_request`` property is deprecated. :pr:`4997`
|
||||
- The ``locked_cached_property`` decorator is deprecated. Use a lock inside the
|
||||
decorated function if locking is needed. :issue:`4993`
|
||||
- Signals are always available. ``blinker>=1.6.2`` is a required dependency. The
|
||||
``signals_available`` attribute is deprecated. :issue:`5056`
|
||||
- Remove uses of locks that could cause requests to block each other very briefly.
|
||||
:issue:`4993`
|
||||
- Use modern packaging metadata with ``pyproject.toml`` instead of ``setup.cfg``.
|
||||
|
|
28
docs/api.rst
28
docs/api.rst
|
@ -333,14 +333,9 @@ Useful Internals
|
|||
Signals
|
||||
-------
|
||||
|
||||
.. versionadded:: 0.6
|
||||
Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introduction.
|
||||
|
||||
.. data:: signals.signals_available
|
||||
|
||||
``True`` if the signaling system is available. This is the case
|
||||
when `blinker`_ is installed.
|
||||
|
||||
The following signals exist in Flask:
|
||||
.. _blinker: https://blinker.readthedocs.io/
|
||||
|
||||
.. data:: template_rendered
|
||||
|
||||
|
@ -507,7 +502,6 @@ The following signals exist in Flask:
|
|||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
|
||||
.. data:: message_flashed
|
||||
|
||||
This signal is sent when the application is flashing a message. The
|
||||
|
@ -525,22 +519,10 @@ The following signals exist in Flask:
|
|||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. class:: signals.Namespace
|
||||
.. data:: signals.signals_available
|
||||
|
||||
An alias for :class:`blinker.base.Namespace` if blinker is available,
|
||||
otherwise a dummy class that creates fake signals. This class is
|
||||
available for Flask extensions that want to provide the same fallback
|
||||
system as Flask itself.
|
||||
|
||||
.. method:: signal(name, doc=None)
|
||||
|
||||
Creates a new signal for this namespace if blinker is available,
|
||||
otherwise returns a fake signal that has a send method that will
|
||||
do nothing but will fail with a :exc:`RuntimeError` for all other
|
||||
operations, including connecting.
|
||||
|
||||
|
||||
.. _blinker: https://pypi.org/project/blinker/
|
||||
.. deprecated:: 2.3
|
||||
Will be removed in Flask 2.4. Signals are always available
|
||||
|
||||
|
||||
Class-Based Views
|
||||
|
|
|
@ -140,10 +140,8 @@ Accessing ``db`` will call ``get_db`` internally, in the same way that
|
|||
Events and Signals
|
||||
------------------
|
||||
|
||||
The application will call functions registered with
|
||||
:meth:`~Flask.teardown_appcontext` when the application context is
|
||||
popped.
|
||||
The application will call functions registered with :meth:`~Flask.teardown_appcontext`
|
||||
when the application context is popped.
|
||||
|
||||
If :data:`~signals.signals_available` is true, the following signals are
|
||||
sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and
|
||||
:data:`appcontext_popped`.
|
||||
The following signals are sent: :data:`appcontext_pushed`,
|
||||
:data:`appcontext_tearing_down`, and :data:`appcontext_popped`.
|
||||
|
|
|
@ -24,12 +24,14 @@ These distributions will be installed automatically when installing Flask.
|
|||
to protect Flask's session cookie.
|
||||
* `Click`_ is a framework for writing command line applications. It provides
|
||||
the ``flask`` command and allows adding custom management commands.
|
||||
* `Blinker`_ provides support for :doc:`signals`.
|
||||
|
||||
.. _Werkzeug: https://palletsprojects.com/p/werkzeug/
|
||||
.. _Jinja: https://palletsprojects.com/p/jinja/
|
||||
.. _MarkupSafe: https://palletsprojects.com/p/markupsafe/
|
||||
.. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/
|
||||
.. _Click: https://palletsprojects.com/p/click/
|
||||
.. _Blinker: https://blinker.readthedocs.io/
|
||||
|
||||
|
||||
Optional dependencies
|
||||
|
@ -38,13 +40,11 @@ Optional dependencies
|
|||
These distributions will not be installed automatically. Flask will detect and
|
||||
use them if you install them.
|
||||
|
||||
* `Blinker`_ provides support for :doc:`signals`.
|
||||
* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``
|
||||
commands.
|
||||
* `Watchdog`_ provides a faster, more efficient reloader for the development
|
||||
server.
|
||||
|
||||
.. _Blinker: https://blinker.readthedocs.io/en/stable/
|
||||
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
||||
.. _watchdog: https://pythonhosted.org/watchdog/
|
||||
|
||||
|
|
|
@ -204,21 +204,16 @@ contexts until the ``with`` block exits.
|
|||
Signals
|
||||
~~~~~~~
|
||||
|
||||
If :data:`~signals.signals_available` is true, the following signals are
|
||||
sent:
|
||||
The following signals are sent:
|
||||
|
||||
#. :data:`request_started` is sent before the
|
||||
:meth:`~Flask.before_request` functions are called.
|
||||
|
||||
#. :data:`request_finished` is sent after the
|
||||
:meth:`~Flask.after_request` functions are called.
|
||||
|
||||
#. :data:`got_request_exception` is sent when an exception begins to
|
||||
be handled, but before an :meth:`~Flask.errorhandler` is looked up or
|
||||
called.
|
||||
|
||||
#. :data:`request_tearing_down` is sent after the
|
||||
:meth:`~Flask.teardown_request` functions are called.
|
||||
#. :data:`request_started` is sent before the :meth:`~Flask.before_request` functions
|
||||
are called.
|
||||
#. :data:`request_finished` is sent after the :meth:`~Flask.after_request` functions
|
||||
are called.
|
||||
#. :data:`got_request_exception` is sent when an exception begins to be handled, but
|
||||
before an :meth:`~Flask.errorhandler` is looked up or called.
|
||||
#. :data:`request_tearing_down` is sent after the :meth:`~Flask.teardown_request`
|
||||
functions are called.
|
||||
|
||||
|
||||
.. _notes-on-proxies:
|
||||
|
|
|
@ -1,33 +1,28 @@
|
|||
Signals
|
||||
=======
|
||||
|
||||
.. versionadded:: 0.6
|
||||
Signals are a lightweight way to notify subscribers of certain events during the
|
||||
lifecycle of the application and each request. When an event occurs, it emits the
|
||||
signal, which calls each subscriber.
|
||||
|
||||
Starting with Flask 0.6, there is integrated support for signalling in
|
||||
Flask. This support is provided by the excellent `blinker`_ library and
|
||||
will gracefully fall back if it is not available.
|
||||
Signals are implemented by the `Blinker`_ library. See its documentation for detailed
|
||||
information. Flask provides some built-in signals. Extensions may provide their own.
|
||||
|
||||
What are signals? Signals help you decouple applications by sending
|
||||
notifications when actions occur elsewhere in the core framework or
|
||||
another Flask extensions. In short, signals allow certain senders to
|
||||
notify subscribers that something happened.
|
||||
Many signals mirror Flask's decorator-based callbacks with similar names. For example,
|
||||
the :data:`.request_started` signal is similar to the :meth:`~.Flask.before_request`
|
||||
decorator. The advantage of signals over handlers is that they can be subscribed to
|
||||
temporarily, and can't directly affect the application. This is useful for testing,
|
||||
metrics, auditing, and more. For example, if you want to know what templates were
|
||||
rendered at what parts of what requests, there is a signal that will notify you of that
|
||||
information.
|
||||
|
||||
Flask comes with a couple of signals and other extensions might provide
|
||||
more. Also keep in mind that signals are intended to notify subscribers
|
||||
and should not encourage subscribers to modify data. You will notice that
|
||||
there are signals that appear to do the same thing like some of the
|
||||
builtin decorators do (eg: :data:`~flask.request_started` is very similar
|
||||
to :meth:`~flask.Flask.before_request`). However, there are differences in
|
||||
how they work. The core :meth:`~flask.Flask.before_request` handler, for
|
||||
example, is executed in a specific order and is able to abort the request
|
||||
early by returning a response. In contrast all signal handlers are
|
||||
executed in undefined order and do not modify any data.
|
||||
|
||||
The big advantage of signals over handlers is that you can safely
|
||||
subscribe to them for just a split second. These temporary
|
||||
subscriptions are helpful for unit testing for example. Say you want to
|
||||
know what templates were rendered as part of a request: signals allow you
|
||||
to do exactly that.
|
||||
Core Signals
|
||||
------------
|
||||
|
||||
See :ref:`core-signals-list` for a list of all built-in signals. The :doc:`lifecycle`
|
||||
page also describes the order that signals and decorators execute.
|
||||
|
||||
|
||||
Subscribing to Signals
|
||||
----------------------
|
||||
|
@ -99,11 +94,6 @@ The example above would then look like this::
|
|||
...
|
||||
template, context = templates[0]
|
||||
|
||||
.. admonition:: Blinker API Changes
|
||||
|
||||
The :meth:`~blinker.base.Signal.connected_to` method arrived in Blinker
|
||||
with version 1.1.
|
||||
|
||||
Creating Signals
|
||||
----------------
|
||||
|
||||
|
@ -123,12 +113,6 @@ The name for the signal here makes it unique and also simplifies
|
|||
debugging. You can access the name of the signal with the
|
||||
:attr:`~blinker.base.NamedSignal.name` attribute.
|
||||
|
||||
.. admonition:: For Extension Developers
|
||||
|
||||
If you are writing a Flask extension and you want to gracefully degrade for
|
||||
missing blinker installations, you can do so by using the
|
||||
:class:`flask.signals.Namespace` class.
|
||||
|
||||
.. _signals-sending:
|
||||
|
||||
Sending Signals
|
||||
|
@ -170,7 +154,7 @@ in :ref:`signals-sending` and the :data:`~flask.request_tearing_down` signal.
|
|||
Decorator Based Signal Subscriptions
|
||||
------------------------------------
|
||||
|
||||
With Blinker 1.1 you can also easily subscribe to signals by using the new
|
||||
You can also easily subscribe to signals by using the
|
||||
:meth:`~blinker.base.NamedSignal.connect_via` decorator::
|
||||
|
||||
from flask import template_rendered
|
||||
|
@ -179,10 +163,5 @@ With Blinker 1.1 you can also easily subscribe to signals by using the new
|
|||
def when_template_rendered(sender, template, context, **extra):
|
||||
print(f'Template {template.name} is rendered with {context}')
|
||||
|
||||
Core Signals
|
||||
------------
|
||||
|
||||
Take a look at :ref:`core-signals-list` for a list of all builtin signals.
|
||||
|
||||
|
||||
.. _blinker: https://pypi.org/project/blinker/
|
||||
|
|
|
@ -11,7 +11,7 @@ dependencies = ["flask"]
|
|||
Documentation = "https://flask.palletsprojects.com/patterns/jquery/"
|
||||
|
||||
[project.optional-dependencies]
|
||||
test = ["pytest", "blinker"]
|
||||
test = ["pytest"]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
|
|
|
@ -24,6 +24,7 @@ dependencies = [
|
|||
"Jinja2>=3.0",
|
||||
"itsdangerous>=2.0",
|
||||
"click>=8.0",
|
||||
"blinker>=1.6.2",
|
||||
"importlib-metadata>=3.6.0; python_version < '3.10'",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
@ -90,7 +91,6 @@ warn_unused_ignores = true
|
|||
[[tool.mypy.overrides]]
|
||||
module = [
|
||||
"asgiref.*",
|
||||
"blinker.*",
|
||||
"dotenv.*",
|
||||
"cryptography.*",
|
||||
"importlib_metadata",
|
||||
|
|
|
@ -11,5 +11,3 @@ packaging==23.0
|
|||
# via build
|
||||
pyproject-hooks==1.0.0
|
||||
# via build
|
||||
tomli==2.0.1
|
||||
# via build
|
||||
|
|
|
@ -3,3 +3,4 @@ Jinja2==3.0.0
|
|||
MarkupSafe==2.0.0
|
||||
itsdangerous==2.0.0
|
||||
click==8.0.0
|
||||
blinker==1.6.2
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# SHA1:4de7d9e6254a945fd97ec10880dd23b6cd43b70d
|
||||
# SHA1:575f86f45391b662630a6080f0a12676215eb0cf
|
||||
#
|
||||
# This file is autogenerated by pip-compile-multi
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile-multi
|
||||
#
|
||||
blinker==1.6.2
|
||||
# via -r requirements/tests-pallets-min.in
|
||||
click==8.0.0
|
||||
# via -r requirements/tests-pallets-min.in
|
||||
itsdangerous==2.0.0
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
pytest
|
||||
asgiref
|
||||
blinker
|
||||
greenlet ; python_version < "3.11"
|
||||
python-dotenv>=1; python_version >= "3.8"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# SHA1:30698f5f4f9cba5088318306829a15b0dc123b38
|
||||
# SHA1:3c8dde35aba20388b22430b17974af8ef8205b4f
|
||||
#
|
||||
# This file is autogenerated by pip-compile-multi
|
||||
# To update, run:
|
||||
|
@ -7,12 +7,6 @@
|
|||
#
|
||||
asgiref==3.6.0
|
||||
# via -r requirements/tests.in
|
||||
blinker==1.6.1
|
||||
# via -r requirements/tests.in
|
||||
exceptiongroup==1.1.1
|
||||
# via pytest
|
||||
greenlet==2.0.2 ; python_version < "3.11"
|
||||
# via -r requirements/tests.in
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
packaging==23.0
|
||||
|
@ -23,7 +17,3 @@ pytest==7.3.0
|
|||
# via -r requirements/tests.in
|
||||
python-dotenv==1.0.0 ; python_version >= "3.8"
|
||||
# via -r requirements/tests.in
|
||||
tomli==2.0.1
|
||||
# via pytest
|
||||
typing-extensions==4.5.0
|
||||
# via blinker
|
||||
|
|
|
@ -15,8 +15,6 @@ mypy-extensions==1.0.0
|
|||
# via mypy
|
||||
pycparser==2.21
|
||||
# via cffi
|
||||
tomli==2.0.1
|
||||
# via mypy
|
||||
types-contextvars==2.4.7.2
|
||||
# via -r requirements/typing.in
|
||||
types-dataclasses==0.6.6
|
||||
|
|
|
@ -32,7 +32,6 @@ from .signals import message_flashed as message_flashed
|
|||
from .signals import request_finished as request_finished
|
||||
from .signals import request_started as request_started
|
||||
from .signals import request_tearing_down as request_tearing_down
|
||||
from .signals import signals_available as signals_available
|
||||
from .signals import template_rendered as template_rendered
|
||||
from .templating import render_template as render_template
|
||||
from .templating import render_template_string as render_template_string
|
||||
|
@ -89,4 +88,15 @@ def __getattr__(name):
|
|||
)
|
||||
return Markup
|
||||
|
||||
if name == "signals_available":
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'signals_available' is deprecated and will be removed in Flask 2.4."
|
||||
" Signals are always available",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return True
|
||||
|
||||
raise AttributeError(name)
|
||||
|
|
|
@ -1,49 +1,13 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import typing as t
|
||||
import warnings
|
||||
|
||||
try:
|
||||
from blinker import Namespace
|
||||
from blinker import Namespace
|
||||
|
||||
signals_available = True
|
||||
except ImportError:
|
||||
signals_available = False
|
||||
|
||||
class Namespace: # type: ignore
|
||||
def signal(self, name: str, doc: t.Optional[str] = None) -> "_FakeSignal":
|
||||
return _FakeSignal(name, doc)
|
||||
|
||||
class _FakeSignal:
|
||||
"""If blinker is unavailable, create a fake class with the same
|
||||
interface that allows sending of signals but will fail with an
|
||||
error on anything else. Instead of doing anything on send, it
|
||||
will just ignore the arguments and do nothing instead.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, doc: t.Optional[str] = None) -> None:
|
||||
self.name = name
|
||||
self.__doc__ = doc
|
||||
|
||||
def send(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
pass
|
||||
|
||||
def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
raise RuntimeError(
|
||||
"Signalling support is unavailable because the blinker"
|
||||
" library is not installed."
|
||||
) from None
|
||||
|
||||
connect = connect_via = connected_to = temporarily_connected_to = _fail
|
||||
disconnect = _fail
|
||||
has_receivers_for = receivers_for = _fail
|
||||
del _fail
|
||||
|
||||
|
||||
# The namespace for code signals. If you are not Flask code, do
|
||||
# not put signals in here. Create your own namespace instead.
|
||||
# This namespace is only for signals provided by Flask itself.
|
||||
_signals = Namespace()
|
||||
|
||||
|
||||
# Core signals. For usage examples grep the source code or consult
|
||||
# the API documentation in docs/api.rst as well as docs/signals.rst
|
||||
template_rendered = _signals.signal("template-rendered")
|
||||
before_render_template = _signals.signal("before-render-template")
|
||||
request_started = _signals.signal("request-started")
|
||||
|
@ -54,3 +18,16 @@ appcontext_tearing_down = _signals.signal("appcontext-tearing-down")
|
|||
appcontext_pushed = _signals.signal("appcontext-pushed")
|
||||
appcontext_popped = _signals.signal("appcontext-popped")
|
||||
message_flashed = _signals.signal("message-flashed")
|
||||
|
||||
|
||||
def __getattr__(name: str) -> t.Any:
|
||||
if name == "signals_available":
|
||||
warnings.warn(
|
||||
"The 'signals_available' attribute is deprecated and will be removed in"
|
||||
" Flask 2.4. Signals are always available.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return True
|
||||
|
||||
raise AttributeError(name)
|
||||
|
|
|
@ -1,16 +1,5 @@
|
|||
import pytest
|
||||
|
||||
try:
|
||||
import blinker
|
||||
except ImportError:
|
||||
blinker = None
|
||||
|
||||
import flask
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
blinker is None, reason="Signals require the blinker library."
|
||||
)
|
||||
|
||||
|
||||
def test_template_rendered(app, client):
|
||||
@app.route("/")
|
||||
|
|
|
@ -10,11 +10,6 @@ from flask.json import jsonify
|
|||
from flask.testing import EnvironBuilder
|
||||
from flask.testing import FlaskCliRunner
|
||||
|
||||
try:
|
||||
import blinker
|
||||
except ImportError:
|
||||
blinker = None
|
||||
|
||||
|
||||
def test_environ_defaults_from_config(app, client):
|
||||
app.config["SERVER_NAME"] = "example.com:1234"
|
||||
|
@ -285,7 +280,6 @@ def test_json_request_and_response(app, client):
|
|||
assert rv.get_json() == json_data
|
||||
|
||||
|
||||
@pytest.mark.skipif(blinker is None, reason="blinker is not installed")
|
||||
def test_client_json_no_app_context(app, client):
|
||||
@app.route("/hello", methods=["POST"])
|
||||
def hello():
|
||||
|
|
Loading…
Reference in New Issue