(self, node)
| 1153 | self.write(f"{quote_type}{string}{quote_type}") |
| 1154 | |
| 1155 | def visit_JoinedStr(self, node): |
| 1156 | self.write("f") |
| 1157 | if self._avoid_backslashes: |
| 1158 | with self.buffered() as buffer: |
| 1159 | self._write_fstring_inner(node) |
| 1160 | return self._write_str_avoiding_backslashes("".join(buffer)) |
| 1161 | |
| 1162 | # If we don't need to avoid backslashes globally (i.e., we only need |
| 1163 | # to avoid them inside FormattedValues), it's cosmetically preferred |
| 1164 | # to use escaped whitespace. That is, it's preferred to use backslashes |
| 1165 | # for cases like: f"{x}\n". To accomplish this, we keep track of what |
| 1166 | # in our buffer corresponds to FormattedValues and what corresponds to |
| 1167 | # Constant parts of the f-string, and allow escapes accordingly. |
| 1168 | fstring_parts = [] |
| 1169 | for value in node.values: |
| 1170 | with self.buffered() as buffer: |
| 1171 | self._write_fstring_inner(value) |
| 1172 | fstring_parts.append( |
| 1173 | ("".join(buffer), isinstance(value, Constant)) |
| 1174 | ) |
| 1175 | |
| 1176 | new_fstring_parts = [] |
| 1177 | quote_types = list(_ALL_QUOTES) |
| 1178 | fallback_to_repr = False |
| 1179 | for value, is_constant in fstring_parts: |
| 1180 | value, new_quote_types = self._str_literal_helper( |
| 1181 | value, |
| 1182 | quote_types=quote_types, |
| 1183 | escape_special_whitespace=is_constant, |
| 1184 | ) |
| 1185 | new_fstring_parts.append(value) |
| 1186 | if set(new_quote_types).isdisjoint(quote_types): |
| 1187 | fallback_to_repr = True |
| 1188 | break |
| 1189 | quote_types = new_quote_types |
| 1190 | |
| 1191 | if fallback_to_repr: |
| 1192 | # If we weren't able to find a quote type that works for all parts |
| 1193 | # of the JoinedStr, fallback to using repr and triple single quotes. |
| 1194 | quote_types = ["'''"] |
| 1195 | new_fstring_parts.clear() |
| 1196 | for value, is_constant in fstring_parts: |
| 1197 | value = repr('"' + value) # force repr to use single quotes |
| 1198 | expected_prefix = "'\"" |
| 1199 | assert value.startswith(expected_prefix), repr(value) |
| 1200 | new_fstring_parts.append(value[len(expected_prefix):-1]) |
| 1201 | |
| 1202 | value = "".join(new_fstring_parts) |
| 1203 | quote_type = quote_types[0] |
| 1204 | self.write(f"{quote_type}{value}{quote_type}") |
| 1205 | |
| 1206 | def _write_fstring_inner(self, node): |
| 1207 | if isinstance(node, JoinedStr): |
nothing calls this directly
no test coverage detected