From c6dfebb38a231b7db8478024ea2f4f971080055e Mon Sep 17 00:00:00 2001 From: Paillat Date: Mon, 21 Jul 2025 22:27:17 +0200 Subject: [PATCH 1/2] refactor: replace print statements with logging for better error handling --- discord/__main__.py | 10 ++++++---- discord/bot.py | 7 ++----- discord/client.py | 19 +++++++------------ discord/ext/commands/bot.py | 10 +++++----- discord/ext/tasks/__init__.py | 11 +++++------ discord/opus.py | 4 ++-- discord/player.py | 2 -- discord/ui/modal.py | 2 +- discord/ui/view.py | 2 +- discord/voice_client.py | 4 ++-- 10 files changed, 31 insertions(+), 40 deletions(-) diff --git a/discord/__main__.py b/discord/__main__.py index ed34bdf42a..0b0b04cda9 100644 --- a/discord/__main__.py +++ b/discord/__main__.py @@ -25,6 +25,7 @@ import argparse import importlib.metadata +import logging import platform import sys from pathlib import Path @@ -34,6 +35,7 @@ import discord +_log = logging.getLogger(__name__) def show_version() -> None: entries = [ @@ -239,7 +241,7 @@ def newbot(parser, args) -> None: init = cogs / "__init__.py" init.touch() except OSError as exc: - print(f"warning: could not create cogs directory ({exc})") + _log.exception(f"Could not create cogs directory.") try: with open(str(new_directory / "config.py"), "w", encoding="utf-8") as fp: @@ -259,9 +261,9 @@ def newbot(parser, args) -> None: with open(str(new_directory / ".gitignore"), "w", encoding="utf-8") as fp: fp.write(_gitignore_template) except OSError as exc: - print(f"warning: could not create .gitignore file ({exc})") + _log.exception(f"Could not create .gitignore file.") - print("successfully made bot at", new_directory) + print("Successfully made bot at", new_directory) def newcog(parser, args) -> None: @@ -269,7 +271,7 @@ def newcog(parser, args) -> None: try: cog_dir.mkdir(exist_ok=True) except OSError as exc: - print(f"warning: could not create cogs directory ({exc})") + _log.exception(f"Could not create cogs directory.") directory = cog_dir / to_path(parser, args.name) directory = directory.with_suffix(".py") diff --git a/discord/bot.py b/discord/bot.py index 7dd246afe3..29fb1ace66 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -1240,7 +1240,7 @@ async def on_application_command_error( The default command error handler provided by the bot. - By default, this prints to :data:`sys.stderr` however it could be + By default, this logs with :meth:`logging.exception` however it could be overridden to have a different implementation. This only fires if you do not specify any listeners for command error. @@ -1255,10 +1255,7 @@ async def on_application_command_error( if cog and cog.has_error_handler(): return - print(f"Ignoring exception in command {context.command}:", file=sys.stderr) - traceback.print_exception( - type(exception), exception, exception.__traceback__, file=sys.stderr - ) + _log.error(f"Ignoring exception in command {context.command}.", exc_info=exception) # global check registration # TODO: Remove these from commands.Bot diff --git a/discord/client.py b/discord/client.py index 2d0f1b8770..abcbb57877 100644 --- a/discord/client.py +++ b/discord/client.py @@ -536,11 +536,11 @@ async def on_error(self, event_method: str, *args: Any, **kwargs: Any) -> None: The default error handler provided by the client. - By default, this prints to :data:`sys.stderr` however it could be + By default, this logs with :meth:`logging.error` however it could be overridden to have a different implementation. Check :func:`~discord.on_error` for more details. """ - print(f"Ignoring exception in {event_method}", file=sys.stderr) + _log.error(f"Ignoring exception in {event_method}") traceback.print_exc() async def on_view_error( @@ -553,27 +553,22 @@ async def on_view_error( This only fires for a view if you did not define its :func:`~discord.ui.View.on_error`. """ - print( + _log.error( f"Ignoring exception in view {interaction.view} for item {item}:", - file=sys.stderr, - ) - traceback.print_exception( - error.__class__, error, error.__traceback__, file=sys.stderr + exc_info=error, ) async def on_modal_error(self, error: Exception, interaction: Interaction) -> None: """|coro| The default modal error handler provided by the client. - The default implementation prints the traceback to stderr. + The default implementation logs the traceback with :meth:`logging.error`. This only fires for a modal if you did not define its :func:`~discord.ui.Modal.on_error`. """ - print(f"Ignoring exception in modal {interaction.modal}:", file=sys.stderr) - traceback.print_exception( - error.__class__, error, error.__traceback__, file=sys.stderr - ) + _log.error(f"Ignoring exception in modal {interaction.modal}", exc_info=error) + # hooks diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index d348b6c536..4bc28e651f 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -28,6 +28,7 @@ import collections import collections.abc import sys +import logging import traceback from typing import TYPE_CHECKING, Any, Callable, Coroutine, Iterable, TypeVar @@ -51,6 +52,8 @@ "AutoShardedBot", ) +_log = logging.getLogger(__name__) + MISSING: Any = discord.utils.MISSING T = TypeVar("T") @@ -156,7 +159,7 @@ async def on_command_error( The default command error handler provided by the bot. - By default, this prints to :data:`sys.stderr` however it could be + By default, this logs with :meth:`logging.error` however it could be overridden to have a different implementation. This only fires if you do not specify any listeners for command error. @@ -172,10 +175,7 @@ async def on_command_error( if cog and cog.has_error_handler(): return - print(f"Ignoring exception in command {context.command}:", file=sys.stderr) - traceback.print_exception( - type(exception), exception, exception.__traceback__, file=sys.stderr - ) + logging.error(f"Ignoring exception in command {context.command}", exc_info=exception) async def can_run(self, ctx: Context, *, call_once: bool = False) -> bool: data = self._check_once if call_once else self._checks diff --git a/discord/ext/tasks/__init__.py b/discord/ext/tasks/__init__.py index af34cc6844..40fc047f59 100644 --- a/discord/ext/tasks/__init__.py +++ b/discord/ext/tasks/__init__.py @@ -29,6 +29,7 @@ import datetime import inspect import sys +import logging import traceback from collections.abc import Sequence from typing import Any, Awaitable, Callable, Generic, TypeVar, cast @@ -47,6 +48,7 @@ FT = TypeVar("FT", bound=_func) ET = TypeVar("ET", bound=Callable[[Any, BaseException], Awaitable[Any]]) +_log = logging.getLogger(__name__) class SleepHandle: __slots__ = ("future", "loop", "handle") @@ -474,12 +476,9 @@ def is_running(self) -> bool: async def _error(self, *args: Any) -> None: exception: Exception = args[-1] - print( + _log.error( f"Unhandled exception in internal background task {self.coro.__name__!r}.", - file=sys.stderr, - ) - traceback.print_exception( - type(exception), exception, exception.__traceback__, file=sys.stderr + exc_info=exception, ) def before_loop(self, coro: FT) -> FT: @@ -544,7 +543,7 @@ def error(self, coro: ET) -> ET: The coroutine must take only one argument the exception raised (except ``self`` in a class context). - By default, this prints to :data:`sys.stderr` however it could be + By default, this logs with the logging module however it could be overridden to have a different implementation. .. versionadded:: 1.4 diff --git a/discord/opus.py b/discord/opus.py index 63362b929a..60b2136812 100644 --- a/discord/opus.py +++ b/discord/opus.py @@ -550,7 +550,7 @@ def run(self): data.decrypted_data ) except OpusError: - print("Error occurred while decoding opus frame.") + _log.exception("Error occurred while decoding opus frame.") continue self.client.recv_decoded_audio(data) @@ -560,7 +560,7 @@ def stop(self): time.sleep(0.1) self.decoder = {} gc.collect() - print("Decoder Process Killed") + _log.info("Decoder Process Killed") self._end_thread.set() def get_decoder(self, ssrc): diff --git a/discord/player.py b/discord/player.py index 65b23ed42a..4d2004dee6 100644 --- a/discord/player.py +++ b/discord/player.py @@ -808,8 +808,6 @@ def _call_after(self) -> None: elif error: msg = f"Exception in voice thread {self.name}" _log.exception(msg, exc_info=error) - print(msg, file=sys.stderr) - traceback.print_exception(type(error), error, error.__traceback__) def stop(self) -> None: self._end.set() diff --git a/discord/ui/modal.py b/discord/ui/modal.py index 58cf7db1e6..b5aa747263 100644 --- a/discord/ui/modal.py +++ b/discord/ui/modal.py @@ -259,7 +259,7 @@ async def on_error(self, error: Exception, interaction: Interaction) -> None: A callback that is called when the modal's callback fails with an error. - The default implementation prints the traceback to stderr. + The default implementation logs the traceback with the logging module. Parameters ---------- diff --git a/discord/ui/view.py b/discord/ui/view.py index 64b0520172..bc11b213ef 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -520,7 +520,7 @@ async def on_error( A callback that is called when an item's callback or :meth:`interaction_check` fails with an error. - The default implementation prints the traceback to stderr. + The default implementation logs the traceback with the logging module. Parameters ---------- diff --git a/discord/voice_client.py b/discord/voice_client.py index 75902ecbe2..69c6beccaa 100644 --- a/discord/voice_client.py +++ b/discord/voice_client.py @@ -852,7 +852,7 @@ def recv_audio(self, sink, callback, *args): ready, _, err = select.select([self.socket], [], [self.socket], 0.01) if not ready: if err: - print(f"Socket error: {err}") + _log.error(f"Socket error: {err}") continue try: @@ -869,7 +869,7 @@ def recv_audio(self, sink, callback, *args): result = callback.result() if result is not None: - print(result) + _log.info(result) def recv_decoded_audio(self, data: RawData): # Add silence when they were not being recorded. From 5920a30cd33eaa014773d1e4bd7ed92617c9d92c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 22:15:24 +0000 Subject: [PATCH 2/2] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/__main__.py | 7 ++++--- discord/bot.py | 6 +++--- discord/client.py | 2 -- discord/ext/commands/bot.py | 6 +++--- discord/ext/tasks/__init__.py | 3 +-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/discord/__main__.py b/discord/__main__.py index 0b0b04cda9..f25a7656d3 100644 --- a/discord/__main__.py +++ b/discord/__main__.py @@ -37,6 +37,7 @@ _log = logging.getLogger(__name__) + def show_version() -> None: entries = [ "- Python v{0.major}.{0.minor}.{0.micro}-{0.releaselevel}".format( @@ -240,7 +241,7 @@ def newbot(parser, args) -> None: cogs.mkdir(exist_ok=True) init = cogs / "__init__.py" init.touch() - except OSError as exc: + except OSError: _log.exception(f"Could not create cogs directory.") try: @@ -260,7 +261,7 @@ def newbot(parser, args) -> None: try: with open(str(new_directory / ".gitignore"), "w", encoding="utf-8") as fp: fp.write(_gitignore_template) - except OSError as exc: + except OSError: _log.exception(f"Could not create .gitignore file.") print("Successfully made bot at", new_directory) @@ -270,7 +271,7 @@ def newcog(parser, args) -> None: cog_dir = to_path(parser, args.directory) try: cog_dir.mkdir(exist_ok=True) - except OSError as exc: + except OSError: _log.exception(f"Could not create cogs directory.") directory = cog_dir / to_path(parser, args.name) diff --git a/discord/bot.py b/discord/bot.py index 29fb1ace66..51868eb9df 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -31,8 +31,6 @@ import copy import inspect import logging -import sys -import traceback from abc import ABC, abstractmethod from typing import ( TYPE_CHECKING, @@ -1255,7 +1253,9 @@ async def on_application_command_error( if cog and cog.has_error_handler(): return - _log.error(f"Ignoring exception in command {context.command}.", exc_info=exception) + _log.error( + f"Ignoring exception in command {context.command}.", exc_info=exception + ) # global check registration # TODO: Remove these from commands.Bot diff --git a/discord/client.py b/discord/client.py index abcbb57877..06d63da01d 100644 --- a/discord/client.py +++ b/discord/client.py @@ -28,7 +28,6 @@ import asyncio import logging import signal -import sys import traceback from types import TracebackType from typing import TYPE_CHECKING, Any, Callable, Coroutine, Generator, Sequence, TypeVar @@ -569,7 +568,6 @@ async def on_modal_error(self, error: Exception, interaction: Interaction) -> No _log.error(f"Ignoring exception in modal {interaction.modal}", exc_info=error) - # hooks async def _call_before_identify_hook( diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index 4bc28e651f..8fa464db29 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -27,9 +27,7 @@ import collections import collections.abc -import sys import logging -import traceback from typing import TYPE_CHECKING, Any, Callable, Coroutine, Iterable, TypeVar import discord @@ -175,7 +173,9 @@ async def on_command_error( if cog and cog.has_error_handler(): return - logging.error(f"Ignoring exception in command {context.command}", exc_info=exception) + logging.error( + f"Ignoring exception in command {context.command}", exc_info=exception + ) async def can_run(self, ctx: Context, *, call_once: bool = False) -> bool: data = self._check_once if call_once else self._checks diff --git a/discord/ext/tasks/__init__.py b/discord/ext/tasks/__init__.py index 40fc047f59..15e194f3a4 100644 --- a/discord/ext/tasks/__init__.py +++ b/discord/ext/tasks/__init__.py @@ -28,9 +28,7 @@ import asyncio import datetime import inspect -import sys import logging -import traceback from collections.abc import Sequence from typing import Any, Awaitable, Callable, Generic, TypeVar, cast @@ -50,6 +48,7 @@ _log = logging.getLogger(__name__) + class SleepHandle: __slots__ = ("future", "loop", "handle")