Index: appengine/monorail/third_party/ezt.py |
diff --git a/appengine/chromium_bugs/third_party/ezt.py b/appengine/monorail/third_party/ezt.py |
similarity index 96% |
copy from appengine/chromium_bugs/third_party/ezt.py |
copy to appengine/monorail/third_party/ezt.py |
index c048090e4febdeec9cebd4d9e91d5d3f32300959..e55c988db910bd0a56e0f0323f1ca82b576094be 100644 |
--- a/appengine/chromium_bugs/third_party/ezt.py |
+++ b/appengine/monorail/third_party/ezt.py |
@@ -39,14 +39,10 @@ __version__ = '1.0' |
__license__ = 'BSD' |
import re |
-from types import StringType, IntType, FloatType, LongType |
+from types import IntType, FloatType, LongType |
import os |
import urllib |
-try: |
- import cStringIO |
-except ImportError: |
- import StringIO |
- cStringIO = StringIO |
+import StringIO |
# |
# Formatting types |
@@ -58,23 +54,31 @@ FORMAT_JS = 'js' |
FORMAT_URL = 'url' |
# |
-# This regular expression matches three alternatives: |
+# This regular expression matches four alternatives: |
# expr: NEWLINE | DIRECTIVE | BRACKET | COMMENT |
-# DIRECTIVE: '[' ITEM (whitespace ITEM)* '] |
+# DIRECTIVE: '[' ITEM (whitespace ARG)* '] |
# ITEM: STRING | NAME |
+# ARG: STRING | NAME | NUMBER |
# STRING: '"' (not-slash-or-dquote | '\' anychar)* '"' |
-# NAME: (alphanum | '_' | '-' | '.')+ |
+# NAME: (alpha | '_') (alphanum | '_' | '-' | '.')* |
+# NUMBER: digit+ |
# BRACKET: '[[]' |
# COMMENT: '[#' not-rbracket* ']' |
# |
+# Note: the above BNR is a bit loose around ITEM/ARG/NAME/NUMBER. The |
+# important point is that the first value in a directive must |
+# start with '_' or an alpha character (no digits). This greatly |
+# helps to avoid simple errors like '[0]' in templates. |
+# |
# When used with the split() method, the return value will be composed of |
# non-matching text and the three paren groups (NEWLINE, DIRECTIVE and |
# BRACKET). Since the COMMENT matches are not placed into a group, they are |
# considered a "splitting" value and simply dropped. |
# |
-_item = r'(?:"(?:[^\\"]|\\.)*"|[-\w.]+)' |
+_item = r'(?:"(?:[^\\"]|\\.)*"|[A-Za-z_][-\w.]*)' |
+_arg = r'(?:"(?:[^\\"]|\\.)*"|[-\w.]+)' |
_re_parse = re.compile(r'(\r?\n)|\[(%s(?: +%s)*)\]|(\[\[\])|\[#[^\]]*\]' % |
- (_item, _item)) |
+ (_item, _arg)) |
_re_args = re.compile(r'"(?:[^\\"]|\\.)*"|[-\w.]+') |
@@ -290,7 +294,7 @@ class Template: |
to the file object 'fp' and functions are called. |
""" |
for step in program: |
- if isinstance(step, StringType): |
+ if isinstance(step, basestring): |
fp.write(step) |
else: |
method, method_args, filename, line_number = step |
@@ -388,7 +392,7 @@ class Template: |
((valref,), unused, section) = args |
list = _get_value(valref, ctx, filename, line_number) |
refname = valref[0] |
- if isinstance(list, StringType): |
+ if isinstance(list, basestring): |
raise NeedSequenceError(refname, filename, line_number) |
ctx.for_index[refname] = idx = [ list, 0 ] |
for item in list: |
@@ -398,7 +402,7 @@ class Template: |
def _cmd_define(self, args, fp, ctx, filename, line_number): |
((name,), unused, section) = args |
- valfp = cStringIO.StringIO() |
+ valfp = StringIO.StringIO() |
if section is not None: |
self._execute(section, valfp, ctx) |
ctx.defines[name] = valfp.getvalue() |
@@ -484,8 +488,11 @@ def _get_value((refname, start, rest), ctx, filename, line_number): |
# walk the rest of the dotted reference |
for attr in rest: |
try: |
- ob = getattr(ob, attr) |
- except AttributeError: |
+ if isinstance(ob, dict): |
+ ob = ob[attr] |
+ else: |
+ ob = getattr(ob, attr) |
+ except AttributeError, KeyError: |
raise UnknownReference(refname, filename, line_number) |
# make sure we return a string instead of some various Python types |