mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-07-16 02:50:48 +00:00
✨ Feature: 优化事件分发方法 (#2067)
This commit is contained in:
@ -25,6 +25,6 @@ def load_plugin(nonebug_init: None) -> Set["Plugin"]:
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def load_example(nonebug_init: None) -> Set["Plugin"]:
|
||||
# preload example plugins
|
||||
return nonebot.load_plugins(str(Path(__file__).parent / "examples"))
|
||||
def load_builtin_plugin(nonebug_init: None) -> Set["Plugin"]:
|
||||
# preload builtin plugins
|
||||
return nonebot.load_builtin_plugins("echo", "single_session")
|
||||
|
@ -1,29 +0,0 @@
|
||||
from nonebot import on_command
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.adapters import Message
|
||||
from nonebot.params import Arg, CommandArg, ArgPlainText
|
||||
|
||||
weather = on_command("weather", rule=to_me(), aliases={"天气", "天气预报"}, priority=5)
|
||||
|
||||
|
||||
@weather.handle()
|
||||
async def handle_first_receive(matcher: Matcher, args: Message = CommandArg()):
|
||||
plain_text = args.extract_plain_text() # 首次发送命令时跟随的参数,例:/天气 上海,则args为上海
|
||||
if plain_text:
|
||||
matcher.set_arg("city", args) # 如果用户发送了参数则直接赋值
|
||||
|
||||
|
||||
@weather.got("city", prompt="你想查询哪个城市的天气呢?")
|
||||
async def handle_city(city: Message = Arg(), city_name: str = ArgPlainText("city")):
|
||||
if city_name not in ["北京", "上海"]: # 如果参数不符合要求,则提示用户重新输入
|
||||
# 可以使用平台的 Message 类直接构造模板消息
|
||||
await weather.reject(city.template("你想查询的城市 {city} 暂不支持,请重新输入!"))
|
||||
|
||||
city_weather = await get_weather(city_name)
|
||||
await weather.finish(city_weather)
|
||||
|
||||
|
||||
# 在这里编写获取天气信息的函数
|
||||
async def get_weather(city: str) -> str:
|
||||
return f"{city}的天气是..."
|
388
tests/test_broadcast.py
Normal file
388
tests/test_broadcast.py
Normal file
@ -0,0 +1,388 @@
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from nonebug import App
|
||||
|
||||
from nonebot import on_message
|
||||
import nonebot.message as message
|
||||
from utils import make_fake_event
|
||||
from nonebot.params import Depends
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.exception import IgnoredException
|
||||
from nonebot.log import logger, default_filter, default_format
|
||||
from nonebot.message import (
|
||||
run_preprocessor,
|
||||
run_postprocessor,
|
||||
event_preprocessor,
|
||||
event_postprocessor,
|
||||
)
|
||||
|
||||
|
||||
async def _dependency() -> int:
|
||||
return 1
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_event_preprocessor(app: App, monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_event_preprocessors", set())
|
||||
|
||||
runned = False
|
||||
|
||||
@event_preprocessor
|
||||
async def test_preprocessor(
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
sub: int = Depends(_dependency),
|
||||
default: int = 1,
|
||||
):
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
assert test_preprocessor in {
|
||||
dependent.call for dependent in message._event_preprocessors
|
||||
}
|
||||
|
||||
with app.provider.context({}):
|
||||
matcher = on_message()
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
|
||||
assert runned, "event_preprocessor should runned"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_event_preprocessor_ignore(app: App, monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_event_preprocessors", set())
|
||||
|
||||
@event_preprocessor
|
||||
async def test_preprocessor():
|
||||
raise IgnoredException("pass")
|
||||
|
||||
assert test_preprocessor in {
|
||||
dependent.call for dependent in message._event_preprocessors
|
||||
}
|
||||
|
||||
runned = False
|
||||
|
||||
async def handler():
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
with app.provider.context({}):
|
||||
matcher = on_message(handlers=[handler])
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
|
||||
assert not runned, "matcher should not runned"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_event_preprocessor_exception(
|
||||
app: App, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_event_preprocessors", set())
|
||||
|
||||
@event_preprocessor
|
||||
async def test_preprocessor():
|
||||
raise RuntimeError("test")
|
||||
|
||||
assert test_preprocessor in {
|
||||
dependent.call for dependent in message._event_preprocessors
|
||||
}
|
||||
|
||||
runned = False
|
||||
|
||||
async def handler():
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
handler_id = logger.add(
|
||||
sys.stdout,
|
||||
level=0,
|
||||
diagnose=False,
|
||||
filter=default_filter,
|
||||
format=default_format,
|
||||
)
|
||||
|
||||
try:
|
||||
with app.provider.context({}):
|
||||
matcher = on_message(handlers=[handler])
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
finally:
|
||||
logger.remove(handler_id)
|
||||
|
||||
assert not runned, "matcher should not runned"
|
||||
assert "RuntimeError: test" in capsys.readouterr().out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_event_postprocessor(app: App, monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_event_postprocessors", set())
|
||||
|
||||
runned = False
|
||||
|
||||
@event_postprocessor
|
||||
async def test_postprocessor(
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
sub: int = Depends(_dependency),
|
||||
default: int = 1,
|
||||
):
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
assert test_postprocessor in {
|
||||
dependent.call for dependent in message._event_postprocessors
|
||||
}
|
||||
|
||||
with app.provider.context({}):
|
||||
matcher = on_message()
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
|
||||
assert runned, "event_postprocessor should runned"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_event_postprocessor_exception(
|
||||
app: App, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_event_postprocessors", set())
|
||||
|
||||
@event_postprocessor
|
||||
async def test_postprocessor():
|
||||
raise RuntimeError("test")
|
||||
|
||||
assert test_postprocessor in {
|
||||
dependent.call for dependent in message._event_postprocessors
|
||||
}
|
||||
|
||||
handler_id = logger.add(
|
||||
sys.stdout,
|
||||
level=0,
|
||||
diagnose=False,
|
||||
filter=default_filter,
|
||||
format=default_format,
|
||||
)
|
||||
|
||||
try:
|
||||
with app.provider.context({}):
|
||||
matcher = on_message()
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
finally:
|
||||
logger.remove(handler_id)
|
||||
|
||||
assert "RuntimeError: test" in capsys.readouterr().out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_preprocessor(app: App, monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_run_preprocessors", set())
|
||||
|
||||
runned = False
|
||||
|
||||
@run_preprocessor
|
||||
async def test_preprocessor(
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
matcher: Matcher,
|
||||
sub: int = Depends(_dependency),
|
||||
default: int = 1,
|
||||
):
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
await matcher.send("test")
|
||||
|
||||
assert test_preprocessor in {
|
||||
dependent.call for dependent in message._run_preprocessors
|
||||
}
|
||||
|
||||
with app.provider.context({}):
|
||||
matcher = on_message()
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(event, "test", True, bot)
|
||||
|
||||
assert runned, "run_preprocessor should runned"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_preprocessor_ignore(app: App, monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_run_preprocessors", set())
|
||||
|
||||
@run_preprocessor
|
||||
async def test_preprocessor():
|
||||
raise IgnoredException("pass")
|
||||
|
||||
assert test_preprocessor in {
|
||||
dependent.call for dependent in message._run_preprocessors
|
||||
}
|
||||
|
||||
runned = False
|
||||
|
||||
async def handler():
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
with app.provider.context({}):
|
||||
matcher = on_message(handlers=[handler])
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
|
||||
assert not runned, "matcher should not runned"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_preprocessor_exception(
|
||||
app: App, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_run_preprocessors", set())
|
||||
|
||||
@run_preprocessor
|
||||
async def test_preprocessor():
|
||||
raise RuntimeError("test")
|
||||
|
||||
assert test_preprocessor in {
|
||||
dependent.call for dependent in message._run_preprocessors
|
||||
}
|
||||
|
||||
runned = False
|
||||
|
||||
async def handler():
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
handler_id = logger.add(
|
||||
sys.stdout,
|
||||
level=0,
|
||||
diagnose=False,
|
||||
filter=default_filter,
|
||||
format=default_format,
|
||||
)
|
||||
|
||||
try:
|
||||
with app.provider.context({}):
|
||||
matcher = on_message(handlers=[handler])
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
finally:
|
||||
logger.remove(handler_id)
|
||||
|
||||
assert not runned, "matcher should not runned"
|
||||
assert "RuntimeError: test" in capsys.readouterr().out
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_postprocessor(app: App, monkeypatch: pytest.MonkeyPatch):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_run_postprocessors", set())
|
||||
|
||||
runned = False
|
||||
|
||||
@run_postprocessor
|
||||
async def test_postprocessor(
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
matcher: Matcher,
|
||||
exception: Optional[Exception],
|
||||
sub: int = Depends(_dependency),
|
||||
default: int = 1,
|
||||
):
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
await matcher.send("test")
|
||||
|
||||
assert test_postprocessor in {
|
||||
dependent.call for dependent in message._run_postprocessors
|
||||
}
|
||||
|
||||
with app.provider.context({}):
|
||||
matcher = on_message()
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(event, "test", True, bot)
|
||||
|
||||
assert runned, "run_postprocessor should runned"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_postprocessor_exception(
|
||||
app: App, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
||||
):
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(message, "_run_postprocessors", set())
|
||||
|
||||
@run_postprocessor
|
||||
async def test_postprocessor():
|
||||
raise RuntimeError("test")
|
||||
|
||||
assert test_postprocessor in {
|
||||
dependent.call for dependent in message._run_postprocessors
|
||||
}
|
||||
|
||||
handler_id = logger.add(
|
||||
sys.stdout,
|
||||
level=0,
|
||||
diagnose=False,
|
||||
filter=default_filter,
|
||||
format=default_format,
|
||||
)
|
||||
|
||||
try:
|
||||
with app.provider.context({}):
|
||||
matcher = on_message()
|
||||
|
||||
async with app.test_matcher(matcher) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
event = make_fake_event()()
|
||||
ctx.receive_event(bot, event)
|
||||
finally:
|
||||
logger.remove(handler_id)
|
||||
|
||||
assert "RuntimeError: test" in capsys.readouterr().out
|
@ -1,76 +0,0 @@
|
||||
import pytest
|
||||
from nonebug import App
|
||||
|
||||
from utils import make_fake_event, make_fake_message
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_weather(app: App):
|
||||
from examples.weather import weather
|
||||
|
||||
# 将此处的 make_fake_message() 替换为你要发送的平台消息 Message 类型
|
||||
# from nonebot.adapters.console import Message
|
||||
Message = make_fake_message()
|
||||
|
||||
async with app.test_matcher(weather) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
|
||||
msg = Message("/天气 上海")
|
||||
# 将此处的 make_fake_event() 替换为你要发送的平台事件 Event 类型
|
||||
# from nonebot.adapters.console import MessageEvent
|
||||
# event = MessageEvent(message=msg, to_me=True, ...)
|
||||
event = make_fake_event(_message=msg, _to_me=True)()
|
||||
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(event, "上海的天气是...", True)
|
||||
ctx.should_finished()
|
||||
|
||||
async with app.test_matcher(weather) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
|
||||
msg = Message("/天气 南京")
|
||||
# 将此处的 make_fake_event() 替换为你要发送的平台事件 Event 类型
|
||||
event = make_fake_event(_message=msg, _to_me=True)()
|
||||
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(
|
||||
event,
|
||||
Message.template("你想查询的城市 {} 暂不支持,请重新输入!").format("南京"),
|
||||
True,
|
||||
)
|
||||
ctx.should_rejected()
|
||||
|
||||
msg = Message("北京")
|
||||
event = make_fake_event(_message=msg)()
|
||||
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(event, "北京的天气是...", True)
|
||||
ctx.should_finished()
|
||||
|
||||
async with app.test_matcher(weather) as ctx:
|
||||
bot = ctx.create_bot()
|
||||
|
||||
msg = Message("/天气")
|
||||
# 将此处的 make_fake_event() 替换为你要发送的平台事件 Event 类型
|
||||
event = make_fake_event(_message=msg, _to_me=True)()
|
||||
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(event, "你想查询哪个城市的天气呢?", True)
|
||||
|
||||
msg = Message("杭州")
|
||||
event = make_fake_event(_message=msg)()
|
||||
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(
|
||||
event,
|
||||
Message.template("你想查询的城市 {} 暂不支持,请重新输入!").format("杭州"),
|
||||
True,
|
||||
)
|
||||
ctx.should_rejected()
|
||||
|
||||
msg = Message("北京")
|
||||
event = make_fake_event(_message=msg)()
|
||||
|
||||
ctx.receive_event(bot, event)
|
||||
ctx.should_call_send(event, "北京的天气是...", True)
|
||||
ctx.should_finished()
|
@ -2,8 +2,8 @@ import pytest
|
||||
from nonebug import App
|
||||
|
||||
from nonebot.permission import User
|
||||
from nonebot.message import _check_matcher
|
||||
from nonebot.matcher import Matcher, matchers
|
||||
from nonebot.message import check_and_run_matcher
|
||||
from utils import make_fake_event, make_fake_message
|
||||
|
||||
|
||||
@ -200,19 +200,19 @@ async def test_expire(app: App):
|
||||
async with app.test_api() as ctx:
|
||||
bot = ctx.create_bot()
|
||||
assert test_temp_matcher in matchers[test_temp_matcher.priority]
|
||||
await _check_matcher(test_temp_matcher, bot, event, {})
|
||||
await check_and_run_matcher(test_temp_matcher, bot, event, {})
|
||||
assert test_temp_matcher not in matchers[test_temp_matcher.priority]
|
||||
|
||||
event = make_fake_event()()
|
||||
async with app.test_api() as ctx:
|
||||
bot = ctx.create_bot()
|
||||
assert test_datetime_matcher in matchers[test_datetime_matcher.priority]
|
||||
await _check_matcher(test_datetime_matcher, bot, event, {})
|
||||
await check_and_run_matcher(test_datetime_matcher, bot, event, {})
|
||||
assert test_datetime_matcher not in matchers[test_datetime_matcher.priority]
|
||||
|
||||
event = make_fake_event()()
|
||||
async with app.test_api() as ctx:
|
||||
bot = ctx.create_bot()
|
||||
assert test_timedelta_matcher in matchers[test_timedelta_matcher.priority]
|
||||
await _check_matcher(test_timedelta_matcher, bot, event, {})
|
||||
await check_and_run_matcher(test_timedelta_matcher, bot, event, {})
|
||||
assert test_timedelta_matcher not in matchers[test_timedelta_matcher.priority]
|
||||
|
@ -22,11 +22,11 @@ async def test_load_plugin():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_load_plugins(load_plugin: Set[Plugin], load_example: Set[Plugin]):
|
||||
async def test_load_plugins(load_plugin: Set[Plugin], load_builtin_plugin: Set[Plugin]):
|
||||
loaded_plugins = {
|
||||
plugin for plugin in nonebot.get_loaded_plugins() if not plugin.parent_plugin
|
||||
}
|
||||
assert loaded_plugins >= load_plugin | load_example
|
||||
assert loaded_plugins >= load_plugin | load_builtin_plugin
|
||||
|
||||
# check simple plugin
|
||||
assert "plugins.export" in sys.modules
|
||||
|
Reference in New Issue
Block a user