Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
events.py3253 linesDownload Raw Back to orm
1# orm/events.py
2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors
3# <see AUTHORS file>
4#
5# This module is part of SQLAlchemy and is released under
6# the MIT License: https://www.opensource.org/licenses/mit-license.php
7
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 instrumentation
27from . import interfaces
28from . import mapperlib
29from .attributes import QueryableAttribute
30from .base import _mapper_or_none
31from .base import NO_KEY
32from .instrumentation import ClassManager
33from .instrumentation import InstrumentationFactory
34from .query import BulkDelete
35from .query import BulkUpdate
36from .query import Query
37from .scoping import scoped_session
38from .session import Session
39from .session import sessionmaker
40from .. import event
41from .. import exc
42from .. import util
43from ..event import EventTarget
44from ..event.registry import _ET
45from ..util.compat import inspect_getfullargspec
46
47if TYPE_CHECKING:
48    from weakref import ReferenceType
49
50    from ._typing import _InstanceDict
51    from ._typing import _InternalEntityType
52    from ._typing import _O
53    from ._typing import _T
54    from .attributes import Event
55    from .base import EventConstants
56    from .session import ORMExecuteState
57    from .session import SessionTransaction
58    from .unitofwork import UOWTransaction
59    from ..engine import Connection
60    from ..event.base import _Dispatch
61    from ..event.base import _HasEventsDispatch
62    from ..event.registry import _EventKey
63    from ..orm.collections import CollectionAdapter
64    from ..orm.context import QueryContext
65    from ..orm.decl_api import DeclarativeAttributeIntercept
66    from ..orm.decl_api import DeclarativeMeta
67    from ..orm.mapper import Mapper
68    from ..orm.state import InstanceState
69
70_KT = TypeVar("_KT", bound=Any)
71_ET2 = TypeVar("_ET2", bound=EventTarget)
72
73
74class InstrumentationEvents(event.Events[InstrumentationFactory]):
75    """Events related to class instrumentation events.
76
77    The listeners here support being established against
78    any new style class, that is any object that is a subclass
79    of 'type'.  Events will then be fired off for events
80    against that class.  If the "propagate=True" flag is passed
81    to event.listen(), the event will fire off for subclasses
82    of that class as well.
83
84    The Python ``type`` builtin is also accepted as a target,
85    which when used has the effect of events being emitted
86    for all classes.
87
88    Note the "propagate" flag here is defaulted to ``True``,
89    unlike the other class level events where it defaults
90    to ``False``.  This means that new subclasses will also
91    be the subject of these events, when a listener
92    is established on a superclass.
93
94    """
95
96    _target_class_doc = "SomeBaseClass"
97    _dispatch_target = InstrumentationFactory
98
99    @classmethod
100    def _accept_with(
101        cls,
102        target: Union[
103            InstrumentationFactory,
104            Type[InstrumentationFactory],
105        ],
106        identifier: str,
107    ) -> Optional[
108        Union[
109            InstrumentationFactory,
110            Type[InstrumentationFactory],
111        ]
112    ]:
113        if isinstance(target, type):
114            return _InstrumentationEventsHold(target)  # type: ignore [return-value] # noqa: E501
115        else:
116            return None
117
118    @classmethod
119    def _listen(
120        cls, event_key: _EventKey[_T], propagate: bool = True, **kw: Any
121    ) -> None:
122        target, identifier, fn = (
123            event_key.dispatch_target,
124            event_key.identifier,
125            event_key._listen_fn,
126        )
127
128        def listen(target_cls: type, *arg: Any) -> Optional[Any]:
129            listen_cls = target()
130
131            # if weakref were collected, however this is not something
132            # that normally happens.   it was occurring during test teardown
133            # between mapper/registry/instrumentation_manager, however this
134            # interaction was changed to not rely upon the event system.
135            if listen_cls is None:
136                return None
137
138            if propagate and issubclass(target_cls, listen_cls):
139                return fn(target_cls, *arg)
140            elif not propagate and target_cls is listen_cls:
141                return fn(target_cls, *arg)
142            else:
143                return None
144
145        def remove(ref: ReferenceType[_T]) -> None:
146            key = event.registry._EventKey(  # type: ignore [type-var]
147                None,
148                identifier,
149                listen,
150                instrumentation._instrumentation_factory,
151            )
152            getattr(
153                instrumentation._instrumentation_factory.dispatch, identifier
154            ).remove(key)
155
156        target = weakref.ref(target.class_, remove)
157
158        event_key.with_dispatch_target(
159            instrumentation._instrumentation_factory
160        ).with_wrapper(listen).base_listen(**kw)
161
162    @classmethod
163    def _clear(cls) -> None:
164        super()._clear()
165        instrumentation._instrumentation_factory.dispatch._clear()
166
167    def class_instrument(self, cls: ClassManager[_O]) -> None:
168        """Called after the given class is instrumented.
169
170        To get at the :class:`.ClassManager`, use
171        :func:`.manager_of_class`.
172
173        """
174
175    def class_uninstrument(self, cls: ClassManager[_O]) -> None:
176        """Called before the given class is uninstrumented.
177
178        To get at the :class:`.ClassManager`, use
179        :func:`.manager_of_class`.
180
181        """
182
183    def attribute_instrument(
184        self, cls: ClassManager[_O], key: _KT, inst: _O
185    ) -> None:
186        """Called when an attribute is instrumented."""
187
188
189class _InstrumentationEventsHold:
190    """temporary marker object used to transfer from _accept_with() to
191    _listen() on the InstrumentationEvents class.
192
193    """
194
195    def __init__(self, class_: type) -> None:
196        self.class_ = class_
197
198    dispatch = event.dispatcher(InstrumentationEvents)
199
200
201class InstanceEvents(event.Events[ClassManager[Any]]):
202    """Define events specific to object lifecycle.
203
204    e.g.::
205
206        from sqlalchemy import event
207
208
209        def my_load_listener(target, context):
210            print("on load!")
211
212
213        event.listen(SomeClass, "load", my_load_listener)
214
215    Available targets include:
216
217    * mapped classes
218    * unmapped superclasses of mapped or to-be-mapped classes
219      (using the ``propagate=True`` flag)
220    * :class:`_orm.Mapper` objects
221    * the :class:`_orm.Mapper` class itself indicates listening for all
222      mappers.
223
224    Instance events are closely related to mapper events, but
225    are more specific to the instance and its instrumentation,
226    rather than its system of persistence.
227
228    When using :class:`.InstanceEvents`, several modifiers are
229    available to the :func:`.event.listen` function.
230
231    :param propagate=False: When True, the event listener should
232       be applied to all inheriting classes as well as the
233       class which is the target of this listener.
234    :param raw=False: When True, the "target" argument passed
235       to applicable event listener functions will be the
236       instance's :class:`.InstanceState` management
237       object, rather than the mapped instance itself.
238    :param restore_load_context=False: Applies to the
239       :meth:`.InstanceEvents.load` and :meth:`.InstanceEvents.refresh`
240       events.  Restores the loader context of the object when the event
241       hook is complete, so that ongoing eager load operations continue
242       to target the object appropriately.  A warning is emitted if the
243       object is moved to a new loader context from within one of these
244       events if this flag is not set.
245
246       .. versionadded:: 1.3.14
247
248
249    """
250
251    _target_class_doc = "SomeClass"
252
253    _dispatch_target = ClassManager
254
255    @classmethod
256    def _new_classmanager_instance(
257        cls,
258        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type],
259        classmanager: ClassManager[_O],
260    ) -> None:
261        _InstanceEventsHold.populate(class_, classmanager)
262
263    @classmethod
264    @util.preload_module("sqlalchemy.orm")
265    def _accept_with(
266        cls,
267        target: Union[
268            ClassManager[Any],
269            Type[ClassManager[Any]],
270        ],
271        identifier: str,
272    ) -> Optional[Union[ClassManager[Any], Type[ClassManager[Any]]]]:
273        orm = util.preloaded.orm
274
275        if isinstance(target, ClassManager):
276            return target
277        elif isinstance(target, mapperlib.Mapper):
278            return target.class_manager
279        elif target is orm.mapper:  # type: ignore [attr-defined]
280            util.warn_deprecated(
281                "The `sqlalchemy.orm.mapper()` symbol is deprecated and "
282                "will be removed in a future release. For the mapper-wide "
283                "event target, use the 'sqlalchemy.orm.Mapper' class.",
284                "2.0",
285            )
286            return ClassManager
287        elif isinstance(target, type):
288            if issubclass(target, mapperlib.Mapper):
289                return ClassManager
290            else:
291                manager = instrumentation.opt_manager_of_class(target)
292                if manager:
293                    return manager
294                else:
295                    return _InstanceEventsHold(target)  # type: ignore [return-value] # noqa: E501
296        return None
297
298    @classmethod
299    def _listen(
300        cls,
301        event_key: _EventKey[ClassManager[Any]],
302        raw: bool = False,
303        propagate: bool = False,
304        restore_load_context: bool = False,
305        **kw: Any,
306    ) -> None:
307        target, fn = (event_key.dispatch_target, event_key._listen_fn)
308
309        if not raw or restore_load_context:
310
311            def wrap(
312                state: InstanceState[_O], *arg: Any, **kw: Any
313            ) -> Optional[Any]:
314                if not raw:
315                    target: Any = state.obj()
316                else:
317                    target = state
318                if restore_load_context:
319                    runid = state.runid
320                try:
321                    return fn(target, *arg, **kw)
322                finally:
323                    if restore_load_context:
324                        state.runid = runid
325
326            event_key = event_key.with_wrapper(wrap)
327
328        event_key.base_listen(propagate=propagate, **kw)
329
330        if propagate:
331            for mgr in target.subclass_managers(True):
332                event_key.with_dispatch_target(mgr).base_listen(propagate=True)
333
334    @classmethod
335    def _clear(cls) -> None:
336        super()._clear()
337        _InstanceEventsHold._clear()
338
339    def first_init(self, manager: ClassManager[_O], cls: Type[_O]) -> None:
340        """Called when the first instance of a particular mapping is called.
341
342        This event is called when the ``__init__`` method of a class
343        is called the first time for that particular class.    The event
344        invokes before ``__init__`` actually proceeds as well as before
345        the :meth:`.InstanceEvents.init` event is invoked.
346
347        """
348
349    def init(self, target: _O, args: Any, kwargs: Any) -> None:
350        """Receive an instance when its constructor is called.
351
352        This method is only called during a userland construction of
353        an object, in conjunction with the object's constructor, e.g.
354        its ``__init__`` method.  It is not called when an object is
355        loaded from the database; see the :meth:`.InstanceEvents.load`
356        event in order to intercept a database load.
357
358        The event is called before the actual ``__init__`` constructor
359        of the object is called.  The ``kwargs`` dictionary may be
360        modified in-place in order to affect what is passed to
361        ``__init__``.
362
363        :param target: the mapped instance.  If
364         the event is configured with ``raw=True``, this will
365         instead be the :class:`.InstanceState` state-management
366         object associated with the instance.
367        :param args: positional arguments passed to the ``__init__`` method.
368         This is passed as a tuple and is currently immutable.
369        :param kwargs: keyword arguments passed to the ``__init__`` method.
370         This structure *can* be altered in place.
371
372        .. seealso::
373
374            :meth:`.InstanceEvents.init_failure`
375
376            :meth:`.InstanceEvents.load`
377
378        """
379
380    def init_failure(self, target: _O, args: Any, kwargs: Any) -> None:
381        """Receive an instance when its constructor has been called,
382        and raised an exception.
383
384        This method is only called during a userland construction of
385        an object, in conjunction with the object's constructor, e.g.
386        its ``__init__`` method. It is not called when an object is loaded
387        from the database.
388
389        The event is invoked after an exception raised by the ``__init__``
390        method is caught.  After the event
391        is invoked, the original exception is re-raised outwards, so that
392        the construction of the object still raises an exception.   The
393        actual exception and stack trace raised should be present in
394        ``sys.exc_info()``.
395
396        :param target: the mapped instance.  If
397         the event is configured with ``raw=True``, this will
398         instead be the :class:`.InstanceState` state-management
399         object associated with the instance.
400        :param args: positional arguments that were passed to the ``__init__``
401         method.
402        :param kwargs: keyword arguments that were passed to the ``__init__``
403         method.
404
405        .. seealso::
406
407            :meth:`.InstanceEvents.init`
408
409            :meth:`.InstanceEvents.load`
410
411        """
412
413    def _sa_event_merge_wo_load(
414        self, target: _O, context: QueryContext
415    ) -> None:
416        """receive an object instance after it was the subject of a merge()
417        call, when load=False was passed.
418
419        The target would be the already-loaded object in the Session which
420        would have had its attributes overwritten by the incoming object. This
421        overwrite operation does not use attribute events, instead just
422        populating dict directly. Therefore the purpose of this event is so
423        that extensions like sqlalchemy.ext.mutable know that object state has
424        changed and incoming state needs to be set up for "parents" etc.
425
426        This functionality is acceptable to be made public in a later release.
427
428        .. versionadded:: 1.4.41
429
430        """
431
432    def load(self, target: _O, context: QueryContext) -> None:
433        """Receive an object instance after it has been created via
434        ``__new__``, and after initial attribute population has
435        occurred.
436
437        This typically occurs when the instance is created based on
438        incoming result rows, and is only called once for that
439        instance's lifetime.
440
441        .. warning::
442
443            During a result-row load, this event is invoked when the
444            first row received for this instance is processed.  When using
445            eager loading with collection-oriented attributes, the additional
446            rows that are to be loaded / processed in order to load subsequent
447            collection items have not occurred yet.   This has the effect
448            both that collections will not be fully loaded, as well as that
449            if an operation occurs within this event handler that emits
450            another database load operation for the object, the "loading
451            context" for the object can change and interfere with the
452            existing eager loaders still in progress.
453
454            Examples of what can cause the "loading context" to change within
455            the event handler include, but are not necessarily limited to:
456
457            * accessing deferred attributes that weren't part of the row,
458              will trigger an "undefer" operation and refresh the object
459
460            * accessing attributes on a joined-inheritance subclass that
461              weren't part of the row, will trigger a refresh operation.
462
463            As of SQLAlchemy 1.3.14, a warning is emitted when this occurs. The
464            :paramref:`.InstanceEvents.restore_load_context` option may  be
465            used on the event to prevent this warning; this will ensure that
466            the existing loading context is maintained for the object after the
467            event is called::
468
469                @event.listens_for(SomeClass, "load", restore_load_context=True)
470                def on_load(instance, context):
471                    instance.some_unloaded_attribute
472
473            .. versionchanged:: 1.3.14 Added
474               :paramref:`.InstanceEvents.restore_load_context`
475               and :paramref:`.SessionEvents.restore_load_context` flags which
476               apply to "on load" events, which will ensure that the loading
477               context for an object is restored when the event hook is
478               complete; a warning is emitted if the load context of the object
479               changes without this flag being set.
480
481
482        The :meth:`.InstanceEvents.load` event is also available in a
483        class-method decorator format called :func:`_orm.reconstructor`.
484
485        :param target: the mapped instance.  If
486         the event is configured with ``raw=True``, this will
487         instead be the :class:`.InstanceState` state-management
488         object associated with the instance.
489        :param context: the :class:`.QueryContext` corresponding to the
490         current :class:`_query.Query` in progress.  This argument may be
491         ``None`` if the load does not correspond to a :class:`_query.Query`,
492         such as during :meth:`.Session.merge`.
493
494        .. seealso::
495
496            :ref:`mapped_class_load_events`
497
498            :meth:`.InstanceEvents.init`
499
500            :meth:`.InstanceEvents.refresh`
501
502            :meth:`.SessionEvents.loaded_as_persistent`
503
504        """  # noqa: E501
505
506    def refresh(
507        self, target: _O, context: QueryContext, attrs: Optional[Iterable[str]]
508    ) -> None:
509        """Receive an object instance after one or more attributes have
510        been refreshed from a query.
511
512        Contrast this to the :meth:`.InstanceEvents.load` method, which
513        is invoked when the object is first loaded from a query.
514
515        .. note:: This event is invoked within the loader process before
516           eager loaders may have been completed, and the object's state may
517           not be complete.  Additionally, invoking row-level refresh
518           operations on the object will place the object into a new loader
519           context, interfering with the existing load context.   See the note
520           on :meth:`.InstanceEvents.load` for background on making use of the
521           :paramref:`.InstanceEvents.restore_load_context` parameter, in
522           order to resolve this scenario.
523
524        :param target: the mapped instance.  If
525         the event is configured with ``raw=True``, this will
526         instead be the :class:`.InstanceState` state-management
527         object associated with the instance.
528        :param context: the :class:`.QueryContext` corresponding to the
529         current :class:`_query.Query` in progress.
530        :param attrs: sequence of attribute names which
531         were populated, or None if all column-mapped, non-deferred
532         attributes were populated.
533
534        .. seealso::
535
536            :ref:`mapped_class_load_events`
537
538            :meth:`.InstanceEvents.load`
539
540        """
541
542    def refresh_flush(
543        self,
544        target: _O,
545        flush_context: UOWTransaction,
546        attrs: Optional[Iterable[str]],
547    ) -> None:
548        """Receive an object instance after one or more attributes that
549        contain a column-level default or onupdate handler have been refreshed
550        during persistence of the object's state.
551
552        This event is the same as :meth:`.InstanceEvents.refresh` except
553        it is invoked within the unit of work flush process, and includes
554        only non-primary-key columns that have column level default or
555        onupdate handlers, including Python callables as well as server side
556        defaults and triggers which may be fetched via the RETURNING clause.
557
558        .. note::
559
560            While the :meth:`.InstanceEvents.refresh_flush` event is triggered
561            for an object that was INSERTed as well as for an object that was
562            UPDATEd, the event is geared primarily  towards the UPDATE process;
563            it is mostly an internal artifact that INSERT actions can also
564            trigger this event, and note that **primary key columns for an
565            INSERTed row are explicitly omitted** from this event.  In order to
566            intercept the newly INSERTed state of an object, the
567            :meth:`.SessionEvents.pending_to_persistent` and
568            :meth:`.MapperEvents.after_insert` are better choices.
569
570        :param target: the mapped instance.  If
571         the event is configured with ``raw=True``, this will
572         instead be the :class:`.InstanceState` state-management
573         object associated with the instance.
574        :param flush_context: Internal :class:`.UOWTransaction` object
575         which handles the details of the flush.
576        :param attrs: sequence of attribute names which
577         were populated.
578
579        .. seealso::
580
581            :ref:`mapped_class_load_events`
582
583            :ref:`orm_server_defaults`
584
585            :ref:`metadata_defaults_toplevel`
586
587        """
588
589    def expire(self, target: _O, attrs: Optional[Iterable[str]]) -> None:
590        """Receive an object instance after its attributes or some subset
591        have been expired.
592
593        'keys' is a list of attribute names.  If None, the entire
594        state was expired.
595
596        :param target: the mapped instance.  If
597         the event is configured with ``raw=True``, this will
598         instead be the :class:`.InstanceState` state-management
599         object associated with the instance.
600        :param attrs: sequence of attribute
601         names which were expired, or None if all attributes were
602         expired.
603
604        """
605
606    def pickle(self, target: _O, state_dict: _InstanceDict) -> None:
607        """Receive an object instance when its associated state is
608        being pickled.
609
610        :param target: the mapped instance.  If
611         the event is configured with ``raw=True``, this will
612         instead be the :class:`.InstanceState` state-management
613         object associated with the instance.
614        :param state_dict: the dictionary returned by
615         :class:`.InstanceState.__getstate__`, containing the state
616         to be pickled.
617
618        """
619
620    def unpickle(self, target: _O, state_dict: _InstanceDict) -> None:
621        """Receive an object instance after its associated state has
622        been unpickled.
623
624        :param target: the mapped instance.  If
625         the event is configured with ``raw=True``, this will
626         instead be the :class:`.InstanceState` state-management
627         object associated with the instance.
628        :param state_dict: the dictionary sent to
629         :class:`.InstanceState.__setstate__`, containing the state
630         dictionary which was pickled.
631
632        """
633
634
635class _EventsHold(event.RefCollection[_ET]):
636    """Hold onto listeners against unmapped, uninstrumented classes.
637
638    Establish _listen() for that class' mapper/instrumentation when
639    those objects are created for that class.
640
641    """
642
643    all_holds: weakref.WeakKeyDictionary[Any, Any]
644
645    def __init__(
646        self,
647        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type],
648    ) -> None:
649        self.class_ = class_
650
651    @classmethod
652    def _clear(cls) -> None:
653        cls.all_holds.clear()
654
655    class HoldEvents(Generic[_ET2]):
656        _dispatch_target: Optional[Type[_ET2]] = None
657
658        @classmethod
659        def _listen(
660            cls,
661            event_key: _EventKey[_ET2],
662            raw: bool = False,
663            propagate: bool = False,
664            retval: bool = False,
665            **kw: Any,
666        ) -> None:
667            target = event_key.dispatch_target
668
669            if target.class_ in target.all_holds:
670                collection = target.all_holds[target.class_]
671            else:
672                collection = target.all_holds[target.class_] = {}
673
674            event.registry._stored_in_collection(event_key, target)
675            collection[event_key._key] = (
676                event_key,
677                raw,
678                propagate,
679                retval,
680                kw,
681            )
682
683            if propagate:
684                stack = list(target.class_.__subclasses__())
685                while stack:
686                    subclass = stack.pop(0)
687                    stack.extend(subclass.__subclasses__())
688                    subject = target.resolve(subclass)
689                    if subject is not None:
690                        # we are already going through __subclasses__()
691                        # so leave generic propagate flag False
692                        event_key.with_dispatch_target(subject).listen(
693                            raw=raw, propagate=False, retval=retval, **kw
694                        )
695
696    def remove(self, event_key: _EventKey[_ET]) -> None:
697        target = event_key.dispatch_target
698
699        if isinstance(target, _EventsHold):
700            collection = target.all_holds[target.class_]
701            del collection[event_key._key]
702
703    @classmethod
704    def populate(
705        cls,
706        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type],
707        subject: Union[ClassManager[_O], Mapper[_O]],
708    ) -> None:
709        for subclass in class_.__mro__:
710            if subclass in cls.all_holds:
711                collection = cls.all_holds[subclass]
712                for (
713                    event_key,
714                    raw,
715                    propagate,
716                    retval,
717                    kw,
718                ) in collection.values():
719                    if propagate or subclass is class_:
720                        # since we can't be sure in what order different
721                        # classes in a hierarchy are triggered with
722                        # populate(), we rely upon _EventsHold for all event
723                        # assignment, instead of using the generic propagate
724                        # flag.
725                        event_key.with_dispatch_target(subject).listen(
726                            raw=raw, propagate=False, retval=retval, **kw
727                        )
728
729
730class _InstanceEventsHold(_EventsHold[_ET]):
731    all_holds: weakref.WeakKeyDictionary[Any, Any] = (
732        weakref.WeakKeyDictionary()
733    )
734
735    def resolve(self, class_: Type[_O]) -> Optional[ClassManager[_O]]:
736        return instrumentation.opt_manager_of_class(class_)
737
738    # this fails on pyright if you use Any.  Fails on mypy if you use _ET
739    class HoldInstanceEvents(_EventsHold.HoldEvents[_ET], InstanceEvents):  # type: ignore[valid-type,misc] # noqa: E501
740        pass
741
742    dispatch = event.dispatcher(HoldInstanceEvents)
743
744
745class MapperEvents(event.Events[mapperlib.Mapper[Any]]):
746    """Define events specific to mappings.
747
748    e.g.::
749
750        from sqlalchemy import event
751
752
753        def my_before_insert_listener(mapper, connection, target):
754            # execute a stored procedure upon INSERT,
755            # apply the value to the row to be inserted
756            target.calculated_value = connection.execute(
757                text("select my_special_function(%d)" % target.special_number)
758            ).scalar()
759
760
761        # associate the listener function with SomeClass,
762        # to execute during the "before_insert" hook
763        event.listen(SomeClass, "before_insert", my_before_insert_listener)
764
765    Available targets include:
766
767    * mapped classes
768    * unmapped superclasses of mapped or to-be-mapped classes
769      (using the ``propagate=True`` flag)
770    * :class:`_orm.Mapper` objects
771    * the :class:`_orm.Mapper` class itself indicates listening for all
772      mappers.
773
774    Mapper events provide hooks into critical sections of the
775    mapper, including those related to object instrumentation,
776    object loading, and object persistence. In particular, the
777    persistence methods :meth:`~.MapperEvents.before_insert`,
778    and :meth:`~.MapperEvents.before_update` are popular
779    places to augment the state being persisted - however, these
780    methods operate with several significant restrictions. The
781    user is encouraged to evaluate the
782    :meth:`.SessionEvents.before_flush` and
783    :meth:`.SessionEvents.after_flush` methods as more
784    flexible and user-friendly hooks in which to apply
785    additional database state during a flush.
786
787    When using :class:`.MapperEvents`, several modifiers are
788    available to the :func:`.event.listen` function.
789
790    :param propagate=False: When True, the event listener should
791       be applied to all inheriting mappers and/or the mappers of
792       inheriting classes, as well as any
793       mapper which is the target of this listener.
794    :param raw=False: When True, the "target" argument passed
795       to applicable event listener functions will be the
796       instance's :class:`.InstanceState` management
797       object, rather than the mapped instance itself.
798    :param retval=False: when True, the user-defined event function
799       must have a return value, the purpose of which is either to
800       control subsequent event propagation, or to otherwise alter
801       the operation in progress by the mapper.   Possible return
802       values are:
803
804       * ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event
805         processing normally.
806       * ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent
807         event handlers in the chain.
808       * other values - the return value specified by specific listeners.
809
810    """
811
812    _target_class_doc = "SomeClass"
813    _dispatch_target = mapperlib.Mapper
814
815    @classmethod
816    def _new_mapper_instance(
817        cls,
818        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type],
819        mapper: Mapper[_O],
820    ) -> None:
821        _MapperEventsHold.populate(class_, mapper)
822
823    @classmethod
824    @util.preload_module("sqlalchemy.orm")
825    def _accept_with(
826        cls,
827        target: Union[mapperlib.Mapper[Any], Type[mapperlib.Mapper[Any]]],
828        identifier: str,
829    ) -> Optional[Union[mapperlib.Mapper[Any], Type[mapperlib.Mapper[Any]]]]:
830        orm = util.preloaded.orm
831
832        if target is orm.mapper:  # type: ignore [attr-defined]
833            util.warn_deprecated(
834                "The `sqlalchemy.orm.mapper()` symbol is deprecated and "
835                "will be removed in a future release. For the mapper-wide "
836                "event target, use the 'sqlalchemy.orm.Mapper' class.",
837                "2.0",
838            )
839            return mapperlib.Mapper
840        elif isinstance(target, type):
841            if issubclass(target, mapperlib.Mapper):
842                return target
843            else:
844                mapper = _mapper_or_none(target)
845                if mapper is not None:
846                    return mapper
847                else:
848                    return _MapperEventsHold(target)
849        else:
850            return target
851
852    @classmethod
853    def _listen(
854        cls,
855        event_key: _EventKey[_ET],
856        raw: bool = False,
857        retval: bool = False,
858        propagate: bool = False,
859        **kw: Any,
860    ) -> None:
861        target, identifier, fn = (
862            event_key.dispatch_target,
863            event_key.identifier,
864            event_key._listen_fn,
865        )
866
867        if (
868            identifier in ("before_configured", "after_configured")
869            and target is not mapperlib.Mapper
870        ):
871            util.warn(
872                "'before_configured' and 'after_configured' ORM events "
873                "only invoke with the Mapper class "
874                "as the target."
875            )
876
877        if not raw or not retval:
878            if not raw:
879                meth = getattr(cls, identifier)
880                try:
881                    target_index = (
882                        inspect_getfullargspec(meth)[0].index("target") - 1
883                    )
884                except ValueError:
885                    target_index = None
886
887            def wrap(*arg: Any, **kw: Any) -> Any:
888                if not raw and target_index is not None:
889                    arg = list(arg)  # type: ignore [assignment]
890                    arg[target_index] = arg[target_index].obj()  # type: ignore [index] # noqa: E501
891                if not retval:
892                    fn(*arg, **kw)
893                    return interfaces.EXT_CONTINUE
894                else:
895                    return fn(*arg, **kw)
896
897            event_key = event_key.with_wrapper(wrap)
898
899        if propagate:
900            for mapper in target.self_and_descendants:
901                event_key.with_dispatch_target(mapper).base_listen(
902                    propagate=True, **kw
903                )
904        else:
905            event_key.base_listen(**kw)
906
907    @classmethod
908    def _clear(cls) -> None:
909        super()._clear()
910        _MapperEventsHold._clear()
911
912    def instrument_class(self, mapper: Mapper[_O], class_: Type[_O]) -> None:
913        r"""Receive a class when the mapper is first constructed,
914        before instrumentation is applied to the mapped class.
915
916        This event is the earliest phase of mapper construction.
917        Most attributes of the mapper are not yet initialized.   To
918        receive an event within initial mapper construction where basic
919        state is available such as the :attr:`_orm.Mapper.attrs` collection,
920        the :meth:`_orm.MapperEvents.after_mapper_constructed` event may
921        be a better choice.
922
923        This listener can either be applied to the :class:`_orm.Mapper`
924        class overall, or to any un-mapped class which serves as a base
925        for classes that will be mapped (using the ``propagate=True`` flag)::
926
927            Base = declarative_base()
928
929
930            @event.listens_for(Base, "instrument_class", propagate=True)
931            def on_new_class(mapper, cls_):
932                "..."
933
934        :param mapper: the :class:`_orm.Mapper` which is the target
935         of this event.
936        :param class\_: the mapped class.
937
938        .. seealso::
939
940            :meth:`_orm.MapperEvents.after_mapper_constructed`
941
942        """
943
944    def after_mapper_constructed(
945        self, mapper: Mapper[_O], class_: Type[_O]
946    ) -> None:
947        """Receive a class and mapper when the :class:`_orm.Mapper` has been
948        fully constructed.
949
950        This event is called after the initial constructor for
951        :class:`_orm.Mapper` completes.  This occurs after the
952        :meth:`_orm.MapperEvents.instrument_class` event and after the
953        :class:`_orm.Mapper` has done an initial pass of its arguments
954        to generate its collection of :class:`_orm.MapperProperty` objects,
955        which are accessible via the :meth:`_orm.Mapper.get_property`
956        method and the :attr:`_orm.Mapper.iterate_properties` attribute.
957
958        This event differs from the
959        :meth:`_orm.MapperEvents.before_mapper_configured` event in that it
960        is invoked within the constructor for :class:`_orm.Mapper`, rather
961        than within the :meth:`_orm.registry.configure` process.   Currently,
962        this event is the only one which is appropriate for handlers that
963        wish to create additional mapped classes in response to the
964        construction of this :class:`_orm.Mapper`, which will be part of the
965        same configure step when :meth:`_orm.registry.configure` next runs.
966
967        .. versionadded:: 2.0.2
968
969        .. seealso::
970
971            :ref:`examples_versioning` - an example which illustrates the use
972            of the :meth:`_orm.MapperEvents.before_mapper_configured`
973            event to create new mappers to record change-audit histories on
974            objects.
975
976        """
977
978    @event._omit_standard_example
979    def before_mapper_configured(
980        self, mapper: Mapper[_O], class_: Type[_O]
981    ) -> None:
982        """Called right before a specific mapper is to be configured.
983
984        The :meth:`.MapperEvents.before_mapper_configured` event is invoked
985        for each mapper that is encountered when the
986        :func:`_orm.configure_mappers` function proceeds through the current
987        list of not-yet-configured mappers.   It is similar to the
988        :meth:`.MapperEvents.mapper_configured` event, except that it's invoked
989        right before the configuration occurs, rather than afterwards.
990
991        The :meth:`.MapperEvents.before_mapper_configured` event includes
992        the special capability where it can force the configure step for a
993        specific mapper to be skipped; to use this feature, establish
994        the event using the ``retval=True`` parameter and return
995        the :attr:`.orm.interfaces.EXT_SKIP` symbol to indicate the mapper
996        should be left unconfigured::
997
998            from sqlalchemy import event
999            from sqlalchemy.orm import EXT_SKIP
1000            from sqlalchemy.orm import DeclarativeBase
1001
1002
1003            class DontConfigureBase(DeclarativeBase):
1004                pass
1005
1006
1007            @event.listens_for(
1008                DontConfigureBase,
1009                "before_mapper_configured",
1010                # support return values for the event
1011                retval=True,
1012                # propagate the listener to all subclasses of
1013                # DontConfigureBase
1014                propagate=True,
1015            )
1016            def dont_configure(mapper, cls):
1017                return EXT_SKIP
1018
1019        .. seealso::
1020
1021            :meth:`.MapperEvents.before_configured`
1022
1023            :meth:`.MapperEvents.after_configured`
1024
1025            :meth:`.MapperEvents.mapper_configured`
1026
1027        """
1028
1029    def mapper_configured(self, mapper: Mapper[_O], class_: Type[_O]) -> None:
1030        r"""Called when a specific mapper has completed its own configuration
1031        within the scope of the :func:`.configure_mappers` call.
1032
1033        The :meth:`.MapperEvents.mapper_configured` event is invoked
1034        for each mapper that is encountered when the
1035        :func:`_orm.configure_mappers` function proceeds through the current
1036        list of not-yet-configured mappers.
1037        :func:`_orm.configure_mappers` is typically invoked
1038        automatically as mappings are first used, as well as each time
1039        new mappers have been made available and new mapper use is
1040        detected.
1041
1042        When the event is called, the mapper should be in its final
1043        state, but **not including backrefs** that may be invoked from
1044        other mappers; they might still be pending within the
1045        configuration operation.    Bidirectional relationships that
1046        are instead configured via the
1047        :paramref:`.orm.relationship.back_populates` argument
1048        *will* be fully available, since this style of relationship does not
1049        rely upon other possibly-not-configured mappers to know that they
1050        exist.
1051
1052        For an event that is guaranteed to have **all** mappers ready
1053        to go including backrefs that are defined only on other
1054        mappings, use the :meth:`.MapperEvents.after_configured`
1055        event; this event invokes only after all known mappings have been
1056        fully configured.
1057
1058        The :meth:`.MapperEvents.mapper_configured` event, unlike the
1059        :meth:`.MapperEvents.before_configured` or
1060        :meth:`.MapperEvents.after_configured` events, is called for each
1061        mapper/class individually, and the mapper is passed to the event
1062        itself.  It also is called exactly once for a particular mapper.  The
1063        event is therefore useful for configurational steps that benefit from
1064        being invoked just once on a specific mapper basis, which don't require
1065        that "backref" configurations are necessarily ready yet.
1066
1067        :param mapper: the :class:`_orm.Mapper` which is the target
1068         of this event.
1069        :param class\_: the mapped class.
1070
1071        .. seealso::
1072
1073            :meth:`.MapperEvents.before_configured`
1074
1075            :meth:`.MapperEvents.after_configured`
1076
1077            :meth:`.MapperEvents.before_mapper_configured`
1078
1079        """
1080        # TODO: need coverage for this event
1081
1082    @event._omit_standard_example
1083    def before_configured(self) -> None:
1084        """Called before a series of mappers have been configured.
1085
1086        The :meth:`.MapperEvents.before_configured` event is invoked
1087        each time the :func:`_orm.configure_mappers` function is
1088        invoked, before the function has done any of its work.
1089        :func:`_orm.configure_mappers` is typically invoked
1090        automatically as mappings are first used, as well as each time
1091        new mappers have been made available and new mapper use is
1092        detected.
1093
1094        Similar events to this one include
1095        :meth:`.MapperEvents.after_configured`, which is invoked after a series
1096        of mappers has been configured, as well as
1097        :meth:`.MapperEvents.before_mapper_configured` and
1098        :meth:`.MapperEvents.mapper_configured`, which are both invoked on a
1099        per-mapper basis.
1100
1101        This event can **only** be applied to the :class:`_orm.Mapper` class,
1102        and not to individual mappings or mapped classes::
1103
1104            from sqlalchemy.orm import Mapper
1105
1106
1107            @event.listens_for(Mapper, "before_configured")
1108            def go(): ...
1109
1110        Typically, this event is called once per application, but in practice
1111        may be called more than once, any time new mappers are to be affected
1112        by a :func:`_orm.configure_mappers` call.   If new mappings are
1113        constructed after existing ones have already been used, this event will
1114        likely be called again.
1115
1116        .. seealso::
1117
1118            :meth:`.MapperEvents.before_mapper_configured`
1119
1120            :meth:`.MapperEvents.mapper_configured`
1121
1122            :meth:`.MapperEvents.after_configured`
1123
1124        """
1125
1126    @event._omit_standard_example
1127    def after_configured(self) -> None:
1128        """Called after a series of mappers have been configured.
1129
1130        The :meth:`.MapperEvents.after_configured` event is invoked
1131        each time the :func:`_orm.configure_mappers` function is
1132        invoked, after the function has completed its work.
1133        :func:`_orm.configure_mappers` is typically invoked
1134        automatically as mappings are first used, as well as each time
1135        new mappers have been made available and new mapper use is
1136        detected.
1137
1138        Similar events to this one include
1139        :meth:`.MapperEvents.before_configured`, which is invoked before a
1140        series of mappers are configured, as well as
1141        :meth:`.MapperEvents.before_mapper_configured` and
1142        :meth:`.MapperEvents.mapper_configured`, which are both invoked on a
1143        per-mapper basis.
1144
1145        This event can **only** be applied to the :class:`_orm.Mapper` class,
1146        and not to individual mappings or mapped classes::
1147
1148            from sqlalchemy.orm import Mapper
1149
1150
1151            @event.listens_for(Mapper, "after_configured")
1152            def go(): ...
1153
1154        Typically, this event is called once per application, but in practice
1155        may be called more than once, any time new mappers are to be affected
1156        by a :func:`_orm.configure_mappers` call.   If new mappings are
1157        constructed after existing ones have already been used, this event will
1158        likely be called again.
1159
1160        .. seealso::
1161
1162            :meth:`.MapperEvents.before_mapper_configured`
1163
1164            :meth:`.MapperEvents.mapper_configured`
1165
1166            :meth:`.MapperEvents.before_configured`
1167
1168        """
1169
1170    def before_insert(
1171        self, mapper: Mapper[_O], connection: Connection, target: _O
1172    ) -> None:
1173        """Receive an object instance before an INSERT statement
1174        is emitted corresponding to that instance.
1175
1176        .. note:: this event **only** applies to the
1177           :ref:`session flush operation <session_flushing>`
1178           and does **not** apply to the ORM DML operations described at
1179           :ref:`orm_expression_update_delete`.  To intercept ORM
1180           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`.
1181
1182        This event is used to modify local, non-object related
1183        attributes on the instance before an INSERT occurs, as well
1184        as to emit additional SQL statements on the given
1185        connection.
1186
1187        The event is often called for a batch of objects of the
1188        same class before their INSERT statements are emitted at
1189        once in a later step. In the extremely rare case that
1190        this is not desirable, the :class:`_orm.Mapper` object can be
1191        configured with ``batch=False``, which will cause
1192        batches of instances to be broken up into individual
1193        (and more poorly performing) event->persist->event
1194        steps.
1195
1196        .. warning::
1197
1198            Mapper-level flush events only allow **very limited operations**,
1199            on attributes local to the row being operated upon only,
1200            as well as allowing any SQL to be emitted on the given

Showing the first 1,200 of 3253 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai