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