a collection of string constants public module variables whitespace a string containing all ascii whitespace asciilowercase a string containing all ascii lowercase letters asciiuppercase a string containing all ascii uppercase letters asciiletters a string containing all ascii letters digits a string containing all ascii decimal digits hexdigits a string containing all ascii hexadecimal digits octdigits a string containing all ascii octal digits punctuation a string containing all ascii punctuation characters printable a string containing all ascii characters considered printable some strings for ctypestyle character classification functions which aren t available as string methods capitalize the words in a string e g abc def abc def capwordss sep string split the argument into words using split capitalize each word using capitalize and join the capitalized words using join if the optional second argument sep is absent or none runs of whitespace characters are replaced by a single space and leading and trailing whitespace are removed otherwise sep is used to split and join the words a string class for supporting substitutions delimiter r az matches to nonascii letters when used with ignorecase but without the ascii flag we can t add re ascii to flags because of backward compatibility so we use the a local flag and az pattern see https bugs python orgissue31672 idpattern r a azaz09 braceidpattern none flags re ignorecase def initsubclasscls super initsubclass if pattern in cls dict pattern cls pattern else delim re escapecls delimiter id cls idpattern bid cls braceidpattern or cls idpattern pattern fr delim pescapeddelim escape sequence of two delimiters pnamedid delimiter and a python identifier pbracedbid delimiter and a braced identifier pinvalid other illformed delimiter exprs search for identifier identifier and any bare s helper function for sub check the most common path first helper function for sub if all the groups are none there must be another group we re not expecting add a named group only the first time it appears if all the groups are none there must be another group we re not expecting initialize template pattern initsubclass is automatically called only for subclasses not for the template class itself the formatter class see pep 3101 for details and purpose of this class the hard parts are reused from the c implementation they re exposed as prefixed methods of str the overall parser is implemented in string formatterparser the field name parser is implemented in string formatterfieldnamesplit output the literal text if there s a field output it this is some markup find the object and do the formatting handle arg indexing when empty fieldnames are given disable auto arg incrementing if it gets used later on then an exception will be raised given the fieldname find the object it references and the argument it came from do any conversion on the resulting object expand the format spec if needed format the object and append to the result do any conversion on the resulting object returns an iterable that contains tuples of the form literaltext fieldname formatspec conversion literaltext can be zero length fieldname can be none in which case there s no object to format and output if fieldname is not none it is looked up formatted with formatspec and conversion and then used given a fieldname find the object it references fieldname the field being looked up e g 0 name or lookup3 usedargs a set of which args have been used args kwargs as passed in to vformat loop through the rest of the fieldname doing getattr or getitem as needed some strings for ctype style character classification _ functions which aren t available as string methods capitalize the words in a string e g abc def abc def capwords s sep string split the argument into words using split capitalize each word using capitalize and join the capitalized words using join if the optional second argument sep is absent or none runs of whitespace characters are replaced by a single space and leading and trailing whitespace are removed otherwise sep is used to split and join the words a string class for supporting substitutions r a z matches to non ascii letters when used with ignorecase but without the ascii flag we can t add re ascii to flags because of backward compatibility so we use the a local flag and a z pattern see https bugs python org issue31672 delim p escaped delim escape sequence of two delimiters p named id delimiter and a python identifier p braced bid delimiter and a braced identifier p invalid other ill formed delimiter exprs search for identifier identifier and any bare s helper function for sub check the most common path first helper function for sub if all the groups are none there must be another group we re not expecting add a named group only the first time it appears if all the groups are none there must be another group we re not expecting initialize template pattern __init_subclass__ is automatically called only for subclasses not for the template class itself the formatter class see pep 3101 for details and purpose of this class the hard parts are reused from the c implementation they re exposed as _ prefixed methods of str the overall parser is implemented in _string formatter_parser the field name parser is implemented in _string formatter_field_name_split output the literal text if there s a field output it this is some markup find the object and do the formatting handle arg indexing when empty field_names are given disable auto arg incrementing if it gets used later on then an exception will be raised given the field_name find the object it references and the argument it came from do any conversion on the resulting object expand the format spec if needed format the object and append to the result do any conversion on the resulting object returns an iterable that contains tuples of the form literal_text field_name format_spec conversion literal_text can be zero length field_name can be none in which case there s no object to format and output if field_name is not none it is looked up formatted with format_spec and conversion and then used given a field_name find the object it references field_name the field being looked up e g 0 name or lookup 3 used_args a set of which args have been used args kwargs as passed in to vformat loop through the rest of the field_name doing getattr or getitem as needed
__all__ = ["ascii_letters", "ascii_lowercase", "ascii_uppercase", "capwords",
           "digits", "hexdigits", "octdigits", "printable", "punctuation",
           "whitespace", "Formatter", "Template"]

import _string


whitespace = ' \t\n\r\v\f'
ascii_lowercase = 'abcdefghijklmnopqrstuvwxyz'
ascii_uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
ascii_letters = ascii_lowercase + ascii_uppercase
digits = '0123456789'
hexdigits = digits + 'abcdef' + 'ABCDEF'
octdigits = '01234567'
punctuation = r
printable = digits + ascii_letters + punctuation + whitespace




def capwords(s, sep=None):
    
    return (sep or ' ').join(map(str.capitalize, s.split(sep)))



import re as _re
from collections import ChainMap as _ChainMap

_sentinel_dict = {}

class Template:
    

    delimiter = '$'
    
    
    
    
    idpattern = r'(?a:[_a-z][_a-z0-9]*)'
    braceidpattern = None
    flags = _re.IGNORECASE

    def __init_subclass__(cls):
        super().__init_subclass__()
        if 'pattern' in cls.__dict__:
            pattern = cls.pattern
        else:
            delim = _re.escape(cls.delimiter)
            id = cls.idpattern
            bid = cls.braceidpattern or cls.idpattern
            pattern = fr
        cls.pattern = _re.compile(pattern, cls.flags | _re.VERBOSE)

    def __init__(self, template):
        self.template = template

    

    def _invalid(self, mo):
        i = mo.start('invalid')
        lines = self.template[:i].splitlines(keepends=True)
        if not lines:
            colno = 1
            lineno = 1
        else:
            colno = i - len(''.join(lines[:-1]))
            lineno = len(lines)
        raise ValueError('Invalid placeholder in string: line %d, col %d' %
                         (lineno, colno))

    def substitute(self, mapping=_sentinel_dict, /, **kws):
        if mapping is _sentinel_dict:
            mapping = kws
        elif kws:
            mapping = _ChainMap(kws, mapping)
        
        def convert(mo):
            
            named = mo.group('named') or mo.group('braced')
            if named is not None:
                return str(mapping[named])
            if mo.group('escaped') is not None:
                return self.delimiter
            if mo.group('invalid') is not None:
                self._invalid(mo)
            raise ValueError('Unrecognized named group in pattern',
                             self.pattern)
        return self.pattern.sub(convert, self.template)

    def safe_substitute(self, mapping=_sentinel_dict, /, **kws):
        if mapping is _sentinel_dict:
            mapping = kws
        elif kws:
            mapping = _ChainMap(kws, mapping)
        
        def convert(mo):
            named = mo.group('named') or mo.group('braced')
            if named is not None:
                try:
                    return str(mapping[named])
                except KeyError:
                    return mo.group()
            if mo.group('escaped') is not None:
                return self.delimiter
            if mo.group('invalid') is not None:
                return mo.group()
            raise ValueError('Unrecognized named group in pattern',
                             self.pattern)
        return self.pattern.sub(convert, self.template)

    def is_valid(self):
        for mo in self.pattern.finditer(self.template):
            if mo.group('invalid') is not None:
                return False
            if (mo.group('named') is None
                and mo.group('braced') is None
                and mo.group('escaped') is None):
                
                
                raise ValueError('Unrecognized named group in pattern',
                    self.pattern)
        return True

    def get_identifiers(self):
        ids = []
        for mo in self.pattern.finditer(self.template):
            named = mo.group('named') or mo.group('braced')
            if named is not None and named not in ids:
                
                ids.append(named)
            elif (named is None
                and mo.group('invalid') is None
                and mo.group('escaped') is None):
                
                
                raise ValueError('Unrecognized named group in pattern',
                    self.pattern)
        return ids



Template.__init_subclass__()












class Formatter:
    def format(self, format_string, /, *args, **kwargs):
        return self.vformat(format_string, args, kwargs)

    def vformat(self, format_string, args, kwargs):
        used_args = set()
        result, _ = self._vformat(format_string, args, kwargs, used_args, 2)
        self.check_unused_args(used_args, args, kwargs)
        return result

    def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
                 auto_arg_index=0):
        if recursion_depth < 0:
            raise ValueError('Max string recursion exceeded')
        result = []
        for literal_text, field_name, format_spec, conversion in \
                self.parse(format_string):

            
            if literal_text:
                result.append(literal_text)

            
            if field_name is not None:
                
                

                
                if field_name == '':
                    if auto_arg_index is False:
                        raise ValueError('cannot switch from manual field '
                                         'specification to automatic field '
                                         'numbering')
                    field_name = str(auto_arg_index)
                    auto_arg_index += 1
                elif field_name.isdigit():
                    if auto_arg_index:
                        raise ValueError('cannot switch from manual field '
                                         'specification to automatic field '
                                         'numbering')
                    
                    
                    auto_arg_index = False

                
                
                obj, arg_used = self.get_field(field_name, args, kwargs)
                used_args.add(arg_used)

                
                obj = self.convert_field(obj, conversion)

                
                format_spec, auto_arg_index = self._vformat(
                    format_spec, args, kwargs,
                    used_args, recursion_depth-1,
                    auto_arg_index=auto_arg_index)

                
                result.append(self.format_field(obj, format_spec))

        return ''.join(result), auto_arg_index


    def get_value(self, key, args, kwargs):
        if isinstance(key, int):
            return args[key]
        else:
            return kwargs[key]


    def check_unused_args(self, used_args, args, kwargs):
        pass


    def format_field(self, value, format_spec):
        return format(value, format_spec)


    def convert_field(self, value, conversion):
        
        if conversion is None:
            return value
        elif conversion == 's':
            return str(value)
        elif conversion == 'r':
            return repr(value)
        elif conversion == 'a':
            return ascii(value)
        raise ValueError("Unknown conversion specifier {0!s}".format(conversion))


    
    
    
    
    
    
    
    def parse(self, format_string):
        return _string.formatter_parser(format_string)


    
    
    
    
    
    def get_field(self, field_name, args, kwargs):
        first, rest = _string.formatter_field_name_split(field_name)

        obj = self.get_value(first, args, kwargs)

        
        
        for is_attr, i in rest:
            if is_attr:
                obj = getattr(obj, i)
            else:
                obj = obj[i]

        return obj, first