Skip to content

Commit 5a3662e

Browse files
committed
[dataclasses] Fix MISSING and KW_ONLY fro Python 3.15+
1 parent c4eaaca commit 5a3662e

3 files changed

Lines changed: 69 additions & 19 deletions

File tree

stdlib/@tests/stubtest_allowlists/common.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,6 @@ _?ctypes.Array.__iter__
242242
calendar._localized_day.__iter__
243243
calendar._localized_month.__iter__
244244

245-
dataclasses.KW_ONLY # white lies around defaults
246-
247245
# __all__-related weirdness (see #6523)
248246
email.__all__
249247
email.base64mime

stdlib/@tests/stubtest_allowlists/py315.txt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ _struct.pack_into
1818
_thread.RLock.__exit__
1919
_thread.lock.__exit__
2020
copy.deepcopy
21-
dataclasses.MISSING
22-
dataclasses._MISSING_TYPE
23-
dataclasses.field
2421
doctest.DocTestRunner.report_skip
2522
importlib._abc.Loader.load_module
2623
importlib._bootstrap_external.NamespacePath

stdlib/dataclasses.pyi

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ from types import GenericAlias
88
from typing import Any, Final, Generic, Literal, Protocol, TypeVar, overload, type_check_only
99
from typing_extensions import Never, TypeIs
1010

11+
if sys.version_info >= (3, 15):
12+
from builtins import sentinel
13+
1114
_T = TypeVar("_T")
1215
_T_co = TypeVar("_T_co", covariant=True)
1316

@@ -48,17 +51,23 @@ class _DataclassFactory(Protocol):
4851
weakref_slot: bool = False,
4952
) -> type[_T]: ...
5053

51-
# define _MISSING_TYPE as an enum within the type stubs,
52-
# even though that is not really its type at runtime
53-
# this allows us to use Literal[_MISSING_TYPE.MISSING]
54-
# for background, see:
55-
# https://github.com/python/typeshed/pull/5900#issuecomment-895513797
56-
class _MISSING_TYPE(enum.Enum):
57-
MISSING = enum.auto()
54+
if sys.version_info >= (3, 15):
55+
MISSING: Final[sentinel]
56+
else:
57+
# define _MISSING_TYPE as an enum within the type stubs,
58+
# even though that is not really its type at runtime
59+
# this allows us to use Literal[_MISSING_TYPE.MISSING]
60+
# for background, see:
61+
# https://github.com/python/typeshed/pull/5900#issuecomment-895513797
62+
class _MISSING_TYPE(enum.Enum):
63+
MISSING = enum.auto()
5864

59-
MISSING: Final = _MISSING_TYPE.MISSING
65+
MISSING: Final = _MISSING_TYPE.MISSING
6066

61-
class KW_ONLY: ...
67+
if sys.version_info >= (3, 15):
68+
KW_ONLY: Final[sentinel]
69+
else:
70+
class KW_ONLY: ...
6271

6372
@overload
6473
def asdict(obj: DataclassInstance) -> dict[str, Any]: ...
@@ -172,8 +181,16 @@ class Field(Generic[_T]):
172181
)
173182
name: str
174183
type: Type[_T] | str | Any
175-
default: _T | Literal[_MISSING_TYPE.MISSING]
176-
default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING]
184+
185+
if sys.version_info >= (3, 15):
186+
default: _T | sentinel
187+
default_factory: _DefaultFactory[_T] | sentinel
188+
kw_only: bool | sentinel
189+
else:
190+
default: _T | Literal[_MISSING_TYPE.MISSING]
191+
default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING]
192+
kw_only: bool | Literal[_MISSING_TYPE.MISSING]
193+
177194
repr: bool
178195
hash: bool | None
179196
init: bool
@@ -183,8 +200,6 @@ class Field(Generic[_T]):
183200
if sys.version_info >= (3, 14):
184201
doc: str | None
185202

186-
kw_only: bool | Literal[_MISSING_TYPE.MISSING]
187-
188203
if sys.version_info >= (3, 14):
189204
def __init__(
190205
self,
@@ -216,7 +231,47 @@ class Field(Generic[_T]):
216231

217232
# NOTE: Actual return type is 'Field[_T]', but we want to help type checkers
218233
# to understand the magic that happens at runtime.
219-
if sys.version_info >= (3, 14):
234+
if sys.version_info >= (3, 15):
235+
@overload # `default` and `default_factory` are optional and mutually exclusive.
236+
def field(
237+
*,
238+
default: _T,
239+
default_factory: sentinel = ...,
240+
init: bool = True,
241+
repr: bool = True,
242+
hash: bool | None = None,
243+
compare: bool = True,
244+
metadata: Mapping[Any, Any] | None = None,
245+
kw_only: bool | sentinel = ...,
246+
doc: str | None = None,
247+
) -> _T: ...
248+
@overload
249+
def field(
250+
*,
251+
default: sentinel = ...,
252+
default_factory: Callable[[], _T],
253+
init: bool = True,
254+
repr: bool = True,
255+
hash: bool | None = None,
256+
compare: bool = True,
257+
metadata: Mapping[Any, Any] | None = None,
258+
kw_only: bool | sentinel = ...,
259+
doc: str | None = None,
260+
) -> _T: ...
261+
@overload
262+
def field(
263+
*,
264+
default: sentinel = ...,
265+
default_factory: sentinel = ...,
266+
init: bool = True,
267+
repr: bool = True,
268+
hash: bool | None = None,
269+
compare: bool = True,
270+
metadata: Mapping[Any, Any] | None = None,
271+
kw_only: bool | sentinel = ...,
272+
doc: str | None = None,
273+
) -> Any: ...
274+
elif sys.version_info >= (3, 14):
220275
@overload # `default` and `default_factory` are optional and mutually exclusive.
221276
def field(
222277
*,

0 commit comments

Comments
 (0)