Some more refinements:
* [include] now uses the current template's directory as a base for including other templates. Thus, you can [include "header.ezt"] to refer to a header.ezt template in the same directory. * _cmd_print() now looks for a "read" attribute on the value, to determine whether it is a stream (rather than a simple string). It can now copy a stream to the output. * added a simple catch for unclosed blocks. It doesn't say *what* is unclosed, but it will at least give you an error about it, rather than quiet failure. * [for] loops now iterate until the end of a list (determined internally by the interpreter when an IndexError occurs), rather than using len(list). This allows for lists of an indeterminate length to be used. git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@392 8cb11bc2-c004-0410-86c3-e597b4017df7remotes/tags/V0_9
parent
a2b579b5ac
commit
62f37986d5
44
lib/ezt.py
44
lib/ezt.py
|
@ -147,6 +147,7 @@ Directives
|
||||||
import string
|
import string
|
||||||
import re
|
import re
|
||||||
from types import StringType, IntType, FloatType
|
from types import StringType, IntType, FloatType
|
||||||
|
import os
|
||||||
|
|
||||||
#
|
#
|
||||||
# This regular expression matches three alternatives:
|
# This regular expression matches three alternatives:
|
||||||
|
@ -205,9 +206,10 @@ class Template:
|
||||||
self._execute(self.program, fp, ctx)
|
self._execute(self.program, fp, ctx)
|
||||||
|
|
||||||
def _parse_file(self, fname, for_names=None):
|
def _parse_file(self, fname, for_names=None):
|
||||||
return self._parse(open(fname, "rt").read(), for_names)
|
return self._parse(open(fname, "rt").read(), for_names,
|
||||||
|
os.path.dirname(fname))
|
||||||
|
|
||||||
def _parse(self, text, for_names=None):
|
def _parse(self, text, for_names=None, base=None):
|
||||||
"""text -> string object containing the HTML template.
|
"""text -> string object containing the HTML template.
|
||||||
|
|
||||||
This is a private helper function doing the real work for method parse.
|
This is a private helper function doing the real work for method parse.
|
||||||
|
@ -277,15 +279,22 @@ class Template:
|
||||||
raise ArgCountSyntaxError()
|
raise ArgCountSyntaxError()
|
||||||
if args[1][0] == '"':
|
if args[1][0] == '"':
|
||||||
include_filename = args[1][1:-1]
|
include_filename = args[1][1:-1]
|
||||||
|
if base:
|
||||||
|
include_filename = os.path.join(base, include_filename)
|
||||||
program.extend(self._parse_file(include_filename, for_names))
|
program.extend(self._parse_file(include_filename, for_names))
|
||||||
else:
|
else:
|
||||||
program.append((self._cmd_include, _prepare_ref(args[1], for_names)))
|
program.append((self._cmd_include,
|
||||||
|
(_prepare_ref(args[1], for_names),
|
||||||
|
base)))
|
||||||
else:
|
else:
|
||||||
# implied PRINT command
|
# implied PRINT command
|
||||||
if len(args) > 1:
|
if len(args) > 1:
|
||||||
raise ArgCountSyntaxError()
|
raise ArgCountSyntaxError()
|
||||||
program.append((self._cmd_print, _prepare_ref(args[0], for_names)))
|
program.append((self._cmd_print, _prepare_ref(args[0], for_names)))
|
||||||
|
|
||||||
|
if stack:
|
||||||
|
### would be nice to say which blocks...
|
||||||
|
raise UnclosedBlocksError()
|
||||||
return program
|
return program
|
||||||
|
|
||||||
def _execute(self, program, fp, ctx):
|
def _execute(self, program, fp, ctx):
|
||||||
|
@ -300,11 +309,23 @@ class Template:
|
||||||
step[0](step[1], fp, ctx)
|
step[0](step[1], fp, ctx)
|
||||||
|
|
||||||
def _cmd_print(self, valref, fp, ctx):
|
def _cmd_print(self, valref, fp, ctx):
|
||||||
### type check the value
|
value = _get_value(valref, ctx)
|
||||||
fp.write(_get_value(valref, ctx))
|
|
||||||
|
|
||||||
def _cmd_include(self, valref, fp, ctx):
|
# if the value has a 'read' attribute, then it is a stream: copy it
|
||||||
self._execute(self._parse_file(_get_value(valref, ctx)), fp, ctx)
|
if hasattr(value, 'read'):
|
||||||
|
while 1:
|
||||||
|
chunk = value.read(16384)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
fp.write(chunk)
|
||||||
|
else:
|
||||||
|
fp.write(value)
|
||||||
|
|
||||||
|
def _cmd_include(self, (valref, base), fp, ctx):
|
||||||
|
fname = _get_value(valref, ctx)
|
||||||
|
if base:
|
||||||
|
fname = os.path.join(base, fname)
|
||||||
|
self._execute(self._parse_file(fname), fp, ctx)
|
||||||
|
|
||||||
def _cmd_if_any(self, args, fp, ctx):
|
def _cmd_if_any(self, args, fp, ctx):
|
||||||
"If the value is a non-empty string or non-empty list, then T else F."
|
"If the value is a non-empty string or non-empty list, then T else F."
|
||||||
|
@ -350,10 +371,10 @@ class Template:
|
||||||
if isinstance(list, StringType):
|
if isinstance(list, StringType):
|
||||||
raise NeedSequenceError()
|
raise NeedSequenceError()
|
||||||
refname = valref[0]
|
refname = valref[0]
|
||||||
ctx.for_index[refname] = [ list, 0 ]
|
ctx.for_index[refname] = idx = [ list, 0 ]
|
||||||
for i in range(len(list)):
|
for item in list:
|
||||||
ctx.for_index[refname][1] = i
|
|
||||||
self._execute(section, fp, ctx)
|
self._execute(section, fp, ctx)
|
||||||
|
idx[1] = idx[1] + 1
|
||||||
del ctx.for_index[refname]
|
del ctx.for_index[refname]
|
||||||
|
|
||||||
|
|
||||||
|
@ -427,6 +448,9 @@ class UnknownReference(Exception):
|
||||||
class NeedSequenceError(Exception):
|
class NeedSequenceError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class UnclosedBlocksError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
# --- standard test environment ---
|
# --- standard test environment ---
|
||||||
def test_parse():
|
def test_parse():
|
||||||
assert _re_parse.split('[a]') == ['', '[a]', None, '']
|
assert _re_parse.split('[a]') == ['', '[a]', None, '']
|
||||||
|
|
Loading…
Reference in New Issue