]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
More overloads: fix cascades for += on a list relation, added operator support to...
authorJason Kirtland <jek@discorporate.us>
Sat, 5 Jan 2008 19:11:58 +0000 (19:11 +0000)
committerJason Kirtland <jek@discorporate.us>
Sat, 5 Jan 2008 19:11:58 +0000 (19:11 +0000)
CHANGES
lib/sqlalchemy/ext/associationproxy.py
lib/sqlalchemy/orm/collections.py
test/ext/associationproxy.py
test/orm/collection.py

diff --git a/CHANGES b/CHANGES
index ea84b24c093c04974c089fd5ba315d455eb7b8e8..f63c50a424b00659a1142aeb22cbddeef12ca840 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -5,7 +5,11 @@ CHANGES
 0.4.2b
 ------
 
+- orm
+    - Fixed cascades on a += assignment to a list-based relation.
 
+- ext
+    - '+', '*', '+=' and '*=' support for association proxied lists.
 
 0.4.2a
 ------
index c5a2b4d073c010a5c9ae4d6d844e516c75a5eb0a..fefc289f8fa6bc5a1c34d9fefb3368c84c7e5a9c 100644 (file)
@@ -402,6 +402,37 @@ class _AssociationList(object):
     def __ge__(self, other): return list(self) >= other
     def __cmp__(self, other): return cmp(list(self), other)
 
+    def __add__(self, iterable):
+        try:
+            other = list(iterable)
+        except TypeError:
+            return NotImplemented
+        return list(self) + other
+    __radd__ = __add__
+
+    def __mul__(self, n):
+        if not isinstance(n, int):
+            return NotImplemented
+        return list(self) * n
+    __rmul__ = __mul__
+
+    def __iadd__(self, iterable):
+        self.extend(iterable)
+        return self
+
+    def __imul__(self, n):
+        # unlike a regular list *=, proxied __imul__ will generate unique
+        # backing objects for each copy.  *= on proxied lists is a bit of
+        # a stretch anyhow, and this interpretation of the __imul__ contract
+        # is more plausibly useful than copying the backing objects.
+        if not isinstance(n, int):
+            return NotImplemented
+        if n == 0:
+            self.clear()
+        elif n > 1:
+            self.extend(list(self) * (n - 1))
+        return self
+
     def copy(self):
         return list(self)
 
index 106601640029496459b7f9d2f85f11af5c81e1ef..c63c4dc8c7bc250f7d6c9d1fe8b04b14dbf4be08 100644 (file)
@@ -968,6 +968,16 @@ def _list_decorators():
         _tidy(extend)
         return extend
 
+    def __iadd__(fn):
+        def __iadd__(self, iterable):
+            # list.__iadd__ takes any iterable and seems to let TypeError raise
+            # as-is instead of returning NotImplemented
+            for value in iterable:
+                self.append(value)
+            return self
+        _tidy(__iadd__)
+        return __iadd__
+
     def pop(fn):
         def pop(self, index=-1):
             __before_delete(self)
@@ -977,6 +987,11 @@ def _list_decorators():
         _tidy(pop)
         return pop
 
+    # __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
+    # desired.  hard to imagine a use case for __imul__, though.
+
     l = locals().copy()
     l.pop('_tidy')
     return l
index b3ce69a97d33957db312dea0c7b80c04e3cd831a..cc402033971a7e30ba234d465275ae3e0624743a 100644 (file)
@@ -189,6 +189,44 @@ class _CollectionOperations(PersistTest):
 
         self.assertRaises(TypeError, set, [p1.children])
 
+        p1.children *= 0
+        after = []
+        self.assert_(p1.children == after)
+        self.assert_([c.name for c in p1._children] == after)
+
+        p1.children += ['a', 'b']
+        after = ['a', 'b']
+        self.assert_(p1.children == after)
+        self.assert_([c.name for c in p1._children] == after)
+
+        p1.children *= 1
+        after = ['a', 'b']
+        self.assert_(p1.children == after)
+        self.assert_([c.name for c in p1._children] == after)
+
+        p1.children *= 2
+        after = ['a', 'b', 'a', 'b']
+        self.assert_(p1.children == after)
+        self.assert_([c.name for c in p1._children] == after)
+
+        p1.children = ['a']
+        after = ['a']
+        self.assert_(p1.children == after)
+        self.assert_([c.name for c in p1._children] == after)
+
+        self.assert_((p1.children * 2) == ['a', 'a'])
+        self.assert_((2 * p1.children) == ['a', 'a'])
+        self.assert_((p1.children * 0) == [])
+        self.assert_((0 * p1.children) == [])
+
+        self.assert_((p1.children + ['a']) == ['a', 'a'])
+        self.assert_((['a'] + p1.children) == ['a', 'a'])
+
+        try:
+            p1.children + 123
+            assert False
+        except TypeError:
+            assert True
 
 class DefaultTest(_CollectionOperations):
     def __init__(self, *args, **kw):
index fb4dbf199c1251503a01d45d5b494dd809898d17..60a0a240feeeb2c7792feecd31c861797c759635 100644 (file)
@@ -230,6 +230,31 @@ class CollectionsTest(PersistTest):
             control.extend(values)
             assert_eq()
 
+        if hasattr(direct, '__iadd__'):
+            values = [creator(), creator(), creator()]
+
+            direct += values
+            control += values
+            assert_eq()
+
+            direct += []
+            control += []
+            assert_eq()
+
+            values = [creator(), creator()]
+            obj.attr += values
+            control += values
+            assert_eq()
+
+        if hasattr(direct, '__imul__'):
+            direct *= 2
+            control *= 2
+            assert_eq()
+
+            obj.attr *= 2
+            control *= 2
+            assert_eq()
+
     def _test_list_bulk(self, typecallable, creator=entity_maker):
         class Foo(object):
             pass