Skip to content

Commit ab21265

Browse files
kragnizblkart
authored andcommitted
Fix Requests breaking download progress bar
The move to the requests library (dbb242b) broke the progress bar during downloading an image with --progress enabled, due to requests returning a generator, which has no __len__ function. This patch adds an iterable wrapper which provides the length, sourced from Content-Length headers. Closes-Bug: #1384664 Change-Id: I48598a824396bc6e7cba69eb3ce32e7c8f30a18a Signed-off-by: blkart <blkart.org@gmail.com>
1 parent 385d97e commit ab21265

File tree

4 files changed

+36
-5
lines changed

4 files changed

+36
-5
lines changed

glanceclient/common/utils.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,18 @@ def safe_header(name, value):
421421
return name, "{SHA1}%s" % d
422422
else:
423423
return name, value
424+
425+
426+
class IterableWithLength(object):
427+
def __init__(self, iterable, length):
428+
self.iterable = iterable
429+
self.length = length
430+
431+
def __iter__(self):
432+
return self.iterable
433+
434+
def next(self):
435+
return next(self.iterable)
436+
437+
def __len__(self):
438+
return self.length

glanceclient/v1/images.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,15 @@ def data(self, image, do_checksum=True, **kwargs):
140140
image_id = base.getid(image)
141141
resp, body = self.client.get('/v1/images/%s'
142142
% urlparse.quote(str(image_id)))
143+
content_length = int(resp.headers.get('content-length', 0))
143144
checksum = resp.headers.get('x-image-meta-checksum', None)
144145
if do_checksum and checksum is not None:
145-
return utils.integrity_iter(body, checksum)
146+
body = utils.integrity_iter(body, checksum)
146147
return_request_id = kwargs.get('return_req_id', None)
147148
if return_request_id is not None:
148149
return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None))
149150

150-
return body
151+
return utils.IterableWithLength(body, content_length)
151152

152153
def list(self, **kwargs):
153154
"""Get a list of images.

glanceclient/v2/images.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,12 @@ def data(self, image_id, do_checksum=True):
115115
url = '/v2/images/%s/file' % image_id
116116
resp, body = self.http_client.get(url)
117117
checksum = resp.headers.get('content-md5', None)
118+
content_length = int(resp.headers.get('content-length', 0))
119+
118120
if do_checksum and checksum is not None:
119-
return utils.integrity_iter(body, checksum)
120-
else:
121-
return body
121+
body = utils.integrity_iter(body, checksum)
122+
123+
return utils.IterableWithLength(body, content_length)
122124

123125
def upload(self, image_id, image_data, image_size=None):
124126
"""

tests/v2/test_shell_v2.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,19 @@ def test_image_upload(self):
341341
test_shell.do_image_upload(self.gc, args)
342342
mocked_upload.assert_called_once_with('IMG-01', 'testfile', 1024)
343343

344+
def test_image_download(self):
345+
args = self._make_args(
346+
{'id': 'IMG-01', 'file': 'test', 'progress': True})
347+
348+
with mock.patch.object(self.gc.images, 'data') as mocked_data:
349+
def _data():
350+
for c in 'abcedf':
351+
yield c
352+
mocked_data.return_value = utils.IterableWithLength(_data(), 5)
353+
354+
test_shell.do_image_download(self.gc, args)
355+
mocked_data.assert_called_once_with('IMG-01')
356+
344357
def test_do_image_delete(self):
345358
args = self._make_args({'id': 'pass', 'file': 'test'})
346359
with mock.patch.object(self.gc.images, 'delete') as mocked_delete:

0 commit comments

Comments
 (0)