teddecor.Exceptions.base
1from functools import cached_property 2from inspect import stack, FrameInfo 3from os import get_terminal_size 4 5from ..TED.markup import TED 6from ..Util import slash 7 8__all__ = [ 9 "BaseException", 10 "HintedException", 11 "MissingValueException", 12 "RangedException", 13] 14 15 16def get_len(string: str) -> int: 17 """Get the length of a string with TED markup removed. 18 19 Args: 20 string (str): The string to get the length of 21 22 Returns: 23 int: The actual length of the string with inline markup 24 """ 25 from re import sub 26 27 string = sub(r"\x1b\[(\d{0,2};?)*m|(?<!\\)\[.*\]|(?<!\\)\*|(?<!\\)_", "", string) 28 return len(string) 29 30 31class Frame: 32 """Parses FrameInfo into it's string representation.""" 33 34 def __init__(self, frame: FrameInfo): 35 self._line = frame.lineno 36 self._func = frame.function 37 self._path = frame.filename 38 self._module = frame.filename.split(slash())[-1].split(".")[0] 39 40 def __str__(self) -> str: 41 return f"<[@F blue ~{self._path}]{TED.encode(self._module)}[~ @F]:[@F yellow]{self._line}[@F]> in {TED.encode(self._func)}" 42 43 44class BaseException(Exception): 45 """Base exception that uses TED markup. It will parse and clean up the stack trace along with printing a custom message. 46 47 Note: 48 The TEDDecor exceptions have two ways of calling them: 49 50 1. raise the exception as you would any other 51 2. Use the included throw method 52 * Will be like raise but use the custom pretty stack trace 53 * Will also allow for the exception to be used like an error and display the result and continue without a try except. 54 """ 55 56 def __init__(self, message: str = "An unknown error occured"): 57 # config: Dict[str, Dict[str, bool]] = {"stack": {"bold": True}} 58 self._stack = [Frame(frame) for frame in stack()] 59 self._message = message 60 super().__init__(TED.parse(message)) 61 self.slice_stack() 62 63 def slice_stack(self) -> list: 64 """Removes the part of the stack trace that includes this exceptions processing. 65 66 Returns: 67 list: The updated stack trace 68 """ 69 self._stack = self._stack[1:] 70 71 @property 72 def stack(self) -> list[str]: 73 """Gets the stack trace as a list of type str instead of type Frame.""" 74 return [str(frame) for frame in self._stack] 75 76 @property 77 def message(self) -> str: 78 """Get the error message.""" 79 return self._message 80 81 def __str__(self) -> str: 82 """Only used when class is called with raise""" 83 return TED.parse(f"[@F red]*{self.message}") 84 85 def throw(self, exits: bool = True) -> str: 86 """Custom throw method that allows for more custom output. 87 88 Args: 89 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 90 91 Returns: 92 str: The output that would be to the screen if exits, else None 93 """ 94 stack = "\n".join(" " + st for st in self.stack) 95 if exits: 96 from sys import exit 97 98 TED.print("*" + stack) 99 TED.print("[@ red]*" + self.message) 100 exit(2) 101 else: 102 output = [ 103 TED.parse(stack), 104 TED.parse(self.message), 105 ] 106 return "\n".join(output) 107 108 109class HintedException(BaseException): 110 """This exception has a hint. In this case this means that the exception points to a part of a string and describes the problem. 111 112 Example: 113 Hint Exception: 114 Are you having a good day 115 ↑ You must end sentances with punctuation 116 """ 117 118 def __init__( 119 self, 120 value: str, 121 index: int, 122 hint: str, 123 message: str = "Error with specific value", 124 ): 125 super().__init__(message) 126 self.slice_stack() 127 128 self._value = value 129 self._hint = hint 130 self._index = index 131 132 @cached_property 133 def error(self) -> str: 134 """The combination of the string that is being hinted to and the hint. 135 Will automatically adjust for terminal width. 136 """ 137 width = get_terminal_size().columns 138 # self._index = self._index % width if self._index > width else self._index 139 arrow = "[@F yellow]↑[@F]" 140 if ((self._index + len(self.hint + arrow) + 1) > width) and ( 141 get_len(self.hint + arrow) + 1 < self._index 142 ): 143 error = ( 144 f" {self.value}\n " 145 + " " * (self._index - get_len(self.hint) + get_len(arrow) - 2) 146 + f"{self.hint} {arrow}" 147 ) 148 else: 149 error = ( 150 f" {self.value}\n " + " " * (self._index + 1) + f"{arrow} {self.hint}" 151 ) 152 153 return error 154 155 @property 156 def value(self) -> str: 157 """The value to be hinted too.""" 158 return self._value 159 160 @property 161 def hint(self) -> str: 162 """The values hint.""" 163 return self._hint 164 165 @property 166 def index(self) -> int: 167 """At what index of the value that is hinted to should the hint be placed. 168 This is where the arrow will appear. 169 """ 170 return self._index 171 172 def throw(self, exits: bool = True) -> str: 173 """Custom throw method that allows for more custom output. 174 175 Args: 176 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 177 178 Returns: 179 str: The output that would be to the screen if exits, else None 180 """ 181 stack = "\n".join(" " + st for st in self.stack) 182 if exits: 183 from sys import exit 184 185 TED.print("*Hinted Error:*") 186 TED.print("*" + stack, "\n") 187 TED.print("*[@F red]" + self.message + "*:*") 188 TED.print("*" + self.error) 189 exit(3) 190 else: 191 output = [TED.parse("*Hinted Error:*")] 192 output.append(TED.parse("*" + stack)) 193 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 194 output.append(TED.parse("*" + self.error)) 195 return "\n".join(output) 196 197 198class MissingValueException(BaseException): 199 """This exception is to point out a missing value. 200 In the example below, ` `, indicates text that is injected as a missing value. 201 202 Example: 203 Missing punctuation: 204 Are you haveing a good day`?` 205 """ 206 207 def __init__( 208 self, 209 value: str, 210 index: int, 211 missing: str, 212 message: str = "Missing Value", 213 ): 214 super().__init__(message) 215 self.slice_stack() 216 217 self._value = value 218 self._index = index 219 self._missing = missing 220 221 @cached_property 222 def error(self) -> str: 223 """The value with the injected missing value.""" 224 return f" {self.value[:self._index]}[@F red]{self._missing}[@F]{self.value[self._index:]}" 225 226 @property 227 def value(self) -> str: 228 """The value that is missing something.""" 229 return self._value 230 231 @property 232 def missing(self) -> str: 233 """What is missing from the value.""" 234 return self._missing 235 236 @property 237 def index(self) -> int: 238 """The index where the value should be injected.""" 239 return self._index 240 241 def throw(self, exits: bool = True) -> str: 242 """Custom throw method that allows for more custom output. 243 244 Args: 245 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 246 247 Returns: 248 str: The output that would be to the screen if exits, else None 249 """ 250 stack = "\n".join(" " + st for st in self.stack) 251 if exits: 252 from sys import exit 253 254 TED.print("*Missing Value Error:*") 255 TED.print("*" + stack + "\n") 256 TED.print("*[@F red]" + self.message + "*:*") 257 TED.print("*" + self.error) 258 259 exit(4) 260 else: 261 output = [TED.parse("*Missing Value Error:*")] 262 output.append(TED.parse("*" + stack + "\n")) 263 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 264 output.append(TED.parse("*" + self.error)) 265 return "\n".join(output) 266 267 268class RangedException(BaseException): 269 """This exception has a hint that applies to a range of a value. In this case this means that the exception points to a part of a string and describes the problem. 270 271 Example: 272 Hint Exception: 273 Are you having a good day 274 --- 275 Day must be `week` 276 """ 277 278 def __init__( 279 self, 280 value: str, 281 hint: str, 282 start: int = 0, 283 end: int = -1, 284 message: str = "Error with specific value", 285 ): 286 super().__init__(message) 287 self.slice_stack() 288 289 self._value = value 290 self._hint = hint 291 self._start = start 292 self._end = end 293 294 @cached_property 295 def error(self) -> str: 296 """The value with the injected missing value.""" 297 return ( 298 f" {self.value[:self._start]}_[@F red]{self.value[self._start:self._end]}[@F]_{self._value[self._end:]}\n" 299 # + " " 300 # + " " * (self._end - (self._end - self._start) // 2) 301 # + self._hint 302 ) 303 304 @property 305 def value(self) -> str: 306 """The value that is missing something.""" 307 return self._value 308 309 @property 310 def hint(self) -> str: 311 """What is missing from the value.""" 312 return self._hint 313 314 @property 315 def start(self) -> int: 316 """The index where the hint starts.""" 317 return self._start 318 319 @property 320 def end(self) -> int: 321 """The index where the value hint ends.""" 322 return self._end 323 324 def throw(self, exits: bool = True) -> str: 325 """Custom throw method that allows for more custom output. 326 327 Args: 328 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 329 330 Returns: 331 str: The output that would be to the screen if exits, else None 332 """ 333 stack = "\n".join(" " + st for st in self.stack) 334 if exits: 335 from sys import exit 336 337 TED.print("*Ranged Error:*") 338 TED.print("*" + stack + "\n") 339 TED.print("*[@F red]" + self.message + "*:*") 340 TED.print("*" + self.error) 341 342 exit(4) 343 else: 344 output = [TED.parse("*Ranged Error:*")] 345 output.append(TED.parse("*" + stack + "\n")) 346 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 347 output.append(TED.parse("*" + self.error)) 348 return "\n".join(output)
45class BaseException(Exception): 46 """Base exception that uses TED markup. It will parse and clean up the stack trace along with printing a custom message. 47 48 Note: 49 The TEDDecor exceptions have two ways of calling them: 50 51 1. raise the exception as you would any other 52 2. Use the included throw method 53 * Will be like raise but use the custom pretty stack trace 54 * Will also allow for the exception to be used like an error and display the result and continue without a try except. 55 """ 56 57 def __init__(self, message: str = "An unknown error occured"): 58 # config: Dict[str, Dict[str, bool]] = {"stack": {"bold": True}} 59 self._stack = [Frame(frame) for frame in stack()] 60 self._message = message 61 super().__init__(TED.parse(message)) 62 self.slice_stack() 63 64 def slice_stack(self) -> list: 65 """Removes the part of the stack trace that includes this exceptions processing. 66 67 Returns: 68 list: The updated stack trace 69 """ 70 self._stack = self._stack[1:] 71 72 @property 73 def stack(self) -> list[str]: 74 """Gets the stack trace as a list of type str instead of type Frame.""" 75 return [str(frame) for frame in self._stack] 76 77 @property 78 def message(self) -> str: 79 """Get the error message.""" 80 return self._message 81 82 def __str__(self) -> str: 83 """Only used when class is called with raise""" 84 return TED.parse(f"[@F red]*{self.message}") 85 86 def throw(self, exits: bool = True) -> str: 87 """Custom throw method that allows for more custom output. 88 89 Args: 90 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 91 92 Returns: 93 str: The output that would be to the screen if exits, else None 94 """ 95 stack = "\n".join(" " + st for st in self.stack) 96 if exits: 97 from sys import exit 98 99 TED.print("*" + stack) 100 TED.print("[@ red]*" + self.message) 101 exit(2) 102 else: 103 output = [ 104 TED.parse(stack), 105 TED.parse(self.message), 106 ] 107 return "\n".join(output)
Base exception that uses TED markup. It will parse and clean up the stack trace along with printing a custom message.
Note: The TEDDecor exceptions have two ways of calling them:
1. raise the exception as you would any other
2. Use the included throw method
* Will be like raise but use the custom pretty stack trace
* Will also allow for the exception to be used like an error and display the result and continue without a try except.
64 def slice_stack(self) -> list: 65 """Removes the part of the stack trace that includes this exceptions processing. 66 67 Returns: 68 list: The updated stack trace 69 """ 70 self._stack = self._stack[1:]
Removes the part of the stack trace that includes this exceptions processing.
Returns: list: The updated stack trace
86 def throw(self, exits: bool = True) -> str: 87 """Custom throw method that allows for more custom output. 88 89 Args: 90 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 91 92 Returns: 93 str: The output that would be to the screen if exits, else None 94 """ 95 stack = "\n".join(" " + st for st in self.stack) 96 if exits: 97 from sys import exit 98 99 TED.print("*" + stack) 100 TED.print("[@ red]*" + self.message) 101 exit(2) 102 else: 103 output = [ 104 TED.parse(stack), 105 TED.parse(self.message), 106 ] 107 return "\n".join(output)
Custom throw method that allows for more custom output.
Args: exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True.
Returns: str: The output that would be to the screen if exits, else None
Inherited Members
110class HintedException(BaseException): 111 """This exception has a hint. In this case this means that the exception points to a part of a string and describes the problem. 112 113 Example: 114 Hint Exception: 115 Are you having a good day 116 ↑ You must end sentances with punctuation 117 """ 118 119 def __init__( 120 self, 121 value: str, 122 index: int, 123 hint: str, 124 message: str = "Error with specific value", 125 ): 126 super().__init__(message) 127 self.slice_stack() 128 129 self._value = value 130 self._hint = hint 131 self._index = index 132 133 @cached_property 134 def error(self) -> str: 135 """The combination of the string that is being hinted to and the hint. 136 Will automatically adjust for terminal width. 137 """ 138 width = get_terminal_size().columns 139 # self._index = self._index % width if self._index > width else self._index 140 arrow = "[@F yellow]↑[@F]" 141 if ((self._index + len(self.hint + arrow) + 1) > width) and ( 142 get_len(self.hint + arrow) + 1 < self._index 143 ): 144 error = ( 145 f" {self.value}\n " 146 + " " * (self._index - get_len(self.hint) + get_len(arrow) - 2) 147 + f"{self.hint} {arrow}" 148 ) 149 else: 150 error = ( 151 f" {self.value}\n " + " " * (self._index + 1) + f"{arrow} {self.hint}" 152 ) 153 154 return error 155 156 @property 157 def value(self) -> str: 158 """The value to be hinted too.""" 159 return self._value 160 161 @property 162 def hint(self) -> str: 163 """The values hint.""" 164 return self._hint 165 166 @property 167 def index(self) -> int: 168 """At what index of the value that is hinted to should the hint be placed. 169 This is where the arrow will appear. 170 """ 171 return self._index 172 173 def throw(self, exits: bool = True) -> str: 174 """Custom throw method that allows for more custom output. 175 176 Args: 177 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 178 179 Returns: 180 str: The output that would be to the screen if exits, else None 181 """ 182 stack = "\n".join(" " + st for st in self.stack) 183 if exits: 184 from sys import exit 185 186 TED.print("*Hinted Error:*") 187 TED.print("*" + stack, "\n") 188 TED.print("*[@F red]" + self.message + "*:*") 189 TED.print("*" + self.error) 190 exit(3) 191 else: 192 output = [TED.parse("*Hinted Error:*")] 193 output.append(TED.parse("*" + stack)) 194 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 195 output.append(TED.parse("*" + self.error)) 196 return "\n".join(output)
This exception has a hint. In this case this means that the exception points to a part of a string and describes the problem.
Example: Hint Exception: Are you having a good day ↑ You must end sentances with punctuation
The combination of the string that is being hinted to and the hint. Will automatically adjust for terminal width.
At what index of the value that is hinted to should the hint be placed. This is where the arrow will appear.
173 def throw(self, exits: bool = True) -> str: 174 """Custom throw method that allows for more custom output. 175 176 Args: 177 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 178 179 Returns: 180 str: The output that would be to the screen if exits, else None 181 """ 182 stack = "\n".join(" " + st for st in self.stack) 183 if exits: 184 from sys import exit 185 186 TED.print("*Hinted Error:*") 187 TED.print("*" + stack, "\n") 188 TED.print("*[@F red]" + self.message + "*:*") 189 TED.print("*" + self.error) 190 exit(3) 191 else: 192 output = [TED.parse("*Hinted Error:*")] 193 output.append(TED.parse("*" + stack)) 194 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 195 output.append(TED.parse("*" + self.error)) 196 return "\n".join(output)
Custom throw method that allows for more custom output.
Args: exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True.
Returns: str: The output that would be to the screen if exits, else None
Inherited Members
199class MissingValueException(BaseException): 200 """This exception is to point out a missing value. 201 In the example below, ` `, indicates text that is injected as a missing value. 202 203 Example: 204 Missing punctuation: 205 Are you haveing a good day`?` 206 """ 207 208 def __init__( 209 self, 210 value: str, 211 index: int, 212 missing: str, 213 message: str = "Missing Value", 214 ): 215 super().__init__(message) 216 self.slice_stack() 217 218 self._value = value 219 self._index = index 220 self._missing = missing 221 222 @cached_property 223 def error(self) -> str: 224 """The value with the injected missing value.""" 225 return f" {self.value[:self._index]}[@F red]{self._missing}[@F]{self.value[self._index:]}" 226 227 @property 228 def value(self) -> str: 229 """The value that is missing something.""" 230 return self._value 231 232 @property 233 def missing(self) -> str: 234 """What is missing from the value.""" 235 return self._missing 236 237 @property 238 def index(self) -> int: 239 """The index where the value should be injected.""" 240 return self._index 241 242 def throw(self, exits: bool = True) -> str: 243 """Custom throw method that allows for more custom output. 244 245 Args: 246 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 247 248 Returns: 249 str: The output that would be to the screen if exits, else None 250 """ 251 stack = "\n".join(" " + st for st in self.stack) 252 if exits: 253 from sys import exit 254 255 TED.print("*Missing Value Error:*") 256 TED.print("*" + stack + "\n") 257 TED.print("*[@F red]" + self.message + "*:*") 258 TED.print("*" + self.error) 259 260 exit(4) 261 else: 262 output = [TED.parse("*Missing Value Error:*")] 263 output.append(TED.parse("*" + stack + "\n")) 264 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 265 output.append(TED.parse("*" + self.error)) 266 return "\n".join(output)
This exception is to point out a missing value.
In the example below, , indicates text that is injected as a missing value.
Example:
Missing punctuation:
Are you haveing a good day?
242 def throw(self, exits: bool = True) -> str: 243 """Custom throw method that allows for more custom output. 244 245 Args: 246 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 247 248 Returns: 249 str: The output that would be to the screen if exits, else None 250 """ 251 stack = "\n".join(" " + st for st in self.stack) 252 if exits: 253 from sys import exit 254 255 TED.print("*Missing Value Error:*") 256 TED.print("*" + stack + "\n") 257 TED.print("*[@F red]" + self.message + "*:*") 258 TED.print("*" + self.error) 259 260 exit(4) 261 else: 262 output = [TED.parse("*Missing Value Error:*")] 263 output.append(TED.parse("*" + stack + "\n")) 264 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 265 output.append(TED.parse("*" + self.error)) 266 return "\n".join(output)
Custom throw method that allows for more custom output.
Args: exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True.
Returns: str: The output that would be to the screen if exits, else None
Inherited Members
269class RangedException(BaseException): 270 """This exception has a hint that applies to a range of a value. In this case this means that the exception points to a part of a string and describes the problem. 271 272 Example: 273 Hint Exception: 274 Are you having a good day 275 --- 276 Day must be `week` 277 """ 278 279 def __init__( 280 self, 281 value: str, 282 hint: str, 283 start: int = 0, 284 end: int = -1, 285 message: str = "Error with specific value", 286 ): 287 super().__init__(message) 288 self.slice_stack() 289 290 self._value = value 291 self._hint = hint 292 self._start = start 293 self._end = end 294 295 @cached_property 296 def error(self) -> str: 297 """The value with the injected missing value.""" 298 return ( 299 f" {self.value[:self._start]}_[@F red]{self.value[self._start:self._end]}[@F]_{self._value[self._end:]}\n" 300 # + " " 301 # + " " * (self._end - (self._end - self._start) // 2) 302 # + self._hint 303 ) 304 305 @property 306 def value(self) -> str: 307 """The value that is missing something.""" 308 return self._value 309 310 @property 311 def hint(self) -> str: 312 """What is missing from the value.""" 313 return self._hint 314 315 @property 316 def start(self) -> int: 317 """The index where the hint starts.""" 318 return self._start 319 320 @property 321 def end(self) -> int: 322 """The index where the value hint ends.""" 323 return self._end 324 325 def throw(self, exits: bool = True) -> str: 326 """Custom throw method that allows for more custom output. 327 328 Args: 329 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 330 331 Returns: 332 str: The output that would be to the screen if exits, else None 333 """ 334 stack = "\n".join(" " + st for st in self.stack) 335 if exits: 336 from sys import exit 337 338 TED.print("*Ranged Error:*") 339 TED.print("*" + stack + "\n") 340 TED.print("*[@F red]" + self.message + "*:*") 341 TED.print("*" + self.error) 342 343 exit(4) 344 else: 345 output = [TED.parse("*Ranged Error:*")] 346 output.append(TED.parse("*" + stack + "\n")) 347 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 348 output.append(TED.parse("*" + self.error)) 349 return "\n".join(output)
This exception has a hint that applies to a range of a value. In this case this means that the exception points to a part of a string and describes the problem.
Example:
Hint Exception:
Are you having a good day
---
Day must be week
325 def throw(self, exits: bool = True) -> str: 326 """Custom throw method that allows for more custom output. 327 328 Args: 329 exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True. 330 331 Returns: 332 str: The output that would be to the screen if exits, else None 333 """ 334 stack = "\n".join(" " + st for st in self.stack) 335 if exits: 336 from sys import exit 337 338 TED.print("*Ranged Error:*") 339 TED.print("*" + stack + "\n") 340 TED.print("*[@F red]" + self.message + "*:*") 341 TED.print("*" + self.error) 342 343 exit(4) 344 else: 345 output = [TED.parse("*Ranged Error:*")] 346 output.append(TED.parse("*" + stack + "\n")) 347 output.append(TED.parse("*[@F red]" + self.message + "*:*")) 348 output.append(TED.parse("*" + self.error)) 349 return "\n".join(output)
Custom throw method that allows for more custom output.
Args: exits (bool, optional): Whether the throw method should exit like raise would. Defaults to True.
Returns: str: The output that would be to the screen if exits, else None