diff --git a/sdk/storage/azure-storage-blob/assets.json b/sdk/storage/azure-storage-blob/assets.json index 0ed403784c2a..fce9d1a7c338 100644 --- a/sdk/storage/azure-storage-blob/assets.json +++ b/sdk/storage/azure-storage-blob/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/storage/azure-storage-blob", - "Tag": "python/storage/azure-storage-blob_863b753fbb" + "Tag": "python/storage/azure-storage-blob_275000b78a" } diff --git a/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/authentication.py b/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/authentication.py index abbbfe88127f..e4d5ed730846 100644 --- a/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/authentication.py +++ b/sdk/storage/azure-storage-blob/azure/storage/blob/_shared/authentication.py @@ -8,6 +8,7 @@ import re from typing import List, Tuple from urllib.parse import unquote, urlparse +from functools import cmp_to_key try: from yarl import URL @@ -27,6 +28,66 @@ logger = logging.getLogger(__name__) +table_lv0 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725, + 0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e, + 0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, + 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, + 0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, + 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, + 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0, +] + +table_lv4 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +] + +def compare(lhs: str, rhs: str) -> int: # pylint:disable=too-many-return-statements + tables = [table_lv0, table_lv4] + curr_level, i, j, n = 0, 0, 0, len(tables) + lhs_len = len(lhs) + rhs_len = len(rhs) + while curr_level < n: + if curr_level == (n - 1) and i != j: + if i > j: + return -1 + if i < j: + return 1 + return 0 + + w1 = tables[curr_level][ord(lhs[i])] if i < lhs_len else 0x1 + w2 = tables[curr_level][ord(rhs[j])] if j < rhs_len else 0x1 + + if w1 == 0x1 and w2 == 0x1: + i = 0 + j = 0 + curr_level += 1 + elif w1 == w2: + i += 1 + j += 1 + elif w1 == 0: + i += 1 + elif w2 == 0: + j += 1 + else: + if w1 < w2: + return -1 + if w1 > w2: + return 1 + return 0 + return 0 + + # wraps a given exception with the desired exception type def _wrap_exception(ex, desired_type): msg = "" @@ -36,8 +97,6 @@ def _wrap_exception(ex, desired_type): # This method attempts to emulate the sorting done by the service def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str, str]]: - # Define the custom alphabet for weights - custom_weights = "-!#$%&*.^_|~+\"\'(),/`~0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz{}" # Build dict of tuples and list of keys header_dict = {} @@ -46,9 +105,8 @@ def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str header_dict[k] = v header_keys.append(k) - # Sort according to custom defined weights try: - header_keys = sorted(header_keys, key=lambda word: [custom_weights.index(c) for c in word]) + header_keys = sorted(header_keys, key=cmp_to_key(compare)) except ValueError as exc: raise ValueError("Illegal character encountered when sorting headers.") from exc diff --git a/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions.py b/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions.py index 18fbae7ef491..c4aba5e31a80 100644 --- a/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions.py +++ b/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions.py @@ -3124,4 +3124,45 @@ def test_header_metadata_sort_in_upload_blob(self, **kwargs): # Act blob_client.upload_blob(data, length=len(data), metadata=metadata) + @BlobPreparer() + @recorded_by_proxy + def test_header_metadata_sort_in_upload_blob_translation(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup() + data = b'hello world' + bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key) + try: + container_client = bsc.create_container(self.container_name) + except: + container_client = bsc.get_container_client(self.container_name) + blob_client = container_client.get_blob_client('blob1') + + # Hand-picked metadata examples that sorted incorrectly with our previous implementation. + metadata = { + 'test': 'val', + 'test-': 'val', + 'test--': 'val', + 'test-_': 'val', + 'test_-': 'val', + 'test__': 'val', + 'test-a': 'val', + 'test-A': 'val', + 'test-_A': 'val', + 'test_a': 'val', + 'test_Z': 'val', + 'test_a_': 'val', + 'test_a-': 'val', + 'test_a-_': 'val', + } + + # Act + # If we hit invalid metadata error, that means we have successfully sorted headers properly to pass auth error + with pytest.raises(HttpResponseError) as e: + blob_client.upload_blob(data, length=len(data), metadata=metadata) + + # Assert + assert StorageErrorCode.invalid_metadata == e.value.error_code + # ------------------------------------------------------------------------------ diff --git a/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions_async.py b/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions_async.py index 2b21f444f00c..ac2a864851ed 100644 --- a/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_blob_access_conditions_async.py @@ -3092,4 +3092,69 @@ async def test_header_metadata_sort_in_upload_blob(self, **kwargs): # Act await blob_client.upload_blob(data, length=len(data), metadata=metadata) + @BlobPreparer() + @recorded_by_proxy_async + async def test_header_metadata_sort_in_upload_blob(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup() + data = b'hello world' + bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key) + try: + container_client = await bsc.create_container(self.container_name) + except: + container_client = bsc.get_container_client(self.container_name) + blob_client = container_client.get_blob_client('blob1') + + # Hand-picked metadata examples as Python & service don't sort '_' with the same weight + metadata = {'a0': 'a', 'a1': 'a', 'a2': 'a', 'a3': 'a', 'a4': 'a', 'a5': 'a', 'a6': 'a', 'a7': 'a', 'a8': 'a', + 'a9': 'a', '_': 'a', '_a': 'a', 'a_': 'a', '__': 'a', '_a_': 'a', 'b': 'a', 'c': 'a', 'y': 'a', + 'z': 'z_', '_z': 'a', '_F': 'a', 'F': 'a', 'F_': 'a', '_F_': 'a', '__F': 'a', '__a': 'a', 'a__': 'a' + } + + # Act + await blob_client.upload_blob(data, length=len(data), metadata=metadata) + + @BlobPreparer() + @recorded_by_proxy_async + async def test_header_metadata_sort_in_upload_blob_translation(self, **kwargs): + storage_account_name = kwargs.pop("storage_account_name") + storage_account_key = kwargs.pop("storage_account_key") + + self._setup() + data = b'hello world' + bsc = BlobServiceClient(self.account_url(storage_account_name, "blob"), storage_account_key) + try: + container_client = await bsc.create_container(self.container_name) + except: + container_client = bsc.get_container_client(self.container_name) + blob_client = container_client.get_blob_client('blob1') + + # Hand-picked metadata examples that sorted incorrectly with our previous implementation. + metadata = { + 'test': 'val', + 'test-': 'val', + 'test--': 'val', + 'test-_': 'val', + 'test_-': 'val', + 'test__': 'val', + 'test-a': 'val', + 'test-A': 'val', + 'test-_A': 'val', + 'test_a': 'val', + 'test_Z': 'val', + 'test_a_': 'val', + 'test_a-': 'val', + 'test_a-_': 'val', + } + + # Act + # If we hit invalid metadata error, that means we have successfully sorted headers properly to pass auth error + with pytest.raises(HttpResponseError) as e: + await blob_client.upload_blob(data, length=len(data), metadata=metadata) + + # Assert + assert StorageErrorCode.invalid_metadata == e.value.error_code + # ------------------------------------------------------------------------------ diff --git a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/authentication.py b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/authentication.py index abbbfe88127f..e4d5ed730846 100644 --- a/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/authentication.py +++ b/sdk/storage/azure-storage-file-datalake/azure/storage/filedatalake/_shared/authentication.py @@ -8,6 +8,7 @@ import re from typing import List, Tuple from urllib.parse import unquote, urlparse +from functools import cmp_to_key try: from yarl import URL @@ -27,6 +28,66 @@ logger = logging.getLogger(__name__) +table_lv0 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725, + 0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e, + 0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, + 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, + 0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, + 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, + 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0, +] + +table_lv4 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +] + +def compare(lhs: str, rhs: str) -> int: # pylint:disable=too-many-return-statements + tables = [table_lv0, table_lv4] + curr_level, i, j, n = 0, 0, 0, len(tables) + lhs_len = len(lhs) + rhs_len = len(rhs) + while curr_level < n: + if curr_level == (n - 1) and i != j: + if i > j: + return -1 + if i < j: + return 1 + return 0 + + w1 = tables[curr_level][ord(lhs[i])] if i < lhs_len else 0x1 + w2 = tables[curr_level][ord(rhs[j])] if j < rhs_len else 0x1 + + if w1 == 0x1 and w2 == 0x1: + i = 0 + j = 0 + curr_level += 1 + elif w1 == w2: + i += 1 + j += 1 + elif w1 == 0: + i += 1 + elif w2 == 0: + j += 1 + else: + if w1 < w2: + return -1 + if w1 > w2: + return 1 + return 0 + return 0 + + # wraps a given exception with the desired exception type def _wrap_exception(ex, desired_type): msg = "" @@ -36,8 +97,6 @@ def _wrap_exception(ex, desired_type): # This method attempts to emulate the sorting done by the service def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str, str]]: - # Define the custom alphabet for weights - custom_weights = "-!#$%&*.^_|~+\"\'(),/`~0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz{}" # Build dict of tuples and list of keys header_dict = {} @@ -46,9 +105,8 @@ def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str header_dict[k] = v header_keys.append(k) - # Sort according to custom defined weights try: - header_keys = sorted(header_keys, key=lambda word: [custom_weights.index(c) for c in word]) + header_keys = sorted(header_keys, key=cmp_to_key(compare)) except ValueError as exc: raise ValueError("Illegal character encountered when sorting headers.") from exc diff --git a/sdk/storage/azure-storage-file-share/assets.json b/sdk/storage/azure-storage-file-share/assets.json index c084ac07ee98..f31202404fee 100644 --- a/sdk/storage/azure-storage-file-share/assets.json +++ b/sdk/storage/azure-storage-file-share/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "python", "TagPrefix": "python/storage/azure-storage-file-share", - "Tag": "python/storage/azure-storage-file-share_a600655193" + "Tag": "python/storage/azure-storage-file-share_e105feb7fb" } diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/_patch.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/_patch.py index 17dbc073e01b..d3be7c117232 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/_patch.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/_patch.py @@ -28,5 +28,7 @@ # This file is used for handwritten extensions to the generated code. Example: # https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md + + def patch_sdk(): pass diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/_patch.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/_patch.py index 17dbc073e01b..d3be7c117232 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/_patch.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/_patch.py @@ -28,5 +28,7 @@ # This file is used for handwritten extensions to the generated code. Example: # https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/customize_code/how-to-patch-sdk-code.md + + def patch_sdk(): pass diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_patch.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_patch.py index f7dd32510333..49900f6ab120 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_patch.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_patch.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ + """Customize generated code here. Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_share_operations.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_share_operations.py index f5d20d560a07..bb89d8fb19c1 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_share_operations.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/aio/operations/_share_operations.py @@ -82,6 +82,7 @@ async def create( # pylint: disable=inconsistent-return-statements access_tier: Optional[Union[str, _models.ShareAccessTier]] = None, enabled_protocols: Optional[str] = None, root_squash: Optional[Union[str, _models.ShareRootSquash]] = None, + enable_snapshot_virtual_directory_access: Optional[bool] = None, **kwargs: Any ) -> None: """Creates a new share under the specified account. If the share with the same name already @@ -105,6 +106,8 @@ async def create( # pylint: disable=inconsistent-return-statements :param root_squash: Root squash to set on the share. Only valid for NFS shares. Known values are: "NoRootSquash", "RootSquash", and "AllSquash". Default value is None. :type root_squash: str or ~azure.storage.fileshare.models.ShareRootSquash + :param enable_snapshot_virtual_directory_access: Default value is None. + :type enable_snapshot_virtual_directory_access: bool :return: None or the result of cls(response) :rtype: None :raises ~azure.core.exceptions.HttpResponseError: @@ -131,6 +134,7 @@ async def create( # pylint: disable=inconsistent-return-statements access_tier=access_tier, enabled_protocols=enabled_protocols, root_squash=root_squash, + enable_snapshot_virtual_directory_access=enable_snapshot_virtual_directory_access, restype=restype, version=self._config.version, headers=_headers, @@ -266,6 +270,9 @@ async def get_properties( # pylint: disable=inconsistent-return-statements "str", response.headers.get("x-ms-enabled-protocols") ) response_headers["x-ms-root-squash"] = self._deserialize("str", response.headers.get("x-ms-root-squash")) + response_headers["x-ms-enable-snapshot-virtual-directory-access"] = self._deserialize( + "bool", response.headers.get("x-ms-enable-snapshot-virtual-directory-access") + ) if cls: return cls(pipeline_response, None, response_headers) # type: ignore @@ -1098,6 +1105,7 @@ async def set_properties( # pylint: disable=inconsistent-return-statements quota: Optional[int] = None, access_tier: Optional[Union[str, _models.ShareAccessTier]] = None, root_squash: Optional[Union[str, _models.ShareRootSquash]] = None, + enable_snapshot_virtual_directory_access: Optional[bool] = None, lease_access_conditions: Optional[_models.LeaseAccessConditions] = None, **kwargs: Any ) -> None: @@ -1116,6 +1124,8 @@ async def set_properties( # pylint: disable=inconsistent-return-statements :param root_squash: Root squash to set on the share. Only valid for NFS shares. Known values are: "NoRootSquash", "RootSquash", and "AllSquash". Default value is None. :type root_squash: str or ~azure.storage.fileshare.models.ShareRootSquash + :param enable_snapshot_virtual_directory_access: Default value is None. + :type enable_snapshot_virtual_directory_access: bool :param lease_access_conditions: Parameter group. Default value is None. :type lease_access_conditions: ~azure.storage.fileshare.models.LeaseAccessConditions :return: None or the result of cls(response) @@ -1148,6 +1158,7 @@ async def set_properties( # pylint: disable=inconsistent-return-statements access_tier=access_tier, lease_id=_lease_id, root_squash=root_squash, + enable_snapshot_virtual_directory_access=enable_snapshot_virtual_directory_access, restype=restype, comp=comp, version=self._config.version, diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_models_py3.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_models_py3.py index 7fe1a4ff1cb1..0730e6732024 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_models_py3.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_models_py3.py @@ -1239,6 +1239,8 @@ class SharePropertiesInternal(_serialization.Model): # pylint: disable=too-many :vartype enabled_protocols: str :ivar root_squash: Known values are: "NoRootSquash", "RootSquash", and "AllSquash". :vartype root_squash: str or ~azure.storage.fileshare.models.ShareRootSquash + :ivar enable_snapshot_virtual_directory_access: + :vartype enable_snapshot_virtual_directory_access: bool """ _validation = { @@ -1266,6 +1268,7 @@ class SharePropertiesInternal(_serialization.Model): # pylint: disable=too-many "lease_duration": {"key": "LeaseDuration", "type": "str"}, "enabled_protocols": {"key": "EnabledProtocols", "type": "str"}, "root_squash": {"key": "RootSquash", "type": "str"}, + "enable_snapshot_virtual_directory_access": {"key": "EnableSnapshotVirtualDirectoryAccess", "type": "bool"}, } def __init__( @@ -1289,6 +1292,7 @@ def __init__( lease_duration: Optional[Union[str, "_models.LeaseDurationType"]] = None, enabled_protocols: Optional[str] = None, root_squash: Optional[Union[str, "_models.ShareRootSquash"]] = None, + enable_snapshot_virtual_directory_access: Optional[bool] = None, **kwargs: Any ) -> None: """ @@ -1331,6 +1335,8 @@ def __init__( :paramtype enabled_protocols: str :keyword root_squash: Known values are: "NoRootSquash", "RootSquash", and "AllSquash". :paramtype root_squash: str or ~azure.storage.fileshare.models.ShareRootSquash + :keyword enable_snapshot_virtual_directory_access: + :paramtype enable_snapshot_virtual_directory_access: bool """ super().__init__(**kwargs) self.last_modified = last_modified @@ -1351,6 +1357,7 @@ def __init__( self.lease_duration = lease_duration self.enabled_protocols = enabled_protocols self.root_squash = root_squash + self.enable_snapshot_virtual_directory_access = enable_snapshot_virtual_directory_access class ShareProtocolSettings(_serialization.Model): diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_patch.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_patch.py index f7dd32510333..49900f6ab120 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_patch.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/models/_patch.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ + """Customize generated code here. Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_patch.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_patch.py index f7dd32510333..49900f6ab120 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_patch.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_patch.py @@ -2,6 +2,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ + """Customize generated code here. Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_share_operations.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_share_operations.py index 3a156414fdb6..4f78e8fd55ff 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_share_operations.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_generated/operations/_share_operations.py @@ -48,6 +48,7 @@ def build_create_request( access_tier: Optional[Union[str, _models.ShareAccessTier]] = None, enabled_protocols: Optional[str] = None, root_squash: Optional[Union[str, _models.ShareRootSquash]] = None, + enable_snapshot_virtual_directory_access: Optional[bool] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) @@ -82,6 +83,10 @@ def build_create_request( _headers["x-ms-enabled-protocols"] = _SERIALIZER.header("enabled_protocols", enabled_protocols, "str") if root_squash is not None: _headers["x-ms-root-squash"] = _SERIALIZER.header("root_squash", root_squash, "str") + if enable_snapshot_virtual_directory_access is not None: + _headers["x-ms-enable-snapshot-virtual-directory-access"] = _SERIALIZER.header( + "enable_snapshot_virtual_directory_access", enable_snapshot_virtual_directory_access, "bool" + ) _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) @@ -527,6 +532,7 @@ def build_set_properties_request( access_tier: Optional[Union[str, _models.ShareAccessTier]] = None, lease_id: Optional[str] = None, root_squash: Optional[Union[str, _models.ShareRootSquash]] = None, + enable_snapshot_virtual_directory_access: Optional[bool] = None, **kwargs: Any ) -> HttpRequest: _headers = case_insensitive_dict(kwargs.pop("headers", {}) or {}) @@ -561,6 +567,10 @@ def build_set_properties_request( _headers["x-ms-lease-id"] = _SERIALIZER.header("lease_id", lease_id, "str") if root_squash is not None: _headers["x-ms-root-squash"] = _SERIALIZER.header("root_squash", root_squash, "str") + if enable_snapshot_virtual_directory_access is not None: + _headers["x-ms-enable-snapshot-virtual-directory-access"] = _SERIALIZER.header( + "enable_snapshot_virtual_directory_access", enable_snapshot_virtual_directory_access, "bool" + ) _headers["Accept"] = _SERIALIZER.header("accept", accept, "str") return HttpRequest(method="PUT", url=_url, params=_params, headers=_headers, **kwargs) @@ -786,6 +796,7 @@ def create( # pylint: disable=inconsistent-return-statements access_tier: Optional[Union[str, _models.ShareAccessTier]] = None, enabled_protocols: Optional[str] = None, root_squash: Optional[Union[str, _models.ShareRootSquash]] = None, + enable_snapshot_virtual_directory_access: Optional[bool] = None, **kwargs: Any ) -> None: """Creates a new share under the specified account. If the share with the same name already @@ -809,6 +820,8 @@ def create( # pylint: disable=inconsistent-return-statements :param root_squash: Root squash to set on the share. Only valid for NFS shares. Known values are: "NoRootSquash", "RootSquash", and "AllSquash". Default value is None. :type root_squash: str or ~azure.storage.fileshare.models.ShareRootSquash + :param enable_snapshot_virtual_directory_access: Default value is None. + :type enable_snapshot_virtual_directory_access: bool :return: None or the result of cls(response) :rtype: None :raises ~azure.core.exceptions.HttpResponseError: @@ -835,6 +848,7 @@ def create( # pylint: disable=inconsistent-return-statements access_tier=access_tier, enabled_protocols=enabled_protocols, root_squash=root_squash, + enable_snapshot_virtual_directory_access=enable_snapshot_virtual_directory_access, restype=restype, version=self._config.version, headers=_headers, @@ -970,6 +984,9 @@ def get_properties( # pylint: disable=inconsistent-return-statements "str", response.headers.get("x-ms-enabled-protocols") ) response_headers["x-ms-root-squash"] = self._deserialize("str", response.headers.get("x-ms-root-squash")) + response_headers["x-ms-enable-snapshot-virtual-directory-access"] = self._deserialize( + "bool", response.headers.get("x-ms-enable-snapshot-virtual-directory-access") + ) if cls: return cls(pipeline_response, None, response_headers) # type: ignore @@ -1802,6 +1819,7 @@ def set_properties( # pylint: disable=inconsistent-return-statements quota: Optional[int] = None, access_tier: Optional[Union[str, _models.ShareAccessTier]] = None, root_squash: Optional[Union[str, _models.ShareRootSquash]] = None, + enable_snapshot_virtual_directory_access: Optional[bool] = None, lease_access_conditions: Optional[_models.LeaseAccessConditions] = None, **kwargs: Any ) -> None: @@ -1820,6 +1838,8 @@ def set_properties( # pylint: disable=inconsistent-return-statements :param root_squash: Root squash to set on the share. Only valid for NFS shares. Known values are: "NoRootSquash", "RootSquash", and "AllSquash". Default value is None. :type root_squash: str or ~azure.storage.fileshare.models.ShareRootSquash + :param enable_snapshot_virtual_directory_access: Default value is None. + :type enable_snapshot_virtual_directory_access: bool :param lease_access_conditions: Parameter group. Default value is None. :type lease_access_conditions: ~azure.storage.fileshare.models.LeaseAccessConditions :return: None or the result of cls(response) @@ -1852,6 +1872,7 @@ def set_properties( # pylint: disable=inconsistent-return-statements access_tier=access_tier, lease_id=_lease_id, root_squash=root_squash, + enable_snapshot_virtual_directory_access=enable_snapshot_virtual_directory_access, restype=restype, comp=comp, version=self._config.version, diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_models.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_models.py index dc42fef21bc3..141de2b503f6 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_models.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_models.py @@ -341,6 +341,9 @@ class ShareProperties(DictMixin): Possible values include: 'NoRootSquash', 'RootSquash', 'AllSquash'. :ivar list(str) protocols: Indicates the protocols enabled on the share. The protocol can be either SMB or NFS. + :ivar bool enable_snapshot_virtual_directory_access: + Specifies whether the snapshot virtual directory should be accessible at the root of the share + mount point when NFS is enabled. if not specified, the default is True. """ def __init__(self, **kwargs): @@ -364,6 +367,8 @@ def __init__(self, **kwargs): self.protocols = [protocol.strip() for protocol in kwargs.get('x-ms-enabled-protocols', None).split(',')]\ if kwargs.get('x-ms-enabled-protocols', None) else None self.root_squash = kwargs.get('x-ms-root-squash', None) + self.enable_snapshot_virtual_directory_access = \ + kwargs.get('x-ms-enable-snapshot-virtual-directory-access') @classmethod def _from_generated(cls, generated): props = cls() @@ -387,6 +392,7 @@ def _from_generated(cls, generated): props.protocols = [protocol.strip() for protocol in generated.properties.enabled_protocols.split(',')]\ if generated.properties.enabled_protocols else None props.root_squash = generated.properties.root_squash + props.enable_snapshot_virtual_directory_access = generated.properties.enable_snapshot_virtual_directory_access return props diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py index 6335fd7ed1c4..2c7c154bb703 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_share_client.py @@ -362,6 +362,9 @@ def create_share(self, **kwargs): Root squash to set on the share. Only valid for NFS shares. Possible values include: 'NoRootSquash', 'RootSquash', 'AllSquash'. :paramtype root_squash: str or ~azure.storage.fileshare.ShareRootSquash + :keyword bool enable_snapshot_virtual_directory_access: + Specifies whether the snapshot virtual directory should be accessible at the root of the share + mount point when NFS is enabled. Default value is True. :returns: Share-updated property dict (Etag and last modified). :rtype: Dict[str, Any] diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/authentication.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/authentication.py index abbbfe88127f..7f01527560b6 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/authentication.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/_shared/authentication.py @@ -8,6 +8,7 @@ import re from typing import List, Tuple from urllib.parse import unquote, urlparse +from functools import cmp_to_key try: from yarl import URL @@ -26,6 +27,65 @@ logger = logging.getLogger(__name__) +table_lv0 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725, + 0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e, + 0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, + 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, + 0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, + 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, + 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0, +] + +table_lv4 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +] + +def compare(lhs: str, rhs: str) -> int: # pylint:disable=too-many-return-statements + tables = [table_lv0, table_lv4] + curr_level, i, j, n = 0, 0, 0, len(tables) + lhs_len = len(lhs) + rhs_len = len(rhs) + while curr_level < n: + if curr_level == (n - 1) and i != j: + if i > j: + return -1 + if i < j: + return 1 + return 0 + + w1 = tables[curr_level][ord(lhs[i])] if i < lhs_len else 0x1 + w2 = tables[curr_level][ord(rhs[j])] if j < rhs_len else 0x1 + + if w1 == 0x1 and w2 == 0x1: + i = 0 + j = 0 + curr_level += 1 + elif w1 == w2: + i += 1 + j += 1 + elif w1 == 0: + i += 1 + elif w2 == 0: + j += 1 + else: + if w1 < w2: + return -1 + if w1 > w2: + return 1 + return 0 + return 0 + # wraps a given exception with the desired exception type def _wrap_exception(ex, desired_type): @@ -36,8 +96,6 @@ def _wrap_exception(ex, desired_type): # This method attempts to emulate the sorting done by the service def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str, str]]: - # Define the custom alphabet for weights - custom_weights = "-!#$%&*.^_|~+\"\'(),/`~0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz{}" # Build dict of tuples and list of keys header_dict = {} @@ -46,9 +104,8 @@ def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str header_dict[k] = v header_keys.append(k) - # Sort according to custom defined weights try: - header_keys = sorted(header_keys, key=lambda word: [custom_weights.index(c) for c in word]) + header_keys = sorted(header_keys, key=cmp_to_key(compare)) except ValueError as exc: raise ValueError("Illegal character encountered when sorting headers.") from exc diff --git a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py index d942fbd17acc..2fc1c179aa9e 100644 --- a/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py +++ b/sdk/storage/azure-storage-file-share/azure/storage/fileshare/aio/_share_client_async.py @@ -225,6 +225,9 @@ async def create_share(self, **kwargs): Root squash to set on the share. Only valid for NFS shares. Possible values include: 'NoRootSquash', 'RootSquash', 'AllSquash'. :paramtype root_squash: str or ~azure.storage.fileshare.ShareRootSquash + :keyword bool enable_snapshot_virtual_directory_access: + Specifies whether the snapshot virtual directory should be accessible at the root of the share + mount point when NFS is enabled. Default value is True. :returns: Share-updated property dict (Etag and last modified). :rtype: Dict[str, Any] diff --git a/sdk/storage/azure-storage-file-share/tests/test_share.py b/sdk/storage/azure-storage-file-share/tests/test_share.py index eb88bfd29a0c..335185e62b97 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_share.py +++ b/sdk/storage/azure-storage-file-share/tests/test_share.py @@ -752,6 +752,27 @@ def test_list_shares_no_options(self, **kwargs): self.assertNamedItemInContainer(shares, share.share_name) self._delete_shares() + @FileSharePreparer() + @recorded_by_proxy + def test_list_shares_enable_snapshot_virtual_directory_access(self, **kwargs): + premium_storage_file_account_name = kwargs.pop("premium_storage_file_account_name") + premium_storage_file_account_key = kwargs.pop("premium_storage_file_account_key") + + self._setup(premium_storage_file_account_name, premium_storage_file_account_key) + share = self._create_share(protocols="NFS", enable_snapshot_virtual_directory_access=False) + + # Act + list_props = list(self.fsc.list_shares()) + share_props = share.get_share_properties() + + # Assert + assert list_props[0].protocols[0] == 'NFS' + assert list_props[0].enable_snapshot_virtual_directory_access is False + + assert share_props.protocols[0] == 'NFS' + assert share_props.enable_snapshot_virtual_directory_access is False + self._delete_shares() + @FileSharePreparer() @recorded_by_proxy def test_list_shares_no_options_for_premium_account(self, **kwargs): diff --git a/sdk/storage/azure-storage-file-share/tests/test_share_async.py b/sdk/storage/azure-storage-file-share/tests/test_share_async.py index a04a18bb4977..3691ab2e7ec6 100644 --- a/sdk/storage/azure-storage-file-share/tests/test_share_async.py +++ b/sdk/storage/azure-storage-file-share/tests/test_share_async.py @@ -749,6 +749,29 @@ async def test_list_shares_no_options(self, **kwargs): self.assertNamedItemInContainer(shares, share.share_name) await self._delete_shares(share.share_name) + @FileSharePreparer() + @recorded_by_proxy_async + async def test_list_shares_enable_snapshot_virtual_directory_access(self, **kwargs): + premium_storage_file_account_name = kwargs.pop("premium_storage_file_account_name") + premium_storage_file_account_key = kwargs.pop("premium_storage_file_account_key") + + self._setup(premium_storage_file_account_name, premium_storage_file_account_key) + share = await self._create_share(protocols="NFS", enable_snapshot_virtual_directory_access=False) + + # Act + list_props = [] + async for s in self.fsc.list_shares(): + list_props.append(s) + share_props = await share.get_share_properties() + + # Assert + assert list_props[0].protocols[0] == 'NFS' + assert list_props[0].enable_snapshot_virtual_directory_access is False + + assert share_props.protocols[0] == 'NFS' + assert share_props.enable_snapshot_virtual_directory_access is False + await self._delete_shares() + @FileSharePreparer() @recorded_by_proxy_async async def test_list_shares_no_options_for_premium_account(self, **kwargs): diff --git a/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/authentication.py b/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/authentication.py index abbbfe88127f..e4d5ed730846 100644 --- a/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/authentication.py +++ b/sdk/storage/azure-storage-queue/azure/storage/queue/_shared/authentication.py @@ -8,6 +8,7 @@ import re from typing import List, Tuple from urllib.parse import unquote, urlparse +from functools import cmp_to_key try: from yarl import URL @@ -27,6 +28,66 @@ logger = logging.getLogger(__name__) +table_lv0 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725, + 0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e, + 0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, + 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, + 0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, + 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, + 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0, +] + +table_lv4 = [ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +] + +def compare(lhs: str, rhs: str) -> int: # pylint:disable=too-many-return-statements + tables = [table_lv0, table_lv4] + curr_level, i, j, n = 0, 0, 0, len(tables) + lhs_len = len(lhs) + rhs_len = len(rhs) + while curr_level < n: + if curr_level == (n - 1) and i != j: + if i > j: + return -1 + if i < j: + return 1 + return 0 + + w1 = tables[curr_level][ord(lhs[i])] if i < lhs_len else 0x1 + w2 = tables[curr_level][ord(rhs[j])] if j < rhs_len else 0x1 + + if w1 == 0x1 and w2 == 0x1: + i = 0 + j = 0 + curr_level += 1 + elif w1 == w2: + i += 1 + j += 1 + elif w1 == 0: + i += 1 + elif w2 == 0: + j += 1 + else: + if w1 < w2: + return -1 + if w1 > w2: + return 1 + return 0 + return 0 + + # wraps a given exception with the desired exception type def _wrap_exception(ex, desired_type): msg = "" @@ -36,8 +97,6 @@ def _wrap_exception(ex, desired_type): # This method attempts to emulate the sorting done by the service def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str, str]]: - # Define the custom alphabet for weights - custom_weights = "-!#$%&*.^_|~+\"\'(),/`~0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]abcdefghijklmnopqrstuvwxyz{}" # Build dict of tuples and list of keys header_dict = {} @@ -46,9 +105,8 @@ def _storage_header_sort(input_headers: List[Tuple[str, str]]) -> List[Tuple[str header_dict[k] = v header_keys.append(k) - # Sort according to custom defined weights try: - header_keys = sorted(header_keys, key=lambda word: [custom_weights.index(c) for c in word]) + header_keys = sorted(header_keys, key=cmp_to_key(compare)) except ValueError as exc: raise ValueError("Illegal character encountered when sorting headers.") from exc