Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37786741
en ru br
Репозитории ALT

Группа :: Система/Библиотеки
Пакет: 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
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin