Skip to content

Commit 732d98e

Browse files
authored
Fix string formatting for string enums (#16555)
Fixes #7563 Inside `check_str_format_call` method, it checks if expression of `format` method call is an Enum member and it takes Literal value of that Enum member to check the `format` call arguments, if so.
1 parent 8019010 commit 732d98e

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

mypy/checkexpr.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,17 @@ def check_str_format_call(self, e: CallExpr) -> None:
636636
if isinstance(e.callee.expr, StrExpr):
637637
format_value = e.callee.expr.value
638638
elif self.chk.has_type(e.callee.expr):
639-
base_typ = try_getting_literal(self.chk.lookup_type(e.callee.expr))
639+
typ = get_proper_type(self.chk.lookup_type(e.callee.expr))
640+
if (
641+
isinstance(typ, Instance)
642+
and typ.type.is_enum
643+
and isinstance(typ.last_known_value, LiteralType)
644+
and isinstance(typ.last_known_value.value, str)
645+
):
646+
value_type = typ.type.names[typ.last_known_value.value].type
647+
if isinstance(value_type, Type):
648+
typ = get_proper_type(value_type)
649+
base_typ = try_getting_literal(typ)
640650
if isinstance(base_typ, LiteralType) and isinstance(base_typ.value, str):
641651
format_value = base_typ.value
642652
if format_value is not None:

test-data/unit/check-formatting.test

+42
Original file line numberDiff line numberDiff line change
@@ -588,3 +588,45 @@ class S:
588588
'{:%}'.format(0.001)
589589
[builtins fixtures/primitives.pyi]
590590
[typing fixtures/typing-medium.pyi]
591+
592+
[case testEnumWithStringToFormatValue]
593+
from enum import Enum
594+
595+
class Responses(str, Enum):
596+
TEMPLATED = 'insert {} here'
597+
TEMPLATED_WITH_KW = 'insert {value} here'
598+
NORMAL = 'something'
599+
600+
Responses.TEMPLATED.format(42)
601+
Responses.TEMPLATED_WITH_KW.format(value=42)
602+
Responses.TEMPLATED.format() # E: Cannot find replacement for positional format specifier 0
603+
Responses.TEMPLATED_WITH_KW.format() # E: Cannot find replacement for named format specifier "value"
604+
Responses.NORMAL.format(42) # E: Not all arguments converted during string formatting
605+
Responses.NORMAL.format(value=42) # E: Not all arguments converted during string formatting
606+
[builtins fixtures/primitives.pyi]
607+
608+
[case testNonStringEnumToFormatValue]
609+
from enum import Enum
610+
611+
class Responses(Enum):
612+
TEMPLATED = 'insert {value} here'
613+
614+
Responses.TEMPLATED.format(value=42) # E: "Responses" has no attribute "format"
615+
[builtins fixtures/primitives.pyi]
616+
617+
[case testStrEnumWithStringToFormatValue]
618+
# flags: --python-version 3.11
619+
from enum import StrEnum
620+
621+
class Responses(StrEnum):
622+
TEMPLATED = 'insert {} here'
623+
TEMPLATED_WITH_KW = 'insert {value} here'
624+
NORMAL = 'something'
625+
626+
Responses.TEMPLATED.format(42)
627+
Responses.TEMPLATED_WITH_KW.format(value=42)
628+
Responses.TEMPLATED.format() # E: Cannot find replacement for positional format specifier 0
629+
Responses.TEMPLATED_WITH_KW.format() # E: Cannot find replacement for named format specifier "value"
630+
Responses.NORMAL.format(42) # E: Not all arguments converted during string formatting
631+
Responses.NORMAL.format(value=42) # E: Not all arguments converted during string formatting
632+
[builtins fixtures/primitives.pyi]

0 commit comments

Comments
 (0)