Skip to content

Commit a5f0670

Browse files
feat: add class method from_container_url()
1 parent 78e18fd commit a5f0670

2 files changed

Lines changed: 62 additions & 2 deletions

File tree

fourinsight/engineroom/utils/_core.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pandas as pd
1010
from azure.core.exceptions import ResourceNotFoundError
11-
from azure.storage.blob import BlobClient
11+
from azure.storage.blob import BlobClient, ContainerClient
1212

1313
from ._constants import API_BASE_URL
1414

@@ -188,8 +188,37 @@ def __init__(
188188
)
189189
super().__init__(encoding=encoding, newline=newline)
190190

191+
@classmethod
192+
def from_container_url(
193+
cls, container_url, blob_name, encoding="utf-8", newline="\n"
194+
):
195+
"""
196+
Instantiate from a container-level SAS URL.
197+
198+
Parameters
199+
----------
200+
container_url : str
201+
Full SAS URL for the container, e.g.
202+
``"https://<account>.blob.core.windows.net/<container>?sv=...&sig=..."``.
203+
blob_name : str
204+
The name of the blob with which to interact.
205+
encoding : str
206+
Defaults to 'utf-8'.
207+
newline : str
208+
Defaults to '\\n'.
209+
"""
210+
211+
instance = cls.__new__(cls)
212+
instance._blob_client = ContainerClient.from_container_url(
213+
container_url
214+
).get_blob_client(blob_name)
215+
instance._blob_name = blob_name
216+
BaseHandler.__init__(instance, encoding=encoding, newline=newline)
217+
return instance
218+
191219
def __repr__(self):
192-
return f"AzureBlobHandler {self._container_name}/{self._blob_name}"
220+
# return f"AzureBlobHandler {self._container_name}/{self._blob_name}"
221+
return f"AzureBlobHandler {self._blob_client.container_name}/{self._blob_client.blob_name}"
193222

194223
def _pull(self):
195224
return self._blob_client.download_blob().readinto(self.buffer)

tests/test_core.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ def azure_blob_handler_mocked(mock_from_connection_string):
5252
blob_name = "some_blob_name"
5353
handler = AzureBlobHandler(connection_string, container_name, blob_name)
5454

55+
handler._blob_client.container_name = "some_container_name"
56+
handler._blob_client.blob_name = "some_blob_name"
57+
5558
remote_content = open(REMOTE_FILE_PATH, mode="r").read()
5659
handler._blob_client.download_blob.return_value.readinto.side_effect = (
5760
lambda buffer: handler.write(remote_content)
@@ -271,6 +274,34 @@ def test_push(self, azure_blob_handler_mocked):
271274
content, overwrite=True
272275
)
273276

277+
@patch("fourinsight.engineroom.utils._core.ContainerClient")
278+
def test_blob_client_is_constructed_from_url(self, mock_container_client_cls):
279+
AzureBlobHandler.from_container_url("some container", "state/state.json")
280+
mock_container_client_cls.from_container_url.assert_called_once_with(
281+
"some container"
282+
)
283+
284+
@patch("fourinsight.engineroom.utils._core.ContainerClient")
285+
def test_blob_client_gets_correct_blob(self, mock_container_client_cls):
286+
AzureBlobHandler.from_container_url("some container", "state/state.json")
287+
mock_container_client_cls.from_container_url().get_blob_client.assert_called_once_with(
288+
"state/state.json"
289+
)
290+
291+
@patch("fourinsight.engineroom.utils._core.ContainerClient")
292+
def test_repr(self, mock_container_client_cls):
293+
mock_blob_client = MagicMock()
294+
mock_blob_client.container_name = "my-project"
295+
mock_blob_client.blob_name = "state/state.json"
296+
mock_container_client_cls.from_container_url().get_blob_client.return_value = (
297+
mock_blob_client
298+
)
299+
300+
handler = AzureBlobHandler.from_container_url(
301+
"some container", "state/state.json"
302+
)
303+
assert repr(handler) == "AzureBlobHandler my-project/state/state.json"
304+
274305

275306
class Test_PersistentDict:
276307
def test__init__(self, local_file_handler_empty):

0 commit comments

Comments
 (0)