Code

Add config-option "nosy" to messages_to_author setting in [nosy] section
[roundup.git] / roundup / cgi / TAL / TALDefs.py
1 ##############################################################################
2 #
3 # Copyright (c) 2001, 2002 Zope Corporation and Contributors.
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the Zope Public License,
7 # Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
8 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11 # FOR A PARTICULAR PURPOSE.
12 #
13 ##############################################################################
14 # Modifications for Roundup:
15 # 1. commented out ITALES references
16 """
17 Common definitions used by TAL and METAL compilation an transformation.
18 """
20 from types import ListType, TupleType
22 #from ITALES import ITALESErrorInfo
24 TAL_VERSION = "1.4"
26 XML_NS = "http://www.w3.org/XML/1998/namespace" # URI for XML namespace
27 XMLNS_NS = "http://www.w3.org/2000/xmlns/" # URI for XML NS declarations
29 ZOPE_TAL_NS = "http://xml.zope.org/namespaces/tal"
30 ZOPE_METAL_NS = "http://xml.zope.org/namespaces/metal"
31 ZOPE_I18N_NS = "http://xml.zope.org/namespaces/i18n"
33 # This RE must exactly match the expression of the same name in the
34 # zope.i18n.simpletranslationservice module:
35 NAME_RE = "[a-zA-Z_][-a-zA-Z0-9_]*"
37 KNOWN_METAL_ATTRIBUTES = [
38     "define-macro",
39     "use-macro",
40     "define-slot",
41     "fill-slot",
42     "slot",
43     ]
45 KNOWN_TAL_ATTRIBUTES = [
46     "define",
47     "condition",
48     "content",
49     "replace",
50     "repeat",
51     "attributes",
52     "on-error",
53     "omit-tag",
54     "tal tag",
55     ]
57 KNOWN_I18N_ATTRIBUTES = [
58     "translate",
59     "domain",
60     "target",
61     "source",
62     "attributes",
63     "data",
64     "name",
65     ]
67 class TALError(Exception):
69     def __init__(self, msg, position=(None, None)):
70         assert msg != ""
71         self.msg = msg
72         self.lineno = position[0]
73         self.offset = position[1]
74         self.filename = None
76     def setFile(self, filename):
77         self.filename = filename
79     def __str__(self):
80         result = self.msg
81         if self.lineno is not None:
82             result = result + ", at line %d" % self.lineno
83         if self.offset is not None:
84             result = result + ", column %d" % (self.offset + 1)
85         if self.filename is not None:
86             result = result + ', in file %s' % self.filename
87         return result
89 class METALError(TALError):
90     pass
92 class TALESError(TALError):
93     pass
95 class I18NError(TALError):
96     pass
99 class ErrorInfo:
101     #__implements__ = ITALESErrorInfo
103     def __init__(self, err, position=(None, None)):
104         if isinstance(err, Exception):
105             self.type = err.__class__
106             self.value = err
107         else:
108             self.type = err
109             self.value = None
110         self.lineno = position[0]
111         self.offset = position[1]
115 import re
116 _attr_re = re.compile(r"\s*([^\s]+)\s+([^\s].*)\Z", re.S)
117 _subst_re = re.compile(r"\s*(?:(text|structure)\s+)?(.*)\Z", re.S)
118 del re
120 def parseAttributeReplacements(arg, xml):
121     dict = {}
122     for part in splitParts(arg):
123         m = _attr_re.match(part)
124         if not m:
125             raise TALError("Bad syntax in attributes: " + `part`)
126         name, expr = m.group(1, 2)
127         if not xml:
128             name = name.lower()
129         if dict.has_key(name):
130             raise TALError("Duplicate attribute name in attributes: " + `part`)
131         dict[name] = expr
132     return dict
134 def parseSubstitution(arg, position=(None, None)):
135     m = _subst_re.match(arg)
136     if not m:
137         raise TALError("Bad syntax in substitution text: " + `arg`, position)
138     key, expr = m.group(1, 2)
139     if not key:
140         key = "text"
141     return key, expr
143 def splitParts(arg):
144     # Break in pieces at undoubled semicolons and
145     # change double semicolons to singles:
146     arg = arg.replace(";;", "\0")
147     parts = arg.split(';')
148     parts = [p.replace("\0", ";") for p in parts]
149     if len(parts) > 1 and not parts[-1].strip():
150         del parts[-1] # It ended in a semicolon
151     return parts
153 def isCurrentVersion(program):
154     version = getProgramVersion(program)
155     return version == TAL_VERSION
157 def getProgramMode(program):
158     version = getProgramVersion(program)
159     if (version == TAL_VERSION and isinstance(program[1], TupleType) and
160         len(program[1]) == 2):
161         opcode, mode = program[1]
162         if opcode == "mode":
163             return mode
164     return None
166 def getProgramVersion(program):
167     if (len(program) >= 2 and
168         isinstance(program[0], TupleType) and len(program[0]) == 2):
169         opcode, version = program[0]
170         if opcode == "version":
171             return version
172     return None
174 import re
175 _ent1_re = re.compile('&(?![A-Z#])', re.I)
176 _entch_re = re.compile('&([A-Z][A-Z0-9]*)(?![A-Z0-9;])', re.I)
177 _entn1_re = re.compile('&#(?![0-9X])', re.I)
178 _entnx_re = re.compile('&(#X[A-F0-9]*)(?![A-F0-9;])', re.I)
179 _entnd_re = re.compile('&(#[0-9][0-9]*)(?![0-9;])')
180 del re
182 def attrEscape(s):
183     """Replace special characters '&<>' by character entities,
184     except when '&' already begins a syntactically valid entity."""
185     s = _ent1_re.sub('&amp;', s)
186     s = _entch_re.sub(r'&amp;\1', s)
187     s = _entn1_re.sub('&amp;#', s)
188     s = _entnx_re.sub(r'&amp;\1', s)
189     s = _entnd_re.sub(r'&amp;\1', s)
190     s = s.replace('<', '&lt;')
191     s = s.replace('>', '&gt;')
192     s = s.replace('"', '&quot;')
193     return s