Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/pool/events.py: 79%

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

52 statements  

1# pool/events.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 

7from __future__ import annotations 

8 

9import typing 

10from typing import Any 

11from typing import Optional 

12from typing import Type 

13from typing import Union 

14 

15from .base import ConnectionPoolEntry 

16from .base import Pool 

17from .base import PoolProxiedConnection 

18from .base import PoolResetState 

19from .. import event 

20from .. import util 

21 

22if typing.TYPE_CHECKING: 

23 from ..engine import Engine 

24 from ..engine.interfaces import DBAPIConnection 

25 

26 

27class PoolEvents(event.Events[Pool]): 

28 """Available events for :class:`_pool.Pool`. 

29 

30 The methods here define the name of an event as well 

31 as the names of members that are passed to listener 

32 functions. 

33 

34 e.g.:: 

35 

36 from sqlalchemy import event 

37 

38 

39 def my_on_checkout(dbapi_conn, connection_rec, connection_proxy): 

40 "handle an on checkout event" 

41 

42 

43 event.listen(Pool, "checkout", my_on_checkout) 

44 

45 In addition to accepting the :class:`_pool.Pool` class and 

46 :class:`_pool.Pool` instances, :class:`_events.PoolEvents` also accepts 

47 :class:`_engine.Engine` objects and the :class:`_engine.Engine` class as 

48 targets, which will be resolved to the ``.pool`` attribute of the 

49 given engine or the :class:`_pool.Pool` class:: 

50 

51 engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test") 

52 

53 # will associate with engine.pool 

54 event.listen(engine, "checkout", my_on_checkout) 

55 

56 """ # noqa: E501 

57 

58 _target_class_doc = "SomeEngineOrPool" 

59 _dispatch_target = Pool 

60 

61 @util.preload_module("sqlalchemy.engine") 

62 @classmethod 

63 def _accept_with( 

64 cls, 

65 target: Union[Pool, Type[Pool], Engine, Type[Engine]], 

66 identifier: str, 

67 ) -> Optional[Union[Pool, Type[Pool]]]: 

68 if not typing.TYPE_CHECKING: 

69 Engine = util.preloaded.engine.Engine 

70 

71 if isinstance(target, type): 

72 if issubclass(target, Engine): 

73 return Pool 

74 else: 

75 assert issubclass(target, Pool) 

76 return target 

77 elif isinstance(target, Engine): 

78 return target.pool 

79 elif isinstance(target, Pool): 

80 return target 

81 elif hasattr(target, "_no_async_engine_events"): 

82 target._no_async_engine_events() 

83 else: 

84 return None 

85 

86 @classmethod 

87 def _listen( 

88 cls, 

89 event_key: event._EventKey[Pool], 

90 **kw: Any, 

91 ) -> None: 

92 target = event_key.dispatch_target 

93 

94 kw.setdefault("asyncio", target._is_asyncio) 

95 

96 event_key.base_listen(**kw) 

97 

98 def connect( 

99 self, 

100 dbapi_connection: DBAPIConnection, 

101 connection_record: ConnectionPoolEntry, 

102 ) -> None: 

103 """Called at the moment a particular DBAPI connection is first 

104 created for a given :class:`_pool.Pool`. 

105 

106 This event allows one to capture the point directly after which 

107 the DBAPI module-level ``.connect()`` method has been used in order 

108 to produce a new DBAPI connection. 

109 

110 :param dbapi_connection: a DBAPI connection. 

111 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

112 

113 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

114 the DBAPI connection. 

115 

116 """ 

117 

118 def first_connect( 

119 self, 

120 dbapi_connection: DBAPIConnection, 

121 connection_record: ConnectionPoolEntry, 

122 ) -> None: 

123 """Called exactly once for the first time a DBAPI connection is 

124 checked out from a particular :class:`_pool.Pool`. 

125 

126 The rationale for :meth:`_events.PoolEvents.first_connect` 

127 is to determine 

128 information about a particular series of database connections based 

129 on the settings used for all connections. Since a particular 

130 :class:`_pool.Pool` 

131 refers to a single "creator" function (which in terms 

132 of a :class:`_engine.Engine` 

133 refers to the URL and connection options used), 

134 it is typically valid to make observations about a single connection 

135 that can be safely assumed to be valid about all subsequent 

136 connections, such as the database version, the server and client 

137 encoding settings, collation settings, and many others. 

138 

139 :param dbapi_connection: a DBAPI connection. 

140 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

141 

142 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

143 the DBAPI connection. 

144 

145 """ 

146 

147 def checkout( 

148 self, 

149 dbapi_connection: DBAPIConnection, 

150 connection_record: ConnectionPoolEntry, 

151 connection_proxy: PoolProxiedConnection, 

152 ) -> None: 

153 """Called when a connection is retrieved from the Pool. 

154 

155 :param dbapi_connection: a DBAPI connection. 

156 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

157 

158 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

159 the DBAPI connection. 

160 

161 :param connection_proxy: the :class:`.PoolProxiedConnection` object 

162 which will proxy the public interface of the DBAPI connection for the 

163 lifespan of the checkout. 

164 

165 If you raise a :class:`~sqlalchemy.exc.DisconnectionError`, the current 

166 connection will be disposed and a fresh connection retrieved. 

167 Processing of all checkout listeners will abort and restart 

168 using the new connection. 

169 

170 .. seealso:: :meth:`_events.ConnectionEvents.engine_connect` 

171 - a similar event 

172 which occurs upon creation of a new :class:`_engine.Connection`. 

173 

174 """ 

175 

176 def checkin( 

177 self, 

178 dbapi_connection: Optional[DBAPIConnection], 

179 connection_record: ConnectionPoolEntry, 

180 ) -> None: 

181 """Called when a connection returns to the pool. 

182 

183 Note that the connection may be closed, and may be None if the 

184 connection has been invalidated. ``checkin`` will not be called 

185 for detached connections. (They do not return to the pool.) 

186 

187 :param dbapi_connection: a DBAPI connection. 

188 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

189 

190 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

191 the DBAPI connection. 

192 

193 """ 

194 

195 @event._legacy_signature( 

196 "2.0", 

197 ["dbapi_connection", "connection_record"], 

198 lambda dbapi_connection, connection_record, reset_state: ( 

199 dbapi_connection, 

200 connection_record, 

201 ), 

202 ) 

203 def reset( 

204 self, 

205 dbapi_connection: DBAPIConnection, 

206 connection_record: ConnectionPoolEntry, 

207 reset_state: PoolResetState, 

208 ) -> None: 

209 """Called before the "reset" action occurs for a pooled connection. 

210 

211 This event represents 

212 when the ``rollback()`` method is called on the DBAPI connection 

213 before it is returned to the pool or discarded. 

214 A custom "reset" strategy may be implemented using this event hook, 

215 which may also be combined with disabling the default "reset" 

216 behavior using the :paramref:`_pool.Pool.reset_on_return` parameter. 

217 

218 The primary difference between the :meth:`_events.PoolEvents.reset` and 

219 :meth:`_events.PoolEvents.checkin` events are that 

220 :meth:`_events.PoolEvents.reset` is called not just for pooled 

221 connections that are being returned to the pool, but also for 

222 connections that were detached using the 

223 :meth:`_engine.Connection.detach` method as well as asyncio connections 

224 that are being discarded due to garbage collection taking place on 

225 connections before the connection was checked in. 

226 

227 Note that the event **is not** invoked for connections that were 

228 invalidated using :meth:`_engine.Connection.invalidate`. These 

229 events may be intercepted using the :meth:`.PoolEvents.soft_invalidate` 

230 and :meth:`.PoolEvents.invalidate` event hooks, and all "connection 

231 close" events may be intercepted using :meth:`.PoolEvents.close`. 

232 

233 The :meth:`_events.PoolEvents.reset` event is usually followed by the 

234 :meth:`_events.PoolEvents.checkin` event, except in those 

235 cases where the connection is discarded immediately after reset. 

236 

237 :param dbapi_connection: a DBAPI connection. 

238 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

239 

240 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

241 the DBAPI connection. 

242 

243 :param reset_state: :class:`.PoolResetState` instance which provides 

244 information about the circumstances under which the connection 

245 is being reset. 

246 

247 .. versionadded:: 2.0 

248 

249 .. seealso:: 

250 

251 :ref:`pool_reset_on_return` 

252 

253 :meth:`_events.ConnectionEvents.rollback` 

254 

255 :meth:`_events.ConnectionEvents.commit` 

256 

257 """ 

258 

259 def invalidate( 

260 self, 

261 dbapi_connection: DBAPIConnection, 

262 connection_record: ConnectionPoolEntry, 

263 exception: Optional[BaseException], 

264 ) -> None: 

265 """Called when a DBAPI connection is to be "invalidated". 

266 

267 This event is called any time the 

268 :meth:`.ConnectionPoolEntry.invalidate` method is invoked, either from 

269 API usage or via "auto-invalidation", without the ``soft`` flag. 

270 

271 The event occurs before a final attempt to call ``.close()`` on the 

272 connection occurs. 

273 

274 :param dbapi_connection: a DBAPI connection. 

275 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

276 

277 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

278 the DBAPI connection. 

279 

280 :param exception: the exception object corresponding to the reason 

281 for this invalidation, if any. May be ``None``. 

282 

283 .. seealso:: 

284 

285 :ref:`pool_connection_invalidation` 

286 

287 """ 

288 

289 def soft_invalidate( 

290 self, 

291 dbapi_connection: DBAPIConnection, 

292 connection_record: ConnectionPoolEntry, 

293 exception: Optional[BaseException], 

294 ) -> None: 

295 """Called when a DBAPI connection is to be "soft invalidated". 

296 

297 This event is called any time the 

298 :meth:`.ConnectionPoolEntry.invalidate` 

299 method is invoked with the ``soft`` flag. 

300 

301 Soft invalidation refers to when the connection record that tracks 

302 this connection will force a reconnect after the current connection 

303 is checked in. It does not actively close the dbapi_connection 

304 at the point at which it is called. 

305 

306 :param dbapi_connection: a DBAPI connection. 

307 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

308 

309 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

310 the DBAPI connection. 

311 

312 :param exception: the exception object corresponding to the reason 

313 for this invalidation, if any. May be ``None``. 

314 

315 """ 

316 

317 def close( 

318 self, 

319 dbapi_connection: DBAPIConnection, 

320 connection_record: ConnectionPoolEntry, 

321 ) -> None: 

322 """Called when a DBAPI connection is closed. 

323 

324 The event is emitted before the close occurs. 

325 

326 The close of a connection can fail; typically this is because 

327 the connection is already closed. If the close operation fails, 

328 the connection is discarded. 

329 

330 The :meth:`.close` event corresponds to a connection that's still 

331 associated with the pool. To intercept close events for detached 

332 connections use :meth:`.close_detached`. 

333 

334 :param dbapi_connection: a DBAPI connection. 

335 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

336 

337 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

338 the DBAPI connection. 

339 

340 """ 

341 

342 def detach( 

343 self, 

344 dbapi_connection: DBAPIConnection, 

345 connection_record: ConnectionPoolEntry, 

346 ) -> None: 

347 """Called when a DBAPI connection is "detached" from a pool. 

348 

349 This event is emitted after the detach occurs. The connection 

350 is no longer associated with the given connection record. 

351 

352 :param dbapi_connection: a DBAPI connection. 

353 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

354 

355 :param connection_record: the :class:`.ConnectionPoolEntry` managing 

356 the DBAPI connection. 

357 

358 """ 

359 

360 def close_detached(self, dbapi_connection: DBAPIConnection) -> None: 

361 """Called when a detached DBAPI connection is closed. 

362 

363 The event is emitted before the close occurs. 

364 

365 The close of a connection can fail; typically this is because 

366 the connection is already closed. If the close operation fails, 

367 the connection is discarded. 

368 

369 :param dbapi_connection: a DBAPI connection. 

370 The :attr:`.ConnectionPoolEntry.dbapi_connection` attribute. 

371 

372 """