Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/event/api.py: 93%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# event/api.py
2# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
8"""Public API functions for the event system.
10"""
11from __future__ import annotations
13from typing import Any
14from typing import Callable
16from .base import _registrars
17from .registry import _ET
18from .registry import _EventKey
19from .registry import _ListenerFnType
20from .. import exc
21from .. import util
24CANCEL = util.symbol("CANCEL")
25NO_RETVAL = util.symbol("NO_RETVAL")
28def _event_key(
29 target: _ET, identifier: str, fn: _ListenerFnType
30) -> _EventKey[_ET]:
31 for evt_cls in _registrars[identifier]:
32 tgt = evt_cls._accept_with(target, identifier)
33 if tgt is not None:
34 return _EventKey(target, identifier, fn, tgt)
35 else:
36 raise exc.InvalidRequestError(
37 "No such event '%s' for target '%s'" % (identifier, target)
38 )
41def listen(
42 target: Any, identifier: str, fn: Callable[..., Any], *args: Any, **kw: Any
43) -> None:
44 """Register a listener function for the given target.
46 The :func:`.listen` function is part of the primary interface for the
47 SQLAlchemy event system, documented at :ref:`event_toplevel`.
49 e.g.::
51 from sqlalchemy import event
52 from sqlalchemy.schema import UniqueConstraint
54 def unique_constraint_name(const, table):
55 const.name = "uq_%s_%s" % (
56 table.name,
57 list(const.columns)[0].name
58 )
59 event.listen(
60 UniqueConstraint,
61 "after_parent_attach",
62 unique_constraint_name)
64 :param bool insert: The default behavior for event handlers is to append
65 the decorated user defined function to an internal list of registered
66 event listeners upon discovery. If a user registers a function with
67 ``insert=True``, SQLAlchemy will insert (prepend) the function to the
68 internal list upon discovery. This feature is not typically used or
69 recommended by the SQLAlchemy maintainers, but is provided to ensure
70 certain user defined functions can run before others, such as when
71 :ref:`Changing the sql_mode in MySQL <mysql_sql_mode>`.
73 :param bool named: When using named argument passing, the names listed in
74 the function argument specification will be used as keys in the
75 dictionary.
76 See :ref:`event_named_argument_styles`.
78 :param bool once: Private/Internal API usage. Deprecated. This parameter
79 would provide that an event function would run only once per given
80 target. It does not however imply automatic de-registration of the
81 listener function; associating an arbitrarily high number of listeners
82 without explicitly removing them will cause memory to grow unbounded even
83 if ``once=True`` is specified.
85 :param bool propagate: The ``propagate`` kwarg is available when working
86 with ORM instrumentation and mapping events.
87 See :class:`_ormevent.MapperEvents` and
88 :meth:`_ormevent.MapperEvents.before_mapper_configured` for examples.
90 :param bool retval: This flag applies only to specific event listeners,
91 each of which includes documentation explaining when it should be used.
92 By default, no listener ever requires a return value.
93 However, some listeners do support special behaviors for return values,
94 and include in their documentation that the ``retval=True`` flag is
95 necessary for a return value to be processed.
97 Event listener suites that make use of :paramref:`_event.listen.retval`
98 include :class:`_events.ConnectionEvents` and
99 :class:`_ormevent.AttributeEvents`.
101 .. note::
103 The :func:`.listen` function cannot be called at the same time
104 that the target event is being run. This has implications
105 for thread safety, and also means an event cannot be added
106 from inside the listener function for itself. The list of
107 events to be run are present inside of a mutable collection
108 that can't be changed during iteration.
110 Event registration and removal is not intended to be a "high
111 velocity" operation; it is a configurational operation. For
112 systems that need to quickly associate and deassociate with
113 events at high scale, use a mutable structure that is handled
114 from inside of a single listener.
116 .. seealso::
118 :func:`.listens_for`
120 :func:`.remove`
122 """
124 _event_key(target, identifier, fn).listen(*args, **kw)
127def listens_for(
128 target: Any, identifier: str, *args: Any, **kw: Any
129) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
130 """Decorate a function as a listener for the given target + identifier.
132 The :func:`.listens_for` decorator is part of the primary interface for the
133 SQLAlchemy event system, documented at :ref:`event_toplevel`.
135 This function generally shares the same kwargs as :func:`.listen`.
137 e.g.::
139 from sqlalchemy import event
140 from sqlalchemy.schema import UniqueConstraint
142 @event.listens_for(UniqueConstraint, "after_parent_attach")
143 def unique_constraint_name(const, table):
144 const.name = "uq_%s_%s" % (
145 table.name,
146 list(const.columns)[0].name
147 )
149 A given function can also be invoked for only the first invocation
150 of the event using the ``once`` argument::
152 @event.listens_for(Mapper, "before_configure", once=True)
153 def on_config():
154 do_config()
157 .. warning:: The ``once`` argument does not imply automatic de-registration
158 of the listener function after it has been invoked a first time; a
159 listener entry will remain associated with the target object.
160 Associating an arbitrarily high number of listeners without explicitly
161 removing them will cause memory to grow unbounded even if ``once=True``
162 is specified.
164 .. seealso::
166 :func:`.listen` - general description of event listening
168 """
170 def decorate(fn: Callable[..., Any]) -> Callable[..., Any]:
171 listen(target, identifier, fn, *args, **kw)
172 return fn
174 return decorate
177def remove(target: Any, identifier: str, fn: Callable[..., Any]) -> None:
178 """Remove an event listener.
180 The arguments here should match exactly those which were sent to
181 :func:`.listen`; all the event registration which proceeded as a result
182 of this call will be reverted by calling :func:`.remove` with the same
183 arguments.
185 e.g.::
187 # if a function was registered like this...
188 @event.listens_for(SomeMappedClass, "before_insert", propagate=True)
189 def my_listener_function(*arg):
190 pass
192 # ... it's removed like this
193 event.remove(SomeMappedClass, "before_insert", my_listener_function)
195 Above, the listener function associated with ``SomeMappedClass`` was also
196 propagated to subclasses of ``SomeMappedClass``; the :func:`.remove`
197 function will revert all of these operations.
199 .. note::
201 The :func:`.remove` function cannot be called at the same time
202 that the target event is being run. This has implications
203 for thread safety, and also means an event cannot be removed
204 from inside the listener function for itself. The list of
205 events to be run are present inside of a mutable collection
206 that can't be changed during iteration.
208 Event registration and removal is not intended to be a "high
209 velocity" operation; it is a configurational operation. For
210 systems that need to quickly associate and deassociate with
211 events at high scale, use a mutable structure that is handled
212 from inside of a single listener.
214 .. seealso::
216 :func:`.listen`
218 """
219 _event_key(target, identifier, fn).remove()
222def contains(target: Any, identifier: str, fn: Callable[..., Any]) -> bool:
223 """Return True if the given target/ident/fn is set up to listen."""
225 return _event_key(target, identifier, fn).contains()