diff options
author | Jason Madden <jamadden@gmail.com> | 2018-09-26 08:20:39 -0500 |
---|---|---|
committer | Jason Madden <jamadden@gmail.com> | 2018-09-26 08:20:39 -0500 |
commit | a8bb58e35a3b71e0da4baab8cec5d3e55c054e90 (patch) | |
tree | f735854016ba00bcce1d1da4077ac3e3fa4d4a8b | |
parent | 13ffadc2f097bc463a597f577fc932ced13fa3b7 (diff) | |
download | zope-configuration-a8bb58e35a3b71e0da4baab8cec5d3e55c054e90.tar.gz |
Stop catching and wrapping BaseException in config and xmlconfig.py
Add new tests for this.
-rw-r--r-- | CHANGES.rst | 5 | ||||
-rw-r--r-- | src/zope/configuration/config.py | 10 | ||||
-rw-r--r-- | src/zope/configuration/tests/test_config.py | 18 | ||||
-rw-r--r-- | src/zope/configuration/tests/test_xmlconfig.py | 138 | ||||
-rw-r--r-- | src/zope/configuration/xmlconfig.py | 18 |
5 files changed, 107 insertions, 82 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index eb5e594..00e185a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -52,6 +52,11 @@ Changes ``ConfigurationExecutionError``. See `issue 10 <https://github.com/zopefoundation/zope.configuration/issues/10>`_. +- Stop catching ``BaseException`` and wrapping it in either + ``ConfigurationExecutionError`` or ``ZopeXMLConfigurationError``. + ``SystemExit`` and ``KeyboardInterrupt`` were always allowed to + propagate; now ``GeneratorExit`` and custom subclasses of + ``BaseException`` are also allowed te propagate. 4.1.0 (2017-04-26) ------------------ diff --git a/src/zope/configuration/config.py b/src/zope/configuration/config.py index 61c37a8..265a528 100644 --- a/src/zope/configuration/config.py +++ b/src/zope/configuration/config.py @@ -333,13 +333,15 @@ class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext): includepath = () info = '' - #: These exceptions are allowed to be raised from `execute_actions` - #: without being re-wrapped into a `ConfigurationExecutionError`. + #: These `Exception` subclasses are allowed to be raised from `execute_actions` + #: without being re-wrapped into a `ConfigurationExecutionError`. (`BaseException` + #: instances are never wrapped.) + #: #: Users of instances of this class may modify this before calling `execute_actions` #: if they need to propagate specific exceptions. #: #: .. versionadded:: 4.2.0 - pass_through_exceptions = (KeyboardInterrupt, SystemExit) + pass_through_exceptions = () def __init__(self): super(ConfigurationMachine, self).__init__() @@ -390,7 +392,7 @@ class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext): callable(*args, **kw) except pass_through_exceptions: raise - except: + except Exception: t, v, tb = sys.exc_info() try: reraise(ConfigurationExecutionError(t, v, info), diff --git a/src/zope/configuration/tests/test_config.py b/src/zope/configuration/tests/test_config.py index 42e7e2b..4bd5930 100644 --- a/src/zope/configuration/tests/test_config.py +++ b/src/zope/configuration/tests/test_config.py @@ -770,11 +770,11 @@ class ConfigurationMachineTests(_ConformsToIConfigurationContext, self.assertEqual(str(exc.exception.evalue), "XXX") self.assertEqual(exc.exception.info, "INFO") - def _check_execute_actions_w_errors_wo_testing(self, ex_kind): + def _check_execute_actions_w_errors_wo_testing(self, ex_kind, cm=None): ex = ex_kind('XXX') def _err(*args, **kw): raise ex - cm = self._makeOne() + cm = cm if cm is not None else self._makeOne() cm.info = 'INFO' cm.action(None, _err) with self.assertRaises(ex_kind) as exc: @@ -790,6 +790,20 @@ class ConfigurationMachineTests(_ConformsToIConfigurationContext, # It gets passed through as-is self._check_execute_actions_w_errors_wo_testing(KeyboardInterrupt) + def test_execute_actions_w_errors_wo_testing_BaseException(self): + # It gets passed through as-is + class Bex(BaseException): + pass + self._check_execute_actions_w_errors_wo_testing(Bex) + + def test_execute_actions_w_errors_custom(self): + # It gets passed through as-is, if we ask it to + class Ex(Exception): + pass + cm = self._makeOne() + cm.pass_through_exceptions += (Ex,) + self._check_execute_actions_w_errors_wo_testing(Ex, cm) + def test_keyword_handling(self): # This is really an integraiton test. from zope.configuration.config import metans diff --git a/src/zope/configuration/tests/test_xmlconfig.py b/src/zope/configuration/tests/test_xmlconfig.py index beafd98..e75a8f0 100644 --- a/src/zope/configuration/tests/test_xmlconfig.py +++ b/src/zope/configuration/tests/test_xmlconfig.py @@ -156,42 +156,62 @@ class ConfigurationHandlerTests(unittest.TestCase): }) self.assertEqual(handler.ignore_depth, 2) - def test_startElementNS_context_begin_raises_wo_testing(self): - from zope.configuration.xmlconfig import ZopeXMLConfigurationError + def _check_elementNS_context_raises(self, raises, catches, + testing=False, + meth='endElementNS', + meth_args=((NS, FOO), FOO)): class ErrorContext(FauxContext): - def begin(self, *args): - raise AttributeError("xxx") + def end(self, *args): + raise raises("xxx") + begin = end + class Info(object): + _line = _col = None + def end(self, line, col): + self._line, self._col = line, col context = ErrorContext() + info = Info() + context.setInfo(info) locator = FauxLocator('tests//sample.zcml', 1, 1) - handler = self._makeOne(context) + handler = self._makeOne(context, testing) handler.setDocumentLocator(locator) - with self.assertRaises(ZopeXMLConfigurationError) as exc: - handler.startElementNS( - (NS, FOO), - FOO, - {(XXX, SPLAT): SPLATV, - (None, A): AVALUE, - (None, B): BVALUE, - }) - self.assertEqual(exc.exception.info.file, 'tests//sample.zcml') - self.assertEqual(exc.exception.info.line, 1) - self.assertEqual(exc.exception.info.column, 1) + locator.line, locator.column = 7, 16 + meth = getattr(handler, meth) + with self.assertRaises(catches) as exc: + meth(*meth_args) + return exc.exception, info + + def _check_startElementNS_context_begin_raises(self, raises, catches, testing=False): + return self._check_elementNS_context_raises( + raises, catches, testing, + meth='startElementNS', + meth_args=((NS, FOO), + FOO, + {(XXX, SPLAT): SPLATV, + (None, A): AVALUE, + (None, B): BVALUE, + }) + ) + + def test_startElementNS_context_begin_raises_wo_testing(self): + from zope.configuration.xmlconfig import ZopeXMLConfigurationError + raised, _ = self._check_startElementNS_context_begin_raises(AttributeError, + ZopeXMLConfigurationError) + info = raised.info + self.assertEqual(info.file, 'tests//sample.zcml') + self.assertEqual(info.line, 7) + self.assertEqual(info.column, 16) def test_startElementNS_context_begin_raises_w_testing(self): - class ErrorContext(FauxContext): - def begin(self, *args): - raise AttributeError("xxx") - context = ErrorContext() - locator = FauxLocator('tests//sample.zcml', 1, 1) - handler = self._makeOne(context, True) - handler.setDocumentLocator(locator) - with self.assertRaises(AttributeError): - handler.startElementNS( - (NS, FOO), FOO, - {(XXX, SPLAT): SPLATV, - (None, A): AVALUE, - (None, B): BVALUE, - }) + self._check_startElementNS_context_begin_raises(AttributeError, + AttributeError, + True) + + def test_startElementNS_context_begin_raises_BaseException(self): + class Bex(BaseException): + pass + self._check_startElementNS_context_begin_raises(Bex, + Bex) + def test_startElementNS_normal(self): # Integration test of startElementNS / endElementNS pair. @@ -222,47 +242,31 @@ class ConfigurationHandlerTests(unittest.TestCase): handler.endElementNS((NS, FOO), FOO) self.assertEqual(handler.ignore_depth, 0) + def _check_endElementNS_context_end_raises(self, raises, catches, testing=False): + return self._check_elementNS_context_raises(raises, catches, testing) + def test_endElementNS_context_end_raises_wo_testing(self): from zope.configuration.xmlconfig import ZopeXMLConfigurationError - class ErrorContext(FauxContext): - def end(self): - raise AttributeError("xxx") - class Info(object): - _line = _col = None - def end(self, line, col): - self._line, self._col = line, col - context = ErrorContext() - info = Info() - context.setInfo(info) - locator = FauxLocator('tests//sample.zcml', 1, 1) - handler = self._makeOne(context) - handler.setDocumentLocator(locator) - locator.line, locator.column = 7, 16 - with self.assertRaises(ZopeXMLConfigurationError) as exc: - handler.endElementNS((NS, FOO), FOO) - self.assertTrue(exc.exception.info is context.info) - self.assertEqual(exc.exception.info._line, 7) - self.assertEqual(exc.exception.info._col, 16) + + raised, info = self._check_endElementNS_context_end_raises(AttributeError, + ZopeXMLConfigurationError) + + self.assertIs(raised.info, info) + self.assertEqual(raised.info._line, 7) + self.assertEqual(raised.info._col, 16) def test_endElementNS_context_end_raises_w_testing(self): - class ErrorContext(FauxContext): - def end(self): - raise AttributeError("xxx") - class Info(object): - _line = _col = None - def end(self, line, col): - self._line, self._col = line, col - context = ErrorContext() - info = Info() - context.setInfo(info) - locator = FauxLocator('tests//sample.zcml', 1, 1) - handler = self._makeOne(context, True) - handler.setDocumentLocator(locator) - locator.line, locator.column = 7, 16 - with self.assertRaises(AttributeError): - handler.endElementNS((NS, FOO), FOO) - self.assertEqual(context.info._line, 7) - self.assertEqual(context.info._col, 16) + _, info = self._check_endElementNS_context_end_raises(AttributeError, + AttributeError, + True) + self.assertEqual(info._line, 7) + self.assertEqual(info._col, 16) + + def test_endElementNS_context_end_raises_BaseException(self): + class Bex(BaseException): + pass + self._check_endElementNS_context_end_raises(Bex, + Bex) def test_evaluateCondition_w_have_no_args(self): context = FauxContext() diff --git a/src/zope/configuration/xmlconfig.py b/src/zope/configuration/xmlconfig.py index d33cd69..24cdcac 100644 --- a/src/zope/configuration/xmlconfig.py +++ b/src/zope/configuration/xmlconfig.py @@ -162,7 +162,11 @@ class ConfigurationHandler(ContentHandler): def __init__(self, context, testing=False): self.context = context - self.testing = testing + # We use this in an `except:` clause. For backwards + # compatibility, ever though this attribute isn't documented, + # we keep it in an attribute named 'testing' which can be interpreted + # as a boolean + self.testing = BaseException if testing else () self.ignore_depth = 0 def setDocumentLocator(self, locator): @@ -201,11 +205,9 @@ class ConfigurationHandler(ContentHandler): try: self.context.begin(name, data, info) - except (KeyboardInterrupt, SystemExit): # pragma: no cover + except self.testing: raise - except: - if self.testing: - raise + except Exception: reraise(ZopeXMLConfigurationError(info, sys.exc_info()[0], sys.exc_info()[1]), @@ -269,11 +271,9 @@ class ConfigurationHandler(ContentHandler): try: self.context.end() - except (KeyboardInterrupt, SystemExit): # pragma: no cover + except self.testing: raise - except: - if self.testing: - raise + except Exception: reraise(ZopeXMLConfigurationError(info, sys.exc_info()[0], sys.exc_info()[1]), |