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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
# Copyright 2018 ke liang <lokyse@163.com>.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
from logging import Formatter, LogRecord
from deprecated import deprecated
from warnings import warn
from typing import Optional, Union, Dict
from onaplogging.utils.system import is_above_python_2_7, is_above_python_3_2
from onaplogging.utils.styles import (
ATTRIBUTES,
HIGHLIGHTS,
COLORS,
ATTRIBUTE_TAG,
HIGHLIGHT_TAG,
COLOR_TAG,
RESET,
FMT_STR
)
class BaseColorFormatter(Formatter):
"""Text color formatter class.
Wraps the logging. Uses Git shell coloring codes. Doesn't support Windows
CMD yet. If `fmt` is not suppied, the `style` is used. Eventually converts
a LogRecord object to "colored" text.
TODO:
Support for Windows CMD.
Extends:
logging.Formatter
Properties:
style : '%', '{' or '$' formatting.
datefrmt : ISO8601-like (or RFC 3339-like) format.
Args:
fmt : human-readable format. Defaults to None.
datefmt : ISO8601-like (or RFC 3339-like) format. Defaults to None.
colorfmt : Color schemas for logging levels. Defaults to None.
style : '%', '{' or '$' formatting. Defaults to '%'.
Methods:
format : formats a LogRecord record.
_parseColor : selects colors based on a logging levels.
"""
@property
def style(self):
# type: () -> str
return self.__style # name mangling with __ to avoid accidents
@property
def colorfmt(self):
# type: () -> str
return self.__colorfmt
@style.setter
def style(self, value):
# type: (str) -> None
"""Assign new style."""
self.__style = value
@colorfmt.setter
def colorfmt(self, value):
# type: (str) -> None
"""Assign new color format."""
self.__colorfmt = value
def __init__(self,
fmt=None, # type: Optional[str]
datefmt=None, # type: Optional[str]
colorfmt=None, # type: Optional[Dict]
style="%"): # type: Optional[str]
if is_above_python_3_2():
super(BaseColorFormatter, self). \
__init__(fmt=fmt, # noqa: E122
datefmt=datefmt,
style=style)
elif is_above_python_2_7():
super(BaseColorFormatter, self). \
__init__(fmt, datefmt) # noqa: E122
else:
Formatter. \
__init__(self, fmt, datefmt) # noqa: E122
self.style = style
self.colorfmt = colorfmt
def format(self, record):
"""Text formatter.
Connects 2 methods. First it extract a level and a colors
assigned to this level in the BaseColorFormatter class.
Second it applied the colors to the text.
Args:
record : an instance of a logged event.
Returns:
str : "colored" text (formatted text).
"""
if is_above_python_2_7():
s = super(BaseColorFormatter, self). \
format(record)
else:
s = Formatter. \
format(self, record)
color, highlight, attribute = self._parse_color(record)
return apply_color(s, color, highlight, attrs=attribute)
def _parse_color(self, record):
# type: (LogRecord) -> (Optional[str], Optional[str], Optional[str])
"""Color formatter based on the logging level.
This method formats the record according to its level
and a color format set for that level. If the level is
not found, then this method will eventually return None.
Args:
record : an instance of a logged event.
Returns:
str : Colors.
str : Hightlight tag.
str : Attribute tag.
"""
if self.colorfmt and \
isinstance(self.colorfmt, dict):
level = record.levelname
colors = self.colorfmt.get(level, None)
if colors is not None and \
isinstance(colors, dict):
return (colors.get(COLOR_TAG, None), # noqa: E201
colors.get(HIGHLIGHT_TAG, None),
colors.get(ATTRIBUTE_TAG, None)) # noqa: E202
return None, None, None
@deprecated(reason="Will be removed. Use _parse_color(record) instead.")
def _parseColor(self, record):
"""
Color based on logging level.
See method _parse_color(record).
"""
return self._parse_color(record)
def apply_color(text, # type: str
color=None, # type: Optional[str]
on_color=None, # type: Optional[str]
attrs=None): # type: Optional[Union[str, list]]
# type: (...) -> str
"""Applies color codes to the text.
Args:
text : text to be "colored" (formatted).
color : Color in human-readable format. Defaults to None.
highlight : Hightlight color in human-readable format.
Previously called "on_color". Defaults to None.
attrs : Colors for attribute(s). Defaults to None.
Returns:
str : "colored" text (formatted text).
"""
warn("`on_color` will be replaced with `highlight`.", DeprecationWarning)
highlight = on_color # replace the parameter and remove
if os.name in ('nt', 'ce'):
return text
if isinstance(attrs, str):
attrs = [attrs]
ansi_disabled = os.getenv('ANSI_COLORS_DISABLED', None)
if ansi_disabled is None:
if color is not None and \
isinstance(color, str):
text = FMT_STR % (COLORS.get(color, 0), text)
if highlight is not None and \
isinstance(highlight, str):
text = FMT_STR % (HIGHLIGHTS.get(highlight, 0), text)
if attrs is not None:
for attr in attrs:
text = FMT_STR % (ATTRIBUTES.get(attr, 0), text)
text += RESET # keep origin color for tail spaces
return text
@deprecated(reason="Will be removed. Call apply_color(...) instead.")
def colored(text, color=None, on_color=None, attrs=None):
"""
Format text with color codes.
See method apply_color(text, color, on_color, attrs).
"""
return apply_color(text, color, on_color, attrs)
|