Skip to content

Commit 6168160

Browse files
committed
Remove monkeypatching of warnings in logger
1 parent d967740 commit 6168160

2 files changed

Lines changed: 72 additions & 81 deletions

File tree

CHANGELOG.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ master
1313

1414
Added
1515
^^^^^
16-
* Added W0621 to diabled list in pylint.
16+
* Added W0621 to disabled list in pylint.
1717

1818
Changed
1919
^^^^^^^
2020
* Changed documentation font size.
2121
* Modified code and readthedocs configuration to use Python 3.6.
22-
* Better logging of warnings. Custom warning are only used when the warning category is a subclass of the base package warning.
22+
* Remove logger warning monkeypatching since it conflicted when used with packages that provide a similar monkeypatching. Replaced with a custom ``logging.warning`` method that produces coloured warning output.
2323
* The ``package_name`` specified when cookiecutting the template is applied in lowercase when creating the package but in ucfirst case when creating classes.
2424
* Renamed ``misc`` to ``utils``.
2525

{{cookiecutter.repo_name}}/python/{{cookiecutter.package_name|lower}}/utils/logger.py

Lines changed: 70 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,15 @@
1818
import sys
1919
import traceback
2020
import warnings
21-
from logging import PercentStyle
2221
from logging.handlers import TimedRotatingFileHandler
2322

2423
from pygments import highlight
2524
from pygments.formatters import TerminalFormatter
2625
from pygments.lexers import get_lexer_by_name
2726

28-
from ..core import exceptions
2927
from .color_print import color_text
3028

3129

32-
# from textwrap import TextWrapper
33-
34-
3530
# Adds custom log level for print and twisted messages
3631
PRINT = 15
3732
logging.addLevelName(PRINT, 'PRINT')
@@ -56,13 +51,11 @@ def print_exception_formatted(type, value, tb):
5651
def colored_formatter(record):
5752
"""Prints log messages with colours."""
5853

59-
colours = {
60-
'info': ('blue', 'normal'),
61-
'debug': ('magenta', 'normal'),
62-
'warning': ('yellow', 'normal'),
63-
'print': ('green', 'normal'),
64-
'error': ('red', 'bold')
65-
}
54+
colours = {'info': ('blue', 'normal'),
55+
'debug': ('magenta', 'normal'),
56+
'warning': ('yellow', 'normal'),
57+
'print': ('green', 'normal'),
58+
'error': ('red', 'bold')}
6659

6760
levelname = record.levelname.lower()
6861

@@ -74,27 +67,15 @@ def colored_formatter(record):
7467
header = color_text('[{}]: '.format(levelname.upper()),
7568
levelname_color)
7669

77-
message = '{0}'.format(record.msg)
70+
message = record.getMessage()
7871

79-
warning_category = re.match('^(\w+Warning\:).*', message)
80-
if warning_category is not None:
81-
warning_category_colour = color_text(warning_category.groups()[0],
82-
'cyan')
83-
message = message.replace(warning_category.groups()[0],
84-
warning_category_colour)
72+
if levelname == 'warning':
73+
warning_category_groups = re.match(r'^\w*?(.+?Warning) (.*)', message)
74+
if warning_category_groups is not None:
75+
warning_category, warning_text = warning_category_groups.groups()
8576

86-
sub_level = re.match('(\[.+\]:)(.*)', message)
87-
if sub_level is not None:
88-
sub_level_name = color_text(sub_level.groups()[0], 'red')
89-
message = '{}{}'.format(sub_level_name, ''.join(
90-
sub_level.groups()[1:]))
91-
92-
# if len(message) > 79:
93-
# tw = TextWrapper()
94-
# tw.width = 79
95-
# tw.subsequent_indent = ' ' * (len(record.levelname) + 2)
96-
# tw.break_on_hyphens = False
97-
# message = '\n'.join(tw.wrap(message))
77+
warning_category_colour = color_text('({})'.format(warning_category), 'cyan')
78+
message = '{} {}'.format(color_text(warning_text, ''), warning_category_colour)
9879

9980
sys.__stdout__.write('{}{}\n'.format(header, message))
10081
sys.__stdout__.flush()
@@ -104,42 +85,42 @@ def colored_formatter(record):
10485

10586
class MyFormatter(logging.Formatter):
10687

107-
warning_fmt = '%(asctime)s - %(levelname)s: %(message)s [%(origin)s]'
108-
info_fmt = '%(asctime)s - %(levelname)s - %(message)s [%(funcName)s @ ' + \
109-
'%(filename)s]'
88+
base_fmt = '%(asctime)s - %(levelname)s - %(message)s [%(funcName)s @ %(filename)s]'
11089

11190
ansi_escape = re.compile(r'\x1b[^m]*m')
11291

92+
def __init__(self, fmt='%(levelname)s - %(message)s [%(funcName)s @ %(filename)s]'):
93+
logging.Formatter.__init__(self, fmt, datefmt='%Y-%m-%d %H:%M:%S')
94+
11395
def format(self, record):
11496

11597
# Save the original format configured by the user
11698
# when the logger formatter was instantiated
117-
# format_orig = self._fmt
99+
format_orig = self._fmt
118100

119101
# Replace the original format with one customized by logging level
120-
121102
if record.levelno == logging.DEBUG:
122-
self._style = PercentStyle(MyFormatter.info_fmt)
103+
self._fmt = MyFormatter.base_fmt
123104

124105
elif record.levelno == logging.getLevelName('PRINT'):
125-
self._style = PercentStyle(MyFormatter.info_fmt)
106+
self._fmt = MyFormatter.base_fmt
126107

127108
elif record.levelno == logging.INFO:
128-
self._style = PercentStyle(MyFormatter.info_fmt)
109+
self._fmt = MyFormatter.base_fmt
129110

130111
elif record.levelno == logging.ERROR:
131-
self._style = PercentStyle(MyFormatter.info_fmt)
112+
self._fmt = MyFormatter.base_fmt
132113

133114
elif record.levelno == logging.WARNING:
134-
self._style = PercentStyle(MyFormatter.warning_fmt)
115+
self._fmt = MyFormatter.base_fmt
135116

136117
record.msg = self.ansi_escape.sub('', record.msg)
137118

138119
# Call the original formatter class to do the grunt work
139120
result = logging.Formatter.format(self, record)
140121

141122
# Restore the original format configured by the user
142-
# self._fmt = format_orig
123+
self._fmt = format_orig
143124

144125
return result
145126

@@ -184,28 +165,6 @@ class MyLogger(Logger):
184165
def save_log(self, path):
185166
shutil.copyfile(self.log_filename, os.path.expanduser(path))
186167

187-
def _show_warning(self, message, category, *args, **kwargs):
188-
189-
if not issubclass(category, exceptions.cookiecutter.package_name[0:1]|upper ~ cookiecutter.package_name[1:]Warning):
190-
warnings._showwarning_orig(message, category, *args, **kwargs)
191-
return
192-
193-
message = '{0}: {1}'.format(message.__class__.__name__, message)
194-
mod_path = args[0]
195-
196-
mod_name = None
197-
mod_path, ext = os.path.splitext(mod_path)
198-
for name, mod in sys.modules.items():
199-
path = os.path.splitext(getattr(mod, '__file__', ''))[0]
200-
if path == mod_path:
201-
mod_name = mod.__name__
202-
break
203-
204-
if mod_name is not None:
205-
self.warning(message, extra={'origin': mod_name})
206-
else:
207-
self.warning(message, extra={'origin': 'no_module'})
208-
209168
def _catch_exceptions(self, exctype, value, tb):
210169
"""Catches all exceptions and logs them."""
211170

@@ -215,6 +174,50 @@ def _catch_exceptions(self, exctype, value, tb):
215174
# First, we print to stdout with some colouring.
216175
print_exception_formatted(exctype, value, tb)
217176

177+
def warning(self, msg, category=None, use_filters=True):
178+
"""Custom ``logging.warning``.
179+
180+
Behaves like the default ``logging.warning`` but accepts ``category``
181+
and ``use_filters`` as arguments. ``category`` is the type of warning
182+
we are issuing (defaults to `UserWarning`). If ``use_filters=True``,
183+
checks whether there are global filters set for the message or the
184+
warning category and behaves accordingly.
185+
186+
"""
187+
188+
if category is None:
189+
category = UserWarning
190+
191+
full_msg = '{0} {1}'.format(category.__name__, msg)
192+
193+
n_issued = 0
194+
if category in self.warning_registry:
195+
if msg in self.warning_registry[category]:
196+
n_issued = self.warning_registry[category]
197+
198+
if use_filters:
199+
200+
category_filter = None
201+
regex_filter = None
202+
for warnings_filter in warnings.filters:
203+
if issubclass(category, warnings_filter[2]):
204+
category_filter = warnings_filter[0]
205+
regex_filter = warnings_filter[1]
206+
207+
if (category_filter == 'ignore') or (category_filter == 'once' and n_issued >= 1):
208+
if regex_filter is None or regex_filter.search(msg) is not None:
209+
return
210+
211+
if category_filter == 'error':
212+
raise ValueError(full_msg)
213+
214+
super(MyLogger, self).warning(full_msg)
215+
216+
if msg in self.warning_registry[category]:
217+
self.warning_registry[category][msg] += 1
218+
else:
219+
self.warning_registry[category][msg] = 1
220+
218221
def _set_defaults(self, log_level=logging.INFO, redirect_stdout=False):
219222
"""Reset logger to its initial state."""
220223

@@ -230,25 +233,13 @@ def _set_defaults(self, log_level=logging.INFO, redirect_stdout=False):
230233

231234
self.sh.setLevel(log_level)
232235

233-
self.enable_warnings()
234-
235236
# Redirects all stdout to the logger
236237
if redirect_stdout:
237238
sys.stdout = LoggerStdout(self._print)
238239

239240
# Catches exceptions
240241
sys.excepthook = self._catch_exceptions
241242

242-
def enable_warnings(self):
243-
"""Redirects warnings to the log."""
244-
245-
warnings.showwarning = self._show_warning
246-
247-
def disable_warnings(self):
248-
"""Restores normal warning system."""
249-
250-
warnings.showwarning = warnings._show_warning
251-
252243
def start_file_logger(self, path, log_file_level=logging.DEBUG):
253244
"""Start file logging."""
254245

@@ -269,16 +260,16 @@ def start_file_logger(self, path, log_file_level=logging.DEBUG):
269260
str(log_file_path), when='midnight', utc=True)
270261
self.fh.suffix = '%Y-%m-%d_%H:%M:%S'
271262
except (IOError, OSError) as ee:
272-
warnings.warn('log file {0!r} could not be opened for writing: '
273-
'{1}'.format(log_file_path, ee), RuntimeWarning)
263+
self.warning('log file {0!r} could not be opened for writing: {1}'.format(
264+
log_file_path, ee), RuntimeWarning)
274265
else:
275266
self.fh.setFormatter(fmt)
276267
self.addHandler(self.fh)
277268
self.fh.setLevel(log_file_level)
278269

279270
self.log_filename = log_file_path
280271

281-
def setLevel(self, level):
272+
def set_level(self, level):
282273
"""Sets levels for both sh and (if initialised) fh."""
283274

284275
self.sh.setLevel(level)

0 commit comments

Comments
 (0)