diff --git a/.flake8 b/.flake8 index e039a641fb..0edf229dc9 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] -min_python_version = 3.10 +min_python_version = 3.11 # Incompatible with black see https://github.com/ambv/black/issues/315 ignore = diff --git a/.github/workflows/lib-checks.yml b/.github/workflows/lib-checks.yml index 2f68f1b3d8..fd29fac20b 100644 --- a/.github/workflows/lib-checks.yml +++ b/.github/workflows/lib-checks.yml @@ -172,7 +172,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - python-version: [ "3.10", "3.14" ] + python-version: [ "3.11", "3.15" ] steps: - name: "Checkout Repository" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -191,7 +191,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] - python-version: [ "3.10", "3.11", "3.12", "3.13", "3.14" ] + python-version: [ "3.11", "3.12", "3.13", "3.14", "3.15" ] steps: - name: "Checkout Repository" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4458152080..4da4a15af5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: hooks: - id: pyupgrade exclude: \.(po|pot|yml|yaml)$ - args: [--py310-plus] + args: [--py311-plus] - repo: https://github.com/PyCQA/isort rev: 9.0.1 hooks: diff --git a/README.rst b/README.rst index c2e160ef95..35f78a93fe 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ Pycord is a modern, easy to use, feature-rich, and async ready API wrapper for D Note ---- -Pycord supports Python ``3.10`` - ``3.14`` +Pycord supports Python ``3.11`` - ``3.15`` Key Features ------------ @@ -45,7 +45,7 @@ Key Features Installing ---------- -**Python 3.10 or higher is required** +**Python 3.11 or higher is required** To install the library without full voice support, run the following command: @@ -105,7 +105,7 @@ Optional Packages Please note that while installing voice support on Linux, you must install the following packages via your preferred package manager (e.g. ``apt``, ``dnf``, etc) BEFORE running the above commands: * libffi-dev (or ``libffi-devel`` on some systems) -* python-dev (e.g. ``python3.10-dev`` for Python 3.10) +* python-dev (e.g. ``python3.11-dev`` for Python 3.11) Quick Example ------------- diff --git a/discord/_version.py b/discord/_version.py index ddc7a10fd7..8efa71827b 100644 --- a/discord/_version.py +++ b/discord/_version.py @@ -30,11 +30,11 @@ import warnings from importlib.metadata import PackageNotFoundError, version -from typing_extensions import TypedDict, deprecated +from typing_extensions import deprecated __all__ = ("__version__", "VersionInfo", "version_info") -from typing import Literal, NamedTuple +from typing import Literal, NamedTuple, TypedDict try: __version__ = version("py-cord") diff --git a/discord/abc.py b/discord/abc.py index ee50b5f9f6..c4616e79e4 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -2053,7 +2053,7 @@ async def connect( Raises ------ - asyncio.TimeoutError + TimeoutError Could not connect to the voice channel in time. ~discord.ClientException You are already connected to a voice channel. @@ -2087,7 +2087,7 @@ async def connect( try: await voice.connect(timeout=timeout, reconnect=reconnect) - except asyncio.TimeoutError: + except TimeoutError: try: await voice.disconnect(force=True) except Exception: diff --git a/discord/activity.py b/discord/activity.py index 81128a5fb0..bb23d96d90 100644 --- a/discord/activity.py +++ b/discord/activity.py @@ -130,7 +130,7 @@ def created_at(self) -> datetime.datetime | None: """ if self._created_at is not None: return datetime.datetime.fromtimestamp( - self._created_at / 1000, tz=datetime.timezone.utc + self._created_at / 1000, tz=datetime.UTC ) def to_dict(self) -> ActivityPayload: @@ -288,7 +288,7 @@ def start(self) -> datetime.datetime | None: except KeyError: return None else: - return datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc) + return datetime.datetime.fromtimestamp(timestamp, tz=datetime.UTC) @property def end(self) -> datetime.datetime | None: @@ -298,7 +298,7 @@ def end(self) -> datetime.datetime | None: except KeyError: return None else: - return datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc) + return datetime.datetime.fromtimestamp(timestamp, tz=datetime.UTC) @property def large_image_url(self) -> str | None: @@ -398,18 +398,14 @@ def type(self) -> ActivityType: def start(self) -> datetime.datetime | None: """When the user started playing this game in UTC, if applicable.""" if self._start: - return datetime.datetime.fromtimestamp( - self._start / 1000, tz=datetime.timezone.utc - ) + return datetime.datetime.fromtimestamp(self._start / 1000, tz=datetime.UTC) return None @property def end(self) -> datetime.datetime | None: """When the user will stop playing this game in UTC, if applicable.""" if self._end: - return datetime.datetime.fromtimestamp( - self._end / 1000, tz=datetime.timezone.utc - ) + return datetime.datetime.fromtimestamp(self._end / 1000, tz=datetime.UTC) return None def __str__(self) -> str: @@ -606,7 +602,7 @@ def created_at(self) -> datetime.datetime | None: """ if self._created_at is not None: return datetime.datetime.fromtimestamp( - self._created_at / 1000, tz=datetime.timezone.utc + self._created_at / 1000, tz=datetime.UTC ) @property @@ -713,14 +709,14 @@ def track_url(self) -> str: def start(self) -> datetime.datetime: """When the user started playing this song in UTC.""" return datetime.datetime.fromtimestamp( - self._timestamps["start"] / 1000, tz=datetime.timezone.utc + self._timestamps["start"] / 1000, tz=datetime.UTC ) @property def end(self) -> datetime.datetime: """When the user will stop playing this song in UTC.""" return datetime.datetime.fromtimestamp( - self._timestamps["end"] / 1000, tz=datetime.timezone.utc + self._timestamps["end"] / 1000, tz=datetime.UTC ) @property diff --git a/discord/bot.py b/discord/bot.py index 0910f5d46e..eb7bb9644e 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -25,7 +25,6 @@ from __future__ import annotations -import asyncio import collections import collections.abc import copy @@ -63,7 +62,7 @@ from .utils import MISSING, async_all, find, get if TYPE_CHECKING: - from typing_extensions import Never + from typing import Never from .cog import Cog from .commands import Option @@ -907,7 +906,7 @@ async def callback() -> None: check=lambda i, c: c == command, timeout=3, ) - except asyncio.TimeoutError: + except TimeoutError: return else: if not autocomplete_task.done(): @@ -1535,7 +1534,7 @@ def before_invoke(self, coro): TypeError The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The pre-invoke hook must be a coroutine.") self._before_invoke = coro @@ -1567,7 +1566,7 @@ def after_invoke(self, coro): The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The post-invoke hook must be a coroutine.") self._after_invoke = coro diff --git a/discord/client.py b/discord/client.py index de9f17ad70..5a70bdbfd8 100644 --- a/discord/client.py +++ b/discord/client.py @@ -26,6 +26,7 @@ from __future__ import annotations import asyncio +import inspect import logging import signal import sys @@ -35,11 +36,13 @@ from typing import ( TYPE_CHECKING, Any, + Self, TypeVar, + overload, ) import aiohttp -from typing_extensions import Self, deprecated +from typing_extensions import deprecated from . import utils from .activity import ActivityTypes, BaseActivity, create_activity @@ -732,7 +735,7 @@ async def connect(self, *, reconnect: bool = True) -> None: GatewayNotFound, ConnectionClosed, aiohttp.ClientError, - asyncio.TimeoutError, + TimeoutError, ) as exc: self.dispatch("disconnect") if not reconnect: @@ -1273,7 +1276,7 @@ def wait_for( The ``timeout`` parameter is passed onto :func:`asyncio.wait_for`. By default, it does not timeout. Note that this does propagate the - :exc:`asyncio.TimeoutError` for you in case of timeout and is provided for + :exc:`TimeoutError` for you in case of timeout and is provided for ease of use. In case the event returns multiple arguments, a :class:`tuple` containing those @@ -1293,7 +1296,7 @@ def wait_for( parameters of the event being waited for. timeout: Optional[:class:`float`] The number of seconds to wait before timing out and raising - :exc:`asyncio.TimeoutError`. + :exc:`TimeoutError`. Returns ------- @@ -1304,7 +1307,7 @@ def wait_for( Raises ------ - asyncio.TimeoutError + TimeoutError Raised if a timeout is provided and reached. Examples @@ -1337,7 +1340,7 @@ def check(reaction, user): try: reaction, user = await client.wait_for('reaction_add', timeout=60.0, check=check) - except asyncio.TimeoutError: + except TimeoutError: await channel.send('\N{THUMBS DOWN SIGN}') else: await channel.send('\N{THUMBS UP SIGN}') @@ -1395,7 +1398,7 @@ async def my_message(message): pass if not name.startswith("on_"): raise ValueError("The 'name' parameter must start with 'on_'") - if not asyncio.iscoroutinefunction(func): + if not inspect.iscoroutinefunction(func): raise TypeError("Listeners must be coroutines") if name in self._event_handlers: @@ -1429,7 +1432,15 @@ def remove_listener(self, func: Coro, name: str = MISSING) -> None: except ValueError: pass - def listen(self, name: str = MISSING, once: bool = False) -> Callable[[Coro], Coro]: + @overload + def listen(self, name: Coro, once: bool = False) -> Coro: ... + + @overload + def listen( + self, name: str = MISSING, once: bool = False + ) -> Callable[[Coro], Coro]: ... + + def listen(self, name=MISSING, once=False): """A decorator that registers another function as an external event listener. Basically this allows you to listen to multiple events from different places e.g. such as :func:`.on_ready` @@ -1475,7 +1486,7 @@ def decorator(func: Coro) -> Coro: self.add_listener(func, name) return func - if asyncio.iscoroutinefunction(name): + if inspect.iscoroutinefunction(name): coro = name name = coro.__name__ return decorator(coro) @@ -1510,7 +1521,7 @@ async def on_ready(): print('Ready!') """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("event registered must be a coroutine function") setattr(self, coro.__name__, coro) diff --git a/discord/colour.py b/discord/colour.py index 420ab9096f..2f4e38bc1f 100644 --- a/discord/colour.py +++ b/discord/colour.py @@ -27,9 +27,9 @@ import colorsys import random -from typing import Any +from typing import Any, Self -from typing_extensions import Self, deprecated, override +from typing_extensions import deprecated, override __all__ = ( "Colour", diff --git a/discord/commands/core.py b/discord/commands/core.py index 6c4e0e73b2..609c6081df 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -39,14 +39,13 @@ Any, Generic, Literal, + Self, TypeVar, Union, get_args, get_origin, ) -from typing_extensions import Self - from ..channel import PartialMessageable, _threaded_guild_channel_factory from ..enums import Enum as DiscordEnum from ..enums import IntegrationType, InteractionContextType, SlashCommandOptionType @@ -83,9 +82,7 @@ ) if TYPE_CHECKING: - from typing import Concatenate, ParamSpec - - from typing_extensions import Never + from typing import Concatenate, Never, ParamSpec from .. import Permissions from ..bot import C @@ -498,7 +495,7 @@ def error(self, coro): The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The error handler must be a coroutine.") self.on_error = coro @@ -527,7 +524,7 @@ def before_invoke(self, coro): TypeError The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The pre-invoke hook must be a coroutine.") self._before_invoke = coro @@ -552,7 +549,7 @@ def after_invoke(self, coro): TypeError The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The post-invoke hook must be a coroutine.") self._after_invoke = coro @@ -731,7 +728,7 @@ def __new__(cls, *args, **kwargs) -> SlashCommand: def __init__(self, func: Callable, *args, **kwargs) -> None: super().__init__(func, **kwargs) - if not asyncio.iscoroutinefunction(func): + if not inspect.iscoroutinefunction(func): raise TypeError("Callback must be a coroutine.") self.callback = func @@ -922,10 +919,9 @@ def _match_option_param_names(self, params, options): return options def _is_typing_union(self, annotation): - return getattr(annotation, "__origin__", None) is Union or type( - annotation - ) is getattr( - types, "UnionType", Union + return ( + getattr(annotation, "__origin__", None) is Union + or type(annotation) is types.UnionType ) # type: ignore def _is_typing_optional(self, annotation): @@ -1678,7 +1674,7 @@ def __new__(cls, *args, **kwargs) -> ContextMenuCommand: def __init__(self, func: Callable, *args, **kwargs) -> None: super().__init__(func, **kwargs) - if not asyncio.iscoroutinefunction(func): + if not inspect.iscoroutinefunction(func): raise TypeError("Callback must be a coroutine.") self.callback = func diff --git a/discord/embeds.py b/discord/embeds.py index 57f4c89302..b11fa338ab 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -1057,11 +1057,11 @@ def to_dict(self) -> EmbedData: if timestamp: if timestamp.tzinfo: result["timestamp"] = timestamp.astimezone( - tz=datetime.timezone.utc + tz=datetime.UTC ).isoformat() else: result["timestamp"] = timestamp.replace( - tzinfo=datetime.timezone.utc + tzinfo=datetime.UTC ).isoformat() # add in the non-raw attribute ones diff --git a/discord/enums.py b/discord/enums.py index 72a4f1ddf4..9765923a3d 100755 --- a/discord/enums.py +++ b/discord/enums.py @@ -27,6 +27,7 @@ import types from collections import namedtuple +from enum import Enum as _Enum from enum import IntEnum from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, Union @@ -844,9 +845,7 @@ def from_datatype(cls, datatype): isinstance(datatype, types.UnionType) or getattr(datatype, "__origin__", None) is Union ): - # Python 3.10+ "|" operator or typing.Union has been used. The __args__ attribute is a tuple of the types. - # Type checking fails for this case, so ignore it. - return cls.from_datatype(datatype.__args__) # type: ignore + return cls.from_datatype(datatype.__args__) if isinstance(datatype, str): datatype_name = datatype @@ -1126,6 +1125,8 @@ class ThreadArchiveDuration(IntEnum): three_days = 4320 one_week = 10080 + __str__ = _Enum.__str__ + class SeparatorSpacingSize(Enum): """A separator component's spacing size.""" @@ -1195,6 +1196,8 @@ class RoleType(IntEnum): CONNECTION = 8 UNKNOWN = 9 + __str__ = _Enum.__str__ + class ApplicationEventWebhookStatus(Enum): """Represents the application event webhook status.""" diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index dad32f30a1..a2111235f1 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -324,7 +324,7 @@ def __init__( ), **kwargs: Any, ): - if not asyncio.iscoroutinefunction(func): + if not inspect.iscoroutinefunction(func): raise TypeError("Callback must be a coroutine.") name = kwargs.get("name") or func.__name__ @@ -852,7 +852,7 @@ async def call_after_hooks(self, ctx: Context) -> None: def _prepare_cooldowns(self, ctx: Context) -> None: if self._buckets.valid: dt = ctx.message.edited_at or ctx.message.created_at - current = dt.replace(tzinfo=datetime.timezone.utc).timestamp() + current = dt.replace(tzinfo=datetime.UTC).timestamp() bucket = self._buckets.get_bucket(ctx.message, current) if bucket is not None: retry_after = bucket.update_rate_limit(current) @@ -907,7 +907,7 @@ def is_on_cooldown(self, ctx: Context) -> bool: bucket = self._buckets.get_bucket(ctx.message) dt = ctx.message.edited_at or ctx.message.created_at - current = dt.replace(tzinfo=datetime.timezone.utc).timestamp() + current = dt.replace(tzinfo=datetime.UTC).timestamp() return bucket.get_tokens(current) == 0 def reset_cooldown(self, ctx: Context) -> None: @@ -941,7 +941,7 @@ def get_cooldown_retry_after(self, ctx: Context) -> float: if self._buckets.valid: bucket = self._buckets.get_bucket(ctx.message) dt = ctx.message.edited_at or ctx.message.created_at - current = dt.replace(tzinfo=datetime.timezone.utc).timestamp() + current = dt.replace(tzinfo=datetime.UTC).timestamp() return bucket.get_retry_after(current) return 0.0 @@ -992,7 +992,7 @@ def error(self, coro: ErrorT) -> ErrorT: The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The error handler must be a coroutine.") self.on_error: Error = coro @@ -1026,7 +1026,7 @@ def before_invoke(self, coro: HookT) -> HookT: TypeError The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The pre-invoke hook must be a coroutine.") self._before_invoke = coro @@ -1053,7 +1053,7 @@ def after_invoke(self, coro: HookT) -> HookT: TypeError The coroutine passed is not actually a coroutine. """ - if not asyncio.iscoroutinefunction(coro): + if not inspect.iscoroutinefunction(coro): raise TypeError("The post-invoke hook must be a coroutine.") self._after_invoke = coro @@ -1081,7 +1081,7 @@ def short_doc(self) -> str: def _is_typing_optional(self, annotation: T | T | None) -> TypeGuard[T | None]: return ( getattr(annotation, "__origin__", None) is Union - or type(annotation) is getattr(types, "UnionType", Union) + or type(annotation) is types.UnionType ) and type( None ) in annotation.__args__ # type: ignore @@ -1599,7 +1599,7 @@ async def reinvoke(self, ctx: Context, *, call_hooks: bool = False) -> None: # Decorators -@overload # for py 3.10 +@overload def command( name: str = ..., cls: type[Command[CogT, P, T]] = ..., diff --git a/discord/ext/tasks/__init__.py b/discord/ext/tasks/__init__.py index c1d1e72274..12b23b57ed 100644 --- a/discord/ext/tasks/__init__.py +++ b/discord/ext/tasks/__init__.py @@ -115,7 +115,7 @@ def __init__( discord.GatewayNotFound, discord.ConnectionClosed, aiohttp.ClientError, - asyncio.TimeoutError, + TimeoutError, ) self._before_loop = None @@ -192,7 +192,7 @@ async def _loop(self, *args: Any, **kwargs: Any) -> None: self._prepare_time_index() self._next_iteration = self._get_next_sleep_time() else: - self._next_iteration = datetime.datetime.now(datetime.timezone.utc) + self._next_iteration = datetime.datetime.now(datetime.UTC) try: await self._try_sleep_until(self._next_iteration) while True: @@ -225,7 +225,7 @@ async def _loop(self, *args: Any, **kwargs: Any) -> None: if self._stop_next_iteration: return - now = datetime.datetime.now(datetime.timezone.utc) + now = datetime.datetime.now(datetime.UTC) if now > self._next_iteration: self._next_iteration = now if self._time is not MISSING: @@ -629,7 +629,7 @@ def _get_next_sleep_time(self) -> datetime.datetime: if self._current_loop == 0: # if we're at the last index on the first iteration, we need to sleep until tomorrow return datetime.datetime.combine( - datetime.datetime.now(self._time[0].tzinfo or datetime.timezone.utc) + datetime.datetime.now(self._time[0].tzinfo or datetime.UTC) + datetime.timedelta(days=1), self._time[0], ) @@ -640,17 +640,15 @@ def _get_next_sleep_time(self) -> datetime.datetime: self._time_index += 1 if ( next_time - > datetime.datetime.now( - next_time.tzinfo or datetime.timezone.utc - ).timetz() + > datetime.datetime.now(next_time.tzinfo or datetime.UTC).timetz() ): return datetime.datetime.combine( - datetime.datetime.now(next_time.tzinfo or datetime.timezone.utc), + datetime.datetime.now(next_time.tzinfo or datetime.UTC), next_time, ) else: return datetime.datetime.combine( - datetime.datetime.now(next_time.tzinfo or datetime.timezone.utc) + datetime.datetime.now(next_time.tzinfo or datetime.UTC) + datetime.timedelta(days=1), next_time, ) @@ -672,7 +670,7 @@ def _prepare_time_index(self, now: datetime.datetime = MISSING) -> None: time_now = ( now if now is not MISSING - else datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0) + else datetime.datetime.now(datetime.UTC).replace(microsecond=0) ) for idx, time in enumerate(self._time): if time >= time_now.astimezone(time.tzinfo).timetz(): @@ -686,7 +684,7 @@ def _get_time_parameter( time: datetime.time | Sequence[datetime.time], *, dt: type[datetime.time] = datetime.time, - utc: datetime.timezone = datetime.timezone.utc, + utc: datetime.timezone = datetime.UTC, ) -> list[datetime.time]: if isinstance(time, dt): inner = time if time.tzinfo is not None else time.replace(tzinfo=utc) diff --git a/discord/gateway.py b/discord/gateway.py index a31922128b..eda4eeaec3 100644 --- a/discord/gateway.py +++ b/discord/gateway.py @@ -43,7 +43,7 @@ from .errors import ConnectionClosed, InvalidArgument if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from .client import Client from .state import ConnectionState @@ -646,13 +646,13 @@ async def poll_event(self) -> None: ): _log.debug("Received %s", msg) raise WebSocketClosure - except (asyncio.TimeoutError, WebSocketClosure) as e: + except (TimeoutError, WebSocketClosure) as e: # Ensure the keep alive handler is closed if self._keep_alive: self._keep_alive.stop() self._keep_alive = None - if isinstance(e, asyncio.TimeoutError): + if isinstance(e, TimeoutError): _log.info("Timed out receiving packet. Attempting a reconnect.") raise ReconnectWebSocket(self.shard_id) from None diff --git a/discord/guild.py b/discord/guild.py index b485123b52..8b4f872578 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -3995,7 +3995,7 @@ async def query_members( Raises ------ - asyncio.TimeoutError + TimeoutError The query timed out waiting for the members. ValueError Invalid parameters were passed to the function diff --git a/discord/integrations.py b/discord/integrations.py index f5d67faad8..eb6a4f4cb2 100644 --- a/discord/integrations.py +++ b/discord/integrations.py @@ -288,7 +288,7 @@ async def sync(self) -> None: Syncing the integration failed. """ await self._state.http.sync_integration(self.guild.id, self.id) - self.synced_at = datetime.datetime.now(datetime.timezone.utc) + self.synced_at = datetime.datetime.now(datetime.UTC) class IntegrationApplication: diff --git a/discord/message.py b/discord/message.py index e1633aa97e..6290a11fff 100644 --- a/discord/message.py +++ b/discord/message.py @@ -266,14 +266,14 @@ def expires_at(self) -> datetime.datetime | None: """This attachment URL's expiry time in UTC.""" if not self._ex: return None - return datetime.datetime.utcfromtimestamp(int(self._ex, 16)) + return datetime.datetime.fromtimestamp(int(self._ex, 16), tz=datetime.UTC) @property def issued_at(self) -> datetime.datetime | None: """The attachment URL's issue time in UTC.""" if not self._is: return None - return datetime.datetime.utcfromtimestamp(int(self._is, 16)) + return datetime.datetime.fromtimestamp(int(self._is, 16), tz=datetime.UTC) def is_spoiler(self) -> bool: """Whether this attachment contains a spoiler.""" diff --git a/discord/player.py b/discord/player.py index 007bd7e8d7..14ff8f4dd2 100644 --- a/discord/player.py +++ b/discord/player.py @@ -49,7 +49,7 @@ from .utils import MISSING if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from .voice import VoiceClient diff --git a/discord/raw_models.py b/discord/raw_models.py index 291a44fadc..423e51d6a6 100644 --- a/discord/raw_models.py +++ b/discord/raw_models.py @@ -516,7 +516,7 @@ def __init__(self, data: TypingEvent) -> None: self.channel_id: int = int(data["channel_id"]) self.user_id: int = int(data["user_id"]) self.when: datetime.datetime = datetime.datetime.fromtimestamp( - data.get("timestamp"), tz=datetime.timezone.utc + data.get("timestamp"), tz=datetime.UTC ) self.member: Member | None = None self.guild_id: int | None = utils._get_as_snowflake(data, "guild_id") diff --git a/discord/role.py b/discord/role.py index e7d24a6f83..7d0da1e2eb 100644 --- a/discord/role.py +++ b/discord/role.py @@ -26,9 +26,9 @@ from __future__ import annotations from contextlib import suppress -from typing import TYPE_CHECKING, Any, TypeVar +from typing import TYPE_CHECKING, Any, Self, TypeVar -from typing_extensions import Self, deprecated +from typing_extensions import deprecated from .asset import Asset from .colour import Colour diff --git a/discord/shard.py b/discord/shard.py index 6d5dfc4dbf..740ac555b1 100644 --- a/discord/shard.py +++ b/discord/shard.py @@ -114,7 +114,7 @@ def __init__( GatewayNotFound, ConnectionClosed, aiohttp.ClientError, - asyncio.TimeoutError, + TimeoutError, ) @property diff --git a/discord/state.py b/discord/state.py index df4fc51056..690e35053b 100644 --- a/discord/state.py +++ b/discord/state.py @@ -617,7 +617,7 @@ async def query_members( nonce=request.nonce, ) return await asyncio.wait_for(request.wait(), timeout=30.0) - except asyncio.TimeoutError: + except TimeoutError: _log.warning( ( "Timed out waiting for chunks with query %r and limit %d for" @@ -645,7 +645,7 @@ async def _delay_ready(self) -> None: guild = await asyncio.wait_for( self._ready_state.get(), timeout=self.guild_ready_timeout ) - except asyncio.TimeoutError: + except TimeoutError: break else: if self._guild_needs_chunking(guild): @@ -659,7 +659,7 @@ async def _delay_ready(self) -> None: for guild, future in states: try: await asyncio.wait_for(future, timeout=5.0) - except asyncio.TimeoutError: + except TimeoutError: _log.warning( "Shard ID %s timed out waiting for chunks for guild_id %s.", guild.shard_id, @@ -1460,7 +1460,7 @@ async def chunk_guild(self, guild, *, wait=True, cache=None): async def _chunk_and_dispatch(self, guild, unavailable): try: await asyncio.wait_for(self.chunk_guild(guild), timeout=60.0) - except asyncio.TimeoutError: + except TimeoutError: _log.info("Somehow timed out waiting for chunks.") if unavailable is False: @@ -2188,7 +2188,7 @@ async def _delay_ready(self) -> None: guild = await asyncio.wait_for( self._ready_state.get(), timeout=self.guild_ready_timeout ) - except asyncio.TimeoutError: + except TimeoutError: break else: if self._guild_needs_chunking(guild): @@ -2204,7 +2204,7 @@ async def _delay_ready(self) -> None: await utils.sane_wait_for( current_bucket, timeout=max_concurrency * 70.0 ) - except asyncio.TimeoutError: + except TimeoutError: fmt = ( "Shard ID %s failed to wait for chunks from a" " sub-bucket with length %d" @@ -2229,7 +2229,7 @@ async def _delay_ready(self) -> None: timeout = 61 * (len(children) / 110) try: await utils.sane_wait_for(futures, timeout=timeout) - except asyncio.TimeoutError: + except TimeoutError: _log.warning( ( "Shard ID %s failed to wait for chunks (timeout=%.2f) for %d" diff --git a/discord/types/activity.py b/discord/types/activity.py index 96d824fb53..d129b5e2bc 100644 --- a/discord/types/activity.py +++ b/discord/types/activity.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from .snowflake import Snowflake from .user import PartialUser diff --git a/discord/types/appinfo.py b/discord/types/appinfo.py index 8fdccf35f2..c3d2df5d2b 100644 --- a/discord/types/appinfo.py +++ b/discord/types/appinfo.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from .guild import Guild from .snowflake import Snowflake diff --git a/discord/types/application_role_connection.py b/discord/types/application_role_connection.py index cf797b8efa..57b7883976 100644 --- a/discord/types/application_role_connection.py +++ b/discord/types/application_role_connection.py @@ -24,9 +24,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict ApplicationRoleConnectionMetadataType = Literal[1, 2, 3, 4, 5, 6, 7, 8] diff --git a/discord/types/audit_log.py b/discord/types/audit_log.py index 0575457e7a..459e342f30 100644 --- a/discord/types/audit_log.py +++ b/discord/types/audit_log.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal, Union - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict, Union from .automod import AutoModRule from .channel import ChannelType, PermissionOverwrite, VideoQualityMode diff --git a/discord/types/automod.py b/discord/types/automod.py index 0417e78497..787375891d 100644 --- a/discord/types/automod.py +++ b/discord/types/automod.py @@ -22,9 +22,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from .snowflake import Snowflake diff --git a/discord/types/channel.py b/discord/types/channel.py index d4661cf8c4..5c436c9868 100644 --- a/discord/types/channel.py +++ b/discord/types/channel.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal, Union - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict, Union from ..enums import SortOrder from ..flags import ChannelFlags diff --git a/discord/types/components.py b/discord/types/components.py index af567b6566..68119998ad 100644 --- a/discord/types/components.py +++ b/discord/types/components.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal, Union - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict, Union from .channel import ChannelType from .emoji import PartialEmoji diff --git a/discord/types/embed.py b/discord/types/embed.py index 7d39c1630d..10ab3d2fd0 100644 --- a/discord/types/embed.py +++ b/discord/types/embed.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict class EmbedFooter(TypedDict): diff --git a/discord/types/guild.py b/discord/types/guild.py index 3c71c8647e..e84df03939 100644 --- a/discord/types/guild.py +++ b/discord/types/guild.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, Required, TypedDict +from typing import Literal, NotRequired, Required, TypedDict from .activity import PartialPresenceUpdate from .channel import GuildChannel diff --git a/discord/types/integration.py b/discord/types/integration.py index 1a6c8b70f1..34c27f1b91 100644 --- a/discord/types/integration.py +++ b/discord/types/integration.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal, Union - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict, Union from .snowflake import Snowflake from .user import User diff --git a/discord/types/interactions.py b/discord/types/interactions.py index da1c345f64..9837e304d6 100644 --- a/discord/types/interactions.py +++ b/discord/types/interactions.py @@ -42,7 +42,7 @@ from .message import AllowedMentions, Message from ..interactions import InteractionChannel -from typing_extensions import NotRequired, TypedDict +from typing import NotRequired, TypedDict ApplicationCommandType = Literal[1, 2, 3] diff --git a/discord/types/invite.py b/discord/types/invite.py index 584b573094..5c06eda3d2 100644 --- a/discord/types/invite.py +++ b/discord/types/invite.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal, Union - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict, Union from .appinfo import PartialAppInfo from .channel import PartialChannel diff --git a/discord/types/member.py b/discord/types/member.py index ce582935d4..749ab6a448 100644 --- a/discord/types/member.py +++ b/discord/types/member.py @@ -23,9 +23,7 @@ DEALINGS IN THE SOFTWARE. """ -from typing import TypedDict - -from typing_extensions import NotRequired +from typing import NotRequired, TypedDict from .collectibles import AvatarDecoration, Collectibles from .snowflake import Snowflake, SnowflakeList diff --git a/discord/types/message.py b/discord/types/message.py index bea906ffe1..515c66efbe 100644 --- a/discord/types/message.py +++ b/discord/types/message.py @@ -41,7 +41,7 @@ if TYPE_CHECKING: from .interactions import InteractionMetadata, MessageInteraction -from typing_extensions import NotRequired, TypedDict +from typing import NotRequired, TypedDict class ChannelMention(TypedDict): diff --git a/discord/types/monetization.py b/discord/types/monetization.py index 13ed22ccc3..5f45ca8d31 100644 --- a/discord/types/monetization.py +++ b/discord/types/monetization.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from .snowflake import Snowflake diff --git a/discord/types/onboarding.py b/discord/types/onboarding.py index 31dda34ca8..fd5bdc91d4 100644 --- a/discord/types/onboarding.py +++ b/discord/types/onboarding.py @@ -24,9 +24,7 @@ from __future__ import annotations -from typing import Literal, TypedDict - -from typing_extensions import NotRequired +from typing import Literal, NotRequired, TypedDict from .emoji import Emoji from .snowflake import Snowflake, SnowflakeList diff --git a/discord/types/poll.py b/discord/types/poll.py index ed747f93da..892e1cb6dd 100644 --- a/discord/types/poll.py +++ b/discord/types/poll.py @@ -24,9 +24,7 @@ from __future__ import annotations -from typing import Literal, TypedDict - -from typing_extensions import NotRequired +from typing import Literal, NotRequired, TypedDict from .emoji import Emoji diff --git a/discord/types/raw_models.py b/discord/types/raw_models.py index 7ec8a446ca..fa02d8696f 100644 --- a/discord/types/raw_models.py +++ b/discord/types/raw_models.py @@ -25,7 +25,7 @@ from __future__ import annotations -from typing_extensions import NotRequired, TypedDict +from typing import NotRequired, TypedDict from .automod import AutoModAction, AutoModTriggerType from .emoji import PartialEmoji diff --git a/discord/types/role.py b/discord/types/role.py index 5e672eb636..77efada149 100644 --- a/discord/types/role.py +++ b/discord/types/role.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from .snowflake import Snowflake diff --git a/discord/types/soundboard.py b/discord/types/soundboard.py index 9a4c19b0ed..04b7678a58 100644 --- a/discord/types/soundboard.py +++ b/discord/types/soundboard.py @@ -24,7 +24,7 @@ from __future__ import annotations -from typing_extensions import NotRequired, TypedDict +from typing import NotRequired, TypedDict from discord.types.user import User diff --git a/discord/types/sticker.py b/discord/types/sticker.py index 95945462e6..18f0465904 100644 --- a/discord/types/sticker.py +++ b/discord/types/sticker.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal, Union - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict, Union from .snowflake import Snowflake from .user import User diff --git a/discord/types/threads.py b/discord/types/threads.py index 447027183b..ac5cf6ecad 100644 --- a/discord/types/threads.py +++ b/discord/types/threads.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from ..flags import ChannelFlags from .snowflake import Snowflake diff --git a/discord/types/voice.py b/discord/types/voice.py index 307f98cee3..9343f00075 100644 --- a/discord/types/voice.py +++ b/discord/types/voice.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from .member import MemberWithUser from .snowflake import Snowflake diff --git a/discord/types/webhook.py b/discord/types/webhook.py index a312b30a41..0ff463d038 100644 --- a/discord/types/webhook.py +++ b/discord/types/webhook.py @@ -25,9 +25,7 @@ from __future__ import annotations -from typing import Literal - -from typing_extensions import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from .channel import PartialChannel from .snowflake import Snowflake diff --git a/discord/types/widget.py b/discord/types/widget.py index d327fa58fb..f1dee1e1da 100644 --- a/discord/types/widget.py +++ b/discord/types/widget.py @@ -25,7 +25,7 @@ from __future__ import annotations -from typing_extensions import NotRequired, TypedDict +from typing import NotRequired, TypedDict from .activity import Activity from .snowflake import Snowflake diff --git a/discord/ui/action_row.py b/discord/ui/action_row.py index 730a1b2905..25ba8914dd 100644 --- a/discord/ui/action_row.py +++ b/discord/ui/action_row.py @@ -40,7 +40,7 @@ __all__ = ("ActionRow",) if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from ..emoji import AppEmoji, GuildEmoji from ..partial_emoji import PartialEmoji, _EmojiTag diff --git a/discord/ui/checkbox_group.py b/discord/ui/checkbox_group.py index ed155898ab..b29a2bf760 100644 --- a/discord/ui/checkbox_group.py +++ b/discord/ui/checkbox_group.py @@ -25,9 +25,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING - -from typing_extensions import Self +from typing import TYPE_CHECKING, Self from ..components import CheckboxGroup as CheckboxGroupComponent from ..components import CheckboxGroupOption diff --git a/discord/ui/container.py b/discord/ui/container.py index e35c05f7d6..ecb1f20f3f 100644 --- a/discord/ui/container.py +++ b/discord/ui/container.py @@ -45,7 +45,7 @@ __all__ = ("Container",) if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from ..types.components import ContainerComponent as ContainerComponentPayload from .view import DesignerView diff --git a/discord/ui/core.py b/discord/ui/core.py index f7acaaf114..d99285a860 100644 --- a/discord/ui/core.py +++ b/discord/ui/core.py @@ -36,7 +36,7 @@ if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from .view import View diff --git a/discord/ui/label.py b/discord/ui/label.py index 720b02b226..583a140bfe 100644 --- a/discord/ui/label.py +++ b/discord/ui/label.py @@ -51,7 +51,7 @@ __all__ = ("Label",) if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from ..emoji import AppEmoji, GuildEmoji from ..interactions import Interaction diff --git a/discord/ui/media_gallery.py b/discord/ui/media_gallery.py index ff74d6b1ce..2b33f20528 100644 --- a/discord/ui/media_gallery.py +++ b/discord/ui/media_gallery.py @@ -34,7 +34,7 @@ __all__ = ("MediaGallery",) if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from ..types.components import MediaGalleryComponent as MediaGalleryComponentPayload from .view import DesignerView diff --git a/discord/ui/modal.py b/discord/ui/modal.py index bb5fabc8ee..ca5c71ede3 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -51,7 +51,7 @@ if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from ..interactions import Interaction from ..state import ConnectionState diff --git a/discord/ui/radio_group.py b/discord/ui/radio_group.py index 3cc2f2457c..3d9cb59bd0 100644 --- a/discord/ui/radio_group.py +++ b/discord/ui/radio_group.py @@ -25,9 +25,7 @@ from __future__ import annotations import os -from typing import TYPE_CHECKING - -from typing_extensions import Self +from typing import TYPE_CHECKING, Self from ..components import RadioGroup as RadioGroupComponent from ..components import RadioGroupOption diff --git a/discord/ui/section.py b/discord/ui/section.py index f0a5f95ac0..b269e346ec 100644 --- a/discord/ui/section.py +++ b/discord/ui/section.py @@ -40,7 +40,7 @@ __all__ = ("Section",) if TYPE_CHECKING: - from typing_extensions import Self + from typing import Self from ..types.components import SectionComponent as SectionComponentPayload from .view import DesignerView diff --git a/discord/ui/select.py b/discord/ui/select.py index df18c9385c..637d7cdca1 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -29,9 +29,9 @@ import os from collections.abc import Callable, Sequence from functools import partial -from typing import TYPE_CHECKING, Any, Generic, Literal, overload +from typing import TYPE_CHECKING, Any, Generic, Literal, Self, overload -from typing_extensions import Self, TypeVar +from typing_extensions import TypeVar from ..channel import _threaded_guild_channel_factory from ..components import SelectDefaultValue, SelectMenu, SelectOption diff --git a/discord/ui/view.py b/discord/ui/view.py index fdc478b868..8305a79ea3 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -38,11 +38,10 @@ TYPE_CHECKING, Any, ClassVar, + Self, TypeVar, ) -from typing_extensions import Self - from ..components import ActionRow as ActionRowComponent from ..components import Button as ButtonComponent from ..components import Checkbox as CheckboxComponent diff --git a/discord/utils.py b/discord/utils.py index 44fc48a6eb..0376c7e154 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -53,6 +53,7 @@ Sequence, ) from inspect import isawaitable as _isawaitable +from inspect import iscoroutinefunction as _iscoroutinefunction from inspect import signature as _signature from operator import attrgetter from typing import ( @@ -460,7 +461,7 @@ def snowflake_time(id: int) -> datetime.datetime: An aware datetime in UTC representing the creation time of the snowflake. """ timestamp = ((id >> 22) + DISCORD_EPOCH) / 1000 - return datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc) + return datetime.datetime.fromtimestamp(timestamp, tz=datetime.UTC) def time_snowflake(dt: datetime.datetime, high: bool = False) -> int: @@ -863,7 +864,7 @@ def _parse_ratelimit_header(request: Any, *, use_clock: bool = False) -> float: reset_after: str | None = request.headers.get("X-Ratelimit-Reset-After") if not use_clock and reset_after: return float(reset_after) - utc = datetime.timezone.utc + utc = datetime.UTC now = datetime.datetime.now(utc) reset = datetime.datetime.fromtimestamp( float(request.headers["X-Ratelimit-Reset"]), utc @@ -895,7 +896,7 @@ async def sane_wait_for(futures, *, timeout): ) if len(pending) != 0: - raise asyncio.TimeoutError() + raise TimeoutError() return done @@ -911,7 +912,7 @@ def get_slots(cls: type[Any]) -> Iterator[str]: def compute_timedelta(dt: datetime.datetime): if dt.tzinfo is None: dt = dt.astimezone() - now = datetime.datetime.now(datetime.timezone.utc) + now = datetime.datetime.now(datetime.UTC) return max((dt - now).total_seconds(), 0) @@ -949,7 +950,7 @@ def utcnow() -> datetime.datetime: :class:`datetime.datetime` The current aware datetime in UTC. """ - return datetime.datetime.now(datetime.timezone.utc) + return datetime.datetime.now(datetime.UTC) def valid_icon_size(size: int) -> bool: @@ -1533,7 +1534,7 @@ async def autocomplete_callback(ctx: AutocompleteContext) -> V: if callable(_values): _values = _values(ctx) - if asyncio.iscoroutine(_values): + if _isawaitable(_values): _values = await _values if filter is None: @@ -1544,7 +1545,7 @@ def _filter(ctx: AutocompleteContext, item: Any) -> bool: gen = (val for val in _values if _filter(ctx, val)) - elif asyncio.iscoroutinefunction(filter): + elif _iscoroutinefunction(filter): gen = (val for val in _values if await filter(ctx, val)) elif callable(filter): diff --git a/discord/voice/client.py b/discord/voice/client.py index 64d879ac95..bfac23c92f 100644 --- a/discord/voice/client.py +++ b/discord/voice/client.py @@ -390,7 +390,7 @@ async def move_to( Raises ------ - asyncio.TimeoutError + TimeoutError Waiting for channel move timed out. """ await self._connection.move_to(channel, timeout) diff --git a/discord/voice/gateway.py b/discord/voice/gateway.py index 7b2aa3a17d..5ecbcf7939 100644 --- a/discord/voice/gateway.py +++ b/discord/voice/gateway.py @@ -50,8 +50,9 @@ from .enums import OpCodes if TYPE_CHECKING: + from typing import Self + from _typeshed import ConvertibleToInt - from typing_extensions import Self from .state import VoiceConnectionState diff --git a/discord/voice/state.py b/discord/voice/state.py index e6d622df56..48b86d9d5e 100644 --- a/discord/voice/state.py +++ b/discord/voice/state.py @@ -491,7 +491,7 @@ async def _wrap_connect( _log.debug("Cancelling voice connection") await self.soft_disconnect() raise - except asyncio.TimeoutError: + except TimeoutError: _log.info("Timed out while connecting to voice") await self.disconnect() raise @@ -652,7 +652,7 @@ async def move_to( try: await self._wait_for_state(ConnectionFlowState.connected, timeout=timeout) - except asyncio.TimeoutError: + except TimeoutError: _log.warning( "Timed out trying to move to channel %s in guild %s", channel.id, @@ -707,7 +707,7 @@ async def _wait_for_state( if pending: # if we're here, it means that the state event # has timed out, so just raise the exception - raise asyncio.TimeoutError + raise TimeoutError async def _voice_connect( self, *, self_deaf: bool = False, self_mute: bool = False @@ -764,7 +764,7 @@ async def _poll_ws(self, reconnect: bool) -> None: await self.ws.poll_event() except asyncio.CancelledError: return - except (ConnectionClosed, asyncio.TimeoutError) as exc: + except (ConnectionClosed, TimeoutError) as exc: if isinstance(exc, ConnectionClosed): # 1000 - normal closure - not resumable # 4014 - externally disconnected - not resumable @@ -823,7 +823,7 @@ async def _poll_ws(self, reconnect: bool) -> None: self_mute=(self.self_voice_state or self).self_mute, resume=True, ) - except asyncio.TimeoutError: + except TimeoutError: _log.info( "Could not resume the voice connection... Disconnecting..." ) @@ -870,7 +870,7 @@ async def _poll_ws(self, reconnect: bool) -> None: self_mute=(self.self_voice_state or self).self_mute, resume=False, ) - except asyncio.TimeoutError: + except TimeoutError: _log.warning("Could not connect to voice... Retrying...") continue @@ -882,7 +882,7 @@ async def _potential_reconnect(self) -> bool: ConnectionFlowState.disconnected, timeout=self.timeout, ) - except asyncio.TimeoutError: + except TimeoutError: return False else: if self.state is ConnectionFlowState.disconnected: @@ -893,7 +893,7 @@ async def _potential_reconnect(self) -> bool: try: self.ws = await self._connect_websocket(False) await self._handshake_websocket() - except (ConnectionClosed, asyncio.TimeoutError): + except (ConnectionClosed, TimeoutError): return False else: return True diff --git a/docs/installing.rst b/docs/installing.rst index 9be3e72cdb..6331b76b6c 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -13,8 +13,8 @@ in creating applications that utilise the Discord API. Prerequisites ------------- -Pycord works with Python 3.10 or higher. Support for earlier versions of Python -is not provided. Python 2.7 or lower is not supported. Python 3.8 or lower is not supported. +Pycord works with Python 3.11 or higher. Support for earlier versions of Python +is not provided. .. _installing: diff --git a/examples/background_task.py b/examples/background_task.py index 9bba50abda..2a411786f2 100644 --- a/examples/background_task.py +++ b/examples/background_task.py @@ -1,6 +1,6 @@ import asyncio import random -from datetime import time, timezone +from datetime import UTC, time import discord from discord.ext import tasks @@ -26,9 +26,7 @@ async def my_background_task(self): self.counter += 1 await channel.send(str(self.counter)) - @tasks.loop( - time=time(3, 0, tzinfo=timezone.utc) - ) # Task that runs every day at 3 AM UTC + @tasks.loop(time=time(3, 0, tzinfo=UTC)) # Task that runs every day at 3 AM UTC async def time_task(self): channel = self.get_channel(1234567) # Your Channel ID goes here await channel.send("It's 3 AM!") diff --git a/pyproject.toml b/pyproject.toml index 45258ea5e7..4e22494f95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ authors = [ ] description = "A Python wrapper for the Discord API" readme = { content-type = "text/x-rst", file = "README.rst" } -requires-python = ">=3.10, <3.15" +requires-python = ">=3.11, <3.16" license = "MIT" license-files = ["LICENSE"] classifiers = [ @@ -20,11 +20,11 @@ classifiers = [ "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", "Topic :: Internet", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", @@ -85,7 +85,7 @@ test = [ [tool.setuptools_scm] [tool.black] -target-version = ['py310', 'py311', 'py312', 'py313', 'py314'] +target-version = ['py311', 'py312', 'py313', 'py314', 'py315'] [tool.isort] profile = "black" @@ -104,7 +104,7 @@ extension-pkg-whitelist = [ "pydantic", "ujson" ] -py-version = 3.10 +py-version = 3.11 [tool.pylint.messages_control] enable = [ @@ -126,7 +126,7 @@ asyncio_default_fixture_loop_scope = "function" [tool.tox] requires = ["tox>=4"] -env_list = ["3.14", "3.13", "3.12", "3.11", "3.10", "3.14-novoice"] +env_list = ["3.15", "3.14", "3.13", "3.12", "3.11", "3.15-novoice"] [tool.tox.env_run_base] description = "run unit tests" @@ -134,7 +134,7 @@ commands = [["pytest", { replace = "posargs", default = ["tests"], extend = true dependency_groups = ["test"] extras = ["voice"] -[tool.tox.env."3.14-novoice"] +[tool.tox.env."3.15-novoice"] description = "run import and warning tests without the voice extra" commands = [[ "pytest", @@ -145,8 +145,8 @@ extras = [] # GitHub actions [tool.tox.gh.python] -"3.14" = ["3.14", "3.14-novoice"] +"3.15" = ["3.15", "3.15-novoice"] +"3.14" = ["3.14"] "3.13" = ["3.13"] "3.12" = ["3.12"] "3.11" = ["3.11"] -"3.10" = ["3.10"]