Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/c7n/varfmt.py: 12%
33 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:51 +0000
1from string import Formatter
2from c7n.utils import DeferredFormatString
5class VarFormat(Formatter):
6 """Behaves exactly like the stdlib formatter, with one additional behavior.
8 when a string has no format_spec and only contains a single expression,
9 retain the type of the source object.
11 inspired by https://pypyr.io/docs/substitutions/format-string/
12 """
14 def _vformat(
15 self, format_string, args, kwargs, used_args, recursion_depth, auto_arg_index=0
16 ):
17 # This is mostly verbatim from stdlib format.Formatter._vformat
18 # https://github.com/python/cpython/blob/main/Lib/string.py
19 #
20 # we have to copy alot of std logic to override the str cast
22 if recursion_depth < 0:
23 raise ValueError('Max string recursion exceeded')
24 result = []
25 for literal_text, field_name, format_spec, conversion in self.parse(
26 format_string
27 ):
29 # output the literal text
30 if literal_text:
31 result.append((literal_text, True, None))
33 # if there's a field, output it
34 if field_name is not None:
35 # this is some markup, find the object and do
36 # the formatting
38 # handle arg indexing when empty field_names are given.
39 if field_name == '':
40 if auto_arg_index is False:
41 raise ValueError(
42 'cannot switch from manual field '
43 'specification to automatic field '
44 'numbering'
45 )
46 field_name = str(auto_arg_index)
47 auto_arg_index += 1
48 elif field_name.isdigit():
49 if auto_arg_index:
50 raise ValueError(
51 'cannot switch from manual field '
52 'specification to automatic field '
53 'numbering'
54 )
55 # disable auto arg incrementing, if it gets
56 # used later on, then an exception will be raised
57 auto_arg_index = False
59 # given the field_name, find the object it references
60 # and the argument it came from
61 obj, arg_used = self.get_field(field_name, args, kwargs)
62 used_args.add(arg_used)
64 # do any conversion on the resulting object
65 obj = self.convert_field(obj, conversion)
67 # expand the format spec, if needed
68 format_spec, auto_arg_index = self._vformat(
69 format_spec,
70 args,
71 kwargs,
72 used_args,
73 recursion_depth - 1,
74 auto_arg_index=auto_arg_index,
75 )
77 # defer format
78 result.append((obj, False, format_spec))
80 # if input is a single expression (ie. '{expr}' don't cast
81 # source to string.
82 if len(result) == 1:
83 obj, is_literal, format_spec = result[0]
84 if is_literal:
85 return obj, auto_arg_index
86 if format_spec or isinstance(obj, DeferredFormatString):
87 return self.format_field(obj, format_spec), auto_arg_index
88 else:
89 return obj, auto_arg_index
90 else:
91 return (
92 ''.join(
93 [
94 obj if is_literal else self.format_field(obj, format_spec)
95 for obj, is_literal, format_spec in result
96 ]
97 ),
98 auto_arg_index,
99 )