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.py75
1 files changed, 21 insertions, 54 deletions
diff --git a/lib/sqlalchemy/orm/collections.py b/lib/sqlalchemy/orm/collections.py
index 4f988a8d4..58a69227c 100644
--- a/lib/sqlalchemy/orm/collections.py
+++ b/lib/sqlalchemy/orm/collections.py
@@ -111,6 +111,7 @@ from ..sql import expression
from .. import util, exc as sa_exc
from . import base
+from sqlalchemy.util.compat import inspect_getargspec
__all__ = ['collection', 'collection_adapter',
'mapped_collection', 'column_mapped_collection',
@@ -573,13 +574,18 @@ class CollectionAdapter(object):
"""
- invalidated = False
+
+ __slots__ = (
+ 'attr', '_key', '_data', 'owner_state', '_converter', 'invalidated')
def __init__(self, attr, owner_state, data):
+ self.attr = attr
self._key = attr.key
self._data = weakref.ref(data)
self.owner_state = owner_state
data._sa_adapter = self
+ self._converter = data._sa_converter
+ self.invalidated = False
def _warn_invalidated(self):
util.warn("This collection has been invalidated.")
@@ -599,53 +605,8 @@ class CollectionAdapter(object):
"""
return self.owner_state.dict[self._key] is self._data()
- @util.memoized_property
- def attr(self):
- return self.owner_state.manager[self._key].impl
-
- def adapt_like_to_iterable(self, obj):
- """Converts collection-compatible objects to an iterable of values.
-
- Can be passed any type of object, and if the underlying collection
- determines that it can be adapted into a stream of values it can
- use, returns an iterable of values suitable for append()ing.
-
- This method may raise TypeError or any other suitable exception
- if adaptation fails.
-
- If a converter implementation is not supplied on the collection,
- a default duck-typing-based implementation is used.
-
- """
- converter = self._data()._sa_converter
- if converter is not None:
- return converter(obj)
-
- setting_type = util.duck_type_collection(obj)
- receiving_type = util.duck_type_collection(self._data())
-
- if obj is None or setting_type != receiving_type:
- given = obj is None and 'None' or obj.__class__.__name__
- if receiving_type is None:
- wanted = self._data().__class__.__name__
- else:
- wanted = receiving_type.__name__
-
- raise TypeError(
- "Incompatible collection type: %s is not %s-like" % (
- given, wanted))
-
- # If the object is an adapted collection, return the (iterable)
- # adapter.
- if getattr(obj, '_sa_adapter', None) is not None:
- return obj._sa_adapter
- elif setting_type == dict:
- if util.py3k:
- return obj.values()
- else:
- return getattr(obj, 'itervalues', obj.values)()
- else:
- return iter(obj)
+ def bulk_appender(self):
+ return self._data()._sa_appender
def append_with_event(self, item, initiator=None):
"""Add an entity to the collection, firing mutation events."""
@@ -662,6 +623,9 @@ class CollectionAdapter(object):
for item in items:
appender(item, _sa_initiator=False)
+ def bulk_remover(self):
+ return self._data()._sa_remover
+
def remove_with_event(self, item, initiator=None):
"""Remove an entity from the collection, firing mutation events."""
self._data()._sa_remover(item, _sa_initiator=initiator)
@@ -776,8 +740,8 @@ def bulk_replace(values, existing_adapter, new_adapter):
"""
- if not isinstance(values, list):
- values = list(values)
+
+ assert isinstance(values, list)
idset = util.IdentitySet
existing_idset = idset(existing_adapter or ())
@@ -785,15 +749,18 @@ def bulk_replace(values, existing_adapter, new_adapter):
additions = idset(values or ()).difference(constants)
removals = existing_idset.difference(constants)
+ appender = new_adapter.bulk_appender()
+
for member in values or ():
if member in additions:
- new_adapter.append_with_event(member)
+ appender(member)
elif member in constants:
- new_adapter.append_without_event(member)
+ appender(member, _sa_initiator=False)
if existing_adapter:
+ remover = existing_adapter.bulk_remover()
for member in removals:
- existing_adapter.remove_with_event(member)
+ remover(member)
def prepare_instrumentation(factory):
@@ -982,7 +949,7 @@ def _instrument_membership_mutator(method, before, argument, after):
adapter."""
# This isn't smart enough to handle @adds(1) for 'def fn(self, (a, b))'
if before:
- fn_args = list(util.flatten_iterator(inspect.getargspec(method)[0]))
+ fn_args = list(util.flatten_iterator(inspect_getargspec(method)[0]))
if isinstance(argument, int):
pos_arg = argument
named_arg = len(fn_args) > argument and fn_args[argument] or None