mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-07-17 03:20:54 +00:00
💥 Remove: 移除 Python 3.8 支持 (#2641)
This commit is contained in:
@ -21,8 +21,6 @@ rules =
|
||||
"sys_platform != 'win32'": py-win32
|
||||
"sys_platform != 'linux'": py-linux
|
||||
"sys_platform != 'darwin'": py-darwin
|
||||
"sys_version_info < (3, 9)": py-gte-39
|
||||
"sys_version_info >= (3, 9)": py-lt-39
|
||||
"sys_version_info < (3, 11)": py-gte-311
|
||||
"sys_version_info >= (3, 11)": py-lt-311
|
||||
"package_version('pydantic') < (2,)": pydantic-v2
|
||||
|
@ -1,7 +1,8 @@
|
||||
import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Set, Generator
|
||||
from typing import TYPE_CHECKING
|
||||
from collections.abc import Generator
|
||||
|
||||
import pytest
|
||||
from nonebug import NONEBOT_INIT_KWARGS
|
||||
@ -38,13 +39,13 @@ def load_driver(request: pytest.FixtureRequest) -> Driver:
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def load_plugin(nonebug_init: None) -> Set["Plugin"]:
|
||||
def load_plugin(nonebug_init: None) -> set["Plugin"]:
|
||||
# preload global plugins
|
||||
return nonebot.load_plugins(str(Path(__file__).parent / "plugins"))
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
def load_builtin_plugin(nonebug_init: None) -> Set["Plugin"]:
|
||||
def load_builtin_plugin(nonebug_init: None) -> set["Plugin"]:
|
||||
# preload builtin plugins
|
||||
return nonebot.load_builtin_plugins("echo", "single_session")
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import json
|
||||
import base64
|
||||
import socket
|
||||
from typing import Dict, List, Union, TypeVar
|
||||
from typing import Union, TypeVar
|
||||
|
||||
from wsproto.events import Ping
|
||||
from werkzeug import Request, Response
|
||||
@ -31,7 +31,7 @@ def json_safe(string, content_type="application/octet-stream") -> str:
|
||||
).decode("utf-8")
|
||||
|
||||
|
||||
def flattern(d: "MultiDict[K, V]") -> Dict[K, Union[V, List[V]]]:
|
||||
def flattern(d: "MultiDict[K, V]") -> dict[K, Union[V, list[V]]]:
|
||||
return {k: v[0] if len(v) == 1 else v for k, v in d.to_dict(flat=False).items()}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing_extensions import Annotated
|
||||
from typing import Annotated
|
||||
|
||||
from nonebot.adapters import Message
|
||||
from nonebot.params import Arg, ArgStr, ArgPlainText
|
||||
|
@ -1,5 +1,5 @@
|
||||
from typing import Annotated
|
||||
from dataclasses import dataclass
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import List, Match, Tuple
|
||||
from re import Match
|
||||
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.adapters import Message
|
||||
@ -32,7 +32,7 @@ async def legacy_state(state):
|
||||
async def not_legacy_state(state: int): ...
|
||||
|
||||
|
||||
async def command(cmd: Tuple[str, ...] = Command()) -> Tuple[str, ...]:
|
||||
async def command(cmd: tuple[str, ...] = Command()) -> tuple[str, ...]:
|
||||
return cmd
|
||||
|
||||
|
||||
@ -59,8 +59,8 @@ async def shell_command_args(
|
||||
|
||||
|
||||
async def shell_command_argv(
|
||||
shell_command_argv: List[str] = ShellCommandArgv(),
|
||||
) -> List[str]:
|
||||
shell_command_argv: list[str] = ShellCommandArgv(),
|
||||
) -> list[str]:
|
||||
return shell_command_argv
|
||||
|
||||
|
||||
@ -68,7 +68,7 @@ async def regex_dict(regex_dict: dict = RegexDict()) -> dict:
|
||||
return regex_dict
|
||||
|
||||
|
||||
async def regex_group(regex_group: Tuple = RegexGroup()) -> Tuple:
|
||||
async def regex_group(regex_group: tuple = RegexGroup()) -> tuple:
|
||||
return regex_group
|
||||
|
||||
|
||||
@ -80,8 +80,8 @@ async def regex_str(
|
||||
entire: str = RegexStr(),
|
||||
type_: str = RegexStr("type"),
|
||||
second: str = RegexStr(2),
|
||||
groups: Tuple[str, ...] = RegexStr(1, "arg"),
|
||||
) -> Tuple[str, str, str, Tuple[str, ...]]:
|
||||
groups: tuple[str, ...] = RegexStr(1, "arg"),
|
||||
) -> tuple[str, str, str, tuple[str, ...]]:
|
||||
return entire, type_, second, groups
|
||||
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
from typing import Type
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from nonebot.adapters import Event
|
||||
@ -52,7 +51,7 @@ matcher_on = on(
|
||||
)
|
||||
|
||||
|
||||
def matcher_on_factory() -> Type[Matcher]:
|
||||
def matcher_on_factory() -> type[Matcher]:
|
||||
return on(
|
||||
"test",
|
||||
rule=rule,
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytest
|
||||
from nonebug import App
|
||||
@ -27,7 +27,7 @@ async def test_bot_call_api(app: App):
|
||||
async def test_bot_calling_api_hook_simple(app: App):
|
||||
runned: bool = False
|
||||
|
||||
async def calling_api_hook(bot: Bot, api: str, data: Dict[str, Any]):
|
||||
async def calling_api_hook(bot: Bot, api: str, data: dict[str, Any]):
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
@ -53,7 +53,7 @@ async def test_bot_calling_api_hook_simple(app: App):
|
||||
async def test_bot_calling_api_hook_mock(app: App):
|
||||
runned: bool = False
|
||||
|
||||
async def calling_api_hook(bot: Bot, api: str, data: Dict[str, Any]):
|
||||
async def calling_api_hook(bot: Bot, api: str, data: dict[str, Any]):
|
||||
nonlocal runned
|
||||
runned = True
|
||||
|
||||
@ -84,7 +84,7 @@ async def test_bot_called_api_hook_simple(app: App):
|
||||
bot: Bot,
|
||||
exception: Optional[Exception],
|
||||
api: str,
|
||||
data: Dict[str, Any],
|
||||
data: dict[str, Any],
|
||||
result: Any,
|
||||
):
|
||||
nonlocal runned
|
||||
@ -116,7 +116,7 @@ async def test_bot_called_api_hook_mock(app: App):
|
||||
bot: Bot,
|
||||
exception: Optional[Exception],
|
||||
api: str,
|
||||
data: Dict[str, Any],
|
||||
data: dict[str, Any],
|
||||
result: Any,
|
||||
):
|
||||
nonlocal runned
|
||||
|
@ -1,9 +1,10 @@
|
||||
from typing import List, Union, Optional
|
||||
from typing import TYPE_CHECKING, Union, Optional
|
||||
|
||||
import pytest
|
||||
from pydantic import Field, BaseModel
|
||||
|
||||
from nonebot.config import DOTENV_TYPE, BaseSettings, SettingsError
|
||||
from nonebot.compat import PYDANTIC_V2
|
||||
from nonebot.config import DOTENV_TYPE, BaseSettings, SettingsError, SettingsConfig
|
||||
|
||||
|
||||
class Simple(BaseModel):
|
||||
@ -14,24 +15,39 @@ class Simple(BaseModel):
|
||||
|
||||
|
||||
class Example(BaseSettings):
|
||||
_env_file: Optional[DOTENV_TYPE] = ".env", ".env.example"
|
||||
_env_nested_delimiter: Optional[str] = "__"
|
||||
if TYPE_CHECKING:
|
||||
_env_file: Optional[DOTENV_TYPE] = ".env", ".env.example"
|
||||
_env_nested_delimiter: Optional[str] = "__"
|
||||
|
||||
if PYDANTIC_V2:
|
||||
model_config = SettingsConfig(
|
||||
env_file=(".env", ".env.example"), env_nested_delimiter="__"
|
||||
)
|
||||
else:
|
||||
|
||||
class Config( # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
SettingsConfig
|
||||
):
|
||||
env_file = ".env", ".env.example"
|
||||
env_nested_delimiter = "__"
|
||||
|
||||
simple: str = ""
|
||||
complex: List[int] = Field(default=[1])
|
||||
complex_none: Optional[List[int]] = None
|
||||
complex_union: Union[int, List[int]] = 1
|
||||
complex: list[int] = Field(default=[1])
|
||||
complex_none: Optional[list[int]] = None
|
||||
complex_union: Union[int, list[int]] = 1
|
||||
nested: Simple = Simple()
|
||||
nested_inner: Simple = Simple()
|
||||
|
||||
class Config:
|
||||
env_file = ".env", ".env.example"
|
||||
env_nested_delimiter = "__"
|
||||
|
||||
|
||||
class ExampleWithoutDelimiter(Example):
|
||||
class Config:
|
||||
env_nested_delimiter = None
|
||||
if PYDANTIC_V2:
|
||||
model_config = SettingsConfig(env_nested_delimiter=None)
|
||||
else:
|
||||
|
||||
class Config( # pyright: ignore[reportIncompatibleVariableOverride]
|
||||
SettingsConfig
|
||||
):
|
||||
env_nested_delimiter = None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -1,7 +1,7 @@
|
||||
import json
|
||||
import asyncio
|
||||
from typing import Any, Optional
|
||||
from http.cookies import SimpleCookie
|
||||
from typing import Any, Set, Optional
|
||||
|
||||
import pytest
|
||||
from nonebug import App
|
||||
@ -479,8 +479,8 @@ async def test_combine_driver(driver: Driver, driver_type: str):
|
||||
@pytest.mark.asyncio
|
||||
async def test_bot_connect_hook(app: App, driver: Driver):
|
||||
with pytest.MonkeyPatch.context() as m:
|
||||
conn_hooks: Set[Dependent[Any]] = set()
|
||||
disconn_hooks: Set[Dependent[Any]] = set()
|
||||
conn_hooks: set[Dependent[Any]] = set()
|
||||
disconn_hooks: set[Dependent[Any]] = set()
|
||||
m.setattr(Driver, "_bot_connection_hook", conn_hooks)
|
||||
m.setattr(Driver, "_bot_disconnection_hook", disconn_hooks)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Tuple, Optional
|
||||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from nonebug import App
|
||||
@ -138,7 +138,7 @@ async def test_superuser(app: App, type: str, user_id: str, expected: bool):
|
||||
],
|
||||
)
|
||||
async def test_user(
|
||||
app: App, session_ids: Tuple[str, ...], session_id: Optional[str], expected: bool
|
||||
app: App, session_ids: tuple[str, ...], session_id: Optional[str], expected: bool
|
||||
):
|
||||
dependent = next(iter(USER(*session_ids).checkers))
|
||||
checker = dependent.call
|
||||
|
@ -1,5 +1,4 @@
|
||||
import sys
|
||||
from typing import Set
|
||||
from pathlib import Path
|
||||
from dataclasses import asdict
|
||||
|
||||
@ -22,7 +21,7 @@ async def test_load_plugin():
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_load_plugins(load_plugin: Set[Plugin], load_builtin_plugin: 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
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
from typing import Type, Callable, Optional
|
||||
from typing import Callable, Optional
|
||||
|
||||
import pytest
|
||||
|
||||
@ -104,7 +104,7 @@ from nonebot.rule import (
|
||||
)
|
||||
async def test_on(
|
||||
matcher_name: str,
|
||||
pre_rule_factory: Optional[Callable[[Type[Event]], T_RuleChecker]],
|
||||
pre_rule_factory: Optional[Callable[[type[Event]], T_RuleChecker]],
|
||||
has_permission: bool,
|
||||
):
|
||||
import plugins.plugin.matchers as module
|
||||
|
@ -1,6 +1,6 @@
|
||||
import re
|
||||
import sys
|
||||
from typing import Match, Tuple, Union, Optional
|
||||
from re import Match
|
||||
from typing import Union, Optional
|
||||
|
||||
import pytest
|
||||
from nonebug import App
|
||||
@ -163,7 +163,7 @@ async def test_trie(app: App):
|
||||
],
|
||||
)
|
||||
async def test_startswith(
|
||||
msg: Union[str, Tuple[str, ...]],
|
||||
msg: Union[str, tuple[str, ...]],
|
||||
ignorecase: bool,
|
||||
type: str,
|
||||
text: Optional[str],
|
||||
@ -203,7 +203,7 @@ async def test_startswith(
|
||||
],
|
||||
)
|
||||
async def test_endswith(
|
||||
msg: Union[str, Tuple[str, ...]],
|
||||
msg: Union[str, tuple[str, ...]],
|
||||
ignorecase: bool,
|
||||
type: str,
|
||||
text: Optional[str],
|
||||
@ -243,7 +243,7 @@ async def test_endswith(
|
||||
],
|
||||
)
|
||||
async def test_fullmatch(
|
||||
msg: Union[str, Tuple[str, ...]],
|
||||
msg: Union[str, tuple[str, ...]],
|
||||
ignorecase: bool,
|
||||
type: str,
|
||||
text: Optional[str],
|
||||
@ -279,7 +279,7 @@ async def test_fullmatch(
|
||||
],
|
||||
)
|
||||
async def test_keyword(
|
||||
kws: Tuple[str, ...],
|
||||
kws: tuple[str, ...],
|
||||
type: str,
|
||||
text: Optional[str],
|
||||
expected: bool,
|
||||
@ -323,9 +323,9 @@ async def test_keyword(
|
||||
],
|
||||
)
|
||||
async def test_command(
|
||||
cmds: Tuple[Tuple[str, ...]],
|
||||
cmds: tuple[tuple[str, ...]],
|
||||
force_whitespace: Optional[Union[str, bool]],
|
||||
cmd: Tuple[str, ...],
|
||||
cmd: tuple[str, ...],
|
||||
whitespace: Optional[str],
|
||||
arg_text: Optional[str],
|
||||
expected: bool,
|
||||
@ -435,21 +435,20 @@ async def test_shell_command():
|
||||
assert state[SHELL_ARGV] == ["-a", MessageSegment.image("test")]
|
||||
assert state[SHELL_ARGS] == Namespace(a=MessageSegment.image("test"))
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
parser = ArgumentParser("test", exit_on_error=False)
|
||||
parser.add_argument("-a", required=True)
|
||||
parser = ArgumentParser("test", exit_on_error=False)
|
||||
parser.add_argument("-a", required=True)
|
||||
|
||||
test_not_exit = shell_command(CMD, parser=parser)
|
||||
dependent = next(iter(test_not_exit.checkers))
|
||||
checker = dependent.call
|
||||
assert isinstance(checker, ShellCommandRule)
|
||||
message = Message()
|
||||
event = make_fake_event(_message=message)()
|
||||
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||
assert await dependent(event=event, state=state)
|
||||
assert state[SHELL_ARGV] == []
|
||||
assert isinstance(state[SHELL_ARGS], ParserExit)
|
||||
assert state[SHELL_ARGS].status != 0
|
||||
test_not_exit = shell_command(CMD, parser=parser)
|
||||
dependent = next(iter(test_not_exit.checkers))
|
||||
checker = dependent.call
|
||||
assert isinstance(checker, ShellCommandRule)
|
||||
message = Message()
|
||||
event = make_fake_event(_message=message)()
|
||||
state = {PREFIX_KEY: {CMD_KEY: CMD, CMD_ARG_KEY: message}}
|
||||
assert await dependent(event=event, state=state)
|
||||
assert state[SHELL_ARGV] == []
|
||||
assert isinstance(state[SHELL_ARGS], ParserExit)
|
||||
assert state[SHELL_ARGS].status != 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
@ -1,5 +1,5 @@
|
||||
import json
|
||||
from typing import Dict, List, Union, Literal, TypeVar, ClassVar
|
||||
from typing import Dict, List, Union, Literal, TypeVar, ClassVar # noqa: UP035
|
||||
|
||||
from utils import FakeMessage, FakeMessageSegment
|
||||
from nonebot.utils import (
|
||||
@ -26,8 +26,10 @@ def test_generic_check_issubclass():
|
||||
assert generic_check_issubclass(Union[int, float, None], (int, float))
|
||||
assert generic_check_issubclass(Literal[1, 2, 3], int)
|
||||
assert not generic_check_issubclass(Literal[1, 2, "3"], int)
|
||||
assert generic_check_issubclass(List[int], list)
|
||||
assert generic_check_issubclass(Dict[str, int], dict)
|
||||
assert generic_check_issubclass(List[int], list) # noqa: UP006
|
||||
assert generic_check_issubclass(Dict[str, int], dict) # noqa: UP006
|
||||
assert generic_check_issubclass(list[int], list)
|
||||
assert generic_check_issubclass(dict[str, int], dict)
|
||||
assert not generic_check_issubclass(ClassVar[int], int)
|
||||
assert generic_check_issubclass(TypeVar("T", int, float), (int, float))
|
||||
assert generic_check_issubclass(TypeVar("T", bound=int), (int, float))
|
||||
|
@ -1,5 +1,6 @@
|
||||
from typing import Union, Optional
|
||||
from typing_extensions import override
|
||||
from typing import Type, Union, Mapping, Iterable, Optional
|
||||
from collections.abc import Mapping, Iterable
|
||||
|
||||
from pydantic import Extra, create_model
|
||||
|
||||
@ -76,7 +77,7 @@ class FakeMessage(Message[FakeMessageSegment]):
|
||||
|
||||
|
||||
def make_fake_event(
|
||||
_base: Optional[Type[Event]] = None,
|
||||
_base: Optional[type[Event]] = None,
|
||||
_type: str = "message",
|
||||
_name: str = "test",
|
||||
_description: str = "test",
|
||||
@ -85,7 +86,7 @@ def make_fake_event(
|
||||
_message: Optional[Message] = None,
|
||||
_to_me: bool = True,
|
||||
**fields,
|
||||
) -> Type[Event]:
|
||||
) -> type[Event]:
|
||||
Base = _base or Event
|
||||
|
||||
class FakeEvent(Base, extra=Extra.forbid):
|
||||
|
Reference in New Issue
Block a user