diff options
Diffstat (limited to 'Lib')
| -rw-r--r-- | Lib/sre.py | 9 | ||||
| -rw-r--r-- | Lib/sre_constants.py | 3 | ||||
| -rw-r--r-- | Lib/sre_parse.py | 34 | ||||
| -rw-r--r-- | Lib/string.py | 140 | ||||
| -rw-r--r-- | Lib/test/test_pep292.py | 84 |
5 files changed, 205 insertions, 65 deletions
@@ -105,9 +105,6 @@ __all__ = [ "match", "search", "sub", "subn", "split", "findall", __version__ = "2.2.1" -# this module works under 1.5.2 and later. don't use string methods -import string - # flags I = IGNORECASE = sre_compile.SRE_FLAG_IGNORECASE # ignore case L = LOCALE = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit locale @@ -201,7 +198,7 @@ def escape(pattern): s[i] = "\\000" else: s[i] = "\\" + c - return _join(s, pattern) + return pattern[:0].join(s) # -------------------------------------------------------------------- # internals @@ -213,10 +210,6 @@ _pattern_type = type(sre_compile.compile("", 0)) _MAXCACHE = 100 -def _join(seq, sep): - # internal: join into string having the same type as sep - return string.join(seq, sep[:0]) - def _compile(*key): # internal: compile pattern cachekey = (type(key[0]),) + key diff --git a/Lib/sre_constants.py b/Lib/sre_constants.py index 002b195..1863f48 100644 --- a/Lib/sre_constants.py +++ b/Lib/sre_constants.py @@ -217,12 +217,11 @@ SRE_INFO_LITERAL = 2 # entire pattern is literal (given by prefix) SRE_INFO_CHARSET = 4 # pattern starts with character from given set if __name__ == "__main__": - import string def dump(f, d, prefix): items = d.items() items.sort(key=lambda a: a[1]) for k, v in items: - f.write("#define %s_%s %s\n" % (prefix, string.upper(k), v)) + f.write("#define %s_%s %s\n" % (prefix, k.upper(), v)) f = open("sre_constants.h", "w") f.write("""\ /* diff --git a/Lib/sre_parse.py b/Lib/sre_parse.py index 94d526d..5c4298a 100644 --- a/Lib/sre_parse.py +++ b/Lib/sre_parse.py @@ -12,8 +12,7 @@ # XXX: show string offset and offending character for all errors -# this module works under 1.5.2 and later. don't use string methods -import string, sys +import sys from sre_constants import * @@ -63,13 +62,6 @@ FLAGS = { "u": SRE_FLAG_UNICODE, } -# figure out best way to convert hex/octal numbers to integers -try: - int("10", 8) - atoi = int # 2.0 and later -except TypeError: - atoi = string.atoi # 1.5.2 - class Pattern: # master pattern object. keeps track of global attributes def __init__(self): @@ -233,7 +225,7 @@ def isname(name): def _group(escape, groups): # check if the escape string represents a valid group try: - gid = atoi(escape[1:]) + gid = int(escape[1:]) if gid and gid < groups: return gid except ValueError: @@ -256,13 +248,13 @@ def _class_escape(source, escape): escape = escape[2:] if len(escape) != 2: raise error, "bogus escape: %s" % repr("\\" + escape) - return LITERAL, atoi(escape, 16) & 0xff + return LITERAL, int(escape, 16) & 0xff elif escape[1:2] in OCTDIGITS: # octal escape (up to three digits) while source.next in OCTDIGITS and len(escape) < 5: escape = escape + source.get() escape = escape[1:] - return LITERAL, atoi(escape, 8) & 0xff + return LITERAL, int(escape, 8) & 0xff if len(escape) == 2: return LITERAL, ord(escape[1]) except ValueError: @@ -284,12 +276,12 @@ def _escape(source, escape, state): escape = escape + source.get() if len(escape) != 4: raise ValueError - return LITERAL, atoi(escape[2:], 16) & 0xff + return LITERAL, int(escape[2:], 16) & 0xff elif escape[1:2] == "0": # octal escape while source.next in OCTDIGITS and len(escape) < 4: escape = escape + source.get() - return LITERAL, atoi(escape[1:], 8) & 0xff + return LITERAL, int(escape[1:], 8) & 0xff elif escape[1:2] in DIGITS: # octal escape *or* decimal group reference (sigh) if source.next in DIGITS: @@ -298,7 +290,7 @@ def _escape(source, escape, state): source.next in OCTDIGITS): # got three octal digits; this is an octal escape escape = escape + source.get() - return LITERAL, atoi(escape[1:], 8) & 0xff + return LITERAL, int(escape[1:], 8) & 0xff # got at least one decimal digit; this is a group reference group = _group(escape, state.groups) if group: @@ -503,9 +495,9 @@ def _parse(source, state): source.seek(here) continue if lo: - min = atoi(lo) + min = int(lo) if hi: - max = atoi(hi) + max = int(hi) if max < min: raise error, "bad repeat interval" else: @@ -617,7 +609,7 @@ def _parse(source, state): raise error, "unknown group name" else: try: - condgroup = atoi(condname) + condgroup = int(condname) except ValueError: raise error, "bad character in group name" else: @@ -730,7 +722,7 @@ def parse_template(source, pattern): if not name: raise error, "bad group name" try: - index = atoi(name) + index = int(name) except ValueError: if not isname(name): raise error, "bad character in group name" @@ -754,7 +746,7 @@ def parse_template(source, pattern): break if not code: this = this[1:] - code = LITERAL, makechar(atoi(this[-6:], 8) & 0xff) + code = LITERAL, makechar(int(this[-6:], 8) & 0xff) if code[0] is LITERAL: literal(code[1]) else: @@ -793,4 +785,4 @@ def expand_template(template, match): raise IndexError except IndexError: raise error, "empty group" - return string.join(literals, sep) + return sep.join(literals) diff --git a/Lib/string.py b/Lib/string.py index bc10c20..d166f38 100644 --- a/Lib/string.py +++ b/Lib/string.py @@ -35,10 +35,116 @@ printable = digits + letters + punctuation + whitespace # Case conversion helpers # Use str to convert Unicode literal in case of -U +# Note that Cookie.py bogusly uses _idmap :( l = map(chr, xrange(256)) _idmap = str('').join(l) del l +# Functions which aren't available as string methods. + +# Capitalize the words in a string, e.g. " aBc dEf " -> "Abc Def". +# See also regsub.capwords(). +def capwords(s, sep=None): + """capwords(s, [sep]) -> string + + Split the argument into words using split, capitalize each + word using capitalize, and join the capitalized words using + join. Note that this replaces runs of whitespace characters by + a single space. + + """ + return (sep or ' ').join([x.capitalize() for x in s.split(sep)]) + + +# Construct a translation string +_idmapL = None +def maketrans(fromstr, tostr): + """maketrans(frm, to) -> string + + Return a translation table (a string of 256 bytes long) + suitable for use in string.translate. The strings frm and to + must be of the same length. + + """ + if len(fromstr) != len(tostr): + raise ValueError, "maketrans arguments must have same length" + global _idmapL + if not _idmapL: + _idmapL = map(None, _idmap) + L = _idmapL[:] + fromstr = map(ord, fromstr) + for i in range(len(fromstr)): + L[fromstr[i]] = tostr[i] + return ''.join(L) + + + +import re as _re + +class Template(unicode): + """A string class for supporting $-substitutions.""" + __slots__ = [] + + # Search for $$, $identifier, ${identifier}, and any bare $'s + pattern = _re.compile(r""" +# Match exactly two $'s -- this is the escape sequence +(?P<escaped>\${2})| +# Match a $ followed by a Python identifier +\$(?P<named>[_a-z][_a-z0-9]*)| +# Match a $ followed by a brace delimited identifier +\${(?P<braced>[_a-z][_a-z0-9]*)}| +# Match any other $'s +(?P<bogus>\$) +""", _re.IGNORECASE | _re.VERBOSE) + + def __mod__(self, mapping): + def convert(mo): + groups = mo.groupdict() + if groups.get('escaped') is not None: + return '$' + if groups.get('bogus') is not None: + raise ValueError('Invalid placeholder at index %d' % + mo.start('bogus')) + val = mapping[groups.get('named') or groups.get('braced')] + return unicode(val) + return self.pattern.sub(convert, self) + + +class SafeTemplate(Template): + """A string class for supporting $-substitutions. + + This class is 'safe' in the sense that you will never get KeyErrors if + there are placeholders missing from the interpolation dictionary. In that + case, you will get the original placeholder in the value string. + """ + __slots__ = [] + + def __mod__(self, mapping): + def convert(mo): + groups = mo.groupdict() + if groups.get('escaped') is not None: + return '$' + if groups.get('bogus') is not None: + raise ValueError('Invalid placeholder at index %d' % + mo.start('bogus')) + named = groups.get('named') + if named is not None: + try: + return unicode(mapping[named]) + except KeyError: + return '$' + named + braced = groups.get('braced') + try: + return unicode(mapping[braced]) + except KeyError: + return '${' + braced + '}' + return self.pattern.sub(convert, self) + + + +# NOTE: Everything below here is deprecated. Use string methods instead. +# This stuff will go away in Python 3.0. + # Backward compatible names for exceptions index_error = ValueError atoi_error = ValueError @@ -336,40 +442,6 @@ def capitalize(s): """ return s.capitalize() -# Capitalize the words in a string, e.g. " aBc dEf " -> "Abc Def". -# See also regsub.capwords(). -def capwords(s, sep=None): - """capwords(s, [sep]) -> string - - Split the argument into words using split, capitalize each - word using capitalize, and join the capitalized words using - join. Note that this replaces runs of whitespace characters by - a single space. - - """ - return join(map(capitalize, s.split(sep)), sep or ' ') - -# Construct a translation string -_idmapL = None -def maketrans(fromstr, tostr): - """maketrans(frm, to) -> string - - Return a translation table (a string of 256 bytes long) - suitable for use in string.translate. The strings frm and to - must be of the same length. - - """ - if len(fromstr) != len(tostr): - raise ValueError, "maketrans arguments must have same length" - global _idmapL - if not _idmapL: - _idmapL = map(None, _idmap) - L = _idmapL[:] - fromstr = map(ord, fromstr) - for i in range(len(fromstr)): - L[fromstr[i]] = tostr[i] - return join(L, "") - # Substring replacement (global) def replace(s, old, new, maxsplit=-1): """replace (str, old, new[, maxsplit]) -> string diff --git a/Lib/test/test_pep292.py b/Lib/test/test_pep292.py new file mode 100644 index 0000000..7eff309 --- /dev/null +++ b/Lib/test/test_pep292.py @@ -0,0 +1,84 @@ +# Copyright (C) 2004 Python Software Foundation +# Author: barry@python.org (Barry Warsaw) +# License: http://www.opensource.org/licenses/PythonSoftFoundation.php + +import unittest +from string import Template, SafeTemplate + +class TestTemplate(unittest.TestCase): + + def test_regular_templates(self): + s = Template('$who likes to eat a bag of $what worth $$100') + self.assertEqual(s % dict(who='tim', what='ham'), + 'tim likes to eat a bag of ham worth $100') + self.assertRaises(KeyError, lambda s, d: s % d, s, dict(who='tim')) + + def test_regular_templates_with_braces(self): + s = Template('$who likes ${what} for ${meal}') + self.assertEqual(s % dict(who='tim', what='ham', meal='dinner'), + 'tim likes ham for dinner') + self.assertRaises(KeyError, lambda s, d: s % d, + s, dict(who='tim', what='ham')) + + def test_escapes(self): + eq = self.assertEqual + s = Template('$who likes to eat a bag of $$what worth $$100') + eq(s % dict(who='tim', what='ham'), + 'tim likes to eat a bag of $what worth $100') + s = Template('$who likes $$') + eq(s % dict(who='tim', what='ham'), 'tim likes $') + + def test_percents(self): + s = Template('%(foo)s $foo ${foo}') + self.assertEqual(s % dict(foo='baz'), '%(foo)s baz baz') + s = SafeTemplate('%(foo)s $foo ${foo}') + self.assertEqual(s % dict(foo='baz'), '%(foo)s baz baz') + + def test_stringification(self): + s = Template('tim has eaten $count bags of ham today') + self.assertEqual(s % dict(count=7), + 'tim has eaten 7 bags of ham today') + s = SafeTemplate('tim has eaten $count bags of ham today') + self.assertEqual(s % dict(count=7), + 'tim has eaten 7 bags of ham today') + s = SafeTemplate('tim has eaten ${count} bags of ham today') + self.assertEqual(s % dict(count=7), + 'tim has eaten 7 bags of ham today') + + def test_SafeTemplate(self): + eq = self.assertEqual + s = SafeTemplate('$who likes ${what} for ${meal}') + eq(s % dict(who='tim'), + 'tim likes ${what} for ${meal}') + eq(s % dict(what='ham'), + '$who likes ham for ${meal}') + eq(s % dict(what='ham', meal='dinner'), + '$who likes ham for dinner') + eq(s % dict(who='tim', what='ham'), + 'tim likes ham for ${meal}') + eq(s % dict(who='tim', what='ham', meal='dinner'), + 'tim likes ham for dinner') + + def test_invalid_placeholders(self): + raises = self.assertRaises + s = Template('$who likes $') + raises(ValueError, lambda s, d: s % d, s, dict(who='tim')) + s = Template('$who likes ${what)') + raises(ValueError, lambda s, d: s % d, s, dict(who='tim')) + s = Template('$who likes $100') + raises(ValueError, lambda s, d: s % d, s, dict(who='tim')) + + +def suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestTemplate)) + return suite + + +def test_main(): + from test import test_support + test_support.run_suite(suite()) + + +if __name__ == '__main__': + unittest.main() |
