💥 Remove: 移除 Python 3.8 支持 (#2641)

This commit is contained in:
Ju4tCode
2024-04-16 00:33:48 +08:00
committed by GitHub
parent e93ee1ffec
commit 4a02dde83f
69 changed files with 1811 additions and 1848 deletions

View File

@ -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

View File

@ -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")

View File

@ -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()}

View File

@ -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

View File

@ -1,5 +1,5 @@
from typing import Annotated
from dataclasses import dataclass
from typing_extensions import Annotated
from pydantic import Field

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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):