quixote ptl_compile.py,1.24,1.25
Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]>
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /home/cvs/quixote
In directory hewson:/tmp/cvs-serv20428
Modified Files:
ptl_compile.py
Log Message:
Add support for HTML templates. The new syntax for templates is:
def foo [html] (...)
or
def foo [plain] (...)
If the former is used then all string literals are converted to htmltext
instances. 'template foo(...)' is still supported. Also, for
convenience, the htmltext constructor is placed into the global
namespace of PTL modules.
Index: ptl_compile.py
===================================================================
RCS file: /home/cvs/quixote/ptl_compile.py,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -d -r1.24 -r1.25
--- ptl_compile.py 10 Oct 2002 13:47:41 -0000 1.24
+++ ptl_compile.py 14 Oct 2002 23:06:39 -0000 1.25
@@ -12,8 +12,6 @@
Note that script/module requires the compiler package.
"""
-# created 2000/07/28, nas
-
__revision__ = "$Id$"
import sys
@@ -23,6 +21,9 @@
import token
import parser
import re
+import types
+
+assert sys.hexversion >= 0x20000b1, 'PTL requires Python 2.0 or newer'
from compiler import pycodegen, visitor, transformer, walk
from compiler import ast
@@ -30,38 +31,42 @@
if sys.hexversion >= 0x20200b1:
from compiler import misc, syntax
-# magic names inserted into the code
-# try to figure out what package we are in
+# magic names inserted into the code
IO_MODULE = "quixote.TemplateIO"
IO_CLASS = "TemplateIO"
-IO_INSTANCE = "__output"
-TEMPLATE_PREFIX = "__template__"
+IO_INSTANCE = "_q_output"
+HTML_TEMPLATE_PREFIX = "_q_html_template_"
+PLAIN_TEMPLATE_PREFIX = "_q_plain_template_"
+TEMPLATE_PREFIX = "_q_template_"
+MARKUP_MODULE = "quixote.html"
+MARKUP_CLASS = "htmltext"
+MARKUP_MANGLED_CLASS = "_q_htmltext"
class TemplateTransformer(transformer.Transformer):
def __init__ (self, *args, **kwargs):
transformer.Transformer.__init__(self, *args, **kwargs)
- self.__in_template = [] # stack, 1 if in template else 0
+ self.__template_type = [] # stack, "html", "plain" or None
def file_input(self, nodelist):
# Add a "from IO_MODULE import IO_CLASS" statement to the
# beginning of the module.
doc = None # self.get_docstring(nodelist, symbol.file_input)
- if sys.hexversion >= 0x20000b1:
- # imports work differently since Python 2.0b1
- imp = ast.From(IO_MODULE, [(IO_CLASS, None)])
- else:
- imp = ast.From(IO_MODULE, [IO_CLASS])
+
+ io_imp = ast.From(IO_MODULE, [(IO_CLASS, None)])
+ markup_imp = ast.From(MARKUP_MODULE, [(MARKUP_CLASS, None)])
+ markup_assign = ast.Assign([ast.AssName(MARKUP_MANGLED_CLASS,
+ OP_ASSIGN)],
+ ast.Name(MARKUP_CLASS))
# Add an IO_INSTANCE binding for module level expressions (like
# doc strings). This instance will not be returned.
- klass = ast.Name(IO_CLASS)
- instance = ast.CallFunc(klass, [])
- assign_name = ast.AssName(IO_INSTANCE, OP_ASSIGN)
- assign = ast.Assign([assign_name], instance)
+ io_instance = ast.CallFunc(ast.Name(IO_CLASS), [])
+ io_assign_name = ast.AssName(IO_INSTANCE, OP_ASSIGN)
+ io_assign = ast.Assign([io_assign_name], io_instance)
- stmts = [ imp, assign ]
+ stmts = [ io_imp, io_assign, markup_imp, markup_assign ]
for node in nodelist:
if node[0] != token.ENDMARKER and node[0] != token.NEWLINE:
@@ -74,15 +79,25 @@
name = nodelist[1][1]
args = nodelist[2][2]
- if not name.startswith(TEMPLATE_PREFIX):
+ if not re.match('_q_((html|plain)_)?template_', name):
# just a normal function, let base class handle it
- self.__in_template.append(0)
+ self.__template_type.append(None)
n = transformer.Transformer.funcdef(self, nodelist)
else:
- self.__in_template.append(1)
+ if name.startswith(PLAIN_TEMPLATE_PREFIX):
+ name = name[len(PLAIN_TEMPLATE_PREFIX):]
+ template_type = "plain"
+ elif name.startswith(HTML_TEMPLATE_PREFIX):
+ name = name[len(HTML_TEMPLATE_PREFIX):]
+ template_type = "html"
+ elif name.startswith(TEMPLATE_PREFIX):
+ name = name[len(TEMPLATE_PREFIX):]
+ template_type = "plain"
+ else:
+ raise RuntimeError, 'unknown prefix on %s' % name
- name = name[len(TEMPLATE_PREFIX):]
+ self.__template_type.append(template_type)
# Add "IO_INSTANCE = IO_CLASS()" statement at the beginning of
# the function and a "return IO_INSTANCE" at the end.
@@ -102,9 +117,10 @@
assign_name = ast.AssName(IO_INSTANCE, OP_ASSIGN)
assign = ast.Assign([assign_name], instance)
- # return the str() of the instance
- func = ast.Getattr(ast.Name(IO_INSTANCE), "__str__")
- ret = ast.Return(ast.CallFunc(func, []))
+ # return the IO_INSTANCE.getvalue(...)
+ func = ast.Getattr(ast.Name(IO_INSTANCE), "getvalue")
+ args = [ast.Const(template_type)]
+ ret = ast.Return(ast.CallFunc(func, args))
# wrap original function code
code = ast.Stmt([assign, code, ret])
@@ -112,11 +128,11 @@
n = ast.Function(name, names, defaults, flags, doc, code)
n.lineno = lineno
- self.__in_template.pop()
+ self.__template_type.pop()
return n
def expr_stmt(self, nodelist):
- if not self.__in_template or not self.__in_template[-1]:
+ if not self.__template_type or not self.__template_type[-1]:
return transformer.Transformer.expr_stmt(self, nodelist)
# Instead of discarding objects on the stack, call
@@ -140,10 +156,26 @@
n.lineno = op[2]
return n
+ def atom_string(self, nodelist):
+ k = ''
+ for node in nodelist:
+ k = k + eval(node[1])
+ n = ast.Const(k)
+ if self.__template_type and self.__template_type[-1] == "html":
+ # change "foo" to _q_htmltext("foo")
+ n = ast.CallFunc(ast.Name(MARKUP_MANGLED_CLASS), [n])
+ return n
-_template_re = re.compile(r"^([ \t]*) template ([ \t]+)"
- r" ([a-zA-Z_][a-zA-Z_0-9]*)" # name of template
- r" ([ \t]*[\(\\])",
+
+_old_template_re = re.compile(r"^([ \t]*) template ([ \t]+)"
+ r" ([a-zA-Z_][a-zA-Z_0-9]*)" # name of template
+ r" ([ \t]*[\(\\])",
+ re.MULTILINE|re.VERBOSE)
+
+_template_re = re.compile(r"^([ \t]*) def (?:[ \t]+)" # def
+ r" ([a-zA-Z_][a-zA-Z_0-9]*)" # <name>
+ r" (?:[ \t]*) \[(plain|html)\] (?:[ \t]*)" # <type>
+ r" (?:[ \t]*[\(\\])", # (
re.MULTILINE|re.VERBOSE)
def translate_tokens(buf, filename):
@@ -152,15 +184,22 @@
must do token translation here. Luckily it does not affect line
numbers.
- template foo(...): -> def __template__foo(...):
+ template foo(...): -> def _q_template__foo(...):
+
+ def foo [plain] (...): -> def _q_plain_template__foo(...):
+
+ def foo [html] (...): -> def _q_html_template__foo(...):
XXX This parser is too stupid. For example, it doesn't understand
triple quoted strings.
"""
- global _def_re, _template_re
+ global _template_re
- # change template to def
- buf = _template_re.sub(r"\1def\2%s\3\4" % TEMPLATE_PREFIX, buf)
+ # handle new style template declarations
+ buf = _template_re.sub(r"\1def _q_\3_template_\2(", buf)
+
+ # change old style template to def
+ buf = _old_template_re.sub(r"\1def\2%s\3\4" % TEMPLATE_PREFIX, buf)
# compile() and parsermodule don't accept certain modules if they are
# missing a trailing newline. The Python interpreter seems to add a
@@ -193,14 +232,13 @@
PTL_EXT = ".ptl"
PTLC_EXT = ".ptlc"
if sys.hexversion >= 0x20200b1:
- PTLC_MAGIC = "PTLC\000\006"
+ PTLC_MAGIC = "PTLC\x00\x09"
elif sys.hexversion >= 0x20100b1:
- PTLC_MAGIC = "PTLC\000\005"
+ PTLC_MAGIC = "PTLC\x00\x08"
elif sys.hexversion >= 0x20000b1:
- PTLC_MAGIC = "PTLC\000\004"
+ PTLC_MAGIC = "PTLC\x00\x07"
else:
- PTLC_MAGIC = "PTLC\000\003"
-
+ raise RuntimeError, 'python too old'
class Template(pycodegen.Module):