Screenshot instructions:
Windows
Mac
Red Hat Linux
Ubuntu
Click URL instructions:
Right-click on ad, choose "Copy Link", then paste here →
(This may not be possible with some types of ads)
From: <subversion@co...> - 2007-04-18 17:49:05
|
Author: luke Date: 2007-04-18 11:49:00 -0600 (Wed, 18 Apr 2007) New Revision: 2565 Modified: SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py Log: cacheObject updates on create/set/delete Modified: SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py =================================================================== --- SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py 2007-04-18 17:47:46 UTC (rev 2564) +++ SQLObject/branches/cache-deps-on-views/sqlobject/materialized/dependency.py 2007-04-18 17:49:00 UTC (rev 2565) @@ -3,6 +3,7 @@ from sqlobject.events import * from sqlobject.include.pydispatch import dispatcher from sqlobject import classregistry +from sqlobject import SQLObject from operator import isCallable import inspect @@ -104,7 +105,6 @@ def __init__(self, target, source, *extras): if isinstance(source[0], sqlbuilder.SQLExpression): source = (str(source[0].tableName), str(source[0].fieldName)) + source[1:] - print source if len(source) == 3: other = source[2] try: @@ -115,6 +115,8 @@ source = source[:2] else: extras = (None,) + extras + if target[1][:5] == '_get_': + target = (target[0], target[1][5:]) self.target = self.DependencyItemClass(target, *extras) self.source = self.DependencyItemClass(source, *extras) @@ -130,44 +132,41 @@ targetClass = classregistry.findClass(targetClassName, class_registry=inst.sqlmeta.registry) - print "Route", route if inspect.isfunction(route): argspec = inspect.getargspec(route) - print "argspec", argspec if len(argspec[0]) == len(argspec[3]) and not argspec[1] and not argspec[2]: - print "exec", route() route = route() - print "RouteX", route if route is None: route = lambda i,c: [i] elif isinstance(route, sqlbuilder.SQLExpression): - route = lambda i,c,r=route: c.select(sqlbuilder.AND(r, i)) + route = lambda i,c,r=route: c.select(sqlbuilder.AND(r, i), connection=i._connection) ret.setdefault((targetClassName, targetAttr),[]).extend(route(inst, targetClass)) return ret - def process(self, inst, attrs): - for (cls, attr), insts in self.instancesToProcess(inst, attrs).iteritems(): + def process(self, inst, attrs, _toProcess=None): + if _toProcess is None: + _toProcess = self.instancesToProcess(inst, attrs) + for (cls, attr), insts in _toProcess.iteritems(): + print insts for inst in insts: - inst._SO_resetCache(attr) + inst._SO_updateCacheObject([attr]) + self.process(inst, [attr]) - - def FK(self, name): - '''route for foreign key''' - def f(inst, cls): - return cls.select(cls.q.id==getattr(inst, name+'ID')) - return f - - def J(self, name): - '''route for join''' - def f(inst, cls): - return cls.select(getattr(cls.q,name)==inst.id) - return f - - def S(self, *args): - '''route for self''' - if not len(args): - return self.S - return args[0] dep = SQLDependencyManager() + + +def _processDepsOnChange(inst, kwargs): + dep.process(inst, kwargs.keys()) + +listen(_processDepsOnChange, SQLObject, RowUpdatedSignal) +listen(_processDepsOnChange, SQLObject, RowCreatedSignal) + +def _processDepsOnDelete(inst, post_funcs): + attrs = ['id'] + inst.sqlmeta.columns.keys() + toProcess = dep.instancesToProcess(inst, attrs) + def f(inst, toProcess=toProcess): + dep.process(inst, attrs, _toProcess=toProcess) + post_funcs.append(f) +listen(_processDepsOnDelete, SQLObject, RowDestroySignal) \ No newline at end of file Modified: SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py =================================================================== --- SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py 2007-04-18 17:47:46 UTC (rev 2564) +++ SQLObject/branches/cache-deps-on-views/sqlobject/materialized/materialized.py 2007-04-18 17:49:00 UTC (rev 2565) @@ -1,22 +1,51 @@ -from sqlobject import SQLObject, sqlmeta as _sqlmeta -from sqlobject import events +from sqlobject import SQLObject, SQLObjectNotFound, sqlmeta as _sqlmeta +from sqlobject import events, col +from sqlobject.main import makeProperties from dependency import * +class materialized_sqlmeta(_sqlmeta): + cachedIn = None + + def setClass(cls, soClass): + super(materialized_sqlmeta, cls).setClass(soClass) + + cls.cachedAttributes = {} + + cachedIn = cls.cachedIn + if cachedIn is None: + cachedIn = cls.table + '_cache' + class cache_sqlmeta: + table = cachedIn + + cls.cacheClass = type(cls.soClass.__name__+'Cache', (SQLObject,), {'sqlmeta': cache_sqlmeta}) + + setClass = classmethod(setClass) + class MaterializedSQLObject(SQLObject): - class sqlmeta(_sqlmeta): - cachedIn = None + sqlmeta = materialized_sqlmeta - def setClass(cls, soClass): - super(cls, sqlmeta).setClass(soClass) - - if cls.cachedIn is None: - cls.cachedIn = cls.table + '_cache' - cls.cacheClass = type(cls.soClass.__name__+'Cache', (SQLObject,)) - - setClass = classmethod(setClass) + def _init(self, id, connection=None, **kw): + super(MaterializedSQLObject, self)._init(id, connection=connection, **kw) + try: + self._SO_cacheObject = self.sqlmeta.cacheClass.get(self.id, connection=connection) + except SQLObjectNotFound: + self._SO_cacheObject = None + + def _create(self, id, **kw): + super(MaterializedSQLObject, self)._create(id, **kw) + self._SO_cacheObject = self.sqlmeta.cacheClass(id=self.id) + def _SO_updateCacheObject(self, attrs): + for attr in attrs: + cacheAttr = self.sqlmeta.cachedAttributes.get(attr, None) + if cacheAttr: + new_value = cacheAttr.func(self) + self._SO_cacheObject.set(**{attr+'_dirty': False, + attr: new_value}) + def _addColumn(cls, connection, column_name, column_definition, changeSchema, post_funcs): + ''' for automatically adding dependencies to foreign keys''' def f(cls, col): if col.foreignKey: dep.add((cls.__name__, col.name), (col.foreignKey, 'id', lambda cls=cls,col=col: getattr(cls.q, col.origName))) @@ -25,6 +54,7 @@ events.listen(_addColumn, MaterializedSQLObject, events.AddColumnSignal) def _addJoin(cls, join_name, join_definition, post_funcs): + ''' for automatically adding dependencies to joins''' def f(cls, join): dep.add((cls.__name__, join.joinMethodName), (join.otherClassName, 'id', lambda cls=cls,join=join: getattr(cls.q, join.joinMethodName))) post_funcs.append(f) @@ -36,18 +66,21 @@ def __init__(self, func, colType, name=None): self.func = func self.colType = colType + if name is None: - self.name = self.func.__name__ - else: - self.name = name + name = self.func.__name__ + if name[:5] == '_get_': + name = name[5:] + self.name = name self.__name__ = self.name self.__dict__.update(func.__dict__) def __call__(self, obj): val = None - if getattr(obj, '_SO_cacheInstance', None): - val = getattr(obj._SO_cacheInstance, self.name) + if getattr(obj, '_SO_cacheObject', None): + if not getattr(obj._SO_cacheObject, self.name+"_dirty"): + val = getattr(obj._SO_cacheObject, self.name) if val is None: val = self.func(obj) return val @@ -58,12 +91,13 @@ return decorate - -def _processCacheClass(new_class_name, bases, new_attrs, post_funcs, early_funcs): +def _addColumnsToCacheClass(new_class_name, bases, new_attrs, post_funcs, early_funcs): def f(cls): for name, value in new_attrs.items(): if isinstance(value, MaterializedAttr): - cls.sqlmeta. - post_funcs.append(f) + cls.sqlmeta.cachedAttributes[value.name] = value + cls.sqlmeta.cacheClass.sqlmeta.addColumn(value.colType(name=value.name, default=None)) + cls.sqlmeta.cacheClass.sqlmeta.addColumn(col.BoolCol(name=value.name+'_dirty', default=True)) + post_funcs.extend((f,makeProperties)) -listen(_processCacheClass, MaterializedSQLObject, ClassCreateSignal) \ No newline at end of file +listen(_addColumnsToCacheClass, MaterializedSQLObject, ClassCreateSignal) Modified: SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py =================================================================== --- SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py 2007-04-18 17:47:46 UTC (rev 2564) +++ SQLObject/branches/cache-deps-on-views/sqlobject/materialized/tests/test_materialized.py 2007-04-18 17:49:00 UTC (rev 2565) @@ -10,11 +10,12 @@ class MaterializedOne(MaterializedSQLObject): name = StringCol() - twos = SQLMultipleJoin('MaterializedTwo', joinColumn='one_id') + twos = SQLMultipleJoin('MaterializedTwo', joinColumn='oneID') @cachedAs(IntCol) - @dep.dependentOn('MaterializedOne', 'twos', MO.q.twos) + @dep.dependentOn('MaterializedOne', 'twos') def _get_twoCount(self): + print "getting twoCount", self.twos.count() return self.twos.count() @cachedAs(IntCol) @@ -41,8 +42,15 @@ def _get_name2(self): return self.one.name + self.detail + @cachedAs(StringCol) + @dep.dependentOn('MaterializedTwo', 'detail') + @dep.dependentOn('MaterializedTwo', 'name') + def _get_name3(self): + ''' dep on dep ''' + return self.name + self.detail + def setup_module(mod): - setupClass([MaterializedOne, MaterializedTwo]) + setupClass([MaterializedOne, MaterializedTwo, MaterializedOne.sqlmeta.cacheClass, MaterializedTwo.sqlmeta.cacheClass]) mod.ones = inserts(MaterializedOne, (('S',), ('T',), ), 'name') @@ -54,8 +62,8 @@ def testSetup(): deps = dep.get('MaterializedOne', 'name') - assert set([(x,y) for x,y,z in deps]) == set([('MaterializedTwo','_get_name'), - ('MaterializedTwo','_get_name2')]) + assert set([(x,y) for x,y,z in deps]) == set([('MaterializedTwo','name'), + ('MaterializedTwo','name2')]) assert [z for x,y,z in deps][0] != [None, None] def testSetupDepsForFK(): @@ -73,14 +81,62 @@ def testProcessInstances(): assert dep.instancesToProcess(ones[0], ['name']) == { - ('MaterializedTwo', '_get_name'): [twos[0], twos[1]], - ('MaterializedTwo', '_get_name2'): [twos[0], twos[1]], + ('MaterializedTwo', 'name'): [twos[0], twos[1]], + ('MaterializedTwo', 'name2'): [twos[0], twos[1]], } -def testCacheObject(): +def testCacheClass(): assert MaterializedOne.sqlmeta.cacheClass.sqlmeta.table == 'materialized_one_cache' # Overridden by MaterializedTow.sqlmeta.cachedIn assert MaterializedTwo.sqlmeta.cacheClass.sqlmeta.table == 'cache_materialized_two' + + assert 'name' in MaterializedTwo.sqlmeta.cacheClass.sqlmeta.columns -def testCacheObject(): - assert ones[0]._SO_cacheObject is None \ No newline at end of file +def testManualCacheObject(): + obj = twos[0] + assert hasattr(obj, '_SO_cacheObject') + assert obj._SO_cacheObject.name == None + obj._SO_cacheObject.name = 'Not Right' + obj._SO_cacheObject.name_dirty = False + assert obj._SO_cacheObject.name == 'Not Right' + +def testDepSettingCacheObject(): + obj = twos[1] + assert obj._SO_cacheObject.name == None + dep.process(obj.one, ['name']) + assert obj._SO_cacheObject.name == obj._get_name.func(obj) + +def testModificationSettingCacheObject(): + obj = twos[2] + assert obj._SO_cacheObject.name == None + obj.one.name = 'G' + assert obj._SO_cacheObject.name == 'G' + assert obj.name == 'G' + +def testJoinUpdateSettingCacheObject(): + obj = ones[0] + prev = obj.detailSize + obj.twos[0].length += 4 + assert obj._SO_cacheObject.detailSize == prev + 4 + assert obj.detailSize == prev + 4 + +def testJoinAdditionSettingCacheObject(): + obj = ones[1] + prevS = obj.detailSize + prevC = obj.twoCount + new_two = MaterializedTwo(detail='f', length=4, one=obj) + assert obj._SO_cacheObject.detailSize == prevS + new_two.length + assert obj._SO_cacheObject.twoCount == prevC + 1 + assert obj.detailSize == prevS + new_two.length + +def testJoinDeletionSettingCacheObject(): + obj = ones[1] + prevS = obj.detailSize + prevC = obj.twoCount + new_two = MaterializedTwo(detail='g', length=5, one=obj) + assert obj._SO_cacheObject.detailSize == prevS + new_two.length + assert obj._SO_cacheObject.twoCount == prevC + 1 + new_two.destroySelf() + assert obj.twos.count() == prevC + assert obj._SO_cacheObject.detailSize == prevS + assert obj._SO_cacheObject.twoCount == prevC \ No newline at end of file |