From 3eaedb38d306095c683900e7e4625be7f7bc91d2 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 14 Jul 2012 11:51:11 -0400 Subject: [PATCH] - move load_scalar_attributes out to loading.py --- lib/sqlalchemy/ext/instrumentation.py | 37 ++++----- lib/sqlalchemy/orm/loading.py | 65 ++++++++++++++++ lib/sqlalchemy/orm/mapper.py | 107 ++++++-------------------- lib/sqlalchemy/orm/state.py | 8 +- 4 files changed, 113 insertions(+), 104 deletions(-) diff --git a/lib/sqlalchemy/ext/instrumentation.py b/lib/sqlalchemy/ext/instrumentation.py index c42cf6ec9b..f840ad066f 100644 --- a/lib/sqlalchemy/ext/instrumentation.py +++ b/lib/sqlalchemy/ext/instrumentation.py @@ -2,7 +2,7 @@ The :mod:`sqlalchemy.ext.instrumentation` package provides for alternate systems of class instrumentation within the ORM. Class instrumentation -refers to how the ORM places attributes on the class which maintain +refers to how the ORM places attributes on the class which maintain data and track changes to that data, as well as event hooks installed on the class. @@ -11,24 +11,24 @@ on the class. with other object management packages, which already perform their own instrumentation. It is not intended for general use. -For examples of how the instrumentation extension is used, +For examples of how the instrumentation extension is used, see the example :ref:`examples_instrumentation`. .. versionchanged:: 0.8 - The :mod:`sqlalchemy.orm.instrumentation` was split out so + The :mod:`sqlalchemy.orm.instrumentation` was split out so that all functionality having to do with non-standard instrumentation was moved out to :mod:`sqlalchemy.ext.instrumentation`. When imported, the module installs itself within :mod:`sqlalchemy.orm.instrumentation` so that it - takes effect, including recognition of - ``__sa_instrumentation_manager__`` on mapped classes, as - well :attr:`.instrumentation_finders` + takes effect, including recognition of + ``__sa_instrumentation_manager__`` on mapped classes, as + well :attr:`.instrumentation_finders` being used to determine class instrumentation resolution. - + """ from ..orm import instrumentation as orm_instrumentation from ..orm.instrumentation import ( - ClassManager, InstrumentationFactory, _default_state_getter, + ClassManager, InstrumentationFactory, _default_state_getter, _default_dict_getter, _default_manager_getter ) from ..orm import attributes, collections @@ -66,9 +66,10 @@ def find_native_user_instrumentation_hook(cls): return getattr(cls, INSTRUMENTATION_MANAGER, None) instrumentation_finders = [find_native_user_instrumentation_hook] -"""An extensible sequence of callables which return instrumentation implementations +"""An extensible sequence of callables which return instrumentation +implementations -When a class is registered, each callable will be passed a class object. +When a class is registered, each callable will be passed a class object. If None is returned, the next finder in the sequence is consulted. Otherwise the return must be an instrumentation factory that follows the same guidelines as @@ -82,9 +83,9 @@ ClassManager instrumentation is used. class ExtendedInstrumentationRegistry(InstrumentationFactory): """Extends :class:`.InstrumentationFactory` with additional - bookkeeping, to accommodate multiple types of + bookkeeping, to accommodate multiple types of class managers. - + """ _manager_finders = weakref.WeakKeyDictionary() _state_finders = weakref.WeakKeyDictionary() @@ -116,7 +117,7 @@ class ExtendedInstrumentationRegistry(InstrumentationFactory): if factory != ClassManager and not self._extended: # somebody invoked a custom ClassManager. - # reinstall global "getter" functions with the more + # reinstall global "getter" functions with the more # expensive ones. self._extended = True _install_instrumented_lookups() @@ -196,9 +197,9 @@ class InstrumentationManager(object): .. versionchanged:: 0.8 :class:`.InstrumentationManager` was moved from - :mod:`sqlalchemy.orm.instrumentation` to - :mod:`sqlalchemy.ext.instrumentation`. - + :mod:`sqlalchemy.orm.instrumentation` to + :mod:`sqlalchemy.ext.instrumentation`. + """ # r4361 added a mandatory (cls) constructor to this interface. @@ -307,7 +308,7 @@ class _ClassInstrumentationAdapter(ClassManager): if delegate: return delegate(key, state, factory) else: - return ClassManager.initialize_collection(self, key, + return ClassManager.initialize_collection(self, key, state, factory) def new_instance(self, state=None): @@ -340,7 +341,7 @@ class _ClassInstrumentationAdapter(ClassManager): def has_state(self, instance): try: - state = self._get_state(instance) + self._get_state(instance) except orm_exc.NO_STATE: return False else: diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index 987d47cead..41b82d8d1a 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -18,6 +18,8 @@ from . import attributes, exc as orm_exc, state as statelib from .interfaces import EXT_CONTINUE from ..sql import util as sql_util from .util import _none_set, state_str +from .. import exc as sa_exc +sessionlib = util.importlater("sqlalchemy.orm", "session") _new_runid = util.counter() @@ -534,3 +536,66 @@ def _configure_subclass_mapper(mapper, context, path, adapter): adapter, polymorphic_from=mapper) return configure_subclass_mapper + +def load_scalar_attributes(mapper, state, attribute_names): + """initiate a column-based attribute refresh operation.""" + + #assert mapper is _state_mapper(state) + session = sessionlib._state_session(state) + if not session: + raise orm_exc.DetachedInstanceError( + "Instance %s is not bound to a Session; " + "attribute refresh operation cannot proceed" % + (state_str(state))) + + has_key = bool(state.key) + + result = False + + if mapper.inherits and not mapper.concrete: + statement = mapper._optimized_get_statement(state, attribute_names) + if statement is not None: + result = load_on_ident( + session.query(mapper).from_statement(statement), + None, + only_load_props=attribute_names, + refresh_state=state + ) + + if result is False: + if has_key: + identity_key = state.key + else: + # this codepath is rare - only valid when inside a flush, and the + # object is becoming persistent but hasn't yet been assigned + # an identity_key. + # check here to ensure we have the attrs we need. + pk_attrs = [mapper._columntoproperty[col].key + for col in mapper.primary_key] + if state.expired_attributes.intersection(pk_attrs): + raise sa_exc.InvalidRequestError( + "Instance %s cannot be refreshed - it's not " + " persistent and does not " + "contain a full primary key." % state_str(state)) + identity_key = mapper._identity_key_from_state(state) + + if (_none_set.issubset(identity_key) and \ + not mapper.allow_partial_pks) or \ + _none_set.issuperset(identity_key): + util.warn("Instance %s to be refreshed doesn't " + "contain a full primary key - can't be refreshed " + "(and shouldn't be expired, either)." + % state_str(state)) + return + + result = load_on_ident( + session.query(mapper), + identity_key, + refresh_state=state, + only_load_props=attribute_names) + + # if instance is pending, a refresh operation + # may not complete (even if PK attributes are assigned) + if has_key and result is None: + raise orm_exc.ObjectDeletedError(state) + diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index dceccf70f8..3339564076 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -16,22 +16,19 @@ available in :class:`~sqlalchemy.orm.`. from __future__ import absolute_import import types import weakref -import operator -from itertools import chain, groupby +from itertools import chain from collections import deque from .. import sql, util, log, exc as sa_exc, event, schema from ..sql import expression, visitors, operators, util as sql_util -from . import instrumentation, attributes, sync, \ +from . import instrumentation, attributes, \ exc as orm_exc, unitofwork, events, loading -from .interfaces import MapperProperty, EXT_CONTINUE, \ - PropComparator +from .interfaces import MapperProperty from .util import _INSTRUMENTOR, _class_to_mapper, \ - _state_mapper, class_mapper, instance_str, state_str,\ - PathRegistry, _none_set + _state_mapper, class_mapper, \ + PathRegistry import sys -sessionlib = util.importlater("sqlalchemy.orm", "session") properties = util.importlater("sqlalchemy.orm", "properties") descriptor_props = util.importlater("sqlalchemy.orm", "descriptor_props") @@ -68,17 +65,17 @@ class Mapper(object): def __init__(self, class_, local_table, - properties = None, - primary_key = None, - non_primary = False, - inherits = None, - inherit_condition = None, - inherit_foreign_keys = None, - extension = None, - order_by = False, - always_refresh = False, - version_id_col = None, - version_id_generator = None, + properties=None, + primary_key=None, + non_primary=False, + inherits=None, + inherit_condition=None, + inherit_foreign_keys=None, + extension=None, + order_by=False, + always_refresh=False, + version_id_col=None, + version_id_generator=None, polymorphic_on=None, _polymorphic_map=None, polymorphic_identity=None, @@ -182,8 +179,8 @@ class Mapper(object): self.configured = False # prevent this mapper from being constructed - # while a configure_mappers() is occurring (and defer a configure_mappers() - # until construction succeeds) + # while a configure_mappers() is occurring (and defer a + # configure_mappers() until construction succeeds) _COMPILE_MUTEX.acquire() try: self._configure_inheritance() @@ -659,7 +656,8 @@ class Mapper(object): self.class_manager = manager manager.mapper = self - manager.deferred_scalar_loader = self._load_scalar_attributes + manager.deferred_scalar_loader = util.partial( + loading.load_scalar_attributes, self) # The remaining members can be added by any mapper, # e_name None or not. @@ -676,10 +674,11 @@ class Mapper(object): self._reconstructor = method event.listen(manager, 'load', _event_on_load, raw=True) elif hasattr(method, '__sa_validators__'): - include_removes = getattr(method, "__sa_include_removes__", False) + include_removes = getattr(method, + "__sa_include_removes__", False) for name in method.__sa_validators__: self.validators = self.validators.union( - {name : (method, include_removes)} + {name: (method, include_removes)} ) manager.info[_INSTRUMENTOR] = self @@ -1678,65 +1677,6 @@ class Mapper(object): return state.manager[prop.key].impl.\ get_committed_value(state, dict_, passive=passive) - def _load_scalar_attributes(self, state, attribute_names): - """initiate a column-based attribute refresh operation.""" - - #assert mapper is _state_mapper(state) - session = sessionlib._state_session(state) - if not session: - raise orm_exc.DetachedInstanceError( - "Instance %s is not bound to a Session; " - "attribute refresh operation cannot proceed" % - (state_str(state))) - - has_key = bool(state.key) - - result = False - - if self.inherits and not self.concrete: - statement = self._optimized_get_statement(state, attribute_names) - if statement is not None: - result = loading.load_on_ident( - session.query(self).from_statement(statement), - None, - only_load_props=attribute_names, - refresh_state=state - ) - - if result is False: - if has_key: - identity_key = state.key - else: - # this codepath is rare - only valid when inside a flush, and the - # object is becoming persistent but hasn't yet been assigned an identity_key. - # check here to ensure we have the attrs we need. - pk_attrs = [self._columntoproperty[col].key - for col in self.primary_key] - if state.expired_attributes.intersection(pk_attrs): - raise sa_exc.InvalidRequestError("Instance %s cannot be refreshed - it's not " - " persistent and does not " - "contain a full primary key." % state_str(state)) - identity_key = self._identity_key_from_state(state) - - if (_none_set.issubset(identity_key) and \ - not self.allow_partial_pks) or \ - _none_set.issuperset(identity_key): - util.warn("Instance %s to be refreshed doesn't " - "contain a full primary key - can't be refreshed " - "(and shouldn't be expired, either)." - % state_str(state)) - return - - result = loading.load_on_ident( - session.query(self), - identity_key, - refresh_state=state, - only_load_props=attribute_names) - - # if instance is pending, a refresh operation - # may not complete (even if PK attributes are assigned) - if has_key and result is None: - raise orm_exc.ObjectDeletedError(state) def _optimized_get_statement(self, state, attribute_names): """assemble a WHERE clause which retrieves a given state by primary @@ -2052,6 +1992,7 @@ def validates(*names, **kw): """ include_removes = kw.pop('include_removes', False) + def wrap(fn): fn.__sa_validators__ = names fn.__sa_include_removes__ = include_removes diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 557eeb1338..ea0e89caf0 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -13,7 +13,7 @@ defines a large part of the ORM's interactivity. import weakref from .. import util -from . import exc as orm_exc, attributes,util as orm_util +from . import exc as orm_exc, attributes, util as orm_util from .attributes import ( PASSIVE_NO_RESULT, SQL_OK, NEVER_SET, ATTR_WAS_SET, NO_VALUE,\ @@ -198,8 +198,10 @@ class InstanceState(object): if manager is None: raise orm_exc.UnmappedInstanceError( inst, - "Cannot deserialize object of type %r - no mapper() has" - " been configured for this class within the current Python process!" % + "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: mapperlib.configure_mappers() -- 2.47.3