Coverage for pyTooling/TerminalUI/__init__.py: 52%
353 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-05 22:21 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-05 22:21 +0000
1# ==================================================================================================================== #
2# _____ _ _ _____ _ _ _ _ ___ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _|_ _|__ _ __ _ __ ___ (_)_ __ __ _| | | | |_ _| #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` | | |/ _ \ '__| '_ ` _ \| | '_ \ / _` | | | | || | #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| |_| | __/ | | | | | | | | | | | (_| | | |_| || | #
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)_|\___|_| |_| |_| |_|_|_| |_|\__,_|_|\___/|___| #
7# |_| |___/ |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2017-2022 Patrick Lehmann - Bötzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""A set of helpers to implement a text user interface (TUI) in a terminal."""
32__author__ = "Patrick Lehmann"
33__email__ = "Paebbels@gmail.com"
34__copyright__ = "2007-2022, Patrick Lehmann"
35__license__ = "Apache License, Version 2.0"
36__version__ = "1.5.9"
37__keywords__ = ["terminal", "shell", "text user interface", "TUI", "console", "message logging"]
39from enum import Enum, unique
40from platform import system as platform_system
41from typing import NoReturn, Tuple, Any
43from pyTooling.Decorators import export
44from pyTooling.MetaClasses import ExtendedType
47@export
48class Terminal:
49 FATAL_EXIT_CODE = 255
51 try:
52 from colorama import Fore as Foreground
53 Foreground = {
54 "RED": Foreground.LIGHTRED_EX,
55 "DARK_RED": Foreground.RED,
56 "GREEN": Foreground.LIGHTGREEN_EX,
57 "DARK_GREEN": Foreground.GREEN,
58 "YELLOW": Foreground.LIGHTYELLOW_EX,
59 "DARK_YELLOW": Foreground.YELLOW,
60 "MAGENTA": Foreground.LIGHTMAGENTA_EX,
61 "BLUE": Foreground.LIGHTBLUE_EX,
62 "DARK_BLUE": Foreground.BLUE,
63 "CYAN": Foreground.LIGHTCYAN_EX,
64 "DARK_CYAN": Foreground.CYAN,
65 "GRAY": Foreground.WHITE,
66 "DARK_GRAY": Foreground.LIGHTBLACK_EX,
67 "WHITE": Foreground.LIGHTWHITE_EX,
68 "NOCOLOR": Foreground.RESET,
70 "HEADLINE": Foreground.LIGHTMAGENTA_EX,
71 "ERROR": Foreground.LIGHTRED_EX,
72 "WARNING": Foreground.LIGHTYELLOW_EX
73 } #: Terminal colors
74 except:
75 Foreground = {
76 "RED": "",
77 "DARK_RED": "",
78 "GREEN": "",
79 "DARK_GREEN": "",
80 "YELLOW": "",
81 "DARK_YELLOW": "",
82 "MAGENTA": "",
83 "BLUE": "",
84 "DARK_BLUE": "",
85 "CYAN": "",
86 "DARK_CYAN": "",
87 "GRAY": "",
88 "DARK_GRAY": "",
89 "WHITE": "",
90 "NOCOLOR": "",
92 "HEADLINE": "",
93 "ERROR": "",
94 "WARNING": ""
95 } #: Terminal colors
97 _width : int = None #: Terminal width in characters
98 _height : int = None #: Terminal height in characters
100 def __init__(self):
101 """
102 Initialize a terminal.
104 If the Python package `colorama <https://pypi.org/project/colorama/>`_ [#f_colorama]_ is available, then initialize it for colored outputs.
106 .. [#f_colorama] Colorama on Github: https://GitHub.com/tartley/colorama
107 """
109 self.initColors()
110 (self._width, self._height) = self.GetTerminalSize()
112 @classmethod
113 def initColors(cls) -> None:
114 """Initialize the terminal for color support by colorama."""
115 try:
116 from colorama import init
118 init()#strip=False)
119 except:
120 pass
122 @classmethod
123 def deinitColors(cls) -> None:
124 """Uninitialize the terminal for color support by colorama."""
125 try:
126 from colorama import deinit
128 deinit()
129 except:
130 pass
132 @classmethod
133 def fatalExit(cls, returnCode:int =0) -> NoReturn:
134 """Exit the terminal application by uninitializing color support and returning a fatal exit code."""
135 cls.exit(cls.FATAL_EXIT_CODE if returnCode == 0 else returnCode)
137 @classmethod
138 def exit(cls, returnCode:int =0) -> NoReturn:
139 """Exit the terminal application by uninitializing color support and returning an exit code."""
140 cls.deinitColors()
141 exit(returnCode)
143 @classmethod
144 def versionCheck(cls, version) -> None:
145 """Check if the used Python interpreter fulfills the minimum version requirements."""
147 from sys import version_info
149 if (version_info < version): 149 ↛ 150line 149 didn't jump to line 150, because the condition on line 149 was never true
150 cls.initColors()
152 print("{RED}ERROR:{NOCOLOR} Used Python interpreter ({major}.{minor}.{micro}-{level}) is to old.".format(
153 major=version_info.major,
154 minor=version_info.minor,
155 micro=version_info.micro,
156 level=version_info.releaselevel,
157 **cls.Foreground
158 ))
159 print(f" Minimal required Python version is {version[0]}.{version[1]}.{version[2]}")
161 cls.exit(1)
163 @classmethod
164 def printException(cls, ex) -> NoReturn:
165 """Prints an exception of type :exc:`Exception`."""
166 from traceback import print_tb, walk_tb
168 cls.initColors()
170 print("{RED}FATAL: An unknown or unhandled exception reached the topmost exception handler!{NOCOLOR}".format(**cls.Foreground))
171 print(" {YELLOW}Exception type:{NOCOLOR} {typename}".format(typename=ex.__class__.__name__, **cls.Foreground))
172 print(" {YELLOW}Exception message:{NOCOLOR} {message!s}".format(message=ex, **cls.Foreground))
173 frame, sourceLine = [x for x in walk_tb(ex.__traceback__)][-1]
174 filename = frame.f_code.co_filename
175 funcName = frame.f_code.co_name
176 print(" {YELLOW}Caused in:{NOCOLOR} {function} in file '{filename}' at line {line}".format(
177 function=funcName,
178 filename=filename,
179 line=sourceLine,
180 **cls.Foreground
181 ))
182 if (ex.__cause__ is not None):
183 print(" {DARK_YELLOW}Caused by type:{NOCOLOR} {typename}".format(typename=ex.__cause__.__class__.__name__, **cls.Foreground))
184 print(" {DARK_YELLOW}Caused by message:{NOCOLOR} {message!s}".format(message=ex.__cause__, **cls.Foreground))
185 print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground))
186 print_tb(ex.__traceback__)
187 print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground))
188 print(("{RED}Please report this bug at GitHub: https://GitHub.com/pyTooling/pyTooling.TerminalUI/issues{NOCOLOR}").format(**cls.Foreground))
189 print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground))
191 cls.exit(1)
193 @classmethod
194 def printNotImplementedError(cls, ex) -> NoReturn:
195 """Prints a not-implemented exception of type :exc:`NotImplementedError`."""
196 from traceback import walk_tb
198 cls.initColors()
200 frame, _ = [x for x in walk_tb(ex.__traceback__)][-1]
201 filename = frame.f_code.co_filename
202 funcName = frame.f_code.co_name
203 print("{RED}NOT IMPLEMENTED:{NOCOLOR} {function} in file '{filename}': {message!s}".format(
204 function=funcName,
205 filename=filename,
206 message=ex,
207 **cls.Foreground
208 ))
209 print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground))
210 print(("{RED}Please report this bug at GitHub: https://GitHub.com/pyTooling/pyTooling.TerminalUI/issues{NOCOLOR}").format(**cls.Foreground))
211 print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground))
213 cls.exit(1)
215 @classmethod
216 def printExceptionBase(cls, ex) -> NoReturn:
217 cls.initColors()
219 print("{RED}FATAL: A known but unhandled exception reached the topmost exception handler!{NOCOLOR}".format(**cls.Foreground))
220 print("{RED}ERROR:{NOCOLOR} {message}".format(message=ex.message, **cls.Foreground))
221 print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground))
222 print(("{RED}Please report this bug at GitHub: https://GitHub.com/pyTooling/pyTooling.TerminalUI/issues{NOCOLOR}").format(**cls.Foreground))
223 print(("{RED}" + ("-" * 80) + "{NOCOLOR}").format(**cls.Foreground))
225 cls.exit(1)
227 @property
228 def Width(self) -> int:
229 """Returns the current terminal window's width."""
230 return self._width
232 @property
233 def Height(self) -> int:
234 """Returns the current terminal window's height."""
235 return self._height
237 @staticmethod
238 def GetTerminalSize() -> Tuple[int, int]:
239 """Returns the terminal size as tuple (width, height) for Windows, Mac OS (Darwin), Linux, cygwin (Windows), MinGW32/64 (Windows)."""
240 size = None
242 platform = platform_system()
243 if (platform == "Windows"): 243 ↛ 244line 243 didn't jump to line 244, because the condition on line 243 was never true
244 size = Terminal.__GetTerminalSizeOnWindows()
245 elif ((platform in ["Linux", "Darwin"]) or 245 ↛ 251line 245 didn't jump to line 251, because the condition on line 245 was never false
246 platform.startswith("CYGWIN") or
247 platform.startswith("MINGW32") or
248 platform.startswith("MINGW64")):
249 size = Terminal.__GetTerminalSizeOnLinux()
251 if (size is None): 251 ↛ 253line 251 didn't jump to line 253, because the condition on line 251 was never false
252 size = (80, 25) # default size
253 return size
255 @staticmethod
256 def __GetTerminalSizeOnWindows() -> Tuple[int, int]:
257 """Returns the current terminal window's size for Windows."""
258 try:
259 from ctypes import windll, create_string_buffer
260 from struct import unpack as struct_unpack
262 hStdError = windll.kernel32.GetStdHandle(-12) # stderr handle = -12
263 stringBuffer = create_string_buffer(22)
264 result = windll.kernel32.GetConsoleScreenBufferInfo(hStdError, stringBuffer)
265 if result:
266 (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct_unpack("hhhhHhhhhhh", stringBuffer.raw)
267 width = right - left + 1
268 height = bottom - top + 1
269 return (width, height)
270 except:
271 pass
273 return Terminal.__GetTerminalSizeWithTPut()
275 @staticmethod
276 def __GetTerminalSizeWithTPut() -> Tuple[int, int]:
277 from shlex import split as shlex_split
278 from subprocess import check_output
280 try:
281 width = int(check_output(shlex_split('tput cols')))
282 height = int(check_output(shlex_split('tput lines')))
283 return (width, height)
284 except:
285 pass
287 @staticmethod
288 def __GetTerminalSizeOnLinux() -> Tuple[int, int]:
289 """Returns the current terminal window's size for Linux."""
290 import os
292 def ioctl_GWINSZ(fd):
293 """GetWindowSize of file descriptor."""
294 try:
295 from fcntl import ioctl as fcntl_ioctl
296 from struct import unpack as struct_unpack
297 from termios import TIOCGWINSZ
299 return struct_unpack('hh', fcntl_ioctl(fd, TIOCGWINSZ, '1234'))
300 except:
301 pass
303 # STDIN STDOUT STDERR
304 cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
305 if not cr: 305 ↛ 313line 305 didn't jump to line 313, because the condition on line 305 was never false
306 try:
308 fd = os.open(os.ctermid(), os.O_RDONLY)
309 cr = ioctl_GWINSZ(fd)
310 os.close(fd)
311 except:
312 pass
313 if not cr: 313 ↛ 318line 313 didn't jump to line 318, because the condition on line 313 was never false
314 try:
315 cr = (os.environ['LINES'], os.environ['COLUMNS'])
316 except:
317 return None
318 return (int(cr[1]), int(cr[0]))
321@export
322@unique
323class Severity(Enum):
324 """Logging message severity levels."""
326 Fatal = 30 #: Fatal messages
327 Error = 25 #: Error messages
328 Quiet = 20 #: Always visible messages, even in quiet mode.
329 Warning = 15 #: Warning messages
330 Info = 10 #: Informative messages
331 DryRun = 5 #: Messages visible in a dry-run
332 Normal = 4 #: Normal messages
333 Verbose = 2 #: Verbose messages
334 Debug = 1 #: Debug messages
335 All = 0 #: All messages
337 def __hash__(self):
338 return hash(self.name)
340 def __eq__(self, other: Any):
341 if isinstance(other, Severity):
342 return self.value == other.value
343 else:
344 raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.")
346 def __ne__(self, other: Any):
347 if isinstance(other, Severity):
348 return self.value != other.value
349 else:
350 raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.")
352 def __lt__(self, other: Any):
353 if isinstance(other, Severity):
354 return self.value < other.value
355 else:
356 raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.")
358 def __le__(self, other: Any):
359 if isinstance(other, Severity):
360 return self.value <= other.value
361 else:
362 raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.")
364 def __gt__(self, other: Any):
365 if isinstance(other, Severity):
366 return self.value > other.value
367 else:
368 raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.")
370 def __ge__(self, other: Any):
371 if isinstance(other, Severity): 371 ↛ 374line 371 didn't jump to line 374, because the condition on line 371 was never false
372 return self.value >= other.value
373 else:
374 raise TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.")
377@export
378class Line:
379 """Represents a single line message with a severity and indentation level."""
381 _LOG_MESSAGE_FORMAT__ = {
382 Severity.Fatal: "FATAL: {message}",
383 Severity.Error: "ERROR: {message}",
384 Severity.Warning: "WARNING: {message}",
385 Severity.Info: "INFO: {message}",
386 Severity.Quiet: "{message}",
387 Severity.Normal: "{message}",
388 Severity.Verbose: "VERBOSE: {message}",
389 Severity.Debug: "DEBUG: {message}",
390 Severity.DryRun: "DRYRUN: {message}"
391 } #: Terminal messages formatting rules
393 def __init__(self, message, severity=Severity.Normal, indent=0, appendLinebreak=True):
394 """Constructor for a new ``Line`` object."""
395 self._severity = severity
396 self._message = message
397 self._indent = indent
398 self.AppendLinebreak = appendLinebreak
401 @property
402 def Severity(self) -> Severity:
403 """Return the line's severity level."""
404 return self._severity
406 @property
407 def Indent(self) -> int:
408 """Return the line's indentation level."""
409 return self._indent
411 @property
412 def Message(self) -> str:
413 """Return the indented line."""
414 return (" " * self._indent) + self._message
416 def IndentBy(self, indent) -> None:
417 """Increase a line's indentation level."""
418 self._indent += indent
420 def __str__(self) -> str:
421 """Returns a formatted version of a ``Line`` objects as a string."""
422 return self._LOG_MESSAGE_FORMAT__[self._severity].format(message=self._message)
425@export
426class ILineTerminal:
427 """A mixin class (interface) to provide class-local terminal writing methods."""
429 _terminal = None
431 def __init__(self, terminal=None):
432 """MixIn initializer."""
433 self._terminal = terminal
435 # FIXME: Alter methods if a terminal is present or set dummy methods
437 @property
438 def Terminal(self) -> Terminal:
439 """Return the local terminal instance."""
440 return self._terminal
442 def WriteLine(self, line : Line, condition=True):
443 """Write an entry to the local terminal."""
444 if ((self._terminal is not None) and condition):
445 return self._terminal.WriteLine(line)
446 return False
448 # def _TryWriteLine(self, *args, condition=True, **kwargs):
449 # if ((self._terminal is not None) and condition):
450 # return self._terminal.TryWrite(*args, **kwargs)
451 # return False
453 def WriteFatal(self, *args, condition=True, **kwargs):
454 """Write a fatal message if ``condition`` is true."""
455 if ((self._terminal is not None) and condition):
456 return self._terminal.WriteFatal(*args, **kwargs)
457 return False
459 def WriteError(self, *args, condition=True, **kwargs):
460 """Write an error message if ``condition`` is true."""
461 if ((self._terminal is not None) and condition):
462 return self._terminal.WriteError(*args, **kwargs)
463 return False
465 def WriteWarning(self, *args, condition=True, **kwargs):
466 """Write a warning message if ``condition`` is true."""
467 if ((self._terminal is not None) and condition):
468 return self._terminal.WriteWarning(*args, **kwargs)
469 return False
471 def WriteInfo(self, *args, condition=True, **kwargs):
472 """Write a info message if ``condition`` is true."""
473 if ((self._terminal is not None) and condition):
474 return self._terminal.WriteInfo(*args, **kwargs)
475 return False
477 def WriteQuiet(self, *args, condition=True, **kwargs):
478 """Write a message even in quiet mode if ``condition`` is true."""
479 if ((self._terminal is not None) and condition):
480 return self._terminal.WriteQuiet(*args, **kwargs)
481 return False
483 def WriteNormal(self, *args, condition=True, **kwargs):
484 """Write a *normal* message if ``condition`` is true."""
485 if ((self._terminal is not None) and condition):
486 return self._terminal.WriteNormal(*args, **kwargs)
487 return False
489 def WriteVerbose(self, *args, condition=True, **kwargs):
490 """Write a verbose message if ``condition`` is true."""
491 if ((self._terminal is not None) and condition):
492 return self._terminal.WriteVerbose(*args, **kwargs)
493 return False
495 def WriteDebug(self, *args, condition=True, **kwargs):
496 """Write a debug message if ``condition`` is true."""
497 if ((self._terminal is not None) and condition):
498 return self._terminal.WriteDebug(*args, **kwargs)
499 return False
501 def WriteDryRun(self, *args, condition=True, **kwargs):
502 """Write a dry-run message if ``condition`` is true."""
503 if ((self._terminal is not None) and condition):
504 return self._terminal.WriteDryRun(*args, **kwargs)
505 return False
508@export
509class LineTerminal(Terminal, ILineTerminal, metaclass=ExtendedType, singleton=True):
510 def __init__(self, verbose=False, debug=False, quiet=False, writeToStdOut=True):
511 """Initializer of a line based terminal interface."""
512 Terminal.__init__(self)
513 ILineTerminal.__init__(self, self)
515 self._verbose = True if debug else verbose
516 self._debug = debug
517 self._quiet = quiet
519 if quiet: 519 ↛ 520line 519 didn't jump to line 520, because the condition on line 519 was never true
520 self._WriteLevel = Severity.Quiet
521 elif debug: 521 ↛ 523line 521 didn't jump to line 523, because the condition on line 521 was never false
522 self._WriteLevel = Severity.Debug
523 elif verbose:
524 self._WriteLevel = Severity.Verbose
525 else:
526 self._WriteLevel = Severity.Normal
528 self._writeToStdOut = writeToStdOut
529 self._lines = []
530 self._baseIndent = 0
532 self._errorCounter = 0
533 self._warningCounter = 0
535 @property
536 def Verbose(self) -> bool:
537 """Returns true, if verbose messages are enabled."""
538 return self._verbose
540 @property
541 def Debug(self) -> bool:
542 """Returns true, if debug messages are enabled."""
543 return self._debug
545 @property
546 def Quiet(self) -> bool:
547 """Returns true, if quiet mode is enabled."""
548 return self._quiet
550 @property
551 def LogLevel(self) -> Severity:
552 """Return the current minimal severity level for writing."""
553 return self._WriteLevel
554 @LogLevel.setter
555 def LogLevel(self, value: Severity) -> None:
556 """Set the minimal severity level for writing."""
557 self._WriteLevel = value
559 @property
560 def BaseIndent(self) -> int:
561 return self._baseIndent
562 @BaseIndent.setter
563 def BaseIndent(self, value: int) -> None:
564 self._baseIndent = value
566 _LOG_MESSAGE_FORMAT__ = {
567 Severity.Fatal: "{DARK_RED}[FATAL] {message}{NOCOLOR}",
568 Severity.Error: "{RED}[ERROR] {message}{NOCOLOR}",
569 Severity.Quiet: "{WHITE}{message}{NOCOLOR}",
570 Severity.Warning: "{YELLOW}[WARNING]{message}{NOCOLOR}",
571 Severity.Info: "{WHITE}{message}{NOCOLOR}",
572 Severity.DryRun: "{DARK_CYAN}[DRY] {message}{NOCOLOR}",
573 Severity.Normal: "{WHITE}{message}{NOCOLOR}",
574 Severity.Verbose: "{GRAY}{message}{NOCOLOR}",
575 Severity.Debug: "{DARK_GRAY}{message}{NOCOLOR}"
576 } #: Message formatting rules.
578 def ExitOnPreviousErrors(self) -> None:
579 """Exit application if errors have been printed."""
580 if self._errorCounter > 0:
581 self.WriteFatal("Too many errors in previous steps.")
582 self.fatalExit()
584 def ExitOnPreviousWarnings(self) -> None:
585 """Exit application if warnings have been printed."""
586 if self._warningCounter > 0:
587 self.WriteError("Too many warnings in previous steps.")
588 self.exit()
590 def WriteLine(self, line : Line):
591 """Print a formatted line to the underlying terminal/console offered by the operating system."""
592 if (line.Severity >= self._WriteLevel): 592 ↛ 598line 592 didn't jump to line 598, because the condition on line 592 was never false
593 self._lines.append(line)
594 if self._writeToStdOut: 594 ↛ 596line 594 didn't jump to line 596, because the condition on line 594 was never false
595 print(self._LOG_MESSAGE_FORMAT__[line.Severity].format(message=line.Message, **self.Foreground), end="\n" if line.AppendLinebreak else "")
596 return True
597 else:
598 return False
600 def TryWriteLine(self, line) -> bool:
601 return (line.Severity >= self._WriteLevel)
603 def WriteFatal(self, message, indent=0, appendLinebreak=True, immediateExit=True):
604 ret = self.WriteLine(Line(message, Severity.Fatal, self._baseIndent + indent, appendLinebreak))
605 if immediateExit: 605 ↛ 606line 605 didn't jump to line 606, because the condition on line 605 was never true
606 self.fatalExit()
607 return ret
609 def WriteError(self, message, indent=0, appendLinebreak=True):
610 self._errorCounter += 1
611 return self.WriteLine(Line(message, Severity.Error, self._baseIndent + indent, appendLinebreak))
613 def WriteWarning(self, message, indent=0, appendLinebreak=True):
614 self._warningCounter += 1
615 return self.WriteLine(Line(message, Severity.Warning, self._baseIndent + indent, appendLinebreak))
617 def WriteInfo(self, message, indent=0, appendLinebreak=True):
618 return self.WriteLine(Line(message, Severity.Info, self._baseIndent + indent, appendLinebreak))
620 def WriteQuiet(self, message, indent=0, appendLinebreak=True):
621 return self.WriteLine(Line(message, Severity.Quiet, self._baseIndent + indent, appendLinebreak))
623 def WriteNormal(self, message, indent=0, appendLinebreak=True):
624 return self.WriteLine(Line(message, Severity.Normal, self._baseIndent + indent, appendLinebreak))
626 def WriteVerbose(self, message, indent=1, appendLinebreak=True):
627 return self.WriteLine(Line(message, Severity.Verbose, self._baseIndent + indent, appendLinebreak))
629 def WriteDebug(self, message, indent=2, appendLinebreak=True):
630 return self.WriteLine(Line(message, Severity.Debug, self._baseIndent + indent, appendLinebreak))
632 def WriteDryRun(self, message, indent=2, appendLinebreak=True):
633 return self.WriteLine(Line(message, Severity.DryRun, self._baseIndent + indent, appendLinebreak))