Update dataclasses for 3.14 (#14016)

This commit is contained in:
Max Muoto
2025-05-12 10:48:40 -05:00
committed by GitHub
parent f0c88fd238
commit 81fc4a7b20
3 changed files with 145 additions and 7 deletions
@@ -85,10 +85,6 @@ ctypes.wintypes.HDROP
ctypes.wintypes.HFILE
ctypes.wintypes.HRESULT
ctypes.wintypes.HSZ
dataclasses.Field.__init__
dataclasses.Field.doc
dataclasses.field
dataclasses.make_dataclass
dis.Instruction.make
enum.Enum.__signature__
enum.EnumMeta.__signature__
@@ -1,6 +1,7 @@
from __future__ import annotations
import dataclasses as dc
import sys
from typing import TYPE_CHECKING, Any, Dict, FrozenSet, Tuple, Type, Union
from typing_extensions import Annotated, assert_type
@@ -99,3 +100,44 @@ D = dc.make_dataclass(
# in case a type checker decides to add some special-casing for
# `make_dataclass` in the future)
assert_type(D.__mro__, Tuple[type, ...])
if sys.version_info >= (3, 14):
from typing import TypeVar
_T = TypeVar("_T")
def custom_dataclass(
cls: type[_T],
/,
*,
init: bool = True,
repr: bool = True,
eq: bool = True,
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
match_args: bool = True,
kw_only: bool = False,
slots: bool = False,
weakref_slot: bool = False,
) -> type[_T]:
custom_dc_maker = dc.dataclass(
init=init,
repr=repr,
eq=eq,
order=order,
unsafe_hash=unsafe_hash,
frozen=frozen,
match_args=match_args,
kw_only=kw_only,
slots=slots,
weakref_slot=weakref_slot,
)
return custom_dc_maker(cls)
dc.make_dataclass(
"D",
[("a", Union[int, None]), "y", ("z", Annotated[FrozenSet[bytes], "metadata"], dc.field(default=frozenset({b"foo"})))],
decorator=custom_dataclass,
)
+103 -3
View File
@@ -5,7 +5,7 @@ from _typeshed import DataclassInstance
from builtins import type as Type # alias to avoid name clashes with fields named "type"
from collections.abc import Callable, Iterable, Mapping
from types import GenericAlias
from typing import Any, Generic, Literal, Protocol, TypeVar, overload
from typing import Any, Generic, Literal, Protocol, TypeVar, overload, type_check_only
from typing_extensions import Never, TypeIs
_T = TypeVar("_T")
@@ -31,6 +31,25 @@ if sys.version_info >= (3, 10):
_DataclassT = TypeVar("_DataclassT", bound=DataclassInstance)
@type_check_only
class _DataclassFactory(Protocol):
def __call__(
self,
cls: type[_T],
/,
*,
init: bool = True,
repr: bool = True,
eq: bool = True,
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
match_args: bool = True,
kw_only: bool = False,
slots: bool = False,
weakref_slot: bool = False,
) -> type[_T]: ...
# define _MISSING_TYPE as an enum within the type stubs,
# even though that is not really its type at runtime
# this allows us to use Literal[_MISSING_TYPE.MISSING]
@@ -114,8 +133,27 @@ class Field(Generic[_T]):
init: bool
compare: bool
metadata: types.MappingProxyType[Any, Any]
if sys.version_info >= (3, 14):
doc: str | None
if sys.version_info >= (3, 10):
kw_only: bool | Literal[_MISSING_TYPE.MISSING]
if sys.version_info >= (3, 14):
def __init__(
self,
default: _T,
default_factory: Callable[[], _T],
init: bool,
repr: bool,
hash: bool | None,
compare: bool,
metadata: Mapping[Any, Any],
kw_only: bool,
doc: str | None,
) -> None: ...
elif sys.version_info >= (3, 10):
def __init__(
self,
default: _T,
@@ -144,7 +182,48 @@ class Field(Generic[_T]):
# NOTE: Actual return type is 'Field[_T]', but we want to help type checkers
# to understand the magic that happens at runtime.
if sys.version_info >= (3, 10):
if sys.version_info >= (3, 14):
@overload # `default` and `default_factory` are optional and mutually exclusive.
def field(
*,
default: _T,
default_factory: Literal[_MISSING_TYPE.MISSING] = ...,
init: bool = True,
repr: bool = True,
hash: bool | None = None,
compare: bool = True,
metadata: Mapping[Any, Any] | None = None,
kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ...,
doc: str | None = None,
) -> _T: ...
@overload
def field(
*,
default: Literal[_MISSING_TYPE.MISSING] = ...,
default_factory: Callable[[], _T],
init: bool = True,
repr: bool = True,
hash: bool | None = None,
compare: bool = True,
metadata: Mapping[Any, Any] | None = None,
kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ...,
doc: str | None = None,
) -> _T: ...
@overload
def field(
*,
default: Literal[_MISSING_TYPE.MISSING] = ...,
default_factory: Literal[_MISSING_TYPE.MISSING] = ...,
init: bool = True,
repr: bool = True,
hash: bool | None = None,
compare: bool = True,
metadata: Mapping[Any, Any] | None = None,
kw_only: bool | Literal[_MISSING_TYPE.MISSING] = ...,
doc: str | None = None,
) -> Any: ...
elif sys.version_info >= (3, 10):
@overload # `default` and `default_factory` are optional and mutually exclusive.
def field(
*,
@@ -237,7 +316,28 @@ class InitVar(Generic[_T], metaclass=type):
@overload
def __class_getitem__(cls, type: Any) -> InitVar[Any]: ... # pyright: ignore[reportInvalidTypeForm]
if sys.version_info >= (3, 12):
if sys.version_info >= (3, 14):
def make_dataclass(
cls_name: str,
fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]],
*,
bases: tuple[type, ...] = (),
namespace: dict[str, Any] | None = None,
init: bool = True,
repr: bool = True,
eq: bool = True,
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
match_args: bool = True,
kw_only: bool = False,
slots: bool = False,
weakref_slot: bool = False,
module: str | None = None,
decorator: _DataclassFactory = ...,
) -> type: ...
elif sys.version_info >= (3, 12):
def make_dataclass(
cls_name: str,
fields: Iterable[str | tuple[str, Any] | tuple[str, Any, Any]],