From e8ad3988621a3caa69074fae8e9320bcabcf806d Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Mon, 5 Jun 2017 15:49:04 -0400 Subject: [PATCH] Implement in-place mutation operators for MutableSet, MutableList Implemented in-place mutation operators ``__ior__``, ``__iand__``, ``__ixor__`` and ``__isub__`` for :class:`.mutable.MutableSet` and ``__iadd__`` for :class:`.mutable.MutableList` so that change events are fired off when these mutator methods are used to alter the collection. Change-Id: Ib357a96d3b06c5deb6b53eb304a8b9f1dc9e9ede Fixes: #3853 --- doc/build/changelog/changelog_12.rst | 14 +++++++ doc/build/changelog/migration_12.rst | 19 +++++++++ lib/sqlalchemy/ext/mutable.py | 20 ++++++++++ test/ext/test_mutable.py | 60 ++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/doc/build/changelog/changelog_12.rst b/doc/build/changelog/changelog_12.rst index ee6eb7f65e..362e731a3f 100644 --- a/doc/build/changelog/changelog_12.rst +++ b/doc/build/changelog/changelog_12.rst @@ -13,6 +13,20 @@ .. changelog:: :version: 1.2.0b1 + .. change:: 3853 + :tags: bug, ext + :tickets: 3853 + + Implemented in-place mutation operators ``__ior__``, ``__iand__``, + ``__ixor__`` and ``__isub__`` for :class:`.mutable.MutableSet` + and ``__iadd__`` for :class:`.mutable.MutableList` so that change + events are fired off when these mutator methods are used to alter the + collection. + + .. seealso:: + + :ref:`change_3853` + .. change:: 4003 :tags: feature, oracle diff --git a/doc/build/changelog/migration_12.rst b/doc/build/changelog/migration_12.rst index 281bda936a..4d4c9e247d 100644 --- a/doc/build/changelog/migration_12.rst +++ b/doc/build/changelog/migration_12.rst @@ -411,6 +411,25 @@ parameter. :ticket:`3991` +.. _change_3853: + +In-place mutation operators work for MutableSet, MutableList +------------------------------------------------------------ + +Implemented the in-place mutation operators ``__ior__``, ``__iand__``, +``__ixor__`` and ``__isub__`` for :class:`.mutable.MutableSet` and ``__iadd__`` +for :class:`.mutable.MutableList`. While these +methods would successfully update the collection previously, they would +not correctly fire off change events. The operators mutate the collection +as before but additionally emit the correct change event so that the change +becomes part of the next flush process:: + + model = session.query(MyModel).first() + model.json_set &= {1, 3} + + +:ticket:`3853` + New Features and Improvements - Core ==================================== diff --git a/lib/sqlalchemy/ext/mutable.py b/lib/sqlalchemy/ext/mutable.py index ccaeb6aa32..b72303bc86 100644 --- a/lib/sqlalchemy/ext/mutable.py +++ b/lib/sqlalchemy/ext/mutable.py @@ -806,6 +806,10 @@ class MutableList(Mutable, list): list.extend(self, x) self.changed() + def __iadd__(self, x): + self.extend(x) + return self + def insert(self, i, x): list.insert(self, i, x) self.changed() @@ -885,6 +889,22 @@ class MutableSet(Mutable, set): set.symmetric_difference_update(self, *arg) self.changed() + def __ior__(self, other): + self.update(other) + return self + + def __iand__(self, other): + self.intersection_update(other) + return self + + def __ixor__(self, other): + self.symmetric_difference_update(other) + return self + + def __isub__(self, other): + self.difference_update(other) + return self + def add(self, elem): set.add(self, elem) self.changed() diff --git a/test/ext/test_mutable.py b/test/ext/test_mutable.py index 366b2006fb..e90ea7af27 100644 --- a/test/ext/test_mutable.py +++ b/test/ext/test_mutable.py @@ -401,6 +401,18 @@ class _MutableListTestBase(_MutableListTestFixture): eq_(f1.data, [1, 2, 5]) + def test_operator_extend(self): + sess = Session() + + f1 = Foo(data=[1, 2]) + sess.add(f1) + sess.commit() + + f1.data += [5] + sess.commit() + + eq_(f1.data, [1, 2, 5]) + def test_insert(self): sess = Session() @@ -561,6 +573,18 @@ class _MutableSetTestBase(_MutableSetTestFixture): eq_(f1.data, set([1, 2, 5])) + def test_binary_update(self): + sess = Session() + + f1 = Foo(data=set([1, 2])) + sess.add(f1) + sess.commit() + + f1.data |= set([2, 5]) + sess.commit() + + eq_(f1.data, set([1, 2, 5])) + def test_intersection_update(self): sess = Session() @@ -573,6 +597,18 @@ class _MutableSetTestBase(_MutableSetTestFixture): eq_(f1.data, set([2])) + def test_binary_intersection_update(self): + sess = Session() + + f1 = Foo(data=set([1, 2])) + sess.add(f1) + sess.commit() + + f1.data &= set([2, 5]) + sess.commit() + + eq_(f1.data, set([2])) + def test_difference_update(self): sess = Session() @@ -585,6 +621,18 @@ class _MutableSetTestBase(_MutableSetTestFixture): eq_(f1.data, set([1])) + def test_operator_difference_update(self): + sess = Session() + + f1 = Foo(data=set([1, 2])) + sess.add(f1) + sess.commit() + + f1.data -= set([2, 5]) + sess.commit() + + eq_(f1.data, set([1])) + def test_symmetric_difference_update(self): sess = Session() @@ -597,6 +645,18 @@ class _MutableSetTestBase(_MutableSetTestFixture): eq_(f1.data, set([1, 5])) + def test_binary_symmetric_difference_update(self): + sess = Session() + + f1 = Foo(data=set([1, 2])) + sess.add(f1) + sess.commit() + + f1.data ^= set([2, 5]) + sess.commit() + + eq_(f1.data, set([1, 5])) + def test_remove(self): sess = Session() -- 2.39.5