Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.

Commit 8348bf2

Browse files
committed
Implemented a LineTerminal.
1 parent ee4349c commit 8348bf2

File tree

2 files changed

+313
-2
lines changed

2 files changed

+313
-2
lines changed

pyTerminalUI/__init__.py

Lines changed: 313 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,109 @@
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

4451
from platform import system as platform_system
4552

53+
4654
class 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))

test/Frontend.py

Whitespace-only changes.

0 commit comments

Comments
 (0)