Skip to content

Commit d3be06d

Browse files
committed
Merge branch 'master' into feat/tests
2 parents f4fd0b7 + c4e93c4 commit d3be06d

File tree

11 files changed

+874
-994
lines changed

11 files changed

+874
-994
lines changed

.github/workflows/codeql.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ jobs:
2828
- name: "Checkout repository"
2929
uses: actions/checkout@v5
3030
- name: "Initialize CodeQL"
31-
uses: github/codeql-action/init@v3
31+
uses: github/codeql-action/init@v4
3232
with:
3333
languages: ${{ matrix.language }}
3434
- name: "Autobuild"
35-
uses: github/codeql-action/autobuild@v3
35+
uses: github/codeql-action/autobuild@v4
3636
- name: "Perform CodeQL Analysis"
37-
uses: github/codeql-action/analyze@v3
37+
uses: github/codeql-action/analyze@v4

.github/workflows/docs-checks.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ jobs:
4040
- name: "Setup Python"
4141
uses: actions/setup-python@v6
4242
with:
43-
python-version: "3.13"
43+
python-version: "3.14"
4444
- name: "Install uv"
45-
uses: astral-sh/setup-uv@v6
45+
uses: astral-sh/setup-uv@v7
4646
with:
4747
enable-cache: true
4848
- name: Sync dependencies

.github/workflows/docs-localization-download.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ jobs:
1919
- name: "Setup Python"
2020
uses: actions/setup-python@v6
2121
with:
22-
python-version: "3.13"
22+
python-version: "3.14"
2323
- name: "Install uv"
24-
uses: astral-sh/setup-uv@v6
24+
uses: astral-sh/setup-uv@v7
2525
with:
2626
enable-cache: true
2727
- name: Sync dependencies

.github/workflows/docs-localization-upload.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ jobs:
2424
- name: "Setup Python"
2525
uses: actions/setup-python@v6
2626
with:
27-
python-version: "3.13"
27+
python-version: "3.14"
2828
- name: "Install uv"
29-
uses: astral-sh/setup-uv@v6
29+
uses: astral-sh/setup-uv@v7
3030
with:
3131
enable-cache: true
3232
- name: Sync dependencies

.github/workflows/lib-checks.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ jobs:
4040
- name: "Setup Python"
4141
uses: actions/setup-python@v6
4242
with:
43-
python-version: "3.13"
43+
python-version: "3.14"
4444
- name: "Install uv"
45-
uses: astral-sh/setup-uv@v6
45+
uses: astral-sh/setup-uv@v7
4646
with:
4747
enable-cache: true
4848
- name: Sync dependencies
@@ -60,9 +60,9 @@ jobs:
6060
- name: "Setup Python"
6161
uses: actions/setup-python@v6
6262
with:
63-
python-version: "3.13"
63+
python-version: "3.14"
6464
- name: "Install uv"
65-
uses: astral-sh/setup-uv@v6
65+
uses: astral-sh/setup-uv@v7
6666
with:
6767
enable-cache: true
6868
- name: Sync dependencies
@@ -80,9 +80,9 @@ jobs:
8080
- name: "Setup Python"
8181
uses: actions/setup-python@v6
8282
with:
83-
python-version: "3.13"
83+
python-version: "3.14"
8484
- name: "Install uv"
85-
uses: astral-sh/setup-uv@v6
85+
uses: astral-sh/setup-uv@v7
8686
with:
8787
enable-cache: true
8888
- name: Sync dependencies

.github/workflows/sync-guild-features.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ jobs:
2323
- name: "Setup Python"
2424
uses: actions/setup-python@v6
2525
with:
26-
python-version: "3.13"
26+
python-version: "3.14"
2727
- name: "Install uv"
28-
uses: astral-sh/setup-uv@v6
28+
uses: astral-sh/setup-uv@v7
2929
with:
3030
enable-cache: true
3131
- name: Sync dependencies

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ repos:
1010
- id: end-of-file-fixer
1111
exclude: \.(po|pot|yml|yaml)$
1212
- repo: https://github.com/astral-sh/ruff-pre-commit
13-
rev: v0.13.3
13+
rev: v0.14.0
1414
hooks:
1515
- id: ruff
1616
args: [ --fix ]

CHANGELOG-V3.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ release.
99

1010
### Changed
1111

12+
- Removed the custom `enums.Enum` implementation in favor of a stdlib `enum.Enum` subclass.
13+
1214
### Deprecated
1315

1416
### Removed

discord/enums.py

Lines changed: 47 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
from __future__ import annotations
2727

2828
import types
29-
from collections import namedtuple
30-
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, Union
29+
from enum import Enum as EnumBase
30+
from typing import Any, Self, TypeVar, Union
31+
32+
E = TypeVar("E", bound="Enum")
3133

3234
__all__ = (
3335
"Enum",
@@ -83,118 +85,57 @@
8385
)
8486

8587

86-
def _create_value_cls(name, comparable):
87-
cls = namedtuple(f"_EnumValue_{name}", "name value")
88-
cls.__repr__ = lambda self: f"<{name}.{self.name}: {self.value!r}>"
89-
cls.__str__ = lambda self: f"{name}.{self.name}"
90-
if comparable:
91-
cls.__le__ = lambda self, other: isinstance(other, self.__class__) and self.value <= other.value
92-
cls.__ge__ = lambda self, other: isinstance(other, self.__class__) and self.value >= other.value
93-
cls.__lt__ = lambda self, other: isinstance(other, self.__class__) and self.value < other.value
94-
cls.__gt__ = lambda self, other: isinstance(other, self.__class__) and self.value > other.value
95-
return cls
96-
97-
98-
def _is_descriptor(obj):
99-
return hasattr(obj, "__get__") or hasattr(obj, "__set__") or hasattr(obj, "__delete__")
100-
101-
102-
class EnumMeta(type):
103-
if TYPE_CHECKING:
104-
__name__: ClassVar[str]
105-
_enum_member_names_: ClassVar[list[str]]
106-
_enum_member_map_: ClassVar[dict[str, Any]]
107-
_enum_value_map_: ClassVar[dict[Any, Any]]
108-
109-
def __new__(cls, name, bases, attrs, *, comparable: bool = False):
110-
value_mapping = {}
111-
member_mapping = {}
112-
member_names = []
113-
114-
value_cls = _create_value_cls(name, comparable)
115-
for key, value in list(attrs.items()):
116-
is_descriptor = _is_descriptor(value)
117-
if key[0] == "_" and not is_descriptor:
118-
continue
119-
120-
# Special case classmethod to just pass through
121-
if isinstance(value, classmethod):
122-
continue
123-
124-
if is_descriptor:
125-
setattr(value_cls, key, value)
126-
del attrs[key]
127-
continue
128-
129-
try:
130-
new_value = value_mapping[value]
131-
except KeyError:
132-
new_value = value_cls(name=key, value=value)
133-
value_mapping[value] = new_value
134-
member_names.append(key)
135-
136-
member_mapping[key] = new_value
137-
attrs[key] = new_value
138-
139-
attrs["_enum_value_map_"] = value_mapping
140-
attrs["_enum_member_map_"] = member_mapping
141-
attrs["_enum_member_names_"] = member_names
142-
attrs["_enum_value_cls_"] = value_cls
143-
actual_cls = super().__new__(cls, name, bases, attrs)
144-
value_cls._actual_enum_cls_ = actual_cls # type: ignore
145-
return actual_cls
146-
147-
def __iter__(cls):
148-
return (cls._enum_member_map_[name] for name in cls._enum_member_names_)
149-
150-
def __reversed__(cls):
151-
return (cls._enum_member_map_[name] for name in reversed(cls._enum_member_names_))
152-
153-
def __len__(cls):
154-
return len(cls._enum_member_names_)
88+
class Enum(EnumBase):
89+
"""An :class:`enum.Enum` subclass that implements a missing value creation behavior if it is
90+
not present in any of the members of it.
91+
"""
15592

156-
def __repr__(cls):
157-
return f"<enum {cls.__name__}>"
93+
def __init_subclass__(cls, *, comparable: bool = False) -> None:
94+
super().__init_subclass__()
15895

159-
@property
160-
def __members__(cls):
161-
return types.MappingProxyType(cls._enum_member_map_)
96+
if comparable:
16297

163-
def __call__(cls, value):
164-
try:
165-
return cls._enum_value_map_[value]
166-
except (KeyError, TypeError) as e:
167-
raise ValueError(f"{value!r} is not a valid {cls.__name__}") from e
98+
def __lt__(self: Enum, other: object) -> bool:
99+
if not isinstance(other, cls):
100+
return NotImplemented
101+
return self.value < other.value
168102

169-
def __getitem__(cls, key):
170-
return cls._enum_member_map_[key]
103+
def __gt__(self: Enum, other: object) -> bool:
104+
if not isinstance(other, cls):
105+
return NotImplemented
106+
return self.value > other.value
171107

172-
def __setattr__(cls, name, value):
173-
raise TypeError("Enums are immutable.")
108+
def __le__(self: Enum, other: object) -> bool:
109+
if not isinstance(other, cls):
110+
return NotImplemented
111+
return self.value <= other.value
174112

175-
def __delattr__(cls, attr):
176-
raise TypeError("Enums are immutable")
113+
def __ge__(self: Enum, other: object) -> bool:
114+
if not isinstance(other, cls):
115+
return NotImplemented
116+
return self.value >= other.value
177117

178-
def __instancecheck__(self, instance):
179-
# isinstance(x, Y)
180-
# -> __instancecheck__(Y, x)
181-
try:
182-
return instance._actual_enum_cls_ is self
183-
except AttributeError:
184-
return False
118+
cls.__lt__ = __lt__
119+
cls.__gt__ = __gt__
120+
cls.__le__ = __le__
121+
cls.__ge__ = __ge__
185122

123+
@classmethod
124+
def _missing_(cls, value: Any) -> Self:
125+
name = f"unknown_{value}"
126+
if name in cls.__members__:
127+
return cls.__members__[name]
186128

187-
if TYPE_CHECKING:
188-
from enum import Enum
189-
else:
129+
# this creates the new unknown value member
130+
obj = object.__new__(cls)
131+
obj._name_ = name
132+
obj._value_ = value
190133

191-
class Enum(metaclass=EnumMeta):
192-
@classmethod
193-
def try_value(cls, value):
194-
try:
195-
return cls._enum_value_map_[value]
196-
except (KeyError, TypeError):
197-
return value
134+
# and adds it to the member mapping of this enum so we don't
135+
# create a different enum member value each time
136+
cls._member_map_[name] = obj
137+
cls._value2member_map_[value] = obj
138+
return obj
198139

199140

200141
class ChannelType(Enum):
@@ -1078,22 +1019,9 @@ def __int__(self):
10781019
return self.value
10791020

10801021

1081-
T = TypeVar("T")
1082-
1083-
1084-
def create_unknown_value(cls: type[T], val: Any) -> T:
1085-
value_cls = cls._enum_value_cls_ # type: ignore
1086-
name = f"unknown_{val}"
1087-
return value_cls(name=name, value=val)
1088-
1089-
1090-
def try_enum(cls: type[T], val: Any) -> T:
1022+
def try_enum(cls: type[E], val: Any) -> E:
10911023
"""A function that tries to turn the value into enum ``cls``.
10921024
10931025
If it fails it returns a proxy invalid value instead.
10941026
"""
1095-
1096-
try:
1097-
return cls._enum_value_map_[val] # type: ignore
1098-
except (KeyError, TypeError, AttributeError):
1099-
return create_unknown_value(cls, val)
1027+
return cls(val)

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors = [
99
]
1010
description = "A Python wrapper for the Discord API"
1111
readme = {content-type = "text/x-rst", file = "README.rst"}
12-
requires-python = ">=3.10"
12+
requires-python = ">=3.10,<3.14"
1313
license = "MIT"
1414
license-files = ["LICENSE"]
1515
classifiers = [
@@ -198,6 +198,7 @@ extend-ignore = [
198198
"N803",
199199
"N804",
200200
"N806",
201+
"N807", # Function name should not start and end with `__` (dunder)
201202
"N811",
202203
"N818",
203204
"PERF102",

0 commit comments

Comments
 (0)