api.py 7.9 KB

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