[cw-shell] Write ignored scripts warning on stderr instead of stdout.
"""custom storages for the system source"""fromosimportunlink,pathasospfromyams.schemaimportrole_namefromcubicwebimportBinaryfromcubicweb.server.hookimportOperationdefset_attribute_storage(repo,etype,attr,storage):repo.system_source.set_storage(etype,attr,storage)defunset_attribute_storage(repo,etype,attr):repo.system_source.unset_storage(etype,attr)classStorage(object):"""abstract storage * If `source_callback` is true (by default), the callback will be run during query result process of fetched attribute's valu and should have the following prototype:: callback(self, source, value) where `value` is the value actually stored in the backend. None values will be skipped (eg callback won't be called). * if `source_callback` is false, the callback will be run during sql generation when some attribute with a custom storage is accessed and should have the following prototype:: callback(self, generator, relation, linkedvar) where `generator` is the sql generator, `relation` the current rql syntax tree relation and linkedvar the principal syntax tree variable holding the attribute. """is_source_callback=Truedefcallback(self,*args):"""see docstring for prototype, which vary according to is_source_callback """raiseNotImplementedError()defentity_added(self,entity,attr):"""an entity using this storage for attr has been added"""raiseNotImplementedError()defentity_updated(self,entity,attr):"""an entity using this storage for attr has been updatded"""raiseNotImplementedError()defentity_deleted(self,entity,attr):"""an entity using this storage for attr has been deleted"""raiseNotImplementedError()# TODO# * make it configurable without code# * better file path attribution# * handle backup/restoredefuniquify_path(dirpath,basename):"""return a unique file name for `basename` in `dirpath`, or None if all attemps failed. XXX subject to race condition. """path=osp.join(dirpath,basename)ifnotosp.isfile(path):returnpathbase,ext=osp.splitext(path)foriinxrange(1,256):path='%s%s%s'%(base,i,ext)ifnotosp.isfile(path):returnpathreturnNoneclassBytesFileSystemStorage(Storage):"""store Bytes attribute value on the file system"""def__init__(self,defaultdir,fsencoding='utf-8'):self.default_directory=defaultdirself.fsencoding=fsencodingdefcallback(self,source,value):"""sql generator callback when some attribute with a custom storage is accessed """fpath=source.binary_to_str(value)try:returnBinary(file(fpath).read())exceptOSError,ex:source.critical("can't open %s: %s",value,ex)returnNonedefentity_added(self,entity,attr):"""an entity using this storage for attr has been added"""ifentity._cw.transaction_data.get('fs_importing'):binary=Binary(file(entity[attr].getvalue()).read())else:binary=entity.pop(attr)fpath=self.new_fs_path(entity,attr)# bytes storage used to store file's pathentity[attr]=Binary(fpath)file(fpath,'w').write(binary.getvalue())AddFileOp(entity._cw,filepath=fpath)returnbinarydefentity_updated(self,entity,attr):"""an entity using this storage for attr has been updatded"""ifentity._cw.transaction_data.get('fs_importing'):oldpath=self.current_fs_path(entity,attr)fpath=entity[attr].getvalue()ifoldpath!=fpath:DeleteFileOp(entity._cw,filepath=oldpath)binary=Binary(file(fpath).read())else:binary=entity.pop(attr)fpath=self.current_fs_path(entity,attr)UpdateFileOp(entity._cw,filepath=fpath,filedata=binary.getvalue())returnbinarydefentity_deleted(self,entity,attr):"""an entity using this storage for attr has been deleted"""DeleteFileOp(entity._cw,filepath=self.current_fs_path(entity,attr))defnew_fs_path(self,entity,attr):# We try to get some hint about how to name the file using attribute's# name metadata, so we use the real file name and extension when# available. Keeping the extension is useful for example in the case of# PIL processing that use filename extension to detect content-type, as# well as providing more understandable file names on the fs.basename=[str(entity.eid),attr]name=entity.attr_metadata(attr,'name')ifnameisnotNone:basename.append(name.encode(self.fsencoding))fspath=uniquify_path(self.default_directory,'_'.join(basename))iffspathisNone:msg=entity._cw._('failed to uniquify path (%s, %s)')%(dirpath,'_'.join(basename))raiseValidationError(entity.eid,{role_name(attr,'subject'):msg})returnfspathdefcurrent_fs_path(self,entity,attr):sysource=entity._cw.pool.source('system')cu=sysource.doexec(entity._cw,'SELECT cw_%s FROM cw_%s WHERE cw_eid=%s'%(attr,entity.__regid__,entity.eid))rawvalue=cu.fetchone()[0]ifrawvalueisNone:# no previous valuereturnself.new_fs_path(entity,attr)returnsysource._process_value(rawvalue,cu.description[0],binarywrap=str)classAddFileOp(Operation):defrollback_event(self):try:unlink(self.filepath)except:passclassDeleteFileOp(Operation):defcommit_event(self):try:unlink(self.filepath)except:passclassUpdateFileOp(Operation):defprecommit_event(self):try:file(self.filepath,'w').write(self.filedata)exceptException,ex:self.exception(str(ex))