Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow empty files to be uploaded. #25

Merged
merged 2 commits into from
Aug 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions google/resumable_media/_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ def _prepare_request(self):
u'This upload has not been initiated. Please call '
u'initiate() before beginning to transmit chunks.')

start_byte, end_byte, payload, content_range = get_next_chunk(
start_byte, payload, content_range = get_next_chunk(
self._stream, self._chunk_size, self._total_bytes)
if start_byte != self.bytes_uploaded:
msg = _STREAM_ERROR_TEMPLATE.format(
Expand Down Expand Up @@ -784,14 +784,15 @@ def get_next_chunk(stream, chunk_size, total_bytes):
in the ``stream``.

Returns:
Tuple[int, int, bytes, str]: Quadruple of:
Tuple[int, bytes, str]: Triple of:

* the start byte index
* the end byte index
* the content in between the start and end bytes (inclusive)
* content range header for the chunk (slice) that has been read

Raises:
ValueError: If ``total_bytes == 0`` but ``stream.read()`` yields
non-empty content.
ValueError: If there is no data left to consume. This corresponds
exactly to the case ``end_byte < start_byte``, which can only
occur if ``end_byte == start_byte - 1``.
Expand All @@ -807,6 +808,13 @@ def get_next_chunk(stream, chunk_size, total_bytes):
if num_bytes_read < chunk_size:
# We now **KNOW** the total number of bytes.
total_bytes = end_byte + 1
elif total_bytes == 0:
# NOTE: We also expect ``start_byte == 0`` here but don't check
# because ``_prepare_initiate_request()`` requires the
# stream to be at the beginning.
if num_bytes_read != 0:
raise ValueError(
u'Stream specified as empty, but produced non-empty content.')

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

else:
if num_bytes_read == 0:
raise ValueError(
Expand All @@ -817,7 +825,7 @@ def get_next_chunk(stream, chunk_size, total_bytes):
raise ValueError(msg)

content_range = get_content_range(start_byte, end_byte, total_bytes)
return start_byte, end_byte, payload, content_range
return start_byte, payload, content_range


def get_content_range(start_byte, end_byte, total_bytes):
Expand Down
29 changes: 21 additions & 8 deletions tests/unit/test__upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,19 @@ def test_exhausted_known_size(self):
exc_info.match(
u'Stream is already exhausted. There is no content remaining.')

def test_exhausted_known_size_zero(self):
stream = io.BytesIO(b'')
answer = _upload.get_next_chunk(stream, 1, 0)
assert answer == (0, b'', 'bytes */0')

def test_exhausted_known_size_zero_nonempty(self):
stream = io.BytesIO(b'not empty WAT!')
with pytest.raises(ValueError) as exc_info:
_upload.get_next_chunk(stream, 1, 0)

exc_info.match(
u'Stream specified as empty, but produced non-empty content.')

def test_read_past_known_size(self):
data = b'more content than we expected'
stream = io.BytesIO(data)
Expand All @@ -882,10 +895,10 @@ def test_success_known_size(self):
result1 = _upload.get_next_chunk(stream, chunk_size, total_bytes)
result2 = _upload.get_next_chunk(stream, chunk_size, total_bytes)
result3 = _upload.get_next_chunk(stream, chunk_size, total_bytes)
assert result0 == (0, 2, b'012', u'bytes 0-2/10')
assert result1 == (3, 5, b'345', u'bytes 3-5/10')
assert result2 == (6, 8, b'678', u'bytes 6-8/10')
assert result3 == (9, 9, b'9', u'bytes 9-9/10')
assert result0 == (0, b'012', u'bytes 0-2/10')
assert result1 == (3, b'345', u'bytes 3-5/10')
assert result2 == (6, b'678', u'bytes 6-8/10')
assert result3 == (9, b'9', u'bytes 9-9/10')
assert stream.tell() == total_bytes

def test_success_unknown_size(self):
Expand All @@ -895,8 +908,8 @@ def test_success_unknown_size(self):
# Splits into 4 chunks: abcdef, ghij
result0 = _upload.get_next_chunk(stream, chunk_size, None)
result1 = _upload.get_next_chunk(stream, chunk_size, None)
assert result0 == (0, chunk_size - 1, b'abcdef', u'bytes 0-5/*')
assert result1 == (chunk_size, len(data) - 1, b'ghij', u'bytes 6-9/10')
assert result0 == (0, b'abcdef', u'bytes 0-5/*')
assert result1 == (chunk_size, b'ghij', u'bytes 6-9/10')
assert stream.tell() == len(data)

# Do the same when the chunk size evenly divides len(data)
Expand All @@ -905,8 +918,8 @@ def test_success_unknown_size(self):
# Splits into 2 chunks: `data` and empty string
result0 = _upload.get_next_chunk(stream, chunk_size, None)
result1 = _upload.get_next_chunk(stream, chunk_size, None)
assert result0 == (0, len(data) - 1, data, u'bytes 0-9/*')
assert result1 == (len(data), len(data) - 1, b'', u'bytes */10')
assert result0 == (0, data, u'bytes 0-9/*')
assert result1 == (len(data), b'', u'bytes */10')
assert stream.tell() == len(data)


Expand Down