diff --git a/docs/source/conf.py b/docs/source/conf.py index 80e954726af..5cc6d15b6cc 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -77,6 +77,12 @@ # and we document the types anyway autodoc_typehints = "none" +# Show docstring for special members +autodoc_default_options = { + "special-members": True, + "exclude-members": "__init__", +} + # Fail on warnings & unresolved references etc nitpicky = True diff --git a/docs/substitutions/global.rst b/docs/substitutions/global.rst index 151e347afd7..bc5fc74e130 100644 --- a/docs/substitutions/global.rst +++ b/docs/substitutions/global.rst @@ -61,3 +61,5 @@ .. |removed_thumb_url_note| replace:: Removed the deprecated argument and attribute ``thumb_url``. .. |removed_thumb_wildcard_note| replace:: Removed the deprecated arguments and attributes ``thumb_*``. + +.. |async_context_manager| replace:: Asynchronous context manager which \ No newline at end of file diff --git a/telegram/_bot.py b/telegram/_bot.py index 59e69dcc8ae..a798a2b39e7 100644 --- a/telegram/_bot.py +++ b/telegram/_bot.py @@ -149,6 +149,8 @@ class Bot(TelegramObject, AsyncContextManager["Bot"]): finally: await bot.shutdown() + .. seealso:: :meth:`__aenter__` and :meth:`__aexit__`. + Note: * Most bot methods have the argument ``api_kwargs`` which allows passing arbitrary keywords to the Telegram API. This can be used to access new features of the API before they are @@ -310,6 +312,16 @@ def __init__( self._freeze() async def __aenter__(self: BT) -> BT: + """ + |async_context_manager| :meth:`initializes ` the Bot. + + Returns: + The initialized Bot instance. + + Raises: + :exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown` + is called in this case. + """ try: await self.initialize() return self @@ -323,6 +335,7 @@ async def __aexit__( exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: + """|async_context_manager| :meth:`shuts down ` the Bot.""" # Make sure not to return `True` so that exceptions are not suppressed # https://docs.python.org/3/reference/datamodel.html?#object.__aexit__ await self.shutdown() diff --git a/telegram/ext/_application.py b/telegram/ext/_application.py index e7970975ce9..99a5e32f328 100644 --- a/telegram/ext/_application.py +++ b/telegram/ext/_application.py @@ -149,6 +149,8 @@ class Application(Generic[BT, CCT, UD, CD, BD, JQ], AsyncContextManager["Applica finally: await application.shutdown() + .. seealso:: :meth:`__aenter__` and :meth:`__aexit__`. + Examples: :any:`Echo Bot ` @@ -345,7 +347,15 @@ def __init__( self.__create_task_tasks: Set[asyncio.Task] = set() # Used for awaiting tasks upon exit async def __aenter__(self: _AppType) -> _AppType: # noqa: PYI019 - """Simple context manager which initializes the App.""" + """|async_context_manager| :meth:`initializes ` the App. + + Returns: + The initialized App instance. + + Raises: + :exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown` + is called in this case. + """ try: await self.initialize() return self @@ -359,7 +369,7 @@ async def __aexit__( exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: - """Shutdown the App from the context manager.""" + """|async_context_manager| :meth:`shuts down ` the App.""" # Make sure not to return `True` so that exceptions are not suppressed # https://docs.python.org/3/reference/datamodel.html?#object.__aexit__ await self.shutdown() diff --git a/telegram/ext/_baseupdateprocessor.py b/telegram/ext/_baseupdateprocessor.py index 42baa02da9d..36807de0ac4 100644 --- a/telegram/ext/_baseupdateprocessor.py +++ b/telegram/ext/_baseupdateprocessor.py @@ -27,6 +27,25 @@ class BaseUpdateProcessor(ABC): """An abstract base class for update processors. You can use this class to implement your own update processor. + Instances of this class can be used as asyncio context managers, where + + .. code:: python + + async with processor: + # code + + is roughly equivalent to + + .. code:: python + + try: + await processor.initialize() + # code + finally: + await processor.shutdown() + + .. seealso:: :meth:`__aenter__` and :meth:`__aexit__`. + .. seealso:: :wiki:`Concurrency` .. versionadded:: 20.4 @@ -49,7 +68,15 @@ def __init__(self, max_concurrent_updates: int): self._semaphore = BoundedSemaphore(self.max_concurrent_updates) async def __aenter__(self) -> "BaseUpdateProcessor": - """Simple context manager which initializes the Processor.""" + """|async_context_manager| :meth:`initializes ` the Processor. + + Returns: + The initialized Processor instance. + + Raises: + :exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown` + is called in this case. + """ try: await self.initialize() return self @@ -63,7 +90,7 @@ async def __aexit__( exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: - """Simple context manager which shuts down the Processor.""" + """|async_context_manager| :meth:`shuts down ` the Processor.""" await self.shutdown() @property diff --git a/telegram/ext/_updater.py b/telegram/ext/_updater.py index 740ca60d4ea..01d3ec524be 100644 --- a/telegram/ext/_updater.py +++ b/telegram/ext/_updater.py @@ -78,6 +78,8 @@ class Updater(AsyncContextManager["Updater"]): finally: await updater.shutdown() + .. seealso:: :meth:`__aenter__` and :meth:`__aexit__`. + .. seealso:: :wiki:`Architecture Overview `, :wiki:`Builder Pattern ` @@ -126,7 +128,16 @@ def __init__( self.__polling_cleanup_cb: Optional[Callable[[], Coroutine[Any, Any, None]]] = None async def __aenter__(self: _UpdaterType) -> _UpdaterType: # noqa: PYI019 - """Simple context manager which initializes the Updater.""" + """ + |async_context_manager| :meth:`initializes ` the Updater. + + Returns: + The initialized Updater instance. + + Raises: + :exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown` + is called in this case. + """ try: await self.initialize() return self @@ -140,7 +151,7 @@ async def __aexit__( exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: - """Shutdown the Updater from the context manager.""" + """|async_context_manager| :meth:`shuts down ` the Updater.""" # Make sure not to return `True` so that exceptions are not suppressed # https://docs.python.org/3/reference/datamodel.html?#object.__aexit__ await self.shutdown() diff --git a/telegram/request/_baserequest.py b/telegram/request/_baserequest.py index 28404ca2347..39214c82079 100644 --- a/telegram/request/_baserequest.py +++ b/telegram/request/_baserequest.py @@ -70,6 +70,8 @@ class BaseRequest( finally: await request_object.shutdown() + .. seealso:: :meth:`__aenter__` and :meth:`__aexit__`. + Tip: JSON encoding and decoding is done with the standard library's :mod:`json` by default. To use a custom library for this, you can override :meth:`parse_json_payload` and implement @@ -99,6 +101,15 @@ class BaseRequest( """ async def __aenter__(self: RT) -> RT: + """|async_context_manager| :meth:`initializes ` the Request. + + Returns: + The initialized Request instance. + + Raises: + :exc:`Exception`: If an exception is raised during initialization, :meth:`shutdown` + is called in this case. + """ try: await self.initialize() return self @@ -112,6 +123,7 @@ async def __aexit__( exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: + """|async_context_manager| :meth:`shuts down ` the Request.""" # Make sure not to return `True` so that exceptions are not suppressed # https://docs.python.org/3/reference/datamodel.html?#object.__aexit__ await self.shutdown()