Repository navigation
Conversation
|
The crash fix is needed and the regression test is real, but the skip end-state drops a live plugin's commands: a handler defined in a plugin submodule (mixin in The resolution that keeps the commands working is to map the handler module to its owning plugin package rather than require an exact Worth a test variant beyond the orphan case: a submodule handler whose owning plugin IS activated should have its commands registered, not skipped. Sibling sites with the same unguarded subscript, in case you want to track them separately rather than fix them here: |
|
Thanks for the careful review — the submodule-drop concern is valid, fixed in dd0e938.
Tests added per your suggestion:
All four pass locally. Happy to rebase onto |
Sourcery withdrew this approval because the latest commits introduced blocking findings.
|
Follow-up in 2251d5a: while re-checking I noticed the prefix-only match dropped handlers defined in a plugin's |
A handler defined in a plugin submodule (e.g. a mixin in `data.plugins.<plugin>.foo`) is registered with `handler_module_path` pointing at that submodule, but `star_map` is keyed by the plugin entry module. `_collect_and_register_commands` subscripted `star_map` directly, raising `KeyError` inside `on_ready` and aborting the loop before `sync_commands()` was reached, so no plugin's slash commands were registered at all. Resolve the owning plugin by package root instead: exact `star_map` lookup first, then a match on `data.plugins.<root_dir_name>` / `astrbot.builtin_stars.<root_dir_name>`. Only handlers that no plugin owns are skipped, and activated plugins keep their slash commands even when the handler lives in a submodule or in the plugin `__init__.py`. Tests cover the unowned-handler regression, submodule handlers of active and inactive plugins, package-root handlers, and true orphans. They also no longer depend on `sys.modules["discord"]` import order, which made the new cases fail when the whole suite imported the real py-cord first. Fixes AstrBotDevs#10626
2251d5a to
91c8b27
Compare
Verified end-to-end against a real Discord botIn addition to the unit tests, I ran both revisions against a real bot in a test guild. Guild-scoped command registration was used so the result is observable immediately instead of waiting for global command propagation. Test setup A throwaway plugin with one command in # mixin.py
class SubmoduleMixin:
@filter.command("submod_cmd")
async def submod_cmd(self, event: AstrMessageEvent):
...so that handler is registered with Before (master, old code)
So the After (this PR)
The submodule handler's command now reaches Discord instead of both aborting the loop and losing its own command. The temporary guild commands were removed after the run and the throwaway instance was shut down. |
|
Thanks — implemented along the lines you suggested. Two things to settle before this lands:
|
Fixes #10626
A plugin handler defined in a submodule (e.g. a mixin class in
plugin/foo.pyinherited by the plugin's mainStarclass) is registered withhandler_module_path = handler.__module__, e.g.data.plugins.<plugin>.foo. Butstar_mapis only keyed by the plugin's entry module (data.plugins.<plugin>.main).DiscordPlatformAdapter._collect_and_register_commandsdereferencedstar_map[handler_md.handler_module_path]directly, so a single such handler raisesKeyErrorinsideon_ready, aborting the loop beforesync_commands()is reached -- and no plugin's slash commands get registered, not just the offending plugin's.Modifications / 改动点
astrbot/core/platform/sources/discord/discord_platform_adapter.py:_collect_and_register_commandsnow resolves the owning plugin through_plugin_for_handler_module(): exactstar_maplookup first, then a packageroot match on
data.plugins.<root_dir_name>/astrbot.builtin_stars.<root_dir_name>(respectingreserved), so handlersdefined in an active plugin's submodule or
__init__.pystill sync theirslash commands. Only handlers that no plugin owns are skipped, instead of
raising
KeyError; theactivated/enabledchecks are unchanged.tests/test_discord_command_sync.py: regression coverage for:__init__.py(module_path == package root) registers;sys.modules["discord"]import order.This is NOT a breaking change. / 这不是一个破坏性变更。
Screenshots or Test Results / 运行截图或测试结果
The new regression test fails on the old code (
KeyError, nothing registered)and passes with this fix. Discord suite, run together with
tests/test_discord_adapter.py(which imports the realdiscordmodule first,matching how CI collects the whole
tests/directory):The rest of the suite is unchanged:
pytest testsreports 4693 passed,12 skipped (verified on Python 3.12 with py-cord installed).
Ruff
checkandformat --checkare both clean.Checklist / 检查清单
Not applicable: this PR adds no new features, it only removes a crash path.
Verification steps:
PYTHONPATH=. python -m pytest tests/test_discord_adapter.py tests/test_discord_command_sync.pydocs/zhanddocs/enagainst the changed navigation, page structure, and labels, and updated them in this PR (or explained why no documentation update is needed).No documentation update needed: this touches only the Discord adapter's internal command collection, with no WebUI navigation, page structure, or terminology change.
requirements.txtandpyproject.toml.Note: the fix is the approach suggested in #10626 and in review. The resolver is kept local to the Discord adapter for now; once #9976 (
plugin_for_handler_moduleinstar_handler.py) lands, this can be rebased onto the shared resolver.