Skip to content

Commit

Permalink
Merge pull request #467 from Azure/dev
Browse files Browse the repository at this point in the history
March 18 service release
  • Loading branch information
zezha-msft authored Jun 26, 2018
2 parents 22bebfe + 86eec8b commit ff51954
Show file tree
Hide file tree
Showing 602 changed files with 24,290 additions and 40,381 deletions.
2 changes: 1 addition & 1 deletion BreakingChanges.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> See the [Change Log](ChangeLog.md) for a summary of storage library changes.
**Note: This changelog is deprecated starting with version XX.XX.XX, please refer to the ChangeLog.md in each package for future change logs.**
**Note: This changelog is deprecated starting with version 0.37.0, please refer to the ChangeLog.md in each package for future change logs.**

## Version 0.37.0:

Expand Down
7 changes: 7 additions & 0 deletions azure-storage-blob/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

> See [BreakingChanges](BreakingChanges.md) for a detailed list of API breaks.
## Version 1.3.0:

- Support for 2018-03-28 REST version. Please see our REST API documentation and blog for information about the related added features.
- Added support for setting static website service properties.
- Added support for getting account information, such as SKU name and account kind.
- Added support for put block from URL(synchronously).

## Version 1.2.0rc1:

- Support for 2017-11-09 REST version. Please see our REST API documentation and blog for information about the related added features.
Expand Down
4 changes: 2 additions & 2 deletions azure-storage-blob/azure/storage/blob/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
# --------------------------------------------------------------------------

__author__ = 'Microsoft Corp. <[email protected]>'
__version__ = '1.2.0rc1'
__version__ = '1.3.0'

# x-ms-version for storage service.
X_MS_VERSION = '2017-11-09'
X_MS_VERSION = '2018-03-28'

# internal configurations, should not be changed
_LARGE_BLOB_UPLOAD_MAX_READ_BUFFER_SIZE = 4 * 1024 * 1024
9 changes: 9 additions & 0 deletions azure-storage-blob/azure/storage/blob/_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
PageBlobProperties,
ResourceProperties,
BlobPrefix,
AccountInformation,
)
from ._encryption import _decrypt_blob
from azure.storage.common.models import _list
Expand Down Expand Up @@ -440,3 +441,11 @@ def _convert_xml_to_page_ranges(response):
)

return page_list


def _parse_account_information(response):
account_info = AccountInformation()
account_info.sku_name = response.headers['x-ms-sku-name']
account_info.account_kind = response.headers['x-ms-account-kind']

return account_info
64 changes: 56 additions & 8 deletions azure-storage-blob/azure/storage/blob/baseblobservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
_parse_lease,
_convert_xml_to_signed_identifiers_and_access,
_parse_base_properties,
_parse_account_information,
)
from ._download_chunking import _download_blob_chunks
from ._error import (
Expand All @@ -85,6 +86,10 @@
__version__ as package_version,
)

_CONTAINER_ALREADY_EXISTS_ERROR_CODE = 'ContainerAlreadyExists'
_BLOB_NOT_FOUND_ERROR_CODE = 'BlobNotFound'
_CONTAINER_NOT_FOUND_ERROR_CODE = 'ContainerNotFound'

if sys.version_info >= (3,):
from io import BytesIO
else:
Expand Down Expand Up @@ -622,7 +627,7 @@ def create_container(self, container_name, metadata=None,

if not fail_on_exist:
try:
self._perform_request(request)
self._perform_request(request, expected_errors=[_CONTAINER_ALREADY_EXISTS_ERROR_CODE])
return True
except AzureHttpError as ex:
_dont_fail_on_exist(ex)
Expand Down Expand Up @@ -873,7 +878,7 @@ def delete_container(self, container_name, fail_not_exist=False,

if not fail_not_exist:
try:
self._perform_request(request)
self._perform_request(request, expected_errors=[_CONTAINER_NOT_FOUND_ERROR_CODE])
return True
except AzureHttpError as ex:
_dont_fail_not_exist(ex)
Expand Down Expand Up @@ -1316,6 +1321,33 @@ def _list_blobs(self, container_name, prefix=None, marker=None,

return self._perform_request(request, _convert_xml_to_blob_list, operation_context=_context)

def get_blob_account_information(self, container_name=None, blob_name=None, timeout=None):
"""
Gets information related to the storage account.
The information can also be retrieved if the user has a SAS to a container or blob.
:param str container_name:
Name of existing container.
Optional, unless using a SAS token to a specific container or blob, in which case it's required.
:param str blob_name:
Name of existing blob.
Optional, unless using a SAS token to a specific blob, in which case it's required.
:param int timeout:
The timeout parameter is expressed in seconds.
:return: The :class:`~azure.storage.blob.models.AccountInformation`.
"""
request = HTTPRequest()
request.method = 'HEAD'
request.host_locations = self._get_host_locations(secondary=True)
request.path = _get_path(container_name, blob_name)
request.query = {
'restype': 'account',
'comp': 'properties',
'timeout': _int_to_str(timeout),
}

return self._perform_request(request, _parse_account_information)

def get_blob_service_stats(self, timeout=None):
'''
Retrieves statistics related to replication for the Blob service. It is
Expand Down Expand Up @@ -1354,7 +1386,7 @@ def get_blob_service_stats(self, timeout=None):

def set_blob_service_properties(
self, logging=None, hour_metrics=None, minute_metrics=None,
cors=None, target_version=None, timeout=None, delete_retention_policy=None):
cors=None, target_version=None, timeout=None, delete_retention_policy=None, static_website=None):
'''
Sets the properties of a storage account's Blob service, including
Azure Storage Analytics. If an element (ex Logging) is left as None, the
Expand Down Expand Up @@ -1389,6 +1421,11 @@ def set_blob_service_properties(
It also specifies the number of days and versions of blob to keep.
:type delete_retention_policy:
:class:`~azure.storage.common.models.DeleteRetentionPolicy`
:param static_website:
Specifies whether the static website feature is enabled,
and if yes, indicates the index document and 404 error document to use.
:type static_website:
:class:`~azure.storage.common.models.StaticWebsite`
'''
request = HTTPRequest()
request.method = 'PUT'
Expand All @@ -1401,7 +1438,7 @@ def set_blob_service_properties(
}
request.body = _get_request_body(
_convert_service_properties_to_xml(logging, hour_metrics, minute_metrics,
cors, target_version, delete_retention_policy))
cors, target_version, delete_retention_policy, static_website))

self._perform_request(request)

Expand Down Expand Up @@ -1575,10 +1612,21 @@ def exists(self, container_name, blob_name=None, snapshot=None, timeout=None):
'''
_validate_not_none('container_name', container_name)
try:
if blob_name is None:
self.get_container_properties(container_name, timeout=timeout)
else:
self.get_blob_properties(container_name, blob_name, snapshot=snapshot, timeout=timeout)
# make head request to see if container/blob/snapshot exists
request = HTTPRequest()
request.method = 'GET' if blob_name is None else 'HEAD'
request.host_locations = self._get_host_locations(secondary=True)
request.path = _get_path(container_name, blob_name)
request.query = {
'snapshot': _to_str(snapshot),
'timeout': _int_to_str(timeout),
'restype': 'container' if blob_name is None else None,
}

expected_errors = [_CONTAINER_NOT_FOUND_ERROR_CODE] if blob_name is None \
else [_CONTAINER_NOT_FOUND_ERROR_CODE, _BLOB_NOT_FOUND_ERROR_CODE]
self._perform_request(request, expected_errors=expected_errors)

return True
except AzureHttpError as ex:
_dont_fail_not_exist(ex)
Expand Down
59 changes: 57 additions & 2 deletions azure-storage-blob/azure/storage/blob/blockblobservice.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def put_block(self, container_name, blob_name, block, block_id,
:param str container_name:
Name of existing container.
:param str blob_name:
Name of existing blob.
Name of blob.
:param block: Content of the block.
:type block: io.IOBase or bytes
Content of the block.
Expand Down Expand Up @@ -315,6 +315,61 @@ def get_block_list(self, container_name, blob_name, snapshot=None,

return self._perform_request(request, _convert_xml_to_block_list)

def put_block_from_url(self, container_name, blob_name, copy_source_url, source_range_start, source_range_end,
block_id, source_content_md5=None, lease_id=None, timeout=None):
"""
Creates a new block to be committed as part of a blob.
:param str container_name:
Name of existing container.
:param str blob_name:
Name of blob.
:param str copy_source_url:
The URL of the source data. It can point to any Azure Blob or File, that is either public or has a
shared access signature attached.
:param int source_range_start:
This indicates the start of the range of bytes(inclusive) that has to be taken from the copy source.
:param int source_range_end:
This indicates the end of the range of bytes(inclusive) that has to be taken from the copy source.
:param str block_id:
A valid Base64 string value that identifies the block. Prior to
encoding, the string must be less than or equal to 64 bytes in size.
For a given blob, the length of the value specified for the blockid
parameter must be the same size for each block. Note that the Base64
string must be URL-encoded.
:param str source_content_md5:
If given, the service will calculate the MD5 hash of the block content and compare against this value.
:param str lease_id:
Required if the blob has an active lease.
:param int timeout:
The timeout parameter is expressed in seconds.
"""
_validate_encryption_unsupported(self.require_encryption, self.key_encryption_key)
_validate_not_none('container_name', container_name)
_validate_not_none('blob_name', blob_name)
_validate_not_none('copy_source_url', copy_source_url)
_validate_not_none('source_range_start', source_range_start)
_validate_not_none('source_range_end', source_range_end)
_validate_not_none('block_id', block_id)

request = HTTPRequest()
request.method = 'PUT'
request.host_locations = self._get_host_locations()
request.path = _get_path(container_name, blob_name)
request.query = {
'comp': 'block',
'blockid': _encode_base64(_to_str(block_id)),
'timeout': _int_to_str(timeout),
}
request.headers = {
'x-ms-lease-id': _to_str(lease_id),
'x-ms-copy-source': copy_source_url,
'x-ms-source-range': 'bytes=' + _to_str(source_range_start) + '-' + _to_str(source_range_end),
'x-ms-source-content-md5': source_content_md5,
}

self._perform_request(request)

# ----Convenience APIs-----------------------------------------------------

def create_blob_from_path(
Expand Down Expand Up @@ -791,7 +846,7 @@ def create_blob_from_text(
timeout=timeout)

def set_standard_blob_tier(
self, container_name, blob_name, standard_blob_tier, timeout=None):
self, container_name, blob_name, standard_blob_tier, timeout=None):
'''
Sets the block blob tiers on the blob. This API is only supported for block blobs on standard storage accounts.
Expand Down
16 changes: 16 additions & 0 deletions azure-storage-blob/azure/storage/blob/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,3 +762,19 @@ class StandardBlobTier(object):

Hot = 'Hot'
''' Hot '''


class AccountInformation(object):
"""
Holds information related to the storage account.
:ivar str sku_name:
Name of the storage SKU, also known as account type.
Example: Standard_LRS, Standard_ZRS, Standard_GRS, Standard_RAGRS, Premium_LRS, Premium_ZRS
:ivar str account_kind:
Describes the flavour of the storage account, also known as account kind.
Example: Storage, StorageV2, BlobStorage
"""
def __init__(self):
self.sku_name = None
self.account_kind = None
4 changes: 2 additions & 2 deletions azure-storage-blob/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@

setup(
name='azure-storage-blob',
version='1.2.0rc1',
version='1.3.0',
description='Microsoft Azure Storage Blob Client Library for Python',
long_description=open('README.rst', 'r').read(),
license='MIT License',
Expand All @@ -74,7 +74,7 @@
packages=find_packages(),
install_requires=[
'azure-common>=1.1.5',
'azure-storage-common>=1.2.0rc1,<1.3.0'
'azure-storage-common>=1.3.0,<1.4.0'
],
extras_require={
":python_version<'3.0'": ['futures'],
Expand Down
4 changes: 4 additions & 0 deletions azure-storage-common/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

> See [BreakingChanges](BreakingChanges.md) for a detailed list of API breaks.
## Version 1.3.0:

- Support for 2018-03-28 REST version. Please see our REST API documentation and blog for information about the related added features.

## Version 1.2.0rc1:

- Increased default socket timeout to a more reasonable number for Python 3.5+.
Expand Down
1 change: 1 addition & 0 deletions azure-storage-common/azure/storage/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Metrics,
CorsRule,
DeleteRetentionPolicy,
StaticWebsite,
ServiceProperties,
AccessPolicy,
ResourceTypes,
Expand Down
4 changes: 2 additions & 2 deletions azure-storage-common/azure/storage/common/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import sys

__author__ = 'Microsoft Corp. <[email protected]>'
__version__ = '1.2.0rc1'
__version__ = '1.3.0'

# UserAgent string sample: 'Azure-Storage/0.37.0-0.38.0 (Python CPython 3.4.2; Windows 8)'
# First version(0.37.0) is the common package, and the second version(0.38.0) is the service package
Expand All @@ -17,7 +17,7 @@
platform.release())

# default values for common package, in case it is used directly
DEFAULT_X_MS_VERSION = '2017-11-09'
DEFAULT_X_MS_VERSION = '2018-03-28'
DEFAULT_USER_AGENT_STRING = '{}None {}'.format(USER_AGENT_STRING_PREFIX, USER_AGENT_STRING_SUFFIX)

# Live ServiceClient URLs
Expand Down
20 changes: 20 additions & 0 deletions azure-storage-common/azure/storage/common/_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
GeoReplication,
ServiceStats,
DeleteRetentionPolicy,
StaticWebsite,
)


Expand Down Expand Up @@ -253,6 +254,11 @@ def _convert_xml_to_service_properties(response):
<Enabled>true|false</Enabled>
<Days>number-of-days</Days>
</DeleteRetentionPolicy>
<StaticWebsite>
<Enabled>true|false</Enabled>
<IndexDocument></IndexDocument>
<ErrorDocument404Path></ErrorDocument404Path>
</StaticWebsite>
</StorageServiceProperties>
'''
if response is None or response.body is None:
Expand Down Expand Up @@ -322,6 +328,20 @@ def _convert_xml_to_service_properties(response):
if policy_enabled:
service_properties.delete_retention_policy.days = int(delete_retention_policy_element.find('Days').text)

# StaticWebsite
static_website_element = service_properties_element.find('StaticWebsite')
if static_website_element is not None:
service_properties.static_website = StaticWebsite()
service_properties.static_website.enabled = _bool(static_website_element.find('Enabled').text)

index_document_element = static_website_element.find('IndexDocument')
if index_document_element is not None:
service_properties.static_website.index_document = index_document_element.text

error_document_element = static_website_element.find('ErrorDocument404Path')
if error_document_element is not None:
service_properties.static_website.error_document_404_path = error_document_element.text

return service_properties


Expand Down
Loading

0 comments on commit ff51954

Please sign in to comment.