summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rwxr-xr-xbin/cheetah2
-rw-r--r--bin/cheetah-compile6
-rw-r--r--src/CheetahCompile.py247
-rwxr-xr-xsrc/CheetahWrapper.py336
5 files changed, 278 insertions, 319 deletions
diff --git a/CHANGES b/CHANGES
index 2bf8895..e54229b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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