summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Collins <rbtcollins@hp.com>2015-06-11 09:21:53 +1200
committerStuart McLaren <stuart.mclaren@hp.com>2015-06-11 18:11:39 +0000
commit43621dc1ac626f7ce3322cee8b7908a93af20f26 (patch)
treeefe52d9b57f79f90035462bc11c8767a831697aa
parentdb6420b44779411d6d1f238f6b887f83f1988986 (diff)
downloadpython-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.py6
-rw-r--r--glanceclient/tests/unit/test_utils.py13
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()