From c3cd88ba91eecd3eece8654f9cab79ee1b1a682f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 13 Dec 2021 11:33:54 +0000 Subject: [PATCH] Add `EnumMeta.__new__` & `EnumMeta.__call__` (#6572) --- stdlib/enum.pyi | 65 +++++++++++++++++++++++- tests/stubtest_allowlists/py3_common.txt | 2 - 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/stdlib/enum.pyi b/stdlib/enum.pyi index d85cc3338..20e4d9fc7 100644 --- a/stdlib/enum.pyi +++ b/stdlib/enum.pyi @@ -2,17 +2,49 @@ import sys import types from abc import ABCMeta from builtins import property as _builtins_property -from typing import Any, Iterator, Type, TypeVar +from collections.abc import Iterable, Iterator, Mapping +from typing import Any, Dict, Tuple, Type, TypeVar, Union, overload _T = TypeVar("_T") _S = TypeVar("_S", bound=Type[Enum]) +# The following all work: +# >>> from enum import Enum +# >>> from string import ascii_lowercase +# >>> Enum('Foo', names='RED YELLOW GREEN') +# +# >>> Enum('Foo', names=[('RED', 1), ('YELLOW, 2)]) +# +# >>> Enum('Foo', names=((x for x in (ascii_lowercase[i], i)) for i in range(5))) +# +# >>> Enum('Foo', names={'RED': 1, 'YELLOW': 2}) +# +_EnumNames = Union[str, Iterable[str], Iterable[Iterable[Union[str, Any]]], Mapping[str, Any]] + +class _EnumDict(Dict[str, Any]): + def __init__(self) -> None: ... + # Note: EnumMeta actually subclasses type directly, not ABCMeta. # This is a temporary workaround to allow multiple creation of enums with builtins # such as str as mixins, which due to the handling of ABCs of builtin types, cause # spurious inconsistent metaclass structure. See #1595. # Structurally: Iterable[T], Reversible[T], Container[T] where T is the enum itself class EnumMeta(ABCMeta): + if sys.version_info >= (3, 11): + def __new__( + metacls: Type[_T], + cls: str, + bases: Tuple[type, ...], + classdict: _EnumDict, + *, + boundary: FlagBoundary | None = ..., + _simple: bool = ..., + **kwds: Any, + ) -> _T: ... + elif sys.version_info >= (3, 9): + def __new__(metacls: Type[_T], cls: str, bases: Tuple[type, ...], classdict: _EnumDict, **kwds: Any) -> _T: ... # type: ignore + else: + def __new__(metacls: Type[_T], cls: str, bases: Tuple[type, ...], classdict: _EnumDict) -> _T: ... # type: ignore def __iter__(self: Type[_T]) -> Iterator[_T]: ... def __reversed__(self: Type[_T]) -> Iterator[_T]: ... def __contains__(self: Type[Any], member: object) -> bool: ... @@ -20,6 +52,37 @@ class EnumMeta(ABCMeta): @_builtins_property def __members__(self: Type[_T]) -> types.MappingProxyType[str, _T]: ... def __len__(self) -> int: ... + if sys.version_info >= (3, 11): + # Simple value lookup + @overload # type: ignore[override] + def __call__(cls: Type[_T], value: Any, names: None = ...) -> _T: ... + # Functional Enum API + @overload + def __call__( + cls, + value: str, + names: _EnumNames, + *, + module: str | None = ..., + qualname: str | None = ..., + type: type | None = ..., + start: int = ..., + boundary: FlagBoundary | None = ..., + ) -> Type[Enum]: ... + else: + @overload # type: ignore[override] + def __call__(cls: Type[_T], value: Any, names: None = ...) -> _T: ... + @overload + def __call__( + cls, + value: str, + names: _EnumNames, + *, + module: str | None = ..., + qualname: str | None = ..., + type: type | None = ..., + start: int = ..., + ) -> Type[Enum]: ... _member_names_: list[str] # undocumented _member_map_: dict[str, Enum] # undocumented _value2member_map_: dict[Any, Enum] # undocumented diff --git a/tests/stubtest_allowlists/py3_common.txt b/tests/stubtest_allowlists/py3_common.txt index 1c98c223c..a75e0c3af 100644 --- a/tests/stubtest_allowlists/py3_common.txt +++ b/tests/stubtest_allowlists/py3_common.txt @@ -83,8 +83,6 @@ distutils.command.bdist_packager # It exists in docs as package name but not in distutils.version.Version._cmp # class should have declared this distutils.version.Version.parse # class should have declared this email.headerregistry.BaseHeader.max_count # docs say subclasses should have this property -enum.EnumMeta.__call__ -enum.EnumMeta.__new__ http.HTTPStatus.description # set in __new__ http.HTTPStatus.phrase # set in __new__ http.client.HTTPConnection.response_class # the actual type at runtime is abc.ABCMeta