馃悕馃悕馃悕
1
2import sys
3import inspect
4import traceback
5import time
6
7from dataclasses import dataclass
8from typing import Optional
9
10reset_color = "\033[0m"
11
12def _log(tag, content, mode, indent):
13 final_color = reset_color
14 if mode == "error":
15 tag_color = "\033[31m"
16 elif mode == "success":
17 tag_color = "\033[92m"
18 elif mode == "warning":
19 tag_color = "\033[33m"
20 else:
21 tag_color = "\033[96m"
22 print(f"{tag_color}{' '*indent}[{tag}]{reset_color} {content}{final_color}")
23
24def _input(prompt, mode, indent):
25 tag_color = "\033[35m"
26 return input(f"{tag_color}{' '*indent}{prompt} {reset_color}")
27
28
29@dataclass
30class Logger:
31 _mode: str = "standard"
32 _indent: int = 0
33 _tag: Optional[str] = None
34
35 def indented(self, n=4):
36 return Logger(_indent=self._indent + n, _tag=self._tag, _mode=self._mode)
37
38 def tag(self, tag: Optional[str]):
39 return Logger(_indent=self._indent, _tag=tag, _mode=self._mode)
40
41 def mode(self, mode: str):
42 return Logger(_indent=self._indent, _tag=self._tag, _mode=mode)
43
44 def __call__(self, content, mode=None, indent=None, tag=None):
45 if mode is None:
46 mode = self._mode
47 if indent is None:
48 indent = self._indent
49 if tag is None:
50 tag = self._tag
51 if tag is None:
52 callsite = inspect.stack()[1]
53 filename = callsite[1].split("/")[-1]
54 tag = f"{filename} {callsite[2]}"
55 _log(tag, content, mode, indent)
56 return self
57
58 def trace(self, source=None):
59 exception_type, exception, trace = sys.exc_info()
60 trace_frames = traceback.extract_tb(trace)
61 tag = "error" if self._tag is None else self._tag
62 exception_content = f"{exception_type.__name__}: {exception}"
63 _log(tag, exception_content, mode="error", indent=self._indent)
64 for frame in trace_frames[::-1]:
65 file, line_number, function, line = frame
66 if file == "<string>" and source is not None:
67 file = inspect.getfile(source)
68 lines, first_line = inspect.getsourcelines(source)
69 if line_number < len(lines):
70 line = lines[line_number-1].strip()
71 line_number = first_line + line_number - 1
72 else:
73 # TODO fix this!!
74 _log(f"SNAKEPYT", "TRACING ERROR", mode="error", indent=self._indent+8)
75 file_end = file.split("/")[-1]
76 _log(f"in {file_end} {line_number}", line, mode="error", indent=self._indent+4)
77 return self
78
79 def log(self, content, mode=None, indent=None, tag=None):
80 return self(content, mode, indent, tag)
81
82 def blank(self):
83 print()
84 return self
85
86 def input(self, username=None):
87 if username is None:
88 username = ":"
89 return _input(f"{username}:", mode="user", indent=self._indent)
90
91
92def inner_log(source, indent):
93 def log(content, mode="standard"):
94 callsite = inspect.stack()[1]
95 file = callsite[1]
96 line_number = callsite[2]
97 if file == "<string>" and source is not None:
98 file = inspect.getfile(source)
99 lines, first_line = inspect.getsourcelines(source)
100 line_number += first_line - 1
101 filename = file.split("/")[-1]
102 _log(f"{filename} {line_number}", content, mode, indent)
103 return log
104
105
106def trace(indent=0, source=None):
107 exception_type, exception, trace = sys.exc_info()
108 trace_frames = traceback.extract_tb(trace)
109 _log("error", exception_type.__name__, mode="error", indent=indent)
110 for frame in trace_frames[::-1]:
111 file, line_number, function, line = frame
112 if file == "<string>" and source is not None:
113 file = inspect.getfile(source)
114 lines, first_line = inspect.getsourcelines(source)
115 line = lines[line_number-1].strip()
116 line_number = first_line + line_number - 1
117 file_end = file.split("/")[-1]
118 _log(f"in {file_end} {line_number}", line, mode="error", indent=indent+4)
119
120class Timer(object):
121 def __init__(self, name):
122 self.name = name
123
124 def __enter__(self):
125 self.t = time.perf_counter()
126
127 def __exit__(self, *args):
128 print(f"{self.name}: {time.perf_counter() - self.t}")
129