diff options
Diffstat (limited to 'lib/sqlalchemy/orm/instrumentation.py')
-rw-r--r-- | lib/sqlalchemy/orm/instrumentation.py | 85 |
1 files changed, 59 insertions, 26 deletions
diff --git a/lib/sqlalchemy/orm/instrumentation.py b/lib/sqlalchemy/orm/instrumentation.py index 368a6a0b1..68b4f0611 100644 --- a/lib/sqlalchemy/orm/instrumentation.py +++ b/lib/sqlalchemy/orm/instrumentation.py @@ -1,5 +1,5 @@ # orm/instrumentation.py -# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file> +# Copyright (C) 2005-2014 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 @@ -29,17 +29,15 @@ alternate instrumentation forms. """ -from . import exc, collections, events, interfaces -from operator import attrgetter -from .. import event, util -state = util.importlater("sqlalchemy.orm", "state") - +from . import exc, collections, interfaces, state +from .. import util +from . import base class ClassManager(dict): """tracks state information at the class level.""" - MANAGER_ATTR = '_sa_class_manager' - STATE_ATTR = '_sa_instance_state' + MANAGER_ATTR = base.DEFAULT_MANAGER_ATTR + STATE_ATTR = base.DEFAULT_STATE_ATTR deferred_scalar_loader = None @@ -63,7 +61,8 @@ class ClassManager(dict): for base in self._bases: self.update(base) - events._InstanceEventsHold.populate(class_, self) + self.dispatch._events._new_classmanager_instance(class_, self) + #events._InstanceEventsHold.populate(class_, self) for basecls in class_.__mro__: mgr = manager_of_class(basecls) @@ -79,7 +78,11 @@ class ClassManager(dict): "reference cycles. Please remove this method." % class_) - dispatch = event.dispatcher(events.InstanceEvents) + def __hash__(self): + return id(self) + + def __eq__(self, other): + return other is self @property def is_mapped(self): @@ -164,9 +167,7 @@ class ClassManager(dict): @util.hybridmethod def manager_getter(self): - def manager_of_class(cls): - return cls.__dict__.get(ClassManager.MANAGER_ATTR, None) - return manager_of_class + return _default_manager_getter @util.hybridmethod def state_getter(self): @@ -177,11 +178,12 @@ class ClassManager(dict): instance. """ - return attrgetter(self.STATE_ATTR) + return _default_state_getter @util.hybridmethod def dict_getter(self): - return attrgetter('__dict__') + return _default_dict_getter + def instrument_attribute(self, key, inst, propagated=False): if propagated: @@ -296,6 +298,9 @@ class ClassManager(dict): def teardown_instance(self, instance): delattr(instance, self.STATE_ATTR) + def _serialize(self, state, state_dict): + return _SerializeManager(state, state_dict) + def _new_state_if_none(self, instance): """Install a default InstanceState if none is present. @@ -335,12 +340,41 @@ class ClassManager(dict): return '<%s of %r at %x>' % ( self.__class__.__name__, self.class_, id(self)) +class _SerializeManager(object): + """Provide serialization of a :class:`.ClassManager`. + + The :class:`.InstanceState` uses ``__init__()`` on serialize + and ``__call__()`` on deserialize. + + """ + def __init__(self, state, d): + self.class_ = state.class_ + manager = state.manager + manager.dispatch.pickle(state, d) + + def __call__(self, state, inst, state_dict): + state.manager = manager = manager_of_class(self.class_) + if manager is None: + raise exc.UnmappedInstanceError( + inst, + "Cannot deserialize object of type %r - " + "no mapper() has " + "been configured for this class within the current " + "Python process!" % + self.class_) + elif manager.is_mapped and not manager.mapper.configured: + manager.mapper._configure_all() + + # setup _sa_instance_state ahead of time so that + # unpickle events can access the object normally. + # see [ticket:2362] + if inst is not None: + manager.setup_instance(inst, state) + manager.dispatch.unpickle(state, state_dict) class InstrumentationFactory(object): """Factory for new ClassManager instances.""" - dispatch = event.dispatcher(events.InstrumentationEvents) - def create_manager_for_cls(self, class_): assert class_ is not None assert manager_of_class(class_) is None @@ -380,6 +414,14 @@ class InstrumentationFactory(object): # when importred. _instrumentation_factory = InstrumentationFactory() +# these attributes are replaced by sqlalchemy.ext.instrumentation +# when a non-standard InstrumentationManager class is first +# used to instrument a class. +instance_state = _default_state_getter = base.instance_state + +instance_dict = _default_dict_getter = base.instance_dict + +manager_of_class = _default_manager_getter = base.manager_of_class def register_class(class_): """Register class instrumentation. @@ -411,15 +453,6 @@ def is_instrumented(instance, key): return manager_of_class(instance.__class__).\ is_instrumented(key, search=True) -# these attributes are replaced by sqlalchemy.ext.instrumentation -# when a non-standard InstrumentationManager class is first -# used to instrument a class. -instance_state = _default_state_getter = ClassManager.state_getter() - -instance_dict = _default_dict_getter = ClassManager.dict_getter() - -manager_of_class = _default_manager_getter = ClassManager.manager_getter() - def _generate_init(class_, class_manager): """Build an __init__ decorator that triggers ClassManager events.""" |