Skip to content

Commit ecee86b

Browse files
committed
Support statically evaluable conditional fields in NamedTuple definitions
1 parent 762b3ef commit ecee86b

2 files changed

Lines changed: 180 additions & 2 deletions

File tree

mypy/semanal_namedtuple.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ def iter_reachable_namedtuple_statements(
150150
) -> Iterator[Statement]:
151151
for stmt in statements:
152152
if isinstance(stmt, IfStmt):
153+
encountered_unknown = False
153154
handled = False
154155

155156
for expr, body in zip(stmt.expr, stmt.body):
@@ -160,10 +161,17 @@ def iter_reachable_namedtuple_statements(
160161
handled = True
161162
break
162163

163-
if truth == ALWAYS_FALSE:
164+
elif truth == ALWAYS_FALSE:
164165
continue
165166

166-
if not handled and stmt.else_body is not None:
167+
else:
168+
encountered_unknown = True
169+
170+
if (
171+
not handled
172+
and not encountered_unknown
173+
and stmt.else_body is not None
174+
):
167175
yield from self.iter_reachable_namedtuple_statements(
168176
stmt.else_body.body
169177
)

test-data/unit/check-namedtuple.test

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,3 +1623,173 @@ main:13: note: Revealed type is "builtins.str"
16231623
main:14: error: "type[NT]" has no attribute "z"
16241624

16251625
[builtins fixtures/tuple.pyi]
1626+
1627+
[case testConditionalNamedTupleElif]
1628+
# flags: --python-version 3.12
1629+
from typing import NamedTuple
1630+
import sys
1631+
1632+
class NT(NamedTuple):
1633+
x: int
1634+
1635+
if sys.version_info >= (3, 13):
1636+
a: int
1637+
elif sys.version_info >= (3, 12):
1638+
b: str
1639+
else:
1640+
c: bool
1641+
1642+
reveal_type(NT.b)
1643+
NT.a
1644+
NT.c
1645+
1646+
[out]
1647+
main:15: note: Revealed type is "builtins.str"
1648+
main:16: error: "type[NT]" has no attribute "a"
1649+
main:17: error: "type[NT]" has no attribute "c"
1650+
1651+
[builtins fixtures/tuple.pyi]
1652+
1653+
[case testConditionalNamedTupleNestedIf]
1654+
# flags: --python-version 3.12
1655+
from typing import NamedTuple
1656+
import sys
1657+
1658+
class NT(NamedTuple):
1659+
x: int
1660+
1661+
if sys.version_info >= (3, 12):
1662+
if sys.platform == "linux":
1663+
y: str
1664+
1665+
reveal_type(NT.y)
1666+
1667+
[out]
1668+
main:12: note: Revealed type is "builtins.str"
1669+
1670+
[builtins fixtures/tuple.pyi]
1671+
1672+
[case testConditionalNamedTupleUnknownCondition]
1673+
from typing import NamedTuple
1674+
1675+
SOME_FLAG = True
1676+
1677+
class NT(NamedTuple):
1678+
x: int
1679+
1680+
if SOME_FLAG:
1681+
y: str
1682+
1683+
NT.y
1684+
1685+
[out]
1686+
main:11: error: "type[NT]" has no attribute "y"
1687+
1688+
[builtins fixtures/tuple.pyi]
1689+
1690+
[case testConditionalNamedTupleMultipleFields]
1691+
# flags: --python-version 3.12
1692+
from typing import NamedTuple
1693+
import sys
1694+
1695+
class NT(NamedTuple):
1696+
if sys.version_info >= (3, 12):
1697+
x: int
1698+
y: str
1699+
1700+
reveal_type(NT.x)
1701+
reveal_type(NT.y)
1702+
1703+
[out]
1704+
main:10: note: Revealed type is "builtins.int"
1705+
main:11: note: Revealed type is "builtins.str"
1706+
1707+
[builtins fixtures/tuple.pyi]
1708+
1709+
[case testConditionalNamedTupleMultipleElif]
1710+
# flags: --python-version 3.12
1711+
from typing import NamedTuple
1712+
import sys
1713+
1714+
class NT(NamedTuple):
1715+
if sys.version_info >= (3, 14):
1716+
a: int
1717+
elif sys.version_info >= (3, 13):
1718+
b: int
1719+
elif sys.version_info >= (3, 12):
1720+
c: str
1721+
else:
1722+
d: bool
1723+
1724+
reveal_type(NT.c)
1725+
NT.a
1726+
NT.b
1727+
NT.d
1728+
1729+
[out]
1730+
main:15: note: Revealed type is "builtins.str"
1731+
main:16: error: "type[NT]" has no attribute "a"
1732+
main:17: error: "type[NT]" has no attribute "b"
1733+
main:18: error: "type[NT]" has no attribute "d"
1734+
1735+
[builtins fixtures/tuple.pyi]
1736+
1737+
[case testConditionalNamedTupleEmptyBranch]
1738+
# flags: --python-version 3.12
1739+
from typing import NamedTuple
1740+
import sys
1741+
1742+
class NT(NamedTuple):
1743+
x: int
1744+
1745+
if sys.version_info >= (3, 12):
1746+
pass
1747+
else:
1748+
y: str
1749+
1750+
NT.y
1751+
1752+
[out]
1753+
main:13: error: "type[NT]" has no attribute "y"
1754+
1755+
[builtins fixtures/tuple.pyi]
1756+
1757+
[case testConditionalNamedTupleUnknownConditionElse]
1758+
from typing import NamedTuple
1759+
1760+
FLAG = True
1761+
1762+
class NT(NamedTuple):
1763+
x: int
1764+
1765+
if FLAG:
1766+
y: str
1767+
else:
1768+
z: int
1769+
1770+
NT.y
1771+
NT.z
1772+
1773+
[out]
1774+
main:13: error: "type[NT]" has no attribute "y"
1775+
main:14: error: "type[NT]" has no attribute "z"
1776+
1777+
[builtins fixtures/tuple.pyi]
1778+
1779+
[case testConditionalNamedTupleDefaultValue]
1780+
# flags: --python-version 3.12
1781+
from typing import NamedTuple
1782+
import sys
1783+
1784+
class NT(NamedTuple):
1785+
if sys.version_info >= (3, 12):
1786+
x: int = 1
1787+
1788+
reveal_type(NT.x)
1789+
reveal_type(NT().x)
1790+
1791+
[out]
1792+
main:9: note: Revealed type is "builtins.int"
1793+
main:10: note: Revealed type is "builtins.int"
1794+
1795+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)