class TestAdvancedBag(unittest.TestCase): def setUp(self): self.bag = Bag() self.bag.setBackRef() self.bag["a.b.c.d"] = 4 self.bag["a.b.e"] = 5 def testparent(self): self.assertEqual(self.bag["a.b.c"].parent, self.bag["a.b"]) c = self.bag["a.b.c"] self.assertEqual(c["../e"], 5) self.bag["a.b"].delParentRef() self.assertFalse(self.bag["a.b"].backref) def testformula(self): self.bag["product"] = self.bag.formula("$x*$y", x="a.b.c.d", y="a.b.e") self.assertEqual(self.bag["product"], self.bag["a.b.c.d"] * self.bag["a.b.e"]) self.bag.defineFormula(calculate_perimeter="2*($base + $height)") self.bag.defineSymbol(base="a.b.c.d", height="a.b.e") self.bag["perimeter"] = self.bag.formula("calculate_perimeter") self.assertEqual(self.bag["perimeter"], 18) def testcallbackitem(self): def hello(): return "Hello!" self.bag.setCallBackItem("say_hello", hello) self.assertEqual(self.bag["say_hello"], hello()) def testnodetrigger(self): self.lastupdates = [] def mycallback(node, info=None, evt=None): self.lastupdates.append((node.getLabel(), info, node.getValue())) self.bag.getNode("a.b.c.d").subscribe("lastupdates", mycallback) self.bag["a.b.c.d"] = 20 self.assertEqual(self.lastupdates[-1], ("d", 4, 20)) def testbagtriggers(self): self.log = [] def log_upd(node, pathlist, oldvalue, evt): self.log.append((".".join(pathlist), node.getValue(), oldvalue, evt)) def log_ins(node, pathlist, ind, evt): self.log.append((".".join(pathlist), node.getValue(), ind, evt)) def log_del(node, pathlist, ind, evt): self.log.append((".".join(pathlist), node.getValue(), ind, evt)) self.bag.subscribe("log", update=log_upd, insert=log_ins, delete=log_del) self.bag["a.b.t"] = 45 self.assertEqual(self.log[-1], ("a.b", 45, 2, "ins")) self.bag["a.b.t"] = 56 self.assertEqual(self.log[-1], ("a.b.t", 56, 45, "upd_value")) self.bag.delItem("a.b.t") self.assertEqual(self.log[-1], ("a.b", 56, 2, "del"))
def addRegisterItem(self,register_item,data=None): register_item_id = register_item['register_item_id'] self.registerItems[register_item_id] = register_item register_item['datachanges'] = list() register_item['datachanges_idx'] = 0 register_item['subscribed_paths'] = set() data = Bag(data) data.subscribe('datachanges', any=lambda **kwargs: self._on_data_trigger(register_item=register_item,**kwargs)) self.itemsData[register_item_id] = data
def register_item(self): if self._register_item != '*': return self._register_item self._register_item = register_item = self.parent.read(self.register_item_id) if not register_item: return data = register_item.get('data') if data is None: data = Bag() register_item['data'] = data register_item['datachanges'] = list() register_item['datachanges_idx'] = 0 register_item['subscribed_paths'] = set() if self.triggered and register_item['subscribed_paths']: data.subscribe('datachanges', any=self._on_data_trigger) return register_item
def register_item(self): if self._register_item != '*': return self._register_item self._register_item = register_item = self.parent.read( self.register_item_id) if not register_item: return data = register_item.get('data') if data is None: data = Bag() register_item['data'] = data register_item['datachanges'] = list() register_item['datachanges_idx'] = 0 register_item['subscribed_paths'] = set() if self.triggered and register_item['subscribed_paths']: data.subscribe('datachanges', any=self._on_data_trigger) return register_item
class TestAdvancedBag(unittest.TestCase): def setUp(self): self.bag = Bag() self.bag.setBackRef() self.bag['a.b.c.d'] = 4 self.bag['a.b.e'] = 5 def testparent(self): self.assertEqual(self.bag['a.b.c'].parent, self.bag['a.b']) c = self.bag['a.b.c'] self.assertEqual(c['../e'], 5) self.bag['a.b'].delParentRef() self.assertFalse(self.bag['a.b'].backref) def testformula(self): self.bag['product'] = self.bag.formula('$x*$y', x='a.b.c.d', y='a.b.e') self.assertEqual(self.bag['product'], self.bag['a.b.c.d'] * self.bag['a.b.e']) self.bag.defineFormula(calculate_perimeter='2*($base + $height)') self.bag.defineSymbol(base='a.b.c.d', height='a.b.e') self.bag['perimeter'] = self.bag.formula('calculate_perimeter') self.assertEqual(self.bag['perimeter'], 18) def testcallbackitem(self): def hello(): return 'Hello!' self.bag.setCallBackItem('say_hello', hello) self.assertEqual(self.bag['say_hello'], hello()) def testnodetrigger(self): self.lastupdates = [] def mycallback(node, info=None, evt=None): self.lastupdates.append((node.getLabel(), info, node.getValue())) self.bag.getNode('a.b.c.d').subscribe('lastupdates', mycallback) self.bag['a.b.c.d'] = 20 self.assertEqual(self.lastupdates[-1], ('d', 4, 20)) def testbagtriggers(self): self.log = [] def log_upd(node, pathlist, oldvalue, evt): self.log.append( ('.'.join(pathlist), node.getValue(), oldvalue, evt)) def log_ins(node, pathlist, ind, evt): self.log.append(('.'.join(pathlist), node.getValue(), ind, evt)) def log_del(node, pathlist, ind, evt): self.log.append(('.'.join(pathlist), node.getValue(), ind, evt)) self.bag.subscribe('log', update=log_upd, insert=log_ins, delete=log_del) self.bag['a.b.t'] = 45 self.assertEqual(self.log[-1], ('a.b', 45, 2, 'ins')) self.bag['a.b.t'] = 56 self.assertEqual(self.log[-1], ('a.b.t', 56, 45, 'upd_value')) self.bag.delItem('a.b.t') self.assertEqual(self.log[-1], ('a.b', 56, 2, 'del'))
class SharedObject(object): default_savedir = 'site:async/sharedobjects' def __init__(self,manager,shared_id,expire=None,startData=None,read_tags=None,write_tags=None, filepath=None, dbSaveKw=None, saveInterval=None, autoSave=None, autoLoad=None,**kwargs): self.manager = manager self.lock=locks.Lock() self.server = manager.server self.shared_id = shared_id self._data = Bag(dict(root=Bag(startData))) self.read_tags = read_tags self.write_tags = write_tags self._data.subscribe('datachanges', any=self._on_data_trigger) self.subscribed_pages = dict() self.expire = expire or 0 self.focusedPaths = {} if self.expire<0: self.expire = 365*24*60*60 self.timeout=None self.autoSave=autoSave self.saveInterval=saveInterval self.autoLoad=autoLoad self.changes=False self.dbSaveKw=dbSaveKw self.onInit(**kwargs) @property def savepath(self): return self.server.gnrsite.getStaticPath(self.default_savedir,'%s.xml' %self.shared_id) @property def data(self): return self._data['root'] @property def sql_data_column(self): return self.dbSaveKw.get('data_column') or 'shared_data' @property def sql_backup_column(self): return self.dbSaveKw.get('backup_column') or 'shared_backup' @lockedThreadpool def save(self): if self.changes : if self.dbSaveKw: kw = dict(self.dbSaveKw) tblobj = self.server.db.table(kw.pop('table')) handler = getattr(tblobj, 'saveSharedObject', None) if handler: handler(self.shared_id, self.data, **kw) else: self.sql_save(tblobj) self.server.db.commit() else: self.data.toXml(self.savepath,unresolved=True,autocreate=True) self.changes=False @lockedThreadpool def load(self): if self.dbSaveKw: tblobj = self.server.db.table(self.dbSaveKw['table']) handler = getattr(tblobj, 'loadSharedObject', None) if handler: data = handler(self.shared_id) else: data = self.sql_load(tblobj) elif os.path.exists(self.savepath): data = Bag(self.savepath) else: data = Bag() self._data['root'] = data self.changes=False def sql_save(self, tblobj): backup = self.dbSaveKw.get('backup') data_column = self.sql_data_column with tblobj.recordToUpdate(self.shared_id) as record: if not self.data: print 'NO DATA IN SAVING', self.shared_id record[data_column] = deepcopy(self.data) onSavingHandler=getattr(tblobj, 'shared_onSaving',None) if onSavingHandler: onSavingHandler(record) if backup: backup_column = self.sql_backup_column if not record[backup_column]: record[backup_column] = Bag() n = 0 else: n = int(record[backup_column].keys()[-1].split('_')[1])+1 record[backup_column].setItem('v_%s' % n, record[data_column], ts=datetime.now()) if len (record[backup_column]) > backup: record[backup_column].popNode('#0') def sql_load(self, tblobj, version=None): record = tblobj.record(self.shared_id).output('bag') onLoadingHandler=getattr(tblobj, 'shared_onLoading',None) if onLoadingHandler: onLoadingHandler(record) if not version: return record[self.sql_data_column] else: return record[self.sql_backup_column].getItem('v_%i'% version) def onInit(self,**kwargs): if self.autoLoad: self.load() def onSubscribePage(self,page_id): pass #print 'onSubscribePage',self.shared_id,page_id def onUnsubscribePage(self,page_id): pass #print 'onUnsubscribePage',self.shared_id,page_id def onDestroy(self): print 'onDestroy',self.shared_id if self.autoSave: self.save() def onShutdown(self): if self.autoSave: self.save() def subscribe(self,page_id=None,**kwargs): page = self.server.pages[page_id] privilege= self.checkPermission(page) if privilege: page.sharedObjects.add(self.shared_id) subkwargs=dict(kwargs) subkwargs['page_id']=page_id subkwargs['user']=page.user self.subscribed_pages[page_id] = subkwargs self.server.sharedStatus.sharedObjectSubscriptionAddPage(self.shared_id,page_id,subkwargs) self.onSubscribePage(page) result=dict(privilege=privilege,data=self.data) return result def unsubscribe(self,page_id=None): self.subscribed_pages.pop(page_id,None) self.server.sharedStatus.sharedObjectSubscriptionRemovePage(self.shared_id,page_id) self.onUnsubscribePage(page_id) if not self.subscribed_pages: self.timeout=self.server.delayedCall(self.expire,self.manager.removeSharedObject,self) def checkPermission(self,page): privilege = 'readwrite' if self.read_tags and not self.server.gnrapp.checkResourcePermission(self.read_tags,page.userTags): privilege = None elif self.write_tags and not self.server.gnrapp.checkResourcePermission(self.write_tags,page.userTags): privilege = 'readonly' return privilege @lockedCoroutine def datachange(self,page_id=None,path=None,value=None,attr=None,evt=None,fired=None,**kwargs): if fired: data = Bag(dict(value=value,attr=attr,path=path,shared_id=self.shared_id,evt=evt,fired=fired)) self.broadcast(command='som.sharedObjectChange',data=data,from_page_id=page_id) else: path = 'root' if not path else 'root.%s' %path if evt=='del': self._data.popNode(path,_reason=page_id) else: self._data.setItem(path,value,_attributes=attr,_reason=page_id) def _on_data_trigger(self, node=None, ind=None, evt=None, pathlist=None,reason=None, **kwargs): self.changes=True if reason=='autocreate': return plist = pathlist[1:] if evt=='ins' or evt=='del': plist = plist+[node.label] path = '.'.join(plist) data = Bag(dict(value=node.value,attr=node.attr,path=path,shared_id=self.shared_id,evt=evt)) from_page_id = reason self.broadcast(command='som.sharedObjectChange',data=data,from_page_id=from_page_id) def onPathFocus(self, page_id=None,curr_path=None,focused=None): if focused: self.focusedPaths[curr_path]=page_id else: self.focusedPaths.pop(curr_path,None) self.broadcast(command='som.onPathLock',from_page_id=page_id,data=Bag(dict(locked=focused,lock_path=curr_path))) def broadcast(self,command=None, data=None, from_page_id=None,): envelope = Bag(dict(command=command,data=data)).toXml() channels = self.server.channels for p in self.subscribed_pages.keys(): if p != from_page_id: channels.get(p).write_message(envelope)