]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- remove the need to use LoadDeferredColumns, LoadLazyAttribute in most cases,
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 17 Dec 2010 02:08:26 +0000 (21:08 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 17 Dec 2010 02:08:26 +0000 (21:08 -0500)
these are back to being part of LoaderStrategy
- simplify attribute.get()
- inline the dict get inside of attribute.__get__()
- revert some memoized attrs from InstanceState which are called in almost
all cases regardless
- inlining

lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/identity.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/state.py
lib/sqlalchemy/orm/strategies.py
test/aaa_profiling/test_orm.py
test/orm/test_attributes.py
test/orm/test_extendedattr.py

index 2ffffc0345356ed6274497d88d2c9768513d1b2a..e9f3763b19c287ffd1770301afde176c638a8450 100644 (file)
@@ -23,6 +23,7 @@ mapperutil = util.importlater("sqlalchemy.orm", "util")
 
 PASSIVE_NO_RESULT = util.symbol('PASSIVE_NO_RESULT')
 ATTR_WAS_SET = util.symbol('ATTR_WAS_SET')
+ATTR_EMPTY = util.symbol('ATTR_EMPTY')
 NO_VALUE = util.symbol('NO_VALUE')
 NEVER_SET = util.symbol('NEVER_SET')
 
@@ -59,7 +60,7 @@ class QueryableAttribute(interfaces.PropComparator):
         self.impl = impl
         self.comparator = comparator
         self.parententity = parententity
-        
+
         manager = manager_of_class(class_)
         # manager is None in the case of AliasedClass
         if manager:
@@ -72,6 +73,10 @@ class QueryableAttribute(interfaces.PropComparator):
     dispatch = event.dispatcher(events.AttributeEvents)
     dispatch.dispatch_cls.active_history = False
     
+    @util.memoized_property
+    def _supports_population(self):
+        return self.impl.supports_population
+        
     def get_history(self, instance, **kwargs):
         return self.impl.get_history(instance_state(instance),
                                         instance_dict(instance), **kwargs)
@@ -127,8 +132,12 @@ class InstrumentedAttribute(QueryableAttribute):
     def __get__(self, instance, owner):
         if instance is None:
             return self
-        return self.impl.get(instance_state(instance),
-                                instance_dict(instance))
+
+        dict_ = instance_dict(instance)
+        if self._supports_population and self.key in dict_:
+            return dict_[self.key]
+        else:
+            return self.impl.get(instance_state(instance),dict_)
 
 def create_proxied_attribute(descriptor):
     """Create an QueryableAttribute / user descriptor hybrid.
@@ -324,31 +333,37 @@ class AttributeImpl(object):
         resulting value will be set as the new value for this attribute.
         """
 
-        try:
+        if self.key in dict_:
             return dict_[self.key]
-        except KeyError:
-            # if no history, check for lazy callables, etc.
-            if state.committed_state.get(self.key, NEVER_SET) is NEVER_SET:
+        else:
+            # if history present, don't load
+            key = self.key
+            if key not in state.committed_state or \
+                state.committed_state[key] is NEVER_SET:
                 if passive is PASSIVE_NO_INITIALIZE:
                     return PASSIVE_NO_RESULT
                     
-                if self.key in state.callables:
-                    callable_ = state.callables[self.key]
-                elif self.callable_ is not None:
-                    callable_ = self.callable_(state)
+                if key in state.callables:
+                    callable_ = state.callables[key]
+                    value = callable_(passive)
+                elif self.callable_:
+                    value = self.callable_(state, passive)
                 else:
-                    callable_ = None
-
-                if callable_ is not None:
-                    value = callable_(passive=passive)
-                    if value is PASSIVE_NO_RESULT:
-                        return value
-                    elif value is not ATTR_WAS_SET:
-                        return self.set_committed_value(state, dict_, value)
-                    else:
-                        if self.key not in dict_:
-                            return self.get(state, dict_, passive=passive)
-                        return dict_[self.key]
+                    value = ATTR_EMPTY
+
+                if value is PASSIVE_NO_RESULT:
+                    return value
+                elif value is ATTR_WAS_SET:
+                    try:
+                        return dict_[key]
+                    except KeyError:
+                        # TODO: no test coverage here.
+                        raise KeyError(
+                                "Deferred loader for attribute "
+                                "%r failed to populate "
+                                "correctly" % key)
+                elif value is not ATTR_EMPTY:
+                    return self.set_committed_value(state, dict_, value)
 
             # Return a new, empty value
             return self.initialize(state, dict_)
index 8604c000845750508b8d951b6369685f2679198b..f1400a8c6b1d63b34cb9b3f1b0220056851b809b 100644 (file)
@@ -121,17 +121,28 @@ class WeakInstanceDict(IdentityMap):
                 
         dict.__setitem__(self, state.key, state)
         self._manage_incoming_state(state)
-                 
+
     def add(self, state):
-        if state.key in self:
-            if dict.__getitem__(self, state.key) is not state:
-                raise AssertionError("A conflicting state is already "
-                                    "present in the identity map for key %r" 
-                                    % (state.key, ))
-        else:
-            dict.__setitem__(self, state.key, state)
-            self._manage_incoming_state(state)
-    
+        key = state.key
+        # inline of self.__contains__
+        if dict.__contains__(self, key):
+            try:
+                existing_state = dict.__getitem__(self, key)
+                if existing_state is not state:
+                    o = existing_state.obj()
+                    if o is None:
+                        o = existing_state._is_really_none()
+                    if o is not None:
+                        raise AssertionError("A conflicting state is already "
+                                        "present in the identity map for key %r" 
+                                        % (key, ))
+                else:
+                    return
+            except KeyError:
+                pass
+        dict.__setitem__(self, key, state)
+        self._manage_incoming_state(state)
+
     def remove_key(self, key):
         state = dict.__getitem__(self, key)
         self.remove(state)
index 58c2246364152e856252fdf399a0487b91b1286b..9acb3485fd6a4ca1010a3cb2cfeba4c52083ec6c 100644 (file)
@@ -1922,7 +1922,7 @@ class Query(object):
                     # TODO: no coverage here
                     return attributes.PASSIVE_NO_RESULT
                 try:
-                    state()
+                    state(passive)
                 except orm_exc.ObjectDeletedError:
                     session._remove_newly_deleted(state)
                     return None
index 48861085d3526931d3df1d4b51412a7411d25af9..392d83d94822c6d39f9d0f485853e2608eeb4ad8 100644 (file)
@@ -36,10 +36,8 @@ class InstanceState(object):
         self.class_ = obj.__class__
         self.manager = manager
         self.obj = weakref.ref(obj, self._cleanup)
-
-    @util.memoized_property
-    def committed_state(self):
-        return {}
+        self.callables = {}
+        self.committed_state = {}
     
     @util.memoized_property
     def parents(self):
@@ -49,10 +47,6 @@ class InstanceState(object):
     def pending(self):
         return {}
 
-    @util.memoized_property
-    def callables(self):
-        return {}
-
     @property
     def has_identity(self):
         return bool(self.key)
@@ -75,10 +69,18 @@ class InstanceState(object):
                 instance_dict.remove(self)
             except AssertionError:
                 pass
+                
         # remove possible cycles
-        self.__dict__.pop('callables', None)
-        self.dispose()
-    
+        self.callables.clear()
+        
+        # inlining of self.dispose()
+        if self.session_id:
+            try:
+                del self.session_id
+            except AttributeError:
+                pass
+        del self.obj
+        
     def obj(self):
         return None
     
@@ -251,11 +253,8 @@ class InstanceState(object):
         else:
             filter_deferred = False
 
-        to_clear = (
-            self.__dict__.get('pending', None),
-            self.__dict__.get('committed_state', None),
-            self.mutable_dict
-        )
+        pending = self.__dict__.get('pending', None)
+        mutable_dict = self.mutable_dict
         
         for key in attribute_names:
             impl = self.manager[key].impl
@@ -264,18 +263,20 @@ class InstanceState(object):
                 self.callables[key] = self
             dict_.pop(key, None)
             
-            for d in to_clear:
-                if d is not None:
-                    d.pop(key, None)
+            self.committed_state.pop(key, None)
+            if mutable_dict:
+                mutable_dict.pop(key, None)
+            if pending:
+                pending.pop(key, None)
 
-    def __call__(self, **kw):
+    def __call__(self, passive):
         """__call__ allows the InstanceState to act as a deferred
         callable for loading expired attributes, which is also
         serializable (picklable).
 
         """
 
-        if kw.get('passive') is PASSIVE_NO_FETCH:
+        if passive is PASSIVE_NO_FETCH:
             return PASSIVE_NO_RESULT
         
         toload = self.expired_attributes.\
@@ -407,16 +408,15 @@ class InstanceState(object):
         if a value was not populated in state.dict.
 
         """
-        
-        self.__dict__.pop('committed_state', None)
-        self.__dict__.pop('pending', None)
 
-        if 'callables' in self.__dict__:
-            callables = self.callables
-            for key in list(callables):
-                if key in dict_ and callables[key] is self:
-                    del callables[key]
+        self.committed_state.clear()
+        self.__dict__.pop('pending', None)
 
+        callables = self.callables
+        for key in list(callables):
+            if key in dict_ and callables[key] is self:
+                del callables[key]
+            
         for key in self.manager.mutable_attributes:
             if key in dict_:
                 self.committed_state[key] = self.manager[key].impl.copy(dict_[key])
index ab7a181a77c4e1eae1a7307575cdf8b5870b8778..c4619d3a72e7022cc5b70877183cf8af02367d93 100644 (file)
@@ -230,7 +230,7 @@ class DeferredColumnLoader(LoaderStrategy):
              compare_function=self.columns[0].type.compare_values,
              copy_function=self.columns[0].type.copy_value,
              mutable_scalars=self.columns[0].type.is_mutable(),
-             callable_=self._class_level_loader,
+             callable_=self._load_for_state,
              expire_missing=False
         )
 
@@ -244,51 +244,26 @@ class DeferredColumnLoader(LoaderStrategy):
                             setup_query(context, entity,
                                         path, adapter, **kwargs)
     
-    def _class_level_loader(self, state):
+    def _load_for_state(self, state, passive):
         if not state.key:
-            return None
-            
-        return LoadDeferredColumns(state, self.key)
-        
-                
-log.class_logger(DeferredColumnLoader)
-
-class LoadDeferredColumns(object):
-    """serializable loader object used by DeferredColumnLoader"""
+            return attributes.ATTR_EMPTY
 
-    __slots__ = 'state', 'key'
-    
-    def __init__(self, state, key):
-        self.state = state
-        self.key = key
-    
-    def __getstate__(self):
-        return self.state, self.key
-    
-    def __setstate__(self, state):
-        self.state, self.key = state
-            
-    def __call__(self, passive=False):
-        state, key = self.state, self.key
-        
         if passive is attributes.PASSIVE_NO_FETCH:
             return attributes.PASSIVE_NO_RESULT
-
-        localparent = mapper._state_mapper(state)
+            
+        prop = self.parent_property
+        localparent = state.manager.mapper
         
-        prop = localparent._props[key]
-        strategy = prop._get_strategy(DeferredColumnLoader)
-
-        if strategy.group:
+        if self.group:
             toload = [
                     p.key for p in 
                     localparent.iterate_properties 
                     if isinstance(p, StrategizedProperty) and 
                       isinstance(p.strategy, DeferredColumnLoader) and 
-                      p.group==strategy.group
+                      p.group==self.group
                     ]
         else:
-            toload = [key]
+            toload = [self.key]
 
         # narrow the keys down to just those which have no history
         group = [k for k in toload if k in state.unmodified]
@@ -298,13 +273,30 @@ class LoadDeferredColumns(object):
             raise orm_exc.DetachedInstanceError(
                 "Parent instance %s is not bound to a Session; "
                 "deferred load operation of attribute '%s' cannot proceed" % 
-                (mapperutil.state_str(state), key)
+                (mapperutil.state_str(state), self.key)
                 )
 
         query = session.query(localparent)
         query._load_on_ident(state.key, 
                     only_load_props=group, refresh_state=state)
         return attributes.ATTR_WAS_SET
+                
+log.class_logger(DeferredColumnLoader)
+
+class LoadDeferredColumns(object):
+    """serializable loader object used by DeferredColumnLoader"""
+
+    def __init__(self, state, key):
+        self.state = state
+        self.key = key
+    
+    def __call__(self, passive=False):
+        state, key = self.state, self.key
+
+        localparent = state.manager.mapper
+        prop = localparent._props[key]
+        strategy = prop._strategies[DeferredColumnLoader]
+        return strategy._load_for_state(state, passive)
 
 class DeferredOption(StrategizedOption):
     propagate_to_loaders = True
@@ -398,7 +390,7 @@ class LazyLoader(AbstractRelationshipLoader):
         _register_attribute(self, 
                 mapper,
                 useobject=True,
-                callable_=self._class_level_loader,
+                callable_=self._load_for_state,
                 uselist = self.parent_property.uselist,
                 backref = self.parent_property.back_populates,
                 typecallable = self.parent_property.collection_class,
@@ -483,112 +475,20 @@ class LazyLoader(AbstractRelationshipLoader):
             criterion = adapt_source(criterion)
         return criterion
         
-    def _class_level_loader(self, state):
+    def _load_for_state(self, state, passive):
         if not state.key and \
             (not self.parent_property.load_on_pending or not state.session_id):
-            return None
-
-        return LoadLazyAttribute(state, self.key)
-
-    def create_row_processor(self, selectcontext, path, mapper, row, adapter):
-        key = self.key
-        if not self.is_class_level:
-            def new_execute(state, dict_, row):
-                # we are not the primary manager for this attribute 
-                # on this class - set up a
-                # per-instance lazyloader, which will override the 
-                # class-level behavior.
-                # this currently only happens when using a 
-                # "lazyload" option on a "no load"
-                # attribute - "eager" attributes always have a 
-                # class-level lazyloader installed.
-                state.set_callable(dict_, key, LoadLazyAttribute(state, key))
-        else:
-            def new_execute(state, dict_, row):
-                # we are the primary manager for this attribute on 
-                # this class - reset its
-                # per-instance attribute state, so that the class-level 
-                # lazy loader is
-                # executed when next referenced on this instance.  
-                # this is needed in
-                # populate_existing() types of scenarios to reset 
-                # any existing state.
-                state.reset(dict_, key)
-
-        return new_execute, None, None
-    
-    @classmethod
-    def _create_lazy_clause(cls, prop, reverse_direction=False):
-        binds = util.column_dict()
-        lookup = util.column_dict()
-        equated_columns = util.column_dict()
-
-        if reverse_direction and prop.secondaryjoin is None:
-            for l, r in prop.local_remote_pairs:
-                _list = lookup.setdefault(r, [])
-                _list.append((r, l))
-                equated_columns[l] = r
-        else:
-            for l, r in prop.local_remote_pairs:
-                _list = lookup.setdefault(l, [])
-                _list.append((l, r))
-                equated_columns[r] = l
-                
-        def col_to_bind(col):
-            if col in lookup:
-                for tobind, equated in lookup[col]:
-                    if equated in binds:
-                        return None
-                if col not in binds:
-                    binds[col] = sql.bindparam(None, None, type_=col.type)
-                return binds[col]
-            return None
+            return attributes.ATTR_EMPTY
         
-        lazywhere = prop.primaryjoin
-
-        if prop.secondaryjoin is None or not reverse_direction:
-            lazywhere = visitors.replacement_traverse(
-                                            lazywhere, {}, col_to_bind) 
-        
-        if prop.secondaryjoin is not None:
-            secondaryjoin = prop.secondaryjoin
-            if reverse_direction:
-                secondaryjoin = visitors.replacement_traverse(
-                                            secondaryjoin, {}, col_to_bind)
-            lazywhere = sql.and_(lazywhere, secondaryjoin)
-    
-        bind_to_col = dict((binds[col].key, col) for col in binds)
-        
-        return lazywhere, bind_to_col, equated_columns
-    
-log.class_logger(LazyLoader)
-
-class LoadLazyAttribute(object):
-    """serializable loader object used by LazyLoader"""
-    
-    __slots__ = 'state', 'key'
-    
-    def __init__(self, state, key):
-        self.state = state
-        self.key = key
-    
-    def __getstate__(self):
-        return self.state, self.key
-    
-    def __setstate__(self, state):
-        self.state, self.key = state
-            
-    def __call__(self, passive=False):
-        state, key = self.state, self.key
         instance_mapper = state.manager.mapper
-        prop = instance_mapper._props[key]
-        prop_mapper = prop.mapper
-        strategy = prop._strategies[LazyLoader]
+        prop = self.parent_property
+        key = self.key
+        prop_mapper = self.mapper
         pending = not state.key
         
         if (
                 passive is attributes.PASSIVE_NO_FETCH and 
-                not strategy.use_get
+                not self.use_get
             ) or (
                 passive is attributes.PASSIVE_ONLY_PERSISTENT and 
                 pending
@@ -605,7 +505,7 @@ class LoadLazyAttribute(object):
 
         # if we have a simple primary key load, check the 
         # identity map without generating a Query at all
-        if strategy.use_get:
+        if self.use_get:
             if session._flushing:
                 get_attr = instance_mapper._get_committed_state_attr_by_column
             else:
@@ -616,7 +516,7 @@ class LoadLazyAttribute(object):
                 get_attr(
                         state,
                         state.dict,
-                        strategy._equated_columns[pk],
+                        self._equated_columns[pk],
                         passive=passive)
                 for pk in prop_mapper.primary_key
             ]
@@ -645,7 +545,7 @@ class LoadLazyAttribute(object):
         if state.load_options:
             q = q._conditional_options(*state.load_options)
 
-        if strategy.use_get:
+        if self.use_get:
             return q._load_on_ident(ident_key)
 
         if prop.order_by:
@@ -659,7 +559,7 @@ class LoadLazyAttribute(object):
                         not isinstance(rev.strategy, LazyLoader):
                 q = q.options(EagerLazyOption((rev.key,), lazy='select'))
 
-        lazy_clause = strategy.lazy_clause(state)
+        lazy_clause = self.lazy_clause(state)
         
         if pending:
             bind_values = sql_util.bind_values(lazy_clause)
@@ -669,7 +569,7 @@ class LoadLazyAttribute(object):
         q = q.filter(lazy_clause)
 
         result = q.all()
-        if strategy.uselist:
+        if self.uselist:
             return result
         else:
             l = len(result)
@@ -684,6 +584,95 @@ class LoadLazyAttribute(object):
             else:
                 return None
 
+    def create_row_processor(self, selectcontext, path, mapper, row, adapter):
+        key = self.key
+        if not self.is_class_level:
+            def new_execute(state, dict_, row):
+                # we are not the primary manager for this attribute 
+                # on this class - set up a
+                # per-instance lazyloader, which will override the 
+                # class-level behavior.
+                # this currently only happens when using a 
+                # "lazyload" option on a "no load"
+                # attribute - "eager" attributes always have a 
+                # class-level lazyloader installed.
+                state.set_callable(dict_, key, LoadLazyAttribute(state, key))
+        else:
+            def new_execute(state, dict_, row):
+                # we are the primary manager for this attribute on 
+                # this class - reset its
+                # per-instance attribute state, so that the class-level 
+                # lazy loader is
+                # executed when next referenced on this instance.  
+                # this is needed in
+                # populate_existing() types of scenarios to reset 
+                # any existing state.
+                state.reset(dict_, key)
+
+        return new_execute, None, None
+    
+    @classmethod
+    def _create_lazy_clause(cls, prop, reverse_direction=False):
+        binds = util.column_dict()
+        lookup = util.column_dict()
+        equated_columns = util.column_dict()
+
+        if reverse_direction and prop.secondaryjoin is None:
+            for l, r in prop.local_remote_pairs:
+                _list = lookup.setdefault(r, [])
+                _list.append((r, l))
+                equated_columns[l] = r
+        else:
+            for l, r in prop.local_remote_pairs:
+                _list = lookup.setdefault(l, [])
+                _list.append((l, r))
+                equated_columns[r] = l
+                
+        def col_to_bind(col):
+            if col in lookup:
+                for tobind, equated in lookup[col]:
+                    if equated in binds:
+                        return None
+                if col not in binds:
+                    binds[col] = sql.bindparam(None, None, type_=col.type)
+                return binds[col]
+            return None
+        
+        lazywhere = prop.primaryjoin
+
+        if prop.secondaryjoin is None or not reverse_direction:
+            lazywhere = visitors.replacement_traverse(
+                                            lazywhere, {}, col_to_bind) 
+        
+        if prop.secondaryjoin is not None:
+            secondaryjoin = prop.secondaryjoin
+            if reverse_direction:
+                secondaryjoin = visitors.replacement_traverse(
+                                            secondaryjoin, {}, col_to_bind)
+            lazywhere = sql.and_(lazywhere, secondaryjoin)
+    
+        bind_to_col = dict((binds[col].key, col) for col in binds)
+        
+        return lazywhere, bind_to_col, equated_columns
+    
+log.class_logger(LazyLoader)
+
+class LoadLazyAttribute(object):
+    """serializable loader object used by LazyLoader"""
+    
+    def __init__(self, state, key):
+        self.state = state
+        self.key = key
+            
+    def __call__(self, passive=False):
+        state, key = self.state, self.key
+        instance_mapper = state.manager.mapper
+        prop = instance_mapper._props[key]
+        strategy = prop._strategies[LazyLoader]
+        
+        return strategy._load_for_state(state, passive)
+        
+
 class ImmediateLoader(AbstractRelationshipLoader):
     def init_class_attribute(self, mapper):
         self.parent_property.\
index 71a5153e6afbfe39353036c2c2808d0bd706da0b..2995c35642d11b23ad2d8e648020fedb800bef8b 100644 (file)
@@ -172,7 +172,7 @@ class LoadManyToOneFromIdentityTest(_base.MappedTest):
         parents = sess.query(Parent).all()
         children = sess.query(Child).all()
         
-        @profiling.function_call_count(23979, {'2.5':28974, '3':25978})
+        @profiling.function_call_count(17987, {'3':20978})
         def go():
             for p in parents:
                 p.child
index b543e79a124173d2a28dbeda5aa167483906df45..91f61c05c05cee6ccf68c743dddb1bb893c4d202 100644 (file)
@@ -49,16 +49,28 @@ class AttributesTest(_base.ORMTest):
     def test_pickleness(self):
         instrumentation.register_class(MyTest)
         instrumentation.register_class(MyTest2)
-        attributes.register_attribute(MyTest, 'user_id', uselist=False, useobject=False)
-        attributes.register_attribute(MyTest, 'user_name', uselist=False, useobject=False)
-        attributes.register_attribute(MyTest, 'email_address', uselist=False, useobject=False)
-        attributes.register_attribute(MyTest, 'some_mutable_data', mutable_scalars=True, copy_function=list, compare_function=cmp, uselist=False, useobject=False)
-        attributes.register_attribute(MyTest2, 'a', uselist=False, useobject=False)
-        attributes.register_attribute(MyTest2, 'b', uselist=False, useobject=False)
+        attributes.register_attribute(MyTest, 'user_id', uselist=False,
+                useobject=False)
+        attributes.register_attribute(MyTest, 'user_name',
+                uselist=False, useobject=False)
+        attributes.register_attribute(MyTest, 'email_address',
+                uselist=False, useobject=False)
+        attributes.register_attribute(MyTest, 'some_mutable_data',
+                mutable_scalars=True, copy_function=list,
+                compare_function=cmp, uselist=False, useobject=False)
+        attributes.register_attribute(MyTest2, 'a', uselist=False,
+                useobject=False)
+        attributes.register_attribute(MyTest2, 'b', uselist=False,
+                useobject=False)
+
         # shouldnt be pickling callables at the class level
-        def somecallable(*args, **kw):
+
+        def somecallable(state, passive):
             return None
-        attributes.register_attribute(MyTest, "mt2", uselist = True, trackparent=True, callable_=somecallable, useobject=True)
+
+        attributes.register_attribute(MyTest, 'mt2', uselist=True,
+                trackparent=True, callable_=somecallable,
+                useobject=True)
 
         o = MyTest()
         o.mt2.append(MyTest2())
@@ -259,26 +271,26 @@ class AttributesTest(_base.ORMTest):
 
         b1, b2, b3, b4 = Bar(id='b1'), Bar(id='b2'), Bar(id='b3'), Bar(id='b4')
         
-        def loadcollection(**kw):
-            if kw.get('passive') is attributes.PASSIVE_NO_FETCH:
+        def loadcollection(state, passive):
+            if passive is attributes.PASSIVE_NO_FETCH:
                 return attributes.PASSIVE_NO_RESULT
             return [b1, b2]
         
-        def loadscalar(**kw):
-            if kw.get('passive') is attributes.PASSIVE_NO_FETCH:
+        def loadscalar(state, passive):
+            if passive is attributes.PASSIVE_NO_FETCH:
                 return attributes.PASSIVE_NO_RESULT
             return b2
             
         attributes.register_attribute(Foo, 'bars', 
                                uselist=True, 
                                useobject=True, 
-                               callable_=lambda o:loadcollection,
+                               callable_=loadcollection,
                                extension=[ReceiveEvents('bars')])
                                
         attributes.register_attribute(Foo, 'bar', 
                               uselist=False, 
                               useobject=True, 
-                              callable_=lambda o:loadscalar,
+                              callable_=loadscalar,
                               extension=[ReceiveEvents('bar')])
                               
         attributes.register_attribute(Foo, 'scalar', 
@@ -341,14 +353,17 @@ class AttributesTest(_base.ORMTest):
         instrumentation.register_class(Bar)
 
         bar1, bar2, bar3 = [Bar(id=1), Bar(id=2), Bar(id=3)]
-        def func1(**kw):
-            if kw.get('passive') is attributes.PASSIVE_NO_FETCH:
+        def func1(state, passive):
+            if passive is attributes.PASSIVE_NO_FETCH:
                 return attributes.PASSIVE_NO_RESULT
             
             return [bar1, bar2, bar3]
 
-        attributes.register_attribute(Foo, 'bars', uselist=True, callable_=lambda o:func1, useobject=True, extension=[ReceiveEvents()])
-        attributes.register_attribute(Bar, 'foos', uselist=True, useobject=True, backref="bars")
+        attributes.register_attribute(Foo, 'bars', uselist=True,
+                callable_=func1, useobject=True,
+                extension=[ReceiveEvents()])
+        attributes.register_attribute(Bar, 'foos', uselist=True,
+                useobject=True, backref='bars')
 
         x = Foo()
         assert_raises(AssertionError, Bar(id=4).foos.append, x)
@@ -423,9 +438,9 @@ class AttributesTest(_base.ORMTest):
         b = Blog()
         p1 = Post()
         attributes.instance_state(b).set_callable(attributes.instance_dict(b), 
-                                                    'posts', lambda **kw:[p1])
+                                                    'posts', lambda passive:[p1])
         attributes.instance_state(p1).set_callable(attributes.instance_dict(p1), 
-                                                    'blog', lambda **kw:b)
+                                                    'blog', lambda passive:b)
         p1, attributes.instance_state(b).commit_all(attributes.instance_dict(b))
 
         # no orphans (called before the lazy loaders fire off)
@@ -452,18 +467,18 @@ class AttributesTest(_base.ORMTest):
         instrumentation.register_class(Foo)
         instrumentation.register_class(Bar)
 
-        def func1(**kw):
+        def func1(state, passive):
             return "this is the foo attr"
-        def func2(**kw):
+        def func2(state, passive):
             return "this is the bar attr"
-        def func3(**kw):
+        def func3(state, passive):
             return "this is the shared attr"
         attributes.register_attribute(Foo, 'element', uselist=False, 
-                                            callable_=lambda o:func1, useobject=True)
+                                            callable_=func1, useobject=True)
         attributes.register_attribute(Foo, 'element2', uselist=False, 
-                                            callable_=lambda o:func3, useobject=True)
+                                            callable_=func3, useobject=True)
         attributes.register_attribute(Bar, 'element', uselist=False, 
-                                            callable_=lambda o:func2, useobject=True)
+                                            callable_=func2, useobject=True)
 
         x = Foo()
         y = Bar()
@@ -525,14 +540,17 @@ class AttributesTest(_base.ORMTest):
         instrumentation.register_class(Bar)
 
         bar1, bar2, bar3, bar4 = [Bar(id=1), Bar(id=2), Bar(id=3), Bar(id=4)]
-        def func1(**kw):
+        def func1(state, passive):
             return "this is func 1"
-        def func2(**kw):
+        def func2(state, passive):
             return [bar1, bar2, bar3]
 
-        attributes.register_attribute(Foo, 'col1', uselist=False, callable_=lambda o:func1, useobject=True)
-        attributes.register_attribute(Foo, 'col2', uselist=True, callable_=lambda o:func2, useobject=True)
-        attributes.register_attribute(Bar, 'id', uselist=False, useobject=True)
+        attributes.register_attribute(Foo, 'col1', uselist=False,
+                callable_=func1, useobject=True)
+        attributes.register_attribute(Foo, 'col2', uselist=True,
+                callable_=func2, useobject=True)
+        attributes.register_attribute(Bar, 'id', uselist=False,
+                useobject=True)
 
         x = Foo()
         attributes.instance_state(x).commit_all(attributes.instance_dict(x))
@@ -864,9 +882,6 @@ class BackrefTest(_base.ORMTest):
         # and this condition changes.
         assert c1 in p1.children
         
-        
-        
-        
 class PendingBackrefTest(_base.ORMTest):
     def setup(self):
         global Post, Blog, called, lazy_load
@@ -888,19 +903,20 @@ class PendingBackrefTest(_base.ORMTest):
         called = [0]
 
         lazy_load = []
-        def lazy_posts(instance):
-            def load(**kw):
-                if kw['passive'] is not attributes.PASSIVE_NO_FETCH:
-                    called[0] += 1
-                    return lazy_load
-                else:
-                    return attributes.PASSIVE_NO_RESULT
-            return load
+        def lazy_posts(state, passive):
+            if passive is not attributes.PASSIVE_NO_FETCH:
+                called[0] += 1
+                return lazy_load
+            else:
+                return attributes.PASSIVE_NO_RESULT
 
         instrumentation.register_class(Post)
         instrumentation.register_class(Blog)
-        attributes.register_attribute(Post, 'blog', uselist=False, backref='posts', trackparent=True, useobject=True)
-        attributes.register_attribute(Blog, 'posts', uselist=True, backref='blog', callable_=lazy_posts, trackparent=True, useobject=True)
+        attributes.register_attribute(Post, 'blog', uselist=False,
+                backref='posts', trackparent=True, useobject=True)
+        attributes.register_attribute(Blog, 'posts', uselist=True,
+                backref='blog', callable_=lazy_posts, trackparent=True,
+                useobject=True)
 
     def test_lazy_add(self):
         global lazy_load
@@ -1384,15 +1400,16 @@ class HistoryTest(_base.ORMTest):
             pass
 
         lazy_load = []
-        def lazyload(instance):
-            def load(**kw):
-                return lazy_load
-            return load
+        def lazyload(state, passive):
+            return lazy_load
 
         instrumentation.register_class(Foo)
         instrumentation.register_class(Bar)
-        attributes.register_attribute(Foo, 'bars', uselist=True, backref='foo', trackparent=True, callable_=lazyload, useobject=True)
-        attributes.register_attribute(Bar, 'foo', uselist=False, backref='bars', trackparent=True, useobject=True)
+        attributes.register_attribute(Foo, 'bars', uselist=True,
+                backref='foo', trackparent=True, callable_=lazyload,
+                useobject=True)
+        attributes.register_attribute(Bar, 'foo', uselist=False,
+                backref='bars', trackparent=True, useobject=True)
 
         bar1, bar2, bar3, bar4 = [Bar(id=1), Bar(id=2), Bar(id=3), Bar(id=4)]
         lazy_load = [bar1, bar2, bar3]
@@ -1419,14 +1436,13 @@ class HistoryTest(_base.ORMTest):
             pass
 
         lazy_load = []
-        def lazyload(instance):
-            def load(**kw):
-                return lazy_load
-            return load
+        def lazyload(state, passive):
+            return lazy_load
 
         instrumentation.register_class(Foo)
         instrumentation.register_class(Bar)
-        attributes.register_attribute(Foo, 'bars', uselist=True, callable_=lazyload, trackparent=True, useobject=True)
+        attributes.register_attribute(Foo, 'bars', uselist=True,
+                callable_=lazyload, trackparent=True, useobject=True)
 
         bar1, bar2, bar3, bar4 = [Bar(id=1), Bar(id=2), Bar(id=3), Bar(id=4)]
         lazy_load = [bar1, bar2, bar3]
@@ -1459,14 +1475,13 @@ class HistoryTest(_base.ORMTest):
             pass
 
         lazy_load = None
-        def lazyload(instance):
-            def load(**kw):
-                return lazy_load
-            return load
+        def lazyload(state, passive):
+            return lazy_load
 
         instrumentation.register_class(Foo)
-        attributes.register_attribute(Foo, 'bar', uselist=False, callable_=lazyload, useobject=False)
-        lazy_load = "hi"
+        attributes.register_attribute(Foo, 'bar', uselist=False,
+                callable_=lazyload, useobject=False)
+        lazy_load = 'hi'
 
         # with scalar non-object and active_history=False, the lazy callable is only executed on gets, not history
         # operations
@@ -1497,14 +1512,14 @@ class HistoryTest(_base.ORMTest):
             pass
 
         lazy_load = None
-        def lazyload(instance):
-            def load(**kw):
-                return lazy_load
-            return load
+        def lazyload(state, passive):
+            return lazy_load
 
         instrumentation.register_class(Foo)
-        attributes.register_attribute(Foo, 'bar', uselist=False, callable_=lazyload, useobject=False, active_history=True)
-        lazy_load = "hi"
+        attributes.register_attribute(Foo, 'bar', uselist=False,
+                callable_=lazyload, useobject=False,
+                active_history=True)
+        lazy_load = 'hi'
 
         # active_history=True means the lazy callable is executed on set as well as get,
         # causing the old value to appear in the history
@@ -1537,14 +1552,13 @@ class HistoryTest(_base.ORMTest):
             pass
 
         lazy_load = None
-        def lazyload(instance):
-            def load(**kw):
-                return lazy_load
-            return load
+        def lazyload(state, passive):
+            return lazy_load
 
         instrumentation.register_class(Foo)
         instrumentation.register_class(Bar)
-        attributes.register_attribute(Foo, 'bar', uselist=False, callable_=lazyload, trackparent=True, useobject=True)
+        attributes.register_attribute(Foo, 'bar', uselist=False,
+                callable_=lazyload, trackparent=True, useobject=True)
         bar1, bar2 = [Bar(id=1), Bar(id=2)]
         lazy_load = bar1
 
index ec7963c29382d9482b67c0d9d6f22de5b2fc7286..2eca1ac387dfedfa5de5843d09a5b146ae664b3e 100644 (file)
@@ -197,18 +197,21 @@ class UserDefinedExtensionTest(_base.ORMTest):
             instrumentation.register_class(Foo)
             instrumentation.register_class(Bar)
 
-            def func1(**kw):
-                print "func1"
+            def func1(state, passive):
                 return "this is the foo attr"
-            def func2(**kw):
-                print "func2"
+            def func2(state, passive):
                 return "this is the bar attr"
-            def func3(**kw):
-                print "func3"
+            def func3(state, passive):
                 return "this is the shared attr"
-            attributes.register_attribute(Foo, 'element', uselist=False, callable_=lambda o:func1, useobject=True)
-            attributes.register_attribute(Foo, 'element2', uselist=False, callable_=lambda o:func3, useobject=True)
-            attributes.register_attribute(Bar, 'element', uselist=False, callable_=lambda o:func2, useobject=True)
+            attributes.register_attribute(Foo, 'element',
+                    uselist=False, callable_=func1,
+                    useobject=True)
+            attributes.register_attribute(Foo, 'element2',
+                    uselist=False, callable_=func3,
+                    useobject=True)
+            attributes.register_attribute(Bar, 'element',
+                    uselist=False, callable_=func2,
+                    useobject=True)
 
             x = Foo()
             y = Bar()
@@ -224,8 +227,10 @@ class UserDefinedExtensionTest(_base.ORMTest):
 
             instrumentation.register_class(Post)
             instrumentation.register_class(Blog)
-            attributes.register_attribute(Post, 'blog', uselist=False, backref='posts', trackparent=True, useobject=True)
-            attributes.register_attribute(Blog, 'posts', uselist=True, backref='blog', trackparent=True, useobject=True)
+            attributes.register_attribute(Post, 'blog', uselist=False,
+                    backref='posts', trackparent=True, useobject=True)
+            attributes.register_attribute(Blog, 'posts', uselist=True,
+                    backref='blog', trackparent=True, useobject=True)
             b = Blog()
             (p1, p2, p3) = (Post(), Post(), Post())
             b.posts.append(p1)