3636# SPDX-License-Identifier: Apache-2.0
3737# ============================================================================
3838#
39+ from enum import Enum , unique
40+
41+
3942__api__ = [
4043 'Terminal' ,
44+ 'Severity' ,
45+ 'Line' ,
46+ 'ILineTerminal' ,
47+ 'LineTerminal' ,
4148]
4249__all__ = __api__
4350
4451from platform import system as platform_system
4552
53+
4654class Terminal :
55+ try :
56+ from colorama import Fore as Foreground
57+ Foreground = {
58+ "RED" : Foreground .LIGHTRED_EX ,
59+ "DARK_RED" : Foreground .RED ,
60+ "GREEN" : Foreground .LIGHTGREEN_EX ,
61+ "DARK_GREEN" : Foreground .GREEN ,
62+ "YELLOW" : Foreground .LIGHTYELLOW_EX ,
63+ "DARK_YELLOW" : Foreground .YELLOW ,
64+ "MAGENTA" : Foreground .LIGHTMAGENTA_EX ,
65+ "BLUE" : Foreground .LIGHTBLUE_EX ,
66+ "CYAN" : Foreground .LIGHTCYAN_EX ,
67+ "DARK_CYAN" : Foreground .CYAN ,
68+ "GRAY" : Foreground .WHITE ,
69+ "DARK_GRAY" : Foreground .LIGHTBLACK_EX ,
70+ "WHITE" : Foreground .LIGHTWHITE_EX ,
71+ "NOCOLOR" : Foreground .RESET ,
72+
73+ "HEADLINE" : Foreground .LIGHTMAGENTA_EX ,
74+ "ERROR" : Foreground .LIGHTRED_EX ,
75+ "WARNING" : Foreground .LIGHTYELLOW_EX
76+ }
77+ except :
78+ Foreground = {
79+ "RED" : "" ,
80+ "DARK_RED" : "" ,
81+ "GREEN" : "" ,
82+ "DARK_GREEN" : "" ,
83+ "YELLOW" : "" ,
84+ "DARK_YELLOW" : "" ,
85+ "MAGENTA" : "" ,
86+ "BLUE" : "" ,
87+ "CYAN" : "" ,
88+ "DARK_CYAN" : "" ,
89+ "GRAY" : "" ,
90+ "DARK_GRAY" : "" ,
91+ "WHITE" : "" ,
92+ "NOCOLOR" : "" ,
93+
94+ "HEADLINE" : "" ,
95+ "ERROR" : "" ,
96+ "WARNING" : ""
97+ }
98+
99+ _width : int = None
100+ _height : int = None
101+
102+ def __init__ (self ):
103+ self .initColors ()
104+ (self ._width , self ._height ) = self .GetTerminalSize ()
105+
106+ @classmethod
107+ def initColors (cls ):
108+ try :
109+ from colorama import init
110+
111+ init ()#strip=False)
112+ except :
113+ pass
114+
115+ @classmethod
116+ def deinitColors (cls ):
117+ try :
118+ from colorama import deinit
119+
120+ deinit ()
121+ except :
122+ pass
123+
124+ @classmethod
125+ def exit (cls , returnCode = 0 ):
126+ cls .deinitColors ()
127+ exit (returnCode )
128+
129+ @property
130+ def Width (self ):
131+ return self ._width
132+
133+ @property
134+ def Height (self ):
135+ return self ._height
136+
47137 @staticmethod
48138 def GetTerminalSize ():
49139 """Returns the terminal size as tuple (width, height) for Windows, Mac OS (Darwin), Linux, cygwin (Windows), MinGW32/64 (Windows)."""
140+ size = None
141+
50142 platform = platform_system ()
51143 if (platform == "Windows" ):
52144 size = Terminal .__GetTerminalSizeOnWindows ()
@@ -55,6 +147,7 @@ def GetTerminalSize():
55147 platform .startswith ("MINGW32" ) or
56148 platform .startswith ("MINGW64" )):
57149 size = Terminal .__GetTerminalSizeOnLinux ()
150+
58151 if (size is None ):
59152 size = (80 , 25 ) # default size
60153 return size
@@ -122,5 +215,223 @@ def __GetTerminalSizeWithTPut():
122215 except :
123216 pass
124217
125- class LineTerminal (Terminal ):
126- pass
218+
219+ @unique
220+ class Severity (Enum ):
221+ """Logging message severity levels."""
222+ Fatal = 30
223+ Error = 25
224+ Quiet = 20
225+ Warning = 15
226+ Info = 10
227+ DryRun = 5
228+ Normal = 4
229+ Verbose = 2
230+ Debug = 1
231+ All = 0
232+
233+ def __hash__ (self ):
234+ return hash (self .name )
235+
236+ def __eq__ (self , other ): return self .value == other .value
237+ def __ne__ (self , other ): return self .value != other .value
238+ def __lt__ (self , other ): return self .value < other .value
239+ def __le__ (self , other ): return self .value <= other .value
240+ def __gt__ (self , other ): return self .value > other .value
241+ def __ge__ (self , other ): return self .value >= other .value
242+
243+
244+ class Line :
245+ """Represents a single line message with a severity and indentation level."""
246+ def __init__ (self , message , severity = Severity .Normal , indent = 0 , appendLinebreak = True ):
247+ self ._severity = severity
248+ self ._message = message
249+ self ._indent = indent
250+ self .AppendLinebreak = appendLinebreak
251+
252+ _LOG_MESSAGE_FORMAT__ = {
253+ Severity .Fatal : "FATAL: {message}" ,
254+ Severity .Error : "ERROR: {message}" ,
255+ Severity .Warning : "WARNING: {message}" ,
256+ Severity .Info : "INFO: {message}" ,
257+ Severity .Quiet : "{message}" ,
258+ Severity .Normal : "{message}" ,
259+ Severity .Verbose : "VERBOSE: {message}" ,
260+ Severity .Debug : "DEBUG: {message}" ,
261+ Severity .DryRun : "DRYRUN: {message}"
262+ }
263+
264+ @property
265+ def Severity (self ):
266+ """Return the line's severity level."""
267+ return self ._severity
268+
269+ @property
270+ def Indent (self ):
271+ """Return the line's indentation level."""
272+ return self ._indent
273+
274+ @property
275+ def Message (self ):
276+ """Return the indented line."""
277+ return (" " * self ._indent ) + self ._message
278+
279+ def IndentBy (self , indent ):
280+ """Increase a line's indentation level."""
281+ self ._indent += indent
282+
283+ def __str__ (self ):
284+ return self ._LOG_MESSAGE_FORMAT__ [self ._severity ].format (message = self ._message )
285+
286+
287+ class ILineTerminal :
288+ """A mixin class to provide local terminal writing methods."""
289+ _terminal = None
290+
291+ def __init__ (self , terminal = None ):
292+ """MixIn initializer."""
293+ self ._terminal = terminal
294+
295+ # FIXME: Alter methods if a terminal is present or set dummy methods
296+
297+ @property
298+ def Terminal (self ):
299+ """Return the local terminal instance."""
300+ return self ._terminal
301+
302+ def WriteLine (self , line : Line , condition = True ):
303+ """Write an entry to the local terminal."""
304+ if ((self ._terminal is not None ) and condition ):
305+ return self ._terminal .WriteLine (line )
306+ return False
307+
308+ # def _TryWriteLine(self, *args, condition=True, **kwargs):
309+ # if ((self._terminal is not None) and condition):
310+ # return self._terminal.TryWrite(*args, **kwargs)
311+ # return False
312+
313+ def WriteFatal (self , * args , condition = True , ** kwargs ):
314+ if ((self ._terminal is not None ) and condition ):
315+ return self ._terminal .WriteFatal (* args , ** kwargs )
316+ return False
317+
318+ def WriteError (self , * args , condition = True , ** kwargs ):
319+ if ((self ._terminal is not None ) and condition ):
320+ return self ._terminal .WriteError (* args , ** kwargs )
321+ return False
322+
323+ def WriteWarning (self , * args , condition = True , ** kwargs ):
324+ if ((self ._terminal is not None ) and condition ):
325+ return self ._terminal .WriteWarning (* args , ** kwargs )
326+ return False
327+
328+ def WriteInfo (self , * args , condition = True , ** kwargs ):
329+ if ((self ._terminal is not None ) and condition ):
330+ return self ._terminal .WriteInfo (* args , ** kwargs )
331+ return False
332+
333+ def WriteQuiet (self , * args , condition = True , ** kwargs ):
334+ if ((self ._terminal is not None ) and condition ):
335+ return self ._terminal .WriteQuiet (* args , ** kwargs )
336+ return False
337+
338+ def WriteNormal (self , * args , condition = True , ** kwargs ):
339+ if ((self ._terminal is not None ) and condition ):
340+ return self ._terminal .WriteNormal (* args , ** kwargs )
341+ return False
342+
343+ def WriteVerbose (self , * args , condition = True , ** kwargs ):
344+ if ((self ._terminal is not None ) and condition ):
345+ return self ._terminal .WriteVerbose (* args , ** kwargs )
346+ return False
347+
348+ def WriteDebug (self , * args , condition = True , ** kwargs ):
349+ if ((self ._terminal is not None ) and condition ):
350+ return self ._terminal .WriteDebug (* args , ** kwargs )
351+ return False
352+
353+ def WriteDryRun (self , * args , condition = True , ** kwargs ):
354+ if ((self ._terminal is not None ) and condition ):
355+ return self ._terminal .WriteDryRun (* args , ** kwargs )
356+ return False
357+
358+
359+ class LineTerminal (Terminal , ILineTerminal ):
360+ def __init__ (self , writeLevel , writeToStdOut = True ):
361+ """Initializer of a line based terminal interface."""
362+
363+ Terminal .__init__ (self )
364+ ILineTerminal .__init__ (self , self )
365+
366+ self ._WriteLevel = writeLevel
367+ self ._writeToStdOut = writeToStdOut
368+ self ._lines = []
369+ self ._baseIndent = 0
370+
371+
372+ @property
373+ def LogLevel (self ):
374+ """Return the current minimal severity level for writing."""
375+ return self ._WriteLevel
376+ @LogLevel .setter
377+ def LogLevel (self , value ):
378+ """Set the minimal severity level for writing."""
379+ self ._WriteLevel = value
380+
381+ @property
382+ def BaseIndent (self ):
383+ return self ._baseIndent
384+ @BaseIndent .setter
385+ def BaseIndent (self , value ):
386+ self ._baseIndent = value
387+
388+ _LOG_MESSAGE_FORMAT__ = {
389+ Severity .Fatal : "{DARK_RED}{message}{NOCOLOR}" ,
390+ Severity .Error : "{RED}{message}{NOCOLOR}" ,
391+ Severity .Quiet : "{WHITE}{message}{NOCOLOR}" ,
392+ Severity .Warning : "{YELLOW}{message}{NOCOLOR}" ,
393+ Severity .Info : "{WHITE}{message}{NOCOLOR}" ,
394+ Severity .DryRun : "{DARK_CYAN}{message}{NOCOLOR}" ,
395+ Severity .Normal : "{WHITE}{message}{NOCOLOR}" ,
396+ Severity .Verbose : "{GRAY}{message}{NOCOLOR}" ,
397+ Severity .Debug : "{DARK_GRAY}{message}{NOCOLOR}"
398+ }
399+
400+ def WriteLine (self , line : Line ):
401+ if (line .Severity >= self ._WriteLevel ):
402+ self ._lines .append (line )
403+ if self ._writeToStdOut :
404+ print (self ._LOG_MESSAGE_FORMAT__ [line .Severity ].format (message = line .Message , ** self .Foreground ), end = "\n " if line .AppendLinebreak else "" )
405+ return True
406+ else :
407+ return False
408+
409+ def TryWriteLine (self , line ):
410+ return (line .Severity >= self ._WriteLevel )
411+
412+ def WriteFatal (self , message , indent = 0 , appendLinebreak = True ):
413+ return self .WriteLine (Line (message , Severity .Fatal , self ._baseIndent + indent , appendLinebreak ))
414+
415+ def WriteError (self , message , indent = 0 , appendLinebreak = True ):
416+ return self .WriteLine (Line (message , Severity .Error , self ._baseIndent + indent , appendLinebreak ))
417+
418+ def WriteWarning (self , message , indent = 0 , appendLinebreak = True ):
419+ return self .WriteLine (Line (message , Severity .Warning , self ._baseIndent + indent , appendLinebreak ))
420+
421+ def WriteInfo (self , message , indent = 0 , appendLinebreak = True ):
422+ return self .WriteLine (Line (message , Severity .Info , self ._baseIndent + indent , appendLinebreak ))
423+
424+ def WriteQuiet (self , message , indent = 0 , appendLinebreak = True ):
425+ return self .WriteLine (Line (message , Severity .Quiet , self ._baseIndent + indent , appendLinebreak ))
426+
427+ def WriteNormal (self , message , indent = 0 , appendLinebreak = True ):
428+ return self .WriteLine (Line (message , Severity .Normal , self ._baseIndent + indent , appendLinebreak ))
429+
430+ def WriteVerbose (self , message , indent = 1 , appendLinebreak = True ):
431+ return self .WriteLine (Line (message , Severity .Verbose , self ._baseIndent + indent , appendLinebreak ))
432+
433+ def WriteDebug (self , message , indent = 2 , appendLinebreak = True ):
434+ return self .WriteLine (Line (message , Severity .Debug , self ._baseIndent + indent , appendLinebreak ))
435+
436+ def WriteDryRun (self , message , indent = 2 , appendLinebreak = True ):
437+ return self .WriteLine (Line (message , Severity .DryRun , self ._baseIndent + indent , appendLinebreak ))
0 commit comments