From: Mike Bayer Date: Fri, 1 Oct 2010 18:23:01 +0000 (-0400) Subject: - reworked the internals of mapper.cascade_iterator() to X-Git-Tag: rel_0_6_5~34 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=755aca7f5f3f33339dce4f4b6b92b8b7a9c3d180;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - reworked the internals of mapper.cascade_iterator() to cut down method calls by about 9% in some circumstances. [ticket:1932] --- diff --git a/CHANGES b/CHANGES index 150cfbfa4f..c6de24280a 100644 --- a/CHANGES +++ b/CHANGES @@ -107,6 +107,10 @@ CHANGES - scoped_session emits a warning when configure() is called if a Session is already present (checks only the current thread) [ticket:1924] + + - reworked the internals of mapper.cascade_iterator() to + cut down method calls by about 9% in some circumstances. + [ticket:1932] - sql - Table.tometadata() now copies Index objects associated diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 0a65c8a44f..fa27859ecb 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -76,7 +76,7 @@ class MapperExtension(object): mapper activity will not be performed. """ - + def instrument_class(self, mapper, class_): """Receive a class when the mapper is first constructed, and has applied instrumentation to the mapped class. @@ -418,6 +418,13 @@ class MapperProperty(object): attribute access, loading behavior, and dependency calculations. """ + cascade = () + """The set of 'cascade' attribute names. + + This collection is checked before the 'cascade_iterator' method is called. + + """ + def setup(self, context, entity, path, adapter, **kwargs): """Called by Query for the purposes of constructing a SQL statement. @@ -469,6 +476,11 @@ class MapperProperty(object): halt_on=None): """Iterate through instances related to the given instance for a particular 'cascade', starting with this MapperProperty. + + Return an iterator3-tuples (instance, mapper, state). + + Note that the 'cascade' collection on this MapperProperty is + checked first for the given type before cascade_iterator is called. See PropertyLoader for the related instance implementation. """ diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 49f5d2190f..d27b996018 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -1401,25 +1401,30 @@ class Mapper(object): """ visited_instances = util.IdentitySet() - visitables = [(self._props.itervalues(), 'property', state)] + prp, mpp = object(), object() + + visitables = [(deque(self._props.values()), prp, state)] while visitables: iterator, item_type, parent_state = visitables[-1] - try: - if item_type == 'property': - prop = iterator.next() - visitables.append( - (prop.cascade_iterator(type_, parent_state, - visited_instances, halt_on), 'mapper', None) - ) - elif item_type == 'mapper': - instance, instance_mapper, corresponding_state = \ - iterator.next() - yield (instance, instance_mapper) - visitables.append((instance_mapper._props.itervalues(), - 'property', corresponding_state)) - except StopIteration: + if not iterator: visitables.pop() + continue + + if item_type is prp: + prop = iterator.popleft() + if type_ not in prop.cascade: + continue + queue = deque(prop.cascade_iterator(type_, parent_state, + visited_instances, halt_on)) + if queue: + visitables.append((queue,mpp, None)) + elif item_type is mpp: + instance, instance_mapper, corresponding_state = \ + iterator.popleft() + yield (instance, instance_mapper) + visitables.append((deque(instance_mapper._props.values()), + prp, corresponding_state)) @_memoized_compiled_property def _compiled_cache(self): diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index a5e6930b28..4efd2acc90 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -868,7 +868,8 @@ class RelationshipProperty(StrategizedProperty): # cascade using the mapper local to this # object, so that its individual properties are located instance_mapper = instance_state.manager.mapper - yield (c, instance_mapper, instance_state) + yield c, instance_mapper, instance_state + def _add_reverse_property(self, key): other = self.mapper.get_property(key, _compile_mappers=False) diff --git a/test/aaa_profiling/test_zoomark_orm.py b/test/aaa_profiling/test_zoomark_orm.py index 3e30efa245..623ec67ba1 100644 --- a/test/aaa_profiling/test_zoomark_orm.py +++ b/test/aaa_profiling/test_zoomark_orm.py @@ -335,11 +335,11 @@ class ZooMarkTest(TestBase): def test_profile_1_create_tables(self): self.test_baseline_1_create_tables() - @profiling.function_call_count(9225) + @profiling.function_call_count(8469) def test_profile_1a_populate(self): self.test_baseline_1a_populate() - @profiling.function_call_count(640) + @profiling.function_call_count(591) def test_profile_2_insert(self): self.test_baseline_2_insert()