Skip to content

Commit ac4ed81

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

2 files changed

Lines changed: 66 additions & 17 deletions

File tree

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: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,22 @@ class _DataclassFactory(Protocol):
4848
weakref_slot: bool = False,
4949
) -> type[_T]: ...
5050

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

61-
class KW_ONLY: ...
63+
if sys.version_info >= (3, 15):
64+
KW_ONLY: Final[sentinel]
65+
else:
66+
class KW_ONLY: ...
6267

6368
@overload
6469
def asdict(obj: DataclassInstance) -> dict[str, Any]: ...
@@ -172,8 +177,16 @@ class Field(Generic[_T]):
172177
)
173178
name: str
174179
type: Type[_T] | str | Any
175-
default: _T | Literal[_MISSING_TYPE.MISSING]
176-
default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING]
180+
181+
if sys.version_info >= (3, 15):
182+
default: _T | sentinel
183+
default_factory: _DefaultFactory[_T] | sentinel
184+
kw_only: bool | sentinel
185+
else:
186+
default: _T | Literal[_MISSING_TYPE.MISSING]
187+
default_factory: _DefaultFactory[_T] | Literal[_MISSING_TYPE.MISSING]
188+
kw_only: bool | Literal[_MISSING_TYPE.MISSING]
189+
177190
repr: bool
178191
hash: bool | None
179192
init: bool
@@ -183,7 +196,6 @@ class Field(Generic[_T]):
183196
if sys.version_info >= (3, 14):
184197
doc: str | None
185198

186-
kw_only: bool | Literal[_MISSING_TYPE.MISSING]
187199

188200
if sys.version_info >= (3, 14):
189201
def __init__(
@@ -216,7 +228,47 @@ class Field(Generic[_T]):
216228

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

0 commit comments

Comments
 (0)