13 """! Controls whether Logger.log() also prints a message to the console. """
21 """! Controls how much detail Logger.log()/new_section() print to the console.
23 OFF: nothing tagged IF_VERBOSE is printed.
24 TEXT: plain, textual verbose output is printed.
25 VISUALISATION: verbose output is printed, and the run additionally
26 produces/updates a visualisation of what is going on.
28 TEXT and VISUALISATION both switch on the same "verbose" console output;
29 they only differ in whether a visualisation is on top of that.
40 Singleton logger for the Peano 4 Python API
42 Python has no dedicated singleton keyword or construct. The idiomatic way
43 to get the same effect is to override __new__, so repeated calls to
44 Logger() all return the one and only instance instead of creating a new
47 The logger holds an in-memory output stream (self._stream), which is
48 protected and never accessed from the outside. Use log()/new_section()
49 to accumulate details into it over the course of the run; nothing is
50 written to disk until the stream is flushed into file_name.
52 Once the singleton is created, it registers itself with atexit, so
53 close() is invoked automatically when the program terminates - you do
54 not have to call it yourself.
63 cls.
_instance._verbosity = LoggerVerbosity.OFF
66 cls.
_instance._visualisation_window_shown =
False
83 Open a window displaying the Peano logo. Called once, the first time
84 verbosity is ever set to LoggerVerbosity.VISUALISATION. If no display
85 is available (e.g. a headless run), matplotlib prints a warning and
86 this silently is a no-op rather than failing the run.
88 The window is opened non-blocking, so the run continues straight
89 away. It is not closed automatically though: we register a
90 blocking plt.show() to run at program exit, so the window stays on
91 screen - the user has to close it manually - rather than vanishing
92 the moment the process would otherwise terminate.
94 import matplotlib.image
as mpimg
95 import matplotlib.pyplot
as plt
97 icon_file = os.path.join(
98 os.path.dirname(os.path.abspath(__file__)),
99 "..",
"..",
"..",
"documentation",
"Peano-icon.png",
101 image = mpimg.imread(icon_file)
105 plt.show(block=
False)
108 atexit.register(plt.show)
114 True if and only if verbosity is set to LoggerVerbosity.VISUALISATION.
115 Implies is_text, since visualising always dumps text alongside the
116 visualisation (@see is_text) - callers that branch on both should
117 therefore never treat these two as mutually exclusive alternatives.
120 return self.
_verbosity == LoggerVerbosity.VISUALISATION
126 True if verbosity is set to LoggerVerbosity.TEXT or
127 LoggerVerbosity.VISUALISATION - visualising always dumps text
128 alongside the visualisation, it is never the only output, so this
129 is also true whenever is_visualisation is (@see is_visualisation;
130 the converse does not hold: plain TEXT verbosity produces no
134 return self.
_verbosity in (LoggerVerbosity.TEXT, LoggerVerbosity.VISUALISATION)
146 print_option: LoggerPrintOption = LoggerPrintOption.IF_VERBOSE,
148 """! By default, only printed to the console if verbosity is not OFF. """
149 if print_option == LoggerPrintOption.ALWAYS_PRINT
or (
150 print_option == LoggerPrintOption.IF_VERBOSE
and self.
_verbosity != LoggerVerbosity.OFF
153 self._stream.write( message )
154 self._stream.write(
"\n" )
156 AcceptWarningsEnvironmentVariable =
"PEANO_ACCEPT_WARNINGS"
161 Log a warning and let the user decide whether to continue
163 The warning is always printed and written into the log. Afterwards,
164 the user has to accept the warning or abort. If the user aborts, the
165 routine raises an exception, i.e. the Python script terminates.
167 If the environment variable PEANO_ACCEPT_WARNINGS is set to 1, all
168 warnings are accepted without asking. Use this for batch jobs. If
169 the variable is not set and there is no terminal to ask the user
170 (stdin is not a terminal), the routine aborts.
173 The warning. It should explain what goes wrong if the user
177 self.
log(
"Warning: " + message, LoggerPrintOption.ALWAYS_PRINT)
179 if os.environ.get(Logger.AcceptWarningsEnvironmentVariable) ==
"1":
180 self.
log(f
"Warning accepted, as {Logger.AcceptWarningsEnvironmentVariable}=1", LoggerPrintOption.ALWAYS_PRINT)
183 if not sys.stdin.isatty():
185 f
"Abort, as there is no terminal to confirm the warning. Set {Logger.AcceptWarningsEnvironmentVariable}=1 to accept all warnings",
186 LoggerPrintOption.ALWAYS_PRINT,
188 raise Exception(
"Warning not accepted: " + message)
191 answer = input(
"[A]ccept or a[b]ort? ").strip().lower()
192 if answer
in (
"a",
"accept"):
193 self.
log(
"Warning accepted by user", LoggerPrintOption.NEVER_PRINT)
195 if answer
in (
"b",
"abort"):
196 self.
log(
"Abort requested by user", LoggerPrintOption.NEVER_PRINT)
197 raise Exception(
"Warning not accepted: " + message)
202 print(
"------------------------------")
204 print(
"------------------------------")
205 self._stream.write(
"\n\n## {}\n\n".format(section_name))
209 Called automatically when the program terminates (see atexit
210 registration in __new__). Flushes the accumulated stream into
211 file_name, preceded by a section stating when the file was closed.
213 If file_name already exists, we do not overwrite it. Instead, we
214 attach a timestamp to the file name, i.e. log.md becomes
215 something similar to log-20240101-120000.md.
217 This routine always prints a report to the console about the newly
218 generated logging file.
220 closed_at = datetime.datetime.now()
223 self.
log( f
"Closed at {closed_at.isoformat()}.")
226 if os.path.exists(file_name):
227 base, ext = os.path.splitext(file_name)
228 file_name = f
"{base}-{closed_at.strftime('%Y%m%d-%H%M%S')}{ext}"
229 with open(file_name,
"w")
as file:
230 file.write(self._stream.getvalue())
232 print(
"Wrote output log to file {}".format(file_name))
Controls whether Logger.log() also prints a message to the console.
Controls how much detail Logger.log()/new_section() print to the console.
Singleton logger for the Peano 4 Python API.
LoggerVerbosity verbosity(self)
_show_visualisation_window(self)
Open a window displaying the Peano logo.
bool is_visualisation(self)
True if and only if verbosity is set to LoggerVerbosity.VISUALISATION.
new_section(self, str section_name)
warn_and_confirm(self, str message)
Log a warning and let the user decide whether to continue.
_visualisation_window_shown
bool is_text(self)
True if verbosity is set to LoggerVerbosity.TEXT or LoggerVerbosity.VISUALISATION - visualising alway...
close(self)
Called automatically when the program terminates (see atexit registration in new).
log(self, str message, LoggerPrintOption print_option=LoggerPrintOption.IF_VERBOSE)
By default, only printed to the console if verbosity is not OFF.