Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

aroundthecode / SQLAlchemy   python

Repository URL to install this package:

Version: 1.2.10 

/ orm / events.py

# orm/events.py
# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

"""ORM event interfaces.

"""
from .. import event, exc, util
from .base import _mapper_or_none
import inspect
import weakref
from . import interfaces
from . import mapperlib, instrumentation
from .session import Session, sessionmaker
from .scoping import scoped_session
from .attributes import QueryableAttribute
from .query import Query
from sqlalchemy.util.compat import inspect_getargspec

class InstrumentationEvents(event.Events):
    """Events related to class instrumentation events.

    The listeners here support being established against
    any new style class, that is any object that is a subclass
    of 'type'.  Events will then be fired off for events
    against that class.  If the "propagate=True" flag is passed
    to event.listen(), the event will fire off for subclasses
    of that class as well.

    The Python ``type`` builtin is also accepted as a target,
    which when used has the effect of events being emitted
    for all classes.

    Note the "propagate" flag here is defaulted to ``True``,
    unlike the other class level events where it defaults
    to ``False``.  This means that new subclasses will also
    be the subject of these events, when a listener
    is established on a superclass.

    .. versionchanged:: 0.8 - events here will emit based
       on comparing the incoming class to the type of class
       passed to :func:`.event.listen`.  Previously, the
       event would fire for any class unconditionally regardless
       of what class was sent for listening, despite
       documentation which stated the contrary.

    """

    _target_class_doc = "SomeBaseClass"
    _dispatch_target = instrumentation.InstrumentationFactory

    @classmethod
    def _accept_with(cls, target):
        if isinstance(target, type):
            return _InstrumentationEventsHold(target)
        else:
            return None

    @classmethod
    def _listen(cls, event_key, propagate=True, **kw):
        target, identifier, fn = \
            event_key.dispatch_target, event_key.identifier, \
            event_key._listen_fn

        def listen(target_cls, *arg):
            listen_cls = target()
            if propagate and issubclass(target_cls, listen_cls):
                return fn(target_cls, *arg)
            elif not propagate and target_cls is listen_cls:
                return fn(target_cls, *arg)

        def remove(ref):
            key = event.registry._EventKey(
                None, identifier, listen,
                instrumentation._instrumentation_factory)
            getattr(instrumentation._instrumentation_factory.dispatch,
                    identifier).remove(key)

        target = weakref.ref(target.class_, remove)

        event_key.\
            with_dispatch_target(instrumentation._instrumentation_factory).\
            with_wrapper(listen).base_listen(**kw)

    @classmethod
    def _clear(cls):
        super(InstrumentationEvents, cls)._clear()
        instrumentation._instrumentation_factory.dispatch._clear()

    def class_instrument(self, cls):
        """Called after the given class is instrumented.

        To get at the :class:`.ClassManager`, use
        :func:`.manager_of_class`.

        """

    def class_uninstrument(self, cls):
        """Called before the given class is uninstrumented.

        To get at the :class:`.ClassManager`, use
        :func:`.manager_of_class`.

        """

    def attribute_instrument(self, cls, key, inst):
        """Called when an attribute is instrumented."""


class _InstrumentationEventsHold(object):
    """temporary marker object used to transfer from _accept_with() to
    _listen() on the InstrumentationEvents class.

    """

    def __init__(self, class_):
        self.class_ = class_

    dispatch = event.dispatcher(InstrumentationEvents)


class InstanceEvents(event.Events):
    """Define events specific to object lifecycle.

    e.g.::

        from sqlalchemy import event

        def my_load_listener(target, context):
            print "on load!"

        event.listen(SomeClass, 'load', my_load_listener)

    Available targets include:

    * mapped classes
    * unmapped superclasses of mapped or to-be-mapped classes
      (using the ``propagate=True`` flag)
    * :class:`.Mapper` objects
    * the :class:`.Mapper` class itself and the :func:`.mapper`
      function indicate listening for all mappers.

    .. versionchanged:: 0.8.0 instance events can be associated with
       unmapped superclasses of mapped classes.

    Instance events are closely related to mapper events, but
    are more specific to the instance and its instrumentation,
    rather than its system of persistence.

    When using :class:`.InstanceEvents`, several modifiers are
    available to the :func:`.event.listen` function.

    :param propagate=False: When True, the event listener should
       be applied to all inheriting classes as well as the
       class which is the target of this listener.
    :param raw=False: When True, the "target" argument passed
       to applicable event listener functions will be the
       instance's :class:`.InstanceState` management
       object, rather than the mapped instance itself.

    """

    _target_class_doc = "SomeClass"

    _dispatch_target = instrumentation.ClassManager

    @classmethod
    def _new_classmanager_instance(cls, class_, classmanager):
        _InstanceEventsHold.populate(class_, classmanager)

    @classmethod
    @util.dependencies("sqlalchemy.orm")
    def _accept_with(cls, orm, target):
        if isinstance(target, instrumentation.ClassManager):
            return target
        elif isinstance(target, mapperlib.Mapper):
            return target.class_manager
        elif target is orm.mapper:
            return instrumentation.ClassManager
        elif isinstance(target, type):
            if issubclass(target, mapperlib.Mapper):
                return instrumentation.ClassManager
            else:
                manager = instrumentation.manager_of_class(target)
                if manager:
                    return manager
                else:
                    return _InstanceEventsHold(target)
        return None

    @classmethod
    def _listen(cls, event_key, raw=False, propagate=False, **kw):
        target, identifier, fn = \
            event_key.dispatch_target, event_key.identifier, \
            event_key._listen_fn

        if not raw:
            def wrap(state, *arg, **kw):
                return fn(state.obj(), *arg, **kw)
            event_key = event_key.with_wrapper(wrap)

        event_key.base_listen(propagate=propagate, **kw)

        if propagate:
            for mgr in target.subclass_managers(True):
                event_key.with_dispatch_target(mgr).base_listen(
                    propagate=True)

    @classmethod
    def _clear(cls):
        super(InstanceEvents, cls)._clear()
        _InstanceEventsHold._clear()

    def first_init(self, manager, cls):
        """Called when the first instance of a particular mapping is called.

        This event is called when the ``__init__`` method of a class
        is called the first time for that particular class.    The event
        invokes before ``__init__`` actually proceeds as well as before
        the :meth:`.InstanceEvents.init` event is invoked.

        """

    def init(self, target, args, kwargs):
        """Receive an instance when its constructor is called.

        This method is only called during a userland construction of
        an object, in conjunction with the object's constructor, e.g.
        its ``__init__`` method.  It is not called when an object is
        loaded from the database; see the :meth:`.InstanceEvents.load`
        event in order to intercept a database load.

        The event is called before the actual ``__init__`` constructor
        of the object is called.  The ``kwargs`` dictionary may be
        modified in-place in order to affect what is passed to
        ``__init__``.

        :param target: the mapped instance.  If
         the event is configured with ``raw=True``, this will
         instead be the :class:`.InstanceState` state-management
         object associated with the instance.
        :param args: positional arguments passed to the ``__init__`` method.
         This is passed as a tuple and is currently immutable.
        :param kwargs: keyword arguments passed to the ``__init__`` method.
         This structure *can* be altered in place.

        .. seealso::

            :meth:`.InstanceEvents.init_failure`

            :meth:`.InstanceEvents.load`

        """

    def init_failure(self, target, args, kwargs):
        """Receive an instance when its constructor has been called,
        and raised an exception.

        This method is only called during a userland construction of
        an object, in conjunction with the object's constructor, e.g.
        its ``__init__`` method. It is not called when an object is loaded
        from the database.

        The event is invoked after an exception raised by the ``__init__``
        method is caught.  After the event
        is invoked, the original exception is re-raised outwards, so that
        the construction of the object still raises an exception.   The
        actual exception and stack trace raised should be present in
        ``sys.exc_info()``.

        :param target: the mapped instance.  If
         the event is configured with ``raw=True``, this will
         instead be the :class:`.InstanceState` state-management
         object associated with the instance.
        :param args: positional arguments that were passed to the ``__init__``
         method.
        :param kwargs: keyword arguments that were passed to the ``__init__``
         method.

        .. seealso::

            :meth:`.InstanceEvents.init`

            :meth:`.InstanceEvents.load`

        """

    def load(self, target, context):
        """Receive an object instance after it has been created via
        ``__new__``, and after initial attribute population has
        occurred.

        This typically occurs when the instance is created based on
        incoming result rows, and is only called once for that
        instance's lifetime.

        Note that during a result-row load, this method is called upon
        the first row received for this instance.  Note that some
        attributes and collections may or may not be loaded or even
        initialized, depending on what's present in the result rows.

        The :meth:`.InstanceEvents.load` event is also available in a
        class-method decorator format called :func:`.orm.reconstructor`.

        :param target: the mapped instance.  If
         the event is configured with ``raw=True``, this will
         instead be the :class:`.InstanceState` state-management
         object associated with the instance.
        :param context: the :class:`.QueryContext` corresponding to the
         current :class:`.Query` in progress.  This argument may be
         ``None`` if the load does not correspond to a :class:`.Query`,
         such as during :meth:`.Session.merge`.

        .. seealso::

            :meth:`.InstanceEvents.init`

            :meth:`.InstanceEvents.refresh`

            :meth:`.SessionEvents.loaded_as_persistent`

            :ref:`mapping_constructors`

        """

    def refresh(self, target, context, attrs):
        """Receive an object instance after one or more attributes have
        been refreshed from a query.

        Contrast this to the :meth:`.InstanceEvents.load` method, which
        is invoked when the object is first loaded from a query.

        :param target: the mapped instance.  If
         the event is configured with ``raw=True``, this will
         instead be the :class:`.InstanceState` state-management
         object associated with the instance.
        :param context: the :class:`.QueryContext` corresponding to the
         current :class:`.Query` in progress.
        :param attrs: sequence of attribute names which
         were populated, or None if all column-mapped, non-deferred
         attributes were populated.
Loading ...