this repo has no description
1#!/usr/bin/env python3
2# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
3
4from _builtins import _str_from_str, _str_guard, _str_len
5
6
7_empty_iterator = iter(())
8
9
10class _SubscriptParser:
11 def __init__(self, field_name, idx):
12 self.field_name = field_name
13 self.idx = idx
14
15 def __iter__(self):
16 return self
17
18 def __next__(self):
19 field_name = self.field_name
20 if not field_name:
21 raise StopIteration
22 if field_name[0] != "[" and field_name[0] != ".":
23 raise ValueError("Only '.' or '[' may follow ']' in format field specifier")
24 if field_name[0] == ".":
25 right_idx = find_arg_end(field_name[1:])
26 subscript = (
27 field_name[self.idx + 1 :]
28 if right_idx == -1
29 else field_name[self.idx + 1 : right_idx + 1]
30 )
31 if subscript == "":
32 raise ValueError("Empty attribute in format string")
33 self.field_name = "" if right_idx == -1 else field_name[right_idx + 1 :]
34 self.idx = 0
35 return (True, subscript)
36 right_idx = field_name.find("]")
37 if right_idx == -1:
38 raise ValueError("Missing ']' in format string")
39 subscript = field_name[self.idx + 1 : right_idx]
40 if subscript == "":
41 raise ValueError("Empty attribute in format string")
42 self.field_name = field_name[right_idx + 1 :]
43 self.idx = 0
44 return (False, int(subscript) if subscript.isdigit() else subscript)
45
46
47def find_arg_end(field_name):
48 """
49 Returns the index of the end of an attribute name or element index.
50 field_name: the field being looked up, e.g. "0.name"
51 or "lookup[3]"
52 """
53 dot_idx = field_name.find(".")
54 lbrack_idx = field_name.find("[")
55 if dot_idx == -1:
56 return lbrack_idx
57 if lbrack_idx == -1:
58 return dot_idx
59 return min(dot_idx, lbrack_idx)
60
61
62def formatter_field_name_split(field_name):
63 _str_guard(field_name)
64
65 idx = find_arg_end(field_name)
66 if idx != -1:
67 field = field_name[:idx]
68 if field.isdigit():
69 field = int(field)
70 elif not field:
71 # In order to accept "[0]" as an implicit "0[0]", return 0 instead
72 # of the empty string here. See https://bugs.python.org/issue39985
73 # for more detail.
74 field = 0
75 return (field, _SubscriptParser(field_name[idx:], 0))
76
77 if field_name.isdigit():
78 field_name = int(field_name)
79 else:
80 field_name = _str_from_str(str, field_name)
81 return (field_name, _empty_iterator)
82
83
84def formatter_parser(string): # noqa: C901
85 _str_guard(string)
86 idx = -1
87 fragment_begin = 0
88 it = str.__iter__(string)
89 end_error = None
90 try:
91 while True:
92 idx += 1
93 ch = it.__next__()
94 if ch == "}":
95 end_error = "Single '}' encountered in format string"
96 idx += 1
97 next_ch = it.__next__()
98
99 if next_ch != "}":
100 raise ValueError(end_error)
101 yield (string[fragment_begin:idx], None, None, None)
102 end_error = None
103 fragment_begin = idx + 1
104 continue
105 if ch != "{":
106 continue
107
108 end_error = "Single '{' encountered in format string"
109 idx += 1
110 next_ch = it.__next__()
111
112 if next_ch == "{":
113 yield (string[fragment_begin:idx], None, None, None)
114 end_error = None
115 fragment_begin = idx + 1
116 continue
117
118 fragment = string[fragment_begin : idx - 1]
119
120 ch = next_ch
121 id_begin = idx
122 end_error = "expected '}' before end of string"
123 while ch != "}" and ch != "!" and ch != ":":
124 if ch == "{":
125 raise ValueError("unexpected '{' in field name")
126 if ch == "[":
127 while True:
128 idx += 1
129 ch = it.__next__()
130 if ch == "]":
131 break
132 idx += 1
133 ch = it.__next__()
134 ident = string[id_begin:idx]
135
136 if ch == "!":
137 end_error = "end of string while looking for conversion specifier"
138 idx += 1
139 ch = it.__next__()
140 end_error = "unmatched '{' in format spec"
141 conversion = ch
142
143 idx += 1
144 ch = it.__next__()
145 if ch != ":" and ch != "}":
146 raise ValueError("expected ':' after conversion specifier")
147 else:
148 conversion = None
149
150 spec = ""
151 if ch == ":":
152 spec_begin = idx + 1
153 end_error = "unmatched '{' in format spec"
154 curly_count = 1
155 while True:
156 idx += 1
157 ch = it.__next__()
158 if ch == "{":
159 curly_count += 1
160 continue
161 if ch == "}":
162 curly_count -= 1
163 if curly_count == 0:
164 spec = string[spec_begin:idx]
165 break
166
167 yield (fragment, ident, spec, conversion)
168 fragment_begin = idx + 1
169 end_error = None
170 except StopIteration:
171 if end_error is not None:
172 raise ValueError(end_error)
173 # Make sure everyone called `idx += 1` before `it.__next__()`.
174 assert idx == _str_len(string)
175
176 if idx - fragment_begin > 0:
177 yield (string[fragment_begin:idx], None, None, None)