Vielleicht optimiert sie jemand, damit das ganze ein bisschen schneller wird.
Code: Alles auswählen
# -*- coding: UTF-8 -*-
"""PlowPlate -- nestable HTML template class
Version 0.3, 24-Apr-2001, by Mike Orr <iron@mso.oz.net>
License: your choice of GPL, Python or Webware. If you modify the API and make
your changes public, please rename the module to prevent confusion.
See the README file for usage examples.
"""
import cStringIO, UserDict, getopt, os, pprint, re, sys
try:
import NewBuiltins
except ImportError:
True, False = (1==1), (1==0)
def irange(lis):
ret = []
for i in range(len(lis)):
tup = i, lis[i]
ret.append(tup)
return ret
############################################################################
# XXX These functions should be in os.path--submit bug to Python.
def find(file, path, exceptional=False):
"""Find a file in a search path.
in : file, string, the filename,
path, list of strings, the directories to look in.
exceptional, boolean, true to raise IOError if file not found.
False to return None in this case.
out: string, the first file found (directory + filename).
exc: IOError if file not found and 'exceptional' is true.
"""
hits = find_all(file, path)
if len(hits) >= 1:
return hits[0]
elif exceptional:
raise IOError("file %s not found in path %s" % (`file`, `path`) )
else:
return None
def find_all(file, path):
"""Find all occurrances of a file in a search path.
in : file, string, the filename,
path, list of strings, the directories to look in.
out: list of strings, all files found (directory + filename). Returns
the empty list if none found.
"""
ret = []
for dir in path:
p = os.path.join(dir, file)
if os.path.exists(p):
ret.append(p)
return ret
############################################################################
def _sln(s):
"""Strip one leading newline off a string. Returns the string unchanged
if there's no leading newline.
"""
if s[0:1] == '\n':
s = s[1:]
return s
PLACEHOLDER_PAT = r"%s([\w-]*?)%s"
BLOCK_PAT = r"(?is)^(?P<before>.*)<!-- BEGIN %s -->(?P<block>.*?)<!-- END %s -->(?P<after>.*)$"
# "(?is)" means dotall and ignorecase.
class PlowPlate(UserDict.UserDict):
"""See the README file for usage examples.
Class attributes:
_left, string, the opening delimeter of a placeholder key.
_right, string, the closing delimeter of a placeholder key.
_path, list of strings, directories to search in for file templates.
Instance attributes:
_template, string, the template.
_components, list of strings, The components of the template. Each even
subscript (starting with 0) is a literal string to be output. Each
odd subscript (starting with 1) is a placeholder key to be
substituted in the output.
_parsedTemplate, string, the template as it was when '_components' was
built. Used to verify whether '_template' has changed since
'_components' was last built. Default is None so that it will never
match '_template' the first time around.
_rows, list of strings, the result of each call to row(). Used only
inside for-blocks.
Rules:
** Any method that uses '_components' must call parseTemplate() first
to ensure '_components' has kept up with any changes to '_template'.
"""
_left = "{"
_right = "}"
_path = [""]
def __init__(self, template, isFile=True, dic={}):
"""Constructor.
in : template, string, the template filename or the template itself.
isFile, boolean, true if 'template' is a filename, false if it's
a literal string.
dic, dictionary, initial plug-in values.
out: None.
"""
UserDict.UserDict.__init__(self, dic)
if isFile:
self.file(template)
else:
self._template = template
self._components = None
self._parsedTemplate = None
self._rows = []
def file(self, file):
"""Read the template from a file.
in : file, string, the filename.
out: None.
act: Replaces the previous template.
exc: IOError if the file is not found or cannot be read.
"""
fullpath = find(file, self._path, 1) # Raises IOError.
f = open(fullpath) # Raises IOError.
self._template = f.read()
f.close()
def __str__(self):
"""Performs the substitution.
"""
if self._rows:
return "".join(self._rows)
else:
return self.sub()
def parseTemplate(self, force=False):
"""Private method to (re)build '_components' from '_template'.
in : force, boolean, true to perform the work even if it looks like
it's already been done.
out: None.
"""
if (self._template is self._parsedTemplate) and not force:
return
components = []
left = re.escape(self._left)
right = re.escape(self._right)
pat = PLACEHOLDER_PAT % (left, right)
rx = re.compile(pat)
template = self._template
pos = 0
while 1:
match = rx.search(template, pos)
if match is None:
components.append(template[pos:])
break
else:
s = template[pos:match.start(0)]
components.append(s)
components.append( match.group(1) )
pos = match.end(0)
self._components = components
self._parsedTemplate = template
def clearRows(self):
"""Throws away any rows that have been generated by row(). Call this
if you want to reuse the same template with a different set of rows,
or to use it again as an ordinary template after row() has been
called. This method is useful only in objects that are for-blocks.
"""
self._rows = []
def findBlock(self, blockName):
"""Finds a block in the template (without modifying the template).
in : blockName, string, the name of the block to search for.
out: tuple:
[0], string, the part of the template before the opening
block delimeter.
[1], string, the block body.
[2], string, the part of the template after the closing block
delimeter.
note: The return value does not include the block delimeters or the
block name.
exc: ValueError, if the block is not found.
"""
pat = BLOCK_PAT % (blockName, blockName)
rx = re.compile(pat)
match = rx.search(self._template)
if match is None:
raise ValueError("blockName '%s' not found" % block)
before = match.group('before')
block = _sln( match.group('block') )
after = _sln( match.group('after') )
return before, block, after
def block(self, blockName):
"""Extracts a block into a separate PlowPlate instance.
in : blockName, string, the name of the block.
out: PlowPlate, an instance whose template is the block body.
act: Replaces the block in the template with a placeholder key to it.
Adds a plug-in of blockName : the new PlowPlate instance.
Invalidates '_parsedTemplate' so that '_components' will be
rebuilt the next time it is used. See the README file if you
don't understand why all this happens.
"""
before, block, after = self.findBlock(blockName)
sio = cStringIO.StringIO()
sio.write(before)
sio.write(self._left)
sio.write(blockName)
sio.write(self._right)
sio.write(after)
self._template = sio.getvalue()
self._parsedTemplate = None
sio.close()
o = PlowPlate(block, False)
self[blockName] = o
return o
def keep(self, blockName, keep=True):
"""Handles an if-block in an optimistic manner.
in : blockName, string, name of the block to find.
keep, boolean, true to modify the template to remove the block
markers but keep the block body. False to remove the block
body also.
out: None.
note: See the README file if you don't understand what this is for.
"""
before, block, after = self.findBlock(blockName)
tup = keep and (before, block, after) or (before, after)
self._template = "".join(tup)
def kill(self, blockName, kill=True):
"""Handle an if-block in a pessemistic manner.
Same as keep() but reverses the sense of the boolean expression.
"""
self.keep(blockName, not kill)
def distribution(self):
"""Shows which placeholders occur the most frequently in the template.
in : none.
out: dictionary, placeholder key : number of occurrences.
note: This method is just an extra and is not needed for ordinary
template use.
"""
self.parseTemplate()
components = self._components
dic = {}
for i in range(1, len(components), 2):
key = components[i]
if dic.has_key(key):
dic[key] = dic[key] + 1
# "dic[key] += 1" raises syntax error. :(
else:
dic[key] = 1
return dic
def placeholders(self):
"""Returns a list of all the unique placeholder keys in the template.
in : none.
out: list of strings, the placeholder keys.
note: This method is useful for debugging your template, but is not
needed for ordinary template use.
"""
distrib = self.distribution()
ret = distrib.keys()
#ret.sort()
return ret
def unknowns(self):
"""Returns a list of all the unknown placeholder keys in the template.
in : none.
out: list of strings, the placeholder keys which do not have a
corresponding key in the plug-in dictionary.
note: This method is for debugging your template and is not needed
for ordinary template use.
"""
ret = []
for key in self.placeholders():
if self.has_key(key):
ret.append(key)
return ret
def sub(self):
"""Perform the substitutions and return the result (without modifying
anything).
in : none.
out: string, the result of performing the substitions on the
template.
"""
self.parseTemplate()
sio = cStringIO.StringIO()
for i, component in irange(self._components):
if i % 2 == 0: # If subscript is even
subst = component
else:
subst = self.valueForKey(component)
subst = str(subst)
sio.write(subst)
ret = sio.getvalue()
sio.close()
return ret
def valueForKey(self, key):
"""Returns the plug-in value mapped to the given key. If the key is
not registered, calls valueForUnknownKey and returns the result.
"""
if self.has_key(key):
return self[key]
else:
return self.valueForUnknownKey(key)
def valueForUnknownKey(self, key):
"""Returns a default substitution for a key which (presumably) does not
have an associated plug-in value. This implementation returns the
key with its surrounding placeholders re-added. When placed in the
output stream, this recreates the placeholder as it was in the
template.
Possible overrides you may want to implement:
** Return the empty string to squeeze out unknown placeholders.
** Return an HTML comment or error message to insert that into he
output stream.
** Raise KeyError to halt the program dead in its tracks.
"""
return self._left + key + self._right
def row(self):
"""Performs a substitution that is one row in a for-block.
in : none.
out: None.
act: Appends the result to '_rows'. Later when you call __str__(),
all the generated rows will be returned.
note: See the README file for usage examples. This method is useful
only in instances which are for-block bodies.
"""
s = self.sub()
self._rows.append(s)
############### MAIN ROUTINE #########################################
def main():
"""See usage string at bottom of function."""
class UsageError(Exception):
pass
err_term = "Cowardly refusing to read standard input from a terminal."
err_argc = "Wrong number of command-line arguments."
try:
print_placeholders_only = False
opts, args = getopt.getopt(sys.argv[1:], 'kp')
for o, a in opts:
if o in ("-k", "-p"):
print_placeholders_only = True
args_len = len(args)
if args_len == 0:
if sys.stdin.isatty():
raise UsageError(err_term)
template = sys.stdin.read()
p = Plow(template, False)
elif args_len == 1:
file = args[0]
p = PlowPlate(file)
else:
raise UsageError(err_argc)
if print_placeholders_only:
for ph in p.placeholders():
print ph
else:
report = p.distribution()
pprint.pprint(report)
except (UsageError, getopt.error), reason:
if reason:
sys.stderr.write("%s\n" % reason)
progname = sys.argv[0]
sys.stderr.write("""\
Usage: %s [-kp] filename
With -k or -p: prints the unique placeholders (=keys), one per line.
Without -k or -p: prints a dictionary of how many times each placeholder occurs.
'filename' is the file containing the template.
Placeholders inside blocks are recognized, but the blocks themselves are not.
""" % progname)
sys.exit(1)
if __name__ == "__main__": main()