From: Mike Bayer Date: Sat, 23 Sep 2006 21:02:33 +0000 (+0000) Subject: - added "mutable" flag to PickleType, set to False to allow old (faster) behavior X-Git-Tag: rel_0_3_0~135 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3e871d2755506f83fd314172df46909f7c58b462;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - added "mutable" flag to PickleType, set to False to allow old (faster) behavior - fix attribute unit test - attributes have explicit flag for "mutable_scalars", propigated by ColumnProperty --- diff --git a/lib/sqlalchemy/attributes.py b/lib/sqlalchemy/attributes.py index 7fd9686e35..46c791bffa 100644 --- a/lib/sqlalchemy/attributes.py +++ b/lib/sqlalchemy/attributes.py @@ -13,15 +13,15 @@ class InstrumentedAttribute(object): 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: @@ -29,7 +29,6 @@ class InstrumentedAttribute(object): # 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 @@ -52,7 +51,7 @@ class InstrumentedAttribute(object): 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 diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index c78593fcba..d846b3396c 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -42,7 +42,7 @@ class ColumnProperty(mapper.MapperProperty): # 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]) @@ -69,7 +69,7 @@ class DeferredColumnProperty(ColumnProperty): # 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) diff --git a/lib/sqlalchemy/types.py b/lib/sqlalchemy/types.py index 5463c23968..47c9d73df8 100644 --- a/lib/sqlalchemy/types.py +++ b/lib/sqlalchemy/types.py @@ -227,23 +227,30 @@ class Binary(TypeEngine): 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 diff --git a/test/base/attributes.py b/test/base/attributes.py index ca3937a98c..c2facd52d3 100644 --- a/test/base/attributes.py +++ b/test/base/attributes.py @@ -294,11 +294,11 @@ class AttributesTest(PersistTest): 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)