Skip to content

Commit

Permalink
Fix issues with startup and logging
Browse files Browse the repository at this point in the history
Remove much of the code execution in package init files to remove import order issues and other undesired side-effects.
  • Loading branch information
super-cooper committed Nov 17, 2024
1 parent c244e2a commit 8d3dc3a
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 34 deletions.
4 changes: 3 additions & 1 deletion memebot/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ async def on_command_error(
) -> None:
command = interaction.command
if not isinstance(command, discord.app_commands.Command):
log.exception(error)
log.critical(
"Non-command error handled by command error handler!", exc_info=error
)
return

invocation = util.parse_invocation(interaction)
Expand Down
3 changes: 0 additions & 3 deletions memebot/db/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@

db_internals = DatabaseInternals()

if config.database_enabled:
db_internals.connect()


def test() -> bool:
"""
Expand Down
71 changes: 48 additions & 23 deletions memebot/log/__init__.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,67 @@
import atexit
import contextlib
import logging
from typing import cast, Any, TYPE_CHECKING
from typing import cast, Any

import discord

from memebot import config
from . import formatter, logger

config.log_location.setFormatter(formatter.MemeBotLogFormatter())
logging.setLoggerClass(logger.MemeBotLogger)
memebot_logger: logger.MemeBotLogger
stdout_logger: logging.Logger


def log(level: int, msg: str, *args: Any, **kwargs: Any) -> None:
memebot_logger.log(level, msg, *args, **kwargs)


def debug(msg: str, *args: Any, **kwargs: Any) -> None:
memebot_logger.debug(msg, *args, **kwargs)


def info(msg: str, *args: Any, **kwargs: Any) -> None:
memebot_logger.info(msg, *args, **kwargs)


def warning(msg: str, *args: Any, **kwargs: Any) -> None:
memebot_logger.warning(msg, *args, **kwargs)


def error(msg: str, *args: Any, **kwargs: Any) -> None:
memebot_logger.error(msg, *args, **kwargs)


def critical(msg: str, *args: Any, **kwargs: Any) -> None:
memebot_logger.critical(msg, *args, **kwargs)


def exception(msg: str, exc_info: Any = True, *args: Any, **kwargs: Any) -> None:
memebot_logger.exception(msg, *args, exc_info=exc_info, **kwargs)

# We use a project-wide logger because of how we shim metadata into log statements
memebot_logger = cast(logger.MemeBotLogger, logging.getLogger("memebot"))

# Redirect all stdout to a logger, as some packages in the stdlib still use print
# for debug messages
stdout_logger = logging.getLogger("stdout")
contextlib.redirect_stdout(stdout_logger).__enter__() # type: ignore[type-var]
def configure_logging() -> None:
config.populate_config_from_command_line()

# Ensure the handler is properly flushed when MemeBot is killed
atexit.register(logging.shutdown)
config.log_location.setFormatter(formatter.MemeBotLogFormatter())
logging.setLoggerClass(logger.MemeBotLogger)

# We use a project-wide logger because of how we shim metadata into log statements
global memebot_logger
memebot_logger = cast(logger.MemeBotLogger, logging.getLogger("memebot"))

# Forward memebot_logger's logging methods as module-level functions
debug = memebot_logger.debug
info = memebot_logger.info
warning = memebot_logger.warning
error = memebot_logger.error
critical = memebot_logger.critical
log = memebot_logger.log
exception = memebot_logger.exception
# Redirect all stdout to a logger, as some packages in the stdlib still use print
# for debug messages
global stdout_logger
stdout_logger = logging.getLogger("stdout")
contextlib.redirect_stdout(stdout_logger).__enter__() # type: ignore[type-var]

# discord is imported as late as possible to ensure its logger is set properly
if TYPE_CHECKING:
import discord
# Ensure the handler is properly flushed when MemeBot is killed
atexit.register(logging.shutdown)


def interaction(
inter: "discord.Interaction",
inter: discord.Interaction,
msg: str,
level: int = logging.INFO,
*args: Any,
Expand Down
6 changes: 3 additions & 3 deletions memebot/log/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class MemeBotLogger(logging.Logger, io.IOBase):
accross all modules
"""

def __init__(self, name: str, level: Union[int, str] = config.log_level):
super(MemeBotLogger, self).__init__(name, level)
def __init__(self, name: str, level: Optional[Union[int, str]] = None):
super(MemeBotLogger, self).__init__(name, level or config.log_level)
self.propagate = False
self.is_interactive = sys.stdin.isatty() or "pydev" in repr(
__builtins__.get("__import__") # type: ignore[attr-defined]
Expand Down Expand Up @@ -96,7 +96,7 @@ def write(self, msg: str) -> None:
"""
# This means we are in interactive mode, in which case peeking at the stack
# can get very wonky
if self.is_interactive:
if self.is_interactive and sys.__stdout__:
sys.__stdout__.writelines([msg])
elif msg and not msg.isspace():
# Capture the stack here, on a line without the string "print(" in it
Expand Down
8 changes: 5 additions & 3 deletions memebot/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from memebot import config
from memebot import db
from memebot import log
from memebot.client import get_memebot

Expand All @@ -9,9 +10,10 @@ def main() -> None:
:return: Exit status of discord.Client.run()
"""
config.populate_config_from_command_line()
# the ``log`` package should be imported ASAP to ensure our logging shims
# are injected into the runtime before external packages configure
# their logging
log.configure_logging()
if config.database_enabled:
db.db_internals.connect()

log.info("Starting up memebot!")
memebot = get_memebot()

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ discord.py~=2.1.0
emoji~=2.0.0
pymongo~=3.13.0
pymongo-stubs~=0.2.0
requests~=2.32.0
types-emoji~=2.0.0
2 changes: 1 addition & 1 deletion tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
dpytest~=0.6.0
mypy~=0.0
mypy~=1.13.0
pytest~=7.2.0
pytest-asyncio~=0.20.0
pytest-mock~=3.10.0

0 comments on commit 8d3dc3a

Please sign in to comment.