1# orm/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 
    7 
    8"""ORM event interfaces.""" 
    9from __future__ import annotations 
    10 
    11from typing import Any 
    12from typing import Callable 
    13from typing import Collection 
    14from typing import Dict 
    15from typing import Generic 
    16from typing import Iterable 
    17from typing import Optional 
    18from typing import Sequence 
    19from typing import Set 
    20from typing import Type 
    21from typing import TYPE_CHECKING 
    22from typing import TypeVar 
    23from typing import Union 
    24import weakref 
    25 
    26from . import decl_api 
    27from . import instrumentation 
    28from . import interfaces 
    29from . import mapperlib 
    30from .attributes import QueryableAttribute 
    31from .base import _mapper_or_none 
    32from .base import NO_KEY 
    33from .instrumentation import ClassManager 
    34from .instrumentation import InstrumentationFactory 
    35from .query import BulkDelete 
    36from .query import BulkUpdate 
    37from .query import Query 
    38from .scoping import scoped_session 
    39from .session import Session 
    40from .session import sessionmaker 
    41from .. import event 
    42from .. import exc 
    43from .. import util 
    44from ..event import EventTarget 
    45from ..event.registry import _ET 
    46from ..util.compat import inspect_getfullargspec 
    47 
    48if TYPE_CHECKING: 
    49    from weakref import ReferenceType 
    50 
    51    from ._typing import _InstanceDict 
    52    from ._typing import _InternalEntityType 
    53    from ._typing import _O 
    54    from ._typing import _T 
    55    from .attributes import Event 
    56    from .base import EventConstants 
    57    from .session import ORMExecuteState 
    58    from .session import SessionTransaction 
    59    from .unitofwork import UOWTransaction 
    60    from ..engine import Connection 
    61    from ..event.base import _Dispatch 
    62    from ..event.base import _HasEventsDispatch 
    63    from ..event.registry import _EventKey 
    64    from ..orm.collections import CollectionAdapter 
    65    from ..orm.context import QueryContext 
    66    from ..orm.decl_api import DeclarativeAttributeIntercept 
    67    from ..orm.decl_api import DeclarativeMeta 
    68    from ..orm.decl_api import registry 
    69    from ..orm.mapper import Mapper 
    70    from ..orm.state import InstanceState 
    71 
    72_KT = TypeVar("_KT", bound=Any) 
    73_ET2 = TypeVar("_ET2", bound=EventTarget) 
    74 
    75 
    76class InstrumentationEvents(event.Events[InstrumentationFactory]): 
    77    """Events related to class instrumentation events. 
    78 
    79    The listeners here support being established against 
    80    any new style class, that is any object that is a subclass 
    81    of 'type'.  Events will then be fired off for events 
    82    against that class.  If the "propagate=True" flag is passed 
    83    to event.listen(), the event will fire off for subclasses 
    84    of that class as well. 
    85 
    86    The Python ``type`` builtin is also accepted as a target, 
    87    which when used has the effect of events being emitted 
    88    for all classes. 
    89 
    90    Note the "propagate" flag here is defaulted to ``True``, 
    91    unlike the other class level events where it defaults 
    92    to ``False``.  This means that new subclasses will also 
    93    be the subject of these events, when a listener 
    94    is established on a superclass. 
    95 
    96    """ 
    97 
    98    _target_class_doc = "SomeBaseClass" 
    99    _dispatch_target = InstrumentationFactory 
    100 
    101    @classmethod 
    102    def _accept_with( 
    103        cls, 
    104        target: Union[ 
    105            InstrumentationFactory, 
    106            Type[InstrumentationFactory], 
    107        ], 
    108        identifier: str, 
    109    ) -> Optional[ 
    110        Union[ 
    111            InstrumentationFactory, 
    112            Type[InstrumentationFactory], 
    113        ] 
    114    ]: 
    115        if isinstance(target, type): 
    116            return _InstrumentationEventsHold(target)  # type: ignore [return-value] # noqa: E501 
    117        else: 
    118            return None 
    119 
    120    @classmethod 
    121    def _listen( 
    122        cls, event_key: _EventKey[_T], propagate: bool = True, **kw: Any 
    123    ) -> None: 
    124        target, identifier, fn = ( 
    125            event_key.dispatch_target, 
    126            event_key.identifier, 
    127            event_key._listen_fn, 
    128        ) 
    129 
    130        def listen(target_cls: type, *arg: Any) -> Optional[Any]: 
    131            listen_cls = target() 
    132 
    133            # if weakref were collected, however this is not something 
    134            # that normally happens.   it was occurring during test teardown 
    135            # between mapper/registry/instrumentation_manager, however this 
    136            # interaction was changed to not rely upon the event system. 
    137            if listen_cls is None: 
    138                return None 
    139 
    140            if propagate and issubclass(target_cls, listen_cls): 
    141                return fn(target_cls, *arg) 
    142            elif not propagate and target_cls is listen_cls: 
    143                return fn(target_cls, *arg) 
    144            else: 
    145                return None 
    146 
    147        def remove(ref: ReferenceType[_T]) -> None: 
    148            key = event.registry._EventKey(  # type: ignore [type-var] 
    149                None, 
    150                identifier, 
    151                listen, 
    152                instrumentation._instrumentation_factory, 
    153            ) 
    154            getattr( 
    155                instrumentation._instrumentation_factory.dispatch, identifier 
    156            ).remove(key) 
    157 
    158        target = weakref.ref(target.class_, remove) 
    159 
    160        event_key.with_dispatch_target( 
    161            instrumentation._instrumentation_factory 
    162        ).with_wrapper(listen).base_listen(**kw) 
    163 
    164    @classmethod 
    165    def _clear(cls) -> None: 
    166        super()._clear() 
    167        instrumentation._instrumentation_factory.dispatch._clear() 
    168 
    169    def class_instrument(self, cls: ClassManager[_O]) -> None: 
    170        """Called after the given class is instrumented. 
    171 
    172        To get at the :class:`.ClassManager`, use 
    173        :func:`.manager_of_class`. 
    174 
    175        """ 
    176 
    177    def class_uninstrument(self, cls: ClassManager[_O]) -> None: 
    178        """Called before the given class is uninstrumented. 
    179 
    180        To get at the :class:`.ClassManager`, use 
    181        :func:`.manager_of_class`. 
    182 
    183        """ 
    184 
    185    def attribute_instrument( 
    186        self, cls: ClassManager[_O], key: _KT, inst: _O 
    187    ) -> None: 
    188        """Called when an attribute is instrumented.""" 
    189 
    190 
    191class _InstrumentationEventsHold: 
    192    """temporary marker object used to transfer from _accept_with() to 
    193    _listen() on the InstrumentationEvents class. 
    194 
    195    """ 
    196 
    197    def __init__(self, class_: type) -> None: 
    198        self.class_ = class_ 
    199 
    200    dispatch = event.dispatcher(InstrumentationEvents) 
    201 
    202 
    203class InstanceEvents(event.Events[ClassManager[Any]]): 
    204    """Define events specific to object lifecycle. 
    205 
    206    e.g.:: 
    207 
    208        from sqlalchemy import event 
    209 
    210 
    211        def my_load_listener(target, context): 
    212            print("on load!") 
    213 
    214 
    215        event.listen(SomeClass, "load", my_load_listener) 
    216 
    217    Available targets include: 
    218 
    219    * mapped classes 
    220    * unmapped superclasses of mapped or to-be-mapped classes 
    221      (using the ``propagate=True`` flag) 
    222    * :class:`_orm.Mapper` objects 
    223    * the :class:`_orm.Mapper` class itself indicates listening for all 
    224      mappers. 
    225 
    226    Instance events are closely related to mapper events, but 
    227    are more specific to the instance and its instrumentation, 
    228    rather than its system of persistence. 
    229 
    230    When using :class:`.InstanceEvents`, several modifiers are 
    231    available to the :func:`.event.listen` function. 
    232 
    233    :param propagate=False: When True, the event listener should 
    234       be applied to all inheriting classes as well as the 
    235       class which is the target of this listener. 
    236    :param raw=False: When True, the "target" argument passed 
    237       to applicable event listener functions will be the 
    238       instance's :class:`.InstanceState` management 
    239       object, rather than the mapped instance itself. 
    240    :param restore_load_context=False: Applies to the 
    241       :meth:`.InstanceEvents.load` and :meth:`.InstanceEvents.refresh` 
    242       events.  Restores the loader context of the object when the event 
    243       hook is complete, so that ongoing eager load operations continue 
    244       to target the object appropriately.  A warning is emitted if the 
    245       object is moved to a new loader context from within one of these 
    246       events if this flag is not set. 
    247 
    248    """ 
    249 
    250    _target_class_doc = "SomeClass" 
    251 
    252    _dispatch_target = ClassManager 
    253 
    254    @classmethod 
    255    def _new_classmanager_instance( 
    256        cls, 
    257        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type], 
    258        classmanager: ClassManager[_O], 
    259    ) -> None: 
    260        _InstanceEventsHold.populate(class_, classmanager) 
    261 
    262    @classmethod 
    263    @util.preload_module("sqlalchemy.orm") 
    264    def _accept_with( 
    265        cls, 
    266        target: Union[ 
    267            ClassManager[Any], 
    268            Type[ClassManager[Any]], 
    269        ], 
    270        identifier: str, 
    271    ) -> Optional[Union[ClassManager[Any], Type[ClassManager[Any]]]]: 
    272        orm = util.preloaded.orm 
    273 
    274        if isinstance(target, ClassManager): 
    275            return target 
    276        elif isinstance(target, mapperlib.Mapper): 
    277            return target.class_manager 
    278        elif target is orm.mapper:  # type: ignore [attr-defined] 
    279            util.warn_deprecated( 
    280                "The `sqlalchemy.orm.mapper()` symbol is deprecated and " 
    281                "will be removed in a future release. For the mapper-wide " 
    282                "event target, use the 'sqlalchemy.orm.Mapper' class.", 
    283                "2.0", 
    284            ) 
    285            return ClassManager 
    286        elif isinstance(target, type): 
    287            if issubclass(target, mapperlib.Mapper): 
    288                return ClassManager 
    289            else: 
    290                manager = instrumentation.opt_manager_of_class(target) 
    291                if manager: 
    292                    return manager 
    293                else: 
    294                    return _InstanceEventsHold(target)  # type: ignore [return-value] # noqa: E501 
    295        return None 
    296 
    297    @classmethod 
    298    def _listen( 
    299        cls, 
    300        event_key: _EventKey[ClassManager[Any]], 
    301        raw: bool = False, 
    302        propagate: bool = False, 
    303        restore_load_context: bool = False, 
    304        **kw: Any, 
    305    ) -> None: 
    306        target, fn = (event_key.dispatch_target, event_key._listen_fn) 
    307 
    308        if not raw or restore_load_context: 
    309 
    310            def wrap( 
    311                state: InstanceState[_O], *arg: Any, **kw: Any 
    312            ) -> Optional[Any]: 
    313                if not raw: 
    314                    target: Any = state.obj() 
    315                else: 
    316                    target = state 
    317                if restore_load_context: 
    318                    runid = state.runid 
    319                try: 
    320                    return fn(target, *arg, **kw) 
    321                finally: 
    322                    if restore_load_context: 
    323                        state.runid = runid 
    324 
    325            event_key = event_key.with_wrapper(wrap) 
    326 
    327        event_key.base_listen(propagate=propagate, **kw) 
    328 
    329        if propagate: 
    330            for mgr in target.subclass_managers(True): 
    331                event_key.with_dispatch_target(mgr).base_listen(propagate=True) 
    332 
    333    @classmethod 
    334    def _clear(cls) -> None: 
    335        super()._clear() 
    336        _InstanceEventsHold._clear() 
    337 
    338    def init(self, target: _O, args: Any, kwargs: Any) -> None: 
    339        """Receive an instance when its constructor is called. 
    340 
    341        This method is only called during a userland construction of 
    342        an object, in conjunction with the object's constructor, e.g. 
    343        its ``__init__`` method.  It is not called when an object is 
    344        loaded from the database; see the :meth:`.InstanceEvents.load` 
    345        event in order to intercept a database load. 
    346 
    347        The event is called before the actual ``__init__`` constructor 
    348        of the object is called.  The ``kwargs`` dictionary may be 
    349        modified in-place in order to affect what is passed to 
    350        ``__init__``. 
    351 
    352        :param target: the mapped instance.  If 
    353         the event is configured with ``raw=True``, this will 
    354         instead be the :class:`.InstanceState` state-management 
    355         object associated with the instance. 
    356        :param args: positional arguments passed to the ``__init__`` method. 
    357         This is passed as a tuple and is currently immutable. 
    358        :param kwargs: keyword arguments passed to the ``__init__`` method. 
    359         This structure *can* be altered in place. 
    360 
    361        .. seealso:: 
    362 
    363            :meth:`.InstanceEvents.init_failure` 
    364 
    365            :meth:`.InstanceEvents.load` 
    366 
    367        """ 
    368 
    369    def init_failure(self, target: _O, args: Any, kwargs: Any) -> None: 
    370        """Receive an instance when its constructor has been called, 
    371        and raised an exception. 
    372 
    373        This method is only called during a userland construction of 
    374        an object, in conjunction with the object's constructor, e.g. 
    375        its ``__init__`` method. It is not called when an object is loaded 
    376        from the database. 
    377 
    378        The event is invoked after an exception raised by the ``__init__`` 
    379        method is caught.  After the event 
    380        is invoked, the original exception is re-raised outwards, so that 
    381        the construction of the object still raises an exception.   The 
    382        actual exception and stack trace raised should be present in 
    383        ``sys.exc_info()``. 
    384 
    385        :param target: the mapped instance.  If 
    386         the event is configured with ``raw=True``, this will 
    387         instead be the :class:`.InstanceState` state-management 
    388         object associated with the instance. 
    389        :param args: positional arguments that were passed to the ``__init__`` 
    390         method. 
    391        :param kwargs: keyword arguments that were passed to the ``__init__`` 
    392         method. 
    393 
    394        .. seealso:: 
    395 
    396            :meth:`.InstanceEvents.init` 
    397 
    398            :meth:`.InstanceEvents.load` 
    399 
    400        """ 
    401 
    402    def _sa_event_merge_wo_load( 
    403        self, target: _O, context: QueryContext 
    404    ) -> None: 
    405        """receive an object instance after it was the subject of a merge() 
    406        call, when load=False was passed. 
    407 
    408        The target would be the already-loaded object in the Session which 
    409        would have had its attributes overwritten by the incoming object. This 
    410        overwrite operation does not use attribute events, instead just 
    411        populating dict directly. Therefore the purpose of this event is so 
    412        that extensions like sqlalchemy.ext.mutable know that object state has 
    413        changed and incoming state needs to be set up for "parents" etc. 
    414 
    415        This functionality is acceptable to be made public in a later release. 
    416 
    417        .. versionadded:: 1.4.41 
    418 
    419        """ 
    420 
    421    def load(self, target: _O, context: QueryContext) -> None: 
    422        """Receive an object instance after it has been created via 
    423        ``__new__``, and after initial attribute population has 
    424        occurred. 
    425 
    426        This typically occurs when the instance is created based on 
    427        incoming result rows, and is only called once for that 
    428        instance's lifetime. 
    429 
    430        .. warning:: 
    431 
    432            During a result-row load, this event is invoked when the 
    433            first row received for this instance is processed.  When using 
    434            eager loading with collection-oriented attributes, the additional 
    435            rows that are to be loaded / processed in order to load subsequent 
    436            collection items have not occurred yet.   This has the effect 
    437            both that collections will not be fully loaded, as well as that 
    438            if an operation occurs within this event handler that emits 
    439            another database load operation for the object, the "loading 
    440            context" for the object can change and interfere with the 
    441            existing eager loaders still in progress. 
    442 
    443            Examples of what can cause the "loading context" to change within 
    444            the event handler include, but are not necessarily limited to: 
    445 
    446            * accessing deferred attributes that weren't part of the row, 
    447              will trigger an "undefer" operation and refresh the object 
    448 
    449            * accessing attributes on a joined-inheritance subclass that 
    450              weren't part of the row, will trigger a refresh operation. 
    451 
    452            As of SQLAlchemy 1.3.14, a warning is emitted when this occurs. The 
    453            :paramref:`.InstanceEvents.restore_load_context` option may  be 
    454            used on the event to prevent this warning; this will ensure that 
    455            the existing loading context is maintained for the object after the 
    456            event is called:: 
    457 
    458                @event.listens_for(SomeClass, "load", restore_load_context=True) 
    459                def on_load(instance, context): 
    460                    instance.some_unloaded_attribute 
    461 
    462        The :meth:`.InstanceEvents.load` event is also available in a 
    463        class-method decorator format called :func:`_orm.reconstructor`. 
    464 
    465        :param target: the mapped instance.  If 
    466         the event is configured with ``raw=True``, this will 
    467         instead be the :class:`.InstanceState` state-management 
    468         object associated with the instance. 
    469        :param context: the :class:`.QueryContext` corresponding to the 
    470         current :class:`_query.Query` in progress.  This argument may be 
    471         ``None`` if the load does not correspond to a :class:`_query.Query`, 
    472         such as during :meth:`.Session.merge`. 
    473 
    474        .. seealso:: 
    475 
    476            :ref:`mapped_class_load_events` 
    477 
    478            :meth:`.InstanceEvents.init` 
    479 
    480            :meth:`.InstanceEvents.refresh` 
    481 
    482            :meth:`.SessionEvents.loaded_as_persistent` 
    483 
    484        """  # noqa: E501 
    485 
    486    def refresh( 
    487        self, target: _O, context: QueryContext, attrs: Optional[Iterable[str]] 
    488    ) -> None: 
    489        """Receive an object instance after one or more attributes have 
    490        been refreshed from a query. 
    491 
    492        Contrast this to the :meth:`.InstanceEvents.load` method, which 
    493        is invoked when the object is first loaded from a query. 
    494 
    495        .. note:: This event is invoked within the loader process before 
    496           eager loaders may have been completed, and the object's state may 
    497           not be complete.  Additionally, invoking row-level refresh 
    498           operations on the object will place the object into a new loader 
    499           context, interfering with the existing load context.   See the note 
    500           on :meth:`.InstanceEvents.load` for background on making use of the 
    501           :paramref:`.InstanceEvents.restore_load_context` parameter, in 
    502           order to resolve this scenario. 
    503 
    504        :param target: the mapped instance.  If 
    505         the event is configured with ``raw=True``, this will 
    506         instead be the :class:`.InstanceState` state-management 
    507         object associated with the instance. 
    508        :param context: the :class:`.QueryContext` corresponding to the 
    509         current :class:`_query.Query` in progress. 
    510        :param attrs: sequence of attribute names which 
    511         were populated, or None if all column-mapped, non-deferred 
    512         attributes were populated. 
    513 
    514        .. seealso:: 
    515 
    516            :ref:`mapped_class_load_events` 
    517 
    518            :meth:`.InstanceEvents.load` 
    519 
    520        """ 
    521 
    522    def refresh_flush( 
    523        self, 
    524        target: _O, 
    525        flush_context: UOWTransaction, 
    526        attrs: Optional[Iterable[str]], 
    527    ) -> None: 
    528        """Receive an object instance after one or more attributes that 
    529        contain a column-level default or onupdate handler have been refreshed 
    530        during persistence of the object's state. 
    531 
    532        This event is the same as :meth:`.InstanceEvents.refresh` except 
    533        it is invoked within the unit of work flush process, and includes 
    534        only non-primary-key columns that have column level default or 
    535        onupdate handlers, including Python callables as well as server side 
    536        defaults and triggers which may be fetched via the RETURNING clause. 
    537 
    538        .. note:: 
    539 
    540            While the :meth:`.InstanceEvents.refresh_flush` event is triggered 
    541            for an object that was INSERTed as well as for an object that was 
    542            UPDATEd, the event is geared primarily  towards the UPDATE process; 
    543            it is mostly an internal artifact that INSERT actions can also 
    544            trigger this event, and note that **primary key columns for an 
    545            INSERTed row are explicitly omitted** from this event.  In order to 
    546            intercept the newly INSERTed state of an object, the 
    547            :meth:`.SessionEvents.pending_to_persistent` and 
    548            :meth:`.MapperEvents.after_insert` are better choices. 
    549 
    550        :param target: the mapped instance.  If 
    551         the event is configured with ``raw=True``, this will 
    552         instead be the :class:`.InstanceState` state-management 
    553         object associated with the instance. 
    554        :param flush_context: Internal :class:`.UOWTransaction` object 
    555         which handles the details of the flush. 
    556        :param attrs: sequence of attribute names which 
    557         were populated. 
    558 
    559        .. seealso:: 
    560 
    561            :ref:`mapped_class_load_events` 
    562 
    563            :ref:`orm_server_defaults` 
    564 
    565            :ref:`metadata_defaults_toplevel` 
    566 
    567        """ 
    568 
    569    def expire(self, target: _O, attrs: Optional[Iterable[str]]) -> None: 
    570        """Receive an object instance after its attributes or some subset 
    571        have been expired. 
    572 
    573        'keys' is a list of attribute names.  If None, the entire 
    574        state was expired. 
    575 
    576        :param target: the mapped instance.  If 
    577         the event is configured with ``raw=True``, this will 
    578         instead be the :class:`.InstanceState` state-management 
    579         object associated with the instance. 
    580        :param attrs: sequence of attribute 
    581         names which were expired, or None if all attributes were 
    582         expired. 
    583 
    584        """ 
    585 
    586    def pickle(self, target: _O, state_dict: _InstanceDict) -> None: 
    587        """Receive an object instance when its associated state is 
    588        being pickled. 
    589 
    590        :param target: the mapped instance.  If 
    591         the event is configured with ``raw=True``, this will 
    592         instead be the :class:`.InstanceState` state-management 
    593         object associated with the instance. 
    594        :param state_dict: the dictionary returned by 
    595         :class:`.InstanceState.__getstate__`, containing the state 
    596         to be pickled. 
    597 
    598        """ 
    599 
    600    def unpickle(self, target: _O, state_dict: _InstanceDict) -> None: 
    601        """Receive an object instance after its associated state has 
    602        been unpickled. 
    603 
    604        :param target: the mapped instance.  If 
    605         the event is configured with ``raw=True``, this will 
    606         instead be the :class:`.InstanceState` state-management 
    607         object associated with the instance. 
    608        :param state_dict: the dictionary sent to 
    609         :class:`.InstanceState.__setstate__`, containing the state 
    610         dictionary which was pickled. 
    611 
    612        """ 
    613 
    614 
    615class _EventsHold(event.RefCollection[_ET]): 
    616    """Hold onto listeners against unmapped, uninstrumented classes. 
    617 
    618    Establish _listen() for that class' mapper/instrumentation when 
    619    those objects are created for that class. 
    620 
    621    """ 
    622 
    623    all_holds: weakref.WeakKeyDictionary[Any, Any] 
    624 
    625    def __init__( 
    626        self, 
    627        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type], 
    628    ) -> None: 
    629        self.class_ = class_ 
    630 
    631    @classmethod 
    632    def _clear(cls) -> None: 
    633        cls.all_holds.clear() 
    634 
    635    class HoldEvents(Generic[_ET2]): 
    636        _dispatch_target: Optional[Type[_ET2]] = None 
    637 
    638        @classmethod 
    639        def _listen( 
    640            cls, 
    641            event_key: _EventKey[_ET2], 
    642            raw: bool = False, 
    643            propagate: bool = False, 
    644            retval: bool = False, 
    645            **kw: Any, 
    646        ) -> None: 
    647            target = event_key.dispatch_target 
    648 
    649            if target.class_ in target.all_holds: 
    650                collection = target.all_holds[target.class_] 
    651            else: 
    652                collection = target.all_holds[target.class_] = {} 
    653 
    654            event.registry._stored_in_collection(event_key, target) 
    655            collection[event_key._key] = ( 
    656                event_key, 
    657                raw, 
    658                propagate, 
    659                retval, 
    660                kw, 
    661            ) 
    662 
    663            if propagate: 
    664                stack = list(target.class_.__subclasses__()) 
    665                while stack: 
    666                    subclass = stack.pop(0) 
    667                    stack.extend(subclass.__subclasses__()) 
    668                    subject = target.resolve(subclass) 
    669                    if subject is not None: 
    670                        # we are already going through __subclasses__() 
    671                        # so leave generic propagate flag False 
    672                        event_key.with_dispatch_target(subject).listen( 
    673                            raw=raw, propagate=False, retval=retval, **kw 
    674                        ) 
    675 
    676    def remove(self, event_key: _EventKey[_ET]) -> None: 
    677        target = event_key.dispatch_target 
    678 
    679        if isinstance(target, _EventsHold): 
    680            collection = target.all_holds[target.class_] 
    681            del collection[event_key._key] 
    682 
    683    @classmethod 
    684    def populate( 
    685        cls, 
    686        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type], 
    687        subject: Union[ClassManager[_O], Mapper[_O]], 
    688    ) -> None: 
    689        for subclass in class_.__mro__: 
    690            if subclass in cls.all_holds: 
    691                collection = cls.all_holds[subclass] 
    692                for ( 
    693                    event_key, 
    694                    raw, 
    695                    propagate, 
    696                    retval, 
    697                    kw, 
    698                ) in collection.values(): 
    699                    if propagate or subclass is class_: 
    700                        # since we can't be sure in what order different 
    701                        # classes in a hierarchy are triggered with 
    702                        # populate(), we rely upon _EventsHold for all event 
    703                        # assignment, instead of using the generic propagate 
    704                        # flag. 
    705                        event_key.with_dispatch_target(subject).listen( 
    706                            raw=raw, propagate=False, retval=retval, **kw 
    707                        ) 
    708 
    709 
    710class _InstanceEventsHold(_EventsHold[_ET]): 
    711    all_holds: weakref.WeakKeyDictionary[Any, Any] = ( 
    712        weakref.WeakKeyDictionary() 
    713    ) 
    714 
    715    def resolve(self, class_: Type[_O]) -> Optional[ClassManager[_O]]: 
    716        return instrumentation.opt_manager_of_class(class_) 
    717 
    718    # this fails on pyright if you use Any.  Fails on mypy if you use _ET 
    719    class HoldInstanceEvents(_EventsHold.HoldEvents[_ET], InstanceEvents):  # type: ignore[valid-type,misc] # noqa: E501 
    720        pass 
    721 
    722    dispatch = event.dispatcher(HoldInstanceEvents) 
    723 
    724 
    725class MapperEvents(event.Events[mapperlib.Mapper[Any]]): 
    726    """Define events specific to mappings. 
    727 
    728    e.g.:: 
    729 
    730        from sqlalchemy import event 
    731 
    732 
    733        def my_before_insert_listener(mapper, connection, target): 
    734            # execute a stored procedure upon INSERT, 
    735            # apply the value to the row to be inserted 
    736            target.calculated_value = connection.execute( 
    737                text("select my_special_function(%d)" % target.special_number) 
    738            ).scalar() 
    739 
    740 
    741        # associate the listener function with SomeClass, 
    742        # to execute during the "before_insert" hook 
    743        event.listen(SomeClass, "before_insert", my_before_insert_listener) 
    744 
    745    Available targets include: 
    746 
    747    * mapped classes 
    748    * unmapped superclasses of mapped or to-be-mapped classes 
    749      (using the ``propagate=True`` flag) 
    750    * :class:`_orm.Mapper` objects 
    751    * the :class:`_orm.Mapper` class itself indicates listening for all 
    752      mappers. 
    753 
    754    Mapper events provide hooks into critical sections of the 
    755    mapper, including those related to object instrumentation, 
    756    object loading, and object persistence. In particular, the 
    757    persistence methods :meth:`~.MapperEvents.before_insert`, 
    758    and :meth:`~.MapperEvents.before_update` are popular 
    759    places to augment the state being persisted - however, these 
    760    methods operate with several significant restrictions. The 
    761    user is encouraged to evaluate the 
    762    :meth:`.SessionEvents.before_flush` and 
    763    :meth:`.SessionEvents.after_flush` methods as more 
    764    flexible and user-friendly hooks in which to apply 
    765    additional database state during a flush. 
    766 
    767    When using :class:`.MapperEvents`, several modifiers are 
    768    available to the :func:`.event.listen` function. 
    769 
    770    :param propagate=False: When True, the event listener should 
    771       be applied to all inheriting mappers and/or the mappers of 
    772       inheriting classes, as well as any 
    773       mapper which is the target of this listener. 
    774    :param raw=False: When True, the "target" argument passed 
    775       to applicable event listener functions will be the 
    776       instance's :class:`.InstanceState` management 
    777       object, rather than the mapped instance itself. 
    778    :param retval=False: when True, the user-defined event function 
    779       must have a return value, the purpose of which is either to 
    780       control subsequent event propagation, or to otherwise alter 
    781       the operation in progress by the mapper.   Possible return 
    782       values are: 
    783 
    784       * ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event 
    785         processing normally. 
    786       * ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent 
    787         event handlers in the chain. 
    788       * other values - the return value specified by specific listeners. 
    789 
    790    """ 
    791 
    792    _target_class_doc = "SomeClass" 
    793    _dispatch_target = mapperlib.Mapper 
    794 
    795    @classmethod 
    796    def _new_mapper_instance( 
    797        cls, 
    798        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type], 
    799        mapper: Mapper[_O], 
    800    ) -> None: 
    801        _MapperEventsHold.populate(class_, mapper) 
    802 
    803    @classmethod 
    804    @util.preload_module("sqlalchemy.orm") 
    805    def _accept_with( 
    806        cls, 
    807        target: Union[mapperlib.Mapper[Any], Type[mapperlib.Mapper[Any]]], 
    808        identifier: str, 
    809    ) -> Optional[Union[mapperlib.Mapper[Any], Type[mapperlib.Mapper[Any]]]]: 
    810        orm = util.preloaded.orm 
    811 
    812        if target is orm.mapper:  # type: ignore [attr-defined] 
    813            util.warn_deprecated( 
    814                "The `sqlalchemy.orm.mapper()` symbol is deprecated and " 
    815                "will be removed in a future release. For the mapper-wide " 
    816                "event target, use the 'sqlalchemy.orm.Mapper' class.", 
    817                "2.0", 
    818            ) 
    819            target = mapperlib.Mapper 
    820 
    821        if identifier in ("before_configured", "after_configured"): 
    822            if target is mapperlib.Mapper: 
    823                return target 
    824            else: 
    825                return None 
    826 
    827        elif isinstance(target, type): 
    828            if issubclass(target, mapperlib.Mapper): 
    829                return target 
    830            else: 
    831                mapper = _mapper_or_none(target) 
    832                if mapper is not None: 
    833                    return mapper 
    834                else: 
    835                    return _MapperEventsHold(target) 
    836        else: 
    837            return target 
    838 
    839    @classmethod 
    840    def _listen( 
    841        cls, 
    842        event_key: _EventKey[_ET], 
    843        raw: bool = False, 
    844        retval: bool = False, 
    845        propagate: bool = False, 
    846        **kw: Any, 
    847    ) -> None: 
    848        target, identifier, fn = ( 
    849            event_key.dispatch_target, 
    850            event_key.identifier, 
    851            event_key._listen_fn, 
    852        ) 
    853 
    854        if not raw or not retval: 
    855            if not raw: 
    856                meth = getattr(cls, identifier) 
    857                try: 
    858                    target_index = ( 
    859                        inspect_getfullargspec(meth)[0].index("target") - 1 
    860                    ) 
    861                except ValueError: 
    862                    target_index = None 
    863 
    864            def wrap(*arg: Any, **kw: Any) -> Any: 
    865                if not raw and target_index is not None: 
    866                    arg = list(arg)  # type: ignore [assignment] 
    867                    arg[target_index] = arg[target_index].obj()  # type: ignore [index] # noqa: E501 
    868                if not retval: 
    869                    fn(*arg, **kw) 
    870                    return interfaces.EXT_CONTINUE 
    871                else: 
    872                    return fn(*arg, **kw) 
    873 
    874            event_key = event_key.with_wrapper(wrap) 
    875 
    876        if propagate: 
    877            for mapper in target.self_and_descendants: 
    878                event_key.with_dispatch_target(mapper).base_listen( 
    879                    propagate=True, **kw 
    880                ) 
    881        else: 
    882            event_key.base_listen(**kw) 
    883 
    884    @classmethod 
    885    def _clear(cls) -> None: 
    886        super()._clear() 
    887        _MapperEventsHold._clear() 
    888 
    889    def instrument_class(self, mapper: Mapper[_O], class_: Type[_O]) -> None: 
    890        r"""Receive a class when the mapper is first constructed, 
    891        before instrumentation is applied to the mapped class. 
    892 
    893        This event is the earliest phase of mapper construction. 
    894        Most attributes of the mapper are not yet initialized.   To 
    895        receive an event within initial mapper construction where basic 
    896        state is available such as the :attr:`_orm.Mapper.attrs` collection, 
    897        the :meth:`_orm.MapperEvents.after_mapper_constructed` event may 
    898        be a better choice. 
    899 
    900        This listener can either be applied to the :class:`_orm.Mapper` 
    901        class overall, or to any un-mapped class which serves as a base 
    902        for classes that will be mapped (using the ``propagate=True`` flag):: 
    903 
    904            Base = declarative_base() 
    905 
    906 
    907            @event.listens_for(Base, "instrument_class", propagate=True) 
    908            def on_new_class(mapper, cls_): 
    909                "..." 
    910 
    911        :param mapper: the :class:`_orm.Mapper` which is the target 
    912         of this event. 
    913        :param class\_: the mapped class. 
    914 
    915        .. seealso:: 
    916 
    917            :meth:`_orm.MapperEvents.after_mapper_constructed` 
    918 
    919        """ 
    920 
    921    def after_mapper_constructed( 
    922        self, mapper: Mapper[_O], class_: Type[_O] 
    923    ) -> None: 
    924        """Receive a class and mapper when the :class:`_orm.Mapper` has been 
    925        fully constructed. 
    926 
    927        This event is called after the initial constructor for 
    928        :class:`_orm.Mapper` completes.  This occurs after the 
    929        :meth:`_orm.MapperEvents.instrument_class` event and after the 
    930        :class:`_orm.Mapper` has done an initial pass of its arguments 
    931        to generate its collection of :class:`_orm.MapperProperty` objects, 
    932        which are accessible via the :meth:`_orm.Mapper.get_property` 
    933        method and the :attr:`_orm.Mapper.iterate_properties` attribute. 
    934 
    935        This event differs from the 
    936        :meth:`_orm.MapperEvents.before_mapper_configured` event in that it 
    937        is invoked within the constructor for :class:`_orm.Mapper`, rather 
    938        than within the :meth:`_orm.registry.configure` process.   Currently, 
    939        this event is the only one which is appropriate for handlers that 
    940        wish to create additional mapped classes in response to the 
    941        construction of this :class:`_orm.Mapper`, which will be part of the 
    942        same configure step when :meth:`_orm.registry.configure` next runs. 
    943 
    944        .. versionadded:: 2.0.2 
    945 
    946        .. seealso:: 
    947 
    948            :ref:`examples_versioning` - an example which illustrates the use 
    949            of the :meth:`_orm.MapperEvents.before_mapper_configured` 
    950            event to create new mappers to record change-audit histories on 
    951            objects. 
    952 
    953        """ 
    954 
    955    @event._omit_standard_example 
    956    def before_mapper_configured( 
    957        self, mapper: Mapper[_O], class_: Type[_O] 
    958    ) -> None: 
    959        """Called right before a specific mapper is to be configured. 
    960 
    961        The :meth:`.MapperEvents.before_mapper_configured` event is invoked 
    962        for each mapper that is encountered when the 
    963        :func:`_orm.configure_mappers` function proceeds through the current 
    964        list of not-yet-configured mappers.   It is similar to the 
    965        :meth:`.MapperEvents.mapper_configured` event, except that it's invoked 
    966        right before the configuration occurs, rather than afterwards. 
    967 
    968        The :meth:`.MapperEvents.before_mapper_configured` event includes 
    969        the special capability where it can force the configure step for a 
    970        specific mapper to be skipped; to use this feature, establish 
    971        the event using the ``retval=True`` parameter and return 
    972        the :attr:`.orm.interfaces.EXT_SKIP` symbol to indicate the mapper 
    973        should be left unconfigured:: 
    974 
    975            from sqlalchemy import event 
    976            from sqlalchemy.orm import EXT_SKIP 
    977            from sqlalchemy.orm import DeclarativeBase 
    978 
    979 
    980            class DontConfigureBase(DeclarativeBase): 
    981                pass 
    982 
    983 
    984            @event.listens_for( 
    985                DontConfigureBase, 
    986                "before_mapper_configured", 
    987                # support return values for the event 
    988                retval=True, 
    989                # propagate the listener to all subclasses of 
    990                # DontConfigureBase 
    991                propagate=True, 
    992            ) 
    993            def dont_configure(mapper, cls): 
    994                return EXT_SKIP 
    995 
    996        .. seealso:: 
    997 
    998            :meth:`.MapperEvents.before_configured` 
    999 
    1000            :meth:`.MapperEvents.after_configured` 
    1001 
    1002            :meth:`.RegistryEvents.before_configured` 
    1003 
    1004            :meth:`.RegistryEvents.after_configured` 
    1005 
    1006            :meth:`.MapperEvents.mapper_configured` 
    1007 
    1008        """ 
    1009 
    1010    def mapper_configured(self, mapper: Mapper[_O], class_: Type[_O]) -> None: 
    1011        r"""Called when a specific mapper has completed its own configuration 
    1012        within the scope of the :func:`.configure_mappers` call. 
    1013 
    1014        The :meth:`.MapperEvents.mapper_configured` event is invoked 
    1015        for each mapper that is encountered when the 
    1016        :func:`_orm.configure_mappers` function proceeds through the current 
    1017        list of not-yet-configured mappers. 
    1018        :func:`_orm.configure_mappers` is typically invoked 
    1019        automatically as mappings are first used, as well as each time 
    1020        new mappers have been made available and new mapper use is 
    1021        detected. 
    1022 
    1023        When the event is called, the mapper should be in its final 
    1024        state, but **not including backrefs** that may be invoked from 
    1025        other mappers; they might still be pending within the 
    1026        configuration operation.    Bidirectional relationships that 
    1027        are instead configured via the 
    1028        :paramref:`.orm.relationship.back_populates` argument 
    1029        *will* be fully available, since this style of relationship does not 
    1030        rely upon other possibly-not-configured mappers to know that they 
    1031        exist. 
    1032 
    1033        For an event that is guaranteed to have **all** mappers ready 
    1034        to go including backrefs that are defined only on other 
    1035        mappings, use the :meth:`.MapperEvents.after_configured` 
    1036        event; this event invokes only after all known mappings have been 
    1037        fully configured. 
    1038 
    1039        The :meth:`.MapperEvents.mapper_configured` event, unlike the 
    1040        :meth:`.MapperEvents.before_configured` or 
    1041        :meth:`.MapperEvents.after_configured` events, is called for each 
    1042        mapper/class individually, and the mapper is passed to the event 
    1043        itself.  It also is called exactly once for a particular mapper.  The 
    1044        event is therefore useful for configurational steps that benefit from 
    1045        being invoked just once on a specific mapper basis, which don't require 
    1046        that "backref" configurations are necessarily ready yet. 
    1047 
    1048        :param mapper: the :class:`_orm.Mapper` which is the target 
    1049         of this event. 
    1050        :param class\_: the mapped class. 
    1051 
    1052        .. seealso:: 
    1053 
    1054            :meth:`.MapperEvents.before_configured` 
    1055 
    1056            :meth:`.MapperEvents.after_configured` 
    1057 
    1058            :meth:`.RegistryEvents.before_configured` 
    1059 
    1060            :meth:`.RegistryEvents.after_configured` 
    1061 
    1062            :meth:`.MapperEvents.before_mapper_configured` 
    1063 
    1064        """ 
    1065        # TODO: need coverage for this event 
    1066 
    1067    @event._omit_standard_example 
    1068    def before_configured(self) -> None: 
    1069        """Called before a series of mappers have been configured. 
    1070 
    1071        The :meth:`.MapperEvents.before_configured` event is invoked 
    1072        each time the :func:`_orm.configure_mappers` function is 
    1073        invoked, before the function has done any of its work. 
    1074        :func:`_orm.configure_mappers` is typically invoked 
    1075        automatically as mappings are first used, as well as each time 
    1076        new mappers have been made available and new mapper use is 
    1077        detected. 
    1078 
    1079        Similar events to this one include 
    1080        :meth:`.MapperEvents.after_configured`, which is invoked after a series 
    1081        of mappers has been configured, as well as 
    1082        :meth:`.MapperEvents.before_mapper_configured` and 
    1083        :meth:`.MapperEvents.mapper_configured`, which are both invoked on a 
    1084        per-mapper basis. 
    1085 
    1086        This event can **only** be applied to the :class:`_orm.Mapper` class, 
    1087        and not to individual mappings or mapped classes:: 
    1088 
    1089            from sqlalchemy.orm import Mapper 
    1090 
    1091 
    1092            @event.listens_for(Mapper, "before_configured") 
    1093            def go(): ... 
    1094 
    1095        Typically, this event is called once per application, but in practice 
    1096        may be called more than once, any time new mappers are to be affected 
    1097        by a :func:`_orm.configure_mappers` call.   If new mappings are 
    1098        constructed after existing ones have already been used, this event will 
    1099        likely be called again. 
    1100 
    1101        .. seealso:: 
    1102 
    1103            :meth:`.MapperEvents.before_mapper_configured` 
    1104 
    1105            :meth:`.MapperEvents.mapper_configured` 
    1106 
    1107            :meth:`.MapperEvents.after_configured` 
    1108 
    1109            :meth:`.RegistryEvents.before_configured` 
    1110 
    1111            :meth:`.RegistryEvents.after_configured` 
    1112 
    1113        """ 
    1114 
    1115    @event._omit_standard_example 
    1116    def after_configured(self) -> None: 
    1117        """Called after a series of mappers have been configured. 
    1118 
    1119        The :meth:`.MapperEvents.after_configured` event is invoked 
    1120        each time the :func:`_orm.configure_mappers` function is 
    1121        invoked, after the function has completed its work. 
    1122        :func:`_orm.configure_mappers` is typically invoked 
    1123        automatically as mappings are first used, as well as each time 
    1124        new mappers have been made available and new mapper use is 
    1125        detected. 
    1126 
    1127        Similar events to this one include 
    1128        :meth:`.MapperEvents.before_configured`, which is invoked before a 
    1129        series of mappers are configured, as well as 
    1130        :meth:`.MapperEvents.before_mapper_configured` and 
    1131        :meth:`.MapperEvents.mapper_configured`, which are both invoked on a 
    1132        per-mapper basis. 
    1133 
    1134        This event can **only** be applied to the :class:`_orm.Mapper` class, 
    1135        and not to individual mappings or mapped classes:: 
    1136 
    1137            from sqlalchemy.orm import Mapper 
    1138 
    1139 
    1140            @event.listens_for(Mapper, "after_configured") 
    1141            def go(): ... 
    1142 
    1143        Typically, this event is called once per application, but in practice 
    1144        may be called more than once, any time new mappers are to be affected 
    1145        by a :func:`_orm.configure_mappers` call.   If new mappings are 
    1146        constructed after existing ones have already been used, this event will 
    1147        likely be called again. 
    1148 
    1149        .. seealso:: 
    1150 
    1151            :meth:`.MapperEvents.before_mapper_configured` 
    1152 
    1153            :meth:`.MapperEvents.mapper_configured` 
    1154 
    1155            :meth:`.MapperEvents.before_configured` 
    1156 
    1157            :meth:`.RegistryEvents.before_configured` 
    1158 
    1159            :meth:`.RegistryEvents.after_configured` 
    1160 
    1161        """ 
    1162 
    1163    def before_insert( 
    1164        self, mapper: Mapper[_O], connection: Connection, target: _O 
    1165    ) -> None: 
    1166        """Receive an object instance before an INSERT statement 
    1167        is emitted corresponding to that instance. 
    1168 
    1169        .. note:: this event **only** applies to the 
    1170           :ref:`session flush operation <session_flushing>` 
    1171           and does **not** apply to the ORM DML operations described at 
    1172           :ref:`orm_expression_update_delete`.  To intercept ORM 
    1173           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`. 
    1174 
    1175        This event is used to modify local, non-object related 
    1176        attributes on the instance before an INSERT occurs, as well 
    1177        as to emit additional SQL statements on the given 
    1178        connection. 
    1179 
    1180        The event is often called for a batch of objects of the 
    1181        same class before their INSERT statements are emitted at 
    1182        once in a later step. In the extremely rare case that 
    1183        this is not desirable, the :class:`_orm.Mapper` object can be 
    1184        configured with ``batch=False``, which will cause 
    1185        batches of instances to be broken up into individual 
    1186        (and more poorly performing) event->persist->event 
    1187        steps. 
    1188 
    1189        .. warning:: 
    1190 
    1191            Mapper-level flush events only allow **very limited operations**, 
    1192            on attributes local to the row being operated upon only, 
    1193            as well as allowing any SQL to be emitted on the given 
    1194            :class:`_engine.Connection`.  **Please read fully** the notes 
    1195            at :ref:`session_persistence_mapper` for guidelines on using 
    1196            these methods; generally, the :meth:`.SessionEvents.before_flush` 
    1197            method should be preferred for general on-flush changes. 
    1198 
    1199        :param mapper: the :class:`_orm.Mapper` which is the target 
    1200         of this event. 
    1201        :param connection: the :class:`_engine.Connection` being used to 
    1202         emit INSERT statements for this instance.  This 
    1203         provides a handle into the current transaction on the 
    1204         target database specific to this instance. 
    1205        :param target: the mapped instance being persisted.  If 
    1206         the event is configured with ``raw=True``, this will 
    1207         instead be the :class:`.InstanceState` state-management 
    1208         object associated with the instance. 
    1209        :return: No return value is supported by this event. 
    1210 
    1211        .. seealso:: 
    1212 
    1213            :ref:`session_persistence_events` 
    1214 
    1215        """ 
    1216 
    1217    def after_insert( 
    1218        self, mapper: Mapper[_O], connection: Connection, target: _O 
    1219    ) -> None: 
    1220        """Receive an object instance after an INSERT statement 
    1221        is emitted corresponding to that instance. 
    1222 
    1223        .. note:: this event **only** applies to the 
    1224           :ref:`session flush operation <session_flushing>` 
    1225           and does **not** apply to the ORM DML operations described at 
    1226           :ref:`orm_expression_update_delete`.  To intercept ORM 
    1227           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`. 
    1228 
    1229        This event is used to modify in-Python-only 
    1230        state on the instance after an INSERT occurs, as well 
    1231        as to emit additional SQL statements on the given 
    1232        connection. 
    1233 
    1234        The event is often called for a batch of objects of the 
    1235        same class after their INSERT statements have been 
    1236        emitted at once in a previous step. In the extremely 
    1237        rare case that this is not desirable, the 
    1238        :class:`_orm.Mapper` object can be configured with ``batch=False``, 
    1239        which will cause batches of instances to be broken up 
    1240        into individual (and more poorly performing) 
    1241        event->persist->event steps. 
    1242 
    1243        .. warning:: 
    1244 
    1245            Mapper-level flush events only allow **very limited operations**, 
    1246            on attributes local to the row being operated upon only, 
    1247            as well as allowing any SQL to be emitted on the given 
    1248            :class:`_engine.Connection`.  **Please read fully** the notes 
    1249            at :ref:`session_persistence_mapper` for guidelines on using 
    1250            these methods; generally, the :meth:`.SessionEvents.before_flush` 
    1251            method should be preferred for general on-flush changes. 
    1252 
    1253        :param mapper: the :class:`_orm.Mapper` which is the target 
    1254         of this event. 
    1255        :param connection: the :class:`_engine.Connection` being used to 
    1256         emit INSERT statements for this instance.  This 
    1257         provides a handle into the current transaction on the 
    1258         target database specific to this instance. 
    1259        :param target: the mapped instance being persisted.  If 
    1260         the event is configured with ``raw=True``, this will 
    1261         instead be the :class:`.InstanceState` state-management 
    1262         object associated with the instance. 
    1263        :return: No return value is supported by this event. 
    1264 
    1265        .. seealso:: 
    1266 
    1267            :ref:`session_persistence_events` 
    1268 
    1269        """ 
    1270 
    1271    def before_update( 
    1272        self, mapper: Mapper[_O], connection: Connection, target: _O 
    1273    ) -> None: 
    1274        """Receive an object instance before an UPDATE statement 
    1275        is emitted corresponding to that instance. 
    1276 
    1277        .. note:: this event **only** applies to the 
    1278           :ref:`session flush operation <session_flushing>` 
    1279           and does **not** apply to the ORM DML operations described at 
    1280           :ref:`orm_expression_update_delete`.  To intercept ORM 
    1281           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`. 
    1282 
    1283        This event is used to modify local, non-object related 
    1284        attributes on the instance before an UPDATE occurs, as well 
    1285        as to emit additional SQL statements on the given 
    1286        connection. 
    1287 
    1288        This method is called for all instances that are 
    1289        marked as "dirty", *even those which have no net changes 
    1290        to their column-based attributes*. An object is marked 
    1291        as dirty when any of its column-based attributes have a 
    1292        "set attribute" operation called or when any of its 
    1293        collections are modified. If, at update time, no 
    1294        column-based attributes have any net changes, no UPDATE 
    1295        statement will be issued. This means that an instance 
    1296        being sent to :meth:`~.MapperEvents.before_update` is 
    1297        *not* a guarantee that an UPDATE statement will be 
    1298        issued, although you can affect the outcome here by 
    1299        modifying attributes so that a net change in value does 
    1300        exist. 
    1301 
    1302        To detect if the column-based attributes on the object have net 
    1303        changes, and will therefore generate an UPDATE statement, use 
    1304        ``object_session(instance).is_modified(instance, 
    1305        include_collections=False)``. 
    1306 
    1307        The event is often called for a batch of objects of the 
    1308        same class before their UPDATE statements are emitted at 
    1309        once in a later step. In the extremely rare case that 
    1310        this is not desirable, the :class:`_orm.Mapper` can be 
    1311        configured with ``batch=False``, which will cause 
    1312        batches of instances to be broken up into individual 
    1313        (and more poorly performing) event->persist->event 
    1314        steps. 
    1315 
    1316        .. warning:: 
    1317 
    1318            Mapper-level flush events only allow **very limited operations**, 
    1319            on attributes local to the row being operated upon only, 
    1320            as well as allowing any SQL to be emitted on the given 
    1321            :class:`_engine.Connection`.  **Please read fully** the notes 
    1322            at :ref:`session_persistence_mapper` for guidelines on using 
    1323            these methods; generally, the :meth:`.SessionEvents.before_flush` 
    1324            method should be preferred for general on-flush changes. 
    1325 
    1326        :param mapper: the :class:`_orm.Mapper` which is the target 
    1327         of this event. 
    1328        :param connection: the :class:`_engine.Connection` being used to 
    1329         emit UPDATE statements for this instance.  This 
    1330         provides a handle into the current transaction on the 
    1331         target database specific to this instance. 
    1332        :param target: the mapped instance being persisted.  If 
    1333         the event is configured with ``raw=True``, this will 
    1334         instead be the :class:`.InstanceState` state-management 
    1335         object associated with the instance. 
    1336        :return: No return value is supported by this event. 
    1337 
    1338        .. seealso:: 
    1339 
    1340            :ref:`session_persistence_events` 
    1341 
    1342        """ 
    1343 
    1344    def after_update( 
    1345        self, mapper: Mapper[_O], connection: Connection, target: _O 
    1346    ) -> None: 
    1347        """Receive an object instance after an UPDATE statement 
    1348        is emitted corresponding to that instance. 
    1349 
    1350        .. note:: this event **only** applies to the 
    1351           :ref:`session flush operation <session_flushing>` 
    1352           and does **not** apply to the ORM DML operations described at 
    1353           :ref:`orm_expression_update_delete`.  To intercept ORM 
    1354           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`. 
    1355 
    1356        This event is used to modify in-Python-only 
    1357        state on the instance after an UPDATE occurs, as well 
    1358        as to emit additional SQL statements on the given 
    1359        connection. 
    1360 
    1361        This method is called for all instances that are 
    1362        marked as "dirty", *even those which have no net changes 
    1363        to their column-based attributes*, and for which 
    1364        no UPDATE statement has proceeded. An object is marked 
    1365        as dirty when any of its column-based attributes have a 
    1366        "set attribute" operation called or when any of its 
    1367        collections are modified. If, at update time, no 
    1368        column-based attributes have any net changes, no UPDATE 
    1369        statement will be issued. This means that an instance 
    1370        being sent to :meth:`~.MapperEvents.after_update` is 
    1371        *not* a guarantee that an UPDATE statement has been 
    1372        issued. 
    1373 
    1374        To detect if the column-based attributes on the object have net 
    1375        changes, and therefore resulted in an UPDATE statement, use 
    1376        ``object_session(instance).is_modified(instance, 
    1377        include_collections=False)``. 
    1378 
    1379        The event is often called for a batch of objects of the 
    1380        same class after their UPDATE statements have been emitted at 
    1381        once in a previous step. In the extremely rare case that 
    1382        this is not desirable, the :class:`_orm.Mapper` can be 
    1383        configured with ``batch=False``, which will cause 
    1384        batches of instances to be broken up into individual 
    1385        (and more poorly performing) event->persist->event 
    1386        steps. 
    1387 
    1388        .. warning:: 
    1389 
    1390            Mapper-level flush events only allow **very limited operations**, 
    1391            on attributes local to the row being operated upon only, 
    1392            as well as allowing any SQL to be emitted on the given 
    1393            :class:`_engine.Connection`.  **Please read fully** the notes 
    1394            at :ref:`session_persistence_mapper` for guidelines on using 
    1395            these methods; generally, the :meth:`.SessionEvents.before_flush` 
    1396            method should be preferred for general on-flush changes. 
    1397 
    1398        :param mapper: the :class:`_orm.Mapper` which is the target 
    1399         of this event. 
    1400        :param connection: the :class:`_engine.Connection` being used to 
    1401         emit UPDATE statements for this instance.  This 
    1402         provides a handle into the current transaction on the 
    1403         target database specific to this instance. 
    1404        :param target: the mapped instance being persisted.  If 
    1405         the event is configured with ``raw=True``, this will 
    1406         instead be the :class:`.InstanceState` state-management 
    1407         object associated with the instance. 
    1408        :return: No return value is supported by this event. 
    1409 
    1410        .. seealso:: 
    1411 
    1412            :ref:`session_persistence_events` 
    1413 
    1414        """ 
    1415 
    1416    def before_delete( 
    1417        self, mapper: Mapper[_O], connection: Connection, target: _O 
    1418    ) -> None: 
    1419        """Receive an object instance before a DELETE statement 
    1420        is emitted corresponding to that instance. 
    1421 
    1422        .. note:: this event **only** applies to the 
    1423           :ref:`session flush operation <session_flushing>` 
    1424           and does **not** apply to the ORM DML operations described at 
    1425           :ref:`orm_expression_update_delete`.  To intercept ORM 
    1426           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`. 
    1427 
    1428        This event is used to emit additional SQL statements on 
    1429        the given connection as well as to perform application 
    1430        specific bookkeeping related to a deletion event. 
    1431 
    1432        The event is often called for a batch of objects of the 
    1433        same class before their DELETE statements are emitted at 
    1434        once in a later step. 
    1435 
    1436        .. warning:: 
    1437 
    1438            Mapper-level flush events only allow **very limited operations**, 
    1439            on attributes local to the row being operated upon only, 
    1440            as well as allowing any SQL to be emitted on the given 
    1441            :class:`_engine.Connection`.  **Please read fully** the notes 
    1442            at :ref:`session_persistence_mapper` for guidelines on using 
    1443            these methods; generally, the :meth:`.SessionEvents.before_flush` 
    1444            method should be preferred for general on-flush changes. 
    1445 
    1446        :param mapper: the :class:`_orm.Mapper` which is the target 
    1447         of this event. 
    1448        :param connection: the :class:`_engine.Connection` being used to 
    1449         emit DELETE statements for this instance.  This 
    1450         provides a handle into the current transaction on the 
    1451         target database specific to this instance. 
    1452        :param target: the mapped instance being deleted.  If 
    1453         the event is configured with ``raw=True``, this will 
    1454         instead be the :class:`.InstanceState` state-management 
    1455         object associated with the instance. 
    1456        :return: No return value is supported by this event. 
    1457 
    1458        .. seealso:: 
    1459 
    1460            :ref:`session_persistence_events` 
    1461 
    1462        """ 
    1463 
    1464    def after_delete( 
    1465        self, mapper: Mapper[_O], connection: Connection, target: _O 
    1466    ) -> None: 
    1467        """Receive an object instance after a DELETE statement 
    1468        has been emitted corresponding to that instance. 
    1469 
    1470        .. note:: this event **only** applies to the 
    1471           :ref:`session flush operation <session_flushing>` 
    1472           and does **not** apply to the ORM DML operations described at 
    1473           :ref:`orm_expression_update_delete`.  To intercept ORM 
    1474           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`. 
    1475 
    1476        This event is used to emit additional SQL statements on 
    1477        the given connection as well as to perform application 
    1478        specific bookkeeping related to a deletion event. 
    1479 
    1480        The event is often called for a batch of objects of the 
    1481        same class after their DELETE statements have been emitted at 
    1482        once in a previous step. 
    1483 
    1484        .. warning:: 
    1485 
    1486            Mapper-level flush events only allow **very limited operations**, 
    1487            on attributes local to the row being operated upon only, 
    1488            as well as allowing any SQL to be emitted on the given 
    1489            :class:`_engine.Connection`.  **Please read fully** the notes 
    1490            at :ref:`session_persistence_mapper` for guidelines on using 
    1491            these methods; generally, the :meth:`.SessionEvents.before_flush` 
    1492            method should be preferred for general on-flush changes. 
    1493 
    1494        :param mapper: the :class:`_orm.Mapper` which is the target 
    1495         of this event. 
    1496        :param connection: the :class:`_engine.Connection` being used to 
    1497         emit DELETE statements for this instance.  This 
    1498         provides a handle into the current transaction on the 
    1499         target database specific to this instance. 
    1500        :param target: the mapped instance being deleted.  If 
    1501         the event is configured with ``raw=True``, this will 
    1502         instead be the :class:`.InstanceState` state-management 
    1503         object associated with the instance. 
    1504        :return: No return value is supported by this event. 
    1505 
    1506        .. seealso:: 
    1507 
    1508            :ref:`session_persistence_events` 
    1509 
    1510        """ 
    1511 
    1512 
    1513class _MapperEventsHold(_EventsHold[_ET]): 
    1514    all_holds = weakref.WeakKeyDictionary() 
    1515 
    1516    def resolve( 
    1517        self, class_: Union[Type[_T], _InternalEntityType[_T]] 
    1518    ) -> Optional[Mapper[_T]]: 
    1519        return _mapper_or_none(class_) 
    1520 
    1521    # this fails on pyright if you use Any.  Fails on mypy if you use _ET 
    1522    class HoldMapperEvents(_EventsHold.HoldEvents[_ET], MapperEvents):  # type: ignore[valid-type,misc] # noqa: E501 
    1523        pass 
    1524 
    1525    dispatch = event.dispatcher(HoldMapperEvents) 
    1526 
    1527 
    1528_sessionevents_lifecycle_event_names: Set[str] = set() 
    1529 
    1530 
    1531class SessionEvents(event.Events[Session]): 
    1532    """Define events specific to :class:`.Session` lifecycle. 
    1533 
    1534    e.g.:: 
    1535 
    1536        from sqlalchemy import event 
    1537        from sqlalchemy.orm import sessionmaker 
    1538 
    1539 
    1540        def my_before_commit(session): 
    1541            print("before commit!") 
    1542 
    1543 
    1544        Session = sessionmaker() 
    1545 
    1546        event.listen(Session, "before_commit", my_before_commit) 
    1547 
    1548    The :func:`~.event.listen` function will accept 
    1549    :class:`.Session` objects as well as the return result 
    1550    of :class:`~.sessionmaker()` and :class:`~.scoped_session()`. 
    1551 
    1552    Additionally, it accepts the :class:`.Session` class which 
    1553    will apply listeners to all :class:`.Session` instances 
    1554    globally. 
    1555 
    1556    :param raw=False: When True, the "target" argument passed 
    1557       to applicable event listener functions that work on individual 
    1558       objects will be the instance's :class:`.InstanceState` management 
    1559       object, rather than the mapped instance itself. 
    1560 
    1561    :param restore_load_context=False: Applies to the 
    1562       :meth:`.SessionEvents.loaded_as_persistent` event.  Restores the loader 
    1563       context of the object when the event hook is complete, so that ongoing 
    1564       eager load operations continue to target the object appropriately.  A 
    1565       warning is emitted if the object is moved to a new loader context from 
    1566       within this event if this flag is not set. 
    1567 
    1568    """ 
    1569 
    1570    _target_class_doc = "SomeSessionClassOrObject" 
    1571 
    1572    _dispatch_target = Session 
    1573 
    1574    def _lifecycle_event(  # type: ignore [misc] 
    1575        fn: Callable[[SessionEvents, Session, Any], None], 
    1576    ) -> Callable[[SessionEvents, Session, Any], None]: 
    1577        _sessionevents_lifecycle_event_names.add(fn.__name__) 
    1578        return fn 
    1579 
    1580    @classmethod 
    1581    def _accept_with(  # type: ignore [return] 
    1582        cls, target: Any, identifier: str 
    1583    ) -> Union[Session, type]: 
    1584        if isinstance(target, scoped_session): 
    1585            target = target.session_factory 
    1586            if not isinstance(target, sessionmaker) and ( 
    1587                not isinstance(target, type) or not issubclass(target, Session) 
    1588            ): 
    1589                raise exc.ArgumentError( 
    1590                    "Session event listen on a scoped_session " 
    1591                    "requires that its creation callable " 
    1592                    "is associated with the Session class." 
    1593                ) 
    1594 
    1595        if isinstance(target, sessionmaker): 
    1596            return target.class_ 
    1597        elif isinstance(target, type): 
    1598            if issubclass(target, scoped_session): 
    1599                return Session 
    1600            elif issubclass(target, Session): 
    1601                return target 
    1602        elif isinstance(target, Session): 
    1603            return target 
    1604        elif hasattr(target, "_no_async_engine_events"): 
    1605            target._no_async_engine_events() 
    1606        else: 
    1607            # allows alternate SessionEvents-like-classes to be consulted 
    1608            return event.Events._accept_with(target, identifier)  # type: ignore [return-value] # noqa: E501 
    1609 
    1610    @classmethod 
    1611    def _listen( 
    1612        cls, 
    1613        event_key: Any, 
    1614        *, 
    1615        raw: bool = False, 
    1616        restore_load_context: bool = False, 
    1617        **kw: Any, 
    1618    ) -> None: 
    1619        is_instance_event = ( 
    1620            event_key.identifier in _sessionevents_lifecycle_event_names 
    1621        ) 
    1622 
    1623        if is_instance_event: 
    1624            if not raw or restore_load_context: 
    1625                fn = event_key._listen_fn 
    1626 
    1627                def wrap( 
    1628                    session: Session, 
    1629                    state: InstanceState[_O], 
    1630                    *arg: Any, 
    1631                    **kw: Any, 
    1632                ) -> Optional[Any]: 
    1633                    if not raw: 
    1634                        target = state.obj() 
    1635                        if target is None: 
    1636                            # existing behavior is that if the object is 
    1637                            # garbage collected, no event is emitted 
    1638                            return None 
    1639                    else: 
    1640                        target = state  # type: ignore [assignment] 
    1641                    if restore_load_context: 
    1642                        runid = state.runid 
    1643                    try: 
    1644                        return fn(session, target, *arg, **kw) 
    1645                    finally: 
    1646                        if restore_load_context: 
    1647                            state.runid = runid 
    1648 
    1649                event_key = event_key.with_wrapper(wrap) 
    1650 
    1651        event_key.base_listen(**kw) 
    1652 
    1653    def do_orm_execute(self, orm_execute_state: ORMExecuteState) -> None: 
    1654        """Intercept statement executions that occur on behalf of an 
    1655        ORM :class:`.Session` object. 
    1656 
    1657        This event is invoked for all top-level SQL statements invoked from the 
    1658        :meth:`_orm.Session.execute` method, as well as related methods such as 
    1659        :meth:`_orm.Session.scalars` and :meth:`_orm.Session.scalar`. As of 
    1660        SQLAlchemy 1.4, all ORM queries that run through the 
    1661        :meth:`_orm.Session.execute` method as well as related methods 
    1662        :meth:`_orm.Session.scalars`, :meth:`_orm.Session.scalar` etc. 
    1663        will participate in this event. 
    1664        This event hook does **not** apply to the queries that are 
    1665        emitted internally within the ORM flush process, i.e. the 
    1666        process described at :ref:`session_flushing`. 
    1667 
    1668        .. note::  The :meth:`_orm.SessionEvents.do_orm_execute` event hook 
    1669           is triggered **for ORM statement executions only**, meaning those 
    1670           invoked via the :meth:`_orm.Session.execute` and similar methods on 
    1671           the :class:`_orm.Session` object. It does **not** trigger for 
    1672           statements that are invoked by SQLAlchemy Core only, i.e. statements 
    1673           invoked directly using :meth:`_engine.Connection.execute` or 
    1674           otherwise originating from an :class:`_engine.Engine` object without 
    1675           any :class:`_orm.Session` involved. To intercept **all** SQL 
    1676           executions regardless of whether the Core or ORM APIs are in use, 
    1677           see the event hooks at :class:`.ConnectionEvents`, such as 
    1678           :meth:`.ConnectionEvents.before_execute` and 
    1679           :meth:`.ConnectionEvents.before_cursor_execute`. 
    1680 
    1681           Also, this event hook does **not** apply to queries that are 
    1682           emitted internally within the ORM flush process, 
    1683           i.e. the process described at :ref:`session_flushing`; to 
    1684           intercept steps within the flush process, see the event 
    1685           hooks described at :ref:`session_persistence_events` as 
    1686           well as :ref:`session_persistence_mapper`. 
    1687 
    1688        This event is a ``do_`` event, meaning it has the capability to replace 
    1689        the operation that the :meth:`_orm.Session.execute` method normally 
    1690        performs.  The intended use for this includes sharding and 
    1691        result-caching schemes which may seek to invoke the same statement 
    1692        across  multiple database connections, returning a result that is 
    1693        merged from each of them, or which don't invoke the statement at all, 
    1694        instead returning data from a cache. 
    1695 
    1696        The hook intends to replace the use of the 
    1697        ``Query._execute_and_instances`` method that could be subclassed prior 
    1698        to SQLAlchemy 1.4. 
    1699 
    1700        :param orm_execute_state: an instance of :class:`.ORMExecuteState` 
    1701         which contains all information about the current execution, as well 
    1702         as helper functions used to derive other commonly required 
    1703         information.   See that object for details. 
    1704 
    1705        .. seealso:: 
    1706 
    1707            :ref:`session_execute_events` - top level documentation on how 
    1708            to use :meth:`_orm.SessionEvents.do_orm_execute` 
    1709 
    1710            :class:`.ORMExecuteState` - the object passed to the 
    1711            :meth:`_orm.SessionEvents.do_orm_execute` event which contains 
    1712            all information about the statement to be invoked.  It also 
    1713            provides an interface to extend the current statement, options, 
    1714            and parameters as well as an option that allows programmatic 
    1715            invocation of the statement at any point. 
    1716 
    1717            :ref:`examples_session_orm_events` - includes examples of using 
    1718            :meth:`_orm.SessionEvents.do_orm_execute` 
    1719 
    1720            :ref:`examples_caching` - an example of how to integrate 
    1721            Dogpile caching with the ORM :class:`_orm.Session` making use 
    1722            of the :meth:`_orm.SessionEvents.do_orm_execute` event hook. 
    1723 
    1724            :ref:`examples_sharding` - the Horizontal Sharding example / 
    1725            extension relies upon the 
    1726            :meth:`_orm.SessionEvents.do_orm_execute` event hook to invoke a 
    1727            SQL statement on multiple backends and return a merged result. 
    1728 
    1729 
    1730        .. versionadded:: 1.4 
    1731 
    1732        """ 
    1733 
    1734    def after_transaction_create( 
    1735        self, session: Session, transaction: SessionTransaction 
    1736    ) -> None: 
    1737        """Execute when a new :class:`.SessionTransaction` is created. 
    1738 
    1739        This event differs from :meth:`~.SessionEvents.after_begin` 
    1740        in that it occurs for each :class:`.SessionTransaction` 
    1741        overall, as opposed to when transactions are begun 
    1742        on individual database connections.  It is also invoked 
    1743        for nested transactions and subtransactions, and is always 
    1744        matched by a corresponding 
    1745        :meth:`~.SessionEvents.after_transaction_end` event 
    1746        (assuming normal operation of the :class:`.Session`). 
    1747 
    1748        :param session: the target :class:`.Session`. 
    1749        :param transaction: the target :class:`.SessionTransaction`. 
    1750 
    1751         To detect if this is the outermost 
    1752         :class:`.SessionTransaction`, as opposed to a "subtransaction" or a 
    1753         SAVEPOINT, test that the :attr:`.SessionTransaction.parent` attribute 
    1754         is ``None``:: 
    1755 
    1756                @event.listens_for(session, "after_transaction_create") 
    1757                def after_transaction_create(session, transaction): 
    1758                    if transaction.parent is None: 
    1759                        ...  # work with top-level transaction 
    1760 
    1761         To detect if the :class:`.SessionTransaction` is a SAVEPOINT, use the 
    1762         :attr:`.SessionTransaction.nested` attribute:: 
    1763 
    1764                @event.listens_for(session, "after_transaction_create") 
    1765                def after_transaction_create(session, transaction): 
    1766                    if transaction.nested: 
    1767                        ...  # work with SAVEPOINT transaction 
    1768 
    1769        .. seealso:: 
    1770 
    1771            :class:`.SessionTransaction` 
    1772 
    1773            :meth:`~.SessionEvents.after_transaction_end` 
    1774 
    1775        """ 
    1776 
    1777    def after_transaction_end( 
    1778        self, session: Session, transaction: SessionTransaction 
    1779    ) -> None: 
    1780        """Execute when the span of a :class:`.SessionTransaction` ends. 
    1781 
    1782        This event differs from :meth:`~.SessionEvents.after_commit` 
    1783        in that it corresponds to all :class:`.SessionTransaction` 
    1784        objects in use, including those for nested transactions 
    1785        and subtransactions, and is always matched by a corresponding 
    1786        :meth:`~.SessionEvents.after_transaction_create` event. 
    1787 
    1788        :param session: the target :class:`.Session`. 
    1789        :param transaction: the target :class:`.SessionTransaction`. 
    1790 
    1791         To detect if this is the outermost 
    1792         :class:`.SessionTransaction`, as opposed to a "subtransaction" or a 
    1793         SAVEPOINT, test that the :attr:`.SessionTransaction.parent` attribute 
    1794         is ``None``:: 
    1795 
    1796                @event.listens_for(session, "after_transaction_create") 
    1797                def after_transaction_end(session, transaction): 
    1798                    if transaction.parent is None: 
    1799                        ...  # work with top-level transaction 
    1800 
    1801         To detect if the :class:`.SessionTransaction` is a SAVEPOINT, use the 
    1802         :attr:`.SessionTransaction.nested` attribute:: 
    1803 
    1804                @event.listens_for(session, "after_transaction_create") 
    1805                def after_transaction_end(session, transaction): 
    1806                    if transaction.nested: 
    1807                        ...  # work with SAVEPOINT transaction 
    1808 
    1809        .. seealso:: 
    1810 
    1811            :class:`.SessionTransaction` 
    1812 
    1813            :meth:`~.SessionEvents.after_transaction_create` 
    1814 
    1815        """ 
    1816 
    1817    def before_commit(self, session: Session) -> None: 
    1818        """Execute before commit is called. 
    1819 
    1820        .. note:: 
    1821 
    1822            The :meth:`~.SessionEvents.before_commit` hook is *not* per-flush, 
    1823            that is, the :class:`.Session` can emit SQL to the database 
    1824            many times within the scope of a transaction. 
    1825            For interception of these events, use the 
    1826            :meth:`~.SessionEvents.before_flush`, 
    1827            :meth:`~.SessionEvents.after_flush`, or 
    1828            :meth:`~.SessionEvents.after_flush_postexec` 
    1829            events. 
    1830 
    1831        :param session: The target :class:`.Session`. 
    1832 
    1833        .. seealso:: 
    1834 
    1835            :meth:`~.SessionEvents.after_commit` 
    1836 
    1837            :meth:`~.SessionEvents.after_begin` 
    1838 
    1839            :meth:`~.SessionEvents.after_transaction_create` 
    1840 
    1841            :meth:`~.SessionEvents.after_transaction_end` 
    1842 
    1843        """ 
    1844 
    1845    def after_commit(self, session: Session) -> None: 
    1846        """Execute after a commit has occurred. 
    1847 
    1848        .. note:: 
    1849 
    1850            The :meth:`~.SessionEvents.after_commit` hook is *not* per-flush, 
    1851            that is, the :class:`.Session` can emit SQL to the database 
    1852            many times within the scope of a transaction. 
    1853            For interception of these events, use the 
    1854            :meth:`~.SessionEvents.before_flush`, 
    1855            :meth:`~.SessionEvents.after_flush`, or 
    1856            :meth:`~.SessionEvents.after_flush_postexec` 
    1857            events. 
    1858 
    1859        .. note:: 
    1860 
    1861            The :class:`.Session` is not in an active transaction 
    1862            when the :meth:`~.SessionEvents.after_commit` event is invoked, 
    1863            and therefore can not emit SQL.  To emit SQL corresponding to 
    1864            every transaction, use the :meth:`~.SessionEvents.before_commit` 
    1865            event. 
    1866 
    1867        :param session: The target :class:`.Session`. 
    1868 
    1869        .. seealso:: 
    1870 
    1871            :meth:`~.SessionEvents.before_commit` 
    1872 
    1873            :meth:`~.SessionEvents.after_begin` 
    1874 
    1875            :meth:`~.SessionEvents.after_transaction_create` 
    1876 
    1877            :meth:`~.SessionEvents.after_transaction_end` 
    1878 
    1879        """ 
    1880 
    1881    def after_rollback(self, session: Session) -> None: 
    1882        """Execute after a real DBAPI rollback has occurred. 
    1883 
    1884        Note that this event only fires when the *actual* rollback against 
    1885        the database occurs - it does *not* fire each time the 
    1886        :meth:`.Session.rollback` method is called, if the underlying 
    1887        DBAPI transaction has already been rolled back.  In many 
    1888        cases, the :class:`.Session` will not be in 
    1889        an "active" state during this event, as the current 
    1890        transaction is not valid.   To acquire a :class:`.Session` 
    1891        which is active after the outermost rollback has proceeded, 
    1892        use the :meth:`.SessionEvents.after_soft_rollback` event, checking the 
    1893        :attr:`.Session.is_active` flag. 
    1894 
    1895        :param session: The target :class:`.Session`. 
    1896 
    1897        """ 
    1898 
    1899    def after_soft_rollback( 
    1900        self, session: Session, previous_transaction: SessionTransaction 
    1901    ) -> None: 
    1902        """Execute after any rollback has occurred, including "soft" 
    1903        rollbacks that don't actually emit at the DBAPI level. 
    1904 
    1905        This corresponds to both nested and outer rollbacks, i.e. 
    1906        the innermost rollback that calls the DBAPI's 
    1907        rollback() method, as well as the enclosing rollback 
    1908        calls that only pop themselves from the transaction stack. 
    1909 
    1910        The given :class:`.Session` can be used to invoke SQL and 
    1911        :meth:`.Session.query` operations after an outermost rollback 
    1912        by first checking the :attr:`.Session.is_active` flag:: 
    1913 
    1914            @event.listens_for(Session, "after_soft_rollback") 
    1915            def do_something(session, previous_transaction): 
    1916                if session.is_active: 
    1917                    session.execute(text("select * from some_table")) 
    1918 
    1919        :param session: The target :class:`.Session`. 
    1920        :param previous_transaction: The :class:`.SessionTransaction` 
    1921         transactional marker object which was just closed.   The current 
    1922         :class:`.SessionTransaction` for the given :class:`.Session` is 
    1923         available via the :attr:`.Session.transaction` attribute. 
    1924 
    1925        """ 
    1926 
    1927    def before_flush( 
    1928        self, 
    1929        session: Session, 
    1930        flush_context: UOWTransaction, 
    1931        instances: Optional[Sequence[_O]], 
    1932    ) -> None: 
    1933        """Execute before flush process has started. 
    1934 
    1935        :param session: The target :class:`.Session`. 
    1936        :param flush_context: Internal :class:`.UOWTransaction` object 
    1937         which handles the details of the flush. 
    1938        :param instances: Usually ``None``, this is the collection of 
    1939         objects which can be passed to the :meth:`.Session.flush` method 
    1940         (note this usage is deprecated). 
    1941 
    1942        .. seealso:: 
    1943 
    1944            :meth:`~.SessionEvents.after_flush` 
    1945 
    1946            :meth:`~.SessionEvents.after_flush_postexec` 
    1947 
    1948            :ref:`session_persistence_events` 
    1949 
    1950        """ 
    1951 
    1952    def after_flush( 
    1953        self, session: Session, flush_context: UOWTransaction 
    1954    ) -> None: 
    1955        """Execute after flush has completed, but before commit has been 
    1956        called. 
    1957 
    1958        Note that the session's state is still in pre-flush, i.e. 'new', 
    1959        'dirty', and 'deleted' lists still show pre-flush state as well 
    1960        as the history settings on instance attributes. 
    1961 
    1962        .. warning:: This event runs after the :class:`.Session` has emitted 
    1963           SQL to modify the database, but **before** it has altered its 
    1964           internal state to reflect those changes, including that newly 
    1965           inserted objects are placed into the identity map.  ORM operations 
    1966           emitted within this event such as loads of related items 
    1967           may produce new identity map entries that will immediately 
    1968           be replaced, sometimes causing confusing results.  SQLAlchemy will 
    1969           emit a warning for this condition as of version 1.3.9. 
    1970 
    1971        :param session: The target :class:`.Session`. 
    1972        :param flush_context: Internal :class:`.UOWTransaction` object 
    1973         which handles the details of the flush. 
    1974 
    1975        .. seealso:: 
    1976 
    1977            :meth:`~.SessionEvents.before_flush` 
    1978 
    1979            :meth:`~.SessionEvents.after_flush_postexec` 
    1980 
    1981            :ref:`session_persistence_events` 
    1982 
    1983        """ 
    1984 
    1985    def after_flush_postexec( 
    1986        self, session: Session, flush_context: UOWTransaction 
    1987    ) -> None: 
    1988        """Execute after flush has completed, and after the post-exec 
    1989        state occurs. 
    1990 
    1991        This will be when the 'new', 'dirty', and 'deleted' lists are in 
    1992        their final state.  An actual commit() may or may not have 
    1993        occurred, depending on whether or not the flush started its own 
    1994        transaction or participated in a larger transaction. 
    1995 
    1996        :param session: The target :class:`.Session`. 
    1997        :param flush_context: Internal :class:`.UOWTransaction` object 
    1998         which handles the details of the flush. 
    1999 
    2000 
    2001        .. seealso:: 
    2002 
    2003            :meth:`~.SessionEvents.before_flush` 
    2004 
    2005            :meth:`~.SessionEvents.after_flush` 
    2006 
    2007            :ref:`session_persistence_events` 
    2008 
    2009        """ 
    2010 
    2011    def after_begin( 
    2012        self, 
    2013        session: Session, 
    2014        transaction: SessionTransaction, 
    2015        connection: Connection, 
    2016    ) -> None: 
    2017        """Execute after a transaction is begun on a connection. 
    2018 
    2019        .. note:: This event is called within the process of the 
    2020          :class:`_orm.Session` modifying its own internal state. 
    2021          To invoke SQL operations within this hook, use the 
    2022          :class:`_engine.Connection` provided to the event; 
    2023          do not run SQL operations using the :class:`_orm.Session` 
    2024          directly. 
    2025 
    2026        :param session: The target :class:`.Session`. 
    2027        :param transaction: The :class:`.SessionTransaction`. 
    2028        :param connection: The :class:`_engine.Connection` object 
    2029         which will be used for SQL statements. 
    2030 
    2031        .. seealso:: 
    2032 
    2033            :meth:`~.SessionEvents.before_commit` 
    2034 
    2035            :meth:`~.SessionEvents.after_commit` 
    2036 
    2037            :meth:`~.SessionEvents.after_transaction_create` 
    2038 
    2039            :meth:`~.SessionEvents.after_transaction_end` 
    2040 
    2041        """ 
    2042 
    2043    @_lifecycle_event 
    2044    def before_attach(self, session: Session, instance: _O) -> None: 
    2045        """Execute before an instance is attached to a session. 
    2046 
    2047        This is called before an add, delete or merge causes 
    2048        the object to be part of the session. 
    2049 
    2050        .. seealso:: 
    2051 
    2052            :meth:`~.SessionEvents.after_attach` 
    2053 
    2054            :ref:`session_lifecycle_events` 
    2055 
    2056        """ 
    2057 
    2058    @_lifecycle_event 
    2059    def after_attach(self, session: Session, instance: _O) -> None: 
    2060        """Execute after an instance is attached to a session. 
    2061 
    2062        This is called after an add, delete or merge. 
    2063 
    2064        .. note:: 
    2065 
    2066           As of 0.8, this event fires off *after* the item 
    2067           has been fully associated with the session, which is 
    2068           different than previous releases.  For event 
    2069           handlers that require the object not yet 
    2070           be part of session state (such as handlers which 
    2071           may autoflush while the target object is not 
    2072           yet complete) consider the 
    2073           new :meth:`.before_attach` event. 
    2074 
    2075        .. seealso:: 
    2076 
    2077            :meth:`~.SessionEvents.before_attach` 
    2078 
    2079            :ref:`session_lifecycle_events` 
    2080 
    2081        """ 
    2082 
    2083    def after_bulk_update(self, update_context: _O) -> None: 
    2084        """Event for after the legacy :meth:`_orm.Query.update` method 
    2085        has been called. 
    2086 
    2087        .. legacy:: The :meth:`_orm.SessionEvents.after_bulk_update` method 
    2088           is a legacy event hook as of SQLAlchemy 2.0.   The event 
    2089           **does not participate** in :term:`2.0 style` invocations 
    2090           using :func:`_dml.update` documented at 
    2091           :ref:`orm_queryguide_update_delete_where`.  For 2.0 style use, 
    2092           the :meth:`_orm.SessionEvents.do_orm_execute` hook will intercept 
    2093           these calls. 
    2094 
    2095        :param update_context: an "update context" object which contains 
    2096         details about the update, including these attributes: 
    2097 
    2098            * ``session`` - the :class:`.Session` involved 
    2099            * ``query`` -the :class:`_query.Query` 
    2100              object that this update operation 
    2101              was called upon. 
    2102            * ``values`` The "values" dictionary that was passed to 
    2103              :meth:`_query.Query.update`. 
    2104            * ``result`` the :class:`_engine.CursorResult` 
    2105              returned as a result of the 
    2106              bulk UPDATE operation. 
    2107 
    2108        .. versionchanged:: 1.4 the update_context no longer has a 
    2109           ``QueryContext`` object associated with it. 
    2110 
    2111        .. seealso:: 
    2112 
    2113            :meth:`.QueryEvents.before_compile_update` 
    2114 
    2115            :meth:`.SessionEvents.after_bulk_delete` 
    2116 
    2117        """ 
    2118 
    2119    def after_bulk_delete(self, delete_context: _O) -> None: 
    2120        """Event for after the legacy :meth:`_orm.Query.delete` method 
    2121        has been called. 
    2122 
    2123        .. legacy:: The :meth:`_orm.SessionEvents.after_bulk_delete` method 
    2124           is a legacy event hook as of SQLAlchemy 2.0.   The event 
    2125           **does not participate** in :term:`2.0 style` invocations 
    2126           using :func:`_dml.delete` documented at 
    2127           :ref:`orm_queryguide_update_delete_where`.  For 2.0 style use, 
    2128           the :meth:`_orm.SessionEvents.do_orm_execute` hook will intercept 
    2129           these calls. 
    2130 
    2131        :param delete_context: a "delete context" object which contains 
    2132         details about the update, including these attributes: 
    2133 
    2134            * ``session`` - the :class:`.Session` involved 
    2135            * ``query`` -the :class:`_query.Query` 
    2136              object that this update operation 
    2137              was called upon. 
    2138            * ``result`` the :class:`_engine.CursorResult` 
    2139              returned as a result of the 
    2140              bulk DELETE operation. 
    2141 
    2142        .. versionchanged:: 1.4 the update_context no longer has a 
    2143           ``QueryContext`` object associated with it. 
    2144 
    2145        .. seealso:: 
    2146 
    2147            :meth:`.QueryEvents.before_compile_delete` 
    2148 
    2149            :meth:`.SessionEvents.after_bulk_update` 
    2150 
    2151        """ 
    2152 
    2153    @_lifecycle_event 
    2154    def transient_to_pending(self, session: Session, instance: _O) -> None: 
    2155        """Intercept the "transient to pending" transition for a specific 
    2156        object. 
    2157 
    2158        This event is a specialization of the 
    2159        :meth:`.SessionEvents.after_attach` event which is only invoked 
    2160        for this specific transition.  It is invoked typically during the 
    2161        :meth:`.Session.add` call. 
    2162 
    2163        :param session: target :class:`.Session` 
    2164 
    2165        :param instance: the ORM-mapped instance being operated upon. 
    2166 
    2167        .. seealso:: 
    2168 
    2169            :ref:`session_lifecycle_events` 
    2170 
    2171        """ 
    2172 
    2173    @_lifecycle_event 
    2174    def pending_to_transient(self, session: Session, instance: _O) -> None: 
    2175        """Intercept the "pending to transient" transition for a specific 
    2176        object. 
    2177 
    2178        This less common transition occurs when an pending object that has 
    2179        not been flushed is evicted from the session; this can occur 
    2180        when the :meth:`.Session.rollback` method rolls back the transaction, 
    2181        or when the :meth:`.Session.expunge` method is used. 
    2182 
    2183        :param session: target :class:`.Session` 
    2184 
    2185        :param instance: the ORM-mapped instance being operated upon. 
    2186 
    2187        .. seealso:: 
    2188 
    2189            :ref:`session_lifecycle_events` 
    2190 
    2191        """ 
    2192 
    2193    @_lifecycle_event 
    2194    def persistent_to_transient(self, session: Session, instance: _O) -> None: 
    2195        """Intercept the "persistent to transient" transition for a specific 
    2196        object. 
    2197 
    2198        This less common transition occurs when an pending object that has 
    2199        has been flushed is evicted from the session; this can occur 
    2200        when the :meth:`.Session.rollback` method rolls back the transaction. 
    2201 
    2202        :param session: target :class:`.Session` 
    2203 
    2204        :param instance: the ORM-mapped instance being operated upon. 
    2205 
    2206        .. seealso:: 
    2207 
    2208            :ref:`session_lifecycle_events` 
    2209 
    2210        """ 
    2211 
    2212    @_lifecycle_event 
    2213    def pending_to_persistent(self, session: Session, instance: _O) -> None: 
    2214        """Intercept the "pending to persistent"" transition for a specific 
    2215        object. 
    2216 
    2217        This event is invoked within the flush process, and is 
    2218        similar to scanning the :attr:`.Session.new` collection within 
    2219        the :meth:`.SessionEvents.after_flush` event.  However, in this 
    2220        case the object has already been moved to the persistent state 
    2221        when the event is called. 
    2222 
    2223        :param session: target :class:`.Session` 
    2224 
    2225        :param instance: the ORM-mapped instance being operated upon. 
    2226 
    2227        .. seealso:: 
    2228 
    2229            :ref:`session_lifecycle_events` 
    2230 
    2231        """ 
    2232 
    2233    @_lifecycle_event 
    2234    def detached_to_persistent(self, session: Session, instance: _O) -> None: 
    2235        """Intercept the "detached to persistent" transition for a specific 
    2236        object. 
    2237 
    2238        This event is a specialization of the 
    2239        :meth:`.SessionEvents.after_attach` event which is only invoked 
    2240        for this specific transition.  It is invoked typically during the 
    2241        :meth:`.Session.add` call, as well as during the 
    2242        :meth:`.Session.delete` call if the object was not previously 
    2243        associated with the 
    2244        :class:`.Session` (note that an object marked as "deleted" remains 
    2245        in the "persistent" state until the flush proceeds). 
    2246 
    2247        .. note:: 
    2248 
    2249            If the object becomes persistent as part of a call to 
    2250            :meth:`.Session.delete`, the object is **not** yet marked as 
    2251            deleted when this event is called.  To detect deleted objects, 
    2252            check the ``deleted`` flag sent to the 
    2253            :meth:`.SessionEvents.persistent_to_detached` to event after the 
    2254            flush proceeds, or check the :attr:`.Session.deleted` collection 
    2255            within the :meth:`.SessionEvents.before_flush` event if deleted 
    2256            objects need to be intercepted before the flush. 
    2257 
    2258        :param session: target :class:`.Session` 
    2259 
    2260        :param instance: the ORM-mapped instance being operated upon. 
    2261 
    2262        .. seealso:: 
    2263 
    2264            :ref:`session_lifecycle_events` 
    2265 
    2266        """ 
    2267 
    2268    @_lifecycle_event 
    2269    def loaded_as_persistent(self, session: Session, instance: _O) -> None: 
    2270        """Intercept the "loaded as persistent" transition for a specific 
    2271        object. 
    2272 
    2273        This event is invoked within the ORM loading process, and is invoked 
    2274        very similarly to the :meth:`.InstanceEvents.load` event.  However, 
    2275        the event here is linkable to a :class:`.Session` class or instance, 
    2276        rather than to a mapper or class hierarchy, and integrates 
    2277        with the other session lifecycle events smoothly.  The object 
    2278        is guaranteed to be present in the session's identity map when 
    2279        this event is called. 
    2280 
    2281        .. note:: This event is invoked within the loader process before 
    2282           eager loaders may have been completed, and the object's state may 
    2283           not be complete.  Additionally, invoking row-level refresh 
    2284           operations on the object will place the object into a new loader 
    2285           context, interfering with the existing load context.   See the note 
    2286           on :meth:`.InstanceEvents.load` for background on making use of the 
    2287           :paramref:`.SessionEvents.restore_load_context` parameter, which 
    2288           works in the same manner as that of 
    2289           :paramref:`.InstanceEvents.restore_load_context`, in  order to 
    2290           resolve this scenario. 
    2291 
    2292        :param session: target :class:`.Session` 
    2293 
    2294        :param instance: the ORM-mapped instance being operated upon. 
    2295 
    2296        .. seealso:: 
    2297 
    2298            :ref:`session_lifecycle_events` 
    2299 
    2300        """ 
    2301 
    2302    @_lifecycle_event 
    2303    def persistent_to_deleted(self, session: Session, instance: _O) -> None: 
    2304        """Intercept the "persistent to deleted" transition for a specific 
    2305        object. 
    2306 
    2307        This event is invoked when a persistent object's identity 
    2308        is deleted from the database within a flush, however the object 
    2309        still remains associated with the :class:`.Session` until the 
    2310        transaction completes. 
    2311 
    2312        If the transaction is rolled back, the object moves again 
    2313        to the persistent state, and the 
    2314        :meth:`.SessionEvents.deleted_to_persistent` event is called. 
    2315        If the transaction is committed, the object becomes detached, 
    2316        which will emit the :meth:`.SessionEvents.deleted_to_detached` 
    2317        event. 
    2318 
    2319        Note that while the :meth:`.Session.delete` method is the primary 
    2320        public interface to mark an object as deleted, many objects 
    2321        get deleted due to cascade rules, which are not always determined 
    2322        until flush time.  Therefore, there's no way to catch 
    2323        every object that will be deleted until the flush has proceeded. 
    2324        the :meth:`.SessionEvents.persistent_to_deleted` event is therefore 
    2325        invoked at the end of a flush. 
    2326 
    2327        .. seealso:: 
    2328 
    2329            :ref:`session_lifecycle_events` 
    2330 
    2331        """ 
    2332 
    2333    @_lifecycle_event 
    2334    def deleted_to_persistent(self, session: Session, instance: _O) -> None: 
    2335        """Intercept the "deleted to persistent" transition for a specific 
    2336        object. 
    2337 
    2338        This transition occurs only when an object that's been deleted 
    2339        successfully in a flush is restored due to a call to 
    2340        :meth:`.Session.rollback`.   The event is not called under 
    2341        any other circumstances. 
    2342 
    2343        .. seealso:: 
    2344 
    2345            :ref:`session_lifecycle_events` 
    2346 
    2347        """ 
    2348 
    2349    @_lifecycle_event 
    2350    def deleted_to_detached(self, session: Session, instance: _O) -> None: 
    2351        """Intercept the "deleted to detached" transition for a specific 
    2352        object. 
    2353 
    2354        This event is invoked when a deleted object is evicted 
    2355        from the session.   The typical case when this occurs is when 
    2356        the transaction for a :class:`.Session` in which the object 
    2357        was deleted is committed; the object moves from the deleted 
    2358        state to the detached state. 
    2359 
    2360        It is also invoked for objects that were deleted in a flush 
    2361        when the :meth:`.Session.expunge_all` or :meth:`.Session.close` 
    2362        events are called, as well as if the object is individually 
    2363        expunged from its deleted state via :meth:`.Session.expunge`. 
    2364 
    2365        .. seealso:: 
    2366 
    2367            :ref:`session_lifecycle_events` 
    2368 
    2369        """ 
    2370 
    2371    @_lifecycle_event 
    2372    def persistent_to_detached(self, session: Session, instance: _O) -> None: 
    2373        """Intercept the "persistent to detached" transition for a specific 
    2374        object. 
    2375 
    2376        This event is invoked when a persistent object is evicted 
    2377        from the session.  There are many conditions that cause this 
    2378        to happen, including: 
    2379 
    2380        * using a method such as :meth:`.Session.expunge` 
    2381          or :meth:`.Session.close` 
    2382 
    2383        * Calling the :meth:`.Session.rollback` method, when the object 
    2384          was part of an INSERT statement for that session's transaction 
    2385 
    2386 
    2387        :param session: target :class:`.Session` 
    2388 
    2389        :param instance: the ORM-mapped instance being operated upon. 
    2390 
    2391        :param deleted: boolean.  If True, indicates this object moved 
    2392         to the detached state because it was marked as deleted and flushed. 
    2393 
    2394 
    2395        .. seealso:: 
    2396 
    2397            :ref:`session_lifecycle_events` 
    2398 
    2399        """ 
    2400 
    2401 
    2402class AttributeEvents(event.Events[QueryableAttribute[Any]]): 
    2403    r"""Define events for object attributes. 
    2404 
    2405    These are typically defined on the class-bound descriptor for the 
    2406    target class. 
    2407 
    2408    For example, to register a listener that will receive the 
    2409    :meth:`_orm.AttributeEvents.append` event:: 
    2410 
    2411        from sqlalchemy import event 
    2412 
    2413 
    2414        @event.listens_for(MyClass.collection, "append", propagate=True) 
    2415        def my_append_listener(target, value, initiator): 
    2416            print("received append event for target: %s" % target) 
    2417 
    2418    Listeners have the option to return a possibly modified version of the 
    2419    value, when the :paramref:`.AttributeEvents.retval` flag is passed to 
    2420    :func:`.event.listen` or :func:`.event.listens_for`, such as below, 
    2421    illustrated using the :meth:`_orm.AttributeEvents.set` event:: 
    2422 
    2423        def validate_phone(target, value, oldvalue, initiator): 
    2424            "Strip non-numeric characters from a phone number" 
    2425 
    2426            return re.sub(r"\D", "", value) 
    2427 
    2428 
    2429        # setup listener on UserContact.phone attribute, instructing 
    2430        # it to use the return value 
    2431        listen(UserContact.phone, "set", validate_phone, retval=True) 
    2432 
    2433    A validation function like the above can also raise an exception 
    2434    such as :exc:`ValueError` to halt the operation. 
    2435 
    2436    The :paramref:`.AttributeEvents.propagate` flag is also important when 
    2437    applying listeners to mapped classes that also have mapped subclasses, 
    2438    as when using mapper inheritance patterns:: 
    2439 
    2440 
    2441        @event.listens_for(MySuperClass.attr, "set", propagate=True) 
    2442        def receive_set(target, value, initiator): 
    2443            print("value set: %s" % target) 
    2444 
    2445    The full list of modifiers available to the :func:`.event.listen` 
    2446    and :func:`.event.listens_for` functions are below. 
    2447 
    2448    :param active_history=False: When True, indicates that the 
    2449      "set" event would like to receive the "old" value being 
    2450      replaced unconditionally, even if this requires firing off 
    2451      database loads. Note that ``active_history`` can also be 
    2452      set directly via :func:`.column_property` and 
    2453      :func:`_orm.relationship`. 
    2454 
    2455    :param propagate=False: When True, the listener function will 
    2456      be established not just for the class attribute given, but 
    2457      for attributes of the same name on all current subclasses 
    2458      of that class, as well as all future subclasses of that 
    2459      class, using an additional listener that listens for 
    2460      instrumentation events. 
    2461    :param raw=False: When True, the "target" argument to the 
    2462      event will be the :class:`.InstanceState` management 
    2463      object, rather than the mapped instance itself. 
    2464    :param retval=False: when True, the user-defined event 
    2465      listening must return the "value" argument from the 
    2466      function.  This gives the listening function the opportunity 
    2467      to change the value that is ultimately used for a "set" 
    2468      or "append" event. 
    2469 
    2470    """ 
    2471 
    2472    _target_class_doc = "SomeClass.some_attribute" 
    2473    _dispatch_target = QueryableAttribute 
    2474 
    2475    @staticmethod 
    2476    def _set_dispatch( 
    2477        cls: Type[_HasEventsDispatch[Any]], dispatch_cls: Type[_Dispatch[Any]] 
    2478    ) -> _Dispatch[Any]: 
    2479        dispatch = event.Events._set_dispatch(cls, dispatch_cls) 
    2480        dispatch_cls._active_history = False 
    2481        return dispatch 
    2482 
    2483    @classmethod 
    2484    def _accept_with( 
    2485        cls, 
    2486        target: Union[QueryableAttribute[Any], Type[QueryableAttribute[Any]]], 
    2487        identifier: str, 
    2488    ) -> Union[QueryableAttribute[Any], Type[QueryableAttribute[Any]]]: 
    2489        # TODO: coverage 
    2490        if isinstance(target, interfaces.MapperProperty): 
    2491            return getattr(target.parent.class_, target.key) 
    2492        else: 
    2493            return target 
    2494 
    2495    @classmethod 
    2496    def _listen(  # type: ignore [override] 
    2497        cls, 
    2498        event_key: _EventKey[QueryableAttribute[Any]], 
    2499        active_history: bool = False, 
    2500        raw: bool = False, 
    2501        retval: bool = False, 
    2502        propagate: bool = False, 
    2503        include_key: bool = False, 
    2504    ) -> None: 
    2505        target, fn = event_key.dispatch_target, event_key._listen_fn 
    2506 
    2507        if active_history: 
    2508            target.dispatch._active_history = True 
    2509 
    2510        if not raw or not retval or not include_key: 
    2511 
    2512            def wrap(target: InstanceState[_O], *arg: Any, **kw: Any) -> Any: 
    2513                if not raw: 
    2514                    target = target.obj()  # type: ignore [assignment] 
    2515                if not retval: 
    2516                    if arg: 
    2517                        value = arg[0] 
    2518                    else: 
    2519                        value = None 
    2520                    if include_key: 
    2521                        fn(target, *arg, **kw) 
    2522                    else: 
    2523                        fn(target, *arg) 
    2524                    return value 
    2525                else: 
    2526                    if include_key: 
    2527                        return fn(target, *arg, **kw) 
    2528                    else: 
    2529                        return fn(target, *arg) 
    2530 
    2531            event_key = event_key.with_wrapper(wrap) 
    2532 
    2533        event_key.base_listen(propagate=propagate) 
    2534 
    2535        if propagate: 
    2536            manager = instrumentation.manager_of_class(target.class_) 
    2537 
    2538            for mgr in manager.subclass_managers(True):  # type: ignore [no-untyped-call] # noqa: E501 
    2539                event_key.with_dispatch_target(mgr[target.key]).base_listen( 
    2540                    propagate=True 
    2541                ) 
    2542                if active_history: 
    2543                    mgr[target.key].dispatch._active_history = True 
    2544 
    2545    def append( 
    2546        self, 
    2547        target: _O, 
    2548        value: _T, 
    2549        initiator: Event, 
    2550        *, 
    2551        key: EventConstants = NO_KEY, 
    2552    ) -> Optional[_T]: 
    2553        """Receive a collection append event. 
    2554 
    2555        The append event is invoked for each element as it is appended 
    2556        to the collection.  This occurs for single-item appends as well 
    2557        as for a "bulk replace" operation. 
    2558 
    2559        :param target: the object instance receiving the event. 
    2560          If the listener is registered with ``raw=True``, this will 
    2561          be the :class:`.InstanceState` object. 
    2562        :param value: the value being appended.  If this listener 
    2563          is registered with ``retval=True``, the listener 
    2564          function must return this value, or a new value which 
    2565          replaces it. 
    2566        :param initiator: An instance of :class:`.attributes.Event` 
    2567          representing the initiation of the event.  May be modified 
    2568          from its original value by backref handlers in order to control 
    2569          chained event propagation, as well as be inspected for information 
    2570          about the source of the event. 
    2571        :param key: When the event is established using the 
    2572         :paramref:`.AttributeEvents.include_key` parameter set to 
    2573         True, this will be the key used in the operation, such as 
    2574         ``collection[some_key_or_index] = value``. 
    2575         The parameter is not passed 
    2576         to the event at all if the the 
    2577         :paramref:`.AttributeEvents.include_key` 
    2578         was not used to set up the event; this is to allow backwards 
    2579         compatibility with existing event handlers that don't include the 
    2580         ``key`` parameter. 
    2581 
    2582         .. versionadded:: 2.0 
    2583 
    2584        :return: if the event was registered with ``retval=True``, 
    2585         the given value, or a new effective value, should be returned. 
    2586 
    2587        .. seealso:: 
    2588 
    2589            :class:`.AttributeEvents` - background on listener options such 
    2590            as propagation to subclasses. 
    2591 
    2592            :meth:`.AttributeEvents.bulk_replace` 
    2593 
    2594        """ 
    2595 
    2596    def append_wo_mutation( 
    2597        self, 
    2598        target: _O, 
    2599        value: _T, 
    2600        initiator: Event, 
    2601        *, 
    2602        key: EventConstants = NO_KEY, 
    2603    ) -> None: 
    2604        """Receive a collection append event where the collection was not 
    2605        actually mutated. 
    2606 
    2607        This event differs from :meth:`_orm.AttributeEvents.append` in that 
    2608        it is fired off for de-duplicating collections such as sets and 
    2609        dictionaries, when the object already exists in the target collection. 
    2610        The event does not have a return value and the identity of the 
    2611        given object cannot be changed. 
    2612 
    2613        The event is used for cascading objects into a :class:`_orm.Session` 
    2614        when the collection has already been mutated via a backref event. 
    2615 
    2616        :param target: the object instance receiving the event. 
    2617          If the listener is registered with ``raw=True``, this will 
    2618          be the :class:`.InstanceState` object. 
    2619        :param value: the value that would be appended if the object did not 
    2620          already exist in the collection. 
    2621        :param initiator: An instance of :class:`.attributes.Event` 
    2622          representing the initiation of the event.  May be modified 
    2623          from its original value by backref handlers in order to control 
    2624          chained event propagation, as well as be inspected for information 
    2625          about the source of the event. 
    2626        :param key: When the event is established using the 
    2627         :paramref:`.AttributeEvents.include_key` parameter set to 
    2628         True, this will be the key used in the operation, such as 
    2629         ``collection[some_key_or_index] = value``. 
    2630         The parameter is not passed 
    2631         to the event at all if the the 
    2632         :paramref:`.AttributeEvents.include_key` 
    2633         was not used to set up the event; this is to allow backwards 
    2634         compatibility with existing event handlers that don't include the 
    2635         ``key`` parameter. 
    2636 
    2637         .. versionadded:: 2.0 
    2638 
    2639        :return: No return value is defined for this event. 
    2640 
    2641        .. versionadded:: 1.4.15 
    2642 
    2643        """ 
    2644 
    2645    def bulk_replace( 
    2646        self, 
    2647        target: _O, 
    2648        values: Iterable[_T], 
    2649        initiator: Event, 
    2650        *, 
    2651        keys: Optional[Iterable[EventConstants]] = None, 
    2652    ) -> None: 
    2653        """Receive a collection 'bulk replace' event. 
    2654 
    2655        This event is invoked for a sequence of values as they are incoming 
    2656        to a bulk collection set operation, which can be 
    2657        modified in place before the values are treated as ORM objects. 
    2658        This is an "early hook" that runs before the bulk replace routine 
    2659        attempts to reconcile which objects are already present in the 
    2660        collection and which are being removed by the net replace operation. 
    2661 
    2662        It is typical that this method be combined with use of the 
    2663        :meth:`.AttributeEvents.append` event.    When using both of these 
    2664        events, note that a bulk replace operation will invoke 
    2665        the :meth:`.AttributeEvents.append` event for all new items, 
    2666        even after :meth:`.AttributeEvents.bulk_replace` has been invoked 
    2667        for the collection as a whole.  In order to determine if an 
    2668        :meth:`.AttributeEvents.append` event is part of a bulk replace, 
    2669        use the symbol :attr:`~.attributes.OP_BULK_REPLACE` to test the 
    2670        incoming initiator:: 
    2671 
    2672            from sqlalchemy.orm.attributes import OP_BULK_REPLACE 
    2673 
    2674 
    2675            @event.listens_for(SomeObject.collection, "bulk_replace") 
    2676            def process_collection(target, values, initiator): 
    2677                values[:] = [_make_value(value) for value in values] 
    2678 
    2679 
    2680            @event.listens_for(SomeObject.collection, "append", retval=True) 
    2681            def process_collection(target, value, initiator): 
    2682                # make sure bulk_replace didn't already do it 
    2683                if initiator is None or initiator.op is not OP_BULK_REPLACE: 
    2684                    return _make_value(value) 
    2685                else: 
    2686                    return value 
    2687 
    2688        :param target: the object instance receiving the event. 
    2689          If the listener is registered with ``raw=True``, this will 
    2690          be the :class:`.InstanceState` object. 
    2691        :param value: a sequence (e.g. a list) of the values being set.  The 
    2692          handler can modify this list in place. 
    2693        :param initiator: An instance of :class:`.attributes.Event` 
    2694          representing the initiation of the event. 
    2695        :param keys: When the event is established using the 
    2696         :paramref:`.AttributeEvents.include_key` parameter set to 
    2697         True, this will be the sequence of keys used in the operation, 
    2698         typically only for a dictionary update.  The parameter is not passed 
    2699         to the event at all if the the 
    2700         :paramref:`.AttributeEvents.include_key` 
    2701         was not used to set up the event; this is to allow backwards 
    2702         compatibility with existing event handlers that don't include the 
    2703         ``key`` parameter. 
    2704 
    2705         .. versionadded:: 2.0 
    2706 
    2707        .. seealso:: 
    2708 
    2709            :class:`.AttributeEvents` - background on listener options such 
    2710            as propagation to subclasses. 
    2711 
    2712 
    2713        """ 
    2714 
    2715    def remove( 
    2716        self, 
    2717        target: _O, 
    2718        value: _T, 
    2719        initiator: Event, 
    2720        *, 
    2721        key: EventConstants = NO_KEY, 
    2722    ) -> None: 
    2723        """Receive a collection remove event. 
    2724 
    2725        :param target: the object instance receiving the event. 
    2726          If the listener is registered with ``raw=True``, this will 
    2727          be the :class:`.InstanceState` object. 
    2728        :param value: the value being removed. 
    2729        :param initiator: An instance of :class:`.attributes.Event` 
    2730          representing the initiation of the event.  May be modified 
    2731          from its original value by backref handlers in order to control 
    2732          chained event propagation. 
    2733 
    2734        :param key: When the event is established using the 
    2735         :paramref:`.AttributeEvents.include_key` parameter set to 
    2736         True, this will be the key used in the operation, such as 
    2737         ``del collection[some_key_or_index]``.  The parameter is not passed 
    2738         to the event at all if the the 
    2739         :paramref:`.AttributeEvents.include_key` 
    2740         was not used to set up the event; this is to allow backwards 
    2741         compatibility with existing event handlers that don't include the 
    2742         ``key`` parameter. 
    2743 
    2744         .. versionadded:: 2.0 
    2745 
    2746        :return: No return value is defined for this event. 
    2747 
    2748 
    2749        .. seealso:: 
    2750 
    2751            :class:`.AttributeEvents` - background on listener options such 
    2752            as propagation to subclasses. 
    2753 
    2754        """ 
    2755 
    2756    def set( 
    2757        self, target: _O, value: _T, oldvalue: _T, initiator: Event 
    2758    ) -> None: 
    2759        """Receive a scalar set event. 
    2760 
    2761        :param target: the object instance receiving the event. 
    2762          If the listener is registered with ``raw=True``, this will 
    2763          be the :class:`.InstanceState` object. 
    2764        :param value: the value being set.  If this listener 
    2765          is registered with ``retval=True``, the listener 
    2766          function must return this value, or a new value which 
    2767          replaces it. 
    2768        :param oldvalue: the previous value being replaced.  This 
    2769          may also be the symbol ``NEVER_SET`` or ``NO_VALUE``. 
    2770          If the listener is registered with ``active_history=True``, 
    2771          the previous value of the attribute will be loaded from 
    2772          the database if the existing value is currently unloaded 
    2773          or expired. 
    2774        :param initiator: An instance of :class:`.attributes.Event` 
    2775          representing the initiation of the event.  May be modified 
    2776          from its original value by backref handlers in order to control 
    2777          chained event propagation. 
    2778 
    2779        :return: if the event was registered with ``retval=True``, 
    2780         the given value, or a new effective value, should be returned. 
    2781 
    2782        .. seealso:: 
    2783 
    2784            :class:`.AttributeEvents` - background on listener options such 
    2785            as propagation to subclasses. 
    2786 
    2787        """ 
    2788 
    2789    def init_scalar( 
    2790        self, target: _O, value: _T, dict_: Dict[Any, Any] 
    2791    ) -> None: 
    2792        r"""Receive a scalar "init" event. 
    2793 
    2794        This event is invoked when an uninitialized, unpersisted scalar 
    2795        attribute is accessed, e.g. read:: 
    2796 
    2797 
    2798            x = my_object.some_attribute 
    2799 
    2800        The ORM's default behavior when this occurs for an un-initialized 
    2801        attribute is to return the value ``None``; note this differs from 
    2802        Python's usual behavior of raising ``AttributeError``.    The 
    2803        event here can be used to customize what value is actually returned, 
    2804        with the assumption that the event listener would be mirroring 
    2805        a default generator that is configured on the Core 
    2806        :class:`_schema.Column` 
    2807        object as well. 
    2808 
    2809        Since a default generator on a :class:`_schema.Column` 
    2810        might also produce 
    2811        a changing value such as a timestamp, the 
    2812        :meth:`.AttributeEvents.init_scalar` 
    2813        event handler can also be used to **set** the newly returned value, so 
    2814        that a Core-level default generation function effectively fires off 
    2815        only once, but at the moment the attribute is accessed on the 
    2816        non-persisted object.   Normally, no change to the object's state 
    2817        is made when an uninitialized attribute is accessed (much older 
    2818        SQLAlchemy versions did in fact change the object's state). 
    2819 
    2820        If a default generator on a column returned a particular constant, 
    2821        a handler might be used as follows:: 
    2822 
    2823            SOME_CONSTANT = 3.1415926 
    2824 
    2825 
    2826            class MyClass(Base): 
    2827                # ... 
    2828 
    2829                some_attribute = Column(Numeric, default=SOME_CONSTANT) 
    2830 
    2831 
    2832            @event.listens_for( 
    2833                MyClass.some_attribute, "init_scalar", retval=True, propagate=True 
    2834            ) 
    2835            def _init_some_attribute(target, dict_, value): 
    2836                dict_["some_attribute"] = SOME_CONSTANT 
    2837                return SOME_CONSTANT 
    2838 
    2839        Above, we initialize the attribute ``MyClass.some_attribute`` to the 
    2840        value of ``SOME_CONSTANT``.   The above code includes the following 
    2841        features: 
    2842 
    2843        * By setting the value ``SOME_CONSTANT`` in the given ``dict_``, 
    2844          we indicate that this value is to be persisted to the database. 
    2845          This supersedes the use of ``SOME_CONSTANT`` in the default generator 
    2846          for the :class:`_schema.Column`.  The ``active_column_defaults.py`` 
    2847          example given at :ref:`examples_instrumentation` illustrates using 
    2848          the same approach for a changing default, e.g. a timestamp 
    2849          generator.    In this particular example, it is not strictly 
    2850          necessary to do this since ``SOME_CONSTANT`` would be part of the 
    2851          INSERT statement in either case. 
    2852 
    2853        * By establishing the ``retval=True`` flag, the value we return 
    2854          from the function will be returned by the attribute getter. 
    2855          Without this flag, the event is assumed to be a passive observer 
    2856          and the return value of our function is ignored. 
    2857 
    2858        * The ``propagate=True`` flag is significant if the mapped class 
    2859          includes inheriting subclasses, which would also make use of this 
    2860          event listener.  Without this flag, an inheriting subclass will 
    2861          not use our event handler. 
    2862 
    2863        In the above example, the attribute set event 
    2864        :meth:`.AttributeEvents.set` as well as the related validation feature 
    2865        provided by :obj:`_orm.validates` is **not** invoked when we apply our 
    2866        value to the given ``dict_``.  To have these events to invoke in 
    2867        response to our newly generated value, apply the value to the given 
    2868        object as a normal attribute set operation:: 
    2869 
    2870            SOME_CONSTANT = 3.1415926 
    2871 
    2872 
    2873            @event.listens_for( 
    2874                MyClass.some_attribute, "init_scalar", retval=True, propagate=True 
    2875            ) 
    2876            def _init_some_attribute(target, dict_, value): 
    2877                # will also fire off attribute set events 
    2878                target.some_attribute = SOME_CONSTANT 
    2879                return SOME_CONSTANT 
    2880 
    2881        When multiple listeners are set up, the generation of the value 
    2882        is "chained" from one listener to the next by passing the value 
    2883        returned by the previous listener that specifies ``retval=True`` 
    2884        as the ``value`` argument of the next listener. 
    2885 
    2886        :param target: the object instance receiving the event. 
    2887         If the listener is registered with ``raw=True``, this will 
    2888         be the :class:`.InstanceState` object. 
    2889        :param value: the value that is to be returned before this event 
    2890         listener were invoked.  This value begins as the value ``None``, 
    2891         however will be the return value of the previous event handler 
    2892         function if multiple listeners are present. 
    2893        :param dict\_: the attribute dictionary of this mapped object. 
    2894         This is normally the ``__dict__`` of the object, but in all cases 
    2895         represents the destination that the attribute system uses to get 
    2896         at the actual value of this attribute.  Placing the value in this 
    2897         dictionary has the effect that the value will be used in the 
    2898         INSERT statement generated by the unit of work. 
    2899 
    2900 
    2901        .. seealso:: 
    2902 
    2903            :meth:`.AttributeEvents.init_collection` - collection version 
    2904            of this event 
    2905 
    2906            :class:`.AttributeEvents` - background on listener options such 
    2907            as propagation to subclasses. 
    2908 
    2909            :ref:`examples_instrumentation` - see the 
    2910            ``active_column_defaults.py`` example. 
    2911 
    2912        """  # noqa: E501 
    2913 
    2914    def init_collection( 
    2915        self, 
    2916        target: _O, 
    2917        collection: Type[Collection[Any]], 
    2918        collection_adapter: CollectionAdapter, 
    2919    ) -> None: 
    2920        """Receive a 'collection init' event. 
    2921 
    2922        This event is triggered for a collection-based attribute, when 
    2923        the initial "empty collection" is first generated for a blank 
    2924        attribute, as well as for when the collection is replaced with 
    2925        a new one, such as via a set event. 
    2926 
    2927        E.g., given that ``User.addresses`` is a relationship-based 
    2928        collection, the event is triggered here:: 
    2929 
    2930            u1 = User() 
    2931            u1.addresses.append(a1)  #  <- new collection 
    2932 
    2933        and also during replace operations:: 
    2934 
    2935            u1.addresses = [a2, a3]  #  <- new collection 
    2936 
    2937        :param target: the object instance receiving the event. 
    2938         If the listener is registered with ``raw=True``, this will 
    2939         be the :class:`.InstanceState` object. 
    2940        :param collection: the new collection.  This will always be generated 
    2941         from what was specified as 
    2942         :paramref:`_orm.relationship.collection_class`, and will always 
    2943         be empty. 
    2944        :param collection_adapter: the :class:`.CollectionAdapter` that will 
    2945         mediate internal access to the collection. 
    2946 
    2947        .. seealso:: 
    2948 
    2949            :class:`.AttributeEvents` - background on listener options such 
    2950            as propagation to subclasses. 
    2951 
    2952            :meth:`.AttributeEvents.init_scalar` - "scalar" version of this 
    2953            event. 
    2954 
    2955        """ 
    2956 
    2957    def dispose_collection( 
    2958        self, 
    2959        target: _O, 
    2960        collection: Collection[Any], 
    2961        collection_adapter: CollectionAdapter, 
    2962    ) -> None: 
    2963        """Receive a 'collection dispose' event. 
    2964 
    2965        This event is triggered for a collection-based attribute when 
    2966        a collection is replaced, that is:: 
    2967 
    2968            u1.addresses.append(a1) 
    2969 
    2970            u1.addresses = [a2, a3]  # <- old collection is disposed 
    2971 
    2972        The old collection received will contain its previous contents. 
    2973 
    2974        .. seealso:: 
    2975 
    2976            :class:`.AttributeEvents` - background on listener options such 
    2977            as propagation to subclasses. 
    2978 
    2979        """ 
    2980 
    2981    def modified(self, target: _O, initiator: Event) -> None: 
    2982        """Receive a 'modified' event. 
    2983 
    2984        This event is triggered when the :func:`.attributes.flag_modified` 
    2985        function is used to trigger a modify event on an attribute without 
    2986        any specific value being set. 
    2987 
    2988        :param target: the object instance receiving the event. 
    2989          If the listener is registered with ``raw=True``, this will 
    2990          be the :class:`.InstanceState` object. 
    2991 
    2992        :param initiator: An instance of :class:`.attributes.Event` 
    2993          representing the initiation of the event. 
    2994 
    2995        .. seealso:: 
    2996 
    2997            :class:`.AttributeEvents` - background on listener options such 
    2998            as propagation to subclasses. 
    2999 
    3000        """ 
    3001 
    3002 
    3003class QueryEvents(event.Events[Query[Any]]): 
    3004    """Represent events within the construction of a :class:`_query.Query` 
    3005    object. 
    3006 
    3007    .. legacy:: The :class:`_orm.QueryEvents` event methods are legacy 
    3008        as of SQLAlchemy 2.0, and only apply to direct use of the 
    3009        :class:`_orm.Query` object. They are not used for :term:`2.0 style` 
    3010        statements. For events to intercept and modify 2.0 style ORM use, 
    3011        use the :meth:`_orm.SessionEvents.do_orm_execute` hook. 
    3012 
    3013 
    3014    The :class:`_orm.QueryEvents` hooks are now superseded by the 
    3015    :meth:`_orm.SessionEvents.do_orm_execute` event hook. 
    3016 
    3017    """ 
    3018 
    3019    _target_class_doc = "SomeQuery" 
    3020    _dispatch_target = Query 
    3021 
    3022    def before_compile(self, query: Query[Any]) -> None: 
    3023        """Receive the :class:`_query.Query` 
    3024        object before it is composed into a 
    3025        core :class:`_expression.Select` object. 
    3026 
    3027        .. deprecated:: 1.4  The :meth:`_orm.QueryEvents.before_compile` event 
    3028           is superseded by the much more capable 
    3029           :meth:`_orm.SessionEvents.do_orm_execute` hook.   In version 1.4, 
    3030           the :meth:`_orm.QueryEvents.before_compile` event is **no longer 
    3031           used** for ORM-level attribute loads, such as loads of deferred 
    3032           or expired attributes as well as relationship loaders.   See the 
    3033           new examples in :ref:`examples_session_orm_events` which 
    3034           illustrate new ways of intercepting and modifying ORM queries 
    3035           for the most common purpose of adding arbitrary filter criteria. 
    3036 
    3037 
    3038        This event is intended to allow changes to the query given:: 
    3039 
    3040            @event.listens_for(Query, "before_compile", retval=True) 
    3041            def no_deleted(query): 
    3042                for desc in query.column_descriptions: 
    3043                    if desc["type"] is User: 
    3044                        entity = desc["entity"] 
    3045                        query = query.filter(entity.deleted == False) 
    3046                return query 
    3047 
    3048        The event should normally be listened with the ``retval=True`` 
    3049        parameter set, so that the modified query may be returned. 
    3050 
    3051        The :meth:`.QueryEvents.before_compile` event by default 
    3052        will disallow "baked" queries from caching a query, if the event 
    3053        hook returns a new :class:`_query.Query` object. 
    3054        This affects both direct 
    3055        use of the baked query extension as well as its operation within 
    3056        lazy loaders and eager loaders for relationships.  In order to 
    3057        re-establish the query being cached, apply the event adding the 
    3058        ``bake_ok`` flag:: 
    3059 
    3060            @event.listens_for(Query, "before_compile", retval=True, bake_ok=True) 
    3061            def my_event(query): 
    3062                for desc in query.column_descriptions: 
    3063                    if desc["type"] is User: 
    3064                        entity = desc["entity"] 
    3065                        query = query.filter(entity.deleted == False) 
    3066                return query 
    3067 
    3068        When ``bake_ok`` is set to True, the event hook will only be invoked 
    3069        once, and not called for subsequent invocations of a particular query 
    3070        that is being cached. 
    3071 
    3072        .. seealso:: 
    3073 
    3074            :meth:`.QueryEvents.before_compile_update` 
    3075 
    3076            :meth:`.QueryEvents.before_compile_delete` 
    3077 
    3078            :ref:`baked_with_before_compile` 
    3079 
    3080        """  # noqa: E501 
    3081 
    3082    def before_compile_update( 
    3083        self, query: Query[Any], update_context: BulkUpdate 
    3084    ) -> None: 
    3085        """Allow modifications to the :class:`_query.Query` object within 
    3086        :meth:`_query.Query.update`. 
    3087 
    3088        .. deprecated:: 1.4  The :meth:`_orm.QueryEvents.before_compile_update` 
    3089           event is superseded by the much more capable 
    3090           :meth:`_orm.SessionEvents.do_orm_execute` hook. 
    3091 
    3092        Like the :meth:`.QueryEvents.before_compile` event, if the event 
    3093        is to be used to alter the :class:`_query.Query` object, it should 
    3094        be configured with ``retval=True``, and the modified 
    3095        :class:`_query.Query` object returned, as in :: 
    3096 
    3097            @event.listens_for(Query, "before_compile_update", retval=True) 
    3098            def no_deleted(query, update_context): 
    3099                for desc in query.column_descriptions: 
    3100                    if desc["type"] is User: 
    3101                        entity = desc["entity"] 
    3102                        query = query.filter(entity.deleted == False) 
    3103 
    3104                        update_context.values["timestamp"] = datetime.datetime.now( 
    3105                            datetime.UTC 
    3106                        ) 
    3107                return query 
    3108 
    3109        The ``.values`` dictionary of the "update context" object can also 
    3110        be modified in place as illustrated above. 
    3111 
    3112        :param query: a :class:`_query.Query` instance; this is also 
    3113         the ``.query`` attribute of the given "update context" 
    3114         object. 
    3115 
    3116        :param update_context: an "update context" object which is 
    3117         the same kind of object as described in 
    3118         :paramref:`.QueryEvents.after_bulk_update.update_context`. 
    3119         The object has a ``.values`` attribute in an UPDATE context which is 
    3120         the dictionary of parameters passed to :meth:`_query.Query.update`. 
    3121         This 
    3122         dictionary can be modified to alter the VALUES clause of the 
    3123         resulting UPDATE statement. 
    3124 
    3125        .. seealso:: 
    3126 
    3127            :meth:`.QueryEvents.before_compile` 
    3128 
    3129            :meth:`.QueryEvents.before_compile_delete` 
    3130 
    3131 
    3132        """  # noqa: E501 
    3133 
    3134    def before_compile_delete( 
    3135        self, query: Query[Any], delete_context: BulkDelete 
    3136    ) -> None: 
    3137        """Allow modifications to the :class:`_query.Query` object within 
    3138        :meth:`_query.Query.delete`. 
    3139 
    3140        .. deprecated:: 1.4  The :meth:`_orm.QueryEvents.before_compile_delete` 
    3141           event is superseded by the much more capable 
    3142           :meth:`_orm.SessionEvents.do_orm_execute` hook. 
    3143 
    3144        Like the :meth:`.QueryEvents.before_compile` event, this event 
    3145        should be configured with ``retval=True``, and the modified 
    3146        :class:`_query.Query` object returned, as in :: 
    3147 
    3148            @event.listens_for(Query, "before_compile_delete", retval=True) 
    3149            def no_deleted(query, delete_context): 
    3150                for desc in query.column_descriptions: 
    3151                    if desc["type"] is User: 
    3152                        entity = desc["entity"] 
    3153                        query = query.filter(entity.deleted == False) 
    3154                return query 
    3155 
    3156        :param query: a :class:`_query.Query` instance; this is also 
    3157         the ``.query`` attribute of the given "delete context" 
    3158         object. 
    3159 
    3160        :param delete_context: a "delete context" object which is 
    3161         the same kind of object as described in 
    3162         :paramref:`.QueryEvents.after_bulk_delete.delete_context`. 
    3163 
    3164        .. seealso:: 
    3165 
    3166            :meth:`.QueryEvents.before_compile` 
    3167 
    3168            :meth:`.QueryEvents.before_compile_update` 
    3169 
    3170 
    3171        """ 
    3172 
    3173    @classmethod 
    3174    def _listen( 
    3175        cls, 
    3176        event_key: _EventKey[_ET], 
    3177        retval: bool = False, 
    3178        bake_ok: bool = False, 
    3179        **kw: Any, 
    3180    ) -> None: 
    3181        fn = event_key._listen_fn 
    3182 
    3183        if not retval: 
    3184 
    3185            def wrap(*arg: Any, **kw: Any) -> Any: 
    3186                if not retval: 
    3187                    query = arg[0] 
    3188                    fn(*arg, **kw) 
    3189                    return query 
    3190                else: 
    3191                    return fn(*arg, **kw) 
    3192 
    3193            event_key = event_key.with_wrapper(wrap) 
    3194        else: 
    3195            # don't assume we can apply an attribute to the callable 
    3196            def wrap(*arg: Any, **kw: Any) -> Any: 
    3197                return fn(*arg, **kw) 
    3198 
    3199            event_key = event_key.with_wrapper(wrap) 
    3200 
    3201        wrap._bake_ok = bake_ok  # type: ignore [attr-defined] 
    3202 
    3203        event_key.base_listen(**kw) 
    3204 
    3205 
    3206class RegistryEvents(event.Events["registry"]): 
    3207    """Define events specific to :class:`_orm.registry` lifecycle. 
    3208 
    3209    The :class:`_orm.RegistryEvents` class defines events that are specific 
    3210    to the lifecycle and operation of the :class:`_orm.registry` object. 
    3211 
    3212    e.g.:: 
    3213 
    3214        from typing import Any 
    3215 
    3216        from sqlalchemy import event 
    3217        from sqlalchemy.orm import registry 
    3218        from sqlalchemy.orm import TypeResolve 
    3219        from sqlalchemy.types import TypeEngine 
    3220 
    3221        reg = registry() 
    3222 
    3223 
    3224        @event.listens_for(reg, "resolve_type_annotation") 
    3225        def resolve_custom_type( 
    3226            resolve_type: TypeResolve, 
    3227        ) -> TypeEngine[Any] | None: 
    3228            if python_type is MyCustomType: 
    3229                return MyCustomSQLType() 
    3230            return None 
    3231 
    3232    The events defined by :class:`_orm.RegistryEvents` include 
    3233    :meth:`_orm.RegistryEvents.resolve_type_annotation`, 
    3234    :meth:`_orm.RegistryEvents.before_configured`, and 
    3235    :meth:`_orm.RegistryEvents.after_configured`.`.   These events may be 
    3236    applied to a :class:`_orm.registry` object as shown in the preceding 
    3237    example, as well as to a declarative base class directly, which will 
    3238    automtically locate the registry for the event to be applied:: 
    3239 
    3240        from typing import Any 
    3241 
    3242        from sqlalchemy import event 
    3243        from sqlalchemy.orm import DeclarativeBase 
    3244        from sqlalchemy.orm import registry as RegistryType 
    3245        from sqlalchemy.orm import TypeResolve 
    3246        from sqlalchemy.types import TypeEngine 
    3247 
    3248 
    3249        class Base(DeclarativeBase): 
    3250            pass 
    3251 
    3252 
    3253        @event.listens_for(Base, "resolve_type_annotation") 
    3254        def resolve_custom_type( 
    3255            resolve_type: TypeResolve, 
    3256        ) -> TypeEngine[Any] | None: 
    3257            if resolve_type.resolved_type is MyCustomType: 
    3258                return MyCustomSQLType() 
    3259            else: 
    3260                return None 
    3261 
    3262 
    3263        @event.listens_for(Base, "after_configured") 
    3264        def after_base_configured(registry: RegistryType) -> None: 
    3265            print(f"Registry {registry} fully configured") 
    3266 
    3267    .. versionadded:: 2.1 
    3268 
    3269 
    3270    """ 
    3271 
    3272    _target_class_doc = "SomeRegistry" 
    3273    _dispatch_target = decl_api.registry 
    3274 
    3275    @classmethod 
    3276    def _accept_with( 
    3277        cls, 
    3278        target: Any, 
    3279        identifier: str, 
    3280    ) -> Any: 
    3281        if isinstance(target, decl_api.registry): 
    3282            return target 
    3283        elif ( 
    3284            isinstance(target, type) 
    3285            and "_sa_registry" in target.__dict__ 
    3286            and isinstance(target.__dict__["_sa_registry"], decl_api.registry) 
    3287        ): 
    3288            return target._sa_registry  # type: ignore[attr-defined] 
    3289        else: 
    3290            return None 
    3291 
    3292    @classmethod 
    3293    def _listen( 
    3294        cls, 
    3295        event_key: _EventKey["registry"], 
    3296        **kw: Any, 
    3297    ) -> None: 
    3298        identifier = event_key.identifier 
    3299 
    3300        # Only resolve_type_annotation needs retval=True 
    3301        if identifier == "resolve_type_annotation": 
    3302            kw["retval"] = True 
    3303 
    3304        event_key.base_listen(**kw) 
    3305 
    3306    def resolve_type_annotation( 
    3307        self, resolve_type: decl_api.TypeResolve 
    3308    ) -> Optional[Any]: 
    3309        """Intercept and customize type annotation resolution. 
    3310 
    3311        This event is fired when the :class:`_orm.registry` attempts to 
    3312        resolve a Python type annotation to a SQLAlchemy type. This is 
    3313        particularly useful for handling advanced typing scenarios such as 
    3314        nested :pep:`695` type aliases. 
    3315 
    3316        The :meth:`.RegistryEvents.resolve_type_annotation` event automatically 
    3317        sets up ``retval=True`` when the event is set up, so that implementing 
    3318        functions may return a resolved type, or ``None`` to indicate no type 
    3319        was resolved, and the default resolution for the type should proceed. 
    3320 
    3321        :param resolve_type: A :class:`_orm.TypeResolve` object which contains 
    3322         all the relevant information about the type, including a link to the 
    3323         registry and its resolver function. 
    3324 
    3325        :return: A SQLAlchemy type to use for the given Python type.  If 
    3326         ``None`` is returned, the default resolution behavior will proceed 
    3327         from there. 
    3328 
    3329        .. versionadded:: 2.1 
    3330 
    3331        .. seealso:: 
    3332 
    3333            :ref:`orm_declarative_resolve_type_event` 
    3334 
    3335        """ 
    3336 
    3337    def before_configured(self, registry: "registry") -> None: 
    3338        """Called before a series of mappers in this registry are configured. 
    3339 
    3340        This event is invoked each time the :func:`_orm.configure_mappers` 
    3341        function is invoked and this registry has mappers that are part of 
    3342        the configuration process. 
    3343 
    3344        Compared to the :meth:`.MapperEvents.before_configured` event hook, 
    3345        this event is local to the mappers within a specific 
    3346        :class:`_orm.registry` and not for all :class:`.Mapper` objects 
    3347        globally. 
    3348 
    3349        :param registry: The :class:`_orm.registry` instance. 
    3350 
    3351        .. versionadded:: 2.1 
    3352 
    3353        .. seealso:: 
    3354 
    3355            :meth:`.RegistryEvents.after_configured` 
    3356 
    3357            :meth:`.MapperEvents.before_configured` 
    3358 
    3359            :meth:`.MapperEvents.after_configured` 
    3360 
    3361        """ 
    3362 
    3363    def after_configured(self, registry: "registry") -> None: 
    3364        """Called after a series of mappers in this registry are configured. 
    3365 
    3366        This event is invoked each time the :func:`_orm.configure_mappers` 
    3367        function completes and this registry had mappers that were part of 
    3368        the configuration process. 
    3369 
    3370        Compared to the :meth:`.MapperEvents.after_configured` event hook, this 
    3371        event is local to the mappers within a specific :class:`_orm.registry` 
    3372        and not for all :class:`.Mapper` objects globally. 
    3373 
    3374        :param registry: The :class:`_orm.registry` instance. 
    3375 
    3376        .. versionadded:: 2.1 
    3377 
    3378        .. seealso:: 
    3379 
    3380            :meth:`.RegistryEvents.before_configured` 
    3381 
    3382            :meth:`.MapperEvents.before_configured` 
    3383 
    3384            :meth:`.MapperEvents.after_configured` 
    3385 
    3386        """