From 85552aa1a0eb789685cf3b1ad79dbcabfd9b2759 Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:52:26 -0400 Subject: [PATCH 1/5] feat: add proper typing support for bridge contexts --- discord/ext/bridge/context.py | 58 +++++++++++++++++++++++++++++++++++ docs/ext/bridge/api.rst | 5 ++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/discord/ext/bridge/context.py b/discord/ext/bridge/context.py index 8e7f9414f6..ea3e335269 100644 --- a/discord/ext/bridge/context.py +++ b/discord/ext/bridge/context.py @@ -25,10 +25,14 @@ from __future__ import annotations +import asyncio from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Union, overload +from typing_extensions import Self + from discord.commands import ApplicationContext +from discord.context_managers import Typing from discord.interactions import Interaction, InteractionMessage from discord.message import Message from discord.webhook import WebhookMessage @@ -36,12 +40,48 @@ from ..commands import Context if TYPE_CHECKING: + from types import TracebackType + from .core import BridgeExtCommand, BridgeSlashCommand __all__ = ("BridgeContext", "BridgeExtContext", "BridgeApplicationContext", "Context") +class DeferTyping: + def __init__(self, ctx: BridgeApplicationContext, ephemeral: bool) -> None: + self.loop: asyncio.AbstractEventLoop = ctx._state.loop + self.ctx = ctx + self.ephemeral = ephemeral + + async def do_defer(self) -> None: + await self.ctx.defer(ephemeral=self.ephemeral) + + def __enter__(self) -> Self: + self.task: asyncio.Task = self.loop.create_task(self.do_defer()) + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + self.task.cancel() + + async def __aenter__(self) -> Self: + await self.do_defer() + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: + pass + + class BridgeContext(ABC): """ The base context class for compatibility commands. This class is an :term:`abstract base class` (also known as an @@ -74,6 +114,9 @@ async def _respond( @abstractmethod async def _defer(self, *args, **kwargs) -> None: ... + @abstractmethod + def _typing(self, *args, **kwargs) -> Typing | DeferTyping: ... + @abstractmethod async def _edit(self, *args, **kwargs) -> InteractionMessage | Message: ... @@ -111,6 +154,14 @@ async def defer(self, *args, **kwargs) -> None: """ return await self._defer(*args, **kwargs) + def typing(self, *args, **kwargs) -> Typing | DeferTyping: + """ + Returns a context manager that allows you to type for an indefinite period of time. + In :class:`BridgeExtContext`, this will be :meth:`~Context.typing` while in :class:`BridgeApplicationContext`, + this is equivalent to a defer() call and does not do any typing calls. + """ + return self._typing(*args, **kwargs) + async def edit(self, *args, **kwargs) -> InteractionMessage | Message: """|coro| @@ -147,6 +198,9 @@ async def _respond(self, *args, **kwargs) -> Interaction | WebhookMessage: async def _defer(self, *args, **kwargs) -> None: return await self._get_super("defer")(*args, **kwargs) + def _typing(self, *args, **kwargs) -> DeferTyping: + return DeferTyping(self, *args, **kwargs) + async def _edit(self, *args, **kwargs) -> InteractionMessage: return await self._get_super("edit")(*args, **kwargs) @@ -174,6 +228,10 @@ async def _defer(self, *args, **kwargs) -> None: kwargs.pop("ephemeral", None) return await self._get_super("trigger_typing")(*args, **kwargs) + def _typing(self, *args, **kwargs) -> Typing: + kwargs.pop("ephemeral", None) + return self._get_super("typing")(*args, **kwargs) + async def _edit(self, *args, **kwargs) -> Message | None: if self._original_response_message: return await self._original_response_message.edit(*args, **kwargs) diff --git a/docs/ext/bridge/api.rst b/docs/ext/bridge/api.rst index 63fe6d18c0..feb954bf60 100644 --- a/docs/ext/bridge/api.rst +++ b/docs/ext/bridge/api.rst @@ -137,7 +137,10 @@ BridgeContext .. autoclass:: discord.ext.bridge.BridgeContext :members: - :exclude-members: _respond, _defer, _edit, _get_super + :exclude-members: _respond, _defer, _typing, _edit, _get_super, typing + + .. automethod:: typing + :async-with: BridgeContext Subclasses ~~~~~~~~~~~~~~~~~~~~~~~~ From 28e4f06b56bcbf47bf664ee5540cf24d92dbcaed Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Wed, 3 Jun 2026 23:11:37 -0400 Subject: [PATCH 2/5] refactor: use __enter__ for __aenter__ --- discord/ext/bridge/context.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/discord/ext/bridge/context.py b/discord/ext/bridge/context.py index ea3e335269..664c2db0e9 100644 --- a/discord/ext/bridge/context.py +++ b/discord/ext/bridge/context.py @@ -70,8 +70,7 @@ def __exit__( self.task.cancel() async def __aenter__(self) -> Self: - await self.do_defer() - return self + return self.__enter__() async def __aexit__( self, From 87997ea5ea452326f9197036b2265d9a85454ec3 Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Mon, 8 Jun 2026 22:22:49 -0400 Subject: [PATCH 3/5] fix: make async context manager cancel on exit --- discord/ext/bridge/context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ext/bridge/context.py b/discord/ext/bridge/context.py index 664c2db0e9..b8abd87d9c 100644 --- a/discord/ext/bridge/context.py +++ b/discord/ext/bridge/context.py @@ -78,7 +78,7 @@ async def __aexit__( exc_value: BaseException | None, traceback: TracebackType | None, ) -> None: - pass + return self.__exit__(exc_type, exc_value, traceback) class BridgeContext(ABC): From f310aafd20ff5aa61a32f537592b6316acd47661 Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Mon, 8 Jun 2026 22:24:21 -0400 Subject: [PATCH 4/5] fix: add DeferTyping to `__all__` --- discord/ext/bridge/context.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/discord/ext/bridge/context.py b/discord/ext/bridge/context.py index b8abd87d9c..335ff576a5 100644 --- a/discord/ext/bridge/context.py +++ b/discord/ext/bridge/context.py @@ -45,7 +45,13 @@ from .core import BridgeExtCommand, BridgeSlashCommand -__all__ = ("BridgeContext", "BridgeExtContext", "BridgeApplicationContext", "Context") +__all__ = ( + "BridgeContext", + "BridgeExtContext", + "BridgeApplicationContext", + "Context", + "DeferTyping", +) class DeferTyping: From 6e7baf59ad26cc9f585e20836ec3d30370ee73b3 Mon Sep 17 00:00:00 2001 From: Astrea <25420078+AstreaTSS@users.noreply.github.com> Date: Mon, 8 Jun 2026 22:26:56 -0400 Subject: [PATCH 5/5] chore: update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c63b835f8c..aa3910bbe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ These changes are available on the `master` branch, but have not yet been releas ### Added +- Add `typing` context manager for Bridge `Context`s. + ([#3251](https://github.com/Pycord-Development/pycord/pull/3251)) + ### Changed ### Fixed