Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/prompt_toolkit/formatted_text/base.py: 26%
69 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1from __future__ import annotations
3from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Tuple, Union, cast
5from prompt_toolkit.mouse_events import MouseEvent
7if TYPE_CHECKING:
8 from typing_extensions import Protocol
10 from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone
12__all__ = [
13 "OneStyleAndTextTuple",
14 "StyleAndTextTuples",
15 "MagicFormattedText",
16 "AnyFormattedText",
17 "to_formatted_text",
18 "is_formatted_text",
19 "Template",
20 "merge_formatted_text",
21 "FormattedText",
22]
24OneStyleAndTextTuple = Union[
25 Tuple[str, str], Tuple[str, str, Callable[[MouseEvent], "NotImplementedOrNone"]]
26]
28# List of (style, text) tuples.
29StyleAndTextTuples = List[OneStyleAndTextTuple]
32if TYPE_CHECKING:
33 from typing_extensions import TypeGuard
35 class MagicFormattedText(Protocol):
36 """
37 Any object that implements ``__pt_formatted_text__`` represents formatted
38 text.
39 """
41 def __pt_formatted_text__(self) -> StyleAndTextTuples:
42 ...
45AnyFormattedText = Union[
46 str,
47 "MagicFormattedText",
48 StyleAndTextTuples,
49 # Callable[[], 'AnyFormattedText'] # Recursive definition not supported by mypy.
50 Callable[[], Any],
51 None,
52]
55def to_formatted_text(
56 value: AnyFormattedText, style: str = "", auto_convert: bool = False
57) -> FormattedText:
58 """
59 Convert the given value (which can be formatted text) into a list of text
60 fragments. (Which is the canonical form of formatted text.) The outcome is
61 always a `FormattedText` instance, which is a list of (style, text) tuples.
63 It can take a plain text string, an `HTML` or `ANSI` object, anything that
64 implements `__pt_formatted_text__` or a callable that takes no arguments and
65 returns one of those.
67 :param style: An additional style string which is applied to all text
68 fragments.
69 :param auto_convert: If `True`, also accept other types, and convert them
70 to a string first.
71 """
72 result: FormattedText | StyleAndTextTuples
74 if value is None:
75 result = []
76 elif isinstance(value, str):
77 result = [("", value)]
78 elif isinstance(value, list):
79 result = value # StyleAndTextTuples
80 elif hasattr(value, "__pt_formatted_text__"):
81 result = cast("MagicFormattedText", value).__pt_formatted_text__()
82 elif callable(value):
83 return to_formatted_text(value(), style=style)
84 elif auto_convert:
85 result = [("", f"{value}")]
86 else:
87 raise ValueError(
88 "No formatted text. Expecting a unicode object, "
89 f"HTML, ANSI or a FormattedText instance. Got {value!r}"
90 )
92 # Apply extra style.
93 if style:
94 result = cast(
95 StyleAndTextTuples,
96 [(style + " " + item_style, *rest) for item_style, *rest in result],
97 )
99 # Make sure the result is wrapped in a `FormattedText`. Among other
100 # reasons, this is important for `print_formatted_text` to work correctly
101 # and distinguish between lists and formatted text.
102 if isinstance(result, FormattedText):
103 return result
104 else:
105 return FormattedText(result)
108def is_formatted_text(value: object) -> TypeGuard[AnyFormattedText]:
109 """
110 Check whether the input is valid formatted text (for use in assert
111 statements).
112 In case of a callable, it doesn't check the return type.
113 """
114 if callable(value):
115 return True
116 if isinstance(value, (str, list)):
117 return True
118 if hasattr(value, "__pt_formatted_text__"):
119 return True
120 return False
123class FormattedText(StyleAndTextTuples):
124 """
125 A list of ``(style, text)`` tuples.
127 (In some situations, this can also be ``(style, text, mouse_handler)``
128 tuples.)
129 """
131 def __pt_formatted_text__(self) -> StyleAndTextTuples:
132 return self
134 def __repr__(self) -> str:
135 return "FormattedText(%s)" % super().__repr__()
138class Template:
139 """
140 Template for string interpolation with formatted text.
142 Example::
144 Template(' ... {} ... ').format(HTML(...))
146 :param text: Plain text.
147 """
149 def __init__(self, text: str) -> None:
150 assert "{0}" not in text
151 self.text = text
153 def format(self, *values: AnyFormattedText) -> AnyFormattedText:
154 def get_result() -> AnyFormattedText:
155 # Split the template in parts.
156 parts = self.text.split("{}")
157 assert len(parts) - 1 == len(values)
159 result = FormattedText()
160 for part, val in zip(parts, values):
161 result.append(("", part))
162 result.extend(to_formatted_text(val))
163 result.append(("", parts[-1]))
164 return result
166 return get_result
169def merge_formatted_text(items: Iterable[AnyFormattedText]) -> AnyFormattedText:
170 """
171 Merge (Concatenate) several pieces of formatted text together.
172 """
174 def _merge_formatted_text() -> AnyFormattedText:
175 result = FormattedText()
176 for i in items:
177 result.extend(to_formatted_text(i))
178 return result
180 return _merge_formatted_text