[web] stop looking at a request's Cache-Control header
As per RFC 7234 (HTTP 1.1 Caching):
The "Cache-Control" header field is used to specify directives for
caches along the request/response chain.
CubicWeb is not a cache.
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""additional cubicweb-ctl commands and command handlers for cubicweb andcubicweb's cubes development"""from__future__importprint_function__docformat__="restructuredtext en"# *ctl module should limit the number of import to be imported as quickly as# possible (for cubicweb-ctl reactivity, necessary for instance for usable bash# completion). So import locally in command helpers.importsysfromdatetimeimportdatetimefromosimportmkdir,chdir,pathasospfromwarningsimportwarnfromlogilab.commonimportSTD_BLACKLISTfromcubicweb.__pkginfo__importversionascubicwebversionfromcubicwebimportCW_SOFTWARE_ROOTasBASEDIR,BadCommandUsage,ExecutionErrorfromcubicweb.cwctlimportCWCTLfromcubicweb.cwconfigimportCubicWebNoAppConfigurationfromcubicweb.toolsutilsimport(SKEL_EXCLUDE,Command,copy_skeleton,underline_title)fromcubicweb.web.webconfigimportWebConfigurationfromcubicweb.server.serverconfigimportServerConfigurationclassDevConfiguration(ServerConfiguration,WebConfiguration):"""dummy config to get full library schema and appobjects for a cube or for cubicweb (without a home) """creating=Truecleanup_unused_appobjects=Falsecubicweb_appobject_path=(ServerConfiguration.cubicweb_appobject_path|WebConfiguration.cubicweb_appobject_path)cube_appobject_path=(ServerConfiguration.cube_appobject_path|WebConfiguration.cube_appobject_path)def__init__(self,*cubes):super(DevConfiguration,self).__init__(cubesandcubes[0]orNone)ifcubes:self._cubes=self.reorder_cubes(self.expand_cubes(cubes,with_recommends=True))self.load_site_cubicweb()else:self._cubes=()@propertydefapphome(self):returnNonedefavailable_languages(self):returnself.cw_languages()defmain_config_file(self):returnNonedefinit_log(self):passdefload_configuration(self,**kw):passdefdefault_log_file(self):returnNonedefdefault_stats_file(self):returnNonedefcleanup_sys_modules(config):# cleanup sys.modules, required when we're updating multiple cubesforname,modinlist(sys.modules.items()):ifmodisNone:# duh ? logilab.common.os for instancedelsys.modules[name]continueifnothasattr(mod,'__file__'):continueifmod.__file__isNone:# odd/rare but realcontinueforpathinconfig.appobjects_path():ifmod.__file__.startswith(path):delsys.modules[name]breakdefgenerate_schema_pot(w,cubedir=None):"""generate a pot file with schema specific i18n messages notice that relation definitions description and static vocabulary should be marked using '_' and extracted using xgettext """fromcubicweb.cwvregimportCWRegistryStoreifcubedir:cube=osp.split(cubedir)[-1]config=DevConfiguration(cube)depcubes=list(config._cubes)depcubes.remove(cube)libconfig=DevConfiguration(*depcubes)else:config=DevConfiguration()cube=libconfig=Nonecleanup_sys_modules(config)schema=config.load_schema(remove_unused_rtypes=False)vreg=CWRegistryStore(config)# set_schema triggers objects registrationsvreg.set_schema(schema)w(DEFAULT_POT_HEAD)_generate_schema_pot(w,vreg,schema,libconfig=libconfig)def_generate_schema_pot(w,vreg,schema,libconfig=None):fromcubicweb.i18nimportadd_msgfromcubicweb.schemaimportNO_I18NCONTEXT,CONSTRAINTSw('# schema pot file, generated on %s\n'%datetime.now().strftime('%Y-%m-%d %H:%M:%S'))w('# \n')w('# singular and plural forms for each entity type\n')w('\n')vregdone=set()afss=vreg['uicfg']['autoform_section']aiams=vreg['uicfg']['actionbox_appearsin_addmenu']iflibconfigisnotNone:# processing a cube, libconfig being a config with all its dependencies# (cubicweb incl.)fromcubicweb.cwvregimportCWRegistryStorelibschema=libconfig.load_schema(remove_unused_rtypes=False)cleanup_sys_modules(libconfig)libvreg=CWRegistryStore(libconfig)libvreg.set_schema(libschema)# trigger objects registrationlibafss=libvreg['uicfg']['autoform_section']libaiams=libvreg['uicfg']['actionbox_appearsin_addmenu']# prefill vregdone setlist(_iter_vreg_objids(libvreg,vregdone))defis_in_lib(rtags,eschema,rschema,role,tschema,predicate=bool):returnany(predicate(rtag.etype_get(eschema,rschema,role,tschema))forrtaginrtags)else:# processing cubicweb itselflibschema={}forcstrtypeinCONSTRAINTS:add_msg(w,cstrtype)libafss=libaiams=Noneis_in_lib=lambda*args:Falsedone=set()foreschemainsorted(schema.entities()):ifeschema.typeinlibschema:done.add(eschema.description)foreschemainsorted(schema.entities()):etype=eschema.typeifetypenotinlibschema:add_msg(w,etype)add_msg(w,'%s_plural'%etype)ifnoteschema.final:add_msg(w,'This %s:'%etype)add_msg(w,'New %s'%etype)add_msg(w,'add a %s'%etype)# AddNewActioniflibconfigisnotNone:# processing a cube# As of 3.20.3 we no longer use it, but keeping this string# allows developers to run i18ncube with new cubicweb and still# have the right translations at runtime for older versionsadd_msg(w,'This %s'%etype)ifeschema.descriptionandnoteschema.descriptionindone:done.add(eschema.description)add_msg(w,eschema.description)ifeschema.final:continueforrschema,targetschemas,roleineschema.relation_definitions(True):ifrschema.final:continuefortschemaintargetschemas:forafsinafss:fsections=afs.etype_get(eschema,rschema,role,tschema)if'main_inlined'infsectionsandnot \is_in_lib(libafss,eschema,rschema,role,tschema,lambdax:'main_inlined'inx):add_msg(w,'add a %s'%tschema,'inlined:%s.%s.%s'%(etype,rschema,role))add_msg(w,str(tschema),'inlined:%s.%s.%s'%(etype,rschema,role))breakforaiaminaiams:ifaiam.etype_get(eschema,rschema,role,tschema)andnot \is_in_lib(libaiams,eschema,rschema,role,tschema):ifrole=='subject':label='add %s%s%s%s'%(eschema,rschema,tschema,role)label2="creating %s (%s%%(linkto)s %s%s)"%(tschema,eschema,rschema,tschema)else:label='add %s%s%s%s'%(tschema,rschema,eschema,role)label2="creating %s (%s%s%s%%(linkto)s)"%(tschema,tschema,rschema,eschema)add_msg(w,label)add_msg(w,label2)break# XXX also generate "creating ...' messages for actions in the# addrelated submenuw('# subject and object forms for each relation type\n')w('# (no object form for final or symmetric relation types)\n')w('\n')forrschemainsorted(schema.relations()):ifrschema.typeinlibschema:done.add(rschema.type)done.add(rschema.description)forrschemainsorted(schema.relations()):rtype=rschema.typeifrtypenotinlibschema:# bw compat, necessary until all translation of relation are done# properly...add_msg(w,rtype)done.add(rtype)ifrschema.descriptionandrschema.descriptionnotindone:add_msg(w,rschema.description)done.add(rschema.description)librschema=Noneelse:librschema=libschema.rschema(rtype)# add context information only for non-metadata rtypesifrschemanotinNO_I18NCONTEXT:libsubjects=librschemaandlibrschema.subjects()or()forsubjschemainrschema.subjects():ifnotsubjschemainlibsubjects:add_msg(w,rtype,subjschema.type)ifnot(rschema.finalorrschema.symmetric):ifrschemanotinNO_I18NCONTEXT:libobjects=librschemaandlibrschema.objects()or()forobjschemainrschema.objects():ifnotobjschemainlibobjects:add_msg(w,'%s_object'%rtype,objschema.type)ifrtypenotinlibschema:# bw compat, necessary until all translation of relation are# done properly...add_msg(w,'%s_object'%rtype)forrdefinrschema.rdefs.values():ifnotrdef.descriptionorrdef.descriptionindone:continueif(librschemaisNoneor(rdef.subject,rdef.object)notinlibrschema.rdefsorlibrschema.rdefs[(rdef.subject,rdef.object)].description!=rdef.description):add_msg(w,rdef.description)done.add(rdef.description)forobjidin_iter_vreg_objids(vreg,vregdone):add_msg(w,'%s_description'%objid)add_msg(w,objid)def_iter_vreg_objids(vreg,done):forreg,objdictinvreg.items():ifregin('boxes','contentnavigation'):continueforobjectsinobjdict.values():forobjinobjects:objid='%s_%s'%(reg,obj.__regid__)ifobjidindone:breakpdefs=getattr(obj,'cw_property_defs',{})ifpdefs:yieldobjiddone.add(objid)breakDEFAULT_POT_HEAD=r'''msgid ""msgstr """Project-Id-Version: cubicweb %s\n""PO-Revision-Date: 2008-03-28 18:14+0100\n""Last-Translator: Logilab Team <contact@logilab.fr>\n""Language-Team: fr <contact@logilab.fr>\n""MIME-Version: 1.0\n""Content-Type: text/plain; charset=UTF-8\n""Content-Transfer-Encoding: 8bit\n""Generated-By: cubicweb-devtools\n""Plural-Forms: nplurals=2; plural=(n > 1);\n"'''%cubicwebversionclassUpdateCubicWebCatalogCommand(Command):"""Update i18n catalogs for cubicweb library. It will regenerate cubicweb/i18n/xx.po files. You'll have then to edit those files to add translations of newly added messages. """name='i18ncubicweb'min_args=max_args=0defrun(self,args):"""run the command with its specific arguments"""importshutilimporttempfileimportyamsfromlogilab.common.fileutilsimportensure_fs_modefromlogilab.common.shellutilsimportglobfind,find,rmfromlogilab.common.modutilsimportget_module_filesfromcubicweb.i18nimportextract_from_tal,execute2tempdir=tempfile.mkdtemp(prefix='cw-')cwi18ndir=WebConfiguration.i18n_lib_dir()print('-> extract messages:',end=' ')print('schema',end=' ')schemapot=osp.join(tempdir,'schema.pot')potfiles=[schemapot]potfiles.append(schemapot)# explicit close necessary else the file may not be yet flushed when# we'll using it belowschemapotstream=open(schemapot,'w')generate_schema_pot(schemapotstream.write,cubedir=None)schemapotstream.close()print('TAL',end=' ')tali18nfile=osp.join(tempdir,'tali18n.py')extract_from_tal(find(osp.join(BASEDIR,'web'),('.py','.pt')),tali18nfile)print('-> generate .pot files.')pyfiles=get_module_files(BASEDIR)pyfiles+=globfind(osp.join(BASEDIR,'misc','migration'),'*.py')schemafiles=globfind(osp.join(BASEDIR,'schemas'),'*.py')jsfiles=globfind(osp.join(BASEDIR,'web'),'cub*.js')forid,files,langin[('pycubicweb',pyfiles,None),('schemadescr',schemafiles,None),('yams',get_module_files(yams.__path__[0]),None),('tal',[tali18nfile],None),('js',jsfiles,'java'),]:potfile=osp.join(tempdir,'%s.pot'%id)cmd=['xgettext','--no-location','--omit-header','-k_']iflangisnotNone:cmd.extend(['-L',lang])cmd.extend(['-o',potfile])cmd.extend(files)execute2(cmd)ifosp.exists(potfile):potfiles.append(potfile)else:print('-> WARNING: %s file was not generated'%potfile)print('-> merging %i .pot files'%len(potfiles))cubicwebpot=osp.join(tempdir,'cubicweb.pot')cmd=['msgcat','-o',cubicwebpot]+potfilesexecute2(cmd)print('-> merging main pot file with existing translations.')chdir(cwi18ndir)toedit=[]forlanginCubicWebNoAppConfiguration.cw_languages():target='%s.po'%langcmd=['msgmerge','-N','--sort-output','-o',target+'new',target,cubicwebpot]execute2(cmd)ensure_fs_mode(target)shutil.move('%snew'%target,target)toedit.append(osp.abspath(target))# cleanuprm(tempdir)# instructions pour la suiteprint('-> regenerated CubicWeb\'s .po catalogs.')print('\nYou can now edit the following files:')print('* '+'\n* '.join(toedit))print('when you are done, run "cubicweb-ctl i18ncube yourcube".')classUpdateCubeCatalogCommand(Command):"""Update i18n catalogs for cubes. If no cube is specified, update catalogs of all registered cubes. """name='i18ncube'arguments='[<cube>...]'defrun(self,args):"""run the command with its specific arguments"""ifargs:cubes=[DevConfiguration.cube_dir(cube)forcubeinargs]else:cubes=[DevConfiguration.cube_dir(cube)forcubeinDevConfiguration.available_cubes()]cubes=[cubepathforcubepathincubesifosp.exists(osp.join(cubepath,'i18n'))]ifnotupdate_cubes_catalogs(cubes):raiseExecutionError("update cubes i18n catalog failed")defupdate_cubes_catalogs(cubes):fromsubprocessimportCalledProcessErrorforcubedirincubes:ifnotosp.isdir(cubedir):print('-> ignoring %s that is not a directory.'%cubedir)continuetry:toedit=update_cube_catalogs(cubedir)exceptCalledProcessErrorasexc:print('\n*** error while updating catalogs for cube',cubedir)print('cmd:\n%s'%exc.cmd)print('stdout:\n%s\nstderr:\n%s'%exc.data)exceptException:importtracebacktraceback.print_exc()print('*** error while updating catalogs for cube',cubedir)returnFalseelse:# instructions pour la suiteiftoedit:print('-> regenerated .po catalogs for cube %s.'%cubedir)print('\nYou can now edit the following files:')print('* '+'\n* '.join(toedit))print('When you are done, run "cubicweb-ctl i18ninstance ''<yourinstance>" to see changes in your instances.')returnTruedefupdate_cube_catalogs(cubedir):importshutilimporttempfilefromlogilab.common.fileutilsimportensure_fs_modefromlogilab.common.shellutilsimportfind,rmfromcubicweb.i18nimportextract_from_tal,execute2cube=osp.basename(osp.normpath(cubedir))tempdir=tempfile.mkdtemp()print(underline_title('Updating i18n catalogs for cube %s'%cube))chdir(cubedir)ifosp.exists(osp.join('i18n','entities.pot')):warn('entities.pot is deprecated, rename file to static-messages.pot (%s)'%osp.join('i18n','entities.pot'),DeprecationWarning)potfiles=[osp.join('i18n','entities.pot')]elifosp.exists(osp.join('i18n','static-messages.pot')):potfiles=[osp.join('i18n','static-messages.pot')]else:potfiles=[]print('-> extracting messages:',end=' ')print('schema',end=' ')schemapot=osp.join(tempdir,'schema.pot')potfiles.append(schemapot)# explicit close necessary else the file may not be yet flushed when# we'll using it belowschemapotstream=open(schemapot,'w')generate_schema_pot(schemapotstream.write,cubedir)schemapotstream.close()print('TAL',end=' ')tali18nfile=osp.join(tempdir,'tali18n.py')ptfiles=find('.',('.py','.pt'),blacklist=STD_BLACKLIST+('test',))extract_from_tal(ptfiles,tali18nfile)print('Javascript')jsfiles=[jsfileforjsfileinfind('.','.js')ifosp.basename(jsfile).startswith('cub')]ifjsfiles:tmppotfile=osp.join(tempdir,'js.pot')cmd=['xgettext','--no-location','--omit-header','-k_','-L','java','--from-code=utf-8','-o',tmppotfile]+jsfilesexecute2(cmd)# no pot file created if there are no string to translateifosp.exists(tmppotfile):potfiles.append(tmppotfile)print('-> creating cube-specific catalog')tmppotfile=osp.join(tempdir,'generated.pot')cubefiles=find('.','.py',blacklist=STD_BLACKLIST+('test',))cubefiles.append(tali18nfile)cmd=['xgettext','--no-location','--omit-header','-k_','-o',tmppotfile]cmd.extend(cubefiles)execute2(cmd)ifosp.exists(tmppotfile):# doesn't exists of no translation string foundpotfiles.append(tmppotfile)potfile=osp.join(tempdir,'cube.pot')print('-> merging %i .pot files'%len(potfiles))cmd=['msgcat','-o',potfile]cmd.extend(potfiles)execute2(cmd)ifnotosp.exists(potfile):print('no message catalog for cube',cube,'nothing to translate')# cleanuprm(tempdir)return()print('-> merging main pot file with existing translations:',end=' ')chdir('i18n')toedit=[]forlanginCubicWebNoAppConfiguration.cw_languages():print(lang,end=' ')cubepo='%s.po'%langifnotosp.exists(cubepo):shutil.copy(potfile,cubepo)else:cmd=['msgmerge','-N','-s','-o',cubepo+'new',cubepo,potfile]execute2(cmd)ensure_fs_mode(cubepo)shutil.move('%snew'%cubepo,cubepo)toedit.append(osp.abspath(cubepo))print()# cleanuprm(tempdir)returntoedit# XXX totally broken, fix it# class LiveServerCommand(Command):# """Run a server from within a cube directory.# """# name = 'live-server'# arguments = ''# options = ()# def run(self, args):# """run the command with its specific arguments"""# from cubicweb.devtools.livetest import runserver# runserver()classNewCubeCommand(Command):"""Create a new cube. <cubename> the name of the new cube. It should be a valid python module name. """name='newcube'arguments='<cubename>'min_args=max_args=1options=(("layout",{'short':'L','type':'choice','metavar':'<cube layout>','default':'simple','choices':('simple','full'),'help':'cube layout. You\'ll get a minimal cube with the "simple" \layout, and a full featured cube with "full" layout.',}),("directory",{'short':'d','type':'string','metavar':'<cubes directory>','help':'directory where the new cube should be created',}),("verbose",{'short':'v','type':'yn','metavar':'<verbose>','default':'n','help':'verbose mode: will ask all possible configuration questions',}),("author",{'short':'a','type':'string','metavar':'<author>','default':'LOGILAB S.A. (Paris, FRANCE)','help':'cube author',}),("author-email",{'short':'e','type':'string','metavar':'<email>','default':'contact@logilab.fr','help':'cube author\'s email',}),("author-web-site",{'short':'w','type':'string','metavar':'<web site>','default':'http://www.logilab.fr','help':'cube author\'s web site',}),("license",{'short':'l','type':'choice','metavar':'<license>','default':'LGPL','choices':('GPL','LGPL',''),'help':'cube license',}),)LICENSES={'LGPL':'''\# This program is free software: you can redistribute it and/or modify it under# the terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## This program is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License# along with this program. If not, see <http://www.gnu.org/licenses/>.''','GPL':'''\# This program is free software: you can redistribute it and/or modify it under# the terms of the GNU General Public License as published by the Free Software# Foundation, either version 2.1 of the License, or (at your option) any later# version.## This program is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more# details.## You should have received a copy of the GNU General Public License along with# this program. If not, see <http://www.gnu.org/licenses/>.''','':'# INSERT LICENSE HERE'}defrun(self,args):importrefromlogilab.common.shellutilsimportASKcubename=args[0]ifnotre.match('[_A-Za-z][_A-Za-z0-9]*$',cubename):raiseBadCommandUsage('cube name must be a valid python module name')verbose=self.get('verbose')cubesdir=self.get('directory')ifnotcubesdir:cubespath=ServerConfiguration.cubes_search_path()iflen(cubespath)>1:raiseBadCommandUsage("can't guess directory where to put the new cube."" Please specify it using the --directory option")cubesdir=cubespath[0]ifnotosp.isdir(cubesdir):print("-> creating cubes directory",cubesdir)try:mkdir(cubesdir)exceptOSErroraserr:self.fail("failed to create directory %r\n(%s)"%(cubesdir,err))cubedir=osp.join(cubesdir,cubename)ifosp.exists(cubedir):self.fail("%s already exists!"%cubedir)skeldir=osp.join(BASEDIR,'skeleton')default_name='cubicweb-%s'%cubename.lower().replace('_','-')ifverbose:distname=raw_input('Debian name for your cube ? [%s]): '%default_name).strip()ifnotdistname:distname=default_nameelifnotdistname.startswith('cubicweb-'):ifASK.confirm('Do you mean cubicweb-%s ?'%distname):distname='cubicweb-'+distnameelse:distname=default_nameifnotre.match('[a-z][-a-z0-9]*$',distname):raiseBadCommandUsage('cube distname should be a valid debian package name')longdesc=shortdesc=raw_input('Enter a short description for your cube: ')ifverbose:longdesc=raw_input('Enter a long description (leave empty to reuse the short one): ')dependencies={'cubicweb':'>= %s'%cubicwebversion,'six':'>= 1.4.0',}ifverbose:dependencies.update(self._ask_for_dependencies())context={'cubename':cubename,'distname':distname,'shortdesc':shortdesc,'longdesc':longdescorshortdesc,'dependencies':dependencies,'version':cubicwebversion,'year':str(datetime.now().year),'author':self['author'],'author-email':self['author-email'],'author-web-site':self['author-web-site'],'license':self['license'],'long-license':self.LICENSES[self['license']],}exclude=SKEL_EXCLUDEifself['layout']=='simple':exclude+=('sobjects.py*','precreate.py*','realdb_test*','cubes.*','uiprops.py*')copy_skeleton(skeldir,cubedir,context,exclude=exclude)def_ask_for_dependencies(self):fromlogilab.common.shellutilsimportASKfromlogilab.common.textutilsimportsplitstripdepcubes=[]forcubeinServerConfiguration.available_cubes():answer=ASK.ask("Depends on cube %s? "%cube,('N','y','skip','type'),'N')ifanswer=='y':depcubes.append(cube)ifanswer=='type':depcubes=splitstrip(raw_input('type dependencies: '))breakelifanswer=='skip':breakreturndict(('cubicweb-'+cube,ServerConfiguration.cube_version(cube))forcubeindepcubes)classExamineLogCommand(Command):"""Examine a rql log file. Will print out the following table Percentage; Cumulative Time (clock); Cumulative Time (CPU); Occurences; Query sorted by descending cumulative time (clock). Time are expressed in seconds. Chances are the lines at the top are the ones that will bring the higher benefit after optimisation. Start there. """arguments='rql.log'name='exlog'options=()defrun(self,args):importrerequests={}forfilepathinargs:try:stream=open(filepath)exceptOSErrorasex:raiseBadCommandUsage("can't open rql log file %s: %s"%(filepath,ex))forlineno,lineinenumerate(stream):ifnot' WHERE 'inline:continuetry:rql,time=line.split('--')rql=re.sub("(\'\w+': \d*)",'',rql)if'{'inrql:rql=rql[:rql.index('{')]req=requests.setdefault(rql,[])time.strip()chunks=time.split()clocktime=float(chunks[0][1:])cputime=float(chunks[-3])req.append((clocktime,cputime))exceptExceptionasexc:sys.stderr.write('Line %s: %s (%s)\n'%(lineno,exc,line))stat=[]forrql,timesinrequests.items():stat.append((sum(time[0]fortimeintimes),sum(time[1]fortimeintimes),len(times),rql))stat.sort()stat.reverse()total_time=sum(clocktimeforclocktime,cputime,occ,rqlinstat)*0.01print('Percentage;Cumulative Time (clock);Cumulative Time (CPU);Occurences;Query')forclocktime,cputime,occ,rqlinstat:print('%.2f;%.2f;%.2f;%s;%s'%(clocktime/total_time,clocktime,cputime,occ,rql))classGenerateSchema(Command):"""Generate schema image for the given cube"""name="schema"arguments='<cube>'min_args=max_args=1options=[('output-file',{'type':'string','default':None,'metavar':'<file>','short':'o','help':'output image file','input':False,}),('viewer',{'type':'string','default':None,'short':"d",'metavar':'<cmd>','help':'command use to view the generated file (empty for none)',}),('show-meta',{'action':'store_true','default':False,'short':"m",'metavar':"<yN>",'help':'include meta and internal entities in schema',}),('show-workflow',{'action':'store_true','default':False,'short':"w",'metavar':"<yN>",'help':'include workflow entities in schema',}),('show-cw-user',{'action':'store_true','default':False,'metavar':"<yN>",'help':'include cubicweb user entities in schema',}),('exclude-type',{'type':'string','default':'','short':"x",'metavar':"<types>",'help':'coma separated list of entity types to remove from view',}),('include-type',{'type':'string','default':'','short':"i",'metavar':"<types>",'help':'coma separated list of entity types to include in view',}),('show-etype',{'type':'string','default':'','metavar':'<etype>','help':'show graph of this etype and its neighbours'}),]defrun(self,args):fromsubprocessimportPopenfromtempfileimportNamedTemporaryFilefromlogilab.common.textutilsimportsplitstripfromlogilab.common.graphimportGraphGenerator,DotBackendfromyamsimportschema2dotass2d,BASE_TYPESfromcubicweb.schemaimport(META_RTYPES,SCHEMA_TYPES,SYSTEM_RTYPES,WORKFLOW_TYPES,INTERNAL_TYPES)cubes=splitstrip(args[0])dev_conf=DevConfiguration(*cubes)schema=dev_conf.load_schema()out,viewer=self['output-file'],self['viewer']ifoutisNone:tmp_file=NamedTemporaryFile(suffix=".svg")out=tmp_file.nameskiptypes=BASE_TYPES|SCHEMA_TYPESifnotself['show-meta']:skiptypes|=META_RTYPES|SYSTEM_RTYPES|INTERNAL_TYPESifnotself['show-workflow']:skiptypes|=WORKFLOW_TYPESifnotself['show-cw-user']:skiptypes|=set(('CWUser','CWGroup','EmailAddress'))skiptypes|=set(self['exclude-type'].split(','))skiptypes-=set(self['include-type'].split(','))ifnotself['show-etype']:s2d.schema2dot(schema,out,skiptypes=skiptypes)else:etype=self['show-etype']visitor=s2d.OneHopESchemaVisitor(schema[etype],skiptypes=skiptypes)propshdlr=s2d.SchemaDotPropsHandler(visitor)backend=DotBackend('schema','BT',ratio='compress',size=None,renderer='dot',additionnal_param={'overlap':'false','splines':'true','sep':'0.2'})generator=s2d.GraphGenerator(backend)generator.generate(visitor,propshdlr,out)ifviewer:p=Popen((viewer,out))p.wait()forcmdclsin(UpdateCubicWebCatalogCommand,UpdateCubeCatalogCommand,#LiveServerCommand,NewCubeCommand,ExamineLogCommand,GenerateSchema,):CWCTL.register(cmdcls)