Mercurial > repos > davidmurphy > codonlogo
comparison corebio/_future/_string.py @ 4:4d47ab2b7bcc
Uploaded
| author | davidmurphy |
|---|---|
| date | Fri, 13 Jan 2012 07:18:19 -0500 |
| parents | c55bdc2fb9fa |
| children |
comparison
equal
deleted
inserted
replaced
| 3:09d2dac9ef73 | 4:4d47ab2b7bcc |
|---|---|
| 1 #################################################################### | |
| 2 import re as _re | |
| 3 | |
| 4 class _multimap: | |
| 5 """Helper class for combining multiple mappings. | |
| 6 | |
| 7 Used by .{safe_,}substitute() to combine the mapping and keyword | |
| 8 arguments. | |
| 9 """ | |
| 10 def __init__(self, primary, secondary): | |
| 11 self._primary = primary | |
| 12 self._secondary = secondary | |
| 13 | |
| 14 def __getitem__(self, key): | |
| 15 try: | |
| 16 return self._primary[key] | |
| 17 except KeyError: | |
| 18 return self._secondary[key] | |
| 19 | |
| 20 | |
| 21 class _TemplateMetaclass(type): | |
| 22 pattern = r""" | |
| 23 %(delim)s(?: | |
| 24 (?P<escaped>%(delim)s) | # Escape sequence of two delimiters | |
| 25 (?P<named>%(id)s) | # delimiter and a Python identifier | |
| 26 {(?P<braced>%(id)s)} | # delimiter and a braced identifier | |
| 27 (?P<invalid>) # Other ill-formed delimiter exprs | |
| 28 ) | |
| 29 """ | |
| 30 | |
| 31 def __init__(cls, name, bases, dct): | |
| 32 super(_TemplateMetaclass, cls).__init__(name, bases, dct) | |
| 33 if 'pattern' in dct: | |
| 34 pattern = cls.pattern | |
| 35 else: | |
| 36 pattern = _TemplateMetaclass.pattern % { | |
| 37 'delim' : _re.escape(cls.delimiter), | |
| 38 'id' : cls.idpattern, | |
| 39 } | |
| 40 cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE) | |
| 41 | |
| 42 | |
| 43 class Template: | |
| 44 """A string class for supporting $-substitutions.""" | |
| 45 __metaclass__ = _TemplateMetaclass | |
| 46 | |
| 47 delimiter = '$' | |
| 48 idpattern = r'[_a-z][_a-z0-9]*' | |
| 49 | |
| 50 def __init__(self, template): | |
| 51 self.template = template | |
| 52 | |
| 53 # Search for $$, $identifier, ${identifier}, and any bare $'s | |
| 54 | |
| 55 def _invalid(self, mo): | |
| 56 i = mo.start('invalid') | |
| 57 lines = self.template[:i].splitlines(True) | |
| 58 if not lines: | |
| 59 colno = 1 | |
| 60 lineno = 1 | |
| 61 else: | |
| 62 colno = i - len(''.join(lines[:-1])) | |
| 63 lineno = len(lines) | |
| 64 raise ValueError('Invalid placeholder in string: line %d, col %d' % | |
| 65 (lineno, colno)) | |
| 66 | |
| 67 def substitute(self, *args, **kws): | |
| 68 if len(args) > 1: | |
| 69 raise TypeError('Too many positional arguments') | |
| 70 if not args: | |
| 71 mapping = kws | |
| 72 elif kws: | |
| 73 mapping = _multimap(kws, args[0]) | |
| 74 else: | |
| 75 mapping = args[0] | |
| 76 # Helper function for .sub() | |
| 77 def convert(mo): | |
| 78 # Check the most common path first. | |
| 79 named = mo.group('named') or mo.group('braced') | |
| 80 if named is not None: | |
| 81 val = mapping[named] | |
| 82 # We use this idiom instead of str() because the latter will | |
| 83 # fail if val is a Unicode containing non-ASCII characters. | |
| 84 return '%s' % val | |
| 85 if mo.group('escaped') is not None: | |
| 86 return self.delimiter | |
| 87 if mo.group('invalid') is not None: | |
| 88 self._invalid(mo) | |
| 89 raise ValueError('Unrecognized named group in pattern', | |
| 90 self.pattern) | |
| 91 return self.pattern.sub(convert, self.template) | |
| 92 | |
| 93 def safe_substitute(self, *args, **kws): | |
| 94 if len(args) > 1: | |
| 95 raise TypeError('Too many positional arguments') | |
| 96 if not args: | |
| 97 mapping = kws | |
| 98 elif kws: | |
| 99 mapping = _multimap(kws, args[0]) | |
| 100 else: | |
| 101 mapping = args[0] | |
| 102 # Helper function for .sub() | |
| 103 def convert(mo): | |
| 104 named = mo.group('named') | |
| 105 if named is not None: | |
| 106 try: | |
| 107 # We use this idiom instead of str() because the latter | |
| 108 # will fail if val is a Unicode containing non-ASCII | |
| 109 return '%s' % mapping[named] | |
| 110 except KeyError: | |
| 111 return self.delimiter + named | |
| 112 braced = mo.group('braced') | |
| 113 if braced is not None: | |
| 114 try: | |
| 115 return '%s' % mapping[braced] | |
| 116 except KeyError: | |
| 117 return self.delimiter + '{' + braced + '}' | |
| 118 if mo.group('escaped') is not None: | |
| 119 return self.delimiter | |
| 120 if mo.group('invalid') is not None: | |
| 121 return self.delimiter | |
| 122 raise ValueError('Unrecognized named group in pattern', | |
| 123 self.pattern) | |
| 124 return self.pattern.sub(convert, self.template) | |
| 125 | |
| 126 | |
| 127 |
