api.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. # event/api.py
  2. # Copyright (C) 2005-2025 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
  7. """Public API functions for the event system."""
  8. from __future__ import annotations
  9. from typing import Any
  10. from typing import Callable
  11. from .base import _registrars
  12. from .registry import _ET
  13. from .registry import _EventKey
  14. from .registry import _ListenerFnType
  15. from .. import exc
  16. from .. import util
  17. CANCEL = util.symbol("CANCEL")
  18. NO_RETVAL = util.symbol("NO_RETVAL")
  19. def _event_key(
  20. target: _ET, identifier: str, fn: _ListenerFnType
  21. ) -> _EventKey[_ET]:
  22. for evt_cls in _registrars[identifier]:
  23. tgt = evt_cls._accept_with(target, identifier)
  24. if tgt is not None:
  25. return _EventKey(target, identifier, fn, tgt)
  26. else:
  27. raise exc.InvalidRequestError(
  28. "No such event '%s' for target '%s'" % (identifier, target)
  29. )
  30. def listen(
  31. target: Any, identifier: str, fn: Callable[..., Any], *args: Any, **kw: Any
  32. ) -> None:
  33. """Register a listener function for the given target.
  34. The :func:`.listen` function is part of the primary interface for the
  35. SQLAlchemy event system, documented at :ref:`event_toplevel`.
  36. e.g.::
  37. from sqlalchemy import event
  38. from sqlalchemy.schema import UniqueConstraint
  39. def unique_constraint_name(const, table):
  40. const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)
  41. event.listen(
  42. UniqueConstraint, "after_parent_attach", unique_constraint_name
  43. )
  44. :param bool insert: The default behavior for event handlers is to append
  45. the decorated user defined function to an internal list of registered
  46. event listeners upon discovery. If a user registers a function with
  47. ``insert=True``, SQLAlchemy will insert (prepend) the function to the
  48. internal list upon discovery. This feature is not typically used or
  49. recommended by the SQLAlchemy maintainers, but is provided to ensure
  50. certain user defined functions can run before others, such as when
  51. :ref:`Changing the sql_mode in MySQL <mysql_sql_mode>`.
  52. :param bool named: When using named argument passing, the names listed in
  53. the function argument specification will be used as keys in the
  54. dictionary.
  55. See :ref:`event_named_argument_styles`.
  56. :param bool once: Private/Internal API usage. Deprecated. This parameter
  57. would provide that an event function would run only once per given
  58. target. It does not however imply automatic de-registration of the
  59. listener function; associating an arbitrarily high number of listeners
  60. without explicitly removing them will cause memory to grow unbounded even
  61. if ``once=True`` is specified.
  62. :param bool propagate: The ``propagate`` kwarg is available when working
  63. with ORM instrumentation and mapping events.
  64. See :class:`_ormevent.MapperEvents` and
  65. :meth:`_ormevent.MapperEvents.before_mapper_configured` for examples.
  66. :param bool retval: This flag applies only to specific event listeners,
  67. each of which includes documentation explaining when it should be used.
  68. By default, no listener ever requires a return value.
  69. However, some listeners do support special behaviors for return values,
  70. and include in their documentation that the ``retval=True`` flag is
  71. necessary for a return value to be processed.
  72. Event listener suites that make use of :paramref:`_event.listen.retval`
  73. include :class:`_events.ConnectionEvents` and
  74. :class:`_ormevent.AttributeEvents`.
  75. .. note::
  76. The :func:`.listen` function cannot be called at the same time
  77. that the target event is being run. This has implications
  78. for thread safety, and also means an event cannot be added
  79. from inside the listener function for itself. The list of
  80. events to be run are present inside of a mutable collection
  81. that can't be changed during iteration.
  82. Event registration and removal is not intended to be a "high
  83. velocity" operation; it is a configurational operation. For
  84. systems that need to quickly associate and deassociate with
  85. events at high scale, use a mutable structure that is handled
  86. from inside of a single listener.
  87. .. seealso::
  88. :func:`.listens_for`
  89. :func:`.remove`
  90. """
  91. _event_key(target, identifier, fn).listen(*args, **kw)
  92. def listens_for(
  93. target: Any, identifier: str, *args: Any, **kw: Any
  94. ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
  95. """Decorate a function as a listener for the given target + identifier.
  96. The :func:`.listens_for` decorator is part of the primary interface for the
  97. SQLAlchemy event system, documented at :ref:`event_toplevel`.
  98. This function generally shares the same kwargs as :func:`.listen`.
  99. e.g.::
  100. from sqlalchemy import event
  101. from sqlalchemy.schema import UniqueConstraint
  102. @event.listens_for(UniqueConstraint, "after_parent_attach")
  103. def unique_constraint_name(const, table):
  104. const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)
  105. A given function can also be invoked for only the first invocation
  106. of the event using the ``once`` argument::
  107. @event.listens_for(Mapper, "before_configure", once=True)
  108. def on_config():
  109. do_config()
  110. .. warning:: The ``once`` argument does not imply automatic de-registration
  111. of the listener function after it has been invoked a first time; a
  112. listener entry will remain associated with the target object.
  113. Associating an arbitrarily high number of listeners without explicitly
  114. removing them will cause memory to grow unbounded even if ``once=True``
  115. is specified.
  116. .. seealso::
  117. :func:`.listen` - general description of event listening
  118. """
  119. def decorate(fn: Callable[..., Any]) -> Callable[..., Any]:
  120. listen(target, identifier, fn, *args, **kw)
  121. return fn
  122. return decorate
  123. def remove(target: Any, identifier: str, fn: Callable[..., Any]) -> None:
  124. """Remove an event listener.
  125. The arguments here should match exactly those which were sent to
  126. :func:`.listen`; all the event registration which proceeded as a result
  127. of this call will be reverted by calling :func:`.remove` with the same
  128. arguments.
  129. e.g.::
  130. # if a function was registered like this...
  131. @event.listens_for(SomeMappedClass, "before_insert", propagate=True)
  132. def my_listener_function(*arg):
  133. pass
  134. # ... it's removed like this
  135. event.remove(SomeMappedClass, "before_insert", my_listener_function)
  136. Above, the listener function associated with ``SomeMappedClass`` was also
  137. propagated to subclasses of ``SomeMappedClass``; the :func:`.remove`
  138. function will revert all of these operations.
  139. .. note::
  140. The :func:`.remove` function cannot be called at the same time
  141. that the target event is being run. This has implications
  142. for thread safety, and also means an event cannot be removed
  143. from inside the listener function for itself. The list of
  144. events to be run are present inside of a mutable collection
  145. that can't be changed during iteration.
  146. Event registration and removal is not intended to be a "high
  147. velocity" operation; it is a configurational operation. For
  148. systems that need to quickly associate and deassociate with
  149. events at high scale, use a mutable structure that is handled
  150. from inside of a single listener.
  151. .. seealso::
  152. :func:`.listen`
  153. """
  154. _event_key(target, identifier, fn).remove()
  155. def contains(target: Any, identifier: str, fn: Callable[..., Any]) -> bool:
  156. """Return True if the given target/ident/fn is set up to listen."""
  157. return _event_key(target, identifier, fn).contains()