expresso-framework 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
expresso/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ from .expresso import Expresso
2
+ from .exposure import Exposure
3
+
4
+ from .enums import (
5
+ ExposureCategory,
6
+ ExposureLevel,
7
+ ExposureTimeSource,
8
+ )
9
+
10
+ __all__ = [
11
+ "Expresso",
12
+ "Exposure",
13
+ "ExposureCategory",
14
+ "ExposureLevel",
15
+ "ExposureTimeSource",
16
+ ]
@@ -0,0 +1,9 @@
1
+ from .exposure_category import ExposureCategory
2
+ from .exposure_level import ExposureLevel
3
+ from .exposure_time_source import ExposureTimeSource
4
+
5
+ __all__ = [
6
+ "ExposureCategory",
7
+ "ExposureLevel",
8
+ "ExposureTimeSource",
9
+ ]
@@ -0,0 +1,24 @@
1
+ class ExposureCategory:
2
+ def __init__(self, name): self.name = name
3
+
4
+ @staticmethod
5
+ def of(customName):
6
+ normalized = customName.strip().upper()
7
+ return ExposureCategory(normalized)
8
+
9
+ def __eq__(self, other):
10
+ if isinstance(other, ExposureCategory):
11
+ return self.name == other.name
12
+ return False
13
+
14
+ def __hash__(self): return hash(self.name)
15
+
16
+ def __str__(self): return self.name
17
+
18
+ ExposureCategory.VANILLA = ExposureCategory("VANILLA")
19
+ ExposureCategory.SYSTEMLOG = ExposureCategory("SYSTEMLOG")
20
+ ExposureCategory.DEBUG = ExposureCategory("DEBUG")
21
+ ExposureCategory.COMPONENTIAL = ExposureCategory("COMPONENTIAL")
22
+ ExposureCategory.LOWERLEVEL = ExposureCategory("LOWERLEVEL")
23
+ ExposureCategory.BITWISE = ExposureCategory("BITWISE")
24
+ ExposureCategory.TEST = ExposureCategory("TEST")
@@ -0,0 +1,10 @@
1
+ from enum import Enum
2
+
3
+ class ExposureLevel(Enum):
4
+ LEVEL1 = 1
5
+ LEVEL2 = 2
6
+ LEVEL3 = 3
7
+ LEVEL4 = 4
8
+ LEVEL5 = 5
9
+
10
+ def getLevel(self) -> int: return self.value
@@ -0,0 +1,5 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ class ExposureTimeSource(ABC):
4
+ @abstractmethod
5
+ def getTime(self) -> str: pass
expresso/exposure.py ADDED
@@ -0,0 +1,69 @@
1
+ from .expresso import Expresso
2
+ from .enums.exposure_level import ExposureLevel
3
+
4
+ class Exposure:
5
+
6
+ def __init__(self, identity = None): self.identity = identity
7
+
8
+ def enabled(self, category, level): return Expresso.isExposed(category, level)
9
+
10
+ def expose(self, category, level, formatString, *args):
11
+ if callable(formatString):
12
+ if not Expresso.isExposed(category, level):
13
+ return
14
+ Expresso.printf(self.identity, category, level, "%s", formatString())
15
+ else:
16
+ Expresso.printf(self.identity, category, level, formatString, *args)
17
+
18
+ def l1(self, category, formatString, *args):
19
+ if callable(formatString):
20
+ if not Expresso.isExposed(category, ExposureLevel.LEVEL1): return
21
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL1, "%s", formatString())
22
+ else:
23
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL1, formatString, *args)
24
+
25
+ def l2(self, category, formatString, *args):
26
+ if callable(formatString):
27
+ if not Expresso.isExposed(category, ExposureLevel.LEVEL2): return
28
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL2, "%s", formatString())
29
+ else:
30
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL2, formatString, *args)
31
+
32
+ def l3(self, category, formatString, *args):
33
+ if callable(formatString):
34
+ if not Expresso.isExposed(category, ExposureLevel.LEVEL3): return
35
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL3, "%s", formatString())
36
+ else:
37
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL3, formatString, *args)
38
+
39
+ def l4(self, category, formatString, *args):
40
+ if callable(formatString):
41
+ if not Expresso.isExposed(category, ExposureLevel.LEVEL4): return
42
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL4, "%s", formatString())
43
+ else:
44
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL4, formatString, *args)
45
+
46
+ def l5(self, category, formatString, *args):
47
+ if callable(formatString):
48
+ if not Expresso.isExposed(category, ExposureLevel.LEVEL5): return
49
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL5, "%s", formatString())
50
+ else:
51
+ Expresso.printf(self.identity, category, ExposureLevel.LEVEL5, formatString, *args)
52
+
53
+ def nl(self, category, requiredLevel):
54
+ Expresso.NEWLINE(category, requiredLevel)
55
+
56
+ def lbr(self, category, requiredLevel):
57
+ Expresso.LINEBREAK(category, requiredLevel)
58
+
59
+ def err(self, formatString, *args):
60
+ if self.identity is not None and str(self.identity).strip():
61
+ Expresso.error("[" + str(self.identity) + "] " + formatString, *args)
62
+ else:
63
+ Expresso.error(formatString, *args)
64
+
65
+ def here(self, label=None, customColor=None, customHeader=None):
66
+ Expresso.hereAnnounce(identity=self.identity, customColor=customColor, customHeader=customHeader, label=label)
67
+
68
+ def getIdentity(self):
69
+ return self.identity
expresso/expresso.py ADDED
@@ -0,0 +1,393 @@
1
+ import sys
2
+ import time
3
+ import traceback
4
+ from datetime import datetime
5
+ from enum import Enum
6
+ from threading import Lock
7
+
8
+ from .enums.exposure_category import ExposureCategory
9
+ from .enums.exposure_level import ExposureLevel
10
+ from .enums.exposure_time_source import ExposureTimeSource
11
+ from . import variable_constants
12
+
13
+
14
+ class Expresso:
15
+
16
+ globalEventCounter = 0
17
+ counterLock = Lock()
18
+
19
+ minIndex = 0
20
+ maxIndex = float("inf")
21
+ indexLock = Lock()
22
+
23
+ showIndexTag = True
24
+ showIdentityTag = True
25
+ showCategoryTag = True
26
+ showLevelTag = True
27
+
28
+ timeSource = None
29
+ showApplicationTimeTag = False
30
+ showTimestampTag = False
31
+ showElapsedTimeTag = False
32
+
33
+ DEFAULT_ERROR_PREFIX = "[ERROR]: "
34
+ DEFAULT_ERROR_COLOR = variable_constants.BOLD + variable_constants.FG_RED
35
+
36
+ DEFAULT_HERE_HEADER = "--> [EXPRESSO REPORT HERE]"
37
+ DEFAULT_HERE_COLOR = variable_constants.BOLD + variable_constants.FG_MAGENTA
38
+
39
+ errorPrefix = DEFAULT_ERROR_PREFIX
40
+ errorColor = DEFAULT_ERROR_COLOR
41
+
42
+ hereHeader = DEFAULT_HERE_HEADER
43
+ hereColor = DEFAULT_HERE_COLOR
44
+
45
+ showDiagnosticTags = False
46
+
47
+ enabledCategories = set()
48
+ categoriesLock = Lock()
49
+
50
+ enabledLevels = { ExposureLevel.LEVEL1 }
51
+ levelsLock = Lock()
52
+
53
+ externalBridge = None
54
+
55
+ runtimeStart = time.perf_counter()
56
+
57
+ RESET = variable_constants.RESET
58
+ BOLD = variable_constants.BOLD
59
+ DIM = variable_constants.DIM
60
+ UNDERLINE = variable_constants.UNDERLINE
61
+ LINEFEED = variable_constants.LINEFEED
62
+
63
+ FG_BLACK = variable_constants.FG_BLACK
64
+ FG_RED = variable_constants.FG_RED
65
+ FG_GREEN = variable_constants.FG_GREEN
66
+ FG_YELLOW = variable_constants.FG_YELLOW
67
+ FG_BLUE = variable_constants.FG_BLUE
68
+ FG_MAGENTA = variable_constants.FG_MAGENTA
69
+ FG_CYAN = variable_constants.FG_CYAN
70
+ FG_WHITE = variable_constants.FG_WHITE
71
+
72
+ BG_BLACK = variable_constants.BG_BLACK
73
+ BG_RED = variable_constants.BG_RED
74
+ BG_GREEN = variable_constants.BG_GREEN
75
+ BG_YELLOW = variable_constants.BG_YELLOW
76
+ BG_BLUE = variable_constants.BG_BLUE
77
+ BG_MAGENTA = variable_constants.BG_MAGENTA
78
+ BG_CYAN = variable_constants.BG_CYAN
79
+ BG_WHITE = variable_constants.BG_WHITE
80
+
81
+ @staticmethod
82
+ def FG_COLOR(id): return variable_constants.FG_COLOR(id)
83
+
84
+ @staticmethod
85
+ def BG_COLOR(id): return variable_constants.BG_COLOR(id)
86
+
87
+ @staticmethod
88
+ def exposure(identity = None):
89
+ from .exposure import Exposure
90
+ return Exposure(identity)
91
+
92
+ @staticmethod
93
+ def enableIndexTag(enable): Expresso.showIndexTag = enable
94
+
95
+ @staticmethod
96
+ def enableIdentityTag(enable): Expresso.showIdentityTag = enable
97
+
98
+ @staticmethod
99
+ def enableCategoryTag(enable): Expresso.showCategoryTag = enable
100
+
101
+ @staticmethod
102
+ def enableLevelTag(enable): Expresso.showLevelTag = enable
103
+
104
+ @staticmethod
105
+ def enableTimestamp(enable): Expresso.showTimestampTag = enable
106
+
107
+ @staticmethod
108
+ def enableElapsedTime(enable): Expresso.showElapsedTimeTag = enable
109
+
110
+ @staticmethod
111
+ def setIndexRange(start, end = None):
112
+ if end is None:
113
+ nextMin = 0
114
+ nextMax = start
115
+ else:
116
+ nextMin = max(0, start)
117
+ nextMax = max(start, end)
118
+
119
+ with Expresso.indexLock:
120
+ Expresso.minIndex = nextMin
121
+ Expresso.maxIndex = nextMax
122
+
123
+ @staticmethod
124
+ def setSingleIndex(targetIndex): Expresso.setIndexRange(targetIndex, targetIndex)
125
+
126
+ @staticmethod
127
+ def setErrorStyle(color = None, prefix = None):
128
+ if color is not None: Expresso.errorColor = color
129
+ if prefix is not None: Expresso.errorPrefix = prefix
130
+
131
+ @staticmethod
132
+ def setHereStyle(color = None, header = None):
133
+ if color is not None: Expresso.hereColor = color
134
+ if header is not None: Expresso.hereHeader = header
135
+
136
+ @staticmethod
137
+ def resetErrorStyle():
138
+ Expresso.errorColor = Expresso.DEFAULT_ERROR_COLOR
139
+ Expresso.errorPrefix = Expresso.DEFAULT_ERROR_PREFIX
140
+
141
+ @staticmethod
142
+ def resetHereStyle():
143
+ Expresso.hereColor = Expresso.DEFAULT_HERE_COLOR
144
+ Expresso.hereHeader = Expresso.DEFAULT_HERE_HEADER
145
+
146
+ @staticmethod
147
+ def enableDiagnosticTags(enable): Expresso.showDiagnosticTags = enable
148
+
149
+ @staticmethod
150
+ def clearIndexFilter():
151
+ with Expresso.indexLock:
152
+ Expresso.minIndex = 0
153
+ Expresso.maxIndex = float("inf")
154
+
155
+ @staticmethod
156
+ def resetEventCounter():
157
+ with Expresso.counterLock: Expresso.globalEventCounter = 0
158
+
159
+ @staticmethod
160
+ def clearAllCategories():
161
+ with Expresso.categoriesLock: Expresso.enabledCategories = set()
162
+
163
+ @staticmethod
164
+ def enableCategory(category):
165
+ if category is not None:
166
+ with Expresso.categoriesLock: Expresso.enabledCategories.add(category.name)
167
+
168
+ @staticmethod
169
+ def disableCategory(category):
170
+ if category is not None:
171
+ with Expresso.categoriesLock: Expresso.enabledCategories.discard(category.name)
172
+
173
+ @staticmethod
174
+ def NEWLINE(category, requiredLevel):
175
+ if Expresso.isExposed(category, requiredLevel): Expresso.emit("\n\n")
176
+
177
+ @staticmethod
178
+ def LINEBREAK(category, requiredLevel):
179
+ if Expresso.isExposed(category, requiredLevel): Expresso.emit("\n--------------------------------\n")
180
+
181
+ @staticmethod
182
+ def setLevel(newLevel):
183
+ with Expresso.levelsLock:
184
+ Expresso.enabledLevels.clear()
185
+ if newLevel is not None: Expresso.enabledLevels.add(newLevel)
186
+
187
+ @staticmethod
188
+ def setLevels(*handPickedLevels):
189
+ with Expresso.levelsLock:
190
+ Expresso.enabledLevels.clear()
191
+ if handPickedLevels is not None:
192
+ for level in handPickedLevels:
193
+ if level is not None:
194
+ Expresso.enabledLevels.add(level)
195
+
196
+ @staticmethod
197
+ def setCategory(category):
198
+ nextSet = set()
199
+ if category is not None: nextSet.add(category.name)
200
+ with Expresso.categoriesLock: Expresso.enabledCategories = nextSet
201
+
202
+ @staticmethod
203
+ def setCategories(*handPickedCategories):
204
+ nextSet = set()
205
+ if handPickedCategories is not None:
206
+ for category in handPickedCategories:
207
+ if category is not None: nextSet.add(category.name)
208
+ with Expresso.categoriesLock: Expresso.enabledCategories = nextSet
209
+
210
+ @staticmethod
211
+ def getCurrentEventCount():
212
+ with Expresso.counterLock: return Expresso.globalEventCounter
213
+
214
+ @staticmethod
215
+ def getCategories():
216
+ with Expresso.categoriesLock: return frozenset(Expresso.enabledCategories)
217
+
218
+ @staticmethod
219
+ def getLevels():
220
+ with Expresso.levelsLock: return frozenset(Expresso.enabledLevels)
221
+
222
+ @staticmethod
223
+ def setTimeSource(source): Expresso.timeSource = source
224
+
225
+ @staticmethod
226
+ def enableApplicationTime(enable): Expresso.showApplicationTimeTag = enable
227
+
228
+ @staticmethod
229
+ def setBridge(outsideSystem): Expresso.externalBridge = outsideSystem
230
+
231
+ @staticmethod
232
+ def reset():
233
+ Expresso.clearAllCategories()
234
+ Expresso.setLevel(ExposureLevel.LEVEL1)
235
+ Expresso.clearIndexFilter()
236
+ Expresso.resetEventCounter()
237
+ Expresso.enableTimestamp(False)
238
+ Expresso.enableElapsedTime(False)
239
+ Expresso.enableApplicationTime(False)
240
+ Expresso.setBridge(None)
241
+
242
+ @staticmethod
243
+ def isExposed(category, requiredLevel):
244
+ if category is None or requiredLevel is None: return False
245
+ with Expresso.categoriesLock:
246
+ activeCategories = Expresso.enabledCategories
247
+ if activeCategories and category.name not in activeCategories:
248
+ return False
249
+
250
+ with Expresso.levelsLock:
251
+ activeLevels = Expresso.enabledLevels
252
+
253
+ if not activeLevels: return False
254
+
255
+ if len(activeLevels) == 1:
256
+ activeLevel = next(iter(activeLevels))
257
+ if activeLevel.getLevel() < requiredLevel.getLevel(): return False
258
+ else:
259
+ if requiredLevel not in activeLevels: return False
260
+
261
+ return True
262
+
263
+ @staticmethod
264
+ def printf(*args):
265
+ if len(args) < 3:
266
+ raise TypeError("printf requires at least category, requiredLevel and format")
267
+
268
+ if isinstance(args[0], ExposureCategory):
269
+ identity = ""
270
+ category = args[0]
271
+ requiredLevel = args[1]
272
+ formatString = args[2]
273
+ formatArgs = args[3:]
274
+ else:
275
+ identity = args[0]
276
+ category = args[1]
277
+ requiredLevel = args[2]
278
+ formatString = args[3]
279
+ formatArgs = args[4:]
280
+
281
+ if not Expresso.isExposed(category, requiredLevel): return
282
+
283
+ with Expresso.counterLock:
284
+ Expresso.globalEventCounter += 1
285
+ currentIndex = Expresso.globalEventCounter
286
+
287
+ with Expresso.indexLock:
288
+ minIndex = Expresso.minIndex
289
+ maxIndex = Expresso.maxIndex
290
+
291
+ if currentIndex < minIndex or currentIndex > maxIndex: return
292
+
293
+ output = ""
294
+
295
+ if Expresso.showApplicationTimeTag:
296
+ source = Expresso.timeSource
297
+ if source is not None:
298
+ applicationTime = source.getTime()
299
+ if applicationTime is not None and str(applicationTime).strip():
300
+ output += Expresso.DIM + "[" + str(applicationTime) + "] " + Expresso.RESET
301
+
302
+ if Expresso.showTimestampTag:
303
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
304
+ output += Expresso.DIM + "[" + timestamp + "] " + Expresso.RESET
305
+
306
+ if Expresso.showElapsedTimeTag:
307
+ elapsedMillis = (time.perf_counter() - Expresso.runtimeStart) * 1000.0
308
+ output += Expresso.DIM + f"[+{elapsedMillis:.3f}ms] " + Expresso.RESET
309
+
310
+ if Expresso.showIndexTag:
311
+ output += Expresso.DIM + f"[#{currentIndex}] " + Expresso.RESET
312
+
313
+ if Expresso.showIdentityTag and identity is not None and str(identity).strip():
314
+ output += Expresso.DIM + f"[{identity}] " + Expresso.RESET
315
+
316
+ if Expresso.showCategoryTag:
317
+ output += Expresso.DIM + f"[{category.name}] " + Expresso.RESET
318
+
319
+ if Expresso.showLevelTag:
320
+ output += Expresso.DIM + f"[{requiredLevel.name}] " + Expresso.RESET
321
+
322
+ if formatArgs:
323
+ try: output += formatString % formatArgs
324
+ except TypeError: output += formatString.format(*formatArgs)
325
+ else: output += formatString
326
+
327
+ Expresso.emit(output)
328
+
329
+ @staticmethod
330
+ def emit(text):
331
+ bridge = Expresso.externalBridge
332
+ if bridge is not None:
333
+ bridge(text)
334
+ else:
335
+ print(text, end="")
336
+
337
+ @staticmethod
338
+ def appendDiagnosticTags():
339
+ output = ""
340
+ if not Expresso.showDiagnosticTags: return output
341
+
342
+ if Expresso.showApplicationTimeTag and Expresso.timeSource is not None:
343
+ appTime = Expresso.timeSource.getTime()
344
+ if appTime is not None and str(appTime).strip():
345
+ output += Expresso.DIM + f"[{appTime}] " + Expresso.RESET
346
+
347
+ if Expresso.showTimestampTag:
348
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
349
+ output += Expresso.DIM + f"[{timestamp}] " + Expresso.RESET
350
+
351
+ if Expresso.showElapsedTimeTag:
352
+ elapsedMillis = (time.perf_counter() - Expresso.runtimeStart) * 1000.0
353
+ output += Expresso.DIM + f"[+{elapsedMillis:.3f}ms] " + Expresso.RESET
354
+
355
+ return output
356
+
357
+ @staticmethod
358
+ def error(formatString, *args):
359
+ exception = None
360
+ formattingArgs = list(args)
361
+
362
+ if formattingArgs and isinstance(formattingArgs[-1], BaseException):
363
+ exception = formattingArgs.pop()
364
+
365
+ output = Expresso.appendDiagnosticTags()
366
+ output += Expresso.errorColor + Expresso.errorPrefix + Expresso.RESET
367
+
368
+ if formattingArgs:
369
+ try: output += formatString % tuple(formattingArgs)
370
+ except TypeError: output += formatString.format(*formattingArgs)
371
+ else:
372
+ output += formatString
373
+
374
+ if exception is not None:
375
+ output += "\n"
376
+ output += "".join(traceback.format_exception(type(exception), exception, exception.__traceback__))
377
+
378
+ Expresso.emit(output)
379
+
380
+ @staticmethod
381
+ def hereAnnounce(identity=None, customColor = None, customHeader = None, label = None):
382
+ output = Expresso.appendDiagnosticTags()
383
+ color = customColor if customColor is not None else Expresso.hereColor
384
+ header = customHeader if customHeader is not None else Expresso.hereHeader
385
+
386
+ if identity is not None and str(identity).strip(): output += "[" + str(identity) + "] "
387
+
388
+ output += color + header + Expresso.RESET
389
+
390
+ if label is not None and str(label).strip(): output += ": " + str(label)
391
+ output += "\n"
392
+
393
+ Expresso.emit(output)
@@ -0,0 +1,71 @@
1
+
2
+ RESET = "\033[0m"
3
+ BOLD = "\033[1m"
4
+ DIM = "\033[2m"
5
+ UNDERLINE = "\033[4m"
6
+ BLINK = "\033[5m"
7
+ INVERT = "\033[7m"
8
+ STRIKETHROUGH = "\033[9m"
9
+
10
+ FG_BLACK = "\033[30m"
11
+ FG_RED = "\033[31m"
12
+ FG_GREEN = "\033[32m"
13
+ FG_YELLOW = "\033[33m"
14
+ FG_BLUE = "\033[34m"
15
+ FG_MAGENTA = "\033[35m"
16
+ FG_CYAN = "\033[36m"
17
+ FG_WHITE = "\033[37m"
18
+
19
+ BG_BLACK = "\033[40m"
20
+ BG_RED = "\033[41m"
21
+ BG_GREEN = "\033[42m"
22
+ BG_YELLOW = "\033[43m"
23
+ BG_BLUE = "\033[44m"
24
+ BG_MAGENTA = "\033[45m"
25
+ BG_CYAN = "\033[46m"
26
+ BG_WHITE = "\033[47m"
27
+
28
+ FG_COLOR = lambda id: f"\033[38;5;{id}m"
29
+ BG_COLOR = lambda id: f"\033[48;5;{id}m"
30
+
31
+ CLEAR_LINE = "\033[2K\r"
32
+
33
+ ENDOFSTRING = 0
34
+ BELL = 7
35
+ BACKSPACE = 8
36
+ HORIZONTALTAB = 9
37
+ LINEFEED = 10
38
+ CARRIAGERETURN = 13
39
+ ESCAPE = 27
40
+
41
+ STARTOFHEADING = 1
42
+ STARTOFTEXT = 2
43
+ ENDOFTEXT = 3
44
+ ENDOFTRANSMISSION = 4
45
+ ENQUIRY = 5
46
+ ACKNOWLEDGE = 6
47
+ NEGATIVEACKNOWLEDGE = 21
48
+ SYNCHRONOUSIDLE = 22
49
+ ENDOFTRANSMITBLOCK = 23
50
+
51
+ VERTICALTAB = 11
52
+ FORMFEED = 12
53
+ SHIFTIN = 15
54
+ DATALINKESCAPE = 16
55
+ DEVICECONTROL1 = 17
56
+ DEVICECONTROL2 = 18
57
+ DEVICECONTROL3 = 19
58
+ DEVICECONTROL4 = 20
59
+ CANCEL = 24
60
+ ENDOFMEDIUM = 25
61
+ SUBSTITUTE = 26
62
+
63
+ FILESEPARATOR = 28
64
+ GROUPSEPARATOR = 29
65
+ RECORDSEPARATOR = 30
66
+ UNITSEPARATOR = 31
67
+ DELETECHAR = 127
68
+
69
+ SPINNER = "|/-\\"
70
+ NEWLINE = lambda: print(RESET + "\n\n")
71
+ LINEBREAK = lambda: print(RESET + "\n--------------------------------\n")