Skip to content

Commit

Permalink
[Storage] Merging STG90 (Azure#31498)
Browse files Browse the repository at this point in the history
  • Loading branch information
jalauzon-msft authored Aug 8, 2023
1 parent e3eb9a9 commit 8fc8556
Show file tree
Hide file tree
Showing 211 changed files with 5,837 additions and 485 deletions.
8 changes: 6 additions & 2 deletions sdk/storage/azure-storage-blob/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Release History

## 12.18.0b1 (Unreleased)
## 12.18.0b1 (2023-08-08)

### Features Added

- Added support for service versions 2023-05-03 and 2023-08-03.
- Added `version_id` as a client constructor parameter to `BlobClient`. This change makes `BlobClient`s version-aware, such that
all APIs that accept `version_id` will operate on the version ID provided during client construction by default.
- Added optional keyword `version_id` to `get_blob_client` APIs which, if provided, will result in a version-aware `BlobClient` in which
all APIs that accept `version_id` will operate on the version ID provided to the `get_blob_client` API call by default.

## 12.17.0 (2023-07-11)

Expand Down
22 changes: 18 additions & 4 deletions sdk/storage/azure-storage-blob/azure/storage/blob/_blob_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
get_source_conditions,
get_cpk_scope_info,
get_api_version,
get_version_id,
serialize_blob_tags_header,
serialize_blob_tags,
serialize_query_format, get_access_conditions
Expand Down Expand Up @@ -131,6 +132,8 @@ class BlobClient(StorageAccountHostsMixin, StorageEncryptionMixin): # pylint: d
the exceeded part will be downloaded in chunks (could be parallel). Defaults to 32*1024*1024, or 32MB.
:keyword int max_chunk_get_size: The maximum chunk size used for downloading a blob. Defaults to 4*1024*1024,
or 4MB.
:keyword str version_id: The version id parameter is an opaque DateTime value that, when present,
specifies the version of the blob to operate on.
.. admonition:: Example:
Expand Down Expand Up @@ -179,6 +182,7 @@ def __init__(
self.snapshot = snapshot['snapshot'] # type: ignore
except TypeError:
self.snapshot = snapshot or path_snapshot
self.version_id = kwargs.pop('version_id', None)

# This parameter is used for the hierarchy traversal. Give precedence to credential.
self._raw_credential = credential if credential else sas_token
Expand Down Expand Up @@ -753,6 +757,7 @@ def _download_blob_options(self, offset=None, length=None, encoding=None, **kwar
validate_content = kwargs.pop('validate_content', False)
access_conditions = get_access_conditions(kwargs.pop('lease', None))
mod_conditions = get_modify_conditions(kwargs)
version_id = get_version_id(self.version_id, kwargs)

cpk = kwargs.pop('cpk', None)
cpk_info = None
Expand All @@ -767,7 +772,7 @@ def _download_blob_options(self, offset=None, length=None, encoding=None, **kwar
'config': self._config,
'start_range': offset,
'end_range': length,
'version_id': kwargs.pop('version_id', None),
'version_id': version_id,
'validate_content': validate_content,
'encryption_options': {
'required': self.require_encryption,
Expand Down Expand Up @@ -1085,9 +1090,10 @@ def _delete_blob_options(self, delete_snapshots=None, **kwargs):
# type: (str, **Any) -> Dict[str, Any]
if self.snapshot and delete_snapshots:
raise ValueError("The delete_snapshots option cannot be used with a specific snapshot.")
version_id = get_version_id(self.version_id, kwargs)
options = self._generic_delete_blob_options(delete_snapshots, **kwargs)
options['snapshot'] = self.snapshot
options['version_id'] = kwargs.pop('version_id', None)
options['version_id'] = version_id
options['blob_delete_type'] = kwargs.pop('blob_delete_type', None)
return options

Expand Down Expand Up @@ -1222,9 +1228,11 @@ def exists(self, **kwargs):
:returns: boolean
:rtype: bool
"""
version_id = get_version_id(self.version_id, kwargs)
try:
self._client.blob.get_properties(
snapshot=self.snapshot,
version_id=version_id,
**kwargs)
return True
# Encrypted with CPK
Expand Down Expand Up @@ -1302,6 +1310,7 @@ def get_blob_properties(self, **kwargs):
# TODO: extract this out as _get_blob_properties_options
access_conditions = get_access_conditions(kwargs.pop('lease', None))
mod_conditions = get_modify_conditions(kwargs)
version_id = get_version_id(self.version_id, kwargs)
cpk = kwargs.pop('cpk', None)
cpk_info = None
if cpk:
Expand All @@ -1315,7 +1324,7 @@ def get_blob_properties(self, **kwargs):
kwargs['cls'] = partial(deserialize_pipeline_response_into_cls, cls_method)
blob_props = self._client.blob.get_properties(
timeout=kwargs.pop('timeout', None),
version_id=kwargs.pop('version_id', None),
version_id=version_id,
snapshot=self.snapshot,
lease_access_conditions=access_conditions,
modified_access_conditions=mod_conditions,
Expand Down Expand Up @@ -2393,6 +2402,7 @@ def set_standard_blob_tier(self, standard_blob_tier, **kwargs):
"""
access_conditions = get_access_conditions(kwargs.pop('lease', None))
mod_conditions = get_modify_conditions(kwargs)
version_id = get_version_id(self.version_id, kwargs)
if standard_blob_tier is None:
raise ValueError("A StandardBlobTier must be specified")
if self.snapshot and kwargs.get('version_id'):
Expand All @@ -2404,6 +2414,7 @@ def set_standard_blob_tier(self, standard_blob_tier, **kwargs):
timeout=kwargs.pop('timeout', None),
modified_access_conditions=mod_conditions,
lease_access_conditions=access_conditions,
version_id=version_id,
**kwargs)
except HttpResponseError as error:
process_storage_error(error)
Expand Down Expand Up @@ -2902,11 +2913,13 @@ def _set_blob_tags_options(self, tags=None, **kwargs):
tags = serialize_blob_tags(tags)
access_conditions = get_access_conditions(kwargs.pop('lease', None))
mod_conditions = get_modify_conditions(kwargs)
version_id = get_version_id(self.version_id, kwargs)

options = {
'tags': tags,
'lease_access_conditions': access_conditions,
'modified_access_conditions': mod_conditions,
'version_id': version_id,
'cls': return_response_headers}
options.update(kwargs)
return options
Expand Down Expand Up @@ -2964,9 +2977,10 @@ def _get_blob_tags_options(self, **kwargs):
# type: (**Any) -> Dict[str, str]
access_conditions = get_access_conditions(kwargs.pop('lease', None))
mod_conditions = get_modify_conditions(kwargs)
version_id = get_version_id(self.version_id, kwargs)

options = {
'version_id': kwargs.pop('version_id', None),
'version_id': version_id,
'snapshot': self.snapshot,
'lease_access_conditions': access_conditions,
'modified_access_conditions': mod_conditions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,9 @@ def get_container_client(self, container):
def get_blob_client(
self, container, # type: Union[ContainerProperties, str]
blob, # type: Union[BlobProperties, str]
snapshot=None # type: Optional[Union[Dict[str, Any], str]]
snapshot=None, # type: Optional[Union[Dict[str, Any], str]]
*,
version_id=None # type: Optional[str]
):
# type: (...) -> BlobClient
"""Get a client to interact with the specified blob.
Expand All @@ -757,6 +759,8 @@ def get_blob_client(
The optional blob snapshot on which to operate. This can either be the ID of the snapshot,
or a dictionary output returned by :func:`~azure.storage.blob.BlobClient.create_snapshot()`.
:type snapshot: str or dict(str, Any)
:keyword str version_id: The version id parameter is an opaque DateTime value that, when present,
specifies the version of the blob to operate on.
:returns: A BlobClient.
:rtype: ~azure.storage.blob.BlobClient
Expand Down Expand Up @@ -786,4 +790,5 @@ def get_blob_client(
credential=self.credential, api_version=self.api_version, _configuration=self._config,
_pipeline=_pipeline, _location_mode=self._location_mode, _hosts=self._hosts,
require_encryption=self.require_encryption, encryption_version=self.encryption_version,
key_encryption_key=self.key_encryption_key, key_resolver_function=self.key_resolver_function)
key_encryption_key=self.key_encryption_key, key_resolver_function=self.key_resolver_function,
version_id=version_id)
Original file line number Diff line number Diff line change
Expand Up @@ -1724,7 +1724,9 @@ def set_premium_page_blob_tier_blobs(

def get_blob_client(
self, blob, # type: Union[str, BlobProperties]
snapshot=None # type: str
snapshot=None, # type: str
*,
version_id=None # type: Optional[str]
):
# type: (...) -> BlobClient
"""Get a client to interact with the specified blob.
Expand All @@ -1737,6 +1739,8 @@ def get_blob_client(
:param str snapshot:
The optional blob snapshot on which to operate. This can be the snapshot ID string
or the response returned from :func:`~BlobClient.create_snapshot()`.
:keyword str version_id: The version id parameter is an opaque DateTime value that, when present,
specifies the version of the blob to operate on.
:returns: A BlobClient.
:rtype: ~azure.storage.blob.BlobClient
Expand All @@ -1759,4 +1763,5 @@ def get_blob_client(
credential=self.credential, api_version=self.api_version, _configuration=self._config,
_pipeline=_pipeline, _location_mode=self._location_mode, _hosts=self._hosts,
require_encryption=self.require_encryption, encryption_version=self.encryption_version,
key_encryption_key=self.key_encryption_key, key_resolver_function=self.key_resolver_function)
key_encryption_key=self.key_encryption_key, key_resolver_function=self.key_resolver_function,
version_id=version_id)
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,8 @@ def query(self, name, data, data_type, **kwargs):
:param data: The data to be serialized.
:param str data_type: The type to be serialized from.
:keyword bool skip_quote: Whether to skip quote the serialized result.
Defaults to False.
:rtype: str
:raises: TypeError if serialization fails.
:raises: ValueError if data is None
Expand All @@ -750,10 +752,8 @@ def query(self, name, data, data_type, **kwargs):
# Treat the list aside, since we don't want to encode the div separator
if data_type.startswith("["):
internal_data_type = data_type[1:-1]
data = [self.serialize_data(d, internal_data_type, **kwargs) if d is not None else "" for d in data]
if not kwargs.get("skip_quote", False):
data = [quote(str(d), safe="") for d in data]
return str(self.serialize_iter(data, internal_data_type, **kwargs))
do_quote = not kwargs.get("skip_quote", False)
return str(self.serialize_iter(data, internal_data_type, do_quote=do_quote, **kwargs))

# Not a list, regular serialization
output = self.serialize_data(data, data_type, **kwargs)
Expand Down Expand Up @@ -892,6 +892,8 @@ def serialize_iter(self, data, iter_type, div=None, **kwargs):
not be None or empty.
:param str div: If set, this str will be used to combine the elements
in the iterable into a combined string. Default is 'None'.
:keyword bool do_quote: Whether to quote the serialized result of each iterable element.
Defaults to False.
:rtype: list, str
"""
if isinstance(data, str):
Expand All @@ -909,6 +911,9 @@ def serialize_iter(self, data, iter_type, div=None, **kwargs):
raise
serialized.append(None)

if kwargs.get("do_quote", False):
serialized = ["" if s is None else quote(str(s), safe="") for s in serialized]

if div:
serialized = ["" if s is None else str(s) for s in serialized]
serialized = div.join(serialized)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class ArchiveStatus(str, Enum, metaclass=CaseInsensitiveEnumMeta):

REHYDRATE_PENDING_TO_HOT = "rehydrate-pending-to-hot"
REHYDRATE_PENDING_TO_COOL = "rehydrate-pending-to-cool"
REHYDRATE_PENDING_TO_COLD = "rehydrate-pending-to-cold"


class BlobCopySourceTags(str, Enum, metaclass=CaseInsensitiveEnumMeta):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -565,8 +565,8 @@ class BlobPropertiesInternal(_serialization.Model): # pylint: disable=too-many-
:vartype access_tier: str or ~azure.storage.blob.models.AccessTier
:ivar access_tier_inferred:
:vartype access_tier_inferred: bool
:ivar archive_status: Known values are: "rehydrate-pending-to-hot" and
"rehydrate-pending-to-cool".
:ivar archive_status: Known values are: "rehydrate-pending-to-hot",
"rehydrate-pending-to-cool", and "rehydrate-pending-to-cold".
:vartype archive_status: str or ~azure.storage.blob.models.ArchiveStatus
:ivar customer_provided_key_sha256:
:vartype customer_provided_key_sha256: str
Expand Down Expand Up @@ -747,8 +747,8 @@ def __init__( # pylint: disable=too-many-locals
:paramtype access_tier: str or ~azure.storage.blob.models.AccessTier
:keyword access_tier_inferred:
:paramtype access_tier_inferred: bool
:keyword archive_status: Known values are: "rehydrate-pending-to-hot" and
"rehydrate-pending-to-cool".
:keyword archive_status: Known values are: "rehydrate-pending-to-hot",
"rehydrate-pending-to-cool", and "rehydrate-pending-to-cold".
:paramtype archive_status: str or ~azure.storage.blob.models.ArchiveStatus
:keyword customer_provided_key_sha256:
:paramtype customer_provided_key_sha256: str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@
'2021-08-06',
'2021-12-02',
'2022-11-02',
'2023-01-03'
'2023-01-03',
'2023-05-03',
'2023-08-03',
]


Expand Down Expand Up @@ -148,6 +150,11 @@ def get_api_version(kwargs):
raise ValueError(f"Unsupported API version '{api_version}'. Please select from:\n{versions}")
return api_version or _SUPPORTED_API_VERSIONS[-1]

def get_version_id(self_vid, kwargs):
# type: (Optional[str], Dict[str, Any]) -> Optional[str]
if 'version_id' in kwargs:
return kwargs.pop('version_id')
return self_vid

def serialize_blob_tags_header(tags=None):
# type: (Optional[Dict[str, str]]) -> str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
)
from .._encryption import StorageEncryptionMixin
from .._models import BlobType, BlobBlock, BlobProperties, PageRange
from .._serialize import get_modify_conditions, get_api_version, get_access_conditions
from .._serialize import get_modify_conditions, get_api_version, get_access_conditions, get_version_id
from ._download_async import StorageStreamDownloader
from ._lease_async import BlobLeaseClient
from ._models import PageRangePaged
Expand Down Expand Up @@ -99,6 +99,8 @@ class BlobClient(AsyncStorageAccountHostsMixin, BlobClientBase, StorageEncryptio
the exceeded part will be downloaded in chunks (could be parallel). Defaults to 32*1024*1024, or 32MB.
:keyword int max_chunk_get_size: The maximum chunk size used for downloading a blob. Defaults to 4*1024*1024,
or 4MB.
:keyword str version_id: The version id parameter is an opaque DateTime value that, when present,
specifies the version of the blob to operate on.
.. admonition:: Example:
Expand Down Expand Up @@ -670,9 +672,11 @@ async def exists(self, **kwargs):
:returns: boolean
:rtype: bool
"""
version_id = get_version_id(self.version_id, kwargs)
try:
await self._client.blob.get_properties(
snapshot=self.snapshot,
version_id=version_id,
**kwargs)
return True
# Encrypted with CPK
Expand Down Expand Up @@ -749,6 +753,7 @@ async def get_blob_properties(self, **kwargs):
"""
access_conditions = get_access_conditions(kwargs.pop('lease', None))
mod_conditions = get_modify_conditions(kwargs)
version_id = get_version_id(self.version_id, kwargs)
cpk = kwargs.pop('cpk', None)
cpk_info = None
if cpk:
Expand All @@ -762,7 +767,7 @@ async def get_blob_properties(self, **kwargs):
kwargs['cls'] = partial(deserialize_pipeline_response_into_cls, cls_method)
blob_props = await self._client.blob.get_properties(
timeout=kwargs.pop('timeout', None),
version_id=kwargs.pop('version_id', None),
version_id=version_id,
snapshot=self.snapshot,
lease_access_conditions=access_conditions,
modified_access_conditions=mod_conditions,
Expand Down Expand Up @@ -1558,6 +1563,7 @@ async def set_standard_blob_tier(self, standard_blob_tier, **kwargs):
"""
access_conditions = get_access_conditions(kwargs.pop('lease', None))
mod_conditions = get_modify_conditions(kwargs)
version_id = get_version_id(self.version_id, kwargs)
if standard_blob_tier is None:
raise ValueError("A StandardBlobTier must be specified")
try:
Expand All @@ -1566,6 +1572,7 @@ async def set_standard_blob_tier(self, standard_blob_tier, **kwargs):
timeout=kwargs.pop('timeout', None),
modified_access_conditions=mod_conditions,
lease_access_conditions=access_conditions,
version_id=version_id,
**kwargs)
except HttpResponseError as error:
process_storage_error(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,9 @@ def get_container_client(self, container):
def get_blob_client(
self, container, # type: Union[ContainerProperties, str]
blob, # type: Union[BlobProperties, str]
snapshot=None # type: Optional[Union[Dict[str, Any], str]]
snapshot=None, # type: Optional[Union[Dict[str, Any], str]]
*,
version_id=None # type: Optional[str]
):
# type: (...) -> BlobClient
"""Get a client to interact with the specified blob.
Expand All @@ -700,6 +702,8 @@ def get_blob_client(
or a dictionary output returned by
:func:`~azure.storage.blob.aio.BlobClient.create_snapshot()`.
:type snapshot: str or dict(str, Any)
:keyword str version_id: The version id parameter is an opaque DateTime value that, when present,
specifies the version of the blob to operate on.
:returns: A BlobClient.
:rtype: ~azure.storage.blob.aio.BlobClient
Expand Down Expand Up @@ -730,4 +734,5 @@ def get_blob_client(
credential=self.credential, api_version=self.api_version, _configuration=self._config,
_pipeline=_pipeline, _location_mode=self._location_mode, _hosts=self._hosts,
require_encryption=self.require_encryption, encryption_version=self.encryption_version,
key_encryption_key=self.key_encryption_key, key_resolver_function=self.key_resolver_function)
key_encryption_key=self.key_encryption_key, key_resolver_function=self.key_resolver_function,
version_id=version_id)
Loading

0 comments on commit 8fc8556

Please sign in to comment.