1"""A collection of string constants. 2 3Public module variables: 4 5whitespace -- a string containing all ASCII whitespace 6ascii_lowercase -- a string containing all ASCII lowercase letters 7ascii_uppercase -- a string containing all ASCII uppercase letters 8ascii_letters -- a string containing all ASCII letters 9digits -- a string containing all ASCII decimal digits 10hexdigits -- a string containing all ASCII hexadecimal digits 11octdigits -- a string containing all ASCII octal digits 12punctuation -- a string containing all ASCII punctuation characters 13printable -- a string containing all ASCII characters considered printable 14 15""" 16 17__all__ = ["ascii_letters", "ascii_lowercase", "ascii_uppercase", "capwords", 18 "digits", "hexdigits", "octdigits", "printable", "punctuation", 19 "whitespace", "Formatter", "Template"] 20 21import _string 22 23# Some strings for ctype-style character classification 24whitespace = ' \t\n\r\v\f' 25ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz' 26ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 27ascii_letters = ascii_lowercase + ascii_uppercase 28digits = '0123456789' 29hexdigits = digits + 'abcdef' + 'ABCDEF' 30octdigits = '01234567' 31punctuation = r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" 32printable = digits + ascii_letters + punctuation + whitespace 33 34# Functions which aren't available as string methods. 35 36# Capitalize the words in a string, e.g. " aBc dEf " -> "Abc Def". 37def capwords(s, sep=None): 38 """capwords(s [,sep]) -> string 39 40 Split the argument into words using split, capitalize each 41 word using capitalize, and join the capitalized words using 42 join. If the optional second argument sep is absent or None, 43 runs of whitespace characters are replaced by a single space 44 and leading and trailing whitespace are removed, otherwise 45 sep is used to split and join the words. 46 47 """ 48 return (sep or ' ').join(x.capitalize() for x in s.split(sep)) 49 50 51#################################################################### 52import re as _re 53from collections import ChainMap as _ChainMap 54 55class _TemplateMetaclass(type): 56 pattern = r""" 57 %(delim)s(?: 58 (?P<escaped>%(delim)s) | # Escape sequence of two delimiters 59 (?P<named>%(id)s) | # delimiter and a Python identifier 60 {(?P<braced>%(id)s)} | # delimiter and a braced identifier 61 (?P<invalid>) # Other ill-formed delimiter exprs 62 ) 63 """ 64 65 def __init__(cls, name, bases, dct): 66 super(_TemplateMetaclass, cls).__init__(name, bases, dct) 67 if 'pattern' in dct: 68 pattern = cls.pattern 69 else: 70 pattern = _TemplateMetaclass.pattern % { 71 'delim' : _re.escape(cls.delimiter), 72 'id' : cls.idpattern, 73 } 74 cls.pattern = _re.compile(pattern, cls.flags | _re.VERBOSE) 75 76 77class Template(metaclass=_TemplateMetaclass): 78 """A string class for supporting $-substitutions.""" 79 80 delimiter = '$' 81 idpattern = r'[_a-z][_a-z0-9]*' 82 flags = _re.IGNORECASE 83 84 def __init__(self, template): 85 self.template = template 86 87 # Search for $$, $identifier, ${identifier}, and any bare $'s 88 89 def _invalid(self, mo): 90 i = mo.start('invalid') 91 lines = self.template[:i].splitlines(keepends=True) 92 if not lines: 93 colno = 1 94 lineno = 1 95 else: 96 colno = i - len(''.join(lines[:-1])) 97 lineno = len(lines) 98 raise ValueError('Invalid placeholder in string: line %d, col %d' % 99 (lineno, colno)) 100 101 def substitute(*args, **kws): 102 if not args: 103 raise TypeError("descriptor 'substitute' of 'Template' object " 104 "needs an argument") 105 self, *args = args # allow the "self" keyword be passed 106 if len(args) > 1: 107 raise TypeError('Too many positional arguments') 108 if not args: 109 mapping = kws 110 elif kws: 111 mapping = _ChainMap(kws, args[0]) 112 else: 113 mapping = args[0] 114 # Helper function for .sub() 115 def convert(mo): 116 # Check the most common path first. 117 named = mo.group('named') or mo.group('braced') 118 if named is not None: 119 return str(mapping[named]) 120 if mo.group('escaped') is not None: 121 return self.delimiter 122 if mo.group('invalid') is not None: 123 self._invalid(mo) 124 raise ValueError('Unrecognized named group in pattern', 125 self.pattern) 126 return self.pattern.sub(convert, self.template) 127 128 def safe_substitute(*args, **kws): 129 if not args: 130 raise TypeError("descriptor 'safe_substitute' of 'Template' object " 131 "needs an argument") 132 self, *args = args # allow the "self" keyword be passed 133 if len(args) > 1: 134 raise TypeError('Too many positional arguments') 135 if not args: 136 mapping = kws 137 elif kws: 138 mapping = _ChainMap(kws, args[0]) 139 else: 140 mapping = args[0] 141 # Helper function for .sub() 142 def convert(mo): 143 named = mo.group('named') or mo.group('braced') 144 if named is not None: 145 try: 146 return str(mapping[named]) 147 except KeyError: 148 return mo.group() 149 if mo.group('escaped') is not None: 150 return self.delimiter 151 if mo.group('invalid') is not None: 152 return mo.group() 153 raise ValueError('Unrecognized named group in pattern', 154 self.pattern) 155 return self.pattern.sub(convert, self.template) 156 157 158 159######################################################################## 160# the Formatter class 161# see PEP 3101 for details and purpose of this class 162 163# The hard parts are reused from the C implementation. They're exposed as "_" 164# prefixed methods of str. 165 166# The overall parser is implemented in _string.formatter_parser. 167# The field name parser is implemented in _string.formatter_field_name_split 168 169class Formatter: 170 def format(*args, **kwargs): 171 if not args: 172 raise TypeError("descriptor 'format' of 'Formatter' object " 173 "needs an argument") 174 self, *args = args # allow the "self" keyword be passed 175 try: 176 format_string, *args = args # allow the "format_string" keyword be passed 177 except ValueError: 178 if 'format_string' in kwargs: 179 format_string = kwargs.pop('format_string') 180 import warnings 181 warnings.warn("Passing 'format_string' as keyword argument is " 182 "deprecated", DeprecationWarning, stacklevel=2) 183 else: 184 raise TypeError("format() missing 1 required positional " 185 "argument: 'format_string'") from None 186 return self.vformat(format_string, args, kwargs) 187 188 def vformat(self, format_string, args, kwargs): 189 used_args = set() 190 result, _ = self._vformat(format_string, args, kwargs, used_args, 2) 191 self.check_unused_args(used_args, args, kwargs) 192 return result 193 194 def _vformat(self, format_string, args, kwargs, used_args, recursion_depth, 195 auto_arg_index=0): 196 if recursion_depth < 0: 197 raise ValueError('Max string recursion exceeded') 198 result = [] 199 for literal_text, field_name, format_spec, conversion in \ 200 self.parse(format_string): 201 202 # output the literal text 203 if literal_text: 204 result.append(literal_text) 205 206 # if there's a field, output it 207 if field_name is not None: 208 # this is some markup, find the object and do 209 # the formatting 210 211 # handle arg indexing when empty field_names are given. 212 if field_name == '': 213 if auto_arg_index is False: 214 raise ValueError('cannot switch from manual field ' 215 'specification to automatic field ' 216 'numbering') 217 field_name = str(auto_arg_index) 218 auto_arg_index += 1 219 elif field_name.isdigit(): 220 if auto_arg_index: 221 raise ValueError('cannot switch from manual field ' 222 'specification to automatic field ' 223 'numbering') 224 # disable auto arg incrementing, if it gets 225 # used later on, then an exception will be raised 226 auto_arg_index = False 227 228 # given the field_name, find the object it references 229 # and the argument it came from 230 obj, arg_used = self.get_field(field_name, args, kwargs) 231 used_args.add(arg_used) 232 233 # do any conversion on the resulting object 234 obj = self.convert_field(obj, conversion) 235 236 # expand the format spec, if needed 237 format_spec, auto_arg_index = self._vformat( 238 format_spec, args, kwargs, 239 used_args, recursion_depth-1, 240 auto_arg_index=auto_arg_index) 241 242 # format the object and append to the result 243 result.append(self.format_field(obj, format_spec)) 244 245 return ''.join(result), auto_arg_index 246 247 248 def get_value(self, key, args, kwargs): 249 if isinstance(key, int): 250 return args[key] 251 else: 252 return kwargs[key] 253 254 255 def check_unused_args(self, used_args, args, kwargs): 256 pass 257 258 259 def format_field(self, value, format_spec): 260 return format(value, format_spec) 261 262 263 def convert_field(self, value, conversion): 264 # do any conversion on the resulting object 265 if conversion is None: 266 return value 267 elif conversion == 's': 268 return str(value) 269 elif conversion == 'r': 270 return repr(value) 271 elif conversion == 'a': 272 return ascii(value) 273 raise ValueError("Unknown conversion specifier {0!s}".format(conversion)) 274 275 276 # returns an iterable that contains tuples of the form: 277 # (literal_text, field_name, format_spec, conversion) 278 # literal_text can be zero length 279 # field_name can be None, in which case there's no 280 # object to format and output 281 # if field_name is not None, it is looked up, formatted 282 # with format_spec and conversion and then used 283 def parse(self, format_string): 284 return _string.formatter_parser(format_string) 285 286 287 # given a field_name, find the object it references. 288 # field_name: the field being looked up, e.g. "0.name" 289 # or "lookup[3]" 290 # used_args: a set of which args have been used 291 # args, kwargs: as passed in to vformat 292 def get_field(self, field_name, args, kwargs): 293 first, rest = _string.formatter_field_name_split(field_name) 294 295 obj = self.get_value(first, args, kwargs) 296 297 # loop through the rest of the field_name, doing 298 # getattr or getitem as needed 299 for is_attr, i in rest: 300 if is_attr: 301 obj = getattr(obj, i) 302 else: 303 obj = obj[i] 304 305 return obj, first 306