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

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 

44 

45AnyFormattedText = Union[ 

46 str, 

47 "MagicFormattedText", 

48 StyleAndTextTuples, 

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

50 Callable[[], Any], 

51 None, 

52] 

53 

54 

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. 

62 

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. 

66 

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 

73 

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 ) 

91 

92 # Apply extra style. 

93 if style: 

94 result = cast( 

95 StyleAndTextTuples, 

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

97 ) 

98 

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) 

106 

107 

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 

121 

122 

123class FormattedText(StyleAndTextTuples): 

124 """ 

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

126 

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

128 tuples.) 

129 """ 

130 

131 def __pt_formatted_text__(self) -> StyleAndTextTuples: 

132 return self 

133 

134 def __repr__(self) -> str: 

135 return "FormattedText(%s)" % super().__repr__() 

136 

137 

138class Template: 

139 """ 

140 Template for string interpolation with formatted text. 

141 

142 Example:: 

143 

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

145 

146 :param text: Plain text. 

147 """ 

148 

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

150 assert "{0}" not in text 

151 self.text = text 

152 

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) 

158 

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 

165 

166 return get_result 

167 

168 

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

170 """ 

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

172 """ 

173 

174 def _merge_formatted_text() -> AnyFormattedText: 

175 result = FormattedText() 

176 for i in items: 

177 result.extend(to_formatted_text(i)) 

178 return result 

179 

180 return _merge_formatted_text