diff options
-rw-r--r-- | CHANGES | 6 | ||||
-rwxr-xr-x | bin/cheetah | 2 | ||||
-rw-r--r-- | bin/cheetah-compile | 6 | ||||
-rw-r--r-- | src/CheetahCompile.py | 247 | ||||
-rwxr-xr-x | src/CheetahWrapper.py | 336 |
5 files changed, 278 insertions, 319 deletions
@@ -13,6 +13,12 @@ release triple single quotes followed immediately by another single quote if the template def contained a '$placeholder' surrounded in single quotes and multiple \n newlines ... plus added new test case.(TR) + - Utils.optik: Optik 1.3 package by Gregory P Ward, for parsing + command-line options in 'cheetah' comamnd. (MO) + - rewrite of the "cheetah" and "cheetah-compile" commands. + The command-line options have changed! (MO) + - Utils.dualglob: new module to recursively generate source+destination + filenames from command-line filespecs. (MO) 0.9.14 (July 14, 2002) diff --git a/bin/cheetah b/bin/cheetah index 414aad6..26c391a 100755 --- a/bin/cheetah +++ b/bin/cheetah @@ -1,3 +1,3 @@ #!/usr/bin/env python from Cheetah.CheetahWrapper import CheetahWrapper -CheetahWrapper().run()
\ No newline at end of file +CheetahWrapper().main() diff --git a/bin/cheetah-compile b/bin/cheetah-compile index f558ea2..5d9ab60 100644 --- a/bin/cheetah-compile +++ b/bin/cheetah-compile @@ -1,3 +1,5 @@ #!/usr/bin/env python -from Cheetah.CheetahCompile import CheetahCompile -CheetahCompile().run()
\ No newline at end of file +import sys +from Cheetah.CheetahWrapper import CheetahWrapper +sys.argv.insert(1, "compile") +CheetahWrapper().main() diff --git a/src/CheetahCompile.py b/src/CheetahCompile.py deleted file mode 100644 index ff20b83..0000000 --- a/src/CheetahCompile.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/env python -# $Id: CheetahCompile.py,v 1.30 2002/04/15 06:22:53 tavis_rudd Exp $ -"""A command line compiler for turning Cheetah files (.tmpl) into Webware -servlet files (.py). - -Meta-Data -================================================================================ -Author: Tavis Rudd <tavis@calrudd.com> -Version: $Revision: 1.30 $ -Start Date: 2001/03/30 -Last Revision Date: $Date: 2002/04/15 06:22:53 $ -""" -__author__ = "Tavis Rudd <tavis@calrudd.com>" -__revision__ = "$Revision: 1.30 $"[11:-2] - -################################################## -## DEPENDENCIES - -import sys -import re -import os -import getopt -import os.path -import traceback -import imp - -from glob import glob -import shutil - -#intra-package imports ... -from _properties import Version -from Compiler import Compiler - - -################################################## -## GLOBALS & CONTANTS - -try: - True,False -except NameError: - True, False = (1==1),(1==0) - -class Error(Exception): - pass - - -class CheetahCompile: - """A command-line compiler for Cheetah .tmpl files.""" - - CHEETAH_EXTENSION = '.tmpl' - SERVLET_EXTENSION = '.py' - BACKUP_SUFFIX = '_bak' - GENERATED_EXT = '.html' - - RECURSIVE = False - MAKE_BACKUPS = True - VERBOSE = False - - WRITE_OUTPUT = False - PRINT_GENERATED = False - - def __init__(self, scriptName=os.path.basename(sys.argv[0]), - cmdLineArgs=sys.argv[1:]): - - self._scriptName = scriptName - self._cmdLineArgs = cmdLineArgs - - def run(self): - """The main program controller.""" - - self._processCmdLineArgs() - - if self._args == ["-"]: # piped input - print Compiler(sys.stdin.read()) - else: - self._buildFileList() - self._processFileList() - - def _processCmdLineArgs(self): - try: - self._opts, self._args = getopt.getopt( - self._cmdLineArgs, 'hpdRwv', ['help']) - - except getopt.GetoptError, v: - # print help information and exit: - print v - self.usage() - sys.exit(2) - - for o, a in self._opts: - if o in ('-h', '--help'): - self.usage() - sys.exit() - if o in ('-R',): - self.RECURSIVE = True - if o in ('-p',): - self.PRINT_GENERATED = True - if o in ('-w',): - self.WRITE_OUTPUT = True - if o in ('-v',): - self.VERBOSE = True - - - def _buildFileList(self): - if not self._args: - self._args = ["."] - - pending = self._args[:] - self._fileList = [] - addFile = self._fileList.append - - while pending: - entry = pending.pop() - if os.path.isdir(entry): - for fileOrDir in os.listdir(entry): - fileOrDir = os.path.join(entry, fileOrDir) - if os.path.isdir(fileOrDir): - if self.RECURSIVE: - pending.append(fileOrDir) - continue - if self._isTemplateFile(fileOrDir): - addFile(fileOrDir) - else: - addFile(entry) - - def _isTemplateFile(self, filename): - return os.path.splitext(filename)[1] == self.CHEETAH_EXTENSION - - def _processFileList(self): - self._compiledFiles = [] - for file in self._fileList: - self._processFile(file) # it appends to self._compiledFiles - - if self.WRITE_OUTPUT: - for fileName in self._compiledFiles: - self._generate(fileName) - - def _processFile(self, fileName): - print 'Compiling %s ' % (fileName) - - srcFile = fileName - fileNameMinusExt = os.path.splitext(fileName)[0] - className = os.path.split(fileNameMinusExt)[1] - pyCode = self._compileFile(srcFile, className) - - self._compiledFiles.append(fileNameMinusExt) - - if not self.PRINT_GENERATED or self.WRITE_OUTPUT: - outputModuleFilename = fileNameMinusExt + self.SERVLET_EXTENSION - - if self.MAKE_BACKUPS and os.path.exists(outputModuleFilename): - print ' Backing up %s before saving new version of %s' % ( - outputModuleFilename, srcFile ) - - shutil.copyfile(outputModuleFilename, - outputModuleFilename + self.BACKUP_SUFFIX) - - print ' Saving compiled version of %s -> %s' % ( - srcFile, outputModuleFilename) - - fp = open(outputModuleFilename,'w') - fp.write(pyCode) - fp.close() - - if self.PRINT_GENERATED: - print pyCode - - def _compileFile(self, srcFile, className): - """Compile an single Cheetah file. """ - - if not re.match(r'[a-zA-Z_][a-zA-Z_0-9]*$', className): - raise Error( - "The filename %s contains invalid characters. It must" \ - " be named according to the same rules as Python modules." - % srcFile) - pyCode = str(Compiler(file=srcFile, moduleName=className, - mainClassName=className)) - return pyCode - - - def _generate(self, fileNameMinusExt): - - """Writes the output of a cheetah template file to an .html file with - the same basename.""" - - oldSysPath = sys.path[:] - try: - outputFilename = fileNameMinusExt + self.GENERATED_EXT - dirname, basename = os.path.split(fileNameMinusExt) - ## @@IB: this sys.path is a hack - sys.path = [dirname] + sys.path - - print 'Writing the output of the template %s -> %s' % ( - fileNameMinusExt, outputFilename) - - fp, pathname, stuff = imp.find_module(basename, [dirname]) - mod = imp.load_module(basename, fp, pathname, stuff) - klass = getattr(mod, basename) - value = str(klass()) - except: - sys.stderr.write( - 'An error occurred while trying to import the compiled template %s\n' - % repr(fileNameMinusExt)) - traceback.print_exc(file=sys.stderr) - else: - if self.MAKE_BACKUPS and os.path.exists(outputFilename): - print ' Backing up %s before saving new version.' % (outputFilename,) - - shutil.copyfile(outputFilename, - outputFilename + self.BACKUP_SUFFIX) - - try: - fp = open(outputFilename, "w") - fp.write(value) - fp.close() - except: - sys.stderr.write('Exception raised while trying to write file %s\n' - % repr(fileNameMinusExt)) - traceback.print_exc(file=sys.stderr) - - sys.path = oldSysPath - - def usage(self): - print \ -"""Cheetah %(Version)s command-line compiler by %(author)s - -Compiles Cheetah files (.tmpl) into Webware servlet modules (.py) - -Usage: - %(scriptName)s [OPTIONS] FILES/DIRECTORIES - %(scriptName)s [OPTIONS] - (accept a file on stdin) - -R Recurse subdirectories - -p Print generated Python code to stdout - -w Write output of template files to *.html - -v Be verbose - -h | --help Print this help message -""" % {'scriptName':self._scriptName, - 'Version':Version, - 'author':'Tavis Rudd and Ian Bicking', - } - -################################################## -## if run from the command line -if __name__ == '__main__': - - CheetahCompile().run() - diff --git a/src/CheetahWrapper.py b/src/CheetahWrapper.py index 4a8e756..1e1af08 100755 --- a/src/CheetahWrapper.py +++ b/src/CheetahWrapper.py @@ -1,30 +1,30 @@ #!/usr/bin/env python -# $Id: CheetahWrapper.py,v 1.4 2002/04/15 06:22:53 tavis_rudd Exp $ -"""A command line interface to everything about Cheetah. +# $Id: CheetahWrapper.py,v 1.5 2002/09/02 21:46:46 hierro Exp $ +"""Cheetah command-line interface. + +BUGS: -p option not implemented. Meta-Data ================================================================================ Author: Tavis Rudd <tavis@calrudd.com> and Mike Orr <iron@mso.oz.net> -Version: $Revision: 1.4 $ +Version: $Revision: 1.5 $ Start Date: 2001/03/30 -Last Revision Date: $Date: 2002/04/15 06:22:53 $ +Last Revision Date: $Date: 2002/09/02 21:46:46 $ """ __author__ = "Tavis Rudd <tavis@calrudd.com> and Mike Orr <iron@mso.oz.net>" -__revision__ = "$Revision: 1.4 $"[11:-2] +__revision__ = "$Revision: 1.5 $"[11:-2] ################################################## ## DEPENDENCIES -import sys -import re -import os -import getopt -import os.path -from glob import glob -import shutil - +import getopt, glob, os, pprint, re, shutil, sys +import cPickle as pickle from _properties import Version +from Compiler import Compiler +from Template import Template +from Cheetah.Utils.dualglob import dualglob +from Cheetah.Utils.optik import OptionParser ################################################## ## GLOBALS & CONSTANTS @@ -34,47 +34,41 @@ try: except NameError: True, False = (1==1),(1==0) +optionDashesRx = re.compile( R"^-{1,2}" ) +moduleNameRx = re.compile( R"^[a-zA-Z_][a-zA-Z_0-9]*$" ) + class Error(Exception): pass +class MyOptionParser(OptionParser): + standard_option_list = [] # We use commands for Optik's standard options. -class CheetahWrapper: - """A command-line interface to everything about Cheetah.""" + def error(self, msg): + """Print our usage+error page.""" + usage(HELP_PAGE2, msg) + + def print_usage(self, file=None): + """Our usage+error page already has this.""" + pass - def __init__(self, scriptName=os.path.basename(sys.argv[0]), - cmdLineArgs=sys.argv[1:]): - self._scriptName = scriptName - self._cmdLineArgs = cmdLineArgs +################################################## +## USAGE FUNCTION & MESSAGES - def run(self): - """The main program controller.""" +def usage(usageMessage, errorMessage="", out=sys.stderr): + """Write help text, an optional error message, and abort the program. + """ + out.write(WRAPPER_TOP) + out.write(usageMessage) + exitStatus = 0 + if errorMessage: + out.write('\n') + out.write("*** USAGE ERROR ***: %s\n" % errorMessage) + exitStatus = 1 + sys.exit(exitStatus) + - if len(sys.argv) > 1 and sys.argv[1] in ("compile","-c"): - ## swap into compile mode then exit - from Cheetah.CheetahCompile import CheetahCompile - del sys.argv[1] - sys.argv[0] += ' compile' - CheetahCompile(sys.argv[0], sys.argv[1:]).run() - sys.exit() - elif len(sys.argv) > 1 and sys.argv[1] in ("test","-t"): - ## swap into unittesting mode then exit - from Cheetah.Tests import Test - import Cheetah.Tests.unittest_local_copy as unittest - del sys.argv[1] - sys.argv[0] += ' test' - unittest.main(testSuite=Test.testSuite) - sys.exit() - elif '-v' in sys.argv or '--version' in sys.argv: - print self.version() - else: - print self.usage() - - def version(self): - return Version - - def usage(self): - return """\ +WRAPPER_TOP = """\ __ ____________ __ \ \/ \/ / \/ * * \/ CHEETAH %(Version)s Command-Line Tool @@ -82,29 +76,233 @@ class CheetahWrapper: \ ==----== / by Tavis Rudd <tavis@calrudd.com> \__________/ and Mike Orr <iron@mso.oz.net> -USAGE ------ -%(script)s --help|-h - - Print this usage information -%(script)s compile|-c [compiler options] - - Run Cheetah's compiler ('%(script)s compile --help' for more) -%(script)s test|-t [test options] - - Run Cheetah's test suite ('%(script)s test --help' for more) -%(script)s --version|-v - - Print Cheetah's version number - - [Think you can draw a better ASCII-art cheetah face? Do it! - See http://www.cheetahtemplate.org/cheetah-face-black-medium.jpg - and send the result to iron@mso.oz.net. ] -""" % {'script':self._scriptName, - 'Version':Version, - 'author':__author__, - } - +""" % globals() + + +HELP_PAGE1 = """\ +USAGE: +------ + cheetah compile [options] [FILES ...] : Compile template definitions + cheetah fill [options] [FILES ...] : Fill template definitions + cheetah help : Print this help message + cheetah options : Print options help message + cheetah test : Run Cheetah's regression tests + cheetah version : Print Cheetah version number + +You may abbreviate the command to the first letter; e.g., 'h' == 'help'. +If FILES is a single "-", read standard input and write standard output. +Run "cheetah options" for the list of valid options. +""" + +HELP_PAGE2 = """\ +OPTIONS FOR "compile" AND "fill": +--------------------------------- + --idir DIR, --odir DIR : input/output directories (default: current dir) + --iext EXT, --oext EXT : input/output filename extensions + (default for compile: tmpl/py, fill: tmpl/html) + -R : recurse subdirectories looking for input files + -p : output to standard output (pipe) + --debug : print lots of diagnostic output to standard error + --env : put the environment in the searchList + --pickle FILE : unpickle FILE and put that object in the searchList + +Run "cheetah help" for the main help screen. +""" + ################################################## -## if run from the command line -if __name__ == '__main__': +## MAIN ROUTINE + +class CheetahWrapper: + + MAKE_BACKUPS = True + BACKUP_SUFFIX = "_bak" + + def __init__(self): + self.progName = None + self.command = None + self.opts = None + self.files = None + self.searchList = [] + + ################################################## + ## HELPER METHODS + + def backup(self, dst): + """Back up a file verbosely.""" + + if not os.path.exists(dst): + return None + backup = dst + self.BACKUP_SUFFIX + shutil.copyfile(dst, backup) + return backup + + + def parseOpts(self, args): + defaultOext = (self.command == 'c') and ".py" or ".html" + parser = MyOptionParser() + pao = parser.add_option + pao("--idir", action="store", dest="idir", default="") + pao("--odir", action="store", dest="odir", default="") + pao("--iext", action="store", dest="iext", default=".tmpl") + pao("--oext", action="store", dest="oext", default=defaultOext) + pao("-R", action="store_true", dest="recurse", default=0) + pao("-p", action="store_true", dest="stdout", default=0) + pao("--debug", action="store_true", dest="debug", default=0) + pao("--env", action="store_true", dest="env", default=0) + pao("--pickle", action="store_true", dest="pickle", default=0) + self.opts, self.files = opts, files = parser.parse_args(args) + self.isCompile = self.command[0] == 'c' + if opts.debug: + print >>sys.stderr, "cheetah compile", args + print >>sys.stderr, "Options are" + print >>sys.stderr, pprint.pformat(vars(opts)) + print >>sys.stderr, "Files are", files + if opts.env: + self.searchList.append(os.environ) + if opts.pickle: + f = open(opts.pickle, 'rb') + unpickled = pickle.load(f) + f.close() + self.searchList.append(unpickled) + - CheetahWrapper().run() + def compileOrFillStdin(self): + if self.isCompile: + output = Compiler(file=sys.stdin) + else: + output = Template(file=sys.stdin) + output = str(output) + sys.stdout.write(output) + + + def verifyBaseIsLegalModuleName(self, base, src): + """Compile an single Cheetah file. + + in : base, string, the base name to check. + src, string, the entire source path (for error message). + """ + + base = os.path.basename(base) + if not moduleNameRx.match(base): + raise Error( + "%s: base name %s contains invalid characters. It must" \ + " be named according to the same rules as Python modules." + % (base, src) ) + + + def compileOrFillPair(self, pair): + src = pair.src + dst = pair.dst + base = pair.base + what = self.isCompile and "Compiling" or "Filling" + print what, src, "->", dst, # No trailing newline. + try: + if self.isCompile: + self.verifyBaseIsLegalModuleName(base, src) + obj = Compiler(file=src, moduleName=base, mainClassName=base) + else: + obj = Template(file=src, searchList=self.searchList) + output = str(obj) + bak = self.backup(dst) + except: + print # Print pending newline before error message. + raise + if bak: + print "(backup %s)" % bak # On same line as previous message. + else: + print # Print pending newline. + f = open(dst, 'w') + f.write(output) + f.close() + + + def compileOrFill(self): + opts, files = self.opts, self.files + if files == ["-"]: + self.compileOrFillStdin() + return + elif not files and opts.recurse: + print "Drilling down recursively from current directory." + files = [os.curdir] + elif not files: + usage(HELP_PAGE1, "Neither files nor -R specified!") + pairs = dualglob(files, iext=opts.iext, oext=opts.oext, + idir=opts.idir, odir=opts.odir, recurse=opts.recurse, + addIextIfMissing=True, verbose=True, debug=opts.debug) + if opts.debug: + print >>sys.stderr, "I will operate on the following files:" + for pair in pairs: + print >>sys.stderr, " %s -> %s" % (pair.src, pair.dst) + for pair in pairs: + self.compileOrFillPair(pair) + + + ################################################## + ## COMMAND METHODS + + def compile(self): + self.compileOrFill() + + def fill(self): + self.compileOrFill() + + def help(self): + usage(HELP_PAGE1, "", sys.stdout) + + def options(self): + usage(HELP_PAGE2, "", sys.stdout) + + def test(self): + from Cheetah.Tests import Test + import Cheetah.Tests.unittest_local_copy as unittest + del sys.argv[1:] # Prevent unittest from misinterpreting options. + unittest.main(testSuite=Test.testSuite) + + def version(self): + print Version + + # If you add a command, also add it to the 'meths' variable in main(). + + ################################################## + ## MAIN ROUTINE + + def main(self, argv=None): + """The main program controller.""" + + if argv is None: + argv = sys.argv + + # Step 1: Determine the command and arguments. + try: + self.progName = progName = os.path.basename(argv[0]) + self.command = command = optionDashesRx.sub("", argv[1]) + self.parseOpts(argv[2:]) + #opts, files = self.opts, self.files + except IndexError: + usage(HELP_PAGE1, "not enough command-line arguments") + + # Step 2: Call the command + meths = (self.compile, self.fill, self.help, self.options, + self.test, self.version) + for meth in meths: + methName = meth.func_name + methInitial = methName[0] + if command in (methName, methInitial): + sys.argv[0] += (" " + methName) + # @@MO: I don't necessarily agree sys.argv[0] should be + # modified. + meth() + return + # If none of the commands matched. + usage(HELP_PAGE1, "unknown command '%s'" % command) + + run = main + + + + +################################################## +## if run from the command line +if __name__ == '__main__': CheetahWrapper().main() -# vim: expandtab +# vim: shiftwidth=4 tabstop=4 expandtab |