summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/collections.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm/collections.py')
-rw-r--r--lib/sqlalchemy/orm/collections.py150
1 files changed, 74 insertions, 76 deletions
diff --git a/lib/sqlalchemy/orm/collections.py b/lib/sqlalchemy/orm/collections.py
index 03917d112..87e351b6c 100644
--- a/lib/sqlalchemy/orm/collections.py
+++ b/lib/sqlalchemy/orm/collections.py
@@ -1,5 +1,5 @@
# orm/collections.py
-# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
+# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -108,8 +108,7 @@ import weakref
from ..sql import expression
from .. import util, exc as sa_exc
-orm_util = util.importlater("sqlalchemy.orm", "util")
-attributes = util.importlater("sqlalchemy.orm", "attributes")
+from . import base
__all__ = ['collection', 'collection_adapter',
@@ -139,8 +138,8 @@ class _PlainColumnGetter(object):
return self.cols
def __call__(self, value):
- state = attributes.instance_state(value)
- m = orm_util._state_mapper(state)
+ state = base.instance_state(value)
+ m = base._state_mapper(state)
key = [
m._get_state_attr_by_column(state, state.dict, col)
@@ -167,8 +166,8 @@ class _SerializableColumnGetter(object):
return _SerializableColumnGetter, (self.colkeys,)
def __call__(self, value):
- state = attributes.instance_state(value)
- m = orm_util._state_mapper(state)
+ state = base.instance_state(value)
+ m = base._state_mapper(state)
key = [m._get_state_attr_by_column(
state, state.dict,
m.mapped_table.columns[k])
@@ -352,7 +351,7 @@ class collection(object):
promulgation to collection events.
"""
- setattr(fn, '_sa_instrument_role', 'appender')
+ fn._sa_instrument_role = 'appender'
return fn
@staticmethod
@@ -379,7 +378,7 @@ class collection(object):
promulgation to collection events.
"""
- setattr(fn, '_sa_instrument_role', 'remover')
+ fn._sa_instrument_role = 'remover'
return fn
@staticmethod
@@ -393,7 +392,7 @@ class collection(object):
def __iter__(self): ...
"""
- setattr(fn, '_sa_instrument_role', 'iterator')
+ fn._sa_instrument_role = 'iterator'
return fn
@staticmethod
@@ -414,7 +413,7 @@ class collection(object):
def extend(self, items): ...
"""
- setattr(fn, '_sa_instrumented', True)
+ fn._sa_instrumented = True
return fn
@staticmethod
@@ -428,7 +427,7 @@ class collection(object):
that has been linked, or None if unlinking.
"""
- setattr(fn, '_sa_instrument_role', 'linker')
+ fn._sa_instrument_role = 'linker'
return fn
link = linker
@@ -464,7 +463,7 @@ class collection(object):
validation on the values about to be assigned.
"""
- setattr(fn, '_sa_instrument_role', 'converter')
+ fn._sa_instrument_role = 'converter'
return fn
@staticmethod
@@ -484,7 +483,7 @@ class collection(object):
"""
def decorator(fn):
- setattr(fn, '_sa_instrument_before', ('fire_append_event', arg))
+ fn._sa_instrument_before = ('fire_append_event', arg)
return fn
return decorator
@@ -504,8 +503,8 @@ class collection(object):
"""
def decorator(fn):
- setattr(fn, '_sa_instrument_before', ('fire_append_event', arg))
- setattr(fn, '_sa_instrument_after', 'fire_remove_event')
+ fn._sa_instrument_before = ('fire_append_event', arg)
+ fn._sa_instrument_after = 'fire_remove_event'
return fn
return decorator
@@ -526,7 +525,7 @@ class collection(object):
"""
def decorator(fn):
- setattr(fn, '_sa_instrument_before', ('fire_remove_event', arg))
+ fn._sa_instrument_before = ('fire_remove_event', arg)
return fn
return decorator
@@ -546,32 +545,13 @@ class collection(object):
"""
def decorator(fn):
- setattr(fn, '_sa_instrument_after', 'fire_remove_event')
+ fn._sa_instrument_after = 'fire_remove_event'
return fn
return decorator
-# public instrumentation interface for 'internally instrumented'
-# implementations
-def collection_adapter(collection):
- """Fetch the :class:`.CollectionAdapter` for a collection."""
-
- return getattr(collection, '_sa_adapter', None)
-
-
-def collection_iter(collection):
- """Iterate over an object supporting the @iterator or __iter__ protocols.
-
- If the collection is an ORM collection, it need not be attached to an
- object to be iterable.
-
- """
- try:
- return getattr(collection, '_sa_iterator',
- getattr(collection, '__iter__'))()
- except AttributeError:
- raise TypeError("'%s' object is not iterable" %
- type(collection).__name__)
+collection_adapter = operator.attrgetter('_sa_adapter')
+"""Fetch the :class:`.CollectionAdapter` for a collection."""
class CollectionAdapter(object):
@@ -584,8 +564,6 @@ class CollectionAdapter(object):
The ORM uses :class:`.CollectionAdapter` exclusively for interaction with
entity collections.
- The usage of getattr()/setattr() is currently to allow injection
- of custom methods, such as to unwrap Zope security proxies.
"""
invalidated = False
@@ -609,16 +587,19 @@ class CollectionAdapter(object):
return self.owner_state.manager[self._key].impl
def link_to_self(self, data):
- """Link a collection to this adapter, and fire a link event."""
- setattr(data, '_sa_adapter', self)
- if hasattr(data, '_sa_linker'):
- getattr(data, '_sa_linker')(self)
+ """Link a collection to this adapter"""
+
+ data._sa_adapter = self
+ if data._sa_linker:
+ data._sa_linker(self)
+
def unlink(self, data):
- """Unlink a collection from any adapter, and fire a link event."""
- setattr(data, '_sa_adapter', None)
- if hasattr(data, '_sa_linker'):
- getattr(data, '_sa_linker')(None)
+ """Unlink a collection from any adapter"""
+
+ del data._sa_adapter
+ if data._sa_linker:
+ data._sa_linker(None)
def adapt_like_to_iterable(self, obj):
"""Converts collection-compatible objects to an iterable of values.
@@ -634,7 +615,7 @@ class CollectionAdapter(object):
a default duck-typing-based implementation is used.
"""
- converter = getattr(self._data(), '_sa_converter', None)
+ converter = self._data()._sa_converter
if converter is not None:
return converter(obj)
@@ -655,60 +636,60 @@ class CollectionAdapter(object):
# If the object is an adapted collection, return the (iterable)
# adapter.
if getattr(obj, '_sa_adapter', None) is not None:
- return getattr(obj, '_sa_adapter')
+ return obj._sa_adapter
elif setting_type == dict:
if util.py3k:
return obj.values()
else:
- return getattr(obj, 'itervalues', getattr(obj, 'values'))()
+ return getattr(obj, 'itervalues', obj.values)()
else:
return iter(obj)
def append_with_event(self, item, initiator=None):
"""Add an entity to the collection, firing mutation events."""
- getattr(self._data(), '_sa_appender')(item, _sa_initiator=initiator)
+ self._data()._sa_appender(item, _sa_initiator=initiator)
def append_without_event(self, item):
"""Add or restore an entity to the collection, firing no events."""
- getattr(self._data(), '_sa_appender')(item, _sa_initiator=False)
+ self._data()._sa_appender(item, _sa_initiator=False)
def append_multiple_without_event(self, items):
"""Add or restore an entity to the collection, firing no events."""
- appender = getattr(self._data(), '_sa_appender')
+ appender = self._data()._sa_appender
for item in items:
appender(item, _sa_initiator=False)
def remove_with_event(self, item, initiator=None):
"""Remove an entity from the collection, firing mutation events."""
- getattr(self._data(), '_sa_remover')(item, _sa_initiator=initiator)
+ self._data()._sa_remover(item, _sa_initiator=initiator)
def remove_without_event(self, item):
"""Remove an entity from the collection, firing no events."""
- getattr(self._data(), '_sa_remover')(item, _sa_initiator=False)
+ self._data()._sa_remover(item, _sa_initiator=False)
def clear_with_event(self, initiator=None):
"""Empty the collection, firing a mutation event for each entity."""
- remover = getattr(self._data(), '_sa_remover')
+ remover = self._data()._sa_remover
for item in list(self):
remover(item, _sa_initiator=initiator)
def clear_without_event(self):
"""Empty the collection, firing no events."""
- remover = getattr(self._data(), '_sa_remover')
+ remover = self._data()._sa_remover
for item in list(self):
remover(item, _sa_initiator=False)
def __iter__(self):
"""Iterate over entities in the collection."""
- return iter(getattr(self._data(), '_sa_iterator')())
+ return iter(self._data()._sa_iterator())
def __len__(self):
"""Count entities in the collection."""
- return len(list(getattr(self._data(), '_sa_iterator')()))
+ return len(list(self._data()._sa_iterator()))
def __bool__(self):
return True
@@ -960,7 +941,12 @@ def _instrument_class(cls):
for role, method_name in roles.items():
setattr(cls, '_sa_%s' % role, getattr(cls, method_name))
- setattr(cls, '_sa_instrumented', id(cls))
+ cls._sa_adapter = None
+ if not hasattr(cls, '_sa_linker'):
+ cls._sa_linker = None
+ if not hasattr(cls, '_sa_converter'):
+ cls._sa_converter = None
+ cls._sa_instrumented = id(cls)
def _instrument_membership_mutator(method, before, argument, after):
@@ -999,7 +985,7 @@ def _instrument_membership_mutator(method, before, argument, after):
if initiator is False:
executor = None
else:
- executor = getattr(args[0], '_sa_adapter', None)
+ executor = args[0]._sa_adapter
if before and executor:
getattr(executor, before)(value, initiator)
@@ -1024,33 +1010,33 @@ def __set(collection, item, _sa_initiator=None):
"""Run set events, may eventually be inlined into decorators."""
if _sa_initiator is not False:
- executor = getattr(collection, '_sa_adapter', None)
+ executor = collection._sa_adapter
if executor:
- item = getattr(executor, 'fire_append_event')(item, _sa_initiator)
+ item = executor.fire_append_event(item, _sa_initiator)
return item
def __del(collection, item, _sa_initiator=None):
"""Run del events, may eventually be inlined into decorators."""
if _sa_initiator is not False:
- executor = getattr(collection, '_sa_adapter', None)
+ executor = collection._sa_adapter
if executor:
- getattr(executor, 'fire_remove_event')(item, _sa_initiator)
+ executor.fire_remove_event(item, _sa_initiator)
def __before_delete(collection, _sa_initiator=None):
"""Special method to run 'commit existing value' methods"""
- executor = getattr(collection, '_sa_adapter', None)
+ executor = collection._sa_adapter
if executor:
- getattr(executor, 'fire_pre_remove_event')(_sa_initiator)
+ executor.fire_pre_remove_event(_sa_initiator)
def _list_decorators():
"""Tailored instrumentation wrappers for any list-like class."""
def _tidy(fn):
- setattr(fn, '_sa_instrumented', True)
- fn.__doc__ = getattr(getattr(list, fn.__name__), '__doc__')
+ fn._sa_instrumented = True
+ fn.__doc__ = getattr(list, fn.__name__).__doc__
def append(fn):
def append(self, item, _sa_initiator=None):
@@ -1089,7 +1075,10 @@ def _list_decorators():
start = index.start or 0
if start < 0:
start += len(self)
- stop = index.stop or len(self)
+ if index.stop is not None:
+ stop = index.stop
+ else:
+ stop = len(self)
if stop < 0:
stop += len(self)
@@ -1172,6 +1161,15 @@ def _list_decorators():
_tidy(pop)
return pop
+ if not util.py2k:
+ def clear(fn):
+ def clear(self, index=-1):
+ for item in self:
+ __del(self, item)
+ fn(self)
+ _tidy(clear)
+ return clear
+
# __imul__ : not wrapping this. all members of the collection are already
# present, so no need to fire appends... wrapping it with an explicit
# decorator is still possible, so events on *= can be had if they're
@@ -1186,8 +1184,8 @@ def _dict_decorators():
"""Tailored instrumentation wrappers for any dict-like mapping class."""
def _tidy(fn):
- setattr(fn, '_sa_instrumented', True)
- fn.__doc__ = getattr(getattr(dict, fn.__name__), '__doc__')
+ fn._sa_instrumented = True
+ fn.__doc__ = getattr(dict, fn.__name__).__doc__
Unspecified = util.symbol('Unspecified')
@@ -1288,8 +1286,8 @@ def _set_decorators():
"""Tailored instrumentation wrappers for any set-like class."""
def _tidy(fn):
- setattr(fn, '_sa_instrumented', True)
- fn.__doc__ = getattr(getattr(set, fn.__name__), '__doc__')
+ fn._sa_instrumented = True
+ fn.__doc__ = getattr(set, fn.__name__).__doc__
Unspecified = util.symbol('Unspecified')