#!/usr/bin/python2.4 -u # TeXLive.py # -*- encoding: utf-8 -*- # Usage: call with argument of --help # 2007-Jul-28 Jim Hefferon ftpmaint@tug.ctan.org """ Read a TeX Live package dB file. """ __version__='0.9.9' __author__='Jim Hefferon' __date__='2007-Jul-15' __notes__="""This version has been stripped of utility routines for the texlive folks. """ import os, os.path, sys import re import optparse import codecs import time import pprint # import library # from util import * # import dBUtils TLPDB_FILENAME='/home/svn/texlive/trunk/Master/texlive.tlpdb' THIS_SCRIPT=sys.argv[0] DEBUG=False LOGGING=False LOGFILE_NAME=os.path.splitext(THIS_SCRIPT)[0]+'.log' VERBOSE=False import locale # What encoding for the terminal? locale_language, locale_output_encoding = locale.getdefaultlocale() if locale_output_encoding is None: locale_output_encoding='UTF-8' #---------------------- stuff from util.py import StringIO # for msg import traceback # for msg import logging # for openLog, msg import locale # for warn messages import types import grp, pwd # get user name and group name import pprint # to make nice debugging structures termEncoding='UTF-8' class utilError(StandardError): pass # Getting the string from the exception is a problem if you want unicode, # since str() is no good for that, and I can't get err.message to work. # I need the same functionality for my exceptions as for Python's so # I wrap a call to err[0] def errorValue(err): """Return the string error message from an exception. err exception instance Note: I cannot get err.message to work. I sent a note to clp on Jan 29 2007 with a related query and this is the best that I figured out. """ try: return str(err) except: return err[0] # one of mine, in unicode class jhError(StandardError): """Subclass this to get exceptions that behave correctly when you do this. try: raise subclassOfJhError, 'some error message with unicode chars' except subclassOfJhError, err mesg='the message is '+unicode(err) I don't believe that the built-in exceptions ever pass unicode so you can use unicode(err) on those also, I think, provided that you don't raise them yourself. """ def __unicode__(self): return errorValue(self) # You can use a string to set the level, or a constant from the logging # module _logLevels={'debug':logging.DEBUG, 'info':logging.INFO, 'warn':logging.WARN, 'error':logging.ERROR, 'critical':logging.CRITICAL, logging.DEBUG:logging.DEBUG, logging.INFO:logging.INFO, logging.WARN:logging.WARN, logging.WARN:logging.ERROR, logging.CRITICAL:logging.CRITICAL} # openLog Open a logging instance def openLog(fn,name=None,purpose=None,format="%(levelname)s\t%(asctime)s: %(message)s",level=logging.DEBUG,announce=False): """Open an instance of the logging module. Does not append to an old file; instead it makes a new one. fn Name of the file. name=None Name of the logging instance; if None then fn is used purpose=None Name of calling program; if None then no head line on log file is written format='%(levelname)s %(asctime)s: %(message)s' Format for log file entries announce=False Announce in log file that logging has started These are available for the format: %(name)s Name of the logger (logging channel) %(levelno)s Numeric logging level for the message (DEBUG, INFO, WARN, ERROR, CRITICAL) %(levelname)s Text logging level for the message ("DEBUG", "INFO", "WARN", "ERROR", "CRITICAL") %(pathname)s Full pathname of the source file where the logging call was issued (if available) %(filename)s Filename portion of pathname %(module)s Module (name portion of filename) %(lineno)d Source line number where the logging call was issued (if available) %(created)f Time when the LogRecord was created (time.time() return value) %(asctime)s Textual time when the LogRecord was created %(msecs)d Millisecond portion of the creation time %(relativeCreated)d Time in milliseconds when the LogRecord was created, relative to the time the logging module was loaded (typically at application startup time) %(thread)d Thread ID (if available) %(message)s The result of record.getMessage(), computed just as the record is emitted May raise a utilError. """ if not(purpose is None): # write a header on the file, if it is new if not(os.path.isfile(fn)): try: f=open(fn,'w') f.write("# log file for %s\n" % (purpose,)) f.close() except IOError, err: raise utilError, "Log file %s not present and unable to open and write to a new log file: %s" % (fn,errorValue(err)) if name is None: log=logging.getLogger(fn) else: log=logging.getLogger(name) logger_formatter=logging.Formatter(format) try: logger_handler=logging.FileHandler(fn) except Exception, err: raise utilError, "Unable to associate a log with %s: %s" % (fn,errorValue(err)) logger_handler.setFormatter(logger_formatter) log.addHandler(logger_handler) try: log.setLevel(_logLevels[level]) except: raise utilError, "Unable to set level of the log" if announce: try: log.info("Logging started") except Exception, err: raise utilError, "Unable to initially write to the log with file name %s: %s" % (fn,errorValue(err)) return log # msg return a message, for debugging, feedback to the user, etc., and # optionally log def msg(m,devel=None,log=None,logLevel='debug',debug=False,**mDct): """Return a message. Options allow it to be logged, or for a special debugging flag to be switched to give additional information m The message returned. Substitutions from the keyword arguments at end devel=None String that will be used if the DEBUG flag is set for developer (and used for writing to the log). If devel=None, then m is used. log A logging object logLevel='debug' One of 'debug', 'info', 'warn', 'error', 'critical', or logging.DEBUG, .. logging.CRITICAL debug=False Boolean select which message m or mDevel is shown. **mDct Keyword argument that are substituted into m and mDevel Note that if an exception is pending then a traceback gets printed to the log file. """ # Indicate log level try: level=_logLevels[logLevel] except: raise utilError, "Unable to set level of the log to %s" % (repr(logLevel),) # The main part if devel is None: devel_str=m else: devel_str=devel #if log: # log.debug("in msg: m=<%s>, devel_str=<%s>" % (m,devel_str)) if log: (excClass,excObj,excTb)=sys.exc_info() # is there a pending exception? if excClass: tbStr=StringIO.StringIO() traceback.print_exc(None,tbStr) mDct['tB']=tbStr.getvalue().rstrip() log.log(level,(devel_str+"\n %(tB)s") % mDct) else: log.log(level,devel_str % mDct) if debug: m=devel_str # expect that everybody needs to be encoded; print can't do characters numbered greater than 128 for (k,v) in mDct.items(): try: if not(isinstance(v,UnicodeType)): mDct[k]=unicode(v,termEncoding,'replace') except: try: mDct[k]=unicode(str(v),termEncoding,'replace') #? has no unicode method, but has a str method? Like StandardError? except: mDct[k]="Error in util.msg() -- string conversion to unicode impossible: %s" % (repr(v),) # give a message with some hint of what's gone wrong return m % mDct DEFAULT_EXCEPTION=utilError WARNINGS_WANTED=False # sometimes, as for unit testing, nice to turn off def noteException(m,devel=None,log=None,logLevel='error',exception=DEFAULT_EXCEPTION,debug=None,**mDct): """ Raise an exception, and also, optionally, note it in a log file m string Error message for users; use '%(id)s'-style substitution devel=None string String for development; sub as in m. log Instance of logging module exception An exception debug Print the error to the stderr? **mDct Additional keyword arguments; like 'id=7' will be subbed into m, devel Uses the msg function; will raise exception. """ # print "util.py.noteException: m is: ",m," mDct is",pprint.pformat(mDct) if sys.exc_info()[0]: # if there is an active exception extraStr=" (exception is %s)" % (exceptionName(),) if devel is None: m+=extraStr else: devel+=extraStr r=msg(m,devel=devel,log=log,logLevel=logLevel,debug=debug,**mDct) if (debug and not(log) and WARNINGS_WANTED): warn(r,log=None,debug=debug) # print to stderr raise exception, r def warn(s,leader='WARNING: ',log=None,debug=False,**mDct): """Send a message to stderr and possibly log it s Message to report log=None A logging object debug=False Whether to report debugging information Warning! You can't use leader, log, or debug as a key """ # print >>sys.stderr, leader, msg(s,log=log,logLevel='error',debug=debug,**mDct).encode(locale.getdefaultlocale()[1]) if not(isinstance(s,types.UnicodeType)): s=unicode(s,termEncoding,'replace') # print "in warn(): s=",s," and mDct.keys() is ",repr(mDct.keys()),"<<<<<===" print >>sys.stderr, leader, msg(s,log=log,logLevel='error',debug=debug,**mDct).encode(termEncoding) return None def ann(s,log=None,debug=False,**mDct): """Issue a warning-like message without the 'WARNING' message. s Message to report; may contain %(var)s as long as they are matched by var=var in the tailing arguments log=None,debug=False debugging stuff **mDct variables to substitute in s """ return warn(s,leader='',log=log,debug=debug,**mDct) def note(s,log,flag=os.environ.has_key('GATEWAY_INTERFACE'),leader="NOTE: "): """Take the message s and either log it or print it, depending on the flag. The idea is to debug with note('informational message',log) and have it do the right thing, in a cgi context or not. s message to use log logging instance flag=os.environ.has_key('GATEWAY_INTERFACE') if True, use log. If False, use stdout (default is True iff running as a CGI script) """ # programming note: # I've avoided too many options, such as potentially outputting # to stderr or using another log level; we'll see if simple works. if flag and log: log.info(s) else: warn(s,leader=leader,log=log) # no debug because no devel string def fail(s,returnCode=1,log=None,debug=False,**mDct): """Drop out, reporting a return code, and possibly logging a message s Message to report returnCode=1 return code log=None A logging object debug=False Whether to report debugging information Warning! You can't use returnCode, leader, log, or debug as a key """ warn(s,leader='ERROR: ',log=log,debug=debug,**mDct) sys.exit(returnCode) # exceptionName: get the name of the raised exception # for use as: # try: # .. # except specificError, err: # .. # except Exception, err: # eN=exceptionName() # print "exception was",eN # def exceptionName(): """Get the name of the currently raised exception. """ fullName=sys.exc_info()[0] # looks like "'exceptions.AttributeError'" if fullName is None: return 'No exception' # return rather than None, as I don't expect it to happen, and I don't want the failure if it is None else: return str(fullName).split('.')[-1] #---------------------------------------- #-------------------------------stuff from dButils.py ## import psycopg2, psycopg2.extensions ## DEFAULT_DBCONNECTION_STRING="user=hefferon dbname=ctan password=Gd6eBitK55y" ## DEFAULT_DATABASE_ENCODING='UTF-8' ## dBConnectionString=DEFAULT_DBCONNECTION_STRING # can be force-changed for debugging ## class dBUtilsError(StandardError): ## pass ## def opendB(connectionstring=dBConnectionString,log=None,debug=DEBUG,encoding=DEFAULT_DATABASE_ENCODING,utc=True): ## """ ## Make a connection to the database; return the pair (connection, cursor). ## connectionstring=dBConnectionstring string See doc for psycopg ## log=None logging object ## debug=DEBUG Whether to output extra information ## encoding='UNICODE' What to set the client encoding to (default is utf-8) ## utc=True Set time zone to utc (alternative is local time) ## May raise a dBUtilsError. ## """ ## if encoding.upper()=='UTF-8': ## encoding='UNICODE' # postgres's name for UTF-8 ## if not(encoding in psycopg2.extensions.encodings.keys()): ## mesg=u'Unable to make a database connection' ## noteException(mesg,devel=mesg+" using connection string %(connectionstring)s because the called-for encoding %(encoding)s is not one known to psycopg2",log=log,exception=dBUtilsError,debug=debug,connectionstring=connectionstring,encoding=encoding) ## try: ## dBcnx = psycopg2.connect(connectionstring) ## # dBcnx.set_client_encoding(encoding) # causes a failure JH 05-10-12 ## dBcsr = dBcnx.cursor() ## if utc: ## dBcsr.execute("SET TIME ZONE 'UTC'") ## dBcsr.execute("SET client_encoding TO "+encoding) ## if encoding=='UNICODE': # any way to systematize this code? ## psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) ## elif encoding=='LATIN1': ## psycopg2.extensions.register_type(psycopg2.extensions.LATIN1) ## elif encoding=='SQL_ASCII': ## psycopg2.extensions.register_type(psycopg2.extensions.SQL_ASCII) ## elif encoding=='latin-1': ## psycopg2.extensions.register_type(psycopg2.extensions.LATIN1) ## except psycopg2.DatabaseError, err: ## mesg=u'Unable to connect to the database' ## noteException(mesg,devel=mesg+" using connection string %(connectionstring)s: %(err)s",log=log,exception=dBUtilsError,debug=debug,connectionstring=connectionstring,err=err) ## except Exception, err: ## eN=exceptionName() ## mesg=u'Unable to connect to the database' ## noteException(mesg,devel=mesg+" (exception: %(eN)s) using connection string %(connectionstring)s: %(err)s",log=log,exception=dBUtilsError,debug=debug,eN=eN,connectionstring=connectionstring,err=err) ## return (dBcnx,dBcsr) ## def getData(sql,dct={},dBcsr=None,selectOne=False,log=None,debug=DEBUG,listtype='list from the database'): ## """ ## Get data out of the database, returning a list of lists ## sql SQL statement; typically a SELECT statement ## dct={} Dictionary of substitutions into SQL statement (unicode strings ## are encoded according to the encoding) ## dBcsr=None database cursor. If None, one will be generated ## log=None Instance of logging object ## selectOne=False With a dB result, issue a fetchone() or a fetchall()? ## debug=DEBUG Whether to include debugging information ## listtype Phrase for use in error messages ## Psycopg2 will handle conversion of unicode strings. ## """ ## if not(dBcsr): # sanity check ## mesg=u'Error getting access to the database' ## noteException(mesg,devel=mesg+": no database cursor, while getting a %(listtype)s",log=log,exception=dBUtilsError,debug=debug,listtype=listtype) ## try: ## dBcsr.execute(sql,dct) ## if selectOne: ## dBres=dBcsr.fetchone() ## if dBres is None: ## lst=[] # an empty list so always yield a sequence ## else: ## lst=dBres # one tuple ## else: ## lst=dBcsr.fetchall() # a list with possibly many tuples ## except psycopg2.DatabaseError, err: ## mesg=u'Database operation error fetching a %(listtype)s' ## noteException(mesg,devel=mesg+" with query "+dBcsr.query+": %(err)s -- "+dBcsr.statusmessage,log=log,exception=dBUtilsError,debug=debug,listtype=listtype,sql=sql,dct=repr(dct),err=err) ## except Exception, err: ## eN=exceptionName() ## noteException("Database error (unknown kind) fetching a %(listtype)s",devel="Database error (exception %(eN)s) fetching a %(listtype)s with sql='%(sql)s' and dct=%(dct)s: %(err)s",log=log,exception=dBUtilsError,debug=debug,listtype=listtype,eN=eN,err=err) ## return lst ## def putData(sql,dct={},dBcsr=None,log=None,encoding=DEFAULT_DATABASE_ENCODING,debug=DEBUG,listtype='list to the database'): ## """ ## Put data into the database, encoding unicode strings on their way in ## sql SQL statement; should be an INSERT or an UPDATE ## dct={} Dictionary of substitutions into SQL statement (unicode strings ## are encoded according to the encoding) ## dBcsr=None database cursor. If None, one will be generated ## log=None Instance of logging object ## encoding=DEFAULT_DATABASE_ENCODING Unicode encoding to use for the ## database ## debug=DEBUG Whether to include debugging information ## listtype Phrase for use in error messages ## Does *not* do a dBcsr.commit(). ## """ ## if not(dBcsr): # sanity check ## mesg=u'Error accessing the database' ## noteException(mesg,devel=mesg+" while putting a %(listtype)s: no database cursor",log=log,exception=dBUtilsError,debug=debug,listtype=listtype) ## try: ## dBcsr.execute(sql,dct) ## except (psycopg2.DatabaseError,psycopg2.ProgrammingError), err: ## eN=exceptionName() ## mesg=u'Database operation error' ## noteException(mesg,devel=mesg+" (exception %(eN)s) %(err)s while putting a %(listtype)s with query="+dBcsr.query+": %(err)s -- "+dBcsr.statusmessage,log=log,exception=dBUtilsError,debug=debug,listtype=listtype,eN=eN,err=err) ## except Exception, err: ## eN=exceptionName() ## mesg=u'Database operation error' ## noteException(mesg,devel=mesg+" (exception %(eN)s) %(err)s while putting a %(listtype)s",log=log,exception=dBUtilsError,debug=debug,eN=eN,err=str(err),listtype=listtype) ## return None #------------------------------------------------- class tlError(jhError): pass tlobjKeys=set(['name','category','catalogue','shortdesc','longdesc','depend','execute','revision','srcsize','srcfiles','docsize','docfiles','runsize','runfiles','binsize','binfiles','cataloguedata']) class tlpobj(object): """Give information based on a TeXLive object file """ def __init__(self,verbose=False,log=None,debug=False): """Initialize an object. """ self.verbose=verbose self.log=log self.debug=debug self.__initializeUserVars() self.value={} # map name --> tlpkg return None def __initializeUserVars(self): self.name=None self.category=None self.catalogue=None self.shortdesc=None self.longdesc=None self.depend=None self.execute=None self.revision=None self.srcfiles=None # list of files self.runfiles=None # list of files self.docfiles=None # dct fileName--> (dct tagkey --> tagval) self.binfiles=None # arch --> list of files self.srcsize=None # int (or None) self.runsize=None # int (or None) self.docsize=None # int (or None) self.binsize=None # arch --> size (or None) self.cataloguedata=None # cataloguekey --> value return None def __unicode__(self): r=[] r.append("name: %s" % (pprint.pformat(self.name),)) r.append(" category: %s" % (pprint.pformat(self.category),)) r.append(" catalogue: %s" % (pprint.pformat(self.catalogue),)) r.append(" shortdesc: %s" % (pprint.pformat(self.shortdesc),)) r.append(" longdesc: %s" % (pprint.pformat(self.longdesc),)) r.append(" depend: %s" % (pprint.pformat(self.depend),)) r.append(" execute: %s" % (pprint.pformat(self.execute),)) r.append(" revision: %s" % (pprint.pformat(self.revision),)) r.append(" srcsize: %s" % (pprint.pformat(self.srcsize),)) r.append(" srcfiles: %s" % (pprint.pformat(self.srcfiles),)) r.append(" docsize: %s" % (pprint.pformat(self.docsize),)) r.append(" docfiles: %s" % (pprint.pformat(self.docfiles),)) r.append(" runsize: %s" % (pprint.pformat(self.runsize),)) r.append(" runfiles: %s" % (pprint.pformat(self.runfiles),)) r.append(" binsize: %s" % (pprint.pformat(self.binsize),)) r.append(" binfiles: %s" % (pprint.pformat(self.binfiles),)) r.append(" cataloguedata: %s" % (pprint.pformat(self.cataloguedata),)) return "\n".join(r) def writeout(self,fn=None): """Write a textual representation fn=None file name; if None use sys.stdout """ if fn is None: f=sys.stdout else: try: f=open(fn,'w') except Exception, err: mesg='unable to open file for writing' noteException(mesg,devel=mesg+' %(fn)s: %(err)s',exception=tlError,log=log,debug=self.debug,fn=repr(fn),err=unicode(err)) self.writeout_fh(f) f.close() return None def writeout_fh(self,f): """Write a string representation of the user variables the filhandle. Note that files are listed in sorted order. f filehandle """ # Make the string r=[] r.append("name %s" % (self.name,)) if self.category: r.append("category %s" % (self.category,)) if self.shortdesc: r.append("shortdesc %s" % (self.shortdesc,)) if self.longdesc: for line in self.longdesc: r.append("longdesc %s" % (line,)) if self.revision: r.append("revision %s" % (self.revision,)) if self.depend: for line in self.depend: r.append("depend %s" % (line,)) if self.execute: for line in self.execute: r.append("execute %s" % (line,)) if self.docfiles: if self.docsize is None: mesg='docsize is not there but there are docfiles' noteException(mesg,devel=mesg,exception=tlError,log=self.log,debug=self.debug) r.append("docfiles size=%s" % (self.docsize,)) dFiles=self.docfiles.keys() dFiles.sort() for line in dFiles: s=[] for (k,v) in self.docfiles[line].items(): s.append("%s=%s" % (k,v)) dLine=" "+line if s: dLine+=" "+(" ".join(s)) r.append(dLine) if self.srcfiles: if self.srcsize is None: mesg='srcsize is not there but there are srcfiles' noteException(mesg,devel=mesg,exception=tlError,log=self.log,debug=self.debug) r.append("srcfiles size=%s" % (self.srcsize,)) self.srcfiles.sort() for line in self.srcfiles: r.append(" "+line) if self.runfiles: if self.runsize is None: mesg='runsize is not there but there are runfiles' noteException(mesg,devel=mesg,exception=tlError,log=self.log,debug=self.debug) r.append("runfiles size=%s" % (self.runsize,)) self.runfiles.sort() for line in self.runfiles: r.append(" "+line) if self.binfiles: arches=self.binfiles.keys() arches.sort() for arch in arches: if self.binsize[arch] is None: mesg='binsize is not there but there are binfiles' noteException(mesg,devel=mesg+" for %(arch)s",exception=tlError,log=self.log,debug=self.debug,arch=repr(arch)) r.append("binfiles arch=%s size=%s" % (arch,self.binsize[arch],)) sBinfiles=self.binfiles[arch] sBinfiles.sort() for line in sBinfiles: r.append(" "+line) if self.cataloguedata: cdKeys=self.cataloguedata.keys() for k in cdKeys: r.append("catalogue-%s %s" % (k,self.cataloguedata[k])) # Write that string f.write("\n".join(r)) return None ## def to_db(self,dBcsr,dBcnx,careful=True,associatedCatPkgs=None): ## """Put the file information to the database ## dBcsr, dBcnx database cursor and connection ## careful=True raise exception on error? ## associatedCatPkgs=None list of catalogue package id's that either ## are part of this texlive package of of which this texlive package ## is a part (None is the same as using what is now in the dB) ## """ ## tlId=self.name ## # Drop the file records if they are there ## sql="DELETE FROM tds_files WHERE tl_id=%(tlId)s" ## dct={'tlId':tlId} ## try: ## dBUtils.putData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='drop old texlive files record') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to drop existing tds files record' ## noteException(mesg,devel=mesg+' for package %(pkgId)s: %(err)s',log=self.log,debug=self.debug,pkgId=repr(pkgId),err=errorValue(err)) ## # Do the tl_cat records; see which ones to do ## if associatedCatPkgs is None: ## sql="SELECT pkg_id FROM tl_cat WHERE tl_id=%(tlId)s" ## dct['tlId']=tlId ## try: ## dBres=dBUtils.getData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='get list of catalogue packages for this texlive package') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to get list of catalogue packages for this texlive packages ' ## noteException(mesg,devel=mesg+'%(tlId)s: %(err)s',log=self.log,debug=self.debug,tlId=repr(tlId),err=errorValue(err)) ## associatedCatPkgs=[a for (a,) in dBres] ## if not(associatedCatPkgs): # try tlId ## possiblePkgId=tlId.lower() ## sql="SELECT id FROM package WHERE id=%(pkgId)s" ## dct['pkgId']=possiblePkgId ## try: ## dBres=dBUtils.getData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='check for existence of a catalogue id') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to check for existence of a catalogue id' ## noteException(mesg,devel=mesg+'%(pkgId)s: %(err)s',log=self.log,debug=self.debug,pkgId=repr(possiblePkgId),err=errorValue(err)) ## if dBres: ## associatedCatPkgs.append(possiblePkgId) ## # drop any that are there now ## sql="DELETE FROM tl_cat WHERE tl_id=%(tlId)s" ## dct={'tlId':tlId} ## try: ## dBUtils.putData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='drop old texlive package-catalogue package association') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to drop existing texlive package record' ## noteException(mesg,devel=mesg+' for package %(tlId)s: %(err)s',log=self.log,debug=self.debug,tlId=repr(tlId),err=errorValue(err)) ## # Drop the tl records if they are there ## sql="DELETE FROM tl WHERE tl_id=%(tlId)s" ## dct={'tlId':tlId} ## try: ## dBUtils.putData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='drop old texlive package record') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to drop existing texlive package record' ## noteException(mesg,devel=mesg+' for package %(tlId)s: %(err)s',log=self.log,debug=self.debug,tlId=repr(tlId),err=errorValue(err)) ## # Insert new tl record ## sql="INSERT INTO tl (tl_id,shortdesc,longdesc) VALUES (%(tlId)s,%(shortdesc)s,%(longdesc)s)" ## dct['shortdesc']=self.shortdesc ## dct['longdesc']=self.longdesc ## try: ## dBUtils.putData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='insert new texlive package record') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to insert new texlive package record' ## noteException(mesg,devel=mesg+' for package %(tlId)s: %(err)s',log=self.log,debug=self.debug,tlId=repr(tlId),err=errorValue(err)) ## # insert new tl_cat records ## if not(tlId.startswith('bin-') ## or tlId.startswith('collection-') ## or tlId.startswith('hyphen-') ## or tlId.startswith('lib-') ## or tlId.startswith('scheme-') ## or tlId.startswith('texlive-')): ## if (not(associatedCatPkgs) ## and careful): ## mesg=u'there are no catalogue packages associated with the TeX Live package %(tlId)s' ## warn(mesg,log=self.log,debug=self.debug,tlId=repr(tlId)) ## for pkgId in associatedCatPkgs: ## sql="INSERT INTO tl_cat (tl_id,pkg_id) VALUES (%(tlId)s,%(pkgId)s)" ## dct['pkgId']=pkgId ## try: ## dBUtils.putData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='insert new tl_cat package record') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to insert new association between a texlive package and a catalogue package' ## noteException(mesg,devel=mesg+' for texlive package %(tlId)s and catalgoue package %(pkgId)s: %(err)s',log=self.log,debug=self.debug,tlId=repr(tlId),pkgId=repr(pkgId),err=errorValue(err)) ## # Insert the file records ## # First build a list of pairs: (fileName,fileType) ## fileList=[] ## if self.srcfiles: ## for fn in self.srcfiles: ## fileList.append((fn,'src')) ## if self.runfiles: ## for fn in self.runfiles: ## fileList.append((fn,'run')) ## if self.docfiles: ## for fn in self.docfiles.keys(): ## fileList.append((fn,'doc')) ## if self.binfiles: ## for arch in self.binfiles.keys(): ## for fn in self.binfiles[arch]: ## fileList.append((fn,'bin')) ## # .. then put the info in the dB ## dct={'tlId':tlId} ## for (fn,fileType) in fileList: ## pathDir,pathBase=os.path.split(fn) ## sql="INSERT INTO tds_files (tl_id,type,path_dir,path_base) VALUES (%(tlId)s,%(fileType)s,%(pathDir)s,%(pathBase)s)" ## dct['fileType']=fileType ## dct['pathDir']=pathDir ## dct['pathBase']=pathBase ## try: ## dBUtils.putData(sql,dct=dct,dBcsr=dBcsr,log=self.log,debug=self.debug,listtype='insert new texlive files record') ## except dBUtils.dBUtilsError, err: ## mesg=u'unable to insert new tds files record' ## noteException(mesg,devel=mesg+' for package %(tlId)s with type=%(fileType)s, pathDir=%(pathDir)s, pathBase=%(pathBase)s: %(err)s',log=self.log,debug=self.debug,tlId=repr(tlId),fileType=repr(fileType),pathDir=repr(pathDir),pathBase=repr(pathBase),err=errorValue(err)) ## # Commit ## try: ## dBcnx.commit() ## except Exception, err: ## mesg=u'unable to commit the insert of the new tds files record' ## noteException(mesg,devel=mesg+' for package %(pkgId)s: %(err)s',log=self.log,debug=self.debug,tlId=repr(tlId),err=errorValue(err)) ## return None def from_file(self,fn=None): """Read a tlpobj from a file. You don't want to do this. """ if fn is None: f=sys.stdin else: try: f=open(fn,'rU') except Exception, err: mesg='unable to open tlpobj file' noteException(mesg,devel=mesg+' %(fn)s: %(err)s',exception=tlError,log=log,debug=self.debug,fn=repr(fn),err=unicode(err)) r=self.from_fh(f,multi=True,lineNo=0) return None initialState='init' keyValueStates=tlobjKeys # line of form "key value" filePathStates=set(['srcfiles_file','runfiles_file','docfiles_file','binfiles_file']) # line contains file path indented by space nextStates={initialState:tlobjKeys, 'name':keyValueStates-set(['name']), 'category':keyValueStates-set(['category']), 'catalogue':keyValueStates-set(['catalogue']), 'shortdesc':keyValueStates-set(['shortdesc']), 'longdesc':keyValueStates, 'depend':keyValueStates, 'execute':keyValueStates, 'revision':keyValueStates-set(['revision']), 'srcfiles':keyValueStates | set(['srcfiles_file'])-set(['srcfiles']), 'srcfiles_file':keyValueStates | set(['srcfiles_file']), 'runfiles':keyValueStates | set(['runfiles_file'])-set(['runfiles']), 'runfiles_file':keyValueStates | set(['runfiles_file']), 'docfiles':keyValueStates | set(['docfiles_file'])-set(['docfiles']), 'docfiles_file':keyValueStates | set(['docfiles_file'])-set(['docfiles']), 'binfiles':keyValueStates | set(['binfiles_file']), 'binfiles_file':keyValueStates | set(['binfiles_file']), 'cataloguedata':keyValueStates} def from_fh(self,f,multi=None,lineNo=None): """Read a tlpobj from a file handle. Returns None if EOF read before anything is found. f an opened file multi=None If not None then multiple tplobj's in the file are treated as an error lineNo=None debugging May raise tlError. """ state=self.initialState currentArch=None # used for binfiles # Parse the lines anyNonblankLinesRead=False while True: line=f.readline() if not(line): # EOF? return False # if self.debug: # note('line is '+repr(line),self.log) line=line.rstrip() if not(anyNonblankLinesRead): if (not(line) or line[0]=='#'): # throw away initial blank lines continue else: anyNonblankLinesRead=True # pkg=tplpkg(log=self.log,debug=self.debug) if not(line): if multi is None: mesg=u'unexpected multiple tpl objects' noteException(mesg,devel=mesg+'; trailing blank line found at line %(lineNo)s',exception=tlError,log=self.log,debug=self.debug,lineNo=repr(lineNo)) else: return self # Find state under which this line will be read fields=None if (state=='srcfiles' or state=='srcfiles_file'): if line[0]==' ': newState='srcfiles_file' fields=line[1:].split() if (state=='runfiles' or state=='runfiles_file'): if line[0]==' ': newState='runfiles_file' fields=line[1:].split() elif (state=='docfiles' or state=='docfiles_file'): if line[0]==' ': newState='docfiles_file' fields=line[1:].split() elif (state=='binfiles' or state=='binfiles_file'): if line[0]==' ': newState='binfiles_file' fields=line[1:].split() if fields is None: try: fields=line.split() newState=fields[0] except: mesg=u'no key unable in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; while in state %(state)s unable to get the new state in line=%(line)s',returnCode=10,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),state=repr(state)) if not(newState=='binfiles_file'): currentArch=None if not(newState in self.nextStates[state]): mesg=u'unable to transition to the next state in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; while in state %(state)s the next state %(newState)s is not legal in line=%(line)s',returnCode=10,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),state=repr(state),newState=repr(newState)) # Handle line state by state if newState==self.initialState: mesg=u'unexpected initial state in line=%(lineNo)s' noteException(mesg,devel=mesg+'; while in state %(state)s now in initial state reading line=%(line)s',returnCode=10,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),state=repr(state)) if newState=='name': try: self.name=fields[1] if self.debug: print "package name is",repr(self.name) except Exception, err: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s: %(err)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState),err=errorValue(err)) elif newState=='category': try: self.category=fields[1] except: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='catalogue': try: self.catalogue=fields[1] except: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='shortdesc': try: if self.shortdesc is None: self.shortdesc="" if self.shortdesc: self.shortdesc+=' ' self.shortdesc+=" ".join(fields[1:]) except: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='longdesc': try: if self.longdesc is None: self.longdesc="" if self.longdesc: self.longdesc+="\n" self.longdesc+=" ".join(fields[1:]) except Exception, err: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s: %(err)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState),err=errorValuse(err)) elif newState=='depend': if len(fields)>0: if self.depend is None: self.depend=[] self.depend.append(fields[1]) else: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='execute': if len(fields)>0: if self.execute is None: self.execute=[] self.execute.append(" ".join(fields[1:])) else: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='revision': try: self.revision=fields[1] except: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='srcfiles': try: sizeString=fields[1] except: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) try: sizeFields=sizeString.split('=') if sizeFields[0].lower()!='size': raise tlError size=int(sizeFields[1]) if self.srcsize is None: self.srcsize=0 self.srcsize+=size except: mesg=u'expected a value of size=NNNNNN in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) if self.srcfiles is None: self.srcfiles=[] elif newState=='srcfiles_file': try: self.srcfiles.append(fields[0].strip()) except: mesg=u'unable to understand file name in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='docfiles': try: sizeString=fields[1] except: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) try: sizeFields=sizeString.split('=') if sizeFields[0].lower()!='size': raise tlError size=int(sizeFields[1]) if self.docsize is None: self.docsize=0 self.docsize+=size except: mesg=u'expected a value of size=NNNNNN in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) if self.docfiles is None: self.docfiles={} elif newState=='docfiles_file': kvDct={} kvString=None try: for kvString in fields[1:]: if kvString: k,v=kvString.split("=") kvDct[k]=v except: mesg=u'unable to understand file description in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s; key-value description string=%(kvString)s; reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState),kvString=repr(kvString)) try: self.docfiles[fields[0].strip()]=kvDct except Exception, err: mesg=u'unable to understand file name in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s: %(err)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState),err=errorValue(err)) elif newState=='runfiles': try: sizeString=fields[1] except: mesg=u'no value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) try: sizeFields=sizeString.split('=') if sizeFields[0].lower()!='size': raise tlError size=int(sizeFields[1]) if self.runsize is None: self.runsize=0 self.runsize+=size except: mesg=u'expected a value of size=NNNNNN in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) if self.runfiles is None: self.runfiles=[] elif newState=='runfiles_file': try: self.runfiles.append(fields[0].strip()) except: mesg=u'unable to understand file name in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) elif newState=='binfiles': try: firstString,secondString=fields[1],fields[2] except: mesg=u'expected an arch=XXXX size=NNNNNN in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) try: firstStringFields=firstString.split('=') secondStringFields=secondString.split('=') if firstStringFields[0].lower()=='arch': archFields=firstStringFields sizeFields=secondStringFields else: archFields=secondStringFields sizeFields=firstStringFields if (archFields[0].lower()!='arch' or sizeFields[0].lower()!='size'): raise tlError currentArch=archFields[1] except: mesg=u'expected two values of the form x=y in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) try: if self.binsize is None: self.binsize={} self.binsize[currentArch]=int(sizeFields[1]) except: mesg=u'expected size to be an integer in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) if self.binfiles is None: self.binfiles={} self.binfiles[currentArch]=[] elif newState=='binfiles_file': if currentArch is None: mesg=u'do not know the right architecture in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) try: self.binfiles[currentArch].append(fields[0].strip()) except: mesg=u'unable to understand file path in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s with architecture %(currentArch)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState),currentArch=repr(currentArch)) elif newState.startswith('cataloguedata-'): if self.cataloguedata is None: self.cataloguedata={} try: if len(newState)<=len('cataloguedata-'): raise tlError self.cataloguedata[newState[len('cataloguedata-'):]]=fields[1] except: mesg=u'expected cataloguedata-xxx value to have a sensible xxx and value in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; now in state %(newState)s reading line=%(line)s',returnCode=14,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) else: mesg=u'unknown key in line number=%(lineNo)s' noteException(mesg,devel=mesg+'; key is %(newState)s reading line=%(line)s',returnCode=13,log=self.log,debug=self.debug,lineNo=lineNo,line=repr(line),newState=repr(newState)) # get ready to read next line state=newState lineNo+=1 return True def is_arch_dependent(self): """Returns True if there are binfiles. """ if self.binfiles: return True else: return False def total_size(self,*archs): """Returns the sum of the sizes of srcfiles, docfiles, runfiles, and binfiles any architectures given. """ if self.srcsize is None: srcsize=0 else: srcsize=self.srcsize if self.docsize is None: docsize=0 else: docsize=self.docsize if self.runsize is None: runsize=0 else: runsize=self.runsize s=srcsize+docsize+runsize try: for arch in archs: s+=self.binsize[arch] except: mesg=u'unable to recognize an architecture' noteException(mesg,devel=mesg+': %(arch)s',exception=tlError,log=self.log,debug=self.debug,arch=repr(arch)) return s def _subtractList(self,l1,l2): """From l1 remove any elements of l2, as many times as they occur. """ for f in l2: while True: # more than one f in l1? try: l1.remove(f) except: break return l1 def add_srcfiles(self,fileList): self.srcfiles+=fileList def remove_srcfiles(self,fileList): self.srcfiles=self._subtractList(self.srcfiles,fileList) def add_docfiles(self,fileList): self.docfiles+=fileList def remove_docfiles(self,fileList): self.docfiles=self._subtractList(self.docfiles,fileList) def add_runfiles(self,fileList): self.runfiles+=fileList def remove_runfiles(self,fileList): self.runfiles=self._subtractList(self.runfiles,fileList) def add_binfiles(self,arch,fileList): self.binfiles[arch]+=fileList def remove_binfiles(self,arch,fileList): self.binfiles[arch]=self._subtractList(self.binfiles[arch],fileList) def add_files(self,t,fileList): if t=='src': self.add_srcfiles(fileList) elif t=='doc': self.add_docfiles(fileList) elif t=='run': self.add_runfiles(fileList) else: mesg=u'file type not known' noteException(mesg,devel=mesg+': %(t)s',exception=tlError,log=self.log,debug=self.debug,t=repr(t)) def remove_files(self,t,fileList): if t=='src': self.remove_srcfiles(fileList) elif t=='doc': self.remove_docfiles(fileList) elif t=='run': self.remove_runfiles(fileList) else: mesg=u'file type not known' noteException(mesg,devel=mesg+': %(t)s',exception=tlError,log=self.log,debug=self.debug,t=repr(t)) def list_files(self): """Return a string of lines with file names for all files """ r=[] if self.srcfiles: r+=self.srcfiles if self.runfiles: r+=self.runfiles if self.docfiles: r+=self.docfiles.keys() if self.binfiles: for arch in self.binfiles.keys(): r+=self.binfiles[arch] r.sort() return "\n".join(r) def write_file(self,fn=None): if fn: try: f=open(fn,'w') except Exception, err: mesg=u'unable to write because unable to open the file' noteException(mesg,devel=mesg+': %(err)s',exception=tlError,log=self.log,debug=self.debug,err=errorValue(err)) else: f=None self.writeout(f) f.close() return None def writeout(self,f=None): if f is None: f=sys.stdout pkgs=self.value.keys() pkgs.sort() for k in pkgs: v=self.value[k] f.write(v.stringout()) f.write("\n\n") return None class tlpobj_db(object): """Give information based on a TeXLive object file database """ def __init__(self,verbose=False,log=None,debug=False): """Initialize an object. """ self.verbose=verbose self.log=log self.debug=debug self.value={} # map name --> tlpkg return None def __unicode__(self): names=self.value.keys() names.sort() return u" ".join(names) def get_package(self,name): try: return self.value[name] except: return None def from_file(self,fn=None): """Read a tlpobj file. Set self.value. """ if fn is None: f=sys.stdin else: try: f=open(fn,'rU') except Exception, err: mesg='unable to open tlpobj file' noteException(mesg,devel=mesg+' %(fn)s: %(err)s',exception=tlError,log=log,debug=self.debug,fn=repr(fn),err=unicode(err)) r=tlpobj(log=self.log,debug=self.debug).from_fh(f,multi=True,lineNo=0) while r: self.value[r.name]=r r=tlpobj(log=self.log,debug=self.debug).from_fh(f,multi=True,lineNo=0) return None def write_file(self,fn=None): if fn is None: f=sys.stdout else: try: f=open(fn,'w') except Exception, err: mesg=u'unable to write because unable to open the file' noteException(mesg,devel=mesg+': %(err)s',exception=tlError,log=self.log,debug=self.debug,err=errorValue(err)) pkgNames=self.value.keys() pkgNames.sort() for k in pkgNames: v=self.value[k] v.writeout_fh(f) f.write("\n\n") f.close() return None ## def to_db(self,dBcsr,dBcnx): ## """Write all records to the database ## dBcsr,dBcnx database cursor and connection ## """ ## pkgNames=self.value.keys() ## pkgNames.sort() ## for k in pkgNames: ## ## if self.debug: ## ## print note("writing tds file information for %s" % (repr(k),),self.log) ## v=self.value[k] ## v.to_db(dBcsr,dBcnx) ## return None #...................................................................... def main(argv=None,log=None,debug=DEBUG): """The main logic if called from the command line argv The arguments to the routine log=None, debug=DEBUG Debugging stuff """ if argv is None: argv=sys.argv # Parse the arguments usage="""%prog: read a TeX Live dB obj file; typically write to the dB %prog [options]""" oP=optparse.OptionParser(usage=usage,version=__version__) oP.add_option('--output','-O',action='store_true',default=False,dest='output',help='show all package records') oP.add_option('--no-database','-n',action='store_true',default=False,dest='noDb',help='do not put all package records to the database') oP.add_option('--debug','-D',action='store_true',default=DEBUG,dest='debug',help='output debugging information') oP.add_option('--verbose','-V',action='store_true',default=VERBOSE,dest='verbose',help='talk a lot') opts, args=oP.parse_args(argv[1:]) # Handle the options # Handle positional arguments if args[1:]: tlpdbFilename=args[1] else: tlpdbFilename=TLPDB_FILENAME # Handle the various command line switches if opts.output: try: t=tlpobj_db(log=log,debug=opts.debug) t.from_file(tlpdbFilename) t.write_file() except Exception, err: mesg="trouble reading the object file and writing: "+errorValue(err) fail(mesg,returnCode=10,log=log,debug=opts.debug) return None if not(opts.noDb): print "dB turned off; you need Python module psycopg for access to PostGreSQL" ## try: ## (dBcnx,dBcsr)=dBUtils.opendB() ## except dBUtils.dBUtilsError, err: ## fail("Unable to connect to the database: %(err)s",returnCode=10,log=log,debug=opts.debug,err=errorValue(err)) ## try: ## t=tlpobj_db(log=log,debug=opts.debug) ## t.from_file(tlpdbFilename) ## t.to_db(dBcsr,dBcnx) ## except Exception, err: ## mesg="trouble reading the object file and sending the results to the database: "+errorValue(err) ## fail(mesg,returnCode=10,log=log,debug=opts.debug) return None # ............... script start ....................... if __name__=='__main__': log=None if LOGGING: log=openLog(LOGFILE_NAME,purpose=THIS_SCRIPT) if DEBUG and __notes__.strip(): note(__notes__,log,leader="NOTES FOR %s:" % (THIS_SCRIPT,),) try: main(argv=sys.argv,log=log,debug=DEBUG) except KeyboardInterrupt: mesg=u"Keyboard interrupt" fail(mesg,returnCode=1,log=log,debug=DEBUG) except StandardError, err: mesg=u"General programming error: "+errorValue(err) fail(mesg,returnCode=2,log=log,debug=DEBUG) except SystemExit, err: # fail() inside a subroutine pass sys.exit(0)