]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- start trying to move things into __slots__. This seems to reduce the
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 4 Jan 2015 06:53:42 +0000 (01:53 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 4 Jan 2015 06:53:42 +0000 (01:53 -0500)
size of the many per-column objects we're hitting, but somehow the overall memory is
hardly being reduced at all in initial testing

12 files changed:
lib/sqlalchemy/event/attr.py
lib/sqlalchemy/event/base.py
lib/sqlalchemy/event/registry.py
lib/sqlalchemy/orm/attributes.py
lib/sqlalchemy/orm/base.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/sql/default_comparator.py
lib/sqlalchemy/sql/operators.py
lib/sqlalchemy/sql/sqltypes.py
lib/sqlalchemy/sql/type_api.py
test/sql/test_operators.py

index be2a82208396a4dc1951c78b99901a221570df49..5e3499209b0951674d65261d5e33a2f66e260788 100644 (file)
@@ -340,6 +340,8 @@ class _ListenerCollection(RefCollection, _CompoundListener):
 
 
 class _JoinedDispatchDescriptor(object):
+    __slots__ = 'name',
+
     def __init__(self, name):
         self.name = name
 
@@ -357,6 +359,8 @@ class _JoinedDispatchDescriptor(object):
 class _JoinedListener(_CompoundListener):
     _exec_once = False
 
+    __slots__ = 'parent', 'name', 'local', 'parent_listeners'
+
     def __init__(self, parent, name, local):
         self.parent = parent
         self.name = name
index 4925f6fface5bccbc58d998c78e1e1b9bb5b8172..37bd2c49e8a536833fdf5fc3ac17e38ef30a3af9 100644 (file)
@@ -195,6 +195,8 @@ class Events(util.with_metaclass(_EventMeta, object)):
 class _JoinedDispatcher(object):
     """Represent a connection between two _Dispatch objects."""
 
+    __slots__ = 'local', 'parent', '_parent_cls'
+
     def __init__(self, local, parent):
         self.local = local
         self.parent = parent
index 5b422c401982d28e0e9b3a61dc746514235318da..fc26f91d72f5f365497f9962c41a7ce9593a212f 100644 (file)
@@ -140,6 +140,10 @@ class _EventKey(object):
     """Represent :func:`.listen` arguments.
     """
 
+    __slots__ = (
+        'target', 'identifier', 'fn', 'fn_key', 'fn_wrap', 'dispatch_target'
+    )
+
     def __init__(self, target, identifier,
                  fn, dispatch_target, _fn_wrap=None):
         self.target = target
index 2b4c3ec75042208582380f4f0d40d3a431c27bdc..e9c8c511afd7a89cab9f3d6ffa84e75a9a2504be 100644 (file)
@@ -345,18 +345,16 @@ class Event(object):
 
     .. versionadded:: 0.9.0
 
-    """
-
-    impl = None
-    """The :class:`.AttributeImpl` which is the current event initiator.
-    """
+    :var impl: The :class:`.AttributeImpl` which is the current event
+     initiator.
 
-    op = None
-    """The symbol :attr:`.OP_APPEND`, :attr:`.OP_REMOVE` or :attr:`.OP_REPLACE`,
-    indicating the source operation.
+    :var op: The symbol :attr:`.OP_APPEND`, :attr:`.OP_REMOVE` or
+     :attr:`.OP_REPLACE`, indicating the source operation.
 
     """
 
+    __slots__ = 'impl', 'op', 'parent_token'
+
     def __init__(self, attribute_impl, op):
         self.impl = attribute_impl
         self.op = op
@@ -455,6 +453,11 @@ class AttributeImpl(object):
 
         self.expire_missing = expire_missing
 
+    __slots__ = (
+        'class_', 'key', 'callable_', 'dispatch', 'trackparent',
+        'parent_token', 'send_modified_events', 'is_equal', 'expire_missing'
+    )
+
     def __str__(self):
         return "%s.%s" % (self.class_.__name__, self.key)
 
@@ -654,6 +657,23 @@ class ScalarAttributeImpl(AttributeImpl):
     supports_population = True
     collection = False
 
+    __slots__ = '_replace_token', '_append_token', '_remove_token'
+
+    def __init__(self, *arg, **kw):
+        super(ScalarAttributeImpl, self).__init__(*arg, **kw)
+        self._replace_token = self._append_token = None
+        self._remove_token = None
+
+    def _init_append_token(self):
+        self._replace_token = self._append_token = Event(self, OP_REPLACE)
+        return self._replace_token
+
+    _init_append_or_replace_token = _init_append_token
+
+    def _init_remove_token(self):
+        self._remove_token = Event(self, OP_REMOVE)
+        return self._remove_token
+
     def delete(self, state, dict_):
 
         # TODO: catch key errors, convert to attributeerror?
@@ -692,27 +712,18 @@ class ScalarAttributeImpl(AttributeImpl):
         state._modified_event(dict_, self, old)
         dict_[self.key] = value
 
-    @util.memoized_property
-    def _replace_token(self):
-        return Event(self, OP_REPLACE)
-
-    @util.memoized_property
-    def _append_token(self):
-        return Event(self, OP_REPLACE)
-
-    @util.memoized_property
-    def _remove_token(self):
-        return Event(self, OP_REMOVE)
-
     def fire_replace_event(self, state, dict_, value, previous, initiator):
         for fn in self.dispatch.set:
             value = fn(
-                state, value, previous, initiator or self._replace_token)
+                state, value, previous,
+                initiator or self._replace_token or
+                self._init_append_or_replace_token())
         return value
 
     def fire_remove_event(self, state, dict_, value, initiator):
         for fn in self.dispatch.remove:
-            fn(state, value, initiator or self._remove_token)
+            fn(state, value,
+               initiator or self._remove_token or self._init_remove_token())
 
     @property
     def type(self):
@@ -732,9 +743,13 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
     supports_population = True
     collection = False
 
+    __slots__ = ()
+
     def delete(self, state, dict_):
         old = self.get(state, dict_)
-        self.fire_remove_event(state, dict_, old, self._remove_token)
+        self.fire_remove_event(
+            state, dict_, old,
+            self._remove_token or self._init_remove_token())
         del dict_[self.key]
 
     def get_history(self, state, dict_, passive=PASSIVE_OFF):
@@ -807,7 +822,8 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
             self.sethasparent(instance_state(value), state, False)
 
         for fn in self.dispatch.remove:
-            fn(state, value, initiator or self._remove_token)
+            fn(state, value, initiator or
+               self._remove_token or self._init_remove_token())
 
         state._modified_event(dict_, self, value)
 
@@ -819,7 +835,8 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
 
         for fn in self.dispatch.set:
             value = fn(
-                state, value, previous, initiator or self._replace_token)
+                state, value, previous, initiator or
+                self._replace_token or self._init_append_or_replace_token())
 
         state._modified_event(dict_, self, previous)
 
@@ -846,6 +863,8 @@ class CollectionAttributeImpl(AttributeImpl):
     supports_population = True
     collection = True
 
+    __slots__ = 'copy', 'collection_factory', '_append_token', '_remove_token'
+
     def __init__(self, class_, key, callable_, dispatch,
                  typecallable=None, trackparent=False, extension=None,
                  copy_function=None, compare_function=None, **kwargs):
@@ -862,6 +881,8 @@ class CollectionAttributeImpl(AttributeImpl):
             copy_function = self.__copy
         self.copy = copy_function
         self.collection_factory = typecallable
+        self._append_token = None
+        self._remove_token = None
 
         if getattr(self.collection_factory, "_sa_linker", None):
 
@@ -873,6 +894,14 @@ class CollectionAttributeImpl(AttributeImpl):
             def unlink(target, collection, collection_adapter):
                 collection._sa_linker(None)
 
+    def _init_append_token(self):
+        self._append_token = Event(self, OP_APPEND)
+        return self._append_token
+
+    def _init_remove_token(self):
+        self._remove_token = Event(self, OP_REMOVE)
+        return self._remove_token
+
     def __copy(self, item):
         return [y for y in collections.collection_adapter(item)]
 
@@ -915,17 +944,11 @@ class CollectionAttributeImpl(AttributeImpl):
 
         return [(instance_state(o), o) for o in current]
 
-    @util.memoized_property
-    def _append_token(self):
-        return Event(self, OP_APPEND)
-
-    @util.memoized_property
-    def _remove_token(self):
-        return Event(self, OP_REMOVE)
-
     def fire_append_event(self, state, dict_, value, initiator):
         for fn in self.dispatch.append:
-            value = fn(state, value, initiator or self._append_token)
+            value = fn(
+                state, value,
+                initiator or self._append_token or self._init_append_token())
 
         state._modified_event(dict_, self, NEVER_SET, True)
 
@@ -942,7 +965,8 @@ class CollectionAttributeImpl(AttributeImpl):
             self.sethasparent(instance_state(value), state, False)
 
         for fn in self.dispatch.remove:
-            fn(state, value, initiator or self._remove_token)
+            fn(state, value,
+               initiator or self._remove_token or self._init_remove_token())
 
         state._modified_event(dict_, self, NEVER_SET, True)
 
@@ -1134,7 +1158,8 @@ def backref_listeners(attribute, key, uselist):
                 impl.pop(old_state,
                          old_dict,
                          state.obj(),
-                         parent_impl._append_token,
+                         parent_impl._append_token or
+                            parent_impl._init_append_token(),
                          passive=PASSIVE_NO_FETCH)
 
         if child is not None:
index 3390ceec4c21dfe978a7fe0e82ceddc914126d47..afeeba3226e18432238e6d555daf0e5714eb1abc 100644 (file)
@@ -438,6 +438,8 @@ class InspectionAttr(object):
 
     """
 
+    __slots__ = ()
+
     is_selectable = False
     """Return True if this object is an instance of :class:`.Selectable`."""
 
@@ -520,3 +522,4 @@ class _MappedAttribute(object):
     attributes.
 
     """
+    __slots__ = ()
index ad2452c1b9e0c143abe2b0a205777c9b4654d234..bff73258c63ae31019f63fce66216c01051e4cae 100644 (file)
@@ -303,6 +303,8 @@ class PropComparator(operators.ColumnOperators):
 
     """
 
+    __slots__ = 'prop', 'property', '_parentmapper', '_adapt_to_entity'
+
     def __init__(self, prop, parentmapper, adapt_to_entity=None):
         self.prop = self.property = prop
         self._parentmapper = parentmapper
@@ -331,7 +333,7 @@ class PropComparator(operators.ColumnOperators):
         else:
             return self._adapt_to_entity._adapt_element
 
-    @util.memoized_property
+    @property
     def info(self):
         return self.property.info
 
index 62ea93fb30e3ffce31313620b79cc4ee2a748c1e..291fabdd0e31c7b92861525db9ca87a73f11f872 100644 (file)
@@ -224,6 +224,7 @@ class ColumnProperty(StrategizedProperty):
         :attr:`.TypeEngine.comparator_factory`
 
         """
+
         @util.memoized_instancemethod
         def __clause_element__(self):
             if self.adapter:
index d26fdc4556a16f06c9ca304fd420a40b29af36e6..c898b78d6af2a7c99c5d618a15a8fd4785d1be7d 100644 (file)
@@ -9,8 +9,8 @@
 """
 
 from .. import exc, util
-from . import operators
 from . import type_api
+from . import operators
 from .elements import BindParameter, True_, False_, BinaryExpression, \
     Null, _const_expr, _clause_element_as_expr, \
     ClauseList, ColumnElement, TextClause, UnaryExpression, \
@@ -18,7 +18,7 @@ from .elements import BindParameter, True_, False_, BinaryExpression, \
 from .selectable import SelectBase, Alias, Selectable, ScalarSelect
 
 
-class _DefaultColumnComparator(operators.ColumnOperators):
+class _DefaultColumnComparator(object):
     """Defines comparison and math operations.
 
     See :class:`.ColumnOperators` and :class:`.Operators` for descriptions
@@ -26,46 +26,6 @@ class _DefaultColumnComparator(operators.ColumnOperators):
 
     """
 
-    @util.memoized_property
-    def type(self):
-        return self.expr.type
-
-    def operate(self, op, *other, **kwargs):
-        o = self.operators[op.__name__]
-        return o[0](self, self.expr, op, *(other + o[1:]), **kwargs)
-
-    def reverse_operate(self, op, other, **kwargs):
-        o = self.operators[op.__name__]
-        return o[0](self, self.expr, op, other,
-                    reverse=True, *o[1:], **kwargs)
-
-    def _adapt_expression(self, op, other_comparator):
-        """evaluate the return type of <self> <op> <othertype>,
-        and apply any adaptations to the given operator.
-
-        This method determines the type of a resulting binary expression
-        given two source types and an operator.   For example, two
-        :class:`.Column` objects, both of the type :class:`.Integer`, will
-        produce a :class:`.BinaryExpression` that also has the type
-        :class:`.Integer` when compared via the addition (``+``) operator.
-        However, using the addition operator with an :class:`.Integer`
-        and a :class:`.Date` object will produce a :class:`.Date`, assuming
-        "days delta" behavior by the database (in reality, most databases
-        other than Postgresql don't accept this particular operation).
-
-        The method returns a tuple of the form <operator>, <type>.
-        The resulting operator and type will be those applied to the
-        resulting :class:`.BinaryExpression` as the final operator and the
-        right-hand side of the expression.
-
-        Note that only a subset of operators make usage of
-        :meth:`._adapt_expression`,
-        including math operators and user-defined operators, but not
-        boolean comparison or special SQL keywords like MATCH or BETWEEN.
-
-        """
-        return op, other_comparator.type
-
     def _boolean_compare(self, expr, op, obj, negate=None, reverse=False,
                          _python_is_types=(util.NoneType, bool),
                          result_type = None,
@@ -320,3 +280,5 @@ class _DefaultColumnComparator(operators.ColumnOperators):
             return expr._bind_param(operator, other)
         else:
             return other
+
+the_comparator = _DefaultColumnComparator()
index a328b023e6e0fe30206cce01303867bd82652c00..f71cba91306ea2c78e171884e400b73ebc397356 100644 (file)
@@ -38,6 +38,7 @@ class Operators(object):
     :class:`.ColumnOperators`.
 
     """
+    __slots__ = ()
 
     def __and__(self, other):
         """Implement the ``&`` operator.
@@ -267,6 +268,8 @@ class ColumnOperators(Operators):
 
     """
 
+    __slots__ = ()
+
     timetuple = None
     """Hack, allows datetime objects to be compared on the LHS."""
 
index 7bb5c55151f24d199f138fb84e2a81f386dc45a7..9b0d2660137c40275ae8dcbd36229662f5acf835 100644 (file)
@@ -14,7 +14,6 @@ import codecs
 
 from .type_api import TypeEngine, TypeDecorator, to_instance
 from .elements import quoted_name, type_coerce, _defer_name
-from .default_comparator import _DefaultColumnComparator
 from .. import exc, util, processors
 from .base import _bind_or_error, SchemaEventTarget
 from . import operators
@@ -1704,19 +1703,4 @@ type_api.NULLTYPE = NULLTYPE
 type_api.MATCHTYPE = MATCHTYPE
 type_api._type_map = _type_map
 
-# this one, there's all kinds of ways to play it, but at the EOD
-# there's just a giant dependency cycle between the typing system and
-# the expression element system, as you might expect.   We can use
-# importlaters or whatnot, but the typing system just necessarily has
-# to have some kind of connection like this.  right now we're injecting the
-# _DefaultColumnComparator implementation into the TypeEngine.Comparator
-# interface.  Alternatively TypeEngine.Comparator could have an "impl"
-# injected, though just injecting the base is simpler, error free, and more
-# performant.
-
-
-class Comparator(_DefaultColumnComparator):
-    BOOLEANTYPE = BOOLEANTYPE
-
-TypeEngine.Comparator.__bases__ = (
-    Comparator, ) + TypeEngine.Comparator.__bases__
+TypeEngine.Comparator.BOOLEANTYPE = BOOLEANTYPE
index 03fed3878f198dcf81b92fbac9d760c99f63fe9c..834640928bc7ad9efa34192530f83138e80a6fa9 100644 (file)
@@ -21,6 +21,7 @@ NULLTYPE = None
 STRINGTYPE = None
 MATCHTYPE = None
 
+
 class TypeEngine(Visitable):
     """The ultimate base class for all SQL datatypes.
 
@@ -45,9 +46,51 @@ class TypeEngine(Visitable):
 
 
         """
+        __slots__ = 'expr', 'type'
 
         def __init__(self, expr):
             self.expr = expr
+            self.type = expr.type
+
+        @util.dependencies('sqlalchemy.sql.default_comparator')
+        def operate(self, default_comparator, op, *other, **kwargs):
+            comp = default_comparator.the_comparator
+            o = comp.operators[op.__name__]
+            return o[0](comp, self.expr, op, *(other + o[1:]), **kwargs)
+
+        @util.dependencies('sqlalchemy.sql.default_comparator')
+        def reverse_operate(self, default_comparator, op, other, **kwargs):
+            comp = default_comparator.the_comparator
+            o = comp.operators[op.__name__]
+            return o[0](comp, self.expr, op, other,
+                        reverse=True, *o[1:], **kwargs)
+
+        def _adapt_expression(self, op, other_comparator):
+            """evaluate the return type of <self> <op> <othertype>,
+            and apply any adaptations to the given operator.
+
+            This method determines the type of a resulting binary expression
+            given two source types and an operator.   For example, two
+            :class:`.Column` objects, both of the type :class:`.Integer`, will
+            produce a :class:`.BinaryExpression` that also has the type
+            :class:`.Integer` when compared via the addition (``+``) operator.
+            However, using the addition operator with an :class:`.Integer`
+            and a :class:`.Date` object will produce a :class:`.Date`, assuming
+            "days delta" behavior by the database (in reality, most databases
+            other than Postgresql don't accept this particular operation).
+
+            The method returns a tuple of the form <operator>, <type>.
+            The resulting operator and type will be those applied to the
+            resulting :class:`.BinaryExpression` as the final operator and the
+            right-hand side of the expression.
+
+            Note that only a subset of operators make usage of
+            :meth:`._adapt_expression`,
+            including math operators and user-defined operators, but not
+            boolean comparison or special SQL keywords like MATCH or BETWEEN.
+
+            """
+            return op, other_comparator.type
 
         def __reduce__(self):
             return _reconstitute_comparator, (self.expr, )
@@ -454,6 +497,8 @@ class UserDefinedType(TypeEngine):
     __visit_name__ = "user_defined"
 
     class Comparator(TypeEngine.Comparator):
+        __slots__ = ()
+
         def _adapt_expression(self, op, other_comparator):
             if hasattr(self.type, 'adapt_operator'):
                 util.warn_deprecated(
@@ -617,6 +662,7 @@ class TypeDecorator(TypeEngine):
     """
 
     class Comparator(TypeEngine.Comparator):
+        __slots__ = ()
 
         def operate(self, op, *other, **kwargs):
             kwargs['_python_is_types'] = self.expr.type.coerce_to_is_types
index 3b8b2051387a7944acd80e92b8674d6eca142aa3..0985020d13dc4325eb46447b399143ee1354d5e0 100644 (file)
@@ -361,7 +361,7 @@ class CustomComparatorTest(_CustomComparatorTests, fixtures.TestBase):
             class comparator_factory(TypeEngine.Comparator):
 
                 def __init__(self, expr):
-                    self.expr = expr
+                    super(MyInteger.comparator_factory, self).__init__(expr)
 
                 def __add__(self, other):
                     return self.expr.op("goofy")(other)
@@ -382,7 +382,7 @@ class TypeDecoratorComparatorTest(_CustomComparatorTests, fixtures.TestBase):
             class comparator_factory(TypeDecorator.Comparator):
 
                 def __init__(self, expr):
-                    self.expr = expr
+                    super(MyInteger.comparator_factory, self).__init__(expr)
 
                 def __add__(self, other):
                     return self.expr.op("goofy")(other)
@@ -404,7 +404,7 @@ class TypeDecoratorTypeDecoratorComparatorTest(
             class comparator_factory(TypeDecorator.Comparator):
 
                 def __init__(self, expr):
-                    self.expr = expr
+                    super(MyIntegerOne.comparator_factory, self).__init__(expr)
 
                 def __add__(self, other):
                     return self.expr.op("goofy")(other)
@@ -429,7 +429,9 @@ class TypeDecoratorWVariantComparatorTest(
             class comparator_factory(TypeEngine.Comparator):
 
                 def __init__(self, expr):
-                    self.expr = expr
+                    super(
+                        SomeOtherInteger.comparator_factory,
+                        self).__init__(expr)
 
                 def __add__(self, other):
                     return self.expr.op("not goofy")(other)
@@ -443,7 +445,7 @@ class TypeDecoratorWVariantComparatorTest(
             class comparator_factory(TypeDecorator.Comparator):
 
                 def __init__(self, expr):
-                    self.expr = expr
+                    super(MyInteger.comparator_factory, self).__init__(expr)
 
                 def __add__(self, other):
                     return self.expr.op("goofy")(other)
@@ -464,7 +466,7 @@ class CustomEmbeddedinTypeDecoratorTest(
             class comparator_factory(TypeEngine.Comparator):
 
                 def __init__(self, expr):
-                    self.expr = expr
+                    super(MyInteger.comparator_factory, self).__init__(expr)
 
                 def __add__(self, other):
                     return self.expr.op("goofy")(other)
@@ -486,7 +488,7 @@ class NewOperatorTest(_CustomComparatorTests, fixtures.TestBase):
             class comparator_factory(TypeEngine.Comparator):
 
                 def __init__(self, expr):
-                    self.expr = expr
+                    super(MyInteger.comparator_factory, self).__init__(expr)
 
                 def foob(self, other):
                     return self.expr.op("foob")(other)