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