PASSIVE_NORESULT = object()
- def __init__(self, manager, key, uselist, callable_, typecallable, trackparent=False, extension=None, copy_function=None, compare_function=None, **kwargs):
+ def __init__(self, manager, key, uselist, callable_, typecallable, trackparent=False, extension=None, copy_function=None, compare_function=None, mutable_scalars=False, **kwargs):
self.manager = manager
self.key = key
self.uselist = uselist
self.callable_ = callable_
self.typecallable= typecallable
self.trackparent = trackparent
+ self.mutable_scalars = mutable_scalars
if copy_function is None:
- self._check_mutable_modified = False
if uselist:
self._copyfunc = lambda x: [y for y in x]
else:
# is passed
self._copyfunc = lambda x: x
else:
- self._check_mutable_modified = True
self._copyfunc = copy_function
if compare_function is None:
self._compare_function = lambda x,y: x == y
return self._copyfunc(value)
def check_mutable_modified(self, obj):
- if self._check_mutable_modified:
+ if self.mutable_scalars:
h = self.get_history(obj, passive=True)
if h is not None and h.is_modified():
obj._state['modified'] = True
# establish a SmartProperty property manager on the object for this key
if self.is_primary():
#print "regiser col on class %s key %s" % (parent.class_.__name__, key)
- sessionlib.attribute_manager.register_attribute(self.parent.class_, self.key, uselist=False, copy_function=lambda x: self.columns[0].type.copy_value(x), compare_function=lambda x,y:self.columns[0].type.compare_values(x,y))
+ sessionlib.attribute_manager.register_attribute(self.parent.class_, self.key, uselist=False, copy_function=lambda x: self.columns[0].type.copy_value(x), compare_function=lambda x,y:self.columns[0].type.compare_values(x,y), mutable_scalars=self.columns[0].type.is_mutable())
def execute(self, session, instance, row, identitykey, imap, isnew):
if isnew:
#print "POPULATING OBJ", instance.__class__.__name__, "COL", self.columns[0]._label, "WITH DATA", row[self.columns[0]], "ROW IS A", row.__class__.__name__, "COL ID", id(self.columns[0])
# establish a SmartProperty property manager on the object for this key,
# containing a callable to load in the attribute
if self.is_primary():
- sessionlib.attribute_manager.register_attribute(self.parent.class_, self.key, uselist=False, callable_=lambda i:self.setup_loader(i), copy_function=lambda x: self.columns[0].type.copy_value(x), compare_function=lambda x,y:self.columns[0].type.compare_values(x,y))
+ sessionlib.attribute_manager.register_attribute(self.parent.class_, self.key, uselist=False, callable_=lambda i:self.setup_loader(i), copy_function=lambda x: self.columns[0].type.copy_value(x), compare_function=lambda x,y:self.columns[0].type.compare_values(x,y), mutable_scalars=self.columns[0].type.is_mutable())
def setup_loader(self, instance):
if not self.localparent.is_assigned(instance):
return mapper.object_mapper(instance).props[self.key].setup_loader(instance)
class PickleType(MutableType, TypeDecorator):
impl = Binary
- def __init__(self, protocol=pickle.HIGHEST_PROTOCOL, pickler=None):
- self.protocol = protocol
- self.pickler = pickler or pickle
- super(PickleType, self).__init__()
+ def __init__(self, protocol=pickle.HIGHEST_PROTOCOL, pickler=None, mutable=True):
+ self.protocol = protocol
+ self.pickler = pickler or pickle
+ self.mutable = mutable
+ super(PickleType, self).__init__()
def convert_result_value(self, value, dialect):
- if value is None:
- return None
- buf = self.impl.convert_result_value(value, dialect)
- return self.pickler.loads(str(buf))
+ if value is None:
+ return None
+ buf = self.impl.convert_result_value(value, dialect)
+ return self.pickler.loads(str(buf))
def convert_bind_param(self, value, dialect):
- if value is None:
- return None
- return self.impl.convert_bind_param(self.pickler.dumps(value, self.protocol), dialect)
+ if value is None:
+ return None
+ return self.impl.convert_bind_param(self.pickler.dumps(value, self.protocol), dialect)
def copy_value(self, value):
- return self.pickler.loads(self.pickler.dumps(value, self.protocol))
+ if self.mutable:
+ return self.pickler.loads(self.pickler.dumps(value, self.protocol))
+ else:
+ return value
def compare_values(self, x, y):
- return self.pickler.dumps(x, self.protocol) == self.pickler.dumps(y, self.protocol)
+ if self.mutable:
+ return self.pickler.dumps(x, self.protocol) == self.pickler.dumps(y, self.protocol)
+ else:
+ return x is y
class Boolean(TypeEngine):
pass
b2.element = None
assert not manager.get_history(b2, 'element').hasparent(f2)
- def testaggressivediffs(self):
- """test the 'double check for changes' behavior of check_modified"""
+ def testmutablescalars(self):
+ """test detection of changes on mutable scalar items"""
class Foo(object):pass
manager = attributes.AttributeManager()
- manager.register_attribute(Foo, 'element', uselist=False, copy_function=lambda x:[y for y in x])
+ manager.register_attribute(Foo, 'element', uselist=False, copy_function=lambda x:[y for y in x], mutable_scalars=True)
x = Foo()
x.element = ['one', 'two', 'three']
manager.commit(x)