Группа :: Система/Библиотеки
Пакет: lasso
Главная Изменения Спек Патчи Sources Загрузить Gear Bugs and FR Repocop
Патч: lasso-web-switch-generation-script-to-python-3.patch
Скачать
Скачать
From 5b78304f422e55d1c1df3b3294417d6c49ad075a Mon Sep 17 00:00:00 2001
From: Frц╘dц╘ric Pц╘ters <fpeters@entrouvert.com>
Date: Sat, 25 Mar 2023 08:36:16 +0100
Subject: [PATCH] web: switch generation script to python 3 (#775802)
---
website/convert-to-static.py | 5 ++---
website/ezt.py | 921 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2 files changed, 506 insertions(+), 420 deletions(-)
diff --git a/website/convert-to-static.py b/website/convert-to-static.py
index 769fc26..f18cbe8 100755
--- a/website/convert-to-static.py
+++ b/website/convert-to-static.py
@@ -1,10 +1,10 @@
-#! /usr/bin/env python
+#! /usr/bin/env python3
import xml.dom.minidom
import os
import stat
import re
-from six import StringIO
+from io import StringIO
import sys
import ezt
@@ -318,4 +318,3 @@ for base, dirs, files in os.walk('web'):
with open(dst_file, 'w')as f:
f.write(fd.getvalue())
continue
-
diff --git a/website/ezt.py b/website/ezt.py
index 5ea229c..16d499e 100644
--- a/website/ezt.py
+++ b/website/ezt.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
"""ezt.py -- easy templating
ezt templates are simply text files in whatever format you so desire
@@ -224,19 +223,19 @@ Directives
# http://svn.webdav.org/repos/projects/ezt/trunk/
#
-import string
-import re
-from types import StringType, IntType, FloatType, LongType
+import datetime
+import html
+import io
import os
-import cgi
-from six import StringIO
+import re
#
# Formatting types
#
FORMAT_RAW = 'raw'
FORMAT_HTML = 'html'
FORMAT_XML = 'xml'
+FORMAT_RTF = 'rtf'
#
# This regular expression matches three alternatives:
@@ -259,7 +258,7 @@ _re_parse = re.compile(r'\[(%s(?: +%s)*)\]|(\[\[\])|\[#[^\]]*\]' % (_item, _item
_re_args = re.compile(r'"(?:[^\\"]|\\.)*"|[-\w.]+')
# block commands and their argument counts
-_block_cmd_specs = { 'if-index':2, 'for':1, 'is':2, 'define':1, 'format':1 }
+_block_cmd_specs = {'if-index': 2, 'for': 1, 'is': 2, 'define': 1, 'format': 1}
_block_cmds = _block_cmd_specs.keys()
# two regular expresssions for compressing whitespace. the first is used to
@@ -274,464 +273,552 @@ _re_whitespace = re.compile(r'\s\s+')
# an integer.
_re_subst = re.compile('%(%|[0-9]+)')
-class Template:
- _printers = {
- FORMAT_RAW : '_cmd_print',
- FORMAT_HTML : '_cmd_print_html',
- FORMAT_XML : '_cmd_print_xml',
+class Template:
+ _printers = {
+ FORMAT_RAW: '_cmd_print',
+ FORMAT_HTML: '_cmd_print_html',
+ FORMAT_XML: '_cmd_print_xml',
+ FORMAT_RTF: '_cmd_print_rtf',
}
- def __init__(self, fname=None, compress_whitespace=1,
- base_format=FORMAT_RAW):
- self.compress_whitespace = compress_whitespace
- if fname:
- self.parse_file(fname, base_format)
+ def __init__(self, fname=None, compress_whitespace=1, base_format=FORMAT_RAW):
+ self.compress_whitespace = compress_whitespace
+ if fname:
+ self.parse_file(fname, base_format)
- def parse_file(self, fname, base_format=FORMAT_RAW):
- "fname -> a string object with pathname of file containg an EZT template."
+ def parse_file(self, fname, base_format=FORMAT_RAW):
+ "fname -> a string object with pathname of file containg an EZT template."
- self.parse(_FileReader(fname), base_format)
+ self.parse(_FileReader(fname), base_format)
- def parse(self, text_or_reader, base_format=FORMAT_RAW):
- """Parse the template specified by text_or_reader.
+ def parse(self, text_or_reader, base_format=FORMAT_RAW):
+ """Parse the template specified by text_or_reader.
- The argument should be a string containing the template, or it should
- specify a subclass of ezt.Reader which can read templates. The base
- format for printing values is given by base_format.
- """
- if not isinstance(text_or_reader, Reader):
- # assume the argument is a plain text string
- text_or_reader = _TextReader(text_or_reader)
-
- printer = getattr(self, self._printers[base_format])
- self.program = self._parse(text_or_reader, base_printer=printer)
-
- def generate(self, fp, data):
- if hasattr(data, '__getitem__') or callable(getattr(data, 'keys', None)):
- # a dictionary-like object was passed. convert it to an
- # attribute-based object.
- class _data_ob:
- def __init__(self, d):
- vars(self).update(d)
- data = _data_ob(data)
-
- ctx = _context()
- ctx.data = data
- ctx.for_index = { }
- ctx.defines = { }
- self._execute(self.program, fp, ctx)
-
- def _parse(self, reader, for_names=None, file_args=(), base_printer=None):
- """text -> string object containing the template.
-
- This is a private helper function doing the real work for method parse.
- It returns the parsed template as a 'program'. This program is a sequence
- made out of strings or (function, argument) 2-tuples.
-
- Note: comment directives [# ...] are automatically dropped by _re_parse.
- """
+ The argument should be a string containing the template, or it should
+ specify a subclass of ezt.Reader which can read templates. The base
+ format for printing values is given by base_format.
+ """
+ if not isinstance(text_or_reader, Reader):
+ # assume the argument is a plain text string
+ text_or_reader = _TextReader(text_or_reader)
- # parse the template program into: (TEXT DIRECTIVE BRACKET)* TEXT
- parts = _re_parse.split(reader.text)
+ printer = getattr(self, self._printers[base_format])
+ self.program = self._parse(text_or_reader, base_printer=printer)
- program = [ ]
- stack = [ ]
- if not for_names:
- for_names = [ ]
+ def generate(self, fp, data):
+ if hasattr(data, '__getitem__') or callable(getattr(data, 'keys', None)):
+ # a dictionary-like object was passed. convert it to an
+ # attribute-based object.
+ class _data_ob:
+ def __init__(self, d):
+ self.data = d
- if base_printer:
- printers = [ base_printer ]
- else:
- printers = [ self._cmd_print ]
-
- for i in range(len(parts)):
- piece = parts[i]
- which = i % 3 # discriminate between: TEXT DIRECTIVE BRACKET
- if which == 0:
- # TEXT. append if non-empty.
- if piece:
- if self.compress_whitespace:
- piece = _re_whitespace.sub(' ', _re_newline.sub('\n', piece))
- program.append(piece)
- elif which == 2:
- # BRACKET directive. append '[' if present.
- if piece:
- program.append('[')
- elif piece:
- # DIRECTIVE is present.
- args = _re_args.findall(piece)
- cmd = args[0]
- if cmd == 'else':
- if len(args) > 1:
- raise ArgCountSyntaxError(str(args[1:]))
- ### check: don't allow for 'for' cmd
- idx = stack[-1][1]
- true_section = program[idx:]
- del program[idx:]
- stack[-1][3] = true_section
- elif cmd == 'end':
- if len(args) > 1:
- raise ArgCountSyntaxError(str(args[1:]))
- # note: true-section may be None
- try:
- cmd, idx, args, true_section = stack.pop()
- except IndexError:
- raise UnmatchedEndError()
- else_section = program[idx:]
- if cmd == 'format':
- printers.pop()
- else:
- func = getattr(self, '_cmd_' + re.sub('-', '_', cmd))
- program[idx:] = [ (func, (args, true_section, else_section)) ]
- if cmd == 'for':
- for_names.pop()
- elif cmd in _block_cmds:
- if len(args) > _block_cmd_specs[cmd] + 1:
- raise ArgCountSyntaxError(str(args[1:]))
- ### this assumes arg1 is always a ref unless cmd is 'define'
- if cmd != 'define':
- args[1] = _prepare_ref(args[1], for_names, file_args)
-
- # handle arg2 for the 'is' command
- if cmd == 'is':
- args[2] = _prepare_ref(args[2], for_names, file_args)
- elif cmd == 'for':
- for_names.append(args[1][0]) # append the refname
- elif cmd == 'format':
- if args[1][0]:
- raise BadFormatConstantError(str(args[1:]))
- funcname = self._printers.get(args[1][1])
- if not funcname:
- raise UnknownFormatConstantError(str(args[1:]))
- printers.append(getattr(self, funcname))
-
- # remember the cmd, current pos, args, and a section placeholder
- stack.append([cmd, len(program), args[1:], None])
- elif cmd == 'include':
- if args[1][0] == '"':
- include_filename = args[1][1:-1]
- f_args = [ ]
- for arg in args[2:]:
- f_args.append(_prepare_ref(arg, for_names, file_args))
- program.extend(self._parse(reader.read_other(include_filename),
- for_names, f_args, printers[-1]))
- else:
- if len(args) != 2:
- raise ArgCountSyntaxError(str(args))
- program.append((self._cmd_include,
- (_prepare_ref(args[1], for_names, file_args),
- reader)))
- elif cmd == 'if-any':
- f_args = [ ]
- for arg in args[1:]:
- f_args.append(_prepare_ref(arg, for_names, file_args))
- stack.append(['if-any', len(program), f_args, None])
+ def __getattr__(self, k):
+ try:
+ return self.data[k]
+ except KeyError:
+ raise AttributeError(k)
+
+ data = _data_ob(data)
+
+ ctx = _context()
+ ctx.data = data
+ ctx.for_index = {}
+ ctx.defines = {}
+ self._execute(self.program, fp, ctx)
+
+ def _parse(self, reader, for_names=None, file_args=(), base_printer=None):
+ """text -> string object containing the template.
+
+ This is a private helper function doing the real work for method parse.
+ It returns the parsed template as a 'program'. This program is a sequence
+ made out of strings or (function, argument) 2-tuples.
+
+ Note: comment directives [# ...] are automatically dropped by _re_parse.
+ """
+
+ # parse the template program into: (TEXT DIRECTIVE BRACKET)* TEXT
+ parts = _re_parse.split(reader.text)
+
+ program = []
+ stack = []
+ if not for_names:
+ for_names = []
+
+ if base_printer:
+ printers = [base_printer]
else:
- # implied PRINT command
- f_args = [ ]
- for arg in args:
- f_args.append(_prepare_ref(arg, for_names, file_args))
- program.append((printers[-1], f_args))
-
- if stack:
- ### would be nice to say which blocks...
- raise UnclosedBlocksError()
- return program
-
- def _execute(self, program, fp, ctx):
- """This private helper function takes a 'program' sequence as created
- by the method '_parse' and executes it step by step. strings are written
- to the file object 'fp' and functions are called.
- """
- for step in program:
- if isinstance(step, StringType):
- fp.write(step)
- else:
- step[0](step[1], fp, ctx)
-
- def _cmd_print(self, valref, fp, ctx):
- _write_value(valref, fp, ctx)
-
- def _cmd_print_html(self, valref, fp, ctx):
- _write_value(valref, fp, ctx, cgi.escape)
-
- def _cmd_print_xml(self, valref, fp, ctx):
- ### use the same quoting as HTML for now
- self._cmd_print_html(valref, fp, ctx)
-
- def _cmd_include(self, valref_reader_tuple, fp, ctx):
- valref, reader = valref_reader_tuple
- fname = _get_value(valref, ctx)
- ### note: we don't have the set of for_names to pass into this parse.
- ### I don't think there is anything to do but document it. we also
- ### don't have a current format (since that is a compile-time concept).
- self._execute(self._parse(reader.read_other(fname)), fp, ctx)
-
- def _cmd_if_any(self, args, fp, ctx):
- "If any value is a non-empty string or non-empty list, then T else F."
- (valrefs, t_section, f_section) = args
- value = 0
- for valref in valrefs:
- if _get_value(valref, ctx):
- value = 1
- break
- self._do_if(value, t_section, f_section, fp, ctx)
-
- def _cmd_if_index(self, args, fp, ctx):
- ((valref, value), t_section, f_section) = args
- list, idx = ctx.for_index[valref[0]]
- if value == 'even':
- value = idx % 2 == 0
- elif value == 'odd':
- value = idx % 2 == 1
- elif value == 'first':
- value = idx == 0
- elif value == 'last':
- value = idx == len(list)-1
- else:
- value = idx == int(value)
- self._do_if(value, t_section, f_section, fp, ctx)
-
- def _cmd_is(self, args, fp, ctx):
- ((left_ref, right_ref), t_section, f_section) = args
- value = _get_value(right_ref, ctx)
- value = string.lower(_get_value(left_ref, ctx)) == string.lower(value)
- self._do_if(value, t_section, f_section, fp, ctx)
-
- def _do_if(self, value, t_section, f_section, fp, ctx):
- if t_section is None:
- t_section = f_section
- f_section = None
- if value:
- section = t_section
- else:
- section = f_section
- if section is not None:
- self._execute(section, fp, ctx)
-
- def _cmd_for(self, args, fp, ctx):
- ((valref,), unused, section) = args
- list = _get_value(valref, ctx)
- if isinstance(list, StringType):
- raise NeedSequenceError()
- refname = valref[0]
- 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]
-
- def _cmd_define(self, args, fp, ctx):
- ((name,), unused, section) = args
- valfp = StringIO()
- if section is not None:
- self._execute(section, valfp, ctx)
- ctx.defines[name] = valfp.getvalue()
+ printers = [self._cmd_print]
+
+ column = 0
+ line = 0
+
+ for i in range(len(parts)):
+ piece = parts[i]
+ which = i % 3 # discriminate between: TEXT DIRECTIVE BRACKET
+ if which == 0:
+ # TEXT. append if non-empty.
+ line += piece.count('\n')
+ last_newline = piece.rfind('\n')
+ if last_newline != -1:
+ column = 0
+ column += len(piece) - last_newline - 1
+ if piece:
+ if self.compress_whitespace:
+ piece = _re_whitespace.sub(' ', _re_newline.sub('\n', piece))
+ program.append(piece)
+ elif which == 2:
+ # BRACKET directive. append '[' if present.
+ if piece:
+ program.append('[')
+ elif piece:
+ # DIRECTIVE is present.
+ args = _re_args.findall(piece)
+ cmd = args[0]
+ if cmd == 'else':
+ if len(args) > 1:
+ raise ArgCountSyntaxError(str(args[1:]), line, column)
+ # check: don't allow for 'for' cmd
+ try:
+ idx = stack[-1][1]
+ except IndexError:
+ raise UnmatchedElseError('', line, column)
+ true_section = program[idx:]
+ del program[idx:]
+ stack[-1][3] = true_section
+ elif cmd == 'end':
+ if len(args) > 1:
+ raise ArgCountSyntaxError(str(args[1:]), line, column)
+ # note: true-section may be None
+ try:
+ cmd, idx, args, true_section = stack.pop()
+ except IndexError:
+ raise UnmatchedEndError('', line, column)
+ else_section = program[idx:]
+ if cmd == 'format':
+ printers.pop()
+ else:
+ func = getattr(self, '_cmd_' + re.sub('-', '_', cmd))
+ program[idx:] = [(func, (args, true_section, else_section))]
+ if cmd == 'for':
+ for_names.pop()
+ elif cmd in _block_cmds:
+ if len(args) > _block_cmd_specs[cmd] + 1:
+ raise ArgCountSyntaxError(str(args[1:]), line, column)
+ # this assumes arg1 is always a ref unless cmd is 'define'
+ if cmd != 'define':
+ if len(args) < 2:
+ raise ArgCountSyntaxError(str(args), line, column)
+ args[1] = _prepare_ref(args[1], for_names, file_args)
+
+ # handle arg2 for the 'is' command
+ if cmd == 'is':
+ if len(args) != 3:
+ raise ArgCountSyntaxError(str(args[1:]), line, column)
+ args[2] = _prepare_ref(args[2], for_names, file_args)
+ elif cmd == 'for':
+ for_names.append(args[1][0]) # append the refname
+ elif cmd == 'format':
+ if args[1][0]:
+ raise BadFormatConstantError(str(args[1:]), line, column)
+ funcname = self._printers.get(args[1][1])
+ if not funcname:
+ raise UnknownFormatConstantError(str(args[1:]), line, column)
+ printers.append(getattr(self, funcname))
+
+ # remember the cmd, current pos, args, and a section placeholder
+ stack.append([cmd, len(program), args[1:], None])
+ elif cmd == 'include':
+ if args[1][0] == '"':
+ include_filename = args[1][1:-1]
+ f_args = []
+ for arg in args[2:]:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ program.extend(
+ self._parse(reader.read_other(include_filename), for_names, f_args, printers[-1])
+ )
+ else:
+ if len(args) != 2:
+ raise ArgCountSyntaxError(str(args), line, column)
+ program.append(
+ (self._cmd_include, (_prepare_ref(args[1], for_names, file_args), reader))
+ )
+ elif cmd == 'if-any':
+ f_args = []
+ for arg in args[1:]:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ stack.append(['if-any', len(program), f_args, None])
+ else:
+ # implied PRINT command
+ f_args = []
+ for arg in args:
+ f_args.append(_prepare_ref(arg, for_names, file_args))
+ program.append((printers[-1], f_args))
+ column += 2 + len(piece)
+
+ if stack:
+ # would be nice to say which blocks...
+ raise UnclosedBlocksError('', line, column)
+ return program
+
+ def _execute(self, program, fp, ctx):
+ """This private helper function takes a 'program' sequence as created
+ by the method '_parse' and executes it step by step. strings are written
+ to the file object 'fp' and functions are called.
+ """
+ for step in program:
+ if isinstance(step, str):
+ fp.write(step)
+ else:
+ step[0](step[1], fp, ctx)
+
+ def _cmd_print(self, valref, fp, ctx):
+ _write_value(valref, fp, ctx)
+
+ def _cmd_print_html(self, valref, fp, ctx):
+ _write_value(valref, fp, ctx, html.escape)
+
+ def _cmd_print_rtf(self, valref, fp, ctx):
+ def char2rtf(c):
+ if ord(c) < 128:
+ return c
+ else:
+ return '\\u%d?' % ord(c)
+
+ def rtf_escape(s):
+ s = ''.join([char2rtf(c) for c in s])
+ return '{\\uc1{%s}}' % s
+
+ _write_value(valref, fp, ctx, rtf_escape)
+
+ def _cmd_print_xml(self, valref, fp, ctx):
+ # use the same quoting as HTML for now
+ self._cmd_print_html(valref, fp, ctx)
+
+ def _cmd_include(self, include_ref, fp, ctx):
+ (valref, reader) = include_ref
+ fname = _get_value(valref, ctx)
+ # note: we don't have the set of for_names to pass into this parse.
+ # I don't think there is anything to do but document it. we also
+ # don't have a current format (since that is a compile-time concept).
+ self._execute(self._parse(reader.read_other(fname)), fp, ctx)
+
+ def _cmd_if_any(self, args, fp, ctx):
+ "If any value is a non-empty string or non-empty list, then T else F."
+ (valrefs, t_section, f_section) = args
+ value = 0
+ for valref in valrefs:
+ try:
+ if _get_value(valref, ctx):
+ value = 1
+ break
+ except UnknownReference:
+ pass
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _cmd_if_index(self, args, fp, ctx):
+ ((valref, value), t_section, f_section) = args
+ list, idx = ctx.for_index[valref[0]]
+ if value == 'even':
+ value = idx % 2 == 0
+ elif value == 'odd':
+ value = idx % 2 == 1
+ elif value == 'first':
+ value = idx == 0
+ elif value == 'last':
+ value = idx == len(list) - 1
+ else:
+ value = idx == int(value)
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _cmd_is(self, args, fp, ctx):
+ ((left_ref, right_ref), t_section, f_section) = args
+ try:
+ value = _get_value(right_ref, ctx)
+ value = str(_get_value(left_ref, ctx)).lower() == str(value).lower()
+ except UnknownReference:
+ value = False
+ self._do_if(value, t_section, f_section, fp, ctx)
+
+ def _do_if(self, value, t_section, f_section, fp, ctx):
+ if t_section is None:
+ t_section = f_section
+ f_section = None
+ if value:
+ section = t_section
+ else:
+ section = f_section
+ if section is not None:
+ self._execute(section, fp, ctx)
+
+ def _cmd_for(self, args, fp, ctx):
+ ((valref,), dummy, section) = args
+ try:
+ list = _get_value(valref, ctx)
+ except UnknownReference:
+ return
+ if isinstance(list, str):
+ raise NeedSequenceError()
+ refname = valref[0]
+ ctx.for_index[refname] = idx = [list, 0]
+ for dummy in list:
+ self._execute(section, fp, ctx)
+ idx[1] = idx[1] + 1
+ del ctx.for_index[refname]
+
+ def _cmd_define(self, args, fp, ctx):
+ ((name,), dummy, section) = args
+ valfp = io.StringIO()
+ if section is not None:
+ self._execute(section, valfp, ctx)
+ ctx.defines[name] = valfp.getvalue()
+
def boolean(value):
- "Return a value suitable for [if-any bool_var] usage in a template."
- if value:
- return 'yes'
- return None
+ "Return a value suitable for [if-any bool_var] usage in a template."
+ if value:
+ return 'yes'
+ return None
def _prepare_ref(refname, for_names, file_args):
- """refname -> a string containing a dotted identifier. example:"foo.bar.bang"
- for_names -> a list of active for sequences.
-
- Returns a `value reference', a 3-tuple made out of (refname, start, rest),
- for fast access later.
- """
- # is the reference a string constant?
- if refname[0] == '"':
- return None, refname[1:-1], None
-
- parts = string.split(refname, '.')
- start = parts[0]
- rest = parts[1:]
-
- # if this is an include-argument, then just return the prepared ref
- if start[:3] == 'arg':
- try:
- idx = int(start[3:])
- except ValueError:
- pass
+ """refname -> a string containing a dotted identifier. example:"foo.bar.bang"
+ for_names -> a list of active for sequences.
+
+ Returns a `value reference', a 3-tuple made out of (refname, start, rest),
+ for fast access later.
+ """
+ # is the reference a string constant?
+ if refname[0] == '"':
+ return None, refname[1:-1], None
+
+ parts = refname.split('.')
+ start = parts[0]
+ rest = parts[1:]
+
+ # if this is an include-argument, then just return the prepared ref
+ if start[:3] == 'arg':
+ try:
+ idx = int(start[3:])
+ except ValueError:
+ pass
+ else:
+ if idx < len(file_args):
+ dummy, start, more_rest = file_args[idx]
+ if more_rest is None:
+ # the include-argument was a string constant
+ return None, start, None
+
+ # prepend the argument's "rest" for our further processing
+ rest[:0] = more_rest
+
+ # rewrite the refname to ensure that any potential 'for' processing
+ # has the correct name
+ # this can make it hard for debugging include files since we lose
+ # the 'argNNN' names
+ if not rest:
+ return start, start, []
+ refname = start + '.' + '.'.join(rest)
+
+ if for_names:
+ # From last to first part, check if this reference is part of a for loop
+ for i in range(len(parts), 0, -1):
+ name = '.'.join(parts[:i])
+ if name in for_names:
+ return refname, name, parts[i:]
+
+ return refname, start, rest
+
+
+def _get_value(value_ref, ctx):
+ """(refname, start, rest) -> a prepared `value reference' (see above).
+ ctx -> an execution context instance.
+
+ Does a name space lookup within the template name space. Active
+ for blocks take precedence over data dictionary members with the
+ same name.
+ """
+ (refname, start, rest) = value_ref
+ if rest is None:
+ # it was a string constant
+ return start
+
+ # get the starting object
+ if start in ctx.for_index:
+ list, idx = ctx.for_index[start]
+ ob = list[idx]
+ elif start in ctx.defines:
+ ob = ctx.defines[start]
+ elif hasattr(ctx.data, start):
+ ob = getattr(ctx.data, start)
+ elif refname in ('True', 'False'):
+ return bool(refname == 'True')
else:
- if idx < len(file_args):
- orig_refname, start, more_rest = file_args[idx]
- if more_rest is None:
- # the include-argument was a string constant
- return None, start, None
-
- # prepend the argument's "rest" for our further processing
- rest[:0] = more_rest
-
- # rewrite the refname to ensure that any potential 'for' processing
- # has the correct name
- ### this can make it hard for debugging include files since we lose
- ### the 'argNNN' names
- if not rest:
- return start, start, [ ]
- refname = start + '.' + string.join(rest, '.')
-
- if for_names:
- # From last to first part, check if this reference is part of a for loop
- for i in range(len(parts), 0, -1):
- name = string.join(parts[:i], '.')
- if name in for_names:
- return refname, name, parts[i:]
-
- return refname, start, rest
-
-def _get_value(refname_start_rest_tuple, ctx):
- """(refname, start, rest) -> a prepared `value reference' (see above).
- ctx -> an execution context instance.
-
- Does a name space lookup within the template name space. Active
- for blocks take precedence over data dictionary members with the
- same name.
- """
- refname, start, rest = refname_start_rest_tuple
- if rest is None:
- # it was a string constant
- return start
-
- # get the starting object
- if ctx.for_index.has_key(start):
- list, idx = ctx.for_index[start]
- ob = list[idx]
- elif ctx.defines.has_key(start):
- ob = ctx.defines[start]
- elif hasattr(ctx.data, start):
- ob = getattr(ctx.data, start)
- else:
- raise UnknownReference(refname)
-
- # walk the rest of the dotted reference
- for attr in rest:
+ raise UnknownReference(refname)
+
+ # walk the rest of the dotted reference
+ for attr in rest:
+ try:
+ ob = getattr(ob, attr)
+ except AttributeError:
+ try:
+ ob = ob[attr]
+ except (TypeError, KeyError):
+ try:
+ ob = ob[int(attr)]
+ except (ValueError, TypeError):
+ raise UnknownReference(refname)
+
+ # make sure we return a string instead of some various Python types
+ if isinstance(ob, (int, float)):
+ return str(ob)
+ if ob is None:
+ return ''
+
+ # string or a sequence
+ return ob
+
+
+def _get_value_fallback(value_ref, ctx):
try:
- ob = getattr(ob, attr)
- except AttributeError:
- raise UnknownReference(refname)
-
- # make sure we return a string instead of some various Python types
- if isinstance(ob, IntType) \
- or isinstance(ob, LongType) \
- or isinstance(ob, FloatType):
- return str(ob)
- if ob is None:
- return ''
+ return _get_value(value_ref, ctx)
+ except UnknownReference:
+ refname = value_ref[0]
+ return '[' + refname + ']'
- # string or a sequence
- return ob
def _write_value(valrefs, fp, ctx, format=lambda s: s):
- value = _get_value(valrefs[0], ctx)
- args = map(lambda valref, ctx=ctx: _get_value(valref, ctx), valrefs[1:])
-
- # 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(format(chunk))
-
- # value is a callback function: call with file pointer and extra args
- elif callable(value):
- apply(value, [fp] + args)
-
- # value is a substitution pattern
- elif args:
- parts = _re_subst.split(value)
- for i in range(len(parts)):
- piece = parts[i]
- if i%2 == 1 and piece != '%':
- idx = int(piece)
- if idx < len(args):
- piece = args[idx]
+ try:
+ value = _get_value(valrefs[0], ctx)
+ except UnknownReference:
+ value = '[' + ' '.join([v[0] for v in valrefs]) + ']'
+ fp.write(format(value))
+ return
+ args = list(map(lambda valref, ctx=ctx: _get_value_fallback(valref, ctx), valrefs[1:]))
+
+ # if the value has a 'read' attribute, then it is a stream: copy it
+ if hasattr(value, 'read'):
+ while True:
+ chunk = value.read(16384)
+ if not chunk:
+ break
+ fp.write(format(chunk))
+
+ # value is a callback function
+ elif callable(value):
+ if getattr(value, 'ezt_call_mode', None) == 'simple':
+ # simple call mode, call with args and write the result
+ fp.write(value(*args))
else:
- piece = '<undef>'
- if format:
- fp.write(format(piece))
-
- # plain old value, write to output
- else:
- fp.write(format(value))
+ # default, call with file pointer and extra args
+ value(*[fp] + args)
+
+ # value is a substitution pattern
+ elif args:
+ parts = _re_subst.split(value)
+ for i in range(len(parts)):
+ piece = parts[i]
+ if i % 2 == 1 and piece != '%':
+ idx = int(piece)
+ if idx < len(args):
+ piece = args[idx]
+ else:
+ piece = '<undef>'
+ if format:
+ fp.write(format(piece))
+
+ elif isinstance(value, datetime.datetime):
+ from .misc import localstrftime
+
+ fp.write(localstrftime(value))
+ elif isinstance(value, datetime.date):
+ from .misc import date_format, strftime
+
+ fp.write(strftime(date_format(), value))
+ # plain old value, write to output
+ else:
+ fp.write(format(str(value)))
class _context:
- """A container for the execution context"""
+ """A container for the execution context"""
class Reader:
- "Abstract class which allows EZT to detect Reader objects."
+ "Abstract class which allows EZT to detect Reader objects."
+
class _FileReader(Reader):
- """Reads templates from the filesystem."""
- def __init__(self, fname):
- self.text = open(fname, 'rb').read()
- self._dir = os.path.dirname(fname)
- def read_other(self, relative):
- return _FileReader(os.path.join(self._dir, relative))
+ """Reads templates from the filesystem."""
+
+ def __init__(self, fname):
+ with open(fname, 'rb') as fd:
+ self.text = fd.read()
+ self._dir = os.path.dirname(fname)
+
+ def read_other(self, relative):
+ return _FileReader(os.path.join(self._dir, relative))
+
class _TextReader(Reader):
- """'Reads' a template from provided text."""
- def __init__(self, text):
- self.text = text
- def read_other(self, relative):
- raise BaseUnavailableError()
+ """'Reads' a template from provided text."""
+
+ def __init__(self, text):
+ self.text = text
+
+ def read_other(self, relative):
+ raise BaseUnavailableError()
class EZTException(Exception):
- """Parent class of all EZT exceptions."""
+ """Parent class of all EZT exceptions."""
+
+ def __init__(self, msg=None, line=None, column=None):
+ self.msg = msg
+ self.line = line
+ self.column = column
+
+ def __str__(self):
+ s = self.__class__.__name__
+ if self.msg:
+ s += ' "%s"' % self.msg
+ if self.line:
+ s += ' at line %d column %d' % (self.line + 1, self.column + 1)
+ return s
+
class ArgCountSyntaxError(EZTException):
- """A bracket directive got the wrong number of arguments."""
+ """A bracket directive got the wrong number of arguments."""
+
class UnknownReference(EZTException):
- """The template references an object not contained in the data dictionary."""
+ """The template references an object not contained in the data dictionary."""
+
class NeedSequenceError(EZTException):
- """The object dereferenced by the template is no sequence (tuple or list)."""
+ """The object dereferenced by the template is no sequence (tuple or list)."""
+
class UnclosedBlocksError(EZTException):
- """This error may be simply a missing [end]."""
+ """This error may be simply a missing [end]."""
+
class UnmatchedEndError(EZTException):
- """This error may be caused by a misspelled if directive."""
+ """This error may be caused by a misspelled if directive."""
+
+
+class UnmatchedElseError(EZTException):
+ """This error may be caused by a misspelled if directive."""
+
class BaseUnavailableError(EZTException):
- """Base location is unavailable, which disables includes."""
+ """Base location is unavailable, which disables includes."""
+
class BadFormatConstantError(EZTException):
- """Format specifiers must be string constants."""
+ """Format specifiers must be string constants."""
+
class UnknownFormatConstantError(EZTException):
- """The format specifier is an unknown value."""
-
-
-# --- standard test environment ---
-def test_parse():
- assert _re_parse.split('[a]') == ['', '[a]', None, '']
- assert _re_parse.split('[a] [b]') == \
- ['', '[a]', None, ' ', '[b]', None, '']
- assert _re_parse.split('[a c] [b]') == \
- ['', '[a c]', None, ' ', '[b]', None, '']
- assert _re_parse.split('x [a] y [b] z') == \
- ['x ', '[a]', None, ' y ', '[b]', None, ' z']
- assert _re_parse.split('[a "b" c "d"]') == \
- ['', '[a "b" c "d"]', None, '']
- assert _re_parse.split(r'["a \"b[foo]" c.d f]') == \
- ['', '["a \\"b[foo]" c.d f]', None, '']
-
-def _test(argv):
- import doctest, ezt
- verbose = "-v" in argv
- return doctest.testmod(ezt, verbose=verbose)
-
-if __name__ == "__main__":
- # invoke unit test for this module:
- import sys
- sys.exit(_test(sys.argv)[0])
+ """The format specifier is an unknown value."""
--
libgit2 1.6.4