Handle BaseException resource leaks as well.

The change to handle resource leaks incorrectly ignored BaseException
- e.g. KeyboardInterrupt.
This commit is contained in:
Robert Collins 2015-06-30 12:58:56 +12:00
parent 5355689a2a
commit 3f965dd8a9
5 changed files with 36 additions and 4 deletions

4
NEWS
View File

@ -6,6 +6,10 @@ fixtures release notes
NEXT
~~~~
* ``Fixture.setUp`` now uses a bare except: and will thus catch BaseException.
Any non-Exception-subclass errors are raised verbatim after calling
``cleanUp``, to avoid inappropriate masking in callers. (Robert Collins)
1.3.0
~~~~~

7
README
View File

@ -235,6 +235,13 @@ the error. As long as you take care to register any cleanups before calling
the code that may fail, this will cause them to be cleaned up. The captured
detail objects are provided to the args of the raised exception.
If the error that occured was a subclass of ``Exception`` then ``setUp`` will
raise ``MultipleExceptions`` with the last element being a ``SetupError`` that
contains the detail objects. Otherwise, to prevent causing normally
uncatchable errors like KeyboardInterrupt being caught inappropriately in the
calling layer, the original exception will be raised as-is and no diagnostic
data other than that from the original exception will be available.
Shared Dependencies
+++++++++++++++++++

View File

@ -24,6 +24,7 @@ __all__ = [
import itertools
import sys
import six
from testtools.compat import (
advance_iterator,
reraise,
@ -174,9 +175,8 @@ class Fixture(object):
def setUp(self):
"""Prepare the Fixture for use.
This should not be overridden.
Concrete fixtures should implement _setUp.
This should not be overridden. Concrete fixtures should implement
_setUp. Overriding of setUp is still supported, just not recommended.
After setUp has completed, the fixture will have one or more attributes
which can be used (these depend totally on the concrete subclass).
@ -189,11 +189,13 @@ class Fixture(object):
:changed in 1.3: The recommendation to override setUp has been
reversed - before 1.3, setUp() should be overridden, now it should
not be.
:changed in 1.3.1: BaseException is now caught, and only subclasses of
Exception are wrapped in MultipleExceptions.
"""
self._clear_cleanups()
try:
self._setUp()
except Exception:
except:
err = sys.exc_info()
details = {}
if gather_details is not None:
@ -206,7 +208,10 @@ class Fixture(object):
raise SetupError(details)
except SetupError as e:
errors.append(sys.exc_info())
if issubclass(err[0], Exception):
raise MultipleExceptions(*errors)
else:
six.reraise(*err)
def _setUp(self):
"""Template method for subclasses to override.

View File

@ -257,6 +257,21 @@ class TestFixture(testtools.TestCase):
self.assertEqual(fixtures.SetupError, e.args[2][0])
self.assertEqual('stuff', e.args[2][1].args[0]['log'].as_text())
def test_setup_failures_with_base_exception(self):
# when _setUp fails with a BaseException (or subclass thereof) that
# exception is propogated as is, but we still call cleanups etc.
class MyBase(BaseException):pass
log = []
class Subclass(fixtures.Fixture):
def _setUp(self):
self.addDetail('log', text_content('stuff'))
self.addCleanup(log.append, 'cleaned')
raise MyBase('fred')
f = Subclass()
e = self.assertRaises(MyBase, f.setUp)
self.assertRaises(TypeError, f.cleanUp)
self.assertEqual(['cleaned'], log)
class TestFunctionFixture(testtools.TestCase):

View File

@ -1,2 +1,3 @@
pbr>=0.11
six
testtools>=0.9.22