mirror of
https://github.com/nonebot/nonebot2.git
synced 2025-11-22 04:36:24 +00:00
🐛 Fix: log level 配置项无法使用 int 类型配置 (#3732)
This commit is contained in:
@@ -11,7 +11,7 @@ FrontMatter:
|
|||||||
|
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
from dataclasses import dataclass, is_dataclass
|
from dataclasses import dataclass, is_dataclass
|
||||||
from functools import cached_property
|
from functools import cached_property, wraps
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Annotated,
|
Annotated,
|
||||||
@@ -25,13 +25,14 @@ from typing import (
|
|||||||
Union,
|
Union,
|
||||||
overload,
|
overload,
|
||||||
)
|
)
|
||||||
from typing_extensions import Self, get_args, get_origin, is_typeddict
|
from typing_extensions import ParamSpec, Self, get_args, get_origin, is_typeddict
|
||||||
|
|
||||||
from pydantic import VERSION, BaseModel
|
from pydantic import VERSION, BaseModel
|
||||||
|
|
||||||
from nonebot.typing import origin_is_annotated
|
from nonebot.typing import origin_is_annotated
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
P = ParamSpec("P")
|
||||||
|
|
||||||
PYDANTIC_V2 = int(VERSION.split(".", 1)[0]) == 2
|
PYDANTIC_V2 = int(VERSION.split(".", 1)[0]) == 2
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@ __all__ = (
|
|||||||
"PYDANTIC_V2",
|
"PYDANTIC_V2",
|
||||||
"ConfigDict",
|
"ConfigDict",
|
||||||
"FieldInfo",
|
"FieldInfo",
|
||||||
|
"LegacyUnionField",
|
||||||
"ModelField",
|
"ModelField",
|
||||||
"PydanticUndefined",
|
"PydanticUndefined",
|
||||||
"PydanticUndefinedType",
|
"PydanticUndefinedType",
|
||||||
@@ -71,7 +73,7 @@ __autodoc__ = {
|
|||||||
|
|
||||||
|
|
||||||
if PYDANTIC_V2: # pragma: pydantic-v2
|
if PYDANTIC_V2: # pragma: pydantic-v2
|
||||||
from pydantic import GetCoreSchemaHandler
|
from pydantic import Field, GetCoreSchemaHandler
|
||||||
from pydantic import TypeAdapter as TypeAdapter
|
from pydantic import TypeAdapter as TypeAdapter
|
||||||
from pydantic import field_validator as field_validator
|
from pydantic import field_validator as field_validator
|
||||||
from pydantic import model_validator as model_validator
|
from pydantic import model_validator as model_validator
|
||||||
@@ -94,6 +96,17 @@ if PYDANTIC_V2: # pragma: pydantic-v2
|
|||||||
DEFAULT_CONFIG = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
DEFAULT_CONFIG = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
||||||
"""Default config for validations"""
|
"""Default config for validations"""
|
||||||
|
|
||||||
|
def _get_legacy_union_field(func: Callable[P, T]) -> Callable[P, T]:
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
||||||
|
kwargs["union_mode"] = "left_to_right"
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
LegacyUnionField = _get_legacy_union_field(Field)
|
||||||
|
LegacyUnionField.__doc__ = "Mark field to use legacy left to right union mode"
|
||||||
|
|
||||||
class FieldInfo(BaseFieldInfo): # pyright: ignore[reportGeneralTypeIssues]
|
class FieldInfo(BaseFieldInfo): # pyright: ignore[reportGeneralTypeIssues]
|
||||||
"""FieldInfo class with extra property for compatibility with pydantic v1"""
|
"""FieldInfo class with extra property for compatibility with pydantic v1"""
|
||||||
|
|
||||||
@@ -292,6 +305,8 @@ else: # pragma: pydantic-v1
|
|||||||
extra = Extra.allow
|
extra = Extra.allow
|
||||||
arbitrary_types_allowed = True
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
from pydantic.fields import Field as LegacyUnionField
|
||||||
|
|
||||||
class FieldInfo(BaseFieldInfo):
|
class FieldInfo(BaseFieldInfo):
|
||||||
def __init__(self, default: Any = PydanticUndefined, **kwargs: Any):
|
def __init__(self, default: Any = PydanticUndefined, **kwargs: Any):
|
||||||
# preprocess default value to make it compatible with pydantic v2
|
# preprocess default value to make it compatible with pydantic v2
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from pydantic.networks import IPvAnyAddress
|
|||||||
from nonebot.compat import (
|
from nonebot.compat import (
|
||||||
PYDANTIC_V2,
|
PYDANTIC_V2,
|
||||||
ConfigDict,
|
ConfigDict,
|
||||||
|
LegacyUnionField,
|
||||||
ModelField,
|
ModelField,
|
||||||
PydanticUndefined,
|
PydanticUndefined,
|
||||||
PydanticUndefinedType,
|
PydanticUndefinedType,
|
||||||
@@ -424,7 +425,7 @@ class Config(BaseSettings):
|
|||||||
"""NoneBot {ref}`nonebot.drivers.ReverseDriver` 服务端监听的 IP/主机名。"""
|
"""NoneBot {ref}`nonebot.drivers.ReverseDriver` 服务端监听的 IP/主机名。"""
|
||||||
port: int = Field(default=8080, ge=1, le=65535)
|
port: int = Field(default=8080, ge=1, le=65535)
|
||||||
"""NoneBot {ref}`nonebot.drivers.ReverseDriver` 服务端监听的端口。"""
|
"""NoneBot {ref}`nonebot.drivers.ReverseDriver` 服务端监听的端口。"""
|
||||||
log_level: Union[int, str] = "INFO"
|
log_level: Union[int, str] = LegacyUnionField(default="INFO")
|
||||||
"""NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。
|
"""NoneBot 日志输出等级,可以为 `int` 类型等级或等级名称。
|
||||||
|
|
||||||
参考 [记录日志](https://nonebot.dev/docs/appendices/log),[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
|
参考 [记录日志](https://nonebot.dev/docs/appendices/log),[loguru 日志等级](https://loguru.readthedocs.io/en/stable/api/logger.html#levels)。
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
SIMPLE=simple
|
SIMPLE=simple
|
||||||
|
int_str=123
|
||||||
COMPLEX='
|
COMPLEX='
|
||||||
[1, 2, 3]
|
[1, 2, 3]
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Optional, Union
|
|||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from nonebot.compat import PYDANTIC_V2
|
from nonebot.compat import PYDANTIC_V2, LegacyUnionField
|
||||||
from nonebot.config import DOTENV_TYPE, BaseSettings, SettingsConfig, SettingsError
|
from nonebot.config import DOTENV_TYPE, BaseSettings, SettingsConfig, SettingsError
|
||||||
|
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ class Example(BaseSettings):
|
|||||||
env_nested_delimiter = "__"
|
env_nested_delimiter = "__"
|
||||||
|
|
||||||
simple: str = ""
|
simple: str = ""
|
||||||
|
int_str: Union[int, str] = LegacyUnionField(default="")
|
||||||
complex: list[int] = Field(default=[1])
|
complex: list[int] = Field(default=[1])
|
||||||
complex_none: Optional[list[int]] = None
|
complex_none: Optional[list[int]] = None
|
||||||
complex_union: Union[int, list[int]] = 1
|
complex_union: Union[int, list[int]] = 1
|
||||||
@@ -62,6 +63,8 @@ def test_config_with_env():
|
|||||||
config = Example(_env_file=(".env", ".env.example"))
|
config = Example(_env_file=(".env", ".env.example"))
|
||||||
assert config.simple == "simple"
|
assert config.simple == "simple"
|
||||||
|
|
||||||
|
assert config.int_str == 123
|
||||||
|
|
||||||
assert config.complex == [1, 2, 3]
|
assert config.complex == [1, 2, 3]
|
||||||
|
|
||||||
assert config.complex_none is None
|
assert config.complex_none is None
|
||||||
|
|||||||
3
tests/uv.lock
generated
Normal file
3
tests/uv.lock
generated
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.12"
|
||||||
Reference in New Issue
Block a user