1# sqlalchemy/interfaces.py 
    2# Copyright (C) 2007-2021 the SQLAlchemy authors and contributors 
    3# <see AUTHORS file> 
    4# Copyright (C) 2007 Jason Kirtland jek@discorporate.us 
    5# 
    6# This module is part of SQLAlchemy and is released under 
    7# the MIT License: http://www.opensource.org/licenses/mit-license.php 
    8 
    9"""Deprecated core event interfaces. 
    10 
    11 
    12.. deprecated:: 0.7 
    13    As of SQLAlchemy 0.7, the new event system described in 
    14    :ref:`event_toplevel` replaces the extension/proxy/listener system, 
    15    providing a consistent interface to all events without the need for 
    16    subclassing. 
    17 
    18""" 
    19 
    20from . import event 
    21from . import util 
    22 
    23 
    24class PoolListener(object): 
    25    """Hooks into the lifecycle of connections in a :class:`_pool.Pool`. 
    26 
    27    .. deprecated:: 0.7 
    28 
    29       :class:`.PoolListener` is deprecated and will be removed in a future 
    30       release.  Please refer to :func:`.event.listen` in conjunction with 
    31       the :class:`_events.PoolEvents` listener interface. 
    32 
    33    Usage:: 
    34 
    35        class MyListener(PoolListener): 
    36            def connect(self, dbapi_con, con_record): 
    37                '''perform connect operations''' 
    38            # etc. 
    39 
    40        # create a new pool with a listener 
    41        p = QueuePool(..., listeners=[MyListener()]) 
    42 
    43        # add a listener after the fact 
    44        p.add_listener(MyListener()) 
    45 
    46        # usage with create_engine() 
    47        e = create_engine("url://", listeners=[MyListener()]) 
    48 
    49    All of the standard connection :class:`~sqlalchemy.pool.Pool` types can 
    50    accept event listeners for key connection lifecycle events: 
    51    creation, pool check-out and check-in.  There are no events fired 
    52    when a connection closes. 
    53 
    54    For any given DB-API connection, there will be one ``connect`` 
    55    event, `n` number of ``checkout`` events, and either `n` or `n - 1` 
    56    ``checkin`` events.  (If a ``Connection`` is detached from its 
    57    pool via the ``detach()`` method, it won't be checked back in.) 
    58 
    59    These are low-level events for low-level objects: raw Python 
    60    DB-API connections, without the conveniences of the SQLAlchemy 
    61    ``Connection`` wrapper, ``Dialect`` services or ``ClauseElement`` 
    62    execution.  If you execute SQL through the connection, explicitly 
    63    closing all cursors and other resources is recommended. 
    64 
    65    Events also receive a ``_ConnectionRecord``, a long-lived internal 
    66    ``Pool`` object that basically represents a "slot" in the 
    67    connection pool.  ``_ConnectionRecord`` objects have one public 
    68    attribute of note: ``info``, a dictionary whose contents are 
    69    scoped to the lifetime of the DB-API connection managed by the 
    70    record.  You can use this shared storage area however you like. 
    71 
    72    There is no need to subclass ``PoolListener`` to handle events. 
    73    Any class that implements one or more of these methods can be used 
    74    as a pool listener.  The ``Pool`` will inspect the methods 
    75    provided by a listener object and add the listener to one or more 
    76    internal event queues based on its capabilities.  In terms of 
    77    efficiency and function call overhead, you're much better off only 
    78    providing implementations for the hooks you'll be using. 
    79 
    80    """ 
    81 
    82    @classmethod 
    83    def _adapt_listener(cls, self, listener): 
    84        """Adapt a :class:`.PoolListener` to individual 
    85        :class:`event.Dispatch` events. 
    86 
    87        """ 
    88 
    89        methods = ["connect", "first_connect", "checkout", "checkin"] 
    90        listener = util.as_interface(listener, methods=methods) 
    91 
    92        for meth in methods: 
    93            me_meth = getattr(PoolListener, meth) 
    94            ls_meth = getattr(listener, meth, None) 
    95 
    96            if ls_meth is not None and not util.methods_equivalent( 
    97                me_meth, ls_meth 
    98            ): 
    99                util.warn_deprecated( 
    100                    "PoolListener.%s is deprecated.  The " 
    101                    "PoolListener class will be removed in a future " 
    102                    "release.  Please transition to the @event interface, " 
    103                    "using @event.listens_for(Engine, '%s')." % (meth, meth) 
    104                ) 
    105 
    106        if hasattr(listener, "connect"): 
    107            event.listen(self, "connect", listener.connect) 
    108        if hasattr(listener, "first_connect"): 
    109            event.listen(self, "first_connect", listener.first_connect) 
    110        if hasattr(listener, "checkout"): 
    111            event.listen(self, "checkout", listener.checkout) 
    112        if hasattr(listener, "checkin"): 
    113            event.listen(self, "checkin", listener.checkin) 
    114 
    115    def connect(self, dbapi_con, con_record): 
    116        """Called once for each new DB-API connection or Pool's ``creator()``. 
    117 
    118        dbapi_con 
    119          A newly connected raw DB-API connection (not a SQLAlchemy 
    120          ``Connection`` wrapper). 
    121 
    122        con_record 
    123          The ``_ConnectionRecord`` that persistently manages the connection 
    124 
    125        """ 
    126 
    127    def first_connect(self, dbapi_con, con_record): 
    128        """Called exactly once for the first DB-API connection. 
    129 
    130        dbapi_con 
    131          A newly connected raw DB-API connection (not a SQLAlchemy 
    132          ``Connection`` wrapper). 
    133 
    134        con_record 
    135          The ``_ConnectionRecord`` that persistently manages the connection 
    136 
    137        """ 
    138 
    139    def checkout(self, dbapi_con, con_record, con_proxy): 
    140        """Called when a connection is retrieved from the Pool. 
    141 
    142        dbapi_con 
    143          A raw DB-API connection 
    144 
    145        con_record 
    146          The ``_ConnectionRecord`` that persistently manages the connection 
    147 
    148        con_proxy 
    149          The ``_ConnectionFairy`` which manages the connection for the span of 
    150          the current checkout. 
    151 
    152        If you raise an ``exc.DisconnectionError``, the current 
    153        connection will be disposed and a fresh connection retrieved. 
    154        Processing of all checkout listeners will abort and restart 
    155        using the new connection. 
    156        """ 
    157 
    158    def checkin(self, dbapi_con, con_record): 
    159        """Called when a connection returns to the pool. 
    160 
    161        Note that the connection may be closed, and may be None if the 
    162        connection has been invalidated.  ``checkin`` will not be called 
    163        for detached connections.  (They do not return to the pool.) 
    164 
    165        dbapi_con 
    166          A raw DB-API connection 
    167 
    168        con_record 
    169          The ``_ConnectionRecord`` that persistently manages the connection 
    170 
    171        """ 
    172 
    173 
    174class ConnectionProxy(object): 
    175    """Allows interception of statement execution by Connections. 
    176 
    177    .. deprecated:: 0.7 
    178 
    179       :class:`.ConnectionProxy` is deprecated and will be removed in a future 
    180       release.  Please refer to :func:`.event.listen` in conjunction with 
    181       the :class:`_events.ConnectionEvents` listener interface. 
    182 
    183    Either or both of the ``execute()`` and ``cursor_execute()`` 
    184    may be implemented to intercept compiled statement and 
    185    cursor level executions, e.g.:: 
    186 
    187        class MyProxy(ConnectionProxy): 
    188            def execute(self, conn, execute, clauseelement, 
    189                        *multiparams, **params): 
    190                print "compiled statement:", clauseelement 
    191                return execute(clauseelement, *multiparams, **params) 
    192 
    193            def cursor_execute(self, execute, cursor, statement, 
    194                               parameters, context, executemany): 
    195                print "raw statement:", statement 
    196                return execute(cursor, statement, parameters, context) 
    197 
    198    The ``execute`` argument is a function that will fulfill the default 
    199    execution behavior for the operation.  The signature illustrated 
    200    in the example should be used. 
    201 
    202    The proxy is installed into an :class:`~sqlalchemy.engine.Engine` via 
    203    the ``proxy`` argument:: 
    204 
    205        e = create_engine('someurl://', proxy=MyProxy()) 
    206 
    207    """ 
    208 
    209    @classmethod 
    210    def _adapt_listener(cls, self, listener): 
    211 
    212        methods = [ 
    213            "execute", 
    214            "cursor_execute", 
    215            "begin", 
    216            "rollback", 
    217            "commit", 
    218            "savepoint", 
    219            "rollback_savepoint", 
    220            "release_savepoint", 
    221            "begin_twophase", 
    222            "prepare_twophase", 
    223            "rollback_twophase", 
    224            "commit_twophase", 
    225        ] 
    226        for meth in methods: 
    227            me_meth = getattr(ConnectionProxy, meth) 
    228            ls_meth = getattr(listener, meth) 
    229 
    230            if not util.methods_equivalent(me_meth, ls_meth): 
    231                util.warn_deprecated( 
    232                    "ConnectionProxy.%s is deprecated.  The " 
    233                    "ConnectionProxy class will be removed in a future " 
    234                    "release.  Please transition to the @event interface, " 
    235                    "using @event.listens_for(Engine, '%s')." % (meth, meth) 
    236                ) 
    237 
    238        def adapt_execute(conn, clauseelement, multiparams, params): 
    239            def execute_wrapper(clauseelement, *multiparams, **params): 
    240                return clauseelement, multiparams, params 
    241 
    242            return listener.execute( 
    243                conn, execute_wrapper, clauseelement, *multiparams, **params 
    244            ) 
    245 
    246        event.listen(self, "before_execute", adapt_execute) 
    247 
    248        def adapt_cursor_execute( 
    249            conn, cursor, statement, parameters, context, executemany 
    250        ): 
    251            def execute_wrapper(cursor, statement, parameters, context): 
    252                return statement, parameters 
    253 
    254            return listener.cursor_execute( 
    255                execute_wrapper, 
    256                cursor, 
    257                statement, 
    258                parameters, 
    259                context, 
    260                executemany, 
    261            ) 
    262 
    263        event.listen(self, "before_cursor_execute", adapt_cursor_execute) 
    264 
    265        def do_nothing_callback(*arg, **kw): 
    266            pass 
    267 
    268        def adapt_listener(fn): 
    269            def go(conn, *arg, **kw): 
    270                fn(conn, do_nothing_callback, *arg, **kw) 
    271 
    272            return util.update_wrapper(go, fn) 
    273 
    274        event.listen(self, "begin", adapt_listener(listener.begin)) 
    275        event.listen(self, "rollback", adapt_listener(listener.rollback)) 
    276        event.listen(self, "commit", adapt_listener(listener.commit)) 
    277        event.listen(self, "savepoint", adapt_listener(listener.savepoint)) 
    278        event.listen( 
    279            self, 
    280            "rollback_savepoint", 
    281            adapt_listener(listener.rollback_savepoint), 
    282        ) 
    283        event.listen( 
    284            self, 
    285            "release_savepoint", 
    286            adapt_listener(listener.release_savepoint), 
    287        ) 
    288        event.listen( 
    289            self, "begin_twophase", adapt_listener(listener.begin_twophase) 
    290        ) 
    291        event.listen( 
    292            self, "prepare_twophase", adapt_listener(listener.prepare_twophase) 
    293        ) 
    294        event.listen( 
    295            self, 
    296            "rollback_twophase", 
    297            adapt_listener(listener.rollback_twophase), 
    298        ) 
    299        event.listen( 
    300            self, "commit_twophase", adapt_listener(listener.commit_twophase) 
    301        ) 
    302 
    303    def execute(self, conn, execute, clauseelement, *multiparams, **params): 
    304        """Intercept high level execute() events.""" 
    305 
    306        return execute(clauseelement, *multiparams, **params) 
    307 
    308    def cursor_execute( 
    309        self, execute, cursor, statement, parameters, context, executemany 
    310    ): 
    311        """Intercept low-level cursor execute() events.""" 
    312 
    313        return execute(cursor, statement, parameters, context) 
    314 
    315    def begin(self, conn, begin): 
    316        """Intercept begin() events.""" 
    317 
    318        return begin() 
    319 
    320    def rollback(self, conn, rollback): 
    321        """Intercept rollback() events.""" 
    322 
    323        return rollback() 
    324 
    325    def commit(self, conn, commit): 
    326        """Intercept commit() events.""" 
    327 
    328        return commit() 
    329 
    330    def savepoint(self, conn, savepoint, name=None): 
    331        """Intercept savepoint() events.""" 
    332 
    333        return savepoint(name=name) 
    334 
    335    def rollback_savepoint(self, conn, rollback_savepoint, name, context): 
    336        """Intercept rollback_savepoint() events.""" 
    337 
    338        return rollback_savepoint(name, context) 
    339 
    340    def release_savepoint(self, conn, release_savepoint, name, context): 
    341        """Intercept release_savepoint() events.""" 
    342 
    343        return release_savepoint(name, context) 
    344 
    345    def begin_twophase(self, conn, begin_twophase, xid): 
    346        """Intercept begin_twophase() events.""" 
    347 
    348        return begin_twophase(xid) 
    349 
    350    def prepare_twophase(self, conn, prepare_twophase, xid): 
    351        """Intercept prepare_twophase() events.""" 
    352 
    353        return prepare_twophase(xid) 
    354 
    355    def rollback_twophase(self, conn, rollback_twophase, xid, is_prepared): 
    356        """Intercept rollback_twophase() events.""" 
    357 
    358        return rollback_twophase(xid, is_prepared) 
    359 
    360    def commit_twophase(self, conn, commit_twophase, xid, is_prepared): 
    361        """Intercept commit_twophase() events.""" 
    362 
    363        return commit_twophase(xid, is_prepared)