xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/string.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
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