Coverage for pycommons / io / console.py: 100%
10 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 03:04 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-11 03:04 +0000
1"""The `logger` routine for writing a log string to stdout."""
2import datetime
3from contextlib import AbstractContextManager, nullcontext
4from typing import Callable, Final
6from pycommons.processes.caller import is_doc_test
8#: the "now" function
9__DTN: Final[Callable[[], datetime.datetime]] = datetime.datetime.now
12def logger(message: str, note: str = "",
13 lock: AbstractContextManager = nullcontext(),
14 do_print: bool = not is_doc_test()) -> None:
15 """
16 Write a message to the console log.
18 The line starts with the current date and time, includes the note, and
19 then the message string after an ": ".
20 This function can use a `lock` context to prevent multiple processes or
21 threads to write to the console at the same time.
23 :param message: the message
24 :param note: a note to put between the time and the message
25 :param lock: the lock to prevent multiple threads to write log
26 output at the same time
27 :param do_print: really print the output, by default `False` if this
28 method is called from a "doctest", `True` otherwise
30 >>> from io import StringIO
31 >>> from contextlib import redirect_stdout
32 >>> sio = StringIO()
33 >>> dt1 = datetime.datetime.now()
34 >>> with redirect_stdout(sio):
35 ... logger("hello world!", do_print=True)
36 >>> line = sio.getvalue().strip()
37 >>> print(line[line.index(" ", line.index(" ") + 1) + 1:])
38 hello world!
39 >>> dt2 = datetime.datetime.now()
40 >>> dtx = datetime.datetime.strptime(line[:26], "%Y-%m-%d %H:%M:%S.%f")
41 >>> dt1 <= dtx <= dt2
42 True
44 >>> sio = StringIO()
45 >>> with redirect_stdout(sio):
46 ... logger("hello world!", "note", do_print=True)
47 >>> line = sio.getvalue().strip()
48 >>> print(line[line.index("n"):])
49 note: hello world!
51 >>> from contextlib import AbstractContextManager
52 >>> class T:
53 ... def __enter__(self):
54 ... print("x")
55 ... def __exit__(self, exc_type, exc_val, exc_tb):
56 ... print("y")
58 >>> sio = StringIO()
59 >>> with redirect_stdout(sio):
60 ... logger("hello world!", "", T(), do_print=True)
61 >>> sio.seek(0)
62 0
63 >>> lines = sio.readlines()
64 >>> print(lines[0].rstrip())
65 x
66 >>> l = lines[1]
67 >>> print(l[l.index(" ", l.index(" ") + 1) + 1:].rstrip())
68 hello world!
69 >>> print(lines[2].rstrip())
70 y
72 >>> sio = StringIO()
73 >>> with redirect_stdout(sio):
74 ... logger("hello world!", "note", T(), do_print=True)
75 >>> sio.seek(0)
76 0
77 >>> lines = sio.readlines()
78 >>> print(lines[0].rstrip())
79 x
80 >>> l = lines[1]
81 >>> print(l[l.index("n"):].rstrip())
82 note: hello world!
83 >>> print(lines[2].rstrip())
84 y
86 >>> logger("hello world") # not printed in doctests
87 >>> logger("hello world", do_print=False) # not printed anyway
88 """
89 if do_print:
90 text: Final[str] = f"{__DTN()}{note}: {message}"
91 with lock:
92 print(text, flush=True) # noqa