]> git.lizzy.rs Git - nhentai.git/blob - nhentai/logger.py
Update README.rst
[nhentai.git] / nhentai / logger.py
1 #
2 # Copyright (C) 2010-2012 Vinay Sajip. All rights reserved. Licensed under the new BSD license.
3 #
4 from __future__ import print_function, unicode_literals
5 import logging
6 import re
7 import platform
8 import sys
9
10
11 if platform.system() == 'Windows':
12     import ctypes
13     import ctypes.wintypes
14
15     # Reference: https://gist.github.com/vsajip/758430
16     #            https://github.com/ipython/ipython/issues/4252
17     #            https://msdn.microsoft.com/en-us/library/windows/desktop/ms686047%28v=vs.85%29.aspx
18     ctypes.windll.kernel32.SetConsoleTextAttribute.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.WORD]
19     ctypes.windll.kernel32.SetConsoleTextAttribute.restype = ctypes.wintypes.BOOL
20
21
22 class ColorizingStreamHandler(logging.StreamHandler):
23     # color names to indices
24     color_map = {
25         'black': 0,
26         'red': 1,
27         'green': 2,
28         'yellow': 3,
29         'blue': 4,
30         'magenta': 5,
31         'cyan': 6,
32         'white': 7,
33     }
34
35     # levels to (background, foreground, bold/intense)
36     level_map = {
37         logging.DEBUG: (None, 'blue', False),
38         logging.INFO: (None, 'green', False),
39         logging.WARNING: (None, 'yellow', False),
40         logging.ERROR: (None, 'red', False),
41         logging.CRITICAL: ('red', 'white', False)
42     }
43     csi = '\x1b['
44     reset = '\x1b[0m'
45     disable_coloring = False
46
47     @property
48     def is_tty(self):
49         isatty = getattr(self.stream, 'isatty', None)
50         return isatty and isatty() and not self.disable_coloring
51
52     def emit(self, record):
53         try:
54             message = self.format(record)
55             stream = self.stream
56
57             if not self.is_tty:
58                 if message and message[0] == "\r":
59                     message = message[1:]
60                 stream.write(message)
61             else:
62                 self.output_colorized(message)
63             stream.write(getattr(self, 'terminator', '\n'))
64
65             self.flush()
66         except (KeyboardInterrupt, SystemExit):
67             raise
68         except IOError:
69             pass
70         except:
71             self.handleError(record)
72
73
74     if not platform.system() == 'Windows':
75         def output_colorized(self, message):
76             self.stream.write(message)
77     else:
78         ansi_esc = re.compile(r'\x1b\[((?:\d+)(?:;(?:\d+))*)m')
79
80         nt_color_map = {
81             0: 0x00,    # black
82             1: 0x04,    # red
83             2: 0x02,    # green
84             3: 0x06,    # yellow
85             4: 0x01,    # blue
86             5: 0x05,    # magenta
87             6: 0x03,    # cyan
88             7: 0x07,    # white
89         }
90
91         def output_colorized(self, message):
92             parts = self.ansi_esc.split(message)
93             write = self.stream.write
94             h = None
95             fd = getattr(self.stream, 'fileno', None)
96
97             if fd is not None:
98                 fd = fd()
99
100                 if fd in (1, 2): # stdout or stderr
101                     h = ctypes.windll.kernel32.GetStdHandle(-10 - fd)
102
103             while parts:
104                 text = parts.pop(0)
105
106                 if text:
107                     if sys.version_info < (3, 0, 0):
108                         write(text.encode('utf-8'))
109                     else:
110                         write(text)
111
112                 if parts:
113                     params = parts.pop(0)
114
115                     if h is not None:
116                         params = [int(p) for p in params.split(';')]
117                         color = 0
118
119                         for p in params:
120                             if 40 <= p <= 47:
121                                 color |= self.nt_color_map[p - 40] << 4
122                             elif 30 <= p <= 37:
123                                 color |= self.nt_color_map[p - 30]
124                             elif p == 1:
125                                 color |= 0x08 # foreground intensity on
126                             elif p == 0: # reset to default color
127                                 color = 0x07
128                             else:
129                                 pass # error condition ignored
130
131                         ctypes.windll.kernel32.SetConsoleTextAttribute(h, color)
132
133     def colorize(self, message, record):
134         if record.levelno in self.level_map and self.is_tty:
135             bg, fg, bold = self.level_map[record.levelno]
136             params = []
137
138             if bg in self.color_map:
139                 params.append(str(self.color_map[bg] + 40))
140
141             if fg in self.color_map:
142                 params.append(str(self.color_map[fg] + 30))
143
144             if bold:
145                 params.append('1')
146
147             if params and message:
148                 if message.lstrip() != message:
149                     prefix = re.search(r"\s+", message).group(0)
150                     message = message[len(prefix):]
151                 else:
152                     prefix = ""
153
154                 message = "%s%s" % (prefix, ''.join((self.csi, ';'.join(params),
155                                     'm', message, self.reset)))
156
157         return message
158
159     def format(self, record):
160         message = logging.StreamHandler.format(self, record)
161         return self.colorize(message, record)
162
163
164 logging.addLevelName(15, "INFO")
165 logger = logging.getLogger('nhentai')
166 LOGGER_HANDLER = ColorizingStreamHandler(sys.stdout)
167 FORMATTER = logging.Formatter("\r[%(asctime)s] [%(levelname)s] %(message)s", "%H:%M:%S")
168 LOGGER_HANDLER.setFormatter(FORMATTER)
169 LOGGER_HANDLER.level_map[logging.getLevelName("INFO")] = (None, "cyan", False)
170 logger.addHandler(LOGGER_HANDLER)
171 logger.setLevel(logging.DEBUG)
172
173
174 if __name__ == '__main__':
175     logger.log(15, 'nhentai')
176     logger.info('info')
177     logger.warn('warn')
178     logger.debug('debug')
179     logger.error('error')
180     logger.critical('critical')