From fd1e8d1926537c45d14f0bbf26b6507e4a366397 Mon Sep 17 00:00:00 2001 From: agessaman Date: Sat, 3 Oct 2026 23:51:32 -0700 Subject: [PATCH] refactor(commands): read the help method's own signature, not the one functools.wraps copies Codex on f0649558: inspect.signature follows __wrapped__, so an operator-installed command under modules/commands that wraps PingCommand.get_help_text with a get_help_text(self) override looked like it accepted the message, was called with it, and raised where dev retried and returned its help. _accepts_message now uses follow_wrapped=False. New test covers that override. No bundled get_help_text is decorated. --- modules/commands/base_command.py | 8 ++++++-- tests/test_help_command.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/modules/commands/base_command.py b/modules/commands/base_command.py index ecee966..4727ab3 100644 --- a/modules/commands/base_command.py +++ b/modules/commands/base_command.py @@ -73,9 +73,13 @@ def _defined_by_bundled_class(command: Any, method: Any) -> bool: def _accepts_message(method: Any, message: Any) -> bool: - """Whether ``method(message)`` binds, per its signature (False when that can't be read).""" + """Whether ``method(message)`` binds, per its own signature (False when that can't be read). + + ``follow_wrapped=False``: a functools.wraps-decorated override reports the + wrapped function's signature otherwise, not its own. + """ try: - inspect.signature(method).bind(message) + inspect.signature(method, follow_wrapped=False).bind(message) except (TypeError, ValueError): return False return True diff --git a/tests/test_help_command.py b/tests/test_help_command.py index cc5a1ec..8092fa1 100644 --- a/tests/test_help_command.py +++ b/tests/test_help_command.py @@ -271,6 +271,23 @@ class TestGetSpecificHelp: cmd = HelpCommand(bot) cmd.get_specific_help("operatorweather", Mock()) # must not raise + def test_wrapped_operator_override_inside_modules_gets_help(self): + """functools.wraps must not lend an operator override the bundled signature.""" + import functools + + from modules.commands.ping_command import PingCommand + + class OperatorPing(PingCommand): + @functools.wraps(PingCommand.get_help_text) + def get_help_text(self): + return "operator help" + + OperatorPing.__module__ = "modules.commands.operator_ping" + bot = _make_bot() + bot.command_manager.commands = {"operatorping": object.__new__(OperatorPing)} + cmd = HelpCommand(bot) + cmd.get_specific_help("operatorping", Mock()) # must not raise + def test_local_plugin_help_keeps_the_typeerror_retry(self): """A local get_help_text(message=None) that raises TypeError is retried without it.""" bot = _make_bot()