diff options
author | Robert Collins <rbtcollins@hp.com> | 2015-06-11 09:21:53 +1200 |
---|---|---|
committer | Stuart McLaren <stuart.mclaren@hp.com> | 2015-06-11 18:11:39 +0000 |
commit | 43621dc1ac626f7ce3322cee8b7908a93af20f26 (patch) | |
tree | efe52d9b57f79f90035462bc11c8767a831697aa | |
parent | db6420b44779411d6d1f238f6b887f83f1988986 (diff) | |
download | python-glanceclient-43621dc1ac626f7ce3322cee8b7908a93af20f26.tar.gz |
Close iterables at the end of iteration
This fixes a bug where if iteration is interrupted, we're stuck until
the iterable is garbage collected, which can be a very long time (e.g. if
the iterable is held in an exception stack frame).
Co-authored-by: Stuart McLaren <stuart.mclaren@hp.com>
Change-Id: Ibe9990e8c337c117a978b1cd8ec388c4bc6d3b4b
Closes-bug: 1461678
-rw-r--r-- | glanceclient/common/utils.py | 6 | ||||
-rw-r--r-- | glanceclient/tests/unit/test_utils.py | 13 |
2 files changed, 18 insertions, 1 deletions
diff --git a/glanceclient/common/utils.py b/glanceclient/common/utils.py index b199bf3..bba0cab 100644 --- a/glanceclient/common/utils.py +++ b/glanceclient/common/utils.py @@ -434,7 +434,11 @@ class IterableWithLength(object): self.length = length def __iter__(self): - return self.iterable + try: + for chunk in self.iterable: + yield chunk + finally: + self.iterable.close() def next(self): return next(self.iterable) diff --git a/glanceclient/tests/unit/test_utils.py b/glanceclient/tests/unit/test_utils.py index 564d7f6..e2c1780 100644 --- a/glanceclient/tests/unit/test_utils.py +++ b/glanceclient/tests/unit/test_utils.py @@ -15,6 +15,7 @@ import sys +import mock import six # NOTE(jokke): simplified transition to py3, behaves like py2 xrange from six.moves import range @@ -163,3 +164,15 @@ class TestUtils(testtools.TestCase): self.assertIn('--test', arg) self.assertEqual(str, opts['type']) self.assertIn('None, opt-1, opt-2', opts['help']) + + def test_iterable_closes(self): + # Regression test for bug 1461678. + def _iterate(i): + for chunk in i: + raise(IOError) + + data = six.moves.StringIO('somestring') + data.close = mock.Mock() + i = utils.IterableWithLength(data, 10) + self.assertRaises(IOError, _iterate, i) + data.close.assert_called_with() |