From 8c9a013cff4ae0cad4c0f4d11585f3854a328719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kub=C3=ADk?= Date: Sat, 30 Nov 2024 01:14:33 +0100 Subject: [PATCH] fix(az): catch HttpResponseError in _check_hns (#487) * fix(az): catch HttpResponseError in _check_hns * chore: update HISTORY.md --- HISTORY.md | 1 + cloudpathlib/azure/azblobclient.py | 36 +++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b39bee8c..2e379a6b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,7 @@ ## Unreleased - Fixed `CloudPath(...) / other` to correctly attempt to fall back on `other`'s `__rtruediv__` implementation, in order to support classes that explicitly support the `/` with a `CloudPath` instance. Previously, this would always raise a `TypeError` if `other` were not a `str` or `PurePosixPath`. (PR [#479](https://github.com/drivendataorg/cloudpathlib/pull/479)) +- Fixed an uncaught exception on Azure Gen2 storage accounts with HNS enabled when used with `DefaultAzureCredential`. (Issue [#486](https://github.com/drivendataorg/cloudpathlib/issues/486)) ## v0.20.0 (2024-10-18) diff --git a/cloudpathlib/azure/azblobclient.py b/cloudpathlib/azure/azblobclient.py index 80a4a92b..acf49908 100644 --- a/cloudpathlib/azure/azblobclient.py +++ b/cloudpathlib/azure/azblobclient.py @@ -1,9 +1,11 @@ from datetime import datetime, timedelta import mimetypes import os +from http import HTTPStatus from pathlib import Path from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union +from typing_extensions import cast from ..client import Client, register_client_class from ..cloudpath import implementation_registry @@ -13,7 +15,7 @@ try: - from azure.core.exceptions import ResourceNotFoundError + from azure.core.exceptions import HttpResponseError, ResourceNotFoundError from azure.core.credentials import AzureNamedKeyCredential from azure.storage.blob import ( @@ -202,20 +204,32 @@ def _check_hns(self, cloud_path: AzureBlobPath) -> Optional[bool]: try: account_info = self.service_client.get_account_information() # type: ignore self._hns_enabled = account_info.get("is_hns_enabled", False) # type: ignore + + # get_account_information() not supported with this credential; we have to fallback to + # checking if the root directory exists and is a has 'metadata': {'hdi_isfolder': 'true'} except ResourceNotFoundError: - # get_account_information() not supported with this credential; we have to fallback to - # checking if the root directory exists and is a has 'metadata': {'hdi_isfolder': 'true'} - root_dir = self.service_client.get_blob_client( - container=cloud_path.container, blob="/" - ) - self._hns_enabled = ( - root_dir.exists() - and root_dir.get_blob_properties().metadata.get("hdi_isfolder", False) - == "true" - ) + return self._check_hns_root_metadata(cloud_path) + except HttpResponseError as error: + if error.status_code == HTTPStatus.FORBIDDEN: + self._check_hns_root_metadata(cloud_path) + else: + raise return self._hns_enabled + def _check_hns_root_metadata(self, cloud_path: AzureBlobPath) -> bool: + root_dir = self.service_client.get_blob_client( + container=cloud_path.container, blob="/" + ) + + self._hns_enabled = ( + root_dir.exists() + and root_dir.get_blob_properties().metadata.get("hdi_isfolder", False) + == "true" + ) + + return cast(bool, self._hns_enabled) + def _get_metadata( self, cloud_path: AzureBlobPath ) -> Union["BlobProperties", "FileProperties", Dict[str, Any]]: