-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement LoggedException in vsc.utils.exceptions module #160
Changes from 3 commits
68ac088
1756cf9
ec53df6
799edbe
99c626c
3e28d5f
e413b38
d94aa70
7cfbb64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
## | ||
# Copyright 2015-2015 Ghent University | ||
# | ||
# This file is part of vsc-base, | ||
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), | ||
# with support of Ghent University (http://ugent.be/hpc), | ||
# the Flemish Supercomputer Centre (VSC) (https://vscentrum.be/nl/en), | ||
# the Hercules foundation (http://www.herculesstichting.be/in_English) | ||
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). | ||
# | ||
# http://github.com/hpcugent/vsc-base | ||
# | ||
# vsc-base is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Library General Public License as | ||
# published by the Free Software Foundation, either version 2 of | ||
# the License, or (at your option) any later version. | ||
# | ||
# vsc-base is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Library General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Library General Public License | ||
# along with vsc-base. If not, see <http://www.gnu.org/licenses/>. | ||
## | ||
""" | ||
Module providing custom exceptions. | ||
|
||
@author: Kenneth Hoste (Ghent University) | ||
@author: Riccardo Murri (University of Zurich) | ||
""" | ||
import inspect | ||
import logging | ||
from vsc.utils import fancylogger | ||
|
||
|
||
def get_callers_logger(): | ||
""" | ||
Get logger defined in caller's environment | ||
@return: logger instance (or None if none was found) | ||
""" | ||
logger_cls = logging.getLoggerClass() | ||
frame = inspect.currentframe() | ||
logger = None | ||
try: | ||
# frame may be None, see https://docs.python.org/2/library/inspect.html#inspect.currentframe | ||
if frame is None: | ||
framerecords = [] | ||
else: | ||
framerecords = inspect.getouterframes(frame) | ||
|
||
for frameinfo in framerecords: | ||
bindings = inspect.getargvalues(frameinfo[0]).locals | ||
for val in bindings.values(): | ||
if isinstance(val, logger_cls): | ||
logger = val | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add |
||
finally: | ||
# make very sure that reference to frame object is removed, to avoid reference cycles | ||
# see https://docs.python.org/2/library/inspect.html#the-interpreter-stack | ||
del frame | ||
|
||
return logger | ||
|
||
|
||
class LoggedException(Exception): | ||
"""Exception that logs it's message when it is created.""" | ||
|
||
def __init__(self, msg, logger=None): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, I'll tackle that |
||
""" | ||
Constructor. | ||
@param logger: logger to use | ||
""" | ||
super(LoggedException, self).__init__(msg) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there anything in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I doubt there's anything, but it can't hurt to do the logging first, so changed. |
||
|
||
# try to use logger defined in caller's environment | ||
if logger is None: | ||
logger = get_callers_logger() | ||
# search can fail, use root logger as a fallback | ||
if logger is None: | ||
logger = fancylogger.getLogger() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make |
||
|
||
logger.error(msg) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also the log function should be configurable with class constant (probably needed in EB) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# -*- coding: utf-8 -*- | ||
# | ||
# Copyright 2015-2015 Ghent University | ||
# | ||
# This file is part of vsc-base, | ||
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), | ||
# with support of Ghent University (http://ugent.be/hpc), | ||
# the Flemish Supercomputer Centre (VSC) (https://vscentrum.be/nl/en), | ||
# the Hercules foundation (http://www.herculesstichting.be/in_English) | ||
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). | ||
# | ||
# http://github.com/hpcugent/vsc-base | ||
# | ||
# vsc-base is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Library General Public License as | ||
# published by the Free Software Foundation, either version 2 of | ||
# the License, or (at your option) any later version. | ||
# | ||
# vsc-base is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Library General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Library General Public License | ||
# along with vsc-base. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
""" | ||
Unit tests for exceptions module. | ||
|
||
@author: Kenneth Hoste (Ghent University) | ||
""" | ||
import logging | ||
import os | ||
import re | ||
import tempfile | ||
from unittest import TestLoader, main | ||
|
||
from vsc.utils.exceptions import LoggedException | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
from vsc.utils.fancylogger import getLogger, logToFile, logToScreen, getRootLoggerName, setLogFormat | ||
from vsc.utils.testing import EnhancedTestCase | ||
|
||
|
||
def raise_loggedexception(msg, logger=None): | ||
"""Utility function: just raise a LoggedException.""" | ||
raise LoggedException(msg, logger=logger) | ||
|
||
|
||
logToScreen(enable=False) | ||
|
||
|
||
class ExceptionsTest(EnhancedTestCase): | ||
"""Tests for exceptions module.""" | ||
|
||
def test_loggedexception_defaultlogger(self): | ||
"""Test LoggedException custom exception class.""" | ||
fd, tmplog = tempfile.mkstemp() | ||
os.close(fd) | ||
|
||
# set log format, for each regex searching | ||
setLogFormat("%(name)s :: %(message)s") | ||
|
||
# if no logger is available, and no logger is specified, use default 'root' fancylogger | ||
logToFile(tmplog, enable=True) | ||
self.assertErrorRegex(LoggedException, 'BOOM', raise_loggedexception, 'BOOM') | ||
logToFile(tmplog, enable=False) | ||
|
||
log_re = re.compile("^%s :: BOOM$" % getRootLoggerName(), re.M) | ||
logtxt = open(tmplog, 'r').read() | ||
self.assertTrue(log_re.match(logtxt), "%s matches %s" % (log_re.pattern, logtxt)) | ||
|
||
os.remove(tmplog) | ||
|
||
def test_loggedexception_specifiedlogger(self): | ||
"""Test LoggedException custom exception class.""" | ||
fd, tmplog = tempfile.mkstemp() | ||
os.close(fd) | ||
|
||
# set log format, for each regex searching | ||
setLogFormat("%(name)s :: %(message)s") | ||
|
||
logger1 = getLogger('testlogger_one') | ||
logger2 = getLogger('testlogger_two') | ||
|
||
# if logger is specified, it should be used | ||
logToFile(tmplog, enable=True) | ||
self.assertErrorRegex(LoggedException, 'BOOM', raise_loggedexception, 'BOOM', logger=logger1) | ||
logToFile(tmplog, enable=False) | ||
|
||
log_re = re.compile("^%s.testlogger_one :: BOOM$" % getRootLoggerName()) | ||
logtxt = open(tmplog, 'r').read() | ||
self.assertTrue(log_re.match(logtxt), "%s matches %s" % (log_re.pattern, logtxt)) | ||
|
||
os.remove(tmplog) | ||
|
||
def test_loggedexception_callerlogger(self): | ||
"""Test LoggedException custom exception class.""" | ||
fd, tmplog = tempfile.mkstemp() | ||
os.close(fd) | ||
|
||
# set log format, for each regex searching | ||
setLogFormat("%(name)s :: %(message)s") | ||
|
||
logger = getLogger('testlogger_local') | ||
|
||
# if no logger is specified, logger available in calling context should be used | ||
logToFile(tmplog, enable=True) | ||
self.assertErrorRegex(LoggedException, 'BOOM', raise_loggedexception, 'BOOM') | ||
logToFile(tmplog, enable=False) | ||
|
||
log_re = re.compile("^%s.testlogger_local :: BOOM$" % getRootLoggerName()) | ||
logtxt = open(tmplog, 'r').read() | ||
self.assertTrue(log_re.match(logtxt), "%s matches %s" % (log_re.pattern, logtxt)) | ||
|
||
os.remove(tmplog) | ||
|
||
def suite(): | ||
""" returns all the testcases in this module """ | ||
return TestLoader().loadTestsFromTestCase(ExceptionsTest) | ||
|
||
if __name__ == '__main__': | ||
"""Use this __main__ block to help write and test unittests""" | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the whole
try/finally
blck is part of the else (or rather theif is not None
)