Skip to content
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
1 change: 1 addition & 0 deletions azure-storage-blob/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## Version XX.XX.XX:

- Fixed bug where get_blob_to_* cannot get a single byte when start_range and end_range are both equal to 0.
- Optimized page blob upload for create_blob_from_* methods, by skipping the empty chunks.
- Added convenient method to generate container url (make_container_url).
- The package has switched from Apache 2.0 to the MIT license.
Expand Down
12 changes: 6 additions & 6 deletions azure-storage-blob/azure/storage/blob/baseblobservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -1928,9 +1928,9 @@ def get_blob_to_stream(
# chunk so a transactional MD5 can be retrieved.
first_get_size = self.MAX_SINGLE_GET_SIZE if not validate_content else self.MAX_CHUNK_GET_SIZE

initial_request_start = start_range if start_range else 0
initial_request_start = start_range if start_range is not None else 0

if end_range and end_range - start_range < first_get_size:
if end_range is not None and end_range - start_range < first_get_size:
initial_request_end = end_range
else:
initial_request_end = initial_request_start + first_get_size - 1
Expand All @@ -1955,15 +1955,15 @@ def get_blob_to_stream(
# Parse the total blob size and adjust the download size if ranges
# were specified
blob_size = _parse_length_from_content_range(blob.properties.content_range)
if end_range:
if end_range is not None:
# Use the end_range unless it is over the end of the blob
download_size = min(blob_size, end_range - start_range + 1)
elif start_range:
elif start_range is not None:
download_size = blob_size - start_range
else:
download_size = blob_size
except AzureHttpError as ex:
if not start_range and ex.status_code == 416:
if start_range is None and ex.status_code == 416:
# Get range will fail on an empty blob. If the user did not
# request a range, do a regular get request in order to get
# any properties.
Expand Down Expand Up @@ -2002,7 +2002,7 @@ def get_blob_to_stream(
if_match = if_match if if_match is not None else blob.properties.etag

end_blob = blob_size
if end_range:
if end_range is not None:
# Use the end_range unless it is over the end of the blob
end_blob = min(blob_size, end_range + 1)

Expand Down
3 changes: 2 additions & 1 deletion azure-storage-file/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@

## Version XX.XX.XX:

- The package has switched from Apache 2.0 to the MIT license.
- The package has switched from Apache 2.0 to the MIT license.
- Fixed bug where get_file_to_* cannot get a single byte when start_range and end_range are both equal to 0.
10 changes: 5 additions & 5 deletions azure-storage-file/azure/storage/file/fileservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -2049,7 +2049,7 @@ def get_file_to_stream(
# chunk so a transactional MD5 can be retrieved.
first_get_size = self.MAX_SINGLE_GET_SIZE if not validate_content else self.MAX_CHUNK_GET_SIZE

initial_request_start = start_range if start_range else 0
initial_request_start = start_range if start_range is not None else 0

if end_range is not None and end_range - start_range < first_get_size:
initial_request_end = end_range
Expand All @@ -2072,15 +2072,15 @@ def get_file_to_stream(
# Parse the total file size and adjust the download size if ranges
# were specified
file_size = _parse_length_from_content_range(file.properties.content_range)
if end_range:
if end_range is not None:
# Use the end_range unless it is over the end of the file
download_size = min(file_size, end_range - start_range + 1)
elif start_range:
elif start_range is not None:
download_size = file_size - start_range
else:
download_size = file_size
except AzureHttpError as ex:
if not start_range and ex.status_code == 416:
if start_range is None and ex.status_code == 416:
# Get range will fail on an empty file. If the user did not
# request a range, do a regular get request in order to get
# any properties.
Expand Down Expand Up @@ -2116,7 +2116,7 @@ def get_file_to_stream(
# this feature is not yet available on the file service.

end_file = file_size
if end_range:
if end_range is not None:
# Use the end_range unless it is over the end of the file
end_file = min(file_size, end_range + 1)

Expand Down
37 changes: 37 additions & 0 deletions tests/blob/test_get_blob.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import os
import unittest

from azure.common import AzureHttpError

from azure.storage.blob import (
Blob,
BlockBlobService,
Expand Down Expand Up @@ -138,6 +140,41 @@ def test_get_blob_to_bytes(self):
# Assert
self.assertEqual(self.byte_data, blob.content)

def test_ranged_get_blob_to_bytes_with_single_byte(self):
# parallel tests introduce random order of requests, can only run live
if TestMode.need_recording_file(self.test_mode):
return

# Arrange

# Act
blob = self.bs.get_blob_to_bytes(self.container_name, self.byte_blob, start_range=0, end_range=0)

# Assert
self.assertEqual(1, len(blob.content))
self.assertEqual(self.byte_data[0], blob.content[0])

# Act
blob = self.bs.get_blob_to_bytes(self.container_name, self.byte_blob, start_range=5, end_range=5)

# Assert
self.assertEqual(1, len(blob.content))
self.assertEqual(self.byte_data[5], blob.content[0])

@record
def test_ranged_get_blob_to_bytes_with_zero_byte(self):
blob_data = b''
blob_name = self._get_blob_reference()
self.bs.create_blob_from_bytes(self.container_name, blob_name, blob_data)

# Act
# the get request should fail in this case since the blob is empty and yet there is a range specified
with self.assertRaises(AzureHttpError):
self.bs.get_blob_to_bytes(self.container_name, blob_name, start_range=0, end_range=5)

with self.assertRaises(AzureHttpError):
self.bs.get_blob_to_bytes(self.container_name, blob_name, start_range=3, end_range=5)

def test_get_blob_to_bytes_snapshot(self):
# parallel tests introduce random order of requests, can only run live
if TestMode.need_recording_file(self.test_mode):
Expand Down
36 changes: 36 additions & 0 deletions tests/file/test_get_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import os
import unittest

from azure.common import AzureHttpError

from azure.storage.file import (
File,
FileService,
Expand Down Expand Up @@ -486,6 +488,40 @@ def test_ranged_get_file_to_path(self):
actual = stream.read()
self.assertEqual(self.byte_data[1:end_range + 1], actual)

def test_ranged_get_file_to_path_with_single_byte(self):
# parallel tests introduce random order of requests, can only run live
if TestMode.need_recording_file(self.test_mode):
return

# Arrange

# Act
end_range = self.fs.MAX_SINGLE_GET_SIZE + 1024
file = self.fs.get_file_to_path(self.share_name, self.directory_name, self.byte_file, FILE_PATH,
start_range=0, end_range=0)

# Assert
self.assertIsInstance(file, File)
with open(FILE_PATH, 'rb') as stream:
actual = stream.read()
self.assertEqual(1, len(actual))
self.assertEqual(self.byte_data[0], actual[0])

@record
def test_ranged_get_file_to_bytes_with_zero_byte(self):
# Arrange
file_data = b''
file_name = self._get_file_reference()
self.fs.create_file_from_bytes(self.share_name, self.directory_name, file_name, file_data)

# Act
# the get request should fail in this case since the blob is empty and yet there is a range specified
with self.assertRaises(AzureHttpError):
self.fs.get_file_to_bytes(self.share_name, self.directory_name, file_name, start_range=0, end_range=5)

with self.assertRaises(AzureHttpError):
self.fs.get_file_to_bytes(self.share_name, self.directory_name, file_name, start_range=3, end_range=5)

def test_ranged_get_file_to_path_with_progress(self):
# parallel tests introduce random order of requests, can only run live
if TestMode.need_recording_file(self.test_mode):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
interactions:
- request:
body: null
headers:
Connection: [keep-alive]
Content-Length: ['0']
User-Agent: [Azure-Storage/0.37.1-0.37.1 (Python CPython 3.6.3; Darwin 17.3.0)]
x-ms-blob-type: [BlockBlob]
x-ms-client-request-id: [65cb5834-f77c-11e7-aad8-b8e8564491f6]
x-ms-date: ['Fri, 12 Jan 2018 09:38:51 GMT']
x-ms-version: ['2017-04-17']
method: PUT
uri: https://storagename.blob.core.windows.net/utcontainerb88c17ce/blobb88c17ce
response:
body: {string: ''}
headers:
Content-MD5: [1B2M2Y8AsgTpgAmY7PhCfg==]
Date: ['Fri, 12 Jan 2018 09:38:51 GMT']
ETag: ['"0x8D559A04A561E06"']
Last-Modified: ['Fri, 12 Jan 2018 09:38:51 GMT']
Server: [Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0]
Transfer-Encoding: [chunked]
x-ms-request-id: [d30d9473-a001-0030-2789-8b2cf4000000]
x-ms-request-server-encrypted: ['true']
x-ms-version: ['2017-04-17']
status: {code: 201, message: Created}
- request:
body: null
headers:
Connection: [keep-alive]
User-Agent: [Azure-Storage/0.37.1-0.37.1 (Python CPython 3.6.3; Darwin 17.3.0)]
x-ms-client-request-id: [6656c728-f77c-11e7-b698-b8e8564491f6]
x-ms-date: ['Fri, 12 Jan 2018 09:38:52 GMT']
x-ms-range: [bytes=0-5]
x-ms-version: ['2017-04-17']
method: GET
uri: https://storagename.blob.core.windows.net/utcontainerb88c17ce/blobb88c17ce
response:
body: {string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>InvalidRange</Code><Message>The\
\ range specified is invalid for the current size of the resource.\nRequestId:d30d947a-a001-0030-2d89-8b2cf4000000\n\
Time:2018-01-12T09:38:51.8033558Z</Message></Error>"}
headers:
Content-Length: ['249']
Content-Range: [bytes */0]
Content-Type: [application/xml]
Date: ['Fri, 12 Jan 2018 09:38:51 GMT']
Server: [Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0]
Vary: [Origin]
x-ms-request-id: [d30d947a-a001-0030-2d89-8b2cf4000000]
x-ms-version: ['2017-04-17']
status: {code: 416, message: The range specified is invalid for the current size
of the resource.}
- request:
body: null
headers:
Connection: [keep-alive]
User-Agent: [Azure-Storage/0.37.1-0.37.1 (Python CPython 3.6.3; Darwin 17.3.0)]
x-ms-client-request-id: [66703582-f77c-11e7-b973-b8e8564491f6]
x-ms-date: ['Fri, 12 Jan 2018 09:38:52 GMT']
x-ms-range: [bytes=3-5]
x-ms-version: ['2017-04-17']
method: GET
uri: https://storagename.blob.core.windows.net/utcontainerb88c17ce/blobb88c17ce
response:
body: {string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>InvalidRange</Code><Message>The\
\ range specified is invalid for the current size of the resource.\nRequestId:d30d9480-a001-0030-3289-8b2cf4000000\n\
Time:2018-01-12T09:38:51.9733628Z</Message></Error>"}
headers:
Content-Length: ['249']
Content-Range: [bytes */0]
Content-Type: [application/xml]
Date: ['Fri, 12 Jan 2018 09:38:51 GMT']
Server: [Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0]
Vary: [Origin]
x-ms-request-id: [d30d9480-a001-0030-3289-8b2cf4000000]
x-ms-version: ['2017-04-17']
status: {code: 416, message: The range specified is invalid for the current size
of the resource.}
version: 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
interactions:
- request:
body: null
headers:
Connection: [keep-alive]
Content-Length: ['0']
User-Agent: [Azure-Storage/0.37.1-0.37.0 (Python CPython 3.6.3; Darwin 17.3.0)]
x-ms-client-request-id: [138eed9a-f77c-11e7-9bfc-b8e8564491f6]
x-ms-content-length: ['0']
x-ms-date: ['Fri, 12 Jan 2018 09:36:33 GMT']
x-ms-type: [file]
x-ms-version: ['2017-04-17']
method: PUT
uri: https://storagename.file.core.windows.net/utshareb8d917d0/utdirb8d917d0/fileb8d917d0
response:
body: {string: ''}
headers:
Date: ['Fri, 12 Jan 2018 09:36:33 GMT']
ETag: ['"0x8D5599FF7EC0F50"']
Last-Modified: ['Fri, 12 Jan 2018 09:36:33 GMT']
Server: [Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0]
Transfer-Encoding: [chunked]
x-ms-request-id: [d1d94f62-001a-0029-3788-8bac4f000000]
x-ms-request-server-encrypted: ['true']
x-ms-version: ['2017-04-17']
status: {code: 201, message: Created}
- request:
body: null
headers:
Connection: [keep-alive]
User-Agent: [Azure-Storage/0.37.1-0.37.0 (Python CPython 3.6.3; Darwin 17.3.0)]
x-ms-client-request-id: [13f43aec-f77c-11e7-8381-b8e8564491f6]
x-ms-date: ['Fri, 12 Jan 2018 09:36:34 GMT']
x-ms-range: [bytes=0-5]
x-ms-version: ['2017-04-17']
method: GET
uri: https://storagename.file.core.windows.net/utshareb8d917d0/utdirb8d917d0/fileb8d917d0
response:
body: {string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>InvalidRange</Code><Message>The\
\ range specified is invalid for the current size of the resource.\nRequestId:d1d94f64-001a-0029-3888-8bac4f000000\n\
Time:2018-01-12T09:36:34.0437990Z</Message></Error>"}
headers:
Content-Length: ['249']
Content-Range: [bytes */0]
Content-Type: [application/xml]
Date: ['Fri, 12 Jan 2018 09:36:33 GMT']
Server: [Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0]
x-ms-request-id: [d1d94f64-001a-0029-3888-8bac4f000000]
x-ms-version: ['2017-04-17']
status: {code: 416, message: The range specified is invalid for the current size
of the resource.}
- request:
body: null
headers:
Connection: [keep-alive]
User-Agent: [Azure-Storage/0.37.1-0.37.0 (Python CPython 3.6.3; Darwin 17.3.0)]
x-ms-client-request-id: [140efa9e-f77c-11e7-b033-b8e8564491f6]
x-ms-date: ['Fri, 12 Jan 2018 09:36:34 GMT']
x-ms-range: [bytes=3-5]
x-ms-version: ['2017-04-17']
method: GET
uri: https://storagename.file.core.windows.net/utshareb8d917d0/utdirb8d917d0/fileb8d917d0
response:
body: {string: "\uFEFF<?xml version=\"1.0\" encoding=\"utf-8\"?><Error><Code>InvalidRange</Code><Message>The\
\ range specified is invalid for the current size of the resource.\nRequestId:d1d94f66-001a-0029-3988-8bac4f000000\n\
Time:2018-01-12T09:36:34.1857814Z</Message></Error>"}
headers:
Content-Length: ['249']
Content-Range: [bytes */0]
Content-Type: [application/xml]
Date: ['Fri, 12 Jan 2018 09:36:34 GMT']
Server: [Windows-Azure-File/1.0 Microsoft-HTTPAPI/2.0]
x-ms-request-id: [d1d94f66-001a-0029-3988-8bac4f000000]
x-ms-version: ['2017-04-17']
status: {code: 416, message: The range specified is invalid for the current size
of the resource.}
version: 1