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-e597b4017df7
remotes/tags/V0_9
gstein 2001-12-18 00:03:53 +00:00
parent a2b579b5ac
commit 62f37986d5
1 changed files with 34 additions and 10 deletions

View File

@ -147,6 +147,7 @@ Directives
import string
import re
from types import StringType, IntType, FloatType
import os
#
# This regular expression matches three alternatives:
@ -205,9 +206,10 @@ class Template:
self._execute(self.program, fp, ctx)
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.
This is a private helper function doing the real work for method parse.
@ -277,15 +279,22 @@ class Template:
raise ArgCountSyntaxError()
if args[1][0] == '"':
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))
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:
# implied PRINT command
if len(args) > 1:
raise ArgCountSyntaxError()
program.append((self._cmd_print, _prepare_ref(args[0], for_names)))
if stack:
### would be nice to say which blocks...
raise UnclosedBlocksError()
return program
def _execute(self, program, fp, ctx):
@ -300,11 +309,23 @@ class Template:
step[0](step[1], fp, ctx)
def _cmd_print(self, valref, fp, ctx):
### type check the value
fp.write(_get_value(valref, ctx))
value = _get_value(valref, ctx)
def _cmd_include(self, valref, fp, ctx):
self._execute(self._parse_file(_get_value(valref, ctx)), fp, ctx)
# if the value has a 'read' attribute, then it is a stream: copy it
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):
"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):
raise NeedSequenceError()
refname = valref[0]
ctx.for_index[refname] = [ list, 0 ]
for i in range(len(list)):
ctx.for_index[refname][1] = i
ctx.for_index[refname] = idx = [ list, 0 ]
for item in list:
self._execute(section, fp, ctx)
idx[1] = idx[1] + 1
del ctx.for_index[refname]
@ -427,6 +448,9 @@ class UnknownReference(Exception):
class NeedSequenceError(Exception):
pass
class UnclosedBlocksError(Exception):
pass
# --- standard test environment ---
def test_parse():
assert _re_parse.split('[a]') == ['', '[a]', None, '']