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
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-22 15:39 -0800
1from __future__ import annotations
3import copy
4import traceback
5from abc import ABC, abstractmethod
6from contextlib import AbstractContextManager
7from types import TracebackType
8from typing import Optional, TypeVar, Union
10from typing_extensions import override
12from .context import PromptContext
13from .printer import Indexing, PrinterPop, PrinterPush, PromptPrinter, PromptRecords
14from .types import MessageRole
17class PrinterModifier(AbstractContextManager):
18 """The contextual compositor of the prompt printer.
20 Controls the behavior of the prompt printer within the context manager.
21 Should only be used within the APPL function.
22 """
24 __need_ctx__: bool = True
26 def __init__(self, _ctx: Optional[PromptContext] = None):
27 """Initialize the PrinterModifier object.
29 Args:
30 _ctx: The prompt context filled automatically by the APPL function.
31 """
32 self._ctx = _ctx
34 @property
35 def push_args(self) -> PrinterPush:
36 """The arguments to push to the printer."""
37 raise NotImplementedError
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)
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
60 def __enter__(self) -> "PrinterModifier":
61 self._enter()
62 return self
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)
73class Compositor(PrinterModifier):
74 """The contextual compositor of the prompts.
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.
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 """
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
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.
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
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 )
160 def __enter__(self) -> "Compositor":
161 super().__enter__()
162 return self
165# Essential for compile
166class ApplStr(Compositor):
167 """A compositor that represents a string in the prompt.
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.
174 Examples:
175 ```py
176 >>> with ApplStr():
177 ... "Hello, "
178 ... "world!"
179 <<< The prompt will be:
180 Hello, world!
181 ```
182 """
184 _sep = ""
185 _new_indent = ""
186 _is_inline = True