Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Update ABSStore to current Azure Storage API version - #620

Closed
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore
Closed

Update ABSStore to current Azure Storage API version#620
jhamman wants to merge 7 commits into
zarr-developers:masterfrom
jhamman:fix/absstore

Conversation

@jhamman

Copy link
Copy Markdown
Member

This PR addresses #618 and updates the minimum supported version of azure-storage-blob>=12.

You'll note the tests will fail for now. I haven't figured out where the emulation feature of the azure api is in the new version. I'll keep digging but tests on actual data are indicating this is mostly functional now.

cc @tjcrone

TODO:

  • Add unit tests and/or doctests in docstrings
  • Add docstrings and API docs for any new/modified user-facing classes and functions
  • New/modified features documented in docs/tutorial.rst
  • Changes documented in docs/release.rst
  • AppVeyor and Travis CI passes
  • Test coverage is 100% (Coveralls passes)

Comment threadzarr/storage.py Outdated
Comment on lines +2259 to +2262
# It is possible azure.store.blob doesn't provide the content_length attribute on
# the blob propoeries object anymore. Something to look into.
#
# def getsize(self, path=None):

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this as one major thing I haven't figured out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tjcrone - I'm pretty confused here. Any points on the listdir method would be much appreciated.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional methods `listdir` (list members of a "directory") and `rmdir` (remove all

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
Comment on lines +2268 to +2270
# if self.client.get_blob_client(fs_path).exists():
# return self.client.get_blob_properties(self.container,
# fs_path).properties.content_length

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR @jhamman

I think the way to go here would be like so

blob_client=self.client.get_blob_client(fs_path) # blob may or may not existifblob_client.exists():
returnblob_client.get_blob_properties().size

Comment threadzarr/storage.py Outdated
Comment on lines 2243 to 2248
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?
items.append(self._strip_prefix_from_path(blob.name, dir_path))
else:
items.append(self._strip_prefix_from_path(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The listdir method which was implemented using the old version of the azure storage library, used the list_blobs method in that old library. That method in the old library had a delimiter parameter, which helps in listing directories much easier. For example if we have two directories in the container, say foo/ and bar/ each of which could have hundreds of thousands of blobs, we would not want to list all of them in the list_blobs operations. Instead if you specify delimiter='/', you would only be returned ['foo/', 'bar/'] in the list_blobs operation. The new version of the azure storage library seems not to have the delimiter option in the list_blobs function. Instead there is a function walk_blobs which has this parameter. I think that function would be more appropriate here instead of list_blobs. I think just replacing self.client.list_blobs(name_starts_with=dir_path) with self.client.walk_blobs(name_starts_with=dir_path, delimiter='/'), should work.

Comment threadzarr/storage.py Outdated
if type(blob) == Blob:
for blob in self.client.list_blobs(name_starts_with=dir_path):
# items.append(self._strip_prefix_from_path(blob.name, dir_path))
if '/' not in blob.name: # what is this doing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the list_blobs will be replaced with walk_blobs with a delimiter='/' parameter, the return items will be the contents of the directory. So if the directory structure is like so:

.zgroup
foo/.zgroup
foo/bar/.zarray

then walk_blobs(self.container, name_starts_with='foo/', delimiter='/') will return ['foo/.zgroup', 'foo/bar/']
The .zgroup will be a case of the first condition if '/' not in blob.name, which if true, means that we have a blob, otherwise it's a directory. And the code in the else block removes the '/' from the directory.
So return value of listdir('foo') should be ['.zgroup', 'bar'].

I admit, it is not immediately obvious. Any refactoring would be greatly appreciated.

@jhamman

Copy link
Copy Markdown
MemberAuthor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

@shikharsg

shikharsg commented Sep 28, 2020

Copy link
Copy Markdown
Contributor

@shikharsg - do you happen to understand how we can do the blob emulation with the new version of the api? The current test uses:

defcreate_store(self, prefix=None):
asb=pytest.importorskip("azure.storage.blob")
blob_client=asb.BlockBlobService(is_emulated=True)
blob_client.delete_container('test')
blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='foo',
account_key='bar', blob_service_kwargs={'is_emulated': True})
store.rmdir()
returnstore

As far as I can tell, this option is no longer available in the azure.storage.blob.

Yes, it seems like that option is not available in the new version.

So the azure storage emulators (azurite or storage emulator) run on a default host and port and have default credentials. The connection string is:

DefaultEndpointsProtocol=https;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=https://127.0.0.1:10000/devstoreaccount1;

Should be something like this I think:

blob_client=asb.BlobServiceClient.from_connection_string(conn_str="PUT CONNECTION STRING HERE")
blob_client.delete_container('test') blob_client.create_container('test')
store=ABSStore(container='test', prefix=prefix, account_name='devstoreaccount1', account_key='Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==') 

@tjcrone

Copy link
Copy Markdown
Member

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

@tjcrone

Copy link
Copy Markdown
Member

@alimanfoo, what is your preferred method for me to work on this PR? Should we merge into a fix/absstore branch in this repo
that we can continue working on or would you like me to grab this branch from @jhamman and start a new PR? Thanks!

@shikharsg

Copy link
Copy Markdown
Contributor

I have some time to help move this forward. @jhamman and @shikharsg, what is the primary blocker on this PR? Thanks!

I think main blocker is getting the tests to work with the emulator. Once the tests work it should most likely tell you if something else was breaking as well. Since the new storage library does not have a convenient way to connect to the emulator, we will have to use the default credentials that the emulator runs on, to connect to it. See this comment.

@joshmoore

Copy link
Copy Markdown
Member

Merged in master and pushed to kick off the tests again.

@joshmoore

Copy link
Copy Markdown
Member

E AttributeError: module 'azure.storage.blob' has no attribute 'BlockBlobService'

@jhamman

Copy link
Copy Markdown
MemberAuthor

Thanks @joshmoore!

I've actually been thinking this we should discuss closing this PR and deprecating the ABSStore all together. Lately, I've been using the adlfs mapper implementation with increasing success. Given the complexities with maintaining the test suite and interface API compatibility, maybe it would be wise to consolidate effort elsewhere? Certainly open to other opinions (@tjcrone, @shikharsg) but I'm thinking I'm unlikely to bring this one across the finish line.

@joshmoore

Copy link
Copy Markdown
Member

@jhamman: this can now be closed with #759 merged, right? That way #781 (which implements #764) doesn't conflict. But if you are still thinking about refactoring ABSStore further, I can hold off.

@jhamman

Copy link
Copy Markdown
MemberAuthor

Yes! Closing.

@jhammanjhamman closed this Jun 17, 2021
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jhamman@shikharsg@tjcrone@joshmoore