跳转至

formatter

基于 format string 的消息链格式化器

Formatter 🔗

类似于 string.Formatter 的消息链格式化器

Source code in src/graia/ariadne/message/formatter.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
class Formatter:
    """类似于 string.Formatter 的消息链格式化器"""

    format_string: str

    def __init__(self, format_string: str) -> None:
        self.format_string = format_string
        self._fields: tuple[tuple[str, str | None, str | None, str | None], ...] = tuple(
            _global_formatter.parse(format_string)
        )
        for field in self._fields:
            # validation, forbidding the expansion for format spec
            *_, format_spec, _ = field
            if format_spec:
                sub_spec = tuple(_global_formatter.parse(format_spec))
                if len(sub_spec) > 1 or any(sub_spec[1:]):  # Definitely not right spec
                    raise ValueError("Format specification expansion is disallowed, found ")

    @staticmethod
    def _convert_field(value: Any, conversion: None | str) -> Any:
        # do any conversion on the resulting object
        if conversion is None:
            return value
        elif conversion == "s":
            return str(value)
        elif conversion == "r":
            return repr(value)
        elif conversion == "a":
            return ascii(value)
        raise ValueError(f"Unknown conversion specifier {conversion!s}")

    @staticmethod
    def _transform(obj: object) -> list[Element]:
        if isinstance(obj, MessageChain):
            return obj.content
        elif isinstance(obj, (Element, str)):
            return [Plain(obj) if isinstance(obj, str) else obj]
        else:
            return [Plain(str(obj))]

    def format(
        self,
        *args: Element | MessageChain | str | Any,
        **kwargs: Element | MessageChain | str | Any,
    ) -> MessageChain:
        """通过初始化时传入的格式字符串 格式化消息链

        Args:
            *args (Union[Element, MessageChain, str, Any]): 格式化时传入的位置参数
            **kwargs (Union[Element, MessageChain, str, Any]): 格式化时传入的关键字参数

        Returns:
            MessageChain: 格式化后的消息链
        """
        result: list[Element] = []
        auto_arg_index: int | Literal[False] = 0
        used_args: set[str] = set()
        for field in self._fields:
            literal_text, field_name, format_spec, conversion = field
            if literal_text:
                result.append(Plain(literal_text))
            if field_name is None:
                continue
            # if there's a field, output it
            # this is some markup, find the object and do
            #  the formatting

            # handle arg indexing when empty field_names are given.
            if field_name == "":
                if auto_arg_index is False:
                    raise ValueError(
                        "cannot switch from manual field specification to automatic field numbering"
                    )
                field_name = str(auto_arg_index)
                auto_arg_index += 1
            elif field_name.isdigit():
                if auto_arg_index:
                    raise ValueError(
                        "cannot switch from manual field specification to automatic field numbering"
                    )
                # disable auto arg incrementing, if it gets
                # used later on, then an exception will be raised
                auto_arg_index = False

            # given the field_name, find the object it references
            #  and the argument it came from
            obj, arg_used = _global_formatter.get_field(field_name, args, kwargs)
            used_args.add(arg_used)

            # do any conversion on the resulting object
            obj = self._convert_field(obj, conversion)

            # format the object and append to the result
            if format_spec:
                obj = format(obj, format_spec)

            result.extend(self._transform(obj))

        return MessageChain(result, inline=True).merge()

format 🔗

format(
    *args: Element | MessageChain | str | Any,
    **kwargs: Element | MessageChain | str | Any
) -> MessageChain

通过初始化时传入的格式字符串 格式化消息链

Parameters:

Returns:

Source code in src/graia/ariadne/message/formatter.py
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
def format(
    self,
    *args: Element | MessageChain | str | Any,
    **kwargs: Element | MessageChain | str | Any,
) -> MessageChain:
    """通过初始化时传入的格式字符串 格式化消息链

    Args:
        *args (Union[Element, MessageChain, str, Any]): 格式化时传入的位置参数
        **kwargs (Union[Element, MessageChain, str, Any]): 格式化时传入的关键字参数

    Returns:
        MessageChain: 格式化后的消息链
    """
    result: list[Element] = []
    auto_arg_index: int | Literal[False] = 0
    used_args: set[str] = set()
    for field in self._fields:
        literal_text, field_name, format_spec, conversion = field
        if literal_text:
            result.append(Plain(literal_text))
        if field_name is None:
            continue
        # if there's a field, output it
        # this is some markup, find the object and do
        #  the formatting

        # handle arg indexing when empty field_names are given.
        if field_name == "":
            if auto_arg_index is False:
                raise ValueError(
                    "cannot switch from manual field specification to automatic field numbering"
                )
            field_name = str(auto_arg_index)
            auto_arg_index += 1
        elif field_name.isdigit():
            if auto_arg_index:
                raise ValueError(
                    "cannot switch from manual field specification to automatic field numbering"
                )
            # disable auto arg incrementing, if it gets
            # used later on, then an exception will be raised
            auto_arg_index = False

        # given the field_name, find the object it references
        #  and the argument it came from
        obj, arg_used = _global_formatter.get_field(field_name, args, kwargs)
        used_args.add(arg_used)

        # do any conversion on the resulting object
        obj = self._convert_field(obj, conversion)

        # format the object and append to the result
        if format_spec:
            obj = format(obj, format_spec)

        result.extend(self._transform(obj))

    return MessageChain(result, inline=True).merge()