-
Notifications
You must be signed in to change notification settings - Fork 28
/
output.py
78 lines (63 loc) · 2.38 KB
/
output.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# Copyright 2016 Camptocamp SA
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0.en.html)
import functools
import time
from contextlib import contextmanager
def safe_print(ustring, errors="replace", **kwargs):
"""Safely print a unicode string"""
print(ustring, **kwargs)
class LogIndent:
def __init__(self):
self.level = 0
@contextmanager
def display(self, name, timing=True, timestamp=False):
self.print_indent("{}...".format(name), timestamp=timestamp)
self.level += 1
start = time.time()
try:
yield
except Exception:
self.level -= 1
self.print_indent("{}: error".format(name), timestamp=timestamp)
raise
end = time.time()
self.level -= 1
if timing:
self.print_indent(
"{}: {:.3f}s".format(name, end - start), timestamp=timestamp
)
def print_indent(self, message, timestamp=False):
if not timestamp:
safe_print("{}{}".format(" " * self.level, message))
else:
safe_print(
"{}{}: {}".format(
" " * self.level, time.strftime("%Y-%m-%d %H:%M:%S"), message
)
)
def log(func=None, name=None, timing=True, timestamp=False):
"""Decorator to show a description of the running function
By default, it outputs the first line of the docstring.
If the docstring is empty, it displays the name of the function.
Alternatively, if a ``name`` is specified, it will display that only.
It can be called as ``@log`` or as
``@log(name='abc, timing=True, timestamp=True)``.
"""
# support to be called as @log or as @log(name='')
if func is None:
return functools.partial(log, name=name, timing=timing, timestamp=timestamp)
@functools.wraps(func)
def decorated(*args, **kwargs):
assert len(args) > 0 and hasattr(
args[0], "log"
), "The first argument of the decorated function must be a Context"
ctx = args[0]
message = name
if message is None:
if func.__doc__:
message = func.__doc__.splitlines()[0].strip()
if message is None:
message = func.__name__
with ctx.log(message, timing=timing, timestamp=timestamp):
return func(*args, **kwargs)
return decorated