#!/usr/bin/env python ################################################################################################ # @(#) $Id: Subsystem.py,v 1.18 2011/10/28 14:39:18 hsommer Exp $ # # ALMA - Atacama Large Millimiter Array # (c) Associated Universities, Inc. Washington DC, USA, 2001 # (c) European Southern Observatory, 2002 # Copyright by ESO (in the framework of the ALMA collaboration) # and Cosylab 2002, All rights reserved # # This library 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 library 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 library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################### # -------- ---------- ---------------------------------------------- # acaproni 2005/02/08 Created. #------------------------------------------------------------------------------ import glob, os, string, re, xml.parsers.expat, sys, time, fpformat from ErrorDefinition import * class Subsystem(object): """The object to scan a subsystem. It assumes that the base directory contains all the sources of the subsystem name is the name of the subsystem basedir is the base directory for the subsystem min is the minimum allowed error number for this subsystem max is the maximum allowed error number for this subsystem ExaplesTestRange is the range for example and test errors exclude is a list of directory trees to discard (CVS is a valid choice here) include the directories to scan: if present only those folder are scanned for the files (idl is a valide choice here) """ def __init__(self, name, basedir,min,max,exaplesAndTestsRange,exclude=["CVS"],include=None): # The maximum and the minimum allowed number for the errors of # this subsystem self.min = min self.max = max #The name of this subsystem self.subsystemName=name #The base directory for the subsystem self.baseDir = basedir # The range for example and test errors self.testErrorRange=exaplesAndTestsRange # The times of start/end of this scan self.startTime=time.ctime() self.startFloatTime=time.time() self.endTime=self.startTime self.endFloatTime=self.startFloatTime print self.startTime+', scanning',self.subsystemName,'....' #The list of all the names of the files defining errors self.files=[] # The name of files and directory to exclude from the search path # (also the subdirectory are discarded) self.exclude=exclude # The directories to include in the search self.include=include # The list of number of duplicated errors self.duplicatedErrors=[] #The list of number out of the allowed range self.outOfRangeErrors=[] # The list of number for test and examples self.testAndExampleErrors = [] # Print some infos print"Subsystem ",self.subsystemName,"with base dir",self.baseDir # Scans the directory of the subsystem self.scans(self.baseDir) print "Found",len(self.files),"candidates" self.candidates=len(self.files) #Parse the candidates to build the list of errors of the subsystems self.errors=self.parseXMLFiles(self.files) print "Found",len(self.files),"error definition files" print "Found",len(self.errors), "errors" #Order the errors self.orderErrors() # Check for duplicated errors self.checkSubsystemErrors() # Print info about duplicated errors if len(self.duplicatedErrors)==0: print "The subsystem contains no duplicated errors" else: print "The subsystem contains the following duplicated error definitions:", print self.duplicatedErrors # Print info about errors out of range if len(self.outOfRangeErrors)==0: print "The subsystem contains no errors out of the allowed range" else: print "The subsystem contains the following errors with an invalid number:", print self.outOfRangeErrors # Print info about the errors for examples and tests if len(self.testAndExampleErrors)==0: print "The subsystem does not containes definitions for tests and examples" else: print "The following errors are used for test and examples:", print self.testAndExampleErrors # Store the time self.endTime=time.ctime() self.endFloatTime=time.time() # Print the time print self.endTime+ ", check terminated" print fpformat.fix(self.endFloatTime-self.startFloatTime,3),"seconds requested to check this system" def excluded(self, name): """Check if name is valid i.e. not part of the excluded""" if self.exclude==None or self.exclude==[]: return False for n in self.exclude: str = "/"+n sstr = name[len(name)-len(str):len(name)] if str == sstr: return True return False def included(self,name): """Check if name is a directory defined in include""" if self.include==None or self.include==[]: return True #Check if name is a directory if not os.path.isdir(name): return False names = string.split(name,'/') return self.include.count(names[len(names)-1])>0 def isErrorDefFile(self,fileName): """Check if fileName contains an error definition. It must be an xml file with the right format""" if os.path.isdir(fileName): return False if fileName[len(fileName)-4:len(fileName)]!=".xml": return False #The first line of an xml file contains something like # So it doesn't work with included xml files.... try: # Read the first line of the file inF=open(fileName) line=inF.readline().strip() inF.close() #Check if the line is of this kind: return re.compile("<*\?xml.*\?>").match(line, 1)!=None except IOError, e: print "Unable to open",f print "Exception",e return False def scans(self,dir): """Scans the base directory looking for the xml files defining erros""" for root, dirs, files in os.walk(dir): # Check if some of the files in the directory is an xml files to be added # to the list for f in files: if self.isErrorDefFile(os.path.join(root,f)) and self.included(root): self.files.append(os.path.join(root,f)) #Checks if some of the directory must be excluded from the scan for d in dirs: if (self.excluded(os.path.join(root,d))): dirs.remove(d) def parseXMLFiles(self,xmlFiles): """Parse all the xml files and bild a list of error definitions xmlFiles is the list of xml files to parse return a list of ErrorDefinition objects""" ret=[] # The errors errorFiles=[]# The new list of files for f in xmlFiles: err = ErrorDefinition(f) if err.isValid(): ret.append(err) errorFiles.append(f) self.files=errorFiles return ret def orderErrors(self): """Order the errors by their number""" newList=[] while len(self.errors)>0: # Extract the error or with the lower type # and insert it in the head of the new list pos=0 min=self.errors[0].getNumber() for s in range(0,len(self.errors)): if self.errors[s].getNumber()self.testErrorRange["Max"]: self.duplicatedErrors.append(actNum) # Looks for errors out of the range for the subsystem # It also fills the list of the example and test errors for t in self.errors: if t.getNumber()>=self.testErrorRange["Min"] and \ t.getNumber()<=self.testErrorRange["Max"]: self.testAndExampleErrors.append(t.getNumber()) else: if t.getNumber()self.max: if self.outOfRangeErrors.count(t.getNumber())==0: self.outOfRangeErrors.append(t.getNumber()) def printErrors(self): """Nicely print the info about this subsystem in the stdout (no html here!) Note: - this output is not defined as the html is. - the user shoud read the out by himself to found errors """ print "Subsystem:",self.subsystemName print "Base dir:",self.baseDir # Print a list of duplicated errors for err in self.errors: if self.duplicatedErrors.count(err.getNumber())>0: print "The following error is defined more then once!" print "\tType:",err.getNumber() print "\tName: ",err.getName() print "\tDefined in",err.getFile() # Print the errors for err in self.errors: err.printError() def getHTMLHeader(self,errorStr): """Return the header of an HTML page (either the main page and the error description page) error is a string with the name of a specific error; if it is None it means that the header is for the main page""" header='\n' header=header+'\n' header=header+'' if errorStr==None: header=header+'Error definitions for '+self.subsystemName else: header=header+'Details of '+self.subsystemName+'::'+errorStr header=header+'\n' header=header+'\n' return header def getHTMLFooter(self): """Return the footer of an HTML page""" return '\n' def insertFileContent(self,fileName): """Return an HTML string with the content of the file with name fileName The string represents the verbatim content of the file""" str='' if os.access(fileName,os.R_OK): theFile = file(fileName,"r") lines = theFile.readlines() str=str+'
\n'
			for line in lines:
				line = line.replace('&','&')
				line = line.replace('<','<')
				line = line.replace('>','>')
				str=str+line
			str=str+'\n
\n' theFile.close() else: str=str+'

Sorry, '+fileName+' is unreadable!' return str def generateHTMLPage(self,fileName): """Generate the main HTML page for the errors The page is written into a file of the given name (filename) If fileName is None the page is written in the stdout For ach error described in this page there is a link to another HTML page with the detailed info about that error""" # count is used to generate the name of the sub pages describing # the details of each error count = 0 # The entire document is written into text text=self.getHTMLHeader(None); #Write the title of the page text=text+'

Error definitions for '+self.subsystemName+'

\n' text=text+'

Generated scanning the basedir '+self.baseDir+'
\n' text=text+'Generated at '+self.startTime+'

' text=text+'
\n' # Print some info about the duplicated errors if len(self.duplicatedErrors)==0: text=text+"

The subsystem contains no duplicated errors

" else: text=text+'

The subsystem contains the following duplicated error definitions:' text=text+'

' # Print some info about the out of range type errors if len(self.outOfRangeErrors)==0: text=text+'

All the number of the errors are in the right range ' else: text=text+'

The subsystem contains the following definition with an invalid number:' text=text+'

' #Print subsystem statistics text=text+'

Statistics

' text=text+'' text=text+'' text=text+'' totErrors=0 totCodes=0 for err in self.errors: if err.getErrors()!=None: totErrors=totErrors+len(err.getErrors()) if err.getCodes()!=None: totCodes=totCodes+len(err.getCodes()) text=text+'' text=text+'' text=text+'
'+str(self.candidates)+'parsed candidate xml files
'+str(len(self.errors))+'error definition files found
'+str(totErrors)+'errors defined
'+str(totCodes)+'codes defined
' text=text+'

The range of the error numbers for this subsystem is ['+str(self.min)+','+str(self.max)+']

' text=text+'

The range for examples and tests is ['+str(self.testErrorRange["Min"])+','+str(self.testErrorRange["Max"])+']

' #Print all the errors (no examples and tests) into a table text=text+'

Errors and codes defined

\n' text=text+'

Type definitions appear in bold
\n' text=text+'Duplicated error types are printed in Red
\n' text=text+'Errors with a number out of the allowed range appears Red italic underline

\n' text=text+'\n' #Go throw all the errors for err in self.errors: # Ignore the error if it is used for tests or examples if self.testAndExampleErrors.count(err.getNumber())>0: continue if self.duplicatedErrors.count(err.getNumber())>0 or self.outOfRangeErrors.count(err.getNumber())>0: # This is a duplicated error number or with an invalid number style ='' endStyle = '' if self.outOfRangeErrors.count(err.getNumber())>0: # This error has a number out of range style=style+'' endStyle=''+endStyle else: style=endStyle='' text=text+self.generateHTMLForError(err,style,endStyle,count,fileName) count=count+1 text=text+'
\n' # Print all the examples and test errors text=text+'

Examples and tests

' text=text+'\n' for err in self.errors: # Print only the tests and the examples if self.testAndExampleErrors.count(err.getNumber())>0: text=text+self.generateHTMLForError(err,'','',count,fileName) count=count+1 text=text+'
\n' # Final infos text=text+'

\n' text=text+'

Subsystem scanned in '+fpformat.fix(self.endFloatTime-self.startFloatTime,3)+' seconds.

' #Close the document text=text+self.getHTMLFooter() #Write the page on disk self.writeHTMLOnFile(text, fileName) def generateHTMLForErrorMember(self,error,fontStyle,endFontStyle): """Return an HTML string of the Members of the error (if any)""" members=error['Members'] htmlText='' htmlText+='' for m in error['Members']: htmlText+='\n'; if m.has_key('name'): htmlText+='\n' else: htmlText+="\n" if m.has_key('type'): htmlText+='\n' else: html+="\n" if m.has_key('description'): htmlText+='\n' else: htmlText+="\n" htmlText+='\n' htmlText+='
NameTypeDescription
'+m['name']+' '+m['type']+' '+m['description']+'
' return htmlText def generateHTMLForError(self,error,fontStyle,endFontStyle,offset,fileName): """Return an html string for the passed error The html generated is a table of errors error is the error to scan fontstyle and endFontStyle are used to color the wrong errors offset is used to generate the name of the sub pages (that describe the details of each error)""" htmlText='\n' errors=error.getErrors() codes=error.getCodes() # Print the error type and number rowSpan=1 if errors!=None: rowSpan = rowSpan + len(errors)+1 if codes!=None: rowSpan = rowSpan + len(codes)+1 # Build the name of the HTML subpage changing the name before the .html # extension # First find the position of '.html' pos = fileName.rfind('html') # Change the name replacing the _d.html to original extension .html detailFileName = fileName[0:pos-1]+'_d'+str(offset)+'.html' # The link of the detailed error HTML page must be relative # so we check if a '/ is present in the detailFileName to create the # linkFileName variable pos = detailFileName.rfind('/') if pos!=-1: # The detail file name is relative linkFileName=detailFileName[pos+1:] else: linkFileName=detailFileName self.generateErrorDetailsHTMLPage(error, detailFileName) htmlText=htmlText+''+fontStyle+''+str(error.getNumber())+''+endFontStyle+'\n' htmlText=htmlText+''+fontStyle+'' htmlText=htmlText+''+error.getName()+'' htmlText=htmlText+''+endFontStyle+'\n' htmlText=htmlText+''+fontStyle+''+error.getFile()+''+endFontStyle+'\n' htmlText=htmlText+'\n' if errors!=None: htmlText=htmlText+''+fontStyle+'Errors'+endFontStyle+'' for t in errors: # Print the defined ErrorCode htmlText=htmlText+'\n' htmlText=htmlText+''+fontStyle+t['name']+endFontStyle+'\n' if t.has_key("shortDescription"): htmlText=htmlText+''+fontStyle+t["shortDescription"]+endFontStyle+'\n' else: htmlText=htmlText+''+fontStyle+"N/A"+endFontStyle+'\n' htmlText=htmlText+'\n' if codes!=None: htmlText=htmlText+''+fontStyle+'Codes'+endFontStyle+'' for t in codes: # Print the defined ErrorCode htmlText=htmlText+'\n' htmlText=htmlText+''+fontStyle+t['name']+endFontStyle+'\n' if t.has_key("shortDescription"): htmlText=htmlText+''+fontStyle+t["shortDescription"]+endFontStyle+'\n' else: htmlText=htmlText+''+fontStyle+"N/A"+endFontStyle+'\n' htmlText=htmlText+'\n' return htmlText def generateHTMLForDetailedError(self,error,fontStyle,endFontStyle): """Return an html string for the passed error The html is a row of the table ....""" errors = error.getErrors() codes = error.getCodes() htmlText='\n' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'
Error name:'+error.getName()+'
Error code: '+str(error.getNumber())+'
The error is defined in: '+error.getFile()+'
IDL
File name:'+error.getName()+'.idl
Module:'+error.getName()+'
C++
Include file name:#include <'+error.getName()+'.h>
Namespace:using namespace '+error.getName()+';
Exception for type:'+error.getName()+'::'+error.getName()+'ExImpl
Library file name:lib'+error.getName()+'.so
lib'+error.getName()+'.a
Python
Python imports:import '+error.getName()+'Impl
Java
Jar file name:'+error.getName()+'.jar
Packages:import alma.'+error.getName()+'.*;
import alma.'+error.getName()+'.wrapper.*;
Exception for type:alma.'+error.getName()+'.'+error.getName()+'Ex
Exception for type (wrapper):alma.'+error.getName()+'.wrapper.AcsJ'+error.getName()+'Ex

\n' # Print the error type and number rowSpan=1 if errors!=None: rowSpan = rowSpan + len(errors) + 1 for t in errors: if t.has_key('Members'): if len(t['Members'])>0: rowSpan = rowSpan +len(t['Members']) + 1 if codes!=None: rowSpan = rowSpan + len(codes) + 1 if errors!=None: htmlText=htmlText+'

Errors

' for t in errors: htmlText=htmlText+'

'+fontStyle+t['name']+endFontStyle+'

\n' htmlText=htmlText+'\n' htmlText=htmlText+'' if t.has_key("shortDescription"): htmlText=htmlText+'\n' else: htmlText=htmlText+'\n' htmlText=htmlText+'' htmlText=htmlText+'' if t.has_key("description"): htmlText=htmlText+'\n' else: htmlText=htmlText+'\n' if t.has_key('Members') and len(t['Members'])>0: htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'
Short description'+fontStyle+t["shortDescription"]+endFontStyle+''+fontStyle+"N/A"+endFontStyle+'
Description'+fontStyle+t["description"]+endFontStyle+''+fontStyle+"N/A"+endFontStyle+'
Members' htmlText=htmlText+self.generateHTMLForErrorMember(t,fontStyle,endFontStyle) htmlText=htmlText+'
IDL
File name:'+error.getName()+'.idl
Module:'+error.getName()+'
Error code:'+t['name']+'
Exception:'+t['name']+'Ex
C++
Include file name:#include <'+error.getName()+'.h>
Namespace:using namespace '+error.getName()+';
Exception for error:'+error.getName()+'::'+t['name']+'ExImpl
Completion:'+error.getName()+'::'+t['name']+'Completion
Library file name:lib'+error.getName()+'.so
lib'+error.getName()+'.a
Python
Imports:import '+error.getName()+'Impl
Raise a local exception:raise '+error.getName()+'Impl.'+t['name']+'ExImpl()
Catch exception:except '+error.getName()+'Impl.'+t['name']+'Ex, e:
Java
Jar file name:'+error.getName()+'.jar
Packages:import alma.'+error.getName()+'.*;
import alma.'+error.getName()+'.wrapper.*;
Exception for error:alma.'+error.getName()+'.'+t['name']+'Ex
Exception for error (wrapper):alma.'+error.getName()+'.wrapper.AcsJ'+t['name']+'Ex

\n' htmlText=htmlText+'

Codes

' if codes!=None: for t in codes: # Print the defined ErrorCode htmlText=htmlText+'

'+fontStyle+t['name']+endFontStyle+'

\n' htmlText=htmlText+'\n' htmlText=htmlText+'' if t.has_key("shortDescription"): htmlText=htmlText+'\n' else: htmlText=htmlText+'\n' htmlText=htmlText+'' htmlText=htmlText+'' if t.has_key("description"): htmlText=htmlText+'\n' else: htmlText=htmlText+'\n' htmlText=htmlText+'\n' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'' htmlText=htmlText+'
Short description'+fontStyle+t["shortDescription"]+endFontStyle+''+fontStyle+"N/A"+endFontStyle+'
Description'+fontStyle+t["description"]+endFontStyle+''+fontStyle+"N/A"+endFontStyle+'
IDL
File name:'+error.getName()+'.idl
Module:'+error.getName()+'
Error code:'+t['name']+'
C++
Include file name:include <'+error.getName()+'.h>
Namespace:using namespace '+error.getName()+';
Completion:'+error.getName()+'::'+t['name']+'Completion
Library file name:lib'+error.getName()+'.so
lib'+error.getName()+'.a
Python
Imports:import '+error.getName()+'Impl
Java
Jar file name:'+error.getName()+'.jar
Packages:import alma.'+error.getName()+'.*;
import alma.'+error.getName()+'.wrapper.*;
\n' else: htmlText=htmlText+'

No codes defined' # Insert the XML htmlText=htmlText+'

XML

\n' htmlText=htmlText+self.insertFileContent(error.getFile()) return htmlText def generateErrorDetailsHTMLPage(self,error,fileName): """Generate the HTML with the detail of a single error""" page=self.getHTMLHeader(error.getName()) #Write the title of the page page=page+'

Error of '+self.subsystemName+'::'+error.getName()+'

\n' page=page+'

Generated scanning the basedir '+self.baseDir+'
\n' page=page+'Generated at '+self.startTime+'

' page=page+'
\n' #Generate the table with the error details page=page+self.generateHTMLForDetailedError(error, '', '') page=page+'

Info and errors

' if self.testAndExampleErrors.count(error.getNumber())>0: page=page+'

This error is used for test/examples

' else: if self.outOfRangeErrors.count(error.getNumber())>0: page=page+'

The error number is out of the allowed range for this subsystem

' else: page=page+'

The number is in the corrrect range

' if self.duplicatedErrors.count(error.getNumber())>0: page=page+'

There is another error in this subsystem with the same number

' else: page=page+'

The error is not duplicated

' # Final infos page=page+'

\n' page=page+'

Subsystem scanned in '+fpformat.fix(self.endFloatTime-self.startFloatTime,3)+' seconds.

' page=page+self.getHTMLFooter() self.writeHTMLOnFile(page, fileName) def writeHTMLOnFile(self,page,fileName): """page if an string with the HTML page to write in the file fileName is the name of the file If fileName is None the page is written in the stdout""" if fileName==None: print page # To stdout else: try: outF=open(fileName,"w+") outF.write(page) outF.flush() outF.close except IOError, e: print "Unable to open ",fileName, "for output" print "Exception",e return