Parse format string into list of conversion specifiers. The specifiers may be nested (two levels maximum), in this case they are ordered as '{0:{1}}, {2:{3}{4}}'. Return None in case of an error.
(
format_value: str, ctx: Context, msg: MessageBuilder, nested: bool = False
)
| 174 | |
| 175 | |
| 176 | def parse_format_value( |
| 177 | format_value: str, ctx: Context, msg: MessageBuilder, nested: bool = False |
| 178 | ) -> list[ConversionSpecifier] | None: |
| 179 | """Parse format string into list of conversion specifiers. |
| 180 | |
| 181 | The specifiers may be nested (two levels maximum), in this case they are ordered as |
| 182 | '{0:{1}}, {2:{3}{4}}'. Return None in case of an error. |
| 183 | """ |
| 184 | top_targets = find_non_escaped_targets(format_value, ctx, msg) |
| 185 | if top_targets is None: |
| 186 | return None |
| 187 | |
| 188 | result: list[ConversionSpecifier] = [] |
| 189 | for target, start_pos in top_targets: |
| 190 | match = FORMAT_RE_NEW.fullmatch(target) |
| 191 | if match: |
| 192 | conv_spec = ConversionSpecifier(match, start_pos=start_pos) |
| 193 | else: |
| 194 | custom_match = FORMAT_RE_NEW_CUSTOM.fullmatch(target) |
| 195 | if custom_match: |
| 196 | conv_spec = ConversionSpecifier( |
| 197 | custom_match, start_pos=start_pos, non_standard_format_spec=True |
| 198 | ) |
| 199 | else: |
| 200 | msg.fail( |
| 201 | "Invalid conversion specifier in format string", |
| 202 | ctx, |
| 203 | code=codes.STRING_FORMATTING, |
| 204 | ) |
| 205 | return None |
| 206 | |
| 207 | if conv_spec.key and ("{" in conv_spec.key or "}" in conv_spec.key): |
| 208 | msg.fail("Conversion value must not contain { or }", ctx, code=codes.STRING_FORMATTING) |
| 209 | return None |
| 210 | result.append(conv_spec) |
| 211 | |
| 212 | # Parse nested conversions that are allowed in format specifier. |
| 213 | if ( |
| 214 | conv_spec.format_spec |
| 215 | and conv_spec.non_standard_format_spec |
| 216 | and ("{" in conv_spec.format_spec or "}" in conv_spec.format_spec) |
| 217 | ): |
| 218 | if nested: |
| 219 | msg.fail( |
| 220 | "Formatting nesting must be at most two levels deep", |
| 221 | ctx, |
| 222 | code=codes.STRING_FORMATTING, |
| 223 | ) |
| 224 | return None |
| 225 | sub_conv_specs = parse_format_value(conv_spec.format_spec, ctx, msg, nested=True) |
| 226 | if sub_conv_specs is None: |
| 227 | return None |
| 228 | result.extend(sub_conv_specs) |
| 229 | return result |
| 230 | |
| 231 | |
| 232 | def find_non_escaped_targets( |
no test coverage detected
searching dependent graphs…