⚗️ template support raw string

This commit is contained in:
yanyongyu 2021-10-04 22:00:32 +08:00
parent 9a84bd175c
commit e12e18022b
4 changed files with 68 additions and 19 deletions

View File

@ -320,9 +320,9 @@ Message(MessageSegment(type='text', data={'text': 'hello world'}))
Message(MessageSegment(type='image', data={'file': 'file///...'}), MessageSegment(type='text', data={'text': 'world'}))
>>> Message.template( # 支持以Message对象作为消息模板
... MessageSegment.text('test {event.user_id}') + MessageSegment.face(233) +
... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'})
Message(MessageSegment(type='text', data={'text': 'test 123456'}),
MessageSegment(type='face', data={'face': 233}),
... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'})
Message(MessageSegment(type='text', data={'text': 'test 123456'}),
MessageSegment(type='face', data={'face': 233}),
MessageSegment(type='text', data={'text': 'test hello world'}))
>>> Message.template("{link:image}").format(link='https://...') # 支持拓展格式化控制符
Message(MessageSegment(type='image', data={'file': 'https://...'}))
@ -547,11 +547,30 @@ Event 基类。提供获取关键信息的方法,其余信息可直接获取
## _class_ `MessageTemplate`
基类:`string.Formatter`, `Generic`[`nonebot.adapters._template.TM`]
基类:`string.Formatter`, `Generic`[`nonebot.adapters._template.TF`]
消息模板格式化实现类
### `__init__(template, factory=<class 'str'>)`
* **说明**
创建一个模板
* **参数**
* `template: Union[str, Message]`: 模板
* `factory: Union[str, Message]`: 消息构造类型,默认为 str
### `format(*args, **kwargs)`

View File

@ -121,9 +121,9 @@ class Message(List[TMS], abc.ABC):
Message(MessageSegment(type='image', data={'file': 'file///...'}), MessageSegment(type='text', data={'text': 'world'}))
>>> Message.template( # 支持以Message对象作为消息模板
... MessageSegment.text('test {event.user_id}') + MessageSegment.face(233) +
... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'})
Message(MessageSegment(type='text', data={'text': 'test 123456'}),
MessageSegment(type='face', data={'face': 233}),
... MessageSegment.text('test {event.message}')).format(event={'user_id':123456, 'message':'hello world'})
Message(MessageSegment(type='text', data={'text': 'test 123456'}),
MessageSegment(type='face', data={'face': 233}),
MessageSegment(type='text', data={'text': 'test hello world'}))
>>> Message.template("{link:image}").format(link='https://...') # 支持拓展格式化控制符
Message(MessageSegment(type='image', data={'file': 'https://...'}))
@ -136,7 +136,7 @@ class Message(List[TMS], abc.ABC):
- ``MessageFormatter[TM]``: 消息格式化器
"""
return MessageTemplate(cls, format_string)
return MessageTemplate(format_string, cls)
@classmethod
@abc.abstractmethod

View File

@ -2,22 +2,44 @@ import inspect
import functools
from string import Formatter
from typing import (TYPE_CHECKING, Any, Set, List, Type, Tuple, Union, Generic,
Mapping, TypeVar, Sequence)
Mapping, TypeVar, Sequence, cast, overload)
if TYPE_CHECKING:
from . import Message, MessageSegment
TM = TypeVar("TM", bound="Message")
TF = TypeVar("TF", str, "Message")
class MessageTemplate(Formatter, Generic[TM]):
class MessageTemplate(Formatter, Generic[TF]):
"""消息模板格式化实现类"""
def __init__(self, factory: Type[TM], template: Union[str, TM]) -> None:
self.template = template
self.factory = factory
@overload
def __init__(self: "MessageTemplate[str]",
template: str,
factory: Type[str] = str) -> None:
...
def format(self, *args: Any, **kwargs: Any) -> TM:
@overload
def __init__(self: "MessageTemplate[TM]", template: Union[str, TM],
factory: Type[TM]) -> None:
...
def __init__(self, template, factory=str) -> None:
"""
:说明:
创建一个模板
:参数:
* ``template: Union[str, Message]``: 模板
* ``factory: Union[str, Message]``: 消息构造类型默认为 `str`
"""
self.template: Union[str, TF] = template
self.factory: Type[TF] = factory
def format(self, *args: Any, **kwargs: Any) -> TF:
"""
:说明:
@ -27,8 +49,8 @@ class MessageTemplate(Formatter, Generic[TM]):
if isinstance(self.template, str):
msg += self.vformat(self.template, args, kwargs)
elif isinstance(self.template, self.factory):
for seg in self.template:
seg: "MessageSegment"
template = cast("Message[MessageSegment]", self.template)
for seg in template:
msg += self.vformat(str(seg), args,
kwargs) if seg.is_text() else seg
else:
@ -37,7 +59,7 @@ class MessageTemplate(Formatter, Generic[TM]):
return msg
def vformat(self, format_string: str, args: Sequence[Any],
kwargs: Mapping[str, Any]) -> TM:
kwargs: Mapping[str, Any]) -> TF:
used_args = set()
result, _ = self._vformat(format_string, args, kwargs, used_args, 2)
self.check_unused_args(list(used_args), args, kwargs)
@ -51,8 +73,7 @@ class MessageTemplate(Formatter, Generic[TM]):
used_args: Set[Union[int, str]],
recursion_depth: int,
auto_arg_index: int = 0,
) -> Tuple[TM, int]:
) -> Tuple[TF, int]:
if recursion_depth < 0:
raise ValueError("Max string recursion exceeded")
@ -115,6 +136,9 @@ class MessageTemplate(Formatter, Generic[TM]):
[""])), auto_arg_index
def format_field(self, value: Any, format_spec: str) -> Any:
if issubclass(self.factory, str):
return super().format_field(value, format_spec)
segment_class: Type[MessageSegment] = self.factory.get_segment_class()
method = getattr(segment_class, format_spec, None)
method_type = inspect.getattr_static(segment_class, format_spec, None)

View File

@ -4,6 +4,10 @@ sidebar: auto
# 更新日志
## v2.0.0a17
- 新增 `MessageTemplate` 对于 `str` 普通模板的支持
## v2.0.0a16
- 新增 `MessageTemplate` 可用于 `Message` 的模板生成
@ -13,6 +17,8 @@ sidebar: auto
- 修复 `fastapi` Driver Websocket 未能正确提供请求头部
- 新增 `fastapi` Driver 更多的 uvicorn 相关配置项
- 新增 `quart` Driver 更多的 uvicorn 相关配置项
- 修复 `endswith` Rule 错误的正则匹配
- 修复 `cqhttp` Adapter `image`, `record`, `video``BytesIO` 不正常的读取操作
## v2.0.0a15