api.py 8.1 KB

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