]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- cut down on a few hundred method calls
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 11 Jan 2010 03:16:10 +0000 (03:16 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 11 Jan 2010 03:16:10 +0000 (03:16 +0000)
examples/beaker_caching/ad_hoc.py
examples/beaker_caching/demo.py
examples/beaker_caching/meta.py
examples/beaker_caching/model.py
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/session.py
lib/sqlalchemy/orm/state.py
lib/sqlalchemy/orm/util.py
test/aaa_profiling/test_orm.py

index 10d80a72c90cd9b91fe65812aec0d39710d584b0..bb6b0823bed7e0c4089cb2e15eca6ce5ead89db0 100644 (file)
@@ -30,7 +30,7 @@ def load_name_range(start, end, invalidate=False):
     """
     q = Session.query(Person).\
             filter(Person.name.between("person %.2d" % start, "person %.2d" % end)).\
-            options(*cache_address_bits).\
+            options(cache_address_bits).\
             options(FromCache("default", "name_range"))
 
     # have the "addresses" collection cached separately
index c4da9f4b29b2f0273739f36f2296db86fed429a6..f2f038d28bae308c9dfe986abcaaf262b103f353 100644 (file)
@@ -11,7 +11,7 @@ from meta import Session
 from sqlalchemy.orm import eagerload
 import os
 
-for p in Session.query(Person).options(eagerload(Person.addresses), *cache_address_bits):
+for p in Session.query(Person).options(eagerload(Person.addresses), cache_address_bits):
     print p.format_full()
 
 
index 458a2c68f9f346457204e29f6b35041f6a67d17a..026d2ce67df118a603ebd4555c82b68fd4ae3279 100644 (file)
@@ -89,7 +89,6 @@ class CachingQuery(Query):
         """
         if hasattr(self, 'cache_region'):
             cache, cache_key = self._get_cache_plus_key()
-            
             ret = cache.get_value(cache_key, createfunc=lambda: list(Query.__iter__(self)))
             return iter(self.session.merge(x, load=False) for x in ret)
         else:
index 73f4a80d6fcec58b7b9d399c708df42c5a67aad0..25ce162fc9617f6c81f019afbddd921504993fe8 100644 (file)
@@ -92,9 +92,9 @@ class Person(Base):
 # Caching options.   A set of three FromCache options
 # which can be applied to Query(), causing the "lazy load"
 # of these attributes to be loaded from cache.
-cache_address_bits = [
+cache_address_bits = (
                 FromCache("default", "byid", PostalCode.city), 
                 FromCache("default", "byid", City.country), 
                 FromCache("default", "byid", Address.postal_code),
-            ]
+            )
 
index 67c9664ad9a00311c99d8162de5628c27e60852d..3021f99042fd13787b8c87fd1f46b39ad9ec36b1 100644 (file)
@@ -1479,7 +1479,13 @@ def is_instrumented(instance, key):
     return manager_of_class(instance.__class__).is_instrumented(key, search=True)
 
 class InstrumentationRegistry(object):
-    """Private instrumentation registration singleton."""
+    """Private instrumentation registration singleton.
+    
+    All classes are routed through this registry 
+    when first instrumented, however the InstrumentationRegistry
+    is not actually needed unless custom ClassManagers are in use.
+    
+    """
 
     _manager_finders = weakref.WeakKeyDictionary()
     _state_finders = util.WeakIdentityMapping()
@@ -1510,6 +1516,9 @@ class InstrumentationRegistry(object):
             manager = _ClassInstrumentationAdapter(class_, manager)
             
         if factory != ClassManager and not self._extended:
+            # somebody invoked a custom ClassManager.
+            # reinstall global "getter" functions with the more 
+            # expensive ones.
             self._extended = True
             _install_lookup_strategy(self)
         
@@ -1548,6 +1557,7 @@ class InstrumentationRegistry(object):
         return factories
 
     def manager_of_class(self, cls):
+        # this is only called when alternate instrumentation has been established
         if cls is None:
             return None
         try:
@@ -1583,7 +1593,9 @@ class InstrumentationRegistry(object):
             del self._manager_finders[class_]
             del self._state_finders[class_]
             del self._dict_finders[class_]
-
+        if ClassManager.MANAGER_ATTR in class_.__dict__:
+            delattr(class_, ClassManager.MANAGER_ATTR)
+        
 instrumentation_registry = InstrumentationRegistry()
 
 def _install_lookup_strategy(implementation):
@@ -1596,16 +1608,23 @@ def _install_lookup_strategy(implementation):
     and unit tests specific to this behavior.
     
     """
-    global instance_state, instance_dict
+    global instance_state, instance_dict, manager_of_class
     if implementation is util.symbol('native'):
         instance_state = attrgetter(ClassManager.STATE_ATTR)
         instance_dict = attrgetter("__dict__")
+        def manager_of_class(cls):
+            return cls.__dict__.get(ClassManager.MANAGER_ATTR, None)
     else:
         instance_state = instrumentation_registry.state_of
         instance_dict = instrumentation_registry.dict_of
+        manager_of_class = instrumentation_registry.manager_of_class
         
-manager_of_class = instrumentation_registry.manager_of_class
 _create_manager_for_cls = instrumentation_registry.create_manager_for_cls
+
+# Install default "lookup" strategies.  These are basically
+# very fast attrgetters for key attributes.
+# When a custom ClassManager is installed, more expensive per-class
+# strategies are copied over these.
 _install_lookup_strategy(util.symbol('native'))
 
 def find_native_user_instrumentation_hook(cls):
index 8399ee1525d48c1c754fa3e1291d759613ca9c34..9e03771051ab87b1e93b2df9c089952c19b08a3b 100644 (file)
@@ -317,7 +317,7 @@ class Mapper(object):
 
         """
         manager = attributes.manager_of_class(self.class_)
-
+        
         if self.non_primary:
             if not manager or manager.mapper is None:
                 raise sa_exc.InvalidRequestError(
@@ -803,7 +803,8 @@ class Mapper(object):
     def get_property(self, key, resolve_synonyms=False, raiseerr=True):
         """return a MapperProperty associated with the given key."""
 
-        self.compile()
+        if not self.compiled:
+            self.compile()
         return self._get_property(key, resolve_synonyms=resolve_synonyms, raiseerr=raiseerr)
 
     def _get_property(self, key, resolve_synonyms=False, raiseerr=True):
@@ -818,7 +819,8 @@ class Mapper(object):
     @property
     def iterate_properties(self):
         """return an iterator of all MapperProperty objects."""
-        self.compile()
+        if not self.compiled:
+            self.compile()
         return self._props.itervalues()
 
     def _mappers_from_spec(self, spec, selectable):
index c8224da87a1c79b09431b4ead38d33ab759ad6bf..5b2fbb5240e47dd3bb3b3d8339939ca73bc7fc4e 100644 (file)
@@ -658,7 +658,7 @@ class Query(object):
         # most MapperOptions write to the '_attributes' dictionary,
         # so copy that as well
         self._attributes = self._attributes.copy()
-        opts = [o for o in util.flatten_iterator(args)]
+        opts = list(util.flatten_iterator(args))
         self._with_options = self._with_options + opts
         if conditional:
             for opt in opts:
index 8217bfa99340db691379d7f2852162bc139ab50c..6590864d6a239eba81ac902d7391ae9745909dbf 100644 (file)
@@ -1104,7 +1104,11 @@ class Session(object):
             util.warn_deprecated("dont_load=True has been renamed to load=False.")
         
         _recursive = {}
-        self._autoflush()
+        
+        if load:
+            # flush current contents if we expect to load data
+            self._autoflush()
+            
         _object_mapper(instance) # verify mapped
         autoflush = self.autoflush
         try:
index 8dd6bd309f369420db0ce97cdcc1e345c245d840..042f06710376a2c601b6d11700e9f6e36cc4ecd2 100644 (file)
@@ -159,11 +159,11 @@ class InstanceState(object):
         if self.modified:
             self._strong_obj = state['instance']
             
-        self.__dict__.update(
+        self.__dict__.update([
             (k, state[k]) for k in (
                 'key', 'load_options', 'expired_attributes', 'mutable_dict'
             ) if k in state 
-        )
+        ])
 
         if 'load_path' in state:
             self.load_path = interfaces.deserialize_path(state['load_path'])
index 60c03be8e87ffda07aa13c3d65cf964eb29173b2..919f980b6e802f16716d639b609658025c484a06 100644 (file)
@@ -7,9 +7,11 @@
 import sqlalchemy.exceptions as sa_exc
 from sqlalchemy import sql, util
 from sqlalchemy.sql import expression, util as sql_util, operators
-from sqlalchemy.orm.interfaces import MapperExtension, EXT_CONTINUE, PropComparator, MapperProperty, AttributeExtension
+from sqlalchemy.orm.interfaces import MapperExtension, EXT_CONTINUE, PropComparator, \
+                                        MapperProperty, AttributeExtension
 from sqlalchemy.orm import attributes, exc
 
+mapperlib = None
 
 all_cascades = frozenset(("delete", "delete-orphan", "all", "merge",
                           "expunge", "save-update", "refresh-expire",
@@ -488,17 +490,27 @@ def _entity_info(entity, compile=True):
     """
     if isinstance(entity, AliasedClass):
         return entity._AliasedClass__mapper, entity._AliasedClass__alias, True
-    elif _is_mapped_class(entity):
-        if isinstance(entity, type):
-            mapper = class_mapper(entity, compile)
-        else:
-            if compile:
-                mapper = entity.compile()
-            else:
-                mapper = entity
-        return mapper, mapper._with_polymorphic_selectable, False
+
+    global mapperlib
+    if mapperlib is None:
+        from sqlalchemy.orm import mapperlib
+    
+    if isinstance(entity, mapperlib.Mapper):
+        mapper = entity
+        
+    elif isinstance(entity, type):
+        class_manager = attributes.manager_of_class(entity)
+        
+        if class_manager is None:
+            return None, entity, False
+            
+        mapper = class_manager.mapper
     else:
         return None, entity, False
+        
+    if compile:
+        mapper = mapper.compile()
+    return mapper, mapper._with_polymorphic_selectable, False
 
 def _entity_descriptor(entity, key):
     """Return attribute/property information given an entity and string name.
@@ -600,8 +612,10 @@ def _state_has_identity(state):
     return bool(state.key)
 
 def _is_mapped_class(cls):
-    from sqlalchemy.orm import mapperlib as mapper
-    if isinstance(cls, (AliasedClass, mapper.Mapper)):
+    global mapperlib
+    if mapperlib is None:
+        from sqlalchemy.orm import mapperlib
+    if isinstance(cls, (AliasedClass, mapperlib.Mapper)):
         return True
     if isinstance(cls, expression.ClauseElement):
         return False
index 58fca389e428e6a7406713e93260134d51ff914f..327f3226fbc2c41150c917be9682478bc436d255 100644 (file)
@@ -58,7 +58,7 @@ class MergeTest(_base.MappedTest):
         # down from 185 on this
         # this is a small slice of a usually bigger
         # operation so using a small variance
-        @profiling.function_call_count(94, variance=0.001)
+        @profiling.function_call_count(87, variance=0.001)
         def go():
             return sess2.merge(p1, load=False)
             
@@ -66,7 +66,7 @@ class MergeTest(_base.MappedTest):
 
         # third call, merge object already present.
         # almost no calls.
-        @profiling.function_call_count(15, variance=0.001)
+        @profiling.function_call_count(10, variance=0.001)
         def go():
             return sess2.merge(p2, load=False)