Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/prompt_toolkit/formatted_text/base.py: 26%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

68 statements  

1from __future__ import annotations 

2 

3from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Tuple, Union, cast 

4 

5from prompt_toolkit.mouse_events import MouseEvent 

6 

7if TYPE_CHECKING: 

8 from typing_extensions import Protocol 

9 

10 from prompt_toolkit.key_binding.key_bindings import NotImplementedOrNone 

11 

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] 

23 

24OneStyleAndTextTuple = Union[ 

25 Tuple[str, str], Tuple[str, str, Callable[[MouseEvent], "NotImplementedOrNone"]] 

26] 

27 

28# List of (style, text) tuples. 

29StyleAndTextTuples = List[OneStyleAndTextTuple] 

30 

31 

32if TYPE_CHECKING: 

33 from typing_extensions import TypeGuard 

34 

35 class MagicFormattedText(Protocol): 

36 """ 

37 Any object that implements ``__pt_formatted_text__`` represents formatted 

38 text. 

39 """ 

40 

41 def __pt_formatted_text__(self) -> StyleAndTextTuples: ... 

42 

43 

44AnyFormattedText = Union[ 

45 str, 

46 "MagicFormattedText", 

47 StyleAndTextTuples, 

48 # Callable[[], 'AnyFormattedText'] # Recursive definition not supported by mypy. 

49 Callable[[], Any], 

50 None, 

51] 

52 

53 

54def to_formatted_text( 

55 value: AnyFormattedText, style: str = "", auto_convert: bool = False 

56) -> FormattedText: 

57 """ 

58 Convert the given value (which can be formatted text) into a list of text 

59 fragments. (Which is the canonical form of formatted text.) The outcome is 

60 always a `FormattedText` instance, which is a list of (style, text) tuples. 

61 

62 It can take a plain text string, an `HTML` or `ANSI` object, anything that 

63 implements `__pt_formatted_text__` or a callable that takes no arguments and 

64 returns one of those. 

65 

66 :param style: An additional style string which is applied to all text 

67 fragments. 

68 :param auto_convert: If `True`, also accept other types, and convert them 

69 to a string first. 

70 """ 

71 result: FormattedText | StyleAndTextTuples 

72 

73 if value is None: 

74 result = [] 

75 elif isinstance(value, str): 

76 result = [("", value)] 

77 elif isinstance(value, list): 

78 result = value # StyleAndTextTuples 

79 elif hasattr(value, "__pt_formatted_text__"): 

80 result = cast("MagicFormattedText", value).__pt_formatted_text__() 

81 elif callable(value): 

82 return to_formatted_text(value(), style=style) 

83 elif auto_convert: 

84 result = [("", f"{value}")] 

85 else: 

86 raise ValueError( 

87 "No formatted text. Expecting a unicode object, " 

88 f"HTML, ANSI or a FormattedText instance. Got {value!r}" 

89 ) 

90 

91 # Apply extra style. 

92 if style: 

93 result = cast( 

94 StyleAndTextTuples, 

95 [(style + " " + item_style, *rest) for item_style, *rest in result], 

96 ) 

97 

98 # Make sure the result is wrapped in a `FormattedText`. Among other 

99 # reasons, this is important for `print_formatted_text` to work correctly 

100 # and distinguish between lists and formatted text. 

101 if isinstance(result, FormattedText): 

102 return result 

103 else: 

104 return FormattedText(result) 

105 

106 

107def is_formatted_text(value: object) -> TypeGuard[AnyFormattedText]: 

108 """ 

109 Check whether the input is valid formatted text (for use in assert 

110 statements). 

111 In case of a callable, it doesn't check the return type. 

112 """ 

113 if callable(value): 

114 return True 

115 if isinstance(value, (str, list)): 

116 return True 

117 if hasattr(value, "__pt_formatted_text__"): 

118 return True 

119 return False 

120 

121 

122class FormattedText(StyleAndTextTuples): 

123 """ 

124 A list of ``(style, text)`` tuples. 

125 

126 (In some situations, this can also be ``(style, text, mouse_handler)`` 

127 tuples.) 

128 """ 

129 

130 def __pt_formatted_text__(self) -> StyleAndTextTuples: 

131 return self 

132 

133 def __repr__(self) -> str: 

134 return f"FormattedText({super().__repr__()})" 

135 

136 

137class Template: 

138 """ 

139 Template for string interpolation with formatted text. 

140 

141 Example:: 

142 

143 Template(' ... {} ... ').format(HTML(...)) 

144 

145 :param text: Plain text. 

146 """ 

147 

148 def __init__(self, text: str) -> None: 

149 assert "{0}" not in text 

150 self.text = text 

151 

152 def format(self, *values: AnyFormattedText) -> AnyFormattedText: 

153 def get_result() -> AnyFormattedText: 

154 # Split the template in parts. 

155 parts = self.text.split("{}") 

156 assert len(parts) - 1 == len(values) 

157 

158 result = FormattedText() 

159 for part, val in zip(parts, values): 

160 result.append(("", part)) 

161 result.extend(to_formatted_text(val)) 

162 result.append(("", parts[-1])) 

163 return result 

164 

165 return get_result 

166 

167 

168def merge_formatted_text(items: Iterable[AnyFormattedText]) -> AnyFormattedText: 

169 """ 

170 Merge (Concatenate) several pieces of formatted text together. 

171 """ 

172 

173 def _merge_formatted_text() -> AnyFormattedText: 

174 result = FormattedText() 

175 for i in items: 

176 result.extend(to_formatted_text(i)) 

177 return result 

178 

179 return _merge_formatted_text