Skip to content

Commit 4ca0c48

Browse files
authored
Improve the first overload of is_dataclass (#9758)
1 parent 70c027e commit 4ca0c48

File tree

2 files changed

+22
-13
lines changed

2 files changed

+22
-13
lines changed

stdlib/dataclasses.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ else:
223223

224224
def fields(class_or_instance: DataclassInstance | type[DataclassInstance]) -> tuple[Field[Any], ...]: ...
225225
@overload
226-
def is_dataclass(obj: DataclassInstance | type[DataclassInstance]) -> Literal[True]: ...
226+
def is_dataclass(obj: DataclassInstance) -> Literal[True]: ...
227227
@overload
228228
def is_dataclass(obj: type) -> TypeGuard[type[DataclassInstance]]: ...
229229
@overload

test_cases/stdlib/check_dataclasses.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from __future__ import annotations
22

33
import dataclasses as dc
4-
from typing import Any, Dict, Tuple, Type
4+
from typing import TYPE_CHECKING, Any, Dict, Tuple, Type, Union
55
from typing_extensions import assert_type
66

7+
if TYPE_CHECKING:
8+
from _typeshed import DataclassInstance
9+
710

811
@dc.dataclass
912
class Foo:
@@ -21,10 +24,9 @@ class Foo:
2124
# dc.astuple(Foo)
2225
# dc.replace(Foo)
2326

24-
if dc.is_dataclass(Foo):
25-
# The inferred type doesn't change
26-
# if it's already known to be a subtype of type[_DataclassInstance]
27-
assert_type(Foo, Type[Foo])
27+
# See #9723 for why we can't make this assertion
28+
# if dc.is_dataclass(Foo):
29+
# assert_type(Foo, Type[Foo])
2830

2931
f = Foo(attr="attr")
3032

@@ -39,7 +41,7 @@ class Foo:
3941
assert_type(f, Foo)
4042

4143

42-
def test_other_isdataclass_overloads(x: type, y: object) -> None:
44+
def check_other_isdataclass_overloads(x: type, y: object) -> None:
4345
# TODO: pyright correctly emits an error on this, but mypy does not -- why?
4446
# dc.fields(x)
4547

@@ -55,17 +57,23 @@ def test_other_isdataclass_overloads(x: type, y: object) -> None:
5557
dc.replace(y) # type: ignore
5658

5759
if dc.is_dataclass(x):
60+
assert_type(x, Type["DataclassInstance"])
5861
assert_type(dc.fields(x), Tuple[dc.Field[Any], ...])
59-
# These should cause type checkers to emit errors
60-
# due to the fact it's a dataclass class, not an instance
61-
dc.asdict(x) # type: ignore
62-
dc.astuple(x) # type: ignore
63-
dc.replace(x) # type: ignore
62+
63+
# Mypy correctly emits an error on these due to the fact
64+
# that it's a dataclass class, not a dataclass instance.
65+
# Pyright, however, handles ClassVar members in protocols differently.
66+
# See https://github.com/microsoft/pyright/issues/4339
67+
#
68+
# dc.asdict(x)
69+
# dc.astuple(x)
70+
# dc.replace(x)
6471

6572
if dc.is_dataclass(y):
73+
assert_type(y, Union["DataclassInstance", Type["DataclassInstance"]])
6674
assert_type(dc.fields(y), Tuple[dc.Field[Any], ...])
6775

68-
# Mypy corrextly emits an error on these due to the fact we don't know
76+
# Mypy correctly emits an error on these due to the fact we don't know
6977
# whether it's a dataclass class or a dataclass instance.
7078
# Pyright, however, handles ClassVar members in protocols differently.
7179
# See https://github.com/microsoft/pyright/issues/4339
@@ -75,6 +83,7 @@ def test_other_isdataclass_overloads(x: type, y: object) -> None:
7583
# dc.replace(y)
7684

7785
if dc.is_dataclass(y) and not isinstance(y, type):
86+
assert_type(y, "DataclassInstance")
7887
assert_type(dc.fields(y), Tuple[dc.Field[Any], ...])
7988
assert_type(dc.asdict(y), Dict[str, Any])
8089
assert_type(dc.astuple(y), Tuple[Any, ...])

0 commit comments

Comments
 (0)