Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/events.py: 56%

77 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1# sqlalchemy/engine/events.py 

2# Copyright (C) 2005-2023 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 

8 

9from .base import Engine 

10from .interfaces import Connectable 

11from .interfaces import Dialect 

12from .. import event 

13from .. import exc 

14 

15 

16class ConnectionEvents(event.Events): 

17 """Available events for :class:`.Connectable`, which includes 

18 :class:`_engine.Connection` and :class:`_engine.Engine`. 

19 

20 The methods here define the name of an event as well as the names of 

21 members that are passed to listener functions. 

22 

23 An event listener can be associated with any :class:`.Connectable` 

24 class or instance, such as an :class:`_engine.Engine`, e.g.:: 

25 

26 from sqlalchemy import event, create_engine 

27 

28 def before_cursor_execute(conn, cursor, statement, parameters, context, 

29 executemany): 

30 log.info("Received statement: %s", statement) 

31 

32 engine = create_engine('postgresql://scott:tiger@localhost/test') 

33 event.listen(engine, "before_cursor_execute", before_cursor_execute) 

34 

35 or with a specific :class:`_engine.Connection`:: 

36 

37 with engine.begin() as conn: 

38 @event.listens_for(conn, 'before_cursor_execute') 

39 def before_cursor_execute(conn, cursor, statement, parameters, 

40 context, executemany): 

41 log.info("Received statement: %s", statement) 

42 

43 When the methods are called with a `statement` parameter, such as in 

44 :meth:`.after_cursor_execute` or :meth:`.before_cursor_execute`, 

45 the statement is the exact SQL string that was prepared for transmission 

46 to the DBAPI ``cursor`` in the connection's :class:`.Dialect`. 

47 

48 The :meth:`.before_execute` and :meth:`.before_cursor_execute` 

49 events can also be established with the ``retval=True`` flag, which 

50 allows modification of the statement and parameters to be sent 

51 to the database. The :meth:`.before_cursor_execute` event is 

52 particularly useful here to add ad-hoc string transformations, such 

53 as comments, to all executions:: 

54 

55 from sqlalchemy.engine import Engine 

56 from sqlalchemy import event 

57 

58 @event.listens_for(Engine, "before_cursor_execute", retval=True) 

59 def comment_sql_calls(conn, cursor, statement, parameters, 

60 context, executemany): 

61 statement = statement + " -- some comment" 

62 return statement, parameters 

63 

64 .. note:: :class:`_events.ConnectionEvents` can be established on any 

65 combination of :class:`_engine.Engine`, :class:`_engine.Connection`, 

66 as well 

67 as instances of each of those classes. Events across all 

68 four scopes will fire off for a given instance of 

69 :class:`_engine.Connection`. However, for performance reasons, the 

70 :class:`_engine.Connection` object determines at instantiation time 

71 whether or not its parent :class:`_engine.Engine` has event listeners 

72 established. Event listeners added to the :class:`_engine.Engine` 

73 class or to an instance of :class:`_engine.Engine` 

74 *after* the instantiation 

75 of a dependent :class:`_engine.Connection` instance will usually 

76 *not* be available on that :class:`_engine.Connection` instance. 

77 The newly 

78 added listeners will instead take effect for 

79 :class:`_engine.Connection` 

80 instances created subsequent to those event listeners being 

81 established on the parent :class:`_engine.Engine` class or instance. 

82 

83 :param retval=False: Applies to the :meth:`.before_execute` and 

84 :meth:`.before_cursor_execute` events only. When True, the 

85 user-defined event function must have a return value, which 

86 is a tuple of parameters that replace the given statement 

87 and parameters. See those methods for a description of 

88 specific return arguments. 

89 

90 """ 

91 

92 _target_class_doc = "SomeEngine" 

93 _dispatch_target = Connectable 

94 

95 @classmethod 

96 def _listen(cls, event_key, retval=False): 

97 target, identifier, fn = ( 

98 event_key.dispatch_target, 

99 event_key.identifier, 

100 event_key._listen_fn, 

101 ) 

102 

103 target._has_events = True 

104 

105 if not retval: 

106 if identifier == "before_execute": 

107 orig_fn = fn 

108 

109 def wrap_before_execute( 

110 conn, clauseelement, multiparams, params, execution_options 

111 ): 

112 orig_fn( 

113 conn, 

114 clauseelement, 

115 multiparams, 

116 params, 

117 execution_options, 

118 ) 

119 return clauseelement, multiparams, params 

120 

121 fn = wrap_before_execute 

122 elif identifier == "before_cursor_execute": 

123 orig_fn = fn 

124 

125 def wrap_before_cursor_execute( 

126 conn, cursor, statement, parameters, context, executemany 

127 ): 

128 orig_fn( 

129 conn, 

130 cursor, 

131 statement, 

132 parameters, 

133 context, 

134 executemany, 

135 ) 

136 return statement, parameters 

137 

138 fn = wrap_before_cursor_execute 

139 elif retval and identifier not in ( 

140 "before_execute", 

141 "before_cursor_execute", 

142 "handle_error", 

143 ): 

144 raise exc.ArgumentError( 

145 "Only the 'before_execute', " 

146 "'before_cursor_execute' and 'handle_error' engine " 

147 "event listeners accept the 'retval=True' " 

148 "argument." 

149 ) 

150 event_key.with_wrapper(fn).base_listen() 

151 

152 @event._legacy_signature( 

153 "1.4", 

154 ["conn", "clauseelement", "multiparams", "params"], 

155 lambda conn, clauseelement, multiparams, params, execution_options: ( 

156 conn, 

157 clauseelement, 

158 multiparams, 

159 params, 

160 ), 

161 ) 

162 def before_execute( 

163 self, conn, clauseelement, multiparams, params, execution_options 

164 ): 

165 """Intercept high level execute() events, receiving uncompiled 

166 SQL constructs and other objects prior to rendering into SQL. 

167 

168 This event is good for debugging SQL compilation issues as well 

169 as early manipulation of the parameters being sent to the database, 

170 as the parameter lists will be in a consistent format here. 

171 

172 This event can be optionally established with the ``retval=True`` 

173 flag. The ``clauseelement``, ``multiparams``, and ``params`` 

174 arguments should be returned as a three-tuple in this case:: 

175 

176 @event.listens_for(Engine, "before_execute", retval=True) 

177 def before_execute(conn, clauseelement, multiparams, params): 

178 # do something with clauseelement, multiparams, params 

179 return clauseelement, multiparams, params 

180 

181 :param conn: :class:`_engine.Connection` object 

182 :param clauseelement: SQL expression construct, :class:`.Compiled` 

183 instance, or string statement passed to 

184 :meth:`_engine.Connection.execute`. 

185 :param multiparams: Multiple parameter sets, a list of dictionaries. 

186 :param params: Single parameter set, a single dictionary. 

187 :param execution_options: dictionary of execution 

188 options passed along with the statement, if any. This is a merge 

189 of all options that will be used, including those of the statement, 

190 the connection, and those passed in to the method itself for 

191 the 2.0 style of execution. 

192 

193 .. versionadded: 1.4 

194 

195 .. seealso:: 

196 

197 :meth:`.before_cursor_execute` 

198 

199 """ 

200 

201 @event._legacy_signature( 

202 "1.4", 

203 ["conn", "clauseelement", "multiparams", "params", "result"], 

204 lambda conn, clauseelement, multiparams, params, execution_options, result: ( # noqa 

205 conn, 

206 clauseelement, 

207 multiparams, 

208 params, 

209 result, 

210 ), 

211 ) 

212 def after_execute( 

213 self, 

214 conn, 

215 clauseelement, 

216 multiparams, 

217 params, 

218 execution_options, 

219 result, 

220 ): 

221 """Intercept high level execute() events after execute. 

222 

223 

224 :param conn: :class:`_engine.Connection` object 

225 :param clauseelement: SQL expression construct, :class:`.Compiled` 

226 instance, or string statement passed to 

227 :meth:`_engine.Connection.execute`. 

228 :param multiparams: Multiple parameter sets, a list of dictionaries. 

229 :param params: Single parameter set, a single dictionary. 

230 :param execution_options: dictionary of execution 

231 options passed along with the statement, if any. This is a merge 

232 of all options that will be used, including those of the statement, 

233 the connection, and those passed in to the method itself for 

234 the 2.0 style of execution. 

235 

236 .. versionadded: 1.4 

237 

238 :param result: :class:`_engine.CursorResult` generated by the 

239 execution. 

240 

241 """ 

242 

243 def before_cursor_execute( 

244 self, conn, cursor, statement, parameters, context, executemany 

245 ): 

246 """Intercept low-level cursor execute() events before execution, 

247 receiving the string SQL statement and DBAPI-specific parameter list to 

248 be invoked against a cursor. 

249 

250 This event is a good choice for logging as well as late modifications 

251 to the SQL string. It's less ideal for parameter modifications except 

252 for those which are specific to a target backend. 

253 

254 This event can be optionally established with the ``retval=True`` 

255 flag. The ``statement`` and ``parameters`` arguments should be 

256 returned as a two-tuple in this case:: 

257 

258 @event.listens_for(Engine, "before_cursor_execute", retval=True) 

259 def before_cursor_execute(conn, cursor, statement, 

260 parameters, context, executemany): 

261 # do something with statement, parameters 

262 return statement, parameters 

263 

264 See the example at :class:`_events.ConnectionEvents`. 

265 

266 :param conn: :class:`_engine.Connection` object 

267 :param cursor: DBAPI cursor object 

268 :param statement: string SQL statement, as to be passed to the DBAPI 

269 :param parameters: Dictionary, tuple, or list of parameters being 

270 passed to the ``execute()`` or ``executemany()`` method of the 

271 DBAPI ``cursor``. In some cases may be ``None``. 

272 :param context: :class:`.ExecutionContext` object in use. May 

273 be ``None``. 

274 :param executemany: boolean, if ``True``, this is an ``executemany()`` 

275 call, if ``False``, this is an ``execute()`` call. 

276 

277 .. seealso:: 

278 

279 :meth:`.before_execute` 

280 

281 :meth:`.after_cursor_execute` 

282 

283 """ 

284 

285 def after_cursor_execute( 

286 self, conn, cursor, statement, parameters, context, executemany 

287 ): 

288 """Intercept low-level cursor execute() events after execution. 

289 

290 :param conn: :class:`_engine.Connection` object 

291 :param cursor: DBAPI cursor object. Will have results pending 

292 if the statement was a SELECT, but these should not be consumed 

293 as they will be needed by the :class:`_engine.CursorResult`. 

294 :param statement: string SQL statement, as passed to the DBAPI 

295 :param parameters: Dictionary, tuple, or list of parameters being 

296 passed to the ``execute()`` or ``executemany()`` method of the 

297 DBAPI ``cursor``. In some cases may be ``None``. 

298 :param context: :class:`.ExecutionContext` object in use. May 

299 be ``None``. 

300 :param executemany: boolean, if ``True``, this is an ``executemany()`` 

301 call, if ``False``, this is an ``execute()`` call. 

302 

303 """ 

304 

305 def handle_error(self, exception_context): 

306 r"""Intercept all exceptions processed by the 

307 :class:`_engine.Connection`. 

308 

309 This includes all exceptions emitted by the DBAPI as well as 

310 within SQLAlchemy's statement invocation process, including 

311 encoding errors and other statement validation errors. Other areas 

312 in which the event is invoked include transaction begin and end, 

313 result row fetching, cursor creation. 

314 

315 Note that :meth:`.handle_error` may support new kinds of exceptions 

316 and new calling scenarios at *any time*. Code which uses this 

317 event must expect new calling patterns to be present in minor 

318 releases. 

319 

320 To support the wide variety of members that correspond to an exception, 

321 as well as to allow extensibility of the event without backwards 

322 incompatibility, the sole argument received is an instance of 

323 :class:`.ExceptionContext`. This object contains data members 

324 representing detail about the exception. 

325 

326 Use cases supported by this hook include: 

327 

328 * read-only, low-level exception handling for logging and 

329 debugging purposes 

330 * exception re-writing 

331 * Establishing or disabling whether a connection or the owning 

332 connection pool is invalidated or expired in response to a 

333 specific exception [1]_. 

334 

335 The hook is called while the cursor from the failed operation 

336 (if any) is still open and accessible. Special cleanup operations 

337 can be called on this cursor; SQLAlchemy will attempt to close 

338 this cursor subsequent to this hook being invoked. If the connection 

339 is in "autocommit" mode, the transaction also remains open within 

340 the scope of this hook; the rollback of the per-statement transaction 

341 also occurs after the hook is called. 

342 

343 .. note:: 

344 

345 .. [1] The pool "pre_ping" handler enabled using the 

346 :paramref:`_sa.create_engine.pool_pre_ping` parameter does 

347 **not** consult this event before deciding if the "ping" 

348 returned false, as opposed to receiving an unhandled error. 

349 For this use case, the :ref:`legacy recipe based on 

350 engine_connect() may be used 

351 <pool_disconnects_pessimistic_custom>`. A future API allow 

352 more comprehensive customization of the "disconnect" 

353 detection mechanism across all functions. 

354 

355 A handler function has two options for replacing 

356 the SQLAlchemy-constructed exception into one that is user 

357 defined. It can either raise this new exception directly, in 

358 which case all further event listeners are bypassed and the 

359 exception will be raised, after appropriate cleanup as taken 

360 place:: 

361 

362 @event.listens_for(Engine, "handle_error") 

363 def handle_exception(context): 

364 if isinstance(context.original_exception, 

365 psycopg2.OperationalError) and \ 

366 "failed" in str(context.original_exception): 

367 raise MySpecialException("failed operation") 

368 

369 .. warning:: Because the 

370 :meth:`_events.ConnectionEvents.handle_error` 

371 event specifically provides for exceptions to be re-thrown as 

372 the ultimate exception raised by the failed statement, 

373 **stack traces will be misleading** if the user-defined event 

374 handler itself fails and throws an unexpected exception; 

375 the stack trace may not illustrate the actual code line that 

376 failed! It is advised to code carefully here and use 

377 logging and/or inline debugging if unexpected exceptions are 

378 occurring. 

379 

380 Alternatively, a "chained" style of event handling can be 

381 used, by configuring the handler with the ``retval=True`` 

382 modifier and returning the new exception instance from the 

383 function. In this case, event handling will continue onto the 

384 next handler. The "chained" exception is available using 

385 :attr:`.ExceptionContext.chained_exception`:: 

386 

387 @event.listens_for(Engine, "handle_error", retval=True) 

388 def handle_exception(context): 

389 if context.chained_exception is not None and \ 

390 "special" in context.chained_exception.message: 

391 return MySpecialException("failed", 

392 cause=context.chained_exception) 

393 

394 Handlers that return ``None`` may be used within the chain; when 

395 a handler returns ``None``, the previous exception instance, 

396 if any, is maintained as the current exception that is passed onto the 

397 next handler. 

398 

399 When a custom exception is raised or returned, SQLAlchemy raises 

400 this new exception as-is, it is not wrapped by any SQLAlchemy 

401 object. If the exception is not a subclass of 

402 :class:`sqlalchemy.exc.StatementError`, 

403 certain features may not be available; currently this includes 

404 the ORM's feature of adding a detail hint about "autoflush" to 

405 exceptions raised within the autoflush process. 

406 

407 :param context: an :class:`.ExceptionContext` object. See this 

408 class for details on all available members. 

409 

410 .. versionadded:: 0.9.7 Added the 

411 :meth:`_events.ConnectionEvents.handle_error` hook. 

412 

413 .. versionchanged:: 1.1 The :meth:`.handle_error` event will now 

414 receive all exceptions that inherit from ``BaseException``, 

415 including ``SystemExit`` and ``KeyboardInterrupt``. The setting for 

416 :attr:`.ExceptionContext.is_disconnect` is ``True`` in this case and 

417 the default for 

418 :attr:`.ExceptionContext.invalidate_pool_on_disconnect` is 

419 ``False``. 

420 

421 .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is now 

422 invoked when an :class:`_engine.Engine` fails during the initial 

423 call to :meth:`_engine.Engine.connect`, as well as when a 

424 :class:`_engine.Connection` object encounters an error during a 

425 reconnect operation. 

426 

427 .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is 

428 not fired off when a dialect makes use of the 

429 ``skip_user_error_events`` execution option. This is used 

430 by dialects which intend to catch SQLAlchemy-specific exceptions 

431 within specific operations, such as when the MySQL dialect detects 

432 a table not present within the ``has_table()`` dialect method. 

433 Prior to 1.0.0, code which implements :meth:`.handle_error` needs 

434 to ensure that exceptions thrown in these scenarios are re-raised 

435 without modification. 

436 

437 """ 

438 

439 def engine_connect(self, conn, branch): 

440 """Intercept the creation of a new :class:`_engine.Connection`. 

441 

442 This event is called typically as the direct result of calling 

443 the :meth:`_engine.Engine.connect` method. 

444 

445 It differs from the :meth:`_events.PoolEvents.connect` method, which 

446 refers to the actual connection to a database at the DBAPI level; 

447 a DBAPI connection may be pooled and reused for many operations. 

448 In contrast, this event refers only to the production of a higher level 

449 :class:`_engine.Connection` wrapper around such a DBAPI connection. 

450 

451 It also differs from the :meth:`_events.PoolEvents.checkout` event 

452 in that it is specific to the :class:`_engine.Connection` object, 

453 not the 

454 DBAPI connection that :meth:`_events.PoolEvents.checkout` deals with, 

455 although 

456 this DBAPI connection is available here via the 

457 :attr:`_engine.Connection.connection` attribute. 

458 But note there can in fact 

459 be multiple :meth:`_events.PoolEvents.checkout` 

460 events within the lifespan 

461 of a single :class:`_engine.Connection` object, if that 

462 :class:`_engine.Connection` 

463 is invalidated and re-established. There can also be multiple 

464 :class:`_engine.Connection` 

465 objects generated for the same already-checked-out 

466 DBAPI connection, in the case that a "branch" of a 

467 :class:`_engine.Connection` 

468 is produced. 

469 

470 :param conn: :class:`_engine.Connection` object. 

471 :param branch: if True, this is a "branch" of an existing 

472 :class:`_engine.Connection`. A branch is generated within the course 

473 of a statement execution to invoke supplemental statements, most 

474 typically to pre-execute a SELECT of a default value for the purposes 

475 of an INSERT statement. 

476 

477 .. seealso:: 

478 

479 :meth:`_events.PoolEvents.checkout` 

480 the lower-level pool checkout event 

481 for an individual DBAPI connection 

482 

483 """ 

484 

485 def set_connection_execution_options(self, conn, opts): 

486 """Intercept when the :meth:`_engine.Connection.execution_options` 

487 method is called. 

488 

489 This method is called after the new :class:`_engine.Connection` 

490 has been 

491 produced, with the newly updated execution options collection, but 

492 before the :class:`.Dialect` has acted upon any of those new options. 

493 

494 Note that this method is not called when a new 

495 :class:`_engine.Connection` 

496 is produced which is inheriting execution options from its parent 

497 :class:`_engine.Engine`; to intercept this condition, use the 

498 :meth:`_events.ConnectionEvents.engine_connect` event. 

499 

500 :param conn: The newly copied :class:`_engine.Connection` object 

501 

502 :param opts: dictionary of options that were passed to the 

503 :meth:`_engine.Connection.execution_options` method. 

504 

505 .. versionadded:: 0.9.0 

506 

507 .. seealso:: 

508 

509 :meth:`_events.ConnectionEvents.set_engine_execution_options` 

510 - event 

511 which is called when :meth:`_engine.Engine.execution_options` 

512 is called. 

513 

514 

515 """ 

516 

517 def set_engine_execution_options(self, engine, opts): 

518 """Intercept when the :meth:`_engine.Engine.execution_options` 

519 method is called. 

520 

521 The :meth:`_engine.Engine.execution_options` method produces a shallow 

522 copy of the :class:`_engine.Engine` which stores the new options. 

523 That new 

524 :class:`_engine.Engine` is passed here. 

525 A particular application of this 

526 method is to add a :meth:`_events.ConnectionEvents.engine_connect` 

527 event 

528 handler to the given :class:`_engine.Engine` 

529 which will perform some per- 

530 :class:`_engine.Connection` task specific to these execution options. 

531 

532 :param conn: The newly copied :class:`_engine.Engine` object 

533 

534 :param opts: dictionary of options that were passed to the 

535 :meth:`_engine.Connection.execution_options` method. 

536 

537 .. versionadded:: 0.9.0 

538 

539 .. seealso:: 

540 

541 :meth:`_events.ConnectionEvents.set_connection_execution_options` 

542 - event 

543 which is called when :meth:`_engine.Connection.execution_options` 

544 is 

545 called. 

546 

547 """ 

548 

549 def engine_disposed(self, engine): 

550 """Intercept when the :meth:`_engine.Engine.dispose` method is called. 

551 

552 The :meth:`_engine.Engine.dispose` method instructs the engine to 

553 "dispose" of it's connection pool (e.g. :class:`_pool.Pool`), and 

554 replaces it with a new one. Disposing of the old pool has the 

555 effect that existing checked-in connections are closed. The new 

556 pool does not establish any new connections until it is first used. 

557 

558 This event can be used to indicate that resources related to the 

559 :class:`_engine.Engine` should also be cleaned up, 

560 keeping in mind that the 

561 :class:`_engine.Engine` 

562 can still be used for new requests in which case 

563 it re-acquires connection resources. 

564 

565 .. versionadded:: 1.0.5 

566 

567 """ 

568 

569 def begin(self, conn): 

570 """Intercept begin() events. 

571 

572 :param conn: :class:`_engine.Connection` object 

573 

574 """ 

575 

576 def rollback(self, conn): 

577 """Intercept rollback() events, as initiated by a 

578 :class:`.Transaction`. 

579 

580 Note that the :class:`_pool.Pool` also "auto-rolls back" 

581 a DBAPI connection upon checkin, if the ``reset_on_return`` 

582 flag is set to its default value of ``'rollback'``. 

583 To intercept this 

584 rollback, use the :meth:`_events.PoolEvents.reset` hook. 

585 

586 :param conn: :class:`_engine.Connection` object 

587 

588 .. seealso:: 

589 

590 :meth:`_events.PoolEvents.reset` 

591 

592 """ 

593 

594 def commit(self, conn): 

595 """Intercept commit() events, as initiated by a 

596 :class:`.Transaction`. 

597 

598 Note that the :class:`_pool.Pool` may also "auto-commit" 

599 a DBAPI connection upon checkin, if the ``reset_on_return`` 

600 flag is set to the value ``'commit'``. To intercept this 

601 commit, use the :meth:`_events.PoolEvents.reset` hook. 

602 

603 :param conn: :class:`_engine.Connection` object 

604 """ 

605 

606 def savepoint(self, conn, name): 

607 """Intercept savepoint() events. 

608 

609 :param conn: :class:`_engine.Connection` object 

610 :param name: specified name used for the savepoint. 

611 

612 """ 

613 

614 def rollback_savepoint(self, conn, name, context): 

615 """Intercept rollback_savepoint() events. 

616 

617 :param conn: :class:`_engine.Connection` object 

618 :param name: specified name used for the savepoint. 

619 :param context: not used 

620 

621 """ 

622 # TODO: deprecate "context" 

623 

624 def release_savepoint(self, conn, name, context): 

625 """Intercept release_savepoint() events. 

626 

627 :param conn: :class:`_engine.Connection` object 

628 :param name: specified name used for the savepoint. 

629 :param context: not used 

630 

631 """ 

632 # TODO: deprecate "context" 

633 

634 def begin_twophase(self, conn, xid): 

635 """Intercept begin_twophase() events. 

636 

637 :param conn: :class:`_engine.Connection` object 

638 :param xid: two-phase XID identifier 

639 

640 """ 

641 

642 def prepare_twophase(self, conn, xid): 

643 """Intercept prepare_twophase() events. 

644 

645 :param conn: :class:`_engine.Connection` object 

646 :param xid: two-phase XID identifier 

647 """ 

648 

649 def rollback_twophase(self, conn, xid, is_prepared): 

650 """Intercept rollback_twophase() events. 

651 

652 :param conn: :class:`_engine.Connection` object 

653 :param xid: two-phase XID identifier 

654 :param is_prepared: boolean, indicates if 

655 :meth:`.TwoPhaseTransaction.prepare` was called. 

656 

657 """ 

658 

659 def commit_twophase(self, conn, xid, is_prepared): 

660 """Intercept commit_twophase() events. 

661 

662 :param conn: :class:`_engine.Connection` object 

663 :param xid: two-phase XID identifier 

664 :param is_prepared: boolean, indicates if 

665 :meth:`.TwoPhaseTransaction.prepare` was called. 

666 

667 """ 

668 

669 

670class DialectEvents(event.Events): 

671 """event interface for execution-replacement functions. 

672 

673 These events allow direct instrumentation and replacement 

674 of key dialect functions which interact with the DBAPI. 

675 

676 .. note:: 

677 

678 :class:`.DialectEvents` hooks should be considered **semi-public** 

679 and experimental. 

680 These hooks are not for general use and are only for those situations 

681 where intricate re-statement of DBAPI mechanics must be injected onto 

682 an existing dialect. For general-use statement-interception events, 

683 please use the :class:`_events.ConnectionEvents` interface. 

684 

685 .. seealso:: 

686 

687 :meth:`_events.ConnectionEvents.before_cursor_execute` 

688 

689 :meth:`_events.ConnectionEvents.before_execute` 

690 

691 :meth:`_events.ConnectionEvents.after_cursor_execute` 

692 

693 :meth:`_events.ConnectionEvents.after_execute` 

694 

695 

696 .. versionadded:: 0.9.4 

697 

698 """ 

699 

700 _target_class_doc = "SomeEngine" 

701 _dispatch_target = Dialect 

702 

703 @classmethod 

704 def _listen(cls, event_key, retval=False): 

705 target = event_key.dispatch_target 

706 

707 target._has_events = True 

708 event_key.base_listen() 

709 

710 @classmethod 

711 def _accept_with(cls, target): 

712 if isinstance(target, type): 

713 if issubclass(target, Engine): 

714 return Dialect 

715 elif issubclass(target, Dialect): 

716 return target 

717 elif isinstance(target, Engine): 

718 return target.dialect 

719 elif isinstance(target, Dialect): 

720 return target 

721 elif hasattr(target, "dispatch") and hasattr( 

722 target.dispatch._events, "_no_async_engine_events" 

723 ): 

724 target.dispatch._events._no_async_engine_events() 

725 else: 

726 return None 

727 

728 def do_connect(self, dialect, conn_rec, cargs, cparams): 

729 """Receive connection arguments before a connection is made. 

730 

731 This event is useful in that it allows the handler to manipulate the 

732 cargs and/or cparams collections that control how the DBAPI 

733 ``connect()`` function will be called. ``cargs`` will always be a 

734 Python list that can be mutated in-place, and ``cparams`` a Python 

735 dictionary that may also be mutated:: 

736 

737 e = create_engine("postgresql+psycopg2://user@host/dbname") 

738 

739 @event.listens_for(e, 'do_connect') 

740 def receive_do_connect(dialect, conn_rec, cargs, cparams): 

741 cparams["password"] = "some_password" 

742 

743 The event hook may also be used to override the call to ``connect()`` 

744 entirely, by returning a non-``None`` DBAPI connection object:: 

745 

746 e = create_engine("postgresql+psycopg2://user@host/dbname") 

747 

748 @event.listens_for(e, 'do_connect') 

749 def receive_do_connect(dialect, conn_rec, cargs, cparams): 

750 return psycopg2.connect(*cargs, **cparams) 

751 

752 

753 .. versionadded:: 1.0.3 

754 

755 .. seealso:: 

756 

757 :ref:`custom_dbapi_args` 

758 

759 """ 

760 

761 def do_executemany(self, cursor, statement, parameters, context): 

762 """Receive a cursor to have executemany() called. 

763 

764 Return the value True to halt further events from invoking, 

765 and to indicate that the cursor execution has already taken 

766 place within the event handler. 

767 

768 """ 

769 

770 def do_execute_no_params(self, cursor, statement, context): 

771 """Receive a cursor to have execute() with no parameters called. 

772 

773 Return the value True to halt further events from invoking, 

774 and to indicate that the cursor execution has already taken 

775 place within the event handler. 

776 

777 """ 

778 

779 def do_execute(self, cursor, statement, parameters, context): 

780 """Receive a cursor to have execute() called. 

781 

782 Return the value True to halt further events from invoking, 

783 and to indicate that the cursor execution has already taken 

784 place within the event handler. 

785 

786 """ 

787 

788 def do_setinputsizes( 

789 self, inputsizes, cursor, statement, parameters, context 

790 ): 

791 """Receive the setinputsizes dictionary for possible modification. 

792 

793 This event is emitted in the case where the dialect makes use of the 

794 DBAPI ``cursor.setinputsizes()`` method which passes information about 

795 parameter binding for a particular statement. The given 

796 ``inputsizes`` dictionary will contain :class:`.BindParameter` objects 

797 as keys, linked to DBAPI-specific type objects as values; for 

798 parameters that are not bound, they are added to the dictionary with 

799 ``None`` as the value, which means the parameter will not be included 

800 in the ultimate setinputsizes call. The event may be used to inspect 

801 and/or log the datatypes that are being bound, as well as to modify the 

802 dictionary in place. Parameters can be added, modified, or removed 

803 from this dictionary. Callers will typically want to inspect the 

804 :attr:`.BindParameter.type` attribute of the given bind objects in 

805 order to make decisions about the DBAPI object. 

806 

807 After the event, the ``inputsizes`` dictionary is converted into 

808 an appropriate datastructure to be passed to ``cursor.setinputsizes``; 

809 either a list for a positional bound parameter execution style, 

810 or a dictionary of string parameter keys to DBAPI type objects for 

811 a named bound parameter execution style. 

812 

813 The setinputsizes hook overall is only used for dialects which include 

814 the flag ``use_setinputsizes=True``. Dialects which use this 

815 include cx_Oracle, pg8000, asyncpg, and pyodbc dialects. 

816 

817 .. note:: 

818 

819 For use with pyodbc, the ``use_setinputsizes`` flag 

820 must be passed to the dialect, e.g.:: 

821 

822 create_engine("mssql+pyodbc://...", use_setinputsizes=True) 

823 

824 .. seealso:: 

825 

826 :ref:`mssql_pyodbc_setinputsizes` 

827 

828 .. versionadded:: 1.2.9 

829 

830 .. seealso:: 

831 

832 :ref:`cx_oracle_setinputsizes` 

833 

834 """ 

835 pass