Skip to content

Commit 106d57e

Browse files
authored
Add a helper function for narrowing list[Expression] to list[StrExpr] (#14877)
There are several places in the code base where we need to narrow `list[Expression]` -> `list[StrExpr]`. Currently we do this using `cast`s, but `TypeGuard`s are arguably a much more idiomatic way to do this kind of operation nowadays.
1 parent 32dd3c0 commit 106d57e

File tree

4 files changed

+15
-7
lines changed

4 files changed

+15
-7
lines changed

mypy/nodes.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
Union,
2121
cast,
2222
)
23-
from typing_extensions import Final, TypeAlias as _TypeAlias
23+
from typing_extensions import Final, TypeAlias as _TypeAlias, TypeGuard
2424

2525
from mypy_extensions import trait
2626

@@ -1635,6 +1635,10 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T:
16351635
return visitor.visit_str_expr(self)
16361636

16371637

1638+
def is_StrExpr_list(seq: list[Expression]) -> TypeGuard[list[StrExpr]]:
1639+
return all(isinstance(item, StrExpr) for item in seq)
1640+
1641+
16381642
class BytesExpr(Expression):
16391643
"""Bytes literal"""
16401644

mypy/semanal_enum.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
TupleExpr,
2828
TypeInfo,
2929
Var,
30+
is_StrExpr_list,
3031
)
3132
from mypy.options import Options
3233
from mypy.semanal_shared import SemanticAnalyzerInterface
@@ -177,8 +178,8 @@ def parse_enum_call_args(
177178
items.append(field)
178179
elif isinstance(names, (TupleExpr, ListExpr)):
179180
seq_items = names.items
180-
if all(isinstance(seq_item, StrExpr) for seq_item in seq_items):
181-
items = [cast(StrExpr, seq_item).value for seq_item in seq_items]
181+
if is_StrExpr_list(seq_items):
182+
items = [seq_item.value for seq_item in seq_items]
182183
elif all(
183184
isinstance(seq_item, (TupleExpr, ListExpr))
184185
and len(seq_item.items) == 2

mypy/semanal_namedtuple.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
TypeInfo,
4242
TypeVarExpr,
4343
Var,
44+
is_StrExpr_list,
4445
)
4546
from mypy.options import Options
4647
from mypy.semanal_shared import (
@@ -392,10 +393,10 @@ def parse_namedtuple_args(
392393
listexpr = args[1]
393394
if fullname == "collections.namedtuple":
394395
# The fields argument contains just names, with implicit Any types.
395-
if any(not isinstance(item, StrExpr) for item in listexpr.items):
396+
if not is_StrExpr_list(listexpr.items):
396397
self.fail('String literal expected as "namedtuple()" item', call)
397398
return None
398-
items = [cast(StrExpr, item).value for item in listexpr.items]
399+
items = [item.value for item in listexpr.items]
399400
else:
400401
type_exprs = [
401402
t.items[1]

mypy/stubgen.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
import sys
4949
import traceback
5050
from collections import defaultdict
51-
from typing import Iterable, List, Mapping, cast
51+
from typing import Iterable, List, Mapping
5252
from typing_extensions import Final
5353

5454
import mypy.build
@@ -102,6 +102,7 @@
102102
TupleExpr,
103103
TypeInfo,
104104
UnaryExpr,
105+
is_StrExpr_list,
105106
)
106107
from mypy.options import Options as MypyOptions
107108
from mypy.stubdoc import Sig, find_unique_signatures, parse_all_signatures
@@ -1052,7 +1053,8 @@ def process_namedtuple(self, lvalue: NameExpr, rvalue: CallExpr) -> None:
10521053
if isinstance(rvalue.args[1], StrExpr):
10531054
items = rvalue.args[1].value.replace(",", " ").split()
10541055
elif isinstance(rvalue.args[1], (ListExpr, TupleExpr)):
1055-
list_items = cast(List[StrExpr], rvalue.args[1].items)
1056+
list_items = rvalue.args[1].items
1057+
assert is_StrExpr_list(list_items)
10561058
items = [item.value for item in list_items]
10571059
else:
10581060
self.add(f"{self._indent}{lvalue.name}: Incomplete")

0 commit comments

Comments
 (0)