summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-01-04 01:53:42 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2015-01-04 01:53:42 -0500
commit315db703a63f5fe5fecf6417f78ff513ff091966 (patch)
treee7cec92b6fae3c9c0f6e717acaa8fdde5ce780f6
parentb1928d72098dd68c8aba468d94407f991f30d465 (diff)
downloadsqlalchemy-slots.tar.gz
- start trying to move things into __slots__. This seems to reduce theslots
size of the many per-column objects we're hitting, but somehow the overall memory is hardly being reduced at all in initial testing
-rw-r--r--lib/sqlalchemy/event/attr.py4
-rw-r--r--lib/sqlalchemy/event/base.py2
-rw-r--r--lib/sqlalchemy/event/registry.py4
-rw-r--r--lib/sqlalchemy/orm/attributes.py97
-rw-r--r--lib/sqlalchemy/orm/base.py3
-rw-r--r--lib/sqlalchemy/orm/interfaces.py4
-rw-r--r--lib/sqlalchemy/orm/properties.py1
-rw-r--r--lib/sqlalchemy/sql/default_comparator.py46
-rw-r--r--lib/sqlalchemy/sql/operators.py3
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py18
-rw-r--r--lib/sqlalchemy/sql/type_api.py46
-rw-r--r--test/sql/test_operators.py16
12 files changed, 141 insertions, 103 deletions
diff --git a/lib/sqlalchemy/event/attr.py b/lib/sqlalchemy/event/attr.py
index be2a82208..5e3499209 100644
--- a/lib/sqlalchemy/event/attr.py
+++ b/lib/sqlalchemy/event/attr.py
@@ -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
diff --git a/lib/sqlalchemy/event/base.py b/lib/sqlalchemy/event/base.py
index 4925f6ffa..37bd2c49e 100644
--- a/lib/sqlalchemy/event/base.py
+++ b/lib/sqlalchemy/event/base.py
@@ -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
diff --git a/lib/sqlalchemy/event/registry.py b/lib/sqlalchemy/event/registry.py
index 5b422c401..fc26f91d7 100644
--- a/lib/sqlalchemy/event/registry.py
+++ b/lib/sqlalchemy/event/registry.py
@@ -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
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index 2b4c3ec75..e9c8c511a 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -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:
diff --git a/lib/sqlalchemy/orm/base.py b/lib/sqlalchemy/orm/base.py
index 3390ceec4..afeeba322 100644
--- a/lib/sqlalchemy/orm/base.py
+++ b/lib/sqlalchemy/orm/base.py
@@ -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__ = ()
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index ad2452c1b..bff73258c 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -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
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 62ea93fb3..291fabdd0 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -224,6 +224,7 @@ class ColumnProperty(StrategizedProperty):
:attr:`.TypeEngine.comparator_factory`
"""
+
@util.memoized_instancemethod
def __clause_element__(self):
if self.adapter:
diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py
index d26fdc455..c898b78d6 100644
--- a/lib/sqlalchemy/sql/default_comparator.py
+++ b/lib/sqlalchemy/sql/default_comparator.py
@@ -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()
diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py
index a328b023e..f71cba913 100644
--- a/lib/sqlalchemy/sql/operators.py
+++ b/lib/sqlalchemy/sql/operators.py
@@ -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."""
diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py
index 7bb5c5515..9b0d26601 100644
--- a/lib/sqlalchemy/sql/sqltypes.py
+++ b/lib/sqlalchemy/sql/sqltypes.py
@@ -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
diff --git a/lib/sqlalchemy/sql/type_api.py b/lib/sqlalchemy/sql/type_api.py
index 03fed3878..834640928 100644
--- a/lib/sqlalchemy/sql/type_api.py
+++ b/lib/sqlalchemy/sql/type_api.py
@@ -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
diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py
index 3b8b20513..0985020d1 100644
--- a/test/sql/test_operators.py
+++ b/test/sql/test_operators.py
@@ -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)