Coverage for src/appl/core/modifiers.py: 92%

74 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2024-11-22 15:39 -0800

1from __future__ import annotations 

2 

3import copy 

4import traceback 

5from abc import ABC, abstractmethod 

6from contextlib import AbstractContextManager 

7from types import TracebackType 

8from typing import Optional, TypeVar, Union 

9 

10from typing_extensions import override 

11 

12from .context import PromptContext 

13from .printer import Indexing, PrinterPop, PrinterPush, PromptPrinter, PromptRecords 

14from .types import MessageRole 

15 

16 

17class PrinterModifier(AbstractContextManager): 

18 """The contextual compositor of the prompt printer. 

19 

20 Controls the behavior of the prompt printer within the context manager. 

21 Should only be used within the APPL function. 

22 """ 

23 

24 __need_ctx__: bool = True 

25 

26 def __init__(self, _ctx: Optional[PromptContext] = None): 

27 """Initialize the PrinterModifier object. 

28 

29 Args: 

30 _ctx: The prompt context filled automatically by the APPL function. 

31 """ 

32 self._ctx = _ctx 

33 

34 @property 

35 def push_args(self) -> PrinterPush: 

36 """The arguments to push to the printer.""" 

37 raise NotImplementedError 

38 

39 def _enter(self) -> None: 

40 if self._ctx is None: 

41 raise ValueError( 

42 "Context is not provided, did you forget mark your function with @ppl?" 

43 ) 

44 self._ctx.push_printer(self.push_args) 

45 

46 def _exit( 

47 self, 

48 _exc_type: Optional[type[BaseException]], 

49 _exc_value: Optional[BaseException], 

50 _traceback: Optional[TracebackType], 

51 ) -> Optional[bool]: 

52 if _exc_type: 

53 # logger.debug(f"Exception occurred: {__exc_value}") 

54 traceback.print_tb(_traceback) 

55 return False 

56 if self._ctx is not None: 

57 self._ctx.pop_printer() 

58 return None 

59 

60 def __enter__(self) -> "PrinterModifier": 

61 self._enter() 

62 return self 

63 

64 def __exit__( 

65 self, 

66 __exc_type: Optional[type[BaseException]], 

67 __exc_value: Optional[BaseException], 

68 __traceback: Optional[TracebackType], 

69 ) -> Optional[bool]: 

70 return self._exit(__exc_type, __exc_value, __traceback) 

71 

72 

73class Compositor(PrinterModifier): 

74 """The contextual compositor of the prompts. 

75 

76 This class represents a contextual compositor that modifies the behavior of the printer. 

77 It provides various options for customizing the output format of the prompts within this context manager. 

78 

79 Attributes: 

80 _sep: The class default separator string is None, indicating inherit from the parent. 

81 _indexing: The class default indexing mode is empty indexing. 

82 _inc_indent: The class default indentation string is empty string. 

83 _new_indent: The class default new indentation string is None, indicating not overwrite the indent. 

84 _is_inline: The class default inline flag is False. 

85 _new_role: The class default role of the modifier is None, indicating not overwrite the role. 

86 """ 

87 

88 # The default value of the printer modifier 

89 _sep: Optional[str] = None # Default: inherit from the parent 

90 _indexing: Indexing = Indexing() # Default: empty indexing 

91 _inc_indent: str = "" # Default: no delta indent 

92 _new_indent: Optional[str] = None # Default: not overwrite the indent 

93 _is_inline: bool = False # Default: not inline 

94 _new_role: Optional[MessageRole] = None # Default: not overwrite the role 

95 

96 def __init__( 

97 self, 

98 sep: Optional[str] = None, 

99 indexing: Union[Indexing, Optional[str]] = None, 

100 indent: Optional[Union[str, int]] = None, 

101 new_indent: Optional[Union[str, int]] = None, 

102 is_inline: Optional[bool] = None, 

103 role: Optional[MessageRole] = None, 

104 _ctx: Optional[PromptContext] = None, 

105 ): 

106 """Initialize the Compositor object. 

107 

108 Args: 

109 sep: 

110 The separator string. Defaults to use the class default. 

111 indexing: 

112 The indexing mode. Defaults to use the class default. 

113 indent: 

114 The indentation string. Defaults to use the class default. 

115 new_indent: 

116 The new indentation string. Defaults to use the class default. 

117 is_inline: 

118 Flag indicating if the modifier is inline. Defaults to use the class default. 

119 role: 

120 The role of the modifier. Defaults to use the class default. 

121 _ctx: The prompt context filled automatically by the APPL function. 

122 """ 

123 super().__init__(_ctx) 

124 if sep is not None: 

125 self._sep = sep 

126 if indexing is not None: 

127 if isinstance(indexing, str): 

128 indexing = Indexing(indexing) 

129 self._indexing = indexing 

130 else: 

131 if self._indexing is None: 

132 raise ValueError("Indexing must be provided.") 

133 self._indexing = copy.copy(self._indexing) 

134 # copy to avoid changing the class default 

135 if indent is not None: 

136 if isinstance(indent, int): 

137 indent = " " * indent 

138 self._inc_indent = indent 

139 if new_indent is not None: 

140 if isinstance(new_indent, int): 

141 new_indent = " " * new_indent 

142 self._new_indent = new_indent 

143 if is_inline is not None: 

144 self._is_inline = is_inline 

145 if role is not None: 

146 self._new_role = role 

147 

148 @override 

149 @property 

150 def push_args(self) -> PrinterPush: 

151 return PrinterPush( 

152 self._new_role, 

153 self._sep, 

154 self._indexing, 

155 self._inc_indent, 

156 self._new_indent, 

157 self._is_inline, 

158 ) 

159 

160 def __enter__(self) -> "Compositor": 

161 super().__enter__() 

162 return self 

163 

164 

165# Essential for compile 

166class ApplStr(Compositor): 

167 """A compositor that represents a string in the prompt. 

168 

169 Attributes: 

170 _sep: The class default separator string is an empty string. 

171 _new_indent: The class default new indentation string is an empty string. 

172 _is_inline: The class default inline flag is True. 

173 

174 Examples: 

175 ```py 

176 >>> with ApplStr(): 

177 ... "Hello, " 

178 ... "world!" 

179 <<< The prompt will be: 

180 Hello, world! 

181 ``` 

182 """ 

183 

184 _sep = "" 

185 _new_indent = "" 

186 _is_inline = True