events.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. # pool/events.py
  2. # Copyright (C) 2005-2026 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. from __future__ import annotations
  8. import typing
  9. from typing import Any
  10. from typing import Optional
  11. from typing import Type
  12. from typing import Union
  13. from .base import ConnectionPoolEntry
  14. from .base import Pool
  15. from .base import PoolProxiedConnection
  16. from .base import PoolResetState
  17. from .. import event
  18. from .. import util
  19. if typing.TYPE_CHECKING:
  20. from ..engine import Engine
  21. from ..engine.interfaces import DBAPIConnection
  22. class PoolEvents(event.Events[Pool]):
  23. """Available events for :class:`_pool.Pool`.
  24. The methods here define the name of an event as well
  25. as the names of members that are passed to listener
  26. functions.
  27. When using an :class:`.Engine` object created via :func:`_sa.create_engine`
  28. (or indirectly via :func:`.create_async_engine`), :class:`.PoolEvents`
  29. listeners are expected to be registered in terms of the :class:`.Engine`,
  30. which will direct the listeners to the :class:`.Pool` contained within::
  31. from sqlalchemy import create_engine
  32. from sqlalchemy import event
  33. engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test")
  34. @event.listens_for(engine, "checkout")
  35. def my_on_checkout(dbapi_conn, connection_rec, connection_proxy):
  36. "handle an on checkout event"
  37. :class:`.PoolEvents` may also be registered with the :class:`_pool.Pool`
  38. class, with the :class:`.Engine` class, as well as with instances of
  39. :class:`_pool.Pool`.
  40. .. tip::
  41. Registering :class:`.PoolEvents` with the :class:`.Engine`, if present,
  42. is recommended since the :meth:`.Engine.dispose` method will carry
  43. along event listeners from the old pool to the new pool.
  44. """ # noqa: E501
  45. _target_class_doc = "SomeEngineOrPool"
  46. _dispatch_target = Pool
  47. @util.preload_module("sqlalchemy.engine")
  48. @classmethod
  49. def _accept_with(
  50. cls,
  51. target: Union[Pool, Type[Pool], Engine, Type[Engine]],
  52. identifier: str,
  53. ) -> Optional[Union[Pool, Type[Pool]]]:
  54. if not typing.TYPE_CHECKING:
  55. Engine = util.preloaded.engine.Engine
  56. if isinstance(target, type):
  57. if issubclass(target, Engine):
  58. return Pool
  59. else:
  60. assert issubclass(target, Pool)
  61. return target
  62. elif isinstance(target, Engine):
  63. return target.pool
  64. elif isinstance(target, Pool):
  65. return target
  66. elif hasattr(target, "_no_async_engine_events"):
  67. target._no_async_engine_events()
  68. else:
  69. return None
  70. @classmethod
  71. def _listen(
  72. cls,
  73. event_key: event._EventKey[Pool],
  74. **kw: Any,
  75. ) -> None:
  76. target = event_key.dispatch_target
  77. kw.setdefault("asyncio", target._is_asyncio)
  78. event_key.base_listen(**kw)
  79. def connect(
  80. self,
  81. dbapi_connection: DBAPIConnection,
  82. connection_record: ConnectionPoolEntry,
  83. ) -> None:
  84. """Called at the moment a particular DBAPI connection is first
  85. created for a given :class:`_pool.Pool`.
  86. This event allows one to capture the point directly after which
  87. the DBAPI module-level ``.connect()`` method has been used in order
  88. to produce a new DBAPI connection.
  89. :param dbapi_connection: a DBAPI connection.
  90. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  91. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  92. the DBAPI connection.
  93. """
  94. def first_connect(
  95. self,
  96. dbapi_connection: DBAPIConnection,
  97. connection_record: ConnectionPoolEntry,
  98. ) -> None:
  99. """Called exactly once for the first time a DBAPI connection is
  100. checked out from a particular :class:`_pool.Pool`.
  101. The rationale for :meth:`_events.PoolEvents.first_connect`
  102. is to determine
  103. information about a particular series of database connections based
  104. on the settings used for all connections. Since a particular
  105. :class:`_pool.Pool`
  106. refers to a single "creator" function (which in terms
  107. of a :class:`_engine.Engine`
  108. refers to the URL and connection options used),
  109. it is typically valid to make observations about a single connection
  110. that can be safely assumed to be valid about all subsequent
  111. connections, such as the database version, the server and client
  112. encoding settings, collation settings, and many others.
  113. :param dbapi_connection: a DBAPI connection.
  114. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  115. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  116. the DBAPI connection.
  117. """
  118. def checkout(
  119. self,
  120. dbapi_connection: DBAPIConnection,
  121. connection_record: ConnectionPoolEntry,
  122. connection_proxy: PoolProxiedConnection,
  123. ) -> None:
  124. """Called when a connection is retrieved from the Pool.
  125. :param dbapi_connection: a DBAPI connection.
  126. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  127. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  128. the DBAPI connection.
  129. :param connection_proxy: the :class:`.PoolProxiedConnection` object
  130. which will proxy the public interface of the DBAPI connection for the
  131. lifespan of the checkout.
  132. If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current
  133. connection will be disposed and a fresh connection retrieved.
  134. Processing of all checkout listeners will abort and restart
  135. using the new connection.
  136. .. seealso:: :meth:`_events.ConnectionEvents.engine_connect`
  137. - a similar event
  138. which occurs upon creation of a new :class:`_engine.Connection`.
  139. """
  140. def checkin(
  141. self,
  142. dbapi_connection: Optional[DBAPIConnection],
  143. connection_record: ConnectionPoolEntry,
  144. ) -> None:
  145. """Called when a connection returns to the pool.
  146. Note that the connection may be closed, and may be None if the
  147. connection has been invalidated. ``checkin`` will not be called
  148. for detached connections. (They do not return to the pool.)
  149. :param dbapi_connection: a DBAPI connection.
  150. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  151. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  152. the DBAPI connection.
  153. """
  154. @event._legacy_signature(
  155. "2.0",
  156. ["dbapi_connection", "connection_record"],
  157. lambda dbapi_connection, connection_record, reset_state: (
  158. dbapi_connection,
  159. connection_record,
  160. ),
  161. )
  162. def reset(
  163. self,
  164. dbapi_connection: DBAPIConnection,
  165. connection_record: ConnectionPoolEntry,
  166. reset_state: PoolResetState,
  167. ) -> None:
  168. """Called before the "reset" action occurs for a pooled connection.
  169. This event represents
  170. when the ``rollback()`` method is called on the DBAPI connection
  171. before it is returned to the pool or discarded.
  172. A custom "reset" strategy may be implemented using this event hook,
  173. which may also be combined with disabling the default "reset"
  174. behavior using the :paramref:`_pool.Pool.reset_on_return` parameter.
  175. The primary difference between the :meth:`_events.PoolEvents.reset` and
  176. :meth:`_events.PoolEvents.checkin` events are that
  177. :meth:`_events.PoolEvents.reset` is called not just for pooled
  178. connections that are being returned to the pool, but also for
  179. connections that were detached using the
  180. :meth:`_engine.Connection.detach` method as well as asyncio connections
  181. that are being discarded due to garbage collection taking place on
  182. connections before the connection was checked in.
  183. Note that the event **is not** invoked for connections that were
  184. invalidated using :meth:`_engine.Connection.invalidate`. These
  185. events may be intercepted using the :meth:`.PoolEvents.soft_invalidate`
  186. and :meth:`.PoolEvents.invalidate` event hooks, and all "connection
  187. close" events may be intercepted using :meth:`.PoolEvents.close`.
  188. The :meth:`_events.PoolEvents.reset` event is usually followed by the
  189. :meth:`_events.PoolEvents.checkin` event, except in those
  190. cases where the connection is discarded immediately after reset.
  191. :param dbapi_connection: a DBAPI connection.
  192. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  193. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  194. the DBAPI connection.
  195. :param reset_state: :class:`.PoolResetState` instance which provides
  196. information about the circumstances under which the connection
  197. is being reset.
  198. .. versionadded:: 2.0
  199. .. seealso::
  200. :ref:`pool_reset_on_return`
  201. :meth:`_events.ConnectionEvents.rollback`
  202. :meth:`_events.ConnectionEvents.commit`
  203. """
  204. def invalidate(
  205. self,
  206. dbapi_connection: DBAPIConnection,
  207. connection_record: ConnectionPoolEntry,
  208. exception: Optional[BaseException],
  209. ) -> None:
  210. """Called when a DBAPI connection is to be "invalidated".
  211. This event is called any time the
  212. :meth:`.ConnectionPoolEntry.invalidate` method is invoked, either from
  213. API usage or via "auto-invalidation", without the ``soft`` flag.
  214. The event occurs before a final attempt to call ``.close()`` on the
  215. connection occurs.
  216. :param dbapi_connection: a DBAPI connection.
  217. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  218. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  219. the DBAPI connection.
  220. :param exception: the exception object corresponding to the reason
  221. for this invalidation, if any. May be ``None``.
  222. .. seealso::
  223. :ref:`pool_connection_invalidation`
  224. """
  225. def soft_invalidate(
  226. self,
  227. dbapi_connection: DBAPIConnection,
  228. connection_record: ConnectionPoolEntry,
  229. exception: Optional[BaseException],
  230. ) -> None:
  231. """Called when a DBAPI connection is to be "soft invalidated".
  232. This event is called any time the
  233. :meth:`.ConnectionPoolEntry.invalidate`
  234. method is invoked with the ``soft`` flag.
  235. Soft invalidation refers to when the connection record that tracks
  236. this connection will force a reconnect after the current connection
  237. is checked in. It does not actively close the dbapi_connection
  238. at the point at which it is called.
  239. :param dbapi_connection: a DBAPI connection.
  240. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  241. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  242. the DBAPI connection.
  243. :param exception: the exception object corresponding to the reason
  244. for this invalidation, if any. May be ``None``.
  245. """
  246. def close(
  247. self,
  248. dbapi_connection: DBAPIConnection,
  249. connection_record: ConnectionPoolEntry,
  250. ) -> None:
  251. """Called when a DBAPI connection is closed.
  252. The event is emitted before the close occurs.
  253. The close of a connection can fail; typically this is because
  254. the connection is already closed. If the close operation fails,
  255. the connection is discarded.
  256. The :meth:`.close` event corresponds to a connection that's still
  257. associated with the pool. To intercept close events for detached
  258. connections use :meth:`.close_detached`.
  259. :param dbapi_connection: a DBAPI connection.
  260. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  261. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  262. the DBAPI connection.
  263. """
  264. def detach(
  265. self,
  266. dbapi_connection: DBAPIConnection,
  267. connection_record: ConnectionPoolEntry,
  268. ) -> None:
  269. """Called when a DBAPI connection is "detached" from a pool.
  270. This event is emitted after the detach occurs. The connection
  271. is no longer associated with the given connection record.
  272. :param dbapi_connection: a DBAPI connection.
  273. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  274. :param connection_record: the :class:`.ConnectionPoolEntry` managing
  275. the DBAPI connection.
  276. """
  277. def close_detached(self, dbapi_connection: DBAPIConnection) -> None:
  278. """Called when a detached DBAPI connection is closed.
  279. The event is emitted before the close occurs.
  280. The close of a connection can fail; typically this is because
  281. the connection is already closed. If the close operation fails,
  282. the connection is discarded.
  283. :param dbapi_connection: a DBAPI connection.
  284. The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute.
  285. """