mirror of
https://github.com/certbot/certbot.git
synced 2026-08-03 20:02:16 +02:00
Added traceback dump
This commit is contained in:
@@ -5,6 +5,7 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
/venv/
|
/venv/
|
||||||
/.tox/
|
/.tox/
|
||||||
|
letsencrypt.log
|
||||||
|
|
||||||
# coverage
|
# coverage
|
||||||
.coverage
|
.coverage
|
||||||
|
|||||||
+32
-7
@@ -645,18 +645,43 @@ def _setup_logging(args):
|
|||||||
|
|
||||||
|
|
||||||
def _handle_exception(exc_type, exc_value, trace, args):
|
def _handle_exception(exc_type, exc_value, trace, args):
|
||||||
|
"""Logs exceptions and reports them to the user.
|
||||||
|
|
||||||
|
Args is used to determine how to display exceptions to the user. In
|
||||||
|
general, if args.debug is True, then the full exception and traceback is
|
||||||
|
shown to the user, otherwise it is suppressed. If args itself is None,
|
||||||
|
then the traceback and exception is attempted to be written to a logfile.
|
||||||
|
If this is successful, the traceback is suppressed, otherwise it is shown
|
||||||
|
to the user. sys.exit is always called with a nonzero status.
|
||||||
|
|
||||||
|
"""
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Exiting abnormally:\n%s",
|
"Exiting abnormally:\n%s",
|
||||||
"".join(traceback.format_exception(exc_type, exc_value, trace)))
|
"".join(traceback.format_exception(exc_type, exc_value, trace)))
|
||||||
|
|
||||||
if issubclass(exc_type, errors.Error) and (not args or not args.debug):
|
if issubclass(exc_type, Exception) and (args is None or not args.debug):
|
||||||
sys.exit(exc_value)
|
if args is None:
|
||||||
elif issubclass(exc_type, Exception) and args and not args.debug:
|
try:
|
||||||
sys.exit(
|
with open("letsencrypt.log", "w") as logfile:
|
||||||
"An unexpected error occurred. Please see the logfiles in {0} for "
|
traceback.print_exception(
|
||||||
"more details.".format(args.logs_dir))
|
exc_type, exc_value, trace, file=logfile)
|
||||||
|
except: # pylint: disable=bare-except
|
||||||
|
sys.exit("".join(
|
||||||
|
traceback.format_exception(exc_type, exc_value, trace)))
|
||||||
|
|
||||||
|
if issubclass(exc_type, errors.Error):
|
||||||
|
sys.exit(exc_value)
|
||||||
|
elif args is None:
|
||||||
|
sys.exit(
|
||||||
|
"An unexpected error occurred. Please see the logfile '{0}' "
|
||||||
|
"for more details.".format(os.path.abspath("letsencrypt.log")))
|
||||||
|
else:
|
||||||
|
sys.exit(
|
||||||
|
"An unexpected error occurred. Please see the logfiles in {0} "
|
||||||
|
"for more details.".format(args.logs_dir))
|
||||||
else:
|
else:
|
||||||
traceback.print_exception(exc_type, exc_value, trace, file=sys.stderr)
|
sys.exit("".join(
|
||||||
|
traceback.format_exception(exc_type, exc_value, trace)))
|
||||||
|
|
||||||
|
|
||||||
def main(cli_args=sys.argv[1:]):
|
def main(cli_args=sys.argv[1:]):
|
||||||
|
|||||||
@@ -2,11 +2,14 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
import traceback
|
||||||
import tempfile
|
import tempfile
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from letsencrypt import errors
|
||||||
|
|
||||||
|
|
||||||
class CLITest(unittest.TestCase):
|
class CLITest(unittest.TestCase):
|
||||||
"""Tests for different commands."""
|
"""Tests for different commands."""
|
||||||
@@ -20,16 +23,13 @@ class CLITest(unittest.TestCase):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
shutil.rmtree(self.tmp_dir)
|
shutil.rmtree(self.tmp_dir)
|
||||||
|
|
||||||
def _call(self, args, client_mock_attrs=None):
|
def _call(self, args):
|
||||||
from letsencrypt import cli
|
from letsencrypt import cli
|
||||||
args = ['--text', '--config-dir', self.config_dir,
|
args = ['--text', '--config-dir', self.config_dir,
|
||||||
'--work-dir', self.work_dir, '--logs-dir', self.logs_dir] + args
|
'--work-dir', self.work_dir, '--logs-dir', self.logs_dir] + args
|
||||||
with mock.patch('letsencrypt.cli.sys.stdout') as stdout:
|
with mock.patch('letsencrypt.cli.sys.stdout') as stdout:
|
||||||
with mock.patch('letsencrypt.cli.sys.stderr') as stderr:
|
with mock.patch('letsencrypt.cli.sys.stderr') as stderr:
|
||||||
with mock.patch('letsencrypt.cli.client') as client:
|
with mock.patch('letsencrypt.cli.client') as client:
|
||||||
if client_mock_attrs:
|
|
||||||
# pylint: disable=star-args
|
|
||||||
client.configure_mock(**client_mock_attrs)
|
|
||||||
ret = cli.main(args)
|
ret = cli.main(args)
|
||||||
return ret, stdout, stderr, client
|
return ret, stdout, stderr, client
|
||||||
|
|
||||||
@@ -59,36 +59,41 @@ class CLITest(unittest.TestCase):
|
|||||||
for r in xrange(len(flags)))):
|
for r in xrange(len(flags)))):
|
||||||
self._call(['plugins',] + list(args))
|
self._call(['plugins',] + list(args))
|
||||||
|
|
||||||
def test_exceptions(self):
|
|
||||||
from letsencrypt import errors
|
|
||||||
cmd_arg = ['config_changes']
|
|
||||||
error = [errors.Error('problem')]
|
|
||||||
attrs = {'view_config_changes.side_effect' : error}
|
|
||||||
self.assertRaises(
|
|
||||||
errors.Error, self._call, ['--debug'] + cmd_arg, attrs)
|
|
||||||
|
|
||||||
attrs['view_config_changes.side_effect'] = [ValueError]
|
|
||||||
self.assertRaises(
|
|
||||||
ValueError, self._call, ['--debug'] + cmd_arg, attrs)
|
|
||||||
|
|
||||||
@mock.patch("letsencrypt.cli.sys")
|
@mock.patch("letsencrypt.cli.sys")
|
||||||
def test_handle_exception(self, mock_sys):
|
def test_handle_exception(self, mock_sys):
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
import StringIO
|
|
||||||
from letsencrypt import cli
|
from letsencrypt import cli
|
||||||
from letsencrypt import errors
|
|
||||||
|
|
||||||
cli._handle_exception(errors.Error, "detail", None, None)
|
mock_open = mock.mock_open()
|
||||||
mock_sys.exit.assert_called_once_with("detail")
|
with mock.patch("letsencrypt.cli.open", mock_open, create=True):
|
||||||
|
exception = Exception("detail")
|
||||||
|
cli._handle_exception(
|
||||||
|
Exception, exc_value=exception, trace=None, args=None)
|
||||||
|
mock_open().write.assert_called_once_with("".join(
|
||||||
|
traceback.format_exception_only(Exception, exception)))
|
||||||
|
error_msg = mock_sys.exit.call_args_list[0][0][0]
|
||||||
|
self.assertTrue("unexpected error" in error_msg)
|
||||||
|
|
||||||
|
with mock.patch("letsencrypt.cli.open", mock_open, create=True):
|
||||||
|
mock_open.side_effect = [KeyboardInterrupt]
|
||||||
|
error = errors.Error("detail")
|
||||||
|
cli._handle_exception(
|
||||||
|
errors.Error, exc_value=error, trace=None, args=None)
|
||||||
|
# assert_any_call used because sys.exit doesn't exit in cli.py
|
||||||
|
mock_sys.exit.assert_any_call("".join(
|
||||||
|
traceback.format_exception_only(errors.Error, error)))
|
||||||
|
|
||||||
args = mock.MagicMock(debug=False)
|
args = mock.MagicMock(debug=False)
|
||||||
cli._handle_exception(ValueError, "detail", None, args)
|
cli._handle_exception(
|
||||||
self.assertTrue("logfile" in mock_sys.exit.call_args_list[1][0][0])
|
Exception, exc_value=Exception("detail"), trace=None, args=args)
|
||||||
|
error_msg = mock_sys.exit.call_args_list[-1][0][0]
|
||||||
|
self.assertTrue("unexpected error" in error_msg)
|
||||||
|
|
||||||
mock_sys.stderr = StringIO.StringIO()
|
interrupt = KeyboardInterrupt("detail")
|
||||||
exc_value = "A very specific string"
|
cli._handle_exception(
|
||||||
cli._handle_exception(KeyboardInterrupt, exc_value, None, None)
|
KeyboardInterrupt, exc_value=interrupt, trace=None, args=None)
|
||||||
self.assertTrue(exc_value in mock_sys.stderr.getvalue())
|
mock_sys.exit.assert_called_with("".join(
|
||||||
|
traceback.format_exception_only(KeyboardInterrupt, interrupt)))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
Reference in New Issue
Block a user