Adjusted inplace-binops on set-based collections and association proxies to

more closely follow builtin (2.4+) set semantics.  Formerly any set duck-type
was accepted, now only types or subtypes of set, frozenset or the collection
type itself are accepted.
This commit is contained in:
Jason Kirtland
2008-05-05 21:33:29 +00:00
parent a566964ac8
commit 586e5be1bb
5 changed files with 57 additions and 13 deletions
+11
View File
@@ -60,6 +60,11 @@ CHANGES
- Fixed duplicate append event emission on repeated
instrumented set.add() operations.
- set-based collections |=, -=, ^= and &= are stricter about
their operands and only operate on sets, frozensets or
subclasses of the collection type. Previously, they would
accept any duck-typed set.
- declarative extension
- Joined table inheritance mappers use a slightly relaxed
function to create the "inherit condition" to the parent
@@ -94,6 +99,12 @@ CHANGES
the databsae state cleanup (eg. issuing a rollback()) when
connections are returned to the pool.
-ext
- set-based association proxies |=, -=, ^= and &= are
stricter about their operands and only operate on sets,
frozensets or other association proxies. Previously, they
would accept any duck-typed set.
- mssql
- Added "odbc_autotranslate" parameter to engine / dburi
parameters. Any given string will be passed through to the
+10 -8
View File
@@ -6,10 +6,12 @@ transparent proxied access to the endpoint of an association object.
See the example ``examples/association/proxied_association.py``.
"""
import weakref, itertools
import sqlalchemy.exceptions as exceptions
import sqlalchemy.orm as orm
import sqlalchemy.util as util
import itertools
import weakref
from sqlalchemy import exceptions
from sqlalchemy import orm
from sqlalchemy import util
from sqlalchemy.orm import collections
def association_proxy(targetcollection, attr, **kw):
@@ -702,7 +704,7 @@ class _AssociationSet(object):
self.add(value)
def __ior__(self, other):
if util.duck_type_collection(other) is not util.Set:
if not collections._set_binops_check_strict(self, other):
return NotImplemented
for value in other:
self.add(value)
@@ -726,7 +728,7 @@ class _AssociationSet(object):
self.discard(value)
def __isub__(self, other):
if util.duck_type_collection(other) is not util.Set:
if not collections._set_binops_check_strict(self, other):
return NotImplemented
for value in other:
self.discard(value)
@@ -748,7 +750,7 @@ class _AssociationSet(object):
self.add(value)
def __iand__(self, other):
if util.duck_type_collection(other) is not util.Set:
if not collections._set_binops_check_strict(self, other):
return NotImplemented
want, have = self.intersection(other), util.Set(self)
@@ -776,7 +778,7 @@ class _AssociationSet(object):
self.add(value)
def __ixor__(self, other):
if util.duck_type_collection(other) is not util.Set:
if not collections._set_binops_check_strict(self, other):
return NotImplemented
want, have = self.symmetric_difference(other), util.Set(self)
+25 -5
View File
@@ -95,7 +95,11 @@ The owning object and InstrumentedCollectionAttribute are also reachable
through the adapter, allowing for some very sophisticated behavior.
"""
import copy, inspect, sys, weakref
import copy
import inspect
import sets
import sys
import weakref
from sqlalchemy import exceptions, schema, util as sautil
from sqlalchemy.util import attrgetter, Set
@@ -1136,6 +1140,22 @@ def _dict_decorators():
l.pop('Unspecified')
return l
try:
_set_binop_bases = (set, frozenset, sets.BaseSet)
except NameError:
_set_binop_bases = (sets.BaseSet,)
def _set_binops_check_strict(self, obj):
"""Allow only set, frozenset and self.__class__-derived objects in binops."""
return isinstance(obj, _set_binop_bases + (self.__class__,))
def _set_binops_check_loose(self, obj):
"""Allow anything set-like to participate in set binops."""
return (isinstance(obj, _set_binop_bases + (self.__class__,)) or
sautil.duck_type_collection(obj) == sautil.Set)
def _set_decorators():
"""Hand-turned instrumentation wrappers that can decorate any set-like
sequence class."""
@@ -1208,7 +1228,7 @@ def _set_decorators():
def __ior__(fn):
def __ior__(self, value):
if sautil.duck_type_collection(value) is not Set:
if not _set_binops_check_strict(self, value):
return NotImplemented
for item in value:
self.add(item)
@@ -1225,7 +1245,7 @@ def _set_decorators():
def __isub__(fn):
def __isub__(self, value):
if sautil.duck_type_collection(value) is not Set:
if not _set_binops_check_strict(self, value):
return NotImplemented
for item in value:
self.discard(item)
@@ -1247,7 +1267,7 @@ def _set_decorators():
def __iand__(fn):
def __iand__(self, other):
if sautil.duck_type_collection(other) is not Set:
if not _set_binops_check_strict(self, other):
return NotImplemented
want, have = self.intersection(other), Set(self)
remove, add = have - want, want - have
@@ -1274,7 +1294,7 @@ def _set_decorators():
def __ixor__(fn):
def __ixor__(self, other):
if sautil.duck_type_collection(other) is not Set:
if not _set_binops_check_strict(self, other):
return NotImplemented
want, have = self.symmetric_difference(other), Set(self)
remove, add = have - want, want - have
+1
View File
@@ -534,6 +534,7 @@ class SetTest(_CollectionOperations):
for other in (set(['a','b','c']), set(['a','b','c','d']),
set(['a']), set(['a','b']),
set(['c','d']), set(['e', 'f', 'g']),
frozenset(['e', 'f', 'g']),
set()):
p = Parent('p')
p.children = base[:]
+10
View File
@@ -484,6 +484,11 @@ class CollectionsTest(TestBase):
control |= values
assert_eq()
values = frozenset([e, creator()])
obj.attr |= values
control |= values
assert_eq()
try:
direct |= [e, creator()]
assert False
@@ -529,6 +534,11 @@ class CollectionsTest(TestBase):
control -= values
assert_eq()
values = frozenset([creator()])
obj.attr -= values
control -= values
assert_eq()
try:
direct -= [e, creator()]
assert False