Add files via upload
This commit is contained in:
parent
e2274b430b
commit
fa2d68ed26
170
yaptu.py
Normal file
170
yaptu.py
Normal file
@ -0,0 +1,170 @@
|
||||
"""Yet Another Python Templating Utility, Version 1.2, by Alex Martelli.
|
||||
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52305
|
||||
(Specialized to HTML and modified by Peter Norvig.)
|
||||
|
||||
Copies input to output, with some substitutions. There are three types
|
||||
of substitutions: lexical, expression, and statement.
|
||||
|
||||
LEXICAL SUBSTITUTIONS:
|
||||
|
||||
& < >
|
||||
These characters, if surrounded by whitespace, are replaced by
|
||||
the corresonding HTML entities: &, <, >.
|
||||
|
||||
EXPRESSION SUBSTITUTIONS:
|
||||
|
||||
<<exp>>
|
||||
Replace <<exp>> by eval(exp), where exp is a Python expression.
|
||||
The most common use is when exp is just a variable name.
|
||||
Example: <<green>>
|
||||
Special case 1: If exp starts with '/', replace '/' by '_'.
|
||||
Example: <</green>> becomes <<_green>
|
||||
Special case 2: If exp evals to a callable, call it.
|
||||
Example: <<random.random>> is the same as <<random.random()>>
|
||||
Special case 3: If exp evals to None, replace it with ''.
|
||||
Example: <<list.append(item)>> generates no text.
|
||||
|
||||
STATEMENT SUBSTITUTIONS:
|
||||
|
||||
All statement substitutions start with a #[ in column 1, and end with
|
||||
a #] in column 1 of a subsequent line. Nesting is allowed, and
|
||||
works like you would expect. There are two variants:
|
||||
|
||||
#[
|
||||
stmts
|
||||
#]
|
||||
Any number of lines of Python stmts are executed.
|
||||
The first line must be empty, except for the #[
|
||||
|
||||
#[ stmt-header:
|
||||
lines
|
||||
#]
|
||||
The lines are interpreted as HTML with embedded expressions,
|
||||
and are sent to output, once for each execution of stmt-header.
|
||||
stmt-header is usually a for or if; This is hard to explain,
|
||||
but easy to see with an example:
|
||||
|
||||
<table><tr><th> Number <th> Number squared
|
||||
#[ for i in range(10):
|
||||
<tr><td> <<i>> <td> <<i**2>>
|
||||
#]
|
||||
</table>
|
||||
|
||||
This produces one line of the table for each value of i in [0 .. 9].
|
||||
If your compound statement has multiple stmt-headers, you use #| to
|
||||
introduce the subsequent stmt-headers (such as else: or except:).
|
||||
Another example:
|
||||
|
||||
#[ if time.localtime()[6] in [5, 6]:
|
||||
Have a good weekend!
|
||||
#| else:
|
||||
Time for work.
|
||||
#]
|
||||
"""
|
||||
|
||||
import sys, re, os, os.path
|
||||
|
||||
class Copier:
|
||||
"Smart-copier (YAPTU) class"
|
||||
|
||||
def copyblock(self, i=0, last=None):
|
||||
"Main copy method: process lines [i,last) of block"
|
||||
|
||||
def repl(match, self=self):
|
||||
"Replace the match with its value as a Python expression."
|
||||
expr = self.preproc(match.group(1), 'eval')
|
||||
if self.verbose: print '=== eval{%s}' % expr,
|
||||
try:
|
||||
val = eval(expr, self.globals)
|
||||
except:
|
||||
self.oops('eval', expr)
|
||||
if callable(val): val = val()
|
||||
if val == None: val = ''
|
||||
if self.verbose: print '========>', val
|
||||
return str(val)
|
||||
|
||||
block = self.globals['_bl']
|
||||
if last is None: last = len(block)
|
||||
while i < last:
|
||||
line = block[i]
|
||||
if line.startswith("#["): # a statement starts at line block[i]
|
||||
# i is the last line to _not_ process
|
||||
stmt = line[2:].strip()
|
||||
j = i+1 # look for 'finish' from here onwards
|
||||
nest = 1 # count nesting levels of statements
|
||||
while j<last and not stmt.endswith("#]"):
|
||||
line = block[j]
|
||||
# first look for nested statements or 'finish' lines
|
||||
if line.startswith("#]"): # found a statement-end
|
||||
nest = nest - 1
|
||||
if nest == 0: break # j is first line to _not_ process
|
||||
elif line.startswith("#["): # found a nested statement
|
||||
nest = nest + 1
|
||||
elif nest == 1 and line.startswith("#|"):
|
||||
# look for continuation only at this nesting
|
||||
nestat = line[2:].strip()
|
||||
stmt = '%s _cb(%s,%s)\n%s' % (stmt,i+1,j,nestat)
|
||||
i=j # again, i is the last line to _not_ process
|
||||
j = j+1
|
||||
if stmt == '': ## A multi-line python suite
|
||||
self.execute(''.join(block[i+1:j]))
|
||||
i = j+1
|
||||
else: ## The header of a for loop (etc.) is on this line
|
||||
self.execute("%s _cb(%s,%s)" % (stmt,i+1,j))
|
||||
i = j+1
|
||||
else: # normal line, just copy with substitution
|
||||
self.outf.write(self.regex.sub(repl,self.preproc(line,'copy')))
|
||||
i = i+1
|
||||
|
||||
def __init__(self, globals):
|
||||
"Create a Copier."
|
||||
self.regex = re.compile("<<(.*?)>>")
|
||||
self.globals = globals
|
||||
self.globals['_cb'] = self.copyblock
|
||||
self.outf = sys.stdout
|
||||
self.verbose = 0
|
||||
|
||||
def execute(self, stmt):
|
||||
stmt = self.preproc(stmt, 'exec') + '\n'
|
||||
if self.verbose:
|
||||
print "******* executing {%s} in %s" % (stmt, self.globals.keys())
|
||||
try:
|
||||
exec stmt in self.globals
|
||||
except:
|
||||
self.oops('exec', stmt)
|
||||
|
||||
def oops(self, why, what):
|
||||
print 'Something went wrong in %sing {%s}' % (why, what)
|
||||
print 'Globals:', self.globals.keys(), \
|
||||
self.globals.get('SECTIONS', '???')
|
||||
raise
|
||||
|
||||
def preproc(self, string, why, reg=re.compile(r"\s([<>&])\s"),
|
||||
table={'&':' & ', '<':' < ', '>':' > '}):
|
||||
# If it starts with '/', change to '_'
|
||||
if why in ('exec', 'eval'):
|
||||
string = string.strip()
|
||||
if string[0] == '/':
|
||||
string = '_' + string[1:]
|
||||
return string
|
||||
elif why == 'copy':
|
||||
# Expand & < > into entitites if surrounded by whitespace
|
||||
return reg.sub(lambda match: table[match.group(1)], string)
|
||||
|
||||
def copyfile(self, filename, ext="html"):
|
||||
"Convert filename.* to filename.ext, where ext defaults to html."
|
||||
global yaptu_filename
|
||||
outname = re.sub('[.][a-zA-Z0-9]+?$', '', filename) + '.'+ext
|
||||
print 'Transforming', filename, 'to', outname
|
||||
self.globals['_bl'] = file(filename).readlines()
|
||||
yaptu_filename = filename
|
||||
self.outf = file(outname, 'w')
|
||||
self.copyblock()
|
||||
|
||||
if __name__ == '__main__':
|
||||
copier = Copier(globals())
|
||||
for filename in sys.argv[1:]:
|
||||
if filename == '-v':
|
||||
copier.verbose = 1
|
||||
else:
|
||||
copier.copyfile(filename)
|
Loading…
Reference in New Issue
Block a user