Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
events.py3260 linesDownload Raw Back to orm
1# orm/events.py
2# Copyright (C) 2005-2024 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.
9
10"""
11from __future__ import annotations
12
13from typing import Any
14from typing import Callable
15from typing import Collection
16from typing import Dict
17from typing import Generic
18from typing import Iterable
19from typing import Optional
20from typing import Sequence
21from typing import Set
22from typing import Type
23from typing import TYPE_CHECKING
24from typing import TypeVar
25from typing import Union
26import weakref
27
28from . import instrumentation
29from . import interfaces
30from . import mapperlib
31from .attributes import QueryableAttribute
32from .base import _mapper_or_none
33from .base import NO_KEY
34from .instrumentation import ClassManager
35from .instrumentation import InstrumentationFactory
36from .query import BulkDelete
37from .query import BulkUpdate
38from .query import Query
39from .scoping import scoped_session
40from .session import Session
41from .session import sessionmaker
42from .. import event
43from .. import exc
44from .. import util
45from ..event import EventTarget
46from ..event.registry import _ET
47from ..util.compat import inspect_getfullargspec
48
49if TYPE_CHECKING:
50    from weakref import ReferenceType
51
52    from ._typing import _InstanceDict
53    from ._typing import _InternalEntityType
54    from ._typing import _O
55    from ._typing import _T
56    from .attributes import Event
57    from .base import EventConstants
58    from .session import ORMExecuteState
59    from .session import SessionTransaction
60    from .unitofwork import UOWTransaction
61    from ..engine import Connection
62    from ..event.base import _Dispatch
63    from ..event.base import _HasEventsDispatch
64    from ..event.registry import _EventKey
65    from ..orm.collections import CollectionAdapter
66    from ..orm.context import QueryContext
67    from ..orm.decl_api import DeclarativeAttributeIntercept
68    from ..orm.decl_api import DeclarativeMeta
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        def my_load_listener(target, context):
211            print("on load!")
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(
470                    SomeClass, "load", restore_load_context=True)
471                def on_load(instance, context):
472                    instance.some_unloaded_attribute
473
474            .. versionchanged:: 1.3.14 Added
475               :paramref:`.InstanceEvents.restore_load_context`
476               and :paramref:`.SessionEvents.restore_load_context` flags which
477               apply to "on load" events, which will ensure that the loading
478               context for an object is restored when the event hook is
479               complete; a warning is emitted if the load context of the object
480               changes without this flag being set.
481
482
483        The :meth:`.InstanceEvents.load` event is also available in a
484        class-method decorator format called :func:`_orm.reconstructor`.
485
486        :param target: the mapped instance.  If
487         the event is configured with ``raw=True``, this will
488         instead be the :class:`.InstanceState` state-management
489         object associated with the instance.
490        :param context: the :class:`.QueryContext` corresponding to the
491         current :class:`_query.Query` in progress.  This argument may be
492         ``None`` if the load does not correspond to a :class:`_query.Query`,
493         such as during :meth:`.Session.merge`.
494
495        .. seealso::
496
497            :ref:`mapped_class_load_events`
498
499            :meth:`.InstanceEvents.init`
500
501            :meth:`.InstanceEvents.refresh`
502
503            :meth:`.SessionEvents.loaded_as_persistent`
504
505        """
506
507    def refresh(
508        self, target: _O, context: QueryContext, attrs: Optional[Iterable[str]]
509    ) -> None:
510        """Receive an object instance after one or more attributes have
511        been refreshed from a query.
512
513        Contrast this to the :meth:`.InstanceEvents.load` method, which
514        is invoked when the object is first loaded from a query.
515
516        .. note:: This event is invoked within the loader process before
517           eager loaders may have been completed, and the object's state may
518           not be complete.  Additionally, invoking row-level refresh
519           operations on the object will place the object into a new loader
520           context, interfering with the existing load context.   See the note
521           on :meth:`.InstanceEvents.load` for background on making use of the
522           :paramref:`.InstanceEvents.restore_load_context` parameter, in
523           order to resolve this scenario.
524
525        :param target: the mapped instance.  If
526         the event is configured with ``raw=True``, this will
527         instead be the :class:`.InstanceState` state-management
528         object associated with the instance.
529        :param context: the :class:`.QueryContext` corresponding to the
530         current :class:`_query.Query` in progress.
531        :param attrs: sequence of attribute names which
532         were populated, or None if all column-mapped, non-deferred
533         attributes were populated.
534
535        .. seealso::
536
537            :ref:`mapped_class_load_events`
538
539            :meth:`.InstanceEvents.load`
540
541        """
542
543    def refresh_flush(
544        self,
545        target: _O,
546        flush_context: UOWTransaction,
547        attrs: Optional[Iterable[str]],
548    ) -> None:
549        """Receive an object instance after one or more attributes that
550        contain a column-level default or onupdate handler have been refreshed
551        during persistence of the object's state.
552
553        This event is the same as :meth:`.InstanceEvents.refresh` except
554        it is invoked within the unit of work flush process, and includes
555        only non-primary-key columns that have column level default or
556        onupdate handlers, including Python callables as well as server side
557        defaults and triggers which may be fetched via the RETURNING clause.
558
559        .. note::
560
561            While the :meth:`.InstanceEvents.refresh_flush` event is triggered
562            for an object that was INSERTed as well as for an object that was
563            UPDATEd, the event is geared primarily  towards the UPDATE process;
564            it is mostly an internal artifact that INSERT actions can also
565            trigger this event, and note that **primary key columns for an
566            INSERTed row are explicitly omitted** from this event.  In order to
567            intercept the newly INSERTed state of an object, the
568            :meth:`.SessionEvents.pending_to_persistent` and
569            :meth:`.MapperEvents.after_insert` are better choices.
570
571        :param target: the mapped instance.  If
572         the event is configured with ``raw=True``, this will
573         instead be the :class:`.InstanceState` state-management
574         object associated with the instance.
575        :param flush_context: Internal :class:`.UOWTransaction` object
576         which handles the details of the flush.
577        :param attrs: sequence of attribute names which
578         were populated.
579
580        .. seealso::
581
582            :ref:`mapped_class_load_events`
583
584            :ref:`orm_server_defaults`
585
586            :ref:`metadata_defaults_toplevel`
587
588        """
589
590    def expire(self, target: _O, attrs: Optional[Iterable[str]]) -> None:
591        """Receive an object instance after its attributes or some subset
592        have been expired.
593
594        'keys' is a list of attribute names.  If None, the entire
595        state was expired.
596
597        :param target: the mapped instance.  If
598         the event is configured with ``raw=True``, this will
599         instead be the :class:`.InstanceState` state-management
600         object associated with the instance.
601        :param attrs: sequence of attribute
602         names which were expired, or None if all attributes were
603         expired.
604
605        """
606
607    def pickle(self, target: _O, state_dict: _InstanceDict) -> None:
608        """Receive an object instance when its associated state is
609        being pickled.
610
611        :param target: the mapped instance.  If
612         the event is configured with ``raw=True``, this will
613         instead be the :class:`.InstanceState` state-management
614         object associated with the instance.
615        :param state_dict: the dictionary returned by
616         :class:`.InstanceState.__getstate__`, containing the state
617         to be pickled.
618
619        """
620
621    def unpickle(self, target: _O, state_dict: _InstanceDict) -> None:
622        """Receive an object instance after its associated state has
623        been unpickled.
624
625        :param target: the mapped instance.  If
626         the event is configured with ``raw=True``, this will
627         instead be the :class:`.InstanceState` state-management
628         object associated with the instance.
629        :param state_dict: the dictionary sent to
630         :class:`.InstanceState.__setstate__`, containing the state
631         dictionary which was pickled.
632
633        """
634
635
636class _EventsHold(event.RefCollection[_ET]):
637    """Hold onto listeners against unmapped, uninstrumented classes.
638
639    Establish _listen() for that class' mapper/instrumentation when
640    those objects are created for that class.
641
642    """
643
644    all_holds: weakref.WeakKeyDictionary[Any, Any]
645
646    def __init__(
647        self,
648        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type],
649    ) -> None:
650        self.class_ = class_
651
652    @classmethod
653    def _clear(cls) -> None:
654        cls.all_holds.clear()
655
656    class HoldEvents(Generic[_ET2]):
657        _dispatch_target: Optional[Type[_ET2]] = None
658
659        @classmethod
660        def _listen(
661            cls,
662            event_key: _EventKey[_ET2],
663            raw: bool = False,
664            propagate: bool = False,
665            retval: bool = False,
666            **kw: Any,
667        ) -> None:
668            target = event_key.dispatch_target
669
670            if target.class_ in target.all_holds:
671                collection = target.all_holds[target.class_]
672            else:
673                collection = target.all_holds[target.class_] = {}
674
675            event.registry._stored_in_collection(event_key, target)
676            collection[event_key._key] = (
677                event_key,
678                raw,
679                propagate,
680                retval,
681                kw,
682            )
683
684            if propagate:
685                stack = list(target.class_.__subclasses__())
686                while stack:
687                    subclass = stack.pop(0)
688                    stack.extend(subclass.__subclasses__())
689                    subject = target.resolve(subclass)
690                    if subject is not None:
691                        # we are already going through __subclasses__()
692                        # so leave generic propagate flag False
693                        event_key.with_dispatch_target(subject).listen(
694                            raw=raw, propagate=False, retval=retval, **kw
695                        )
696
697    def remove(self, event_key: _EventKey[_ET]) -> None:
698        target = event_key.dispatch_target
699
700        if isinstance(target, _EventsHold):
701            collection = target.all_holds[target.class_]
702            del collection[event_key._key]
703
704    @classmethod
705    def populate(
706        cls,
707        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type],
708        subject: Union[ClassManager[_O], Mapper[_O]],
709    ) -> None:
710        for subclass in class_.__mro__:
711            if subclass in cls.all_holds:
712                collection = cls.all_holds[subclass]
713                for (
714                    event_key,
715                    raw,
716                    propagate,
717                    retval,
718                    kw,
719                ) in collection.values():
720                    if propagate or subclass is class_:
721                        # since we can't be sure in what order different
722                        # classes in a hierarchy are triggered with
723                        # populate(), we rely upon _EventsHold for all event
724                        # assignment, instead of using the generic propagate
725                        # flag.
726                        event_key.with_dispatch_target(subject).listen(
727                            raw=raw, propagate=False, retval=retval, **kw
728                        )
729
730
731class _InstanceEventsHold(_EventsHold[_ET]):
732    all_holds: weakref.WeakKeyDictionary[Any, Any] = (
733        weakref.WeakKeyDictionary()
734    )
735
736    def resolve(self, class_: Type[_O]) -> Optional[ClassManager[_O]]:
737        return instrumentation.opt_manager_of_class(class_)
738
739    class HoldInstanceEvents(_EventsHold.HoldEvents[_ET], InstanceEvents):  # type: ignore [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        def my_before_insert_listener(mapper, connection, target):
753            # execute a stored procedure upon INSERT,
754            # apply the value to the row to be inserted
755            target.calculated_value = connection.execute(
756                text("select my_special_function(%d)" % target.special_number)
757            ).scalar()
758
759        # associate the listener function with SomeClass,
760        # to execute during the "before_insert" hook
761        event.listen(
762            SomeClass, 'before_insert', my_before_insert_listener)
763
764    Available targets include:
765
766    * mapped classes
767    * unmapped superclasses of mapped or to-be-mapped classes
768      (using the ``propagate=True`` flag)
769    * :class:`_orm.Mapper` objects
770    * the :class:`_orm.Mapper` class itself indicates listening for all
771      mappers.
772
773    Mapper events provide hooks into critical sections of the
774    mapper, including those related to object instrumentation,
775    object loading, and object persistence. In particular, the
776    persistence methods :meth:`~.MapperEvents.before_insert`,
777    and :meth:`~.MapperEvents.before_update` are popular
778    places to augment the state being persisted - however, these
779    methods operate with several significant restrictions. The
780    user is encouraged to evaluate the
781    :meth:`.SessionEvents.before_flush` and
782    :meth:`.SessionEvents.after_flush` methods as more
783    flexible and user-friendly hooks in which to apply
784    additional database state during a flush.
785
786    When using :class:`.MapperEvents`, several modifiers are
787    available to the :func:`.event.listen` function.
788
789    :param propagate=False: When True, the event listener should
790       be applied to all inheriting mappers and/or the mappers of
791       inheriting classes, as well as any
792       mapper which is the target of this listener.
793    :param raw=False: When True, the "target" argument passed
794       to applicable event listener functions will be the
795       instance's :class:`.InstanceState` management
796       object, rather than the mapped instance itself.
797    :param retval=False: when True, the user-defined event function
798       must have a return value, the purpose of which is either to
799       control subsequent event propagation, or to otherwise alter
800       the operation in progress by the mapper.   Possible return
801       values are:
802
803       * ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event
804         processing normally.
805       * ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent
806         event handlers in the chain.
807       * other values - the return value specified by specific listeners.
808
809    """
810
811    _target_class_doc = "SomeClass"
812    _dispatch_target = mapperlib.Mapper
813
814    @classmethod
815    def _new_mapper_instance(
816        cls,
817        class_: Union[DeclarativeAttributeIntercept, DeclarativeMeta, type],
818        mapper: Mapper[_O],
819    ) -> None:
820        _MapperEventsHold.populate(class_, mapper)
821
822    @classmethod
823    @util.preload_module("sqlalchemy.orm")
824    def _accept_with(
825        cls,
826        target: Union[mapperlib.Mapper[Any], Type[mapperlib.Mapper[Any]]],
827        identifier: str,
828    ) -> Optional[Union[mapperlib.Mapper[Any], Type[mapperlib.Mapper[Any]]]]:
829        orm = util.preloaded.orm
830
831        if target is orm.mapper:  # type: ignore [attr-defined]
832            util.warn_deprecated(
833                "The `sqlalchemy.orm.mapper()` symbol is deprecated and "
834                "will be removed in a future release. For the mapper-wide "
835                "event target, use the 'sqlalchemy.orm.Mapper' class.",
836                "2.0",
837            )
838            return mapperlib.Mapper
839        elif isinstance(target, type):
840            if issubclass(target, mapperlib.Mapper):
841                return target
842            else:
843                mapper = _mapper_or_none(target)
844                if mapper is not None:
845                    return mapper
846                else:
847                    return _MapperEventsHold(target)
848        else:
849            return target
850
851    @classmethod
852    def _listen(
853        cls,
854        event_key: _EventKey[_ET],
855        raw: bool = False,
856        retval: bool = False,
857        propagate: bool = False,
858        **kw: Any,
859    ) -> None:
860        target, identifier, fn = (
861            event_key.dispatch_target,
862            event_key.identifier,
863            event_key._listen_fn,
864        )
865
866        if (
867            identifier in ("before_configured", "after_configured")
868            and target is not mapperlib.Mapper
869        ):
870            util.warn(
871                "'before_configured' and 'after_configured' ORM events "
872                "only invoke with the Mapper class "
873                "as the target."
874            )
875
876        if not raw or not retval:
877            if not raw:
878                meth = getattr(cls, identifier)
879                try:
880                    target_index = (
881                        inspect_getfullargspec(meth)[0].index("target") - 1
882                    )
883                except ValueError:
884                    target_index = None
885
886            def wrap(*arg: Any, **kw: Any) -> Any:
887                if not raw and target_index is not None:
888                    arg = list(arg)  # type: ignore [assignment]
889                    arg[target_index] = arg[target_index].obj()  # type: ignore [index] # noqa: E501
890                if not retval:
891                    fn(*arg, **kw)
892                    return interfaces.EXT_CONTINUE
893                else:
894                    return fn(*arg, **kw)
895
896            event_key = event_key.with_wrapper(wrap)
897
898        if propagate:
899            for mapper in target.self_and_descendants:
900                event_key.with_dispatch_target(mapper).base_listen(
901                    propagate=True, **kw
902                )
903        else:
904            event_key.base_listen(**kw)
905
906    @classmethod
907    def _clear(cls) -> None:
908        super()._clear()
909        _MapperEventsHold._clear()
910
911    def instrument_class(self, mapper: Mapper[_O], class_: Type[_O]) -> None:
912        r"""Receive a class when the mapper is first constructed,
913        before instrumentation is applied to the mapped class.
914
915        This event is the earliest phase of mapper construction.
916        Most attributes of the mapper are not yet initialized.   To
917        receive an event within initial mapper construction where basic
918        state is available such as the :attr:`_orm.Mapper.attrs` collection,
919        the :meth:`_orm.MapperEvents.after_mapper_constructed` event may
920        be a better choice.
921
922        This listener can either be applied to the :class:`_orm.Mapper`
923        class overall, or to any un-mapped class which serves as a base
924        for classes that will be mapped (using the ``propagate=True`` flag)::
925
926            Base = declarative_base()
927
928            @event.listens_for(Base, "instrument_class", propagate=True)
929            def on_new_class(mapper, cls_):
930                " ... "
931
932        :param mapper: the :class:`_orm.Mapper` which is the target
933         of this event.
934        :param class\_: the mapped class.
935
936        .. seealso::
937
938            :meth:`_orm.MapperEvents.after_mapper_constructed`
939
940        """
941
942    def after_mapper_constructed(
943        self, mapper: Mapper[_O], class_: Type[_O]
944    ) -> None:
945        """Receive a class and mapper when the :class:`_orm.Mapper` has been
946        fully constructed.
947
948        This event is called after the initial constructor for
949        :class:`_orm.Mapper` completes.  This occurs after the
950        :meth:`_orm.MapperEvents.instrument_class` event and after the
951        :class:`_orm.Mapper` has done an initial pass of its arguments
952        to generate its collection of :class:`_orm.MapperProperty` objects,
953        which are accessible via the :meth:`_orm.Mapper.get_property`
954        method and the :attr:`_orm.Mapper.iterate_properties` attribute.
955
956        This event differs from the
957        :meth:`_orm.MapperEvents.before_mapper_configured` event in that it
958        is invoked within the constructor for :class:`_orm.Mapper`, rather
959        than within the :meth:`_orm.registry.configure` process.   Currently,
960        this event is the only one which is appropriate for handlers that
961        wish to create additional mapped classes in response to the
962        construction of this :class:`_orm.Mapper`, which will be part of the
963        same configure step when :meth:`_orm.registry.configure` next runs.
964
965        .. versionadded:: 2.0.2
966
967        .. seealso::
968
969            :ref:`examples_versioning` - an example which illustrates the use
970            of the :meth:`_orm.MapperEvents.before_mapper_configured`
971            event to create new mappers to record change-audit histories on
972            objects.
973
974        """
975
976    def before_mapper_configured(
977        self, mapper: Mapper[_O], class_: Type[_O]
978    ) -> None:
979        """Called right before a specific mapper is to be configured.
980
981        This event is intended to allow a specific mapper to be skipped during
982        the configure step, by returning the :attr:`.orm.interfaces.EXT_SKIP`
983        symbol which indicates to the :func:`.configure_mappers` call that this
984        particular mapper (or hierarchy of mappers, if ``propagate=True`` is
985        used) should be skipped in the current configuration run. When one or
986        more mappers are skipped, the he "new mappers" flag will remain set,
987        meaning the :func:`.configure_mappers` function will continue to be
988        called when mappers are used, to continue to try to configure all
989        available mappers.
990
991        In comparison to the other configure-level events,
992        :meth:`.MapperEvents.before_configured`,
993        :meth:`.MapperEvents.after_configured`, and
994        :meth:`.MapperEvents.mapper_configured`, the
995        :meth;`.MapperEvents.before_mapper_configured` event provides for a
996        meaningful return value when it is registered with the ``retval=True``
997        parameter.
998
999        .. versionadded:: 1.3
1000
1001        e.g.::
1002
1003            from sqlalchemy.orm import EXT_SKIP
1004
1005            Base = declarative_base()
1006
1007            DontConfigureBase = declarative_base()
1008
1009            @event.listens_for(
1010                DontConfigureBase,
1011                "before_mapper_configured", retval=True, propagate=True)
1012            def dont_configure(mapper, cls):
1013                return EXT_SKIP
1014
1015
1016        .. seealso::
1017
1018            :meth:`.MapperEvents.before_configured`
1019
1020            :meth:`.MapperEvents.after_configured`
1021
1022            :meth:`.MapperEvents.mapper_configured`
1023
1024        """
1025
1026    def mapper_configured(self, mapper: Mapper[_O], class_: Type[_O]) -> None:
1027        r"""Called when a specific mapper has completed its own configuration
1028        within the scope of the :func:`.configure_mappers` call.
1029
1030        The :meth:`.MapperEvents.mapper_configured` event is invoked
1031        for each mapper that is encountered when the
1032        :func:`_orm.configure_mappers` function proceeds through the current
1033        list of not-yet-configured mappers.
1034        :func:`_orm.configure_mappers` is typically invoked
1035        automatically as mappings are first used, as well as each time
1036        new mappers have been made available and new mapper use is
1037        detected.
1038
1039        When the event is called, the mapper should be in its final
1040        state, but **not including backrefs** that may be invoked from
1041        other mappers; they might still be pending within the
1042        configuration operation.    Bidirectional relationships that
1043        are instead configured via the
1044        :paramref:`.orm.relationship.back_populates` argument
1045        *will* be fully available, since this style of relationship does not
1046        rely upon other possibly-not-configured mappers to know that they
1047        exist.
1048
1049        For an event that is guaranteed to have **all** mappers ready
1050        to go including backrefs that are defined only on other
1051        mappings, use the :meth:`.MapperEvents.after_configured`
1052        event; this event invokes only after all known mappings have been
1053        fully configured.
1054
1055        The :meth:`.MapperEvents.mapper_configured` event, unlike
1056        :meth:`.MapperEvents.before_configured` or
1057        :meth:`.MapperEvents.after_configured`,
1058        is called for each mapper/class individually, and the mapper is
1059        passed to the event itself.  It also is called exactly once for
1060        a particular mapper.  The event is therefore useful for
1061        configurational steps that benefit from being invoked just once
1062        on a specific mapper basis, which don't require that "backref"
1063        configurations are necessarily ready yet.
1064
1065        :param mapper: the :class:`_orm.Mapper` which is the target
1066         of this event.
1067        :param class\_: the mapped class.
1068
1069        .. seealso::
1070
1071            :meth:`.MapperEvents.before_configured`
1072
1073            :meth:`.MapperEvents.after_configured`
1074
1075            :meth:`.MapperEvents.before_mapper_configured`
1076
1077        """
1078        # TODO: need coverage for this event
1079
1080    def before_configured(self) -> None:
1081        """Called before a series of mappers have been configured.
1082
1083        The :meth:`.MapperEvents.before_configured` event is invoked
1084        each time the :func:`_orm.configure_mappers` function is
1085        invoked, before the function has done any of its work.
1086        :func:`_orm.configure_mappers` is typically invoked
1087        automatically as mappings are first used, as well as each time
1088        new mappers have been made available and new mapper use is
1089        detected.
1090
1091        This event can **only** be applied to the :class:`_orm.Mapper` class,
1092        and not to individual mappings or mapped classes. It is only invoked
1093        for all mappings as a whole::
1094
1095            from sqlalchemy.orm import Mapper
1096
1097            @event.listens_for(Mapper, "before_configured")
1098            def go():
1099                ...
1100
1101        Contrast this event to :meth:`.MapperEvents.after_configured`,
1102        which is invoked after the series of mappers has been configured,
1103        as well as :meth:`.MapperEvents.before_mapper_configured`
1104        and :meth:`.MapperEvents.mapper_configured`, which are both invoked
1105        on a per-mapper basis.
1106
1107        Theoretically this event is called once per
1108        application, but is actually called any time new mappers
1109        are to be affected by a :func:`_orm.configure_mappers`
1110        call.   If new mappings are constructed after existing ones have
1111        already been used, this event will likely be called again.  To ensure
1112        that a particular event is only called once and no further, the
1113        ``once=True`` argument (new in 0.9.4) can be applied::
1114
1115            from sqlalchemy.orm import mapper
1116
1117            @event.listens_for(mapper, "before_configured", once=True)
1118            def go():
1119                ...
1120
1121
1122        .. seealso::
1123
1124            :meth:`.MapperEvents.before_mapper_configured`
1125
1126            :meth:`.MapperEvents.mapper_configured`
1127
1128            :meth:`.MapperEvents.after_configured`
1129
1130        """
1131
1132    def after_configured(self) -> None:
1133        """Called after a series of mappers have been configured.
1134
1135        The :meth:`.MapperEvents.after_configured` event is invoked
1136        each time the :func:`_orm.configure_mappers` function is
1137        invoked, after the function has completed its work.
1138        :func:`_orm.configure_mappers` is typically invoked
1139        automatically as mappings are first used, as well as each time
1140        new mappers have been made available and new mapper use is
1141        detected.
1142
1143        Contrast this event to the :meth:`.MapperEvents.mapper_configured`
1144        event, which is called on a per-mapper basis while the configuration
1145        operation proceeds; unlike that event, when this event is invoked,
1146        all cross-configurations (e.g. backrefs) will also have been made
1147        available for any mappers that were pending.
1148        Also contrast to :meth:`.MapperEvents.before_configured`,
1149        which is invoked before the series of mappers has been configured.
1150
1151        This event can **only** be applied to the :class:`_orm.Mapper` class,
1152        and not to individual mappings or
1153        mapped classes.  It is only invoked for all mappings as a whole::
1154
1155            from sqlalchemy.orm import Mapper
1156
1157            @event.listens_for(Mapper, "after_configured")
1158            def go():
1159                # ...
1160
1161        Theoretically this event is called once per
1162        application, but is actually called any time new mappers
1163        have been affected by a :func:`_orm.configure_mappers`
1164        call.   If new mappings are constructed after existing ones have
1165        already been used, this event will likely be called again.  To ensure
1166        that a particular event is only called once and no further, the
1167        ``once=True`` argument (new in 0.9.4) can be applied::
1168
1169            from sqlalchemy.orm import mapper
1170
1171            @event.listens_for(mapper, "after_configured", once=True)
1172            def go():
1173                # ...
1174
1175        .. seealso::
1176
1177            :meth:`.MapperEvents.before_mapper_configured`
1178
1179            :meth:`.MapperEvents.mapper_configured`
1180
1181            :meth:`.MapperEvents.before_configured`
1182
1183        """
1184
1185    def before_insert(
1186        self, mapper: Mapper[_O], connection: Connection, target: _O
1187    ) -> None:
1188        """Receive an object instance before an INSERT statement
1189        is emitted corresponding to that instance.
1190
1191        .. note:: this event **only** applies to the
1192           :ref:`session flush operation <session_flushing>`
1193           and does **not** apply to the ORM DML operations described at
1194           :ref:`orm_expression_update_delete`.  To intercept ORM
1195           DML events, use :meth:`_orm.SessionEvents.do_orm_execute`.
1196
1197        This event is used to modify local, non-object related
1198        attributes on the instance before an INSERT occurs, as well
1199        as to emit additional SQL statements on the given
1200        connection.

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

codekingpro/portable-devtools · Team Ai