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

1from string import Formatter 

2from c7n.utils import DeferredFormatString 

3 

4 

5class VarFormat(Formatter): 

6 """Behaves exactly like the stdlib formatter, with one additional behavior. 

7 

8 when a string has no format_spec and only contains a single expression, 

9 retain the type of the source object. 

10 

11 inspired by https://pypyr.io/docs/substitutions/format-string/ 

12 """ 

13 

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 

21 

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 ): 

28 

29 # output the literal text 

30 if literal_text: 

31 result.append((literal_text, True, None)) 

32 

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 

37 

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 

58 

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) 

63 

64 # do any conversion on the resulting object 

65 obj = self.convert_field(obj, conversion) 

66 

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 ) 

76 

77 # defer format 

78 result.append((obj, False, format_spec)) 

79 

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 )