def getCacheFile( self, name, **option ): cacheFile = self.cacheFiles.get( name, None ) if cacheFile: return cacheFile if option.get( 'affirm', True ): cacheFile = CacheManager.get().getCacheFile( self.getPath(), name, **option ) self.cacheFiles[ name ] = cacheFile return cacheFile else: return None
def getCacheFile(self, name, **option): cacheFile = self.cacheFiles.get(name, None) if cacheFile: return cacheFile if option.get('affirm', True): cacheFile = CacheManager.get().getCacheFile( self.getPath(), name, **option) self.cacheFiles[name] = cacheFile return cacheFile else: return None
def saveAssetTable( self, **option ): outputPath = option.get( 'path', self.assetIndexPath ) deployContext = option.get( 'deploy_context', False ) mapping = deployContext and deployContext.fileMapping table={} for path, node in self.assetTable.items(): item={} table[ path ]=item #common item['type'] = node.getType() item['groupType'] = node.getGroupType() item['filePath'] = node.getFilePath() or False item['tags'] = node.getTags() #oebjectfiles if mapping: mapped = {} for key, path in node.objectFiles.items(): mapped[ key ] = mapping.get( path, path ) item['objectFiles'] = mapped else: item['objectFiles'] = node.objectFiles item['dependency'] = node.dependency item['fileTime'] = node.getFileTime() #non deploy information if not deployContext: item['manager'] = node.managerName item['deploy'] = node.deployState item['cacheFiles'] = node.cacheFiles item['properties'] = node.properties item['modifyState'] = node.modifyState #mark cache files for name, cacheFile in node.cacheFiles.items(): CacheManager.get().touchCacheFile( cacheFile ) node.saveMetaDataTable() if not jsonHelper.trySaveJSON( table, outputPath, 'asset index' ): return False logging.info( 'asset table saved' ) return True
def saveAssetTable(self, **option): outputPath = option.get('path', self.assetIndexPath) deployContext = option.get('deploy_context', False) mapping = deployContext and deployContext.fileMapping table = {} for path, node in self.assetTable.items(): item = {} table[path] = item #common item['type'] = node.getType() item['groupType'] = node.getGroupType() item['filePath'] = node.getFilePath() or False item['tags'] = node.getTags() #oebjectfiles if mapping: mapped = {} for key, path in node.objectFiles.items(): mapped[key] = mapping.get(path, path) item['objectFiles'] = mapped else: item['objectFiles'] = node.objectFiles item['dependency'] = node.dependency item['fileTime'] = node.getFileTime() #non deploy information if not deployContext: item['manager'] = node.managerName item['deploy'] = node.deployState item['cacheFiles'] = node.cacheFiles item['properties'] = node.properties item['modifyState'] = node.modifyState #mark cache files for name, cacheFile in node.cacheFiles.items(): CacheManager.get().touchCacheFile(cacheFile) node.saveMetaDataTable() if not jsonHelper.trySaveJSON(table, outputPath, 'asset index'): return False logging.info('asset table saved') return True
def scanProject(self): #scan self.projectScanScheduled = False logging.info('scan project in:' + self.rootAbsPath ) #scan meta files first ( will be used in asset importing ) #TODO #check missing asset for assetPath, node in self.assetTable.copy().items(): if not self.assetTable.has_key( assetPath ): #already removed(as child of removed node) continue #check parentnode if not node.getParent(): self.unregisterAssetNode( node ) continue if node.isVirtual(): #don't check virtual node's file continue filePath = node.getAbsFilePath() #file deleted if not os.path.exists( filePath ): node.modifyState = 'removed' self.unregisterAssetNode( node ) continue #file become ignored if self.checkFileIgnorable( filePath ): node.modifyState = 'ignored' self.unregisterAssetNode( node ) continue #check new asset for currentDir, dirs, files in os.walk( unicode(self.rootAbsPath) ): relDir = os.path.relpath( currentDir, self.rootAbsPath ) for filename in files: if self.checkFileIgnorable(filename): continue nodePath = self.fixPath( relDir + '/' + filename ) absPath = self.getAbsPath( nodePath ) mtime = os.path.getmtime( absPath ) bundle = self._getParentBundle( nodePath ) if bundle: if mtime > bundle.getFileTime(): bundle.markModified() if not bundle.checkObjectFiles(): bundle.markModified() else: if not self.getAssetNode( nodePath ): #new self.initAssetNode( nodePath ) else: node = self.getAssetNode( nodePath ) #modified if mtime > node.getFileTime(): node.markModified() if not node.checkObjectFiles(): node.markModified() dirs2 = dirs[:] for dirname in dirs2: if self.checkFileIgnorable(dirname): dirs.pop(dirs.index(dirname)) #skip walk this continue nodePath = self.fixPath( relDir + '/' + dirname ) if not self.getAssetNode( nodePath ): self.initAssetNode( nodePath ) self.importModifiedAssets() self.saveAssetTable() CacheManager.get().save()
class UnitOfWork(object): ''' 工作单元 @note: 由于数据库连接线程安全的限制,工作单元只提供thread local的访问实例,不提供进程级实例 ''' def __init__(self): from db import ConnectionManager self.connection_manager = ConnectionManager(XConfig.get('db')) self.cache_manager = CacheManager(XConfig.get('cache')) self.entity_list = {} self.use_cache = False self.use_preload = True self.use_validator = False self.bad_entitys = [] def idgenerator(self): if not hasattr(self, '_idgenerator') or not self._idgenerator: connection = self.connection_manager.get( XConfig.get('idgenerator.db')) self._idgenerator = IdGenerator( connection, XConfig.get('idgenerator.count') or 5) return self._idgenerator def register(self, entity): '''注册实体到工作单元 ''' cls_name = entity.__class__.__name__ if self.entity_list.get(cls_name) is None: self.entity_list[cls_name] = {} self.entity_list[cls_name][str(entity.getId())] = entity entity._unitofwork = self def commit(self): ''' ''' deletes = [] updates = [] news = [] db_names = set() for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): entity.onDelete() elif entity.isNew(): entity.onNew() elif entity.isDirty(): entity.onUpdate() else: continue self.bad_entitys = [] for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): deletes.append(entity) elif entity.isNew(): news.append(entity) elif entity.isDirty(): updates.append(entity) else: continue if entity.isLoadedFromCache(): raise ModifyBasedCacheError( "%s(%s) is loaded from cache, so can't be modified!!" % (entity.__class__.__name__, entity.id)) if self.use_validator and not entity.doValidate(): self.bad_entitys.append(entity) db_names.add(entity._db) if self.use_validator and self.bad_entitys: return False for name in db_names: connection = self.connection_manager.get(name) if connection and name == connection.name: connection.begin() try: for entitys in [deletes, updates, news]: for entity in entitys: self.sync(entity) for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.commit() for entity in deletes: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.delete(cache_key) except: logging.exception("delete cache fail") for entitys in [updates, news]: for entity in entitys: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.set(cache_key, entity.getCacheDict()) except: logging.exception("set cache fail") return True except: logging.exception("[XWEB] COMMIT FAILED, ROLLBACK") for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.rollback() return False finally: self.entity_list.clear() def getEntityInMemory(self, cls, entity_id): cls_name = cls.__name__ if self.entity_list.get(cls_name) is None: return None return self.entity_list.get(cls_name).get(str(entity_id)) def getList(self, cls, entity_ids, **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) query_db_ids = [] query_cache_ids = [] for entity_id in entity_ids: entity = self.getEntityInMemory(cls, entity_id) if not entity: query_cache_ids.append(entity_id) if query_cache_ids: if self.use_cache: keys = [ self.makeKey(cls, entity_id) for entity_id in query_cache_ids ] cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if not cache: raise ValueError( 'CACHE DOES NOT EXSITS WHEN USE_CACHE IS TRUE') entitys = cache.getList(keys) for entity_id, key in zip(query_cache_ids, keys): cache_dict = entitys.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s" % (entity, cache_name)) else: query_db_ids.append(entity_id) else: query_db_ids = query_cache_ids entitys = connection.getEntityList(cls, query_db_ids) if not entitys: return [] first_entity = entitys[0] first_entity.setProps('entity_ids_in_query', entity_ids) for entity in entitys: self.register(entity) entity.setProps('first_entity_in_query', first_entity.id) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s" % (entity, db_conn)) return [ self.getEntityInMemory(cls, entity_id) for entity_id in entity_ids if self.getEntityInMemory(cls, entity_id) ] def getListByCond(self, criteria, **kwargs): if not isinstance(criteria, QueryCriteria): return [] cls = criteria.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) entity_ids = connection.fetchEntityIds(criteria) return self.getList(cls, entity_ids, **kwargs) def fetchRowsByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowsByCond(cr) def fetchRowByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowByCond(cr) def getListByCond2(self, cls, condition=None, args=[], **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) rows = connection.queryRowsByCond(cls, condition, args) results = [] for row in rows: data = {} for k, v in zip(cls.allKeys(), row): data[k] = v entity_id = tuple([data.get(k) for k in cls.primaryKey()]) entity = self.getEntityInMemory(cls, entity_id) if not entity: entity = connection.createEntity(cls, row) self.register(entity) key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if cache: cache.set(key, entity.getCacheDict()) results.append(entity) return results def get(self, cls, entity_id, **kwargs): #@ReservedAssignment entity = self.getEntityInMemory(cls, entity_id) if entity: return entity key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if self.use_cache and cache: cache_dict = cache.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s" % (entity, cache_name)) return entity db_conn = cls.dbName(entity_id=entity_id, **kwargs) connection = self.connection_manager.get(db_conn) entity = connection.getEntity(cls, entity_id) if entity is None: return None self.register(entity) if cache: cache.set(key, entity.getCacheDict()) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s" % (entity, db_conn)) return entity def sync(self, entity): connection = self.connection_manager.get(entity._db) if entity.isNew(): if connection.insert(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True elif entity.isDelete(): if connection.delete(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = True return True elif entity.isDirty(): if connection.update(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True else: raise EntityStatusError() return False def makeKey(self, cls, entity_id): return "%s:%s:%s:%s" % (XConfig.get('app_name'), cls.__name__, entity_id, cls._version) def close(self): self.connection_manager.close() # static method @classmethod def inst(cls): thread = threading.currentThread() if not hasattr(thread, 'unitofwork') or not thread.unitofwork: thread.unitofwork = UnitOfWork() return thread.unitofwork @classmethod def Commit(cls): cls.inst().commit() @classmethod def Close(cls): cls.inst().close() @classmethod def Reset(cls, force=False): thread = threading.currentThread() if hasattr(thread, 'unitofwork') and thread.unitofwork: if not force: unitofwork = thread.unitofwork unitofwork.entity_list = {} unitofwork.bad_entitys = {} unitofwork.use_cache = True unitofwork.use_preload = False else: del thread.unitofwork @classmethod def reset(cls, force=False): cls.Reset(force)
class UnitOfWork(object): ''' 工作单元 @note: 由于数据库连接线程安全的限制,工作单元只提供thread local的访问实例,不提供进程级实例 ''' def __init__(self): from db import ConnectionManager self.connection_manager = ConnectionManager(XConfig.get('db')) self.cache_manager = CacheManager(XConfig.get('cache')) self.entity_list = {} self.use_cache = False self.use_preload = True self.use_validator = False self.bad_entitys = [] def idgenerator(self): if not hasattr(self, '_idgenerator') or not self._idgenerator: connection = self.connection_manager.get(XConfig.get('idgenerator.db')) self._idgenerator = IdGenerator(connection, XConfig.get('idgenerator.count') or 5) return self._idgenerator def register(self, entity): '''注册实体到工作单元 ''' cls_name = entity.__class__.__name__ if self.entity_list.get(cls_name) is None: self.entity_list[cls_name] = {} self.entity_list[cls_name][str(entity.getId())] = entity entity._unitofwork = self def commit(self): ''' ''' deletes = [] updates = [] news = [] db_names = set() for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): entity.onDelete() elif entity.isNew(): entity.onNew() elif entity.isDirty(): entity.onUpdate() else: continue self.bad_entitys = [] for entity_class_name in self.entity_list.keys(): entity_dict = self.entity_list.get(entity_class_name) for entity_id in entity_dict.keys(): entity = entity_dict.get(entity_id) if entity.isDelete(): deletes.append(entity) elif entity.isNew(): news.append(entity) elif entity.isDirty(): updates.append(entity) else: continue if entity.isLoadedFromCache(): raise ModifyBasedCacheError("%s(%s) is loaded from cache, so can't be modified!!"%( entity.__class__.__name__, entity.id)) if self.use_validator and not entity.doValidate(): self.bad_entitys.append(entity) db_names.add(entity._db) if self.use_validator and self.bad_entitys: return False for name in db_names: connection = self.connection_manager.get(name) if connection and name == connection.name: connection.begin() try: for entitys in [deletes, updates, news]: for entity in entitys: self.sync(entity) for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.commit() for entity in deletes: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.delete(cache_key) except: logging.exception("delete cache fail") for entitys in [updates, news]: for entity in entitys: try: cache = self.cache_manager.get(entity._cache) if not cache: continue cache_key = self.makeKey(entity.__class__, entity.id) cache.set(cache_key, entity.getCacheDict()) except: logging.exception("set cache fail") return True except: logging.exception("[XWEB] COMMIT FAILED, ROLLBACK") for name in db_names: connection = self.connection_manager.get(name) if name == connection.name: connection.rollback() return False finally: self.entity_list.clear() def getEntityInMemory(self, cls, entity_id): cls_name = cls.__name__ if self.entity_list.get(cls_name) is None: return None return self.entity_list.get(cls_name).get(str(entity_id)) def getList(self, cls, entity_ids, **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) query_db_ids = [] query_cache_ids = [] for entity_id in entity_ids: entity = self.getEntityInMemory(cls, entity_id) if not entity: query_cache_ids.append(entity_id) if query_cache_ids: if self.use_cache: keys = [self.makeKey(cls, entity_id) for entity_id in query_cache_ids] cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if not cache: raise ValueError('CACHE DOES NOT EXSITS WHEN USE_CACHE IS TRUE') entitys = cache.getList(keys) for entity_id, key in zip(query_cache_ids, keys): cache_dict = entitys.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s"%(entity, cache_name)) else: query_db_ids.append(entity_id) else: query_db_ids = query_cache_ids entitys = connection.getEntityList(cls, query_db_ids) if not entitys: return [] first_entity = entitys[0] first_entity.setProps('entity_ids_in_query', entity_ids) for entity in entitys: self.register(entity) entity.setProps('first_entity_in_query', first_entity.id) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s"%(entity, db_conn)) return [self.getEntityInMemory(cls, entity_id) for entity_id in entity_ids if self.getEntityInMemory(cls, entity_id)] def getListByCond(self, criteria, **kwargs): if not isinstance(criteria, QueryCriteria): return [] cls = criteria.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) entity_ids = connection.fetchEntityIds(criteria) return self.getList(cls, entity_ids, **kwargs) def fetchRowsByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowsByCond(cr) def fetchRowByCond(self, cr, **kwargs): cls = cr.entity_cls db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) return connection.fetchRowByCond(cr) def getListByCond2(self, cls, condition=None, args=[], **kwargs): db_conn = cls.dbName(**kwargs) connection = self.connection_manager.get(db_conn) rows = connection.queryRowsByCond(cls, condition, args) results = [] for row in rows: data = {} for k,v in zip(cls.allKeys(), row): data[k] = v entity_id = tuple([data.get(k) for k in cls.primaryKey()]) entity = self.getEntityInMemory(cls, entity_id) if not entity: entity = connection.createEntity(cls, row) self.register(entity) key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if cache: cache.set(key, entity.getCacheDict()) results.append(entity) return results def get(self, cls, entity_id, **kwargs): #@ReservedAssignment entity = self.getEntityInMemory(cls, entity_id) if entity: return entity key = self.makeKey(cls, entity_id) cache_name = cls._cache_name cache = self.cache_manager.get(cache_name) if self.use_cache and cache: cache_dict = cache.get(key) if cache_dict: entity = cls(**cache_dict) entity._is_new = False entity._is_delete = False entity._is_dirty = False entity._load_from_cache = True entity._db = 'default' entity._cache = cache_name self.register(entity) logging.debug("[XWEB] LOAD ENTITY %s FROM CACHE: %s"%(entity, cache_name)) return entity db_conn = cls.dbName(entity_id=entity_id, **kwargs) connection = self.connection_manager.get(db_conn) entity = connection.getEntity(cls, entity_id) if entity is None: return None self.register(entity) if cache: cache.set(key, entity.getCacheDict()) logging.debug("[XWEB] LOAD ENTITY %s FROM DB: %s"%(entity, db_conn)) return entity def sync(self, entity): connection = self.connection_manager.get(entity._db) if entity.isNew(): if connection.insert(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True elif entity.isDelete(): if connection.delete(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = True return True elif entity.isDirty(): if connection.update(entity): entity._is_dirty = False entity._is_new = False entity.is_delete = False return True else: raise EntityStatusError() return False def makeKey(self, cls, entity_id): return "%s:%s:%s:%s"%(XConfig.get('app_name'), cls.__name__, entity_id, cls._version) def close(self): self.connection_manager.close() # static method @classmethod def inst(cls): thread = threading.currentThread() if not hasattr(thread, 'unitofwork') or not thread.unitofwork: thread.unitofwork = UnitOfWork() return thread.unitofwork @classmethod def Commit(cls): cls.inst().commit() @classmethod def Close(cls): cls.inst().close() @classmethod def Reset(cls, force=False): thread = threading.currentThread() if hasattr(thread, 'unitofwork') and thread.unitofwork: if not force: unitofwork = thread.unitofwork unitofwork.entity_list = {} unitofwork.bad_entitys = {} unitofwork.use_cache = True unitofwork.use_preload = False else: del thread.unitofwork @classmethod def reset(cls, force=False): cls.Reset(force)
def scanProject(self): #scan self.projectScanScheduled = False logging.info('scan project in:' + self.rootAbsPath) #scan meta files first ( will be used in asset importing ) #TODO #check missing asset for assetPath, node in self.assetTable.copy().items(): if not self.assetTable.has_key( assetPath): #already removed(as child of removed node) continue #check parentnode if not node.getParent(): self.unregisterAssetNode(node) continue if node.isVirtual(): #don't check virtual node's file continue filePath = node.getAbsFilePath() #file deleted if not os.path.exists(filePath): node.modifyState = 'removed' self.unregisterAssetNode(node) continue #file become ignored if self.checkFileIgnorable(filePath): node.modifyState = 'ignored' self.unregisterAssetNode(node) continue #check new asset for currentDir, dirs, files in os.walk(unicode(self.rootAbsPath)): relDir = os.path.relpath(currentDir, self.rootAbsPath) for filename in files: if self.checkFileIgnorable(filename): continue nodePath = self.fixPath(relDir + '/' + filename) absPath = self.getAbsPath(nodePath) mtime = os.path.getmtime(absPath) bundle = self._getParentBundle(nodePath) if bundle: if mtime > bundle.getFileTime(): bundle.markModified() if not bundle.checkObjectFiles(): bundle.markModified() else: if not self.getAssetNode(nodePath): #new self.initAssetNode(nodePath) else: node = self.getAssetNode(nodePath) #modified if mtime > node.getFileTime(): node.markModified() if not node.checkObjectFiles(): node.markModified() dirs2 = dirs[:] for dirname in dirs2: if self.checkFileIgnorable(dirname): dirs.pop(dirs.index(dirname)) #skip walk this continue nodePath = self.fixPath(relDir + '/' + dirname) if not self.getAssetNode(nodePath): self.initAssetNode(nodePath) self.importModifiedAssets() self.saveAssetTable() CacheManager.get().save()