mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-29 22:16:42 +00:00 
			
		
		
		
	✨ Feature: 支持 re.Match 依赖注入 (#1950)
				
					
				
			This commit is contained in:
		| @@ -42,12 +42,6 @@ SHELL_ARGV: Literal["_argv"] = "_argv" | ||||
|  | ||||
| REGEX_MATCHED: Literal["_matched"] = "_matched" | ||||
| """正则匹配结果存储 key""" | ||||
| REGEX_STR: Literal["_matched_str"] = "_matched_str" | ||||
| """正则匹配文本存储 key""" | ||||
| REGEX_GROUP: Literal["_matched_groups"] = "_matched_groups" | ||||
| """正则匹配 group 元组存储 key""" | ||||
| REGEX_DICT: Literal["_matched_dict"] = "_matched_dict" | ||||
| """正则匹配 group 字典存储 key""" | ||||
| STARTSWITH_KEY: Literal["_startswith"] = "_startswith" | ||||
| """响应触发前缀 key""" | ||||
| ENDSWITH_KEY: Literal["_endswith"] = "_endswith" | ||||
|   | ||||
| @@ -5,8 +5,7 @@ FrontMatter: | ||||
|     description: nonebot.params 模块 | ||||
| """ | ||||
|  | ||||
| import warnings | ||||
| from typing import Any, Dict, List, Tuple, Union, Optional | ||||
| from typing import Any, Dict, List, Match, Tuple, Union, Optional | ||||
|  | ||||
| from nonebot.typing import T_State | ||||
| from nonebot.matcher import Matcher | ||||
| @@ -25,15 +24,12 @@ from nonebot.internal.params import MatcherParam as MatcherParam | ||||
| from nonebot.internal.params import ExceptionParam as ExceptionParam | ||||
| from nonebot.consts import ( | ||||
|     CMD_KEY, | ||||
|     REGEX_STR, | ||||
|     PREFIX_KEY, | ||||
|     REGEX_DICT, | ||||
|     SHELL_ARGS, | ||||
|     SHELL_ARGV, | ||||
|     CMD_ARG_KEY, | ||||
|     KEYWORD_KEY, | ||||
|     RAW_CMD_KEY, | ||||
|     REGEX_GROUP, | ||||
|     ENDSWITH_KEY, | ||||
|     CMD_START_KEY, | ||||
|     FULLMATCH_KEY, | ||||
| @@ -142,23 +138,17 @@ def ShellCommandArgv() -> Any: | ||||
|     return Depends(_shell_command_argv, use_cache=False) | ||||
|  | ||||
|  | ||||
| def _regex_matched(state: T_State) -> str: | ||||
| def _regex_matched(state: T_State) -> Match[str]: | ||||
|     return state[REGEX_MATCHED] | ||||
|  | ||||
|  | ||||
| def RegexMatched() -> str: | ||||
| def RegexMatched() -> Match[str]: | ||||
|     """正则匹配结果""" | ||||
|     warnings.warn( | ||||
|         '"RegexMatched()" will be changed to "re.Match" object, ' | ||||
|         'use "RegexStr()" instead. ' | ||||
|         "See https://github.com/nonebot/nonebot2/pull/1453 .", | ||||
|         DeprecationWarning, | ||||
|     ) | ||||
|     return Depends(_regex_matched, use_cache=False) | ||||
|  | ||||
|  | ||||
| def _regex_str(state: T_State) -> str: | ||||
|     return state[REGEX_STR] | ||||
|     return _regex_matched(state).group() | ||||
|  | ||||
|  | ||||
| def RegexStr() -> str: | ||||
| @@ -167,7 +157,7 @@ def RegexStr() -> str: | ||||
|  | ||||
|  | ||||
| def _regex_group(state: T_State) -> Tuple[Any, ...]: | ||||
|     return state[REGEX_GROUP] | ||||
|     return _regex_matched(state).groups() | ||||
|  | ||||
|  | ||||
| def RegexGroup() -> Tuple[Any, ...]: | ||||
| @@ -176,7 +166,7 @@ def RegexGroup() -> Tuple[Any, ...]: | ||||
|  | ||||
|  | ||||
| def _regex_dict(state: T_State) -> Dict[str, Any]: | ||||
|     return state[REGEX_DICT] | ||||
|     return _regex_matched(state).groupdict() | ||||
|  | ||||
|  | ||||
| def RegexDict() -> Dict[str, Any]: | ||||
|   | ||||
| @@ -44,15 +44,12 @@ from nonebot.adapters import Bot, Event, Message, MessageSegment | ||||
| from nonebot.params import Command, EventToMe, CommandArg, CommandWhitespace | ||||
| from nonebot.consts import ( | ||||
|     CMD_KEY, | ||||
|     REGEX_STR, | ||||
|     PREFIX_KEY, | ||||
|     REGEX_DICT, | ||||
|     SHELL_ARGS, | ||||
|     SHELL_ARGV, | ||||
|     CMD_ARG_KEY, | ||||
|     KEYWORD_KEY, | ||||
|     RAW_CMD_KEY, | ||||
|     REGEX_GROUP, | ||||
|     ENDSWITH_KEY, | ||||
|     CMD_START_KEY, | ||||
|     FULLMATCH_KEY, | ||||
| @@ -678,10 +675,7 @@ class RegexRule: | ||||
|         except Exception: | ||||
|             return False | ||||
|         if matched := re.search(self.regex, str(msg), self.flags): | ||||
|             state[REGEX_MATCHED] = matched.group() | ||||
|             state[REGEX_STR] = matched.group() | ||||
|             state[REGEX_GROUP] = matched.groups() | ||||
|             state[REGEX_DICT] = matched.groupdict() | ||||
|             state[REGEX_MATCHED] = matched | ||||
|             return True | ||||
|         else: | ||||
|             return False | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| from typing import List, Tuple | ||||
| from typing import List, Match, Tuple | ||||
|  | ||||
| from nonebot.typing import T_State | ||||
| from nonebot.adapters import Message | ||||
| @@ -73,12 +73,12 @@ async def regex_group(regex_group: Tuple = RegexGroup()) -> Tuple: | ||||
|     return regex_group | ||||
|  | ||||
|  | ||||
| async def regex_matched(regex_matched: str = RegexMatched()) -> str: | ||||
| async def regex_matched(regex_matched: Match[str] = RegexMatched()) -> Match[str]: | ||||
|     return regex_matched | ||||
|  | ||||
|  | ||||
| async def regex_str(regex_matched: str = RegexStr()) -> str: | ||||
|     return regex_matched | ||||
| async def regex_str(regex_str: str = RegexStr()) -> str: | ||||
|     return regex_str | ||||
|  | ||||
|  | ||||
| async def startswith(startswith: str = Startswith()) -> str: | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import re | ||||
|  | ||||
| import pytest | ||||
| from nonebug import App | ||||
|  | ||||
| @@ -16,15 +18,12 @@ from nonebot.params import ( | ||||
| ) | ||||
| from nonebot.consts import ( | ||||
|     CMD_KEY, | ||||
|     REGEX_STR, | ||||
|     PREFIX_KEY, | ||||
|     REGEX_DICT, | ||||
|     SHELL_ARGS, | ||||
|     SHELL_ARGV, | ||||
|     CMD_ARG_KEY, | ||||
|     KEYWORD_KEY, | ||||
|     RAW_CMD_KEY, | ||||
|     REGEX_GROUP, | ||||
|     ENDSWITH_KEY, | ||||
|     CMD_START_KEY, | ||||
|     FULLMATCH_KEY, | ||||
| @@ -226,6 +225,7 @@ async def test_state(app: App): | ||||
|     ) | ||||
|  | ||||
|     fake_message = make_fake_message()("text") | ||||
|     fake_matched = re.match(r"\[cq:(?P<type>.*?),(?P<arg>.*?)\]", "[cq:test,arg=value]") | ||||
|     fake_state = { | ||||
|         PREFIX_KEY: { | ||||
|             CMD_KEY: ("cmd",), | ||||
| @@ -236,10 +236,7 @@ async def test_state(app: App): | ||||
|         }, | ||||
|         SHELL_ARGV: ["-h"], | ||||
|         SHELL_ARGS: {"help": True}, | ||||
|         REGEX_MATCHED: "[cq:test,arg=value]", | ||||
|         REGEX_STR: "[cq:test,arg=value]", | ||||
|         REGEX_GROUP: ("test", "arg=value"), | ||||
|         REGEX_DICT: {"type": "test", "arg": "value"}, | ||||
|         REGEX_MATCHED: fake_matched, | ||||
|         STARTSWITH_KEY: "startswith", | ||||
|         ENDSWITH_KEY: "endswith", | ||||
|         FULLMATCH_KEY: "fullmatch", | ||||
| @@ -312,19 +309,19 @@ async def test_state(app: App): | ||||
|         regex_str, allow_types=[StateParam, DependParam] | ||||
|     ) as ctx: | ||||
|         ctx.pass_params(state=fake_state) | ||||
|         ctx.should_return(fake_state[REGEX_STR]) | ||||
|         ctx.should_return("[cq:test,arg=value]") | ||||
|  | ||||
|     async with app.test_dependent( | ||||
|         regex_group, allow_types=[StateParam, DependParam] | ||||
|     ) as ctx: | ||||
|         ctx.pass_params(state=fake_state) | ||||
|         ctx.should_return(fake_state[REGEX_GROUP]) | ||||
|         ctx.should_return(("test", "arg=value")) | ||||
|  | ||||
|     async with app.test_dependent( | ||||
|         regex_dict, allow_types=[StateParam, DependParam] | ||||
|     ) as ctx: | ||||
|         ctx.pass_params(state=fake_state) | ||||
|         ctx.should_return(fake_state[REGEX_DICT]) | ||||
|         ctx.should_return({"type": "test", "arg": "arg=value"}) | ||||
|  | ||||
|     async with app.test_dependent( | ||||
|         startswith, allow_types=[StateParam, DependParam] | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import re | ||||
| import sys | ||||
| from typing import Dict, Tuple, Union, Optional | ||||
| from typing import Match, Tuple, Union, Optional | ||||
|  | ||||
| import pytest | ||||
| from nonebug import App | ||||
| @@ -9,14 +10,11 @@ from utils import make_fake_event, make_fake_message | ||||
| from nonebot.exception import ParserExit, SkippedException | ||||
| from nonebot.consts import ( | ||||
|     CMD_KEY, | ||||
|     REGEX_STR, | ||||
|     PREFIX_KEY, | ||||
|     REGEX_DICT, | ||||
|     SHELL_ARGS, | ||||
|     SHELL_ARGV, | ||||
|     CMD_ARG_KEY, | ||||
|     KEYWORD_KEY, | ||||
|     REGEX_GROUP, | ||||
|     ENDSWITH_KEY, | ||||
|     FULLMATCH_KEY, | ||||
|     REGEX_MATCHED, | ||||
| @@ -414,21 +412,18 @@ async def test_shell_command(): | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
| @pytest.mark.parametrize( | ||||
|     "pattern, type, text, expected, matched, string, group, dict", | ||||
|     "pattern, type, text, expected, matched", | ||||
|     [ | ||||
|         ( | ||||
|             r"(?P<key>key\d)", | ||||
|             "message", | ||||
|             "_key1_", | ||||
|             True, | ||||
|             "key1", | ||||
|             "key1", | ||||
|             ("key1",), | ||||
|             {"key": "key1"}, | ||||
|             re.search(r"(?P<key>key\d)", "_key1_"), | ||||
|         ), | ||||
|         (r"foo", "message", None, False, None, None, None, None), | ||||
|         (r"foo", "notice", "foo", True, "foo", "foo", tuple(), {}), | ||||
|         (r"foo", "notice", "bar", False, None, None, None, None), | ||||
|         (r"foo", "message", None, False, None), | ||||
|         (r"foo", "notice", "foo", True, re.search(r"foo", "foo")), | ||||
|         (r"foo", "notice", "bar", False, None), | ||||
|     ], | ||||
| ) | ||||
| async def test_regex( | ||||
| @@ -436,10 +431,7 @@ async def test_regex( | ||||
|     type: str, | ||||
|     text: Optional[str], | ||||
|     expected: bool, | ||||
|     matched: Optional[str], | ||||
|     string: Optional[str], | ||||
|     group: Optional[Tuple[str, ...]], | ||||
|     dict: Optional[Dict[str, str]], | ||||
|     matched: Optional[Match[str]], | ||||
| ): | ||||
|     test_regex = regex(pattern) | ||||
|     dependent = list(test_regex.checkers)[0] | ||||
| @@ -452,10 +444,13 @@ async def test_regex( | ||||
|     event = make_fake_event(_type=type, _message=message)() | ||||
|     state = {} | ||||
|     assert await dependent(event=event, state=state) == expected | ||||
|     assert state.get(REGEX_MATCHED) == matched | ||||
|     assert state.get(REGEX_STR) == string | ||||
|     assert state.get(REGEX_GROUP) == group | ||||
|     assert state.get(REGEX_DICT) == dict | ||||
|     result: Optional[Match[str]] = state.get(REGEX_MATCHED) | ||||
|     if matched is None: | ||||
|         assert result is None | ||||
|     else: | ||||
|         assert isinstance(result, Match) | ||||
|         assert result.group() == matched.group() | ||||
|         assert result.span() == matched.span() | ||||
|  | ||||
|  | ||||
| @pytest.mark.asyncio | ||||
|   | ||||
		Reference in New Issue
	
	Block a user