summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2018-12-23 15:44:37 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2018-12-23 15:44:37 +0000
commitd27023a30dc24c2fd5941025b36d051da3e596bd (patch)
tree794493fa576757f67a2c218e2899a1a3a396afd7 /lib
parent504432ed575fe48d378f6bc461e75eaa1ae48a82 (diff)
parent65ea042302693492cbaee96dfed5e9337e0d96a4 (diff)
downloadsqlalchemy-d27023a30dc24c2fd5941025b36d051da3e596bd.tar.gz
Merge "Add before_mapper_configured event"
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/event/registry.py2
-rw-r--r--lib/sqlalchemy/orm/__init__.py1
-rw-r--r--lib/sqlalchemy/orm/base.py1
-rw-r--r--lib/sqlalchemy/orm/events.py71
-rw-r--r--lib/sqlalchemy/orm/interfaces.py3
-rw-r--r--lib/sqlalchemy/orm/mapper.py19
6 files changed, 86 insertions, 11 deletions
diff --git a/lib/sqlalchemy/event/registry.py b/lib/sqlalchemy/event/registry.py
index 758230487..8d4bada0b 100644
--- a/lib/sqlalchemy/event/registry.py
+++ b/lib/sqlalchemy/event/registry.py
@@ -225,7 +225,7 @@ class _EventKey(object):
return self._key in _key_to_collection
def base_listen(self, propagate=False, insert=False,
- named=False):
+ named=False, retval=None):
target, identifier, fn = \
self.dispatch_target, self.identifier, self._listen_fn
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index f279c9b40..1784ea21f 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -25,6 +25,7 @@ from .mapper import (
from .interfaces import (
EXT_CONTINUE,
EXT_STOP,
+ EXT_SKIP,
PropComparator,
)
from .deprecated_interfaces import (
diff --git a/lib/sqlalchemy/orm/base.py b/lib/sqlalchemy/orm/base.py
index 62b4f59a9..deddaa5a4 100644
--- a/lib/sqlalchemy/orm/base.py
+++ b/lib/sqlalchemy/orm/base.py
@@ -149,6 +149,7 @@ _INSTRUMENTOR = ('mapper', 'instrumentor')
EXT_CONTINUE = util.symbol('EXT_CONTINUE')
EXT_STOP = util.symbol('EXT_STOP')
+EXT_SKIP = util.symbol('EXT_SKIP')
ONETOMANY = util.symbol(
'ONETOMANY',
diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py
index f27849e15..c414f548e 100644
--- a/lib/sqlalchemy/orm/events.py
+++ b/lib/sqlalchemy/orm/events.py
@@ -437,7 +437,9 @@ class _EventsHold(event.RefCollection):
_dispatch_target = None
@classmethod
- def _listen(cls, event_key, raw=False, propagate=False, **kw):
+ def _listen(
+ cls, event_key, raw=False, propagate=False,
+ retval=False, **kw):
target, identifier, fn = \
event_key.dispatch_target, event_key.identifier, event_key.fn
@@ -447,7 +449,7 @@ class _EventsHold(event.RefCollection):
collection = target.all_holds[target.class_] = {}
event.registry._stored_in_collection(event_key, target)
- collection[event_key._key] = (event_key, raw, propagate)
+ collection[event_key._key] = (event_key, raw, propagate, retval)
if propagate:
stack = list(target.class_.__subclasses__())
@@ -459,7 +461,7 @@ class _EventsHold(event.RefCollection):
# we are already going through __subclasses__()
# so leave generic propagate flag False
event_key.with_dispatch_target(subject).\
- listen(raw=raw, propagate=False, **kw)
+ listen(raw=raw, propagate=False, retval=retval, **kw)
def remove(self, event_key):
target, identifier, fn = \
@@ -474,7 +476,7 @@ class _EventsHold(event.RefCollection):
for subclass in class_.__mro__:
if subclass in cls.all_holds:
collection = cls.all_holds[subclass]
- for event_key, raw, propagate in collection.values():
+ for event_key, raw, propagate, retval in collection.values():
if propagate or subclass is class_:
# since we can't be sure in what order different
# classes in a hierarchy are triggered with
@@ -482,7 +484,7 @@ class _EventsHold(event.RefCollection):
# assignment, instead of using the generic propagate
# flag.
event_key.with_dispatch_target(subject).\
- listen(raw=raw, propagate=False)
+ listen(raw=raw, propagate=False, retval=retval)
class _InstanceEventsHold(_EventsHold):
@@ -659,6 +661,54 @@ class MapperEvents(event.Events):
"""
+ def before_mapper_configured(self, mapper, class_):
+ """Called right before a specific mapper is to be configured.
+
+ This event is intended to allow a specific mapper to be skipped during
+ the configure step, by returning the :attr:`.orm.interfaces.EXT_SKIP`
+ symbol which indicates to the :func:`.configure_mappers` call that this
+ particular mapper (or hierarchy of mappers, if ``propagate=True`` is
+ used) should be skipped in the current configuration run. When one or
+ more mappers are skipped, the he "new mappers" flag will remain set,
+ meaning the :func:`.configure_mappers` function will continue to be
+ called when mappers are used, to continue to try to configure all
+ available mappers.
+
+ In comparison to the other configure-level events,
+ :meth:`.MapperEvents.before_configured`,
+ :meth:`.MapperEvents.after_configured`, and
+ :meth:`.MapperEvents.mapper_configured`, the
+ :meth;`.MapperEvents.before_mapper_configured` event provides for a
+ meaningful return value when it is registered with the ``retval=True``
+ parameter.
+
+ .. versionadded:: 1.3
+
+ e.g.::
+
+ from sqlalchemy.orm import EXT_SKIP
+
+ Base = declarative_base()
+
+ DontConfigureBase = declarative_base()
+
+ @event.listens_for(
+ DontConfigureBase,
+ "before_mapper_configured", retval=True, propagate=True)
+ def dont_configure(mapper, cls):
+ return EXT_SKIP
+
+
+ .. seealso::
+
+ :meth:`.MapperEvents.before_configured`
+
+ :meth:`.MapperEvents.after_configured`
+
+ :meth:`.MapperEvents.mapper_configured`
+
+ """
+
def mapper_configured(self, mapper, class_):
r"""Called when a specific mapper has completed its own configuration
within the scope of the :func:`.configure_mappers` call.
@@ -708,6 +758,8 @@ class MapperEvents(event.Events):
:meth:`.MapperEvents.after_configured`
+ :meth:`.MapperEvents.before_mapper_configured`
+
"""
# TODO: need coverage for this event
@@ -734,8 +786,9 @@ class MapperEvents(event.Events):
Contrast this event to :meth:`.MapperEvents.after_configured`,
which is invoked after the series of mappers has been configured,
- as well as :meth:`.MapperEvents.mapper_configured`, which is invoked
- on a per-mapper basis as each one is configured to the extent possible.
+ as well as :meth:`.MapperEvents.before_mapper_configured`
+ and :meth:`.MapperEvents.mapper_configured`, which are both invoked
+ on a per-mapper basis.
Theoretically this event is called once per
application, but is actually called any time new mappers
@@ -757,6 +810,8 @@ class MapperEvents(event.Events):
.. seealso::
+ :meth:`.MapperEvents.before_mapper_configured`
+
:meth:`.MapperEvents.mapper_configured`
:meth:`.MapperEvents.after_configured`
@@ -808,6 +863,8 @@ class MapperEvents(event.Events):
.. seealso::
+ :meth:`.MapperEvents.before_mapper_configured`
+
:meth:`.MapperEvents.mapper_configured`
:meth:`.MapperEvents.before_configured`
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index 95291377b..80d0a6303 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -23,7 +23,7 @@ from __future__ import absolute_import
from .. import util
from ..sql import operators
from .base import (ONETOMANY, MANYTOONE, MANYTOMANY,
- EXT_CONTINUE, EXT_STOP, NOT_EXTENSION)
+ EXT_CONTINUE, EXT_STOP, EXT_SKIP, NOT_EXTENSION)
from .base import InspectionAttr, InspectionAttrInfo, _MappedAttribute
import collections
from .. import inspect
@@ -36,6 +36,7 @@ __all__ = (
'AttributeExtension',
'EXT_CONTINUE',
'EXT_STOP',
+ 'EXT_SKIP',
'ONETOMANY',
'MANYTOMANY',
'MANYTOONE',
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index f2b326359..fa731f729 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -26,7 +26,9 @@ from ..sql import expression, visitors, operators, util as sql_util
from . import instrumentation, attributes, exc as orm_exc, loading
from . import properties
from . import util as orm_util
-from .interfaces import MapperProperty, InspectionAttr, _MappedAttribute
+from .interfaces import MapperProperty, InspectionAttr, _MappedAttribute, \
+ EXT_SKIP
+
from .base import _class_to_mapper, _state_mapper, class_mapper, \
state_str, _INSTRUMENTOR
@@ -3014,6 +3016,8 @@ def configure_mappers():
if not Mapper._new_mappers:
return
+ has_skip = False
+
Mapper.dispatch._for_class(Mapper).before_configured()
# initialize properties on all mappers
# note that _mapper_registry is unordered, which
@@ -3021,6 +3025,15 @@ def configure_mappers():
# the order of mapper compilation
for mapper in list(_mapper_registry):
+ run_configure = None
+ for fn in mapper.dispatch.before_mapper_configured:
+ run_configure = fn(mapper, mapper.class_)
+ if run_configure is EXT_SKIP:
+ has_skip = True
+ break
+ if run_configure is EXT_SKIP:
+ continue
+
if getattr(mapper, '_configure_failed', False):
e = sa_exc.InvalidRequestError(
"One or more mappers failed to initialize - "
@@ -3030,6 +3043,7 @@ def configure_mappers():
% (mapper, mapper._configure_failed))
e._configure_failed = mapper._configure_failed
raise e
+
if not mapper.configured:
try:
mapper._post_configure_properties()
@@ -3042,7 +3056,8 @@ def configure_mappers():
mapper._configure_failed = exc
raise
- Mapper._new_mappers = False
+ if not has_skip:
+ Mapper._new_mappers = False
finally:
_already_compiling = False
finally: