summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-01-05 19:02:08 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2015-01-05 19:02:08 -0500
commit1104dcaa67062f27bf7519c8589f550bd5d5b4af (patch)
tree702802bd3f0e5db235a109ba48707ea262f1782b /lib/sqlalchemy/orm
parent41ae0270d99793608ce563b84e7befb3aa39252e (diff)
downloadsqlalchemy-1104dcaa67062f27bf7519c8589f550bd5d5b4af.tar.gz
- add MemoizedSlots, a generalized solution to using __getattr__
for memoization on a class that uses slots. - apply many more __slots__. mem use for nova now at 46% savings
Diffstat (limited to 'lib/sqlalchemy/orm')
-rw-r--r--lib/sqlalchemy/orm/base.py7
-rw-r--r--lib/sqlalchemy/orm/descriptor_props.py4
-rw-r--r--lib/sqlalchemy/orm/interfaces.py51
-rw-r--r--lib/sqlalchemy/orm/mapper.py2
-rw-r--r--lib/sqlalchemy/orm/properties.py20
-rw-r--r--lib/sqlalchemy/orm/relationships.py1
-rw-r--r--lib/sqlalchemy/orm/util.py10
7 files changed, 76 insertions, 19 deletions
diff --git a/lib/sqlalchemy/orm/base.py b/lib/sqlalchemy/orm/base.py
index afeeba322..c5c8c5e2e 100644
--- a/lib/sqlalchemy/orm/base.py
+++ b/lib/sqlalchemy/orm/base.py
@@ -437,7 +437,6 @@ class InspectionAttr(object):
here intact for forwards-compatibility.
"""
-
__slots__ = ()
is_selectable = False
@@ -490,6 +489,12 @@ class InspectionAttr(object):
"""
+
+class InspectionAttrInfo(InspectionAttr):
+ """Adds the ``.info`` attribute to :class:`.Inspectionattr`.
+
+ """
+
@util.memoized_property
def info(self):
"""Info dictionary associated with the object, allowing user-defined
diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py
index 19ff71f73..e68ff1bea 100644
--- a/lib/sqlalchemy/orm/descriptor_props.py
+++ b/lib/sqlalchemy/orm/descriptor_props.py
@@ -143,6 +143,7 @@ class CompositeProperty(DescriptorProperty):
class. **Deprecated.** Please see :class:`.AttributeEvents`.
"""
+ super(CompositeProperty, self).__init__()
self.attrs = attrs
self.composite_class = class_
@@ -471,6 +472,7 @@ class ConcreteInheritedProperty(DescriptorProperty):
return comparator_callable
def __init__(self):
+ super(ConcreteInheritedProperty, self).__init__()
def warn():
raise AttributeError("Concrete %s does not implement "
"attribute %r at the instance level. Add "
@@ -555,6 +557,7 @@ class SynonymProperty(DescriptorProperty):
more complicated attribute-wrapping schemes than synonyms.
"""
+ super(SynonymProperty, self).__init__()
self.name = name
self.map_column = map_column
@@ -684,6 +687,7 @@ class ComparableProperty(DescriptorProperty):
.. versionadded:: 1.0.0
"""
+ super(ComparableProperty, self).__init__()
self.descriptor = descriptor
self.comparator_factory = comparator_factory
self.doc = doc or (descriptor and descriptor.__doc__) or None
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index 68b86268c..346e2412e 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -24,7 +24,8 @@ from .. import util
from ..sql import operators
from .base import (ONETOMANY, MANYTOONE, MANYTOMANY,
EXT_CONTINUE, EXT_STOP, NOT_EXTENSION)
-from .base import InspectionAttr, _MappedAttribute
+from .base import (InspectionAttr, InspectionAttr,
+ InspectionAttrInfo, _MappedAttribute)
import collections
# imported later
@@ -48,7 +49,7 @@ __all__ = (
)
-class MapperProperty(_MappedAttribute, InspectionAttr):
+class MapperProperty(_MappedAttribute, InspectionAttr, util.MemoizedSlots):
"""Manage the relationship of a ``Mapper`` to a single class
attribute, as well as that attribute as it appears on individual
instances of the class, including attribute instrumentation,
@@ -63,6 +64,11 @@ class MapperProperty(_MappedAttribute, InspectionAttr):
"""
+ __slots__ = (
+ '_configure_started', '_configure_finished', 'parent', 'key',
+ 'info'
+ )
+
cascade = frozenset()
"""The set of 'cascade' attribute names.
@@ -78,6 +84,32 @@ class MapperProperty(_MappedAttribute, InspectionAttr):
"""
+ def _memoized_attr_info(self):
+ """Info dictionary associated with the object, allowing user-defined
+ data to be associated with this :class:`.InspectionAttr`.
+
+ The dictionary is generated when first accessed. Alternatively,
+ it can be specified as a constructor argument to the
+ :func:`.column_property`, :func:`.relationship`, or :func:`.composite`
+ functions.
+
+ .. versionadded:: 0.8 Added support for .info to all
+ :class:`.MapperProperty` subclasses.
+
+ .. versionchanged:: 1.0.0 :attr:`.InspectionAttr.info` moved
+ from :class:`.MapperProperty` so that it can apply to a wider
+ variety of ORM and extension constructs.
+
+ .. seealso::
+
+ :attr:`.QueryableAttribute.info`
+
+ :attr:`.SchemaItem.info`
+
+ """
+ return {}
+
+
def setup(self, context, entity, path, adapter, **kwargs):
"""Called by Query for the purposes of constructing a SQL statement.
@@ -139,8 +171,9 @@ class MapperProperty(_MappedAttribute, InspectionAttr):
"""
- _configure_started = False
- _configure_finished = False
+ def __init__(self):
+ self._configure_started = False
+ self._configure_finished = False
def init(self):
"""Called after all mappers are created to assemble
@@ -422,6 +455,8 @@ class StrategizedProperty(MapperProperty):
"""
+ __slots__ = '_strategies', 'strategy'
+
strategy_wildcard_key = None
def _get_context_loader(self, context, path):
@@ -485,14 +520,14 @@ class StrategizedProperty(MapperProperty):
not mapper.class_manager._attr_has_impl(self.key):
self.strategy.init_class_attribute(mapper)
- _strategies = collections.defaultdict(dict)
+ _all_strategies = collections.defaultdict(dict)
@classmethod
def strategy_for(cls, **kw):
def decorate(dec_cls):
dec_cls._strategy_keys = []
key = tuple(sorted(kw.items()))
- cls._strategies[cls][key] = dec_cls
+ cls._all_strategies[cls][key] = dec_cls
dec_cls._strategy_keys.append(key)
return dec_cls
return decorate
@@ -500,8 +535,8 @@ class StrategizedProperty(MapperProperty):
@classmethod
def _strategy_lookup(cls, *key):
for prop_cls in cls.__mro__:
- if prop_cls in cls._strategies:
- strategies = cls._strategies[prop_cls]
+ if prop_cls in cls._all_strategies:
+ strategies = cls._all_strategies[prop_cls]
try:
return strategies[key]
except KeyError:
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 9fe6b77f0..0469c2139 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -2787,6 +2787,8 @@ def _event_on_init(state, args, kwargs):
class _ColumnMapping(dict):
"""Error reporting helper for mapper._columntoproperty."""
+ __slots__ = 'mapper',
+
def __init__(self, mapper):
self.mapper = mapper
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 291fabdd0..d51b6920d 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -34,6 +34,13 @@ class ColumnProperty(StrategizedProperty):
strategy_wildcard_key = 'column'
+ __slots__ = (
+ '_orig_columns', 'columns', 'group', 'deferred',
+ 'instrument', 'comparator_factory', 'descriptor', 'extension',
+ 'active_history', 'expire_on_flush', 'info', 'doc',
+ 'strategy_class', '_creation_order', '_is_polymorphic_discriminator',
+ '_mapped_by_synonym')
+
def __init__(self, *columns, **kwargs):
"""Provide a column-level property for use with a Mapper.
@@ -109,6 +116,7 @@ class ColumnProperty(StrategizedProperty):
**Deprecated.** Please see :class:`.AttributeEvents`.
"""
+ super(ColumnProperty, self).__init__()
self._orig_columns = [expression._labeled(c) for c in columns]
self.columns = [expression._labeled(_orm_full_deannotate(c))
for c in columns]
@@ -206,7 +214,7 @@ class ColumnProperty(StrategizedProperty):
elif dest_state.has_identity and self.key not in dest_dict:
dest_state._expire_attributes(dest_dict, [self.key])
- class Comparator(PropComparator):
+ class Comparator(util.MemoizedSlots, PropComparator):
"""Produce boolean, comparison, and other operators for
:class:`.ColumnProperty` attributes.
@@ -225,8 +233,9 @@ class ColumnProperty(StrategizedProperty):
"""
- @util.memoized_instancemethod
- def __clause_element__(self):
+ __slots__ = '__clause_element__', 'info'
+
+ def _memoized_method___clause_element__(self):
if self.adapter:
return self.adapter(self.prop.columns[0])
else:
@@ -234,15 +243,14 @@ class ColumnProperty(StrategizedProperty):
"parententity": self._parentmapper,
"parentmapper": self._parentmapper})
- @util.memoized_property
- def info(self):
+ def _memoized_attr_info(self):
ce = self.__clause_element__()
try:
return ce.info
except AttributeError:
return self.prop.info
- def __getattr__(self, key):
+ def _fallback_getattr(self, key):
"""proxy attribute access down to the mapped column.
this allows user-defined comparison methods to be accessed.
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index d3ae107b9..df2250a4c 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -775,6 +775,7 @@ class RelationshipProperty(StrategizedProperty):
"""
+ super(RelationshipProperty, self).__init__()
self.uselist = uselist
self.argument = argument
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index 4be8d19ff..ee629b034 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -30,6 +30,10 @@ class CascadeOptions(frozenset):
'all', 'none', 'delete-orphan'])
_allowed_cascades = all_cascades
+ __slots__ = (
+ 'save_update', 'delete', 'refresh_expire', 'merge',
+ 'expunge', 'delete_orphan')
+
def __new__(cls, value_list):
if isinstance(value_list, str) or value_list is None:
return cls.from_string(value_list)
@@ -38,10 +42,7 @@ class CascadeOptions(frozenset):
raise sa_exc.ArgumentError(
"Invalid cascade option(s): %s" %
", ".join([repr(x) for x in
- sorted(
- values.difference(cls._allowed_cascades)
- )])
- )
+ sorted(values.difference(cls._allowed_cascades))]))
if "all" in values:
values.update(cls._add_w_all_cascades)
@@ -76,6 +77,7 @@ class CascadeOptions(frozenset):
]
return cls(values)
+
def _validator_events(
desc, key, validator, include_removes, include_backrefs):
"""Runs a validation method on an attribute value to be set or