-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Storage Blobs - prototype API listing for comments #7373
Conversation
Can one of the admins verify this patch? |
def __exit__(self, *args): ... | ||
def __init__(self, blob_url: str, container: container=None, blob: blob=None, snapshot: str=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def abort_copy(self, copy_id: copy_id, **kwargs) -> None: ... | ||
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should align the parameter names with the result of discussion on ETag handling.
Short term, let's make these if_*
parameters keyword-only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method signature has already been condensed, so maybe this was generated from out-of-date code?
https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/storage/azure-storage-blob/azure/storage/blob/aio/blob_client_async.py#L967
def __init__(self, blob_url: str, container: container=None, blob: blob=None, snapshot: str=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def abort_copy(self, copy_id: copy_id, **kwargs) -> None: ... | ||
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... | ||
def append_block(self, data, length: int=None, validate_content: bool=False, maxsize_condition: int=None, appendpos_condition: int=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, encoding, cpk, timeout, **kwargs) -> dict(str, Any): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should appendpos_condition
be **kwonly
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maxsize_condition can be **kwonly
def __init__(self, blob_url: str, container: container=None, blob: blob=None, snapshot: str=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def abort_copy(self, copy_id: copy_id, **kwargs) -> None: ... | ||
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... | ||
def append_block(self, data, length: int=None, validate_content: bool=False, maxsize_condition: int=None, appendpos_condition: int=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, encoding, cpk, timeout, **kwargs) -> dict(str, Any): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make validate_content
a **kwonly
parameter.
def get_service_stats(self, timeout: int=None, **kwargs) -> ~azure.storage.blob._generated.models.StorageServiceStats: ... | ||
def get_user_delegation_key(self, key_start_time: datetime, key_expiry_time: datetime, timeout: int=None, **kwargs) -> ~azure.storage.blob._shared.models.UserDelegationKey: ... | ||
def list_containers(self, name_starts_with: str=None, include_metadata: bool=False, results_per_page: int=None, timeout: int=None, **kwargs) -> ~azure.core.async_paging.AsyncItemPaged[~azure.storage.blob.models.ContainerProperties]: ... | ||
def set_service_properties(self, logging: logging=None, hour_metrics: hour_metrics=None, minute_metrics: minute_metrics=None, cors: cors=None, target_version: str=None, delete_retention_policy: delete_retention_policy=None, static_website: static_website=None, timeout: int=None, **kwargs) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parameter name logging
is somewhat unfortunate (since it is not related to the standard logging
module in any way)
def stage_block_from_url(self, block_id: str, source_url, source_offset=None, source_length=None, source_content_md5: bytearray=None, *, lease, cpk, timeout, **kwargs) -> None: ... | ||
def start_copy_from_url(self, source_url: str, metadata: metadata=None, incremental_copy: bool=False, *, source_if_modified_since, source_if_unmodified_since, source_if_match, source_if_none_match, destination_if_modified_since, destination_if_unmodified_since, destination_if_match, destination_if_none_match, destination_lease, source_lease, timeout, premium_page_blob_tier, requires_sync, **kwargs) -> Dict[str, Union[str, datetime]]: ... | ||
def undelete_blob(self, *, timeout, **kwargs) -> None: ... | ||
def upload_blob(self, data, blob_type: ~azure.storage.blob.models.BlobType=BlockBlob, overwrite: bool=False, length: int=None, metadata: metadata=None, content_settings: ~azure.storage.blob.models.ContentSettings=None, validate_content: bool=False, max_connections: int=1, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, premium_page_blob_tier, maxsize_condition, cpk, encoding, timeout, **kwargs) -> dict[str, Any]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should upload overwrite an existing blob by default?
Currently this throws
RequestId:57d5ae44-401e-00e7-3929-73f5dc000000
Time:2019-09-24T22:42:22.4225937Z
ErrorCode:BlobAlreadyExists
Error:None
while in .Net it simply overwrites...not sure which way to go..but i believe its good to make it consistent,
Currently to overwrite a blob in python overwrite=True is passed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should have same behaviour across languages - also part of the etag conversation.
More discussion offline.
def serialize(self, keep_readonly: bool=False) -> dict: ... | ||
def validate(self) -> list: ... | ||
|
||
class AccountPermissions(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the other languages, we appear to include Sas
(or similar) as part of the type-name. Should we do the same here and for the other "Permissions" types here?
def __add__(self, other): ... | ||
def __init__(self, read: bool=False, write: bool=False, delete: bool=False, list: bool=False, add: bool=False, create: bool=False, update: bool=False, process: bool=False, _str: str=None, *, AccountPermissions.ADD, AccountPermissions.CREATE, AccountPermissions.DELETE, AccountPermissions.LIST, AccountPermissions.PROCESS, AccountPermissions.READ, AccountPermissions.UPDATE, AccountPermissions.WRITE): ... | ||
def __or__(self, other): ... | ||
def __str__(self): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the other languages, there is parse
like method that allows you to go from a permission string (e.g "rld
") to an object representing the permission. Should we have a similar one here (or is there some other way to go from these strings to permissions?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rakshith91 , can you verify that all the consumers of this class takes a string. And please add a suggestion/issue to support a from_string
class method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to be non-lossy between versions - more consideration needed
def create_append_blob(self, content_settings: ~azure.storage.blob.models.ContentSettings=None, metadata: metadata=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> dict[str, Any]: ... | ||
def create_page_blob(self, size: int, content_settings: ~azure.storage.blob.models.ContentSettings=None, sequence_number: int=None, metadata: metadata=None, premium_page_blob_tier: ~azure.storage.blob.models.PremiumPageBlobTier=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> dict[str, Any]: ... | ||
def create_snapshot(self, metadata: metadata=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, lease, cpk, timeout, **kwargs) -> dict[str, Any]: ... | ||
def delete_blob(self, delete_snapshots: str=False, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is delete_snapshots
' a string? I think it should be bool.
EDIT: seems like a typo here.
def create_page_blob(self, size: int, content_settings: ~azure.storage.blob.models.ContentSettings=None, sequence_number: int=None, metadata: metadata=None, premium_page_blob_tier: ~azure.storage.blob.models.PremiumPageBlobTier=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> dict[str, Any]: ... | ||
def create_snapshot(self, metadata: metadata=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, lease, cpk, timeout, **kwargs) -> dict[str, Any]: ... | ||
def delete_blob(self, delete_snapshots: str=False, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> None: ... | ||
def download_blob(self, offset: int=None, length: int=None, validate_content: bool=False, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> ~azure.storage.blob._blob_utils.StorageStreamDownloader: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name length is not accurate (and misguiding). It should be 'range_end' ?
For example `download_blob(offset=5, length=5) returns 1 byte, not 5.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move some of the options currently passed into the StreamDownloader, specifically max_connections
up to this functions. Not sure if it should be keyword only or regular - either was consistent with upload.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rakshith91 FYI I opened #7061 about this last month (very confusing, as it is)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change: Also, max_connections
should be renamed to max_concurrency
start:start | ||
|
||
def __eq__(self, other): ... | ||
def __init__(self, permission: permission=None, expiry: expiry=None, start: start=None): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rakshith91, what is the semantics if you create an empty AccessPolicy (i.e. don't specify any parameters to the constructor)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default permission is None, same with start and expiry
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe permission shouldn't be optional
@@ -0,0 +1,591 @@ | |||
class AccessPolicy(Model, object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rakshith91, should this inherit Model or contain a model?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AccessPolicy
just exposes the generated AccessPolicy model which in turn inherits from Model
.
I think its fine.
_str:str | ||
|
||
def __add__(self, other): ... | ||
def __init__(self, read: bool=False, write: bool=False, delete: bool=False, list: bool=False, add: bool=False, create: bool=False, update: bool=False, process: bool=False, _str: str=None, *, AccountPermissions.ADD, AccountPermissions.CREATE, AccountPermissions.DELETE, AccountPermissions.LIST, AccountPermissions.PROCESS, AccountPermissions.READ, AccountPermissions.UPDATE, AccountPermissions.WRITE): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change: I would suggest removing the "enum-like" list parameters. @rakshith91, please open an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: similar for blobpermissions
_str:str | ||
|
||
def __add__(self, other): ... | ||
def __init__(self, read: bool=False, write: bool=False, delete: bool=False, list: bool=False, add: bool=False, create: bool=False, update: bool=False, process: bool=False, _str: str=None, *, AccountPermissions.ADD, AccountPermissions.CREATE, AccountPermissions.DELETE, AccountPermissions.LIST, AccountPermissions.PROCESS, AccountPermissions.READ, AccountPermissions.UPDATE, AccountPermissions.WRITE): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change: Please remove the _str
constructor parameter. Internal positional parameters are pure evil.
process:bool | ||
_str:str | ||
|
||
def __add__(self, other): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change: @rakshith91, what does adding two AccountPermissions
do? And what is the difference between __add__
and __or__
(in the implementation of this class). Unless we are doing something sane here, please remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
adding two AccountPermissions will give you both permissons
ex:
a = AccountPermissions.READ (read =True)
b = AccountPermissions.WRITE (write=True)
=> a + b = AccountPermissions.READ + AccountPermissions.WRITE (read=True, write = True)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove these since we are removing the enum like params
state:str | ||
size:int | ||
|
||
def __init__(self, block_id: str=None, state: str=Latest, *, size): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change:
@rakshith91, what happens if I don't provide a block_id
? I very much assume it is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is required.
It will eventually be passed into encode_base64(str(block_id))
which encode the string 'None'
def __aexit__(self, *args): ... | ||
def __enter__(self): ... | ||
def __exit__(self, *args): ... | ||
def __init__(self, blob_url: str, container: container=None, blob: blob=None, snapshot: str=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The blob
and container
parameters should only take strings.
We should move the handling of the BlobProperties.name
and ContainerProperties.name
to the parent get_client
functions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the construct parameters non-optional:
def __init__(self, account_url, container, blob, snapshot=None, credential=None, **kwargs)
Then add a classmethod:
@classmethod
def from_url(blob_url, snapshot=None, credential=None, **kwargs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move loop
into **kwargs
and verify that it's being passed through to the creation of the transport.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If blob
and container
are strings, they should be renamed to blob_name
and container_name
This also applies to the from_connection_string
classmethod.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Continue offline
def __enter__(self): ... | ||
def __exit__(self, *args): ... | ||
def __init__(self, blob_url: str, container: container=None, blob: blob=None, snapshot: str=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def abort_copy(self, copy_id: copy_id, **kwargs) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copy_id can be a dict, str, or BlobProperties
def __exit__(self, *args): ... | ||
def __init__(self, blob_url: str, container: container=None, blob: blob=None, snapshot: str=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def abort_copy(self, copy_id: copy_id, **kwargs) -> None: ... | ||
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should lease_id be proposed_lease_id? Compare with other languages
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leave as is
def __init__(self, blob_url: str, container: container=None, blob: blob=None, snapshot: str=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def abort_copy(self, copy_id: copy_id, **kwargs) -> None: ... | ||
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... | ||
def append_block(self, data, length: int=None, validate_content: bool=False, maxsize_condition: int=None, appendpos_condition: int=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, encoding, cpk, timeout, **kwargs) -> dict(str, Any): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
data can be type: Union[Anystr, Iterable[AnyStr], IO[AnyStr]]
def abort_copy(self, copy_id: copy_id, **kwargs) -> None: ... | ||
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... | ||
def append_block(self, data, length: int=None, validate_content: bool=False, maxsize_condition: int=None, appendpos_condition: int=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, encoding, cpk, timeout, **kwargs) -> dict(str, Any): ... | ||
def append_block_from_url(self, copy_source_url: str, source_range_start: int=None, source_range_end: int=None, source_content_md5: bytearray=None, maxsize_condition: int=None, appendpos_condition: int=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, source_if_modified_since, source_if_unmodified_since, source_if_match, source_if_none_match, cpk, timeout, **kwargs): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
source_content_md5, maxsize_condition, appendpos_condition can all be **kwonly
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... | ||
def append_block(self, data, length: int=None, validate_content: bool=False, maxsize_condition: int=None, appendpos_condition: int=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, encoding, cpk, timeout, **kwargs) -> dict(str, Any): ... | ||
def append_block_from_url(self, copy_source_url: str, source_range_start: int=None, source_range_end: int=None, source_content_md5: bytearray=None, maxsize_condition: int=None, appendpos_condition: int=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, source_if_modified_since, source_if_unmodified_since, source_if_match, source_if_none_match, cpk, timeout, **kwargs): ... | ||
def clear_page(self, start_range: int, end_range: int, *, lease, if_sequence_number_lte, if_sequence_number_lt, if_sequence_number_eq, if_modified_since, if_unmodified_since, if_match, if_none_match, premium_page_blob_tier, timeout, **kwargs) -> dict(str, Any): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename start_range and end_range to offset
and length
?
also it seems like in some APIs, offset
and length
are behaving like start_range and end_range
#7061
cc: @annatisch @johanste
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
start_offset and end_offset? Index? Inclusive values? offset
and length
for everything and be consistent!
"Page ranges" is page blob terminology.
More discussion needed here..... also check our behaviour matches terminology for length
def clear_page(self, start_range: int, end_range: int, *, lease, if_sequence_number_lte, if_sequence_number_lt, if_sequence_number_eq, if_modified_since, if_unmodified_since, if_match, if_none_match, premium_page_blob_tier, timeout, **kwargs) -> dict(str, Any): ... | ||
def commit_block_list(self, block_list: list, content_settings: ~azure.storage.blob.models.ContentSettings=None, metadata: metadata=None, validate_content: bool=False, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> dict(str, Any): ... | ||
def create_append_blob(self, content_settings: ~azure.storage.blob.models.ContentSettings=None, metadata: metadata=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> dict[str, Any]: ... | ||
def create_page_blob(self, size: int, content_settings: ~azure.storage.blob.models.ContentSettings=None, sequence_number: int=None, metadata: metadata=None, premium_page_blob_tier: ~azure.storage.blob.models.PremiumPageBlobTier=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> dict[str, Any]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sequence_number can be moved to **kwonly
def __len__(self): ... | ||
def content_as_bytes(self, max_connections: int=1) -> bytes: ... | ||
def content_as_text(self, max_connections: int=1, encoding=UTF-8) -> str: ... | ||
def download_to_stream(self, stream, max_connections=1) -> Any: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should rewrite this API to match the Python stream interface:
https://docs.python.org/3/library/io.html
content_as_bytes => readall (with optional encoding parameter to decode to str)
download_to_stream -> readinto
add read(size)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johanste - you mentioned supporting a open
and close
interface here - what do you envision that translating to in terms service calls?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also - do you believe we should remove the iterable functionality of this object entirely?
Currently a file-like object can be read iterably
def download_blob(self, offset: int=None, length: int=None, validate_content: bool=False, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> ~azure.storage.blob._blob_utils.StorageStreamDownloader: ... | ||
@classmethod | ||
def from_connection_string(cls, conn_str: str, container: container, blob: blob, snapshot: str=None, credential=None, **kwargs): ... | ||
def generate_shared_access_signature(self, permission: permission=None, expiry: expiry=None, start: start=None, policy_id: str=None, ip: str=None, protocol: str=None, account_name: str=None, cache_control: str=None, content_disposition: str=None, content_encoding: str=None, content_language: str=None, content_type: str=None, user_delegation_key: ~azure.storage.blob._shared.models.UserDelegationKey=None) -> str: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
account_name
, cache_control
, protocol
, all content_*
can be moved to **kwonly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compare to simplest overloads in .Net/Java?
def generate_shared_access_signature(self, permission: permission=None, expiry: expiry=None, start: start=None, policy_id: str=None, ip: str=None, protocol: str=None, account_name: str=None, cache_control: str=None, content_disposition: str=None, content_encoding: str=None, content_language: str=None, content_type: str=None, user_delegation_key: ~azure.storage.blob._shared.models.UserDelegationKey=None) -> str: ... | ||
def get_account_information(self, **kwargs) -> dict(str, str): ... | ||
def get_blob_properties(self, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> ~azure.storage.blob.models.BlobProperties: ... | ||
def get_block_list(self, block_list_type: str=committed, *, lease, timeout, **kwargs) -> tuple(list(~azure.storage.blob.models.BlobBlock), list(~azure.storage.blob.models.BlobBlock)): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
breaking change: return a single list instead of two separate lists for committed and un-committed blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Further discussion. Low traffic anyway.
def get_account_information(self, **kwargs) -> dict(str, str): ... | ||
def get_blob_properties(self, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs) -> ~azure.storage.blob.models.BlobProperties: ... | ||
def get_block_list(self, block_list_type: str=committed, *, lease, timeout, **kwargs) -> tuple(list(~azure.storage.blob.models.BlobBlock), list(~azure.storage.blob.models.BlobBlock)): ... | ||
def get_page_ranges(self, start_range: int=None, end_range: int=None, previous_snapshot_diff: str=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> tuple(list(dict(str, str), list(dict(str, str)): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
confirm behavior of end_range
and start_range
vs the behavior of offset
and length
.
- Check with other languages
- Make it consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
previous_snapshot_diff
can be **kwonly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
breaking change:
Make it similar to list_blob_blocks.
- Create a PageRange model similar to
BlobBlock
, which specifiesclear == True|False
- Return a single list instead of tuple
def get_page_ranges(self, start_range: int=None, end_range: int=None, previous_snapshot_diff: str=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> tuple(list(dict(str, str), list(dict(str, str)): ... | ||
def resize_blob(self, size: int, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, premium_page_blob_tier, timeout, **kwargs) -> dict(str, Any): ... | ||
def set_blob_metadata(self, metadata: metadata=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs): ... | ||
def set_http_headers(self, content_settings: ~azure.storage.blob.models.ContentSettings=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> Dict[str, Any]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
content_settings should be **kwonly
def set_sequence_number(self, sequence_number_action: str, sequence_number: str=None, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, timeout, **kwargs) -> dict(str, Any): ... | ||
def set_standard_blob_tier(self, standard_blob_tier: standard_blob_tier, *, timeout, lease, **kwargs) -> None: ... | ||
def stage_block(self, block_id: str, data, length: int=None, validate_content: bool=False, *, lease, encoding, cpk, timeout, **kwargs) -> None: ... | ||
def stage_block_from_url(self, block_id: str, source_url, source_offset=None, source_length=None, source_content_md5: bytearray=None, *, lease, cpk, timeout, **kwargs) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
double check that offset and length aren't acting like start_range and end_range
def stage_block(self, block_id: str, data, length: int=None, validate_content: bool=False, *, lease, encoding, cpk, timeout, **kwargs) -> None: ... | ||
def stage_block_from_url(self, block_id: str, source_url, source_offset=None, source_length=None, source_content_md5: bytearray=None, *, lease, cpk, timeout, **kwargs) -> None: ... | ||
def start_copy_from_url(self, source_url: str, metadata: metadata=None, incremental_copy: bool=False, *, source_if_modified_since, source_if_unmodified_since, source_if_match, source_if_none_match, destination_if_modified_since, destination_if_unmodified_since, destination_if_match, destination_if_none_match, destination_lease, source_lease, timeout, premium_page_blob_tier, requires_sync, **kwargs) -> Dict[str, Union[str, datetime]]: ... | ||
def undelete_blob(self, *, timeout, **kwargs) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: undelete sounds odd..,may rename to restore_blob?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keyvault uses "recover" and "soft delete"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider recover as in keyvault.
def start_copy_from_url(self, source_url: str, metadata: metadata=None, incremental_copy: bool=False, *, source_if_modified_since, source_if_unmodified_since, source_if_match, source_if_none_match, destination_if_modified_since, destination_if_unmodified_since, destination_if_match, destination_if_none_match, destination_lease, source_lease, timeout, premium_page_blob_tier, requires_sync, **kwargs) -> Dict[str, Union[str, datetime]]: ... | ||
def undelete_blob(self, *, timeout, **kwargs) -> None: ... | ||
def upload_blob(self, data, blob_type: ~azure.storage.blob.models.BlobType=BlockBlob, overwrite: bool=False, length: int=None, metadata: metadata=None, content_settings: ~azure.storage.blob.models.ContentSettings=None, validate_content: bool=False, max_connections: int=1, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, premium_page_blob_tier, maxsize_condition, cpk, encoding, timeout, **kwargs) -> dict[str, Any]: ... | ||
def upload_page(self, page: bytes, start_range: int, end_range: int, length: int=None, validate_content: bool=False, *, lease, if_sequence_number_lte, if_sequence_number_lt, if_sequence_number_eq, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, encoding, timeout, **kwargs) -> dict(str, Any): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
startrange and end_range should be offset and length
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure why length is needed?
def undelete_blob(self, *, timeout, **kwargs) -> None: ... | ||
def upload_blob(self, data, blob_type: ~azure.storage.blob.models.BlobType=BlockBlob, overwrite: bool=False, length: int=None, metadata: metadata=None, content_settings: ~azure.storage.blob.models.ContentSettings=None, validate_content: bool=False, max_connections: int=1, *, lease, if_modified_since, if_unmodified_since, if_match, if_none_match, premium_page_blob_tier, maxsize_condition, cpk, encoding, timeout, **kwargs) -> dict[str, Any]: ... | ||
def upload_page(self, page: bytes, start_range: int, end_range: int, length: int=None, validate_content: bool=False, *, lease, if_sequence_number_lte, if_sequence_number_lt, if_sequence_number_eq, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, encoding, timeout, **kwargs) -> dict(str, Any): ... | ||
def upload_pages_from_url(self, source_url: str, range_start: int, range_end: int, source_range_start: int, source_content_md5: bytes=None, *, source_if_modified_since, source_if_unmodified_since, source_if_match, source_if_none_match, lease, if_sequence_number_lte, if_sequence_number_lt, if_sequence_number_eq, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
source_content_md5 can be **kwonly
def upload_page(self, page: bytes, start_range: int, end_range: int, length: int=None, validate_content: bool=False, *, lease, if_sequence_number_lte, if_sequence_number_lt, if_sequence_number_eq, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, encoding, timeout, **kwargs) -> dict(str, Any): ... | ||
def upload_pages_from_url(self, source_url: str, range_start: int, range_end: int, source_range_start: int, source_content_md5: bytes=None, *, source_if_modified_since, source_if_unmodified_since, source_if_match, source_if_none_match, lease, if_sequence_number_lte, if_sequence_number_lt, if_sequence_number_eq, if_modified_since, if_unmodified_since, if_match, if_none_match, cpk, timeout, **kwargs): ... | ||
|
||
class BlobPermissions(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
breaking change: I would suggest removing the "enum-like" list parameters.
|
||
def __add__(self, other): ... | ||
def __init__(self, read: bool=False, add: bool=False, create: bool=False, write: bool=False, delete: bool=False, _str: str=None, *, BlobPermissions.ADD, BlobPermissions.CREATE, BlobPermissions.DELETE, BlobPermissions.READ, BlobPermissions.WRITE): ... | ||
def __or__(self, other): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add and or are doing the same thing...check the behaviour and remove/modify them
def __aexit__(self, *args): ... | ||
def __enter__(self): ... | ||
def __exit__(self, *args): ... | ||
def __init__(self, account_url: str, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like a method to initialize using account_name
and credential
instead of account_url
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
apparent security issues
def __enter__(self): ... | ||
def __exit__(self, *args): ... | ||
def __init__(self, account_url: str, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def create_container(self, name: str, metadata: metadata=None, public_access: public_access=None, timeout: int=None, **kwargs) -> ~azure.storage.blob.aio.container_client_async.ContainerClient: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should timeout be **kwonly
def from_connection_string(cls, conn_str: str, credential=None, **kwargs): ... | ||
def generate_shared_access_signature(self, resource_types: resource_types, permission: permission, expiry: expiry, start: start=None, ip: str=None, protocol: str=None) -> str: ... | ||
def get_account_information(self, **kwargs) -> dict(str, str): ... | ||
def get_blob_client(self, container: container, blob: blob, snapshot: snapshot=None) -> ~azure.storage.blob.aio.blob_client_async.BlobClient: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be changed to accept only string for container (which should be renamed to container_name).
def __exit__(self, *args): ... | ||
def __init__(self, account_url: str, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def create_container(self, name: str, metadata: metadata=None, public_access: public_access=None, timeout: int=None, **kwargs) -> ~azure.storage.blob.aio.container_client_async.ContainerClient: ... | ||
def delete_container(self, container: container, lease: ~azure.storage.blob.lease.LeaseClient=None, timeout: int=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, **kwargs) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
container should only be string and not ContainerProperties.
Also should be renamed to container_name
def get_blob_client(self, container: container, blob: blob, snapshot: snapshot=None) -> ~azure.storage.blob.aio.blob_client_async.BlobClient: ... | ||
def get_container_client(self, container: container) -> ~azure.core.blob.aio.container_client_async.ContainerClient: ... | ||
def get_service_properties(self, timeout: int=None, **kwargs) -> ~azure.storage.blob._generated.models.StorageServiceProperties: ... | ||
def get_service_stats(self, timeout: int=None, **kwargs) -> ~azure.storage.blob._generated.models.StorageServiceStats: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeout in all bsc related APIs should be **kwonly.
def __aexit__(self, *args): ... | ||
def __enter__(self): ... | ||
def __exit__(self, *args): ... | ||
def __init__(self, container_url: str, container: container=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
container should only be string and renamed to container_name.
def __init__(self, container_url: str, container: container=None, credential=None, loop=None, *, url, primary_endpoint, primary_hostname, secondary_endpoint, secondary_hostname, location_mode, **kwargs): ... | ||
def acquire_lease(self, lease_duration: int=-1, lease_id: str=None, timeout: int=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, **kwargs) -> ~azure.storage.blob.aio.lease_async.LeaseClient: ... | ||
def create_container(self, metadata: metadata=None, public_access: ~azure.storage.blob.models.PublicAccess=None, timeout: int=None, **kwargs) -> None: ... | ||
def delete_blob(self, blob: blob, delete_snapshots: str=None, lease: lease=None, timeout: int=None, *, if_modified_since, if_unmodified_since, if_match, if_none_match, **kwargs) -> None: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blob should be str only
def from_connection_string(cls, conn_str: str, container: container, credential=None, **kwargs): ... | ||
def generate_shared_access_signature(self, permission: permission=None, expiry: expiry=None, start: start=None, policy_id: str=None, ip: str=None, protocol: str=None, account_name: str=None, cache_control: str=None, content_disposition: str=None, content_encoding: str=None, content_language: str=None, content_type: str=None, user_delegation_key: ~azure.storage.blob._shared.models.UserDelegationKey=None) -> str: ... | ||
def get_account_information(self, **kwargs) -> dict(str, str): ... | ||
def get_blob_client(self, blob: blob, snapshot: str=None) -> ~azure.storage.blob.aio.blob_client_async.BlobClient: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blob should be str only, changed to blob_name
def get_account_information(self, **kwargs) -> dict(str, str): ... | ||
def get_blob_client(self, blob: blob, snapshot: str=None) -> ~azure.storage.blob.aio.blob_client_async.BlobClient: ... | ||
def get_container_access_policy(self, lease: str=None, timeout: int=None, **kwargs) -> dict[str, str]: ... | ||
def get_container_properties(self, lease: ~azure.storage.blob.aio.lease_async.LeaseClient=None, timeout: int=None, **kwargs) -> ~azure.storage.blob.models.ContainerProperties: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeout should be **kwonly
def get_blob_client(self, blob: blob, snapshot: str=None) -> ~azure.storage.blob.aio.blob_client_async.BlobClient: ... | ||
def get_container_access_policy(self, lease: str=None, timeout: int=None, **kwargs) -> dict[str, str]: ... | ||
def get_container_properties(self, lease: ~azure.storage.blob.aio.lease_async.LeaseClient=None, timeout: int=None, **kwargs) -> ~azure.storage.blob.models.ContainerProperties: ... | ||
def list_blobs(self, name_starts_with: str=None, include: list[str]=None, timeout: int=None, **kwargs) -> ~azure.core.async_paging.AsyncItemPaged[~azure.storage.blob.models.BlobProperties]: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
include can be both string and a list.
I think timeout in all APIs should be **kwonly
def get_container_properties(self, lease: ~azure.storage.blob.aio.lease_async.LeaseClient=None, timeout: int=None, **kwargs) -> ~azure.storage.blob.models.ContainerProperties: ... | ||
def list_blobs(self, name_starts_with: str=None, include: list[str]=None, timeout: int=None, **kwargs) -> ~azure.core.async_paging.AsyncItemPaged[~azure.storage.blob.models.BlobProperties]: ... | ||
def set_container_access_policy(self, signed_identifiers: signed_identifiers=None, public_access: ~azure.storage.blob.models.PublicAccess=None, lease: lease=None, timeout: int=None, *, if_modified_since, if_unmodified_since, **kwargs): ... | ||
def set_container_metadata(self, metadata: metadata=None, lease: str=None, timeout: int=None, *, if_modified_since, **kwargs): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should lease
be **kwonly
in all the APIs
def list_blobs(self, name_starts_with: str=None, include: list[str]=None, timeout: int=None, **kwargs) -> ~azure.core.async_paging.AsyncItemPaged[~azure.storage.blob.models.BlobProperties]: ... | ||
def set_container_access_policy(self, signed_identifiers: signed_identifiers=None, public_access: ~azure.storage.blob.models.PublicAccess=None, lease: lease=None, timeout: int=None, *, if_modified_since, if_unmodified_since, **kwargs): ... | ||
def set_container_metadata(self, metadata: metadata=None, lease: str=None, timeout: int=None, *, if_modified_since, **kwargs): ... | ||
def upload_blob(self, name: name, data, blob_type: ~azure.storage.blob.models.BlobType=BlockBlob, overwrite: bool=False, length: int=None, metadata: metadata=None, content_settings: ~azure.storage.blob.models.ContentSettings=None, validate_content: bool=False, lease: lease=None, timeout: int=None, max_connections: int=1, encoding: str=UTF-8, *, if_modified_since, if_unmodified_since, if_match, if_none_match, premium_page_blob_tier, maxsize_condition, **kwargs) -> ~azure.storage.blob.aio.blob_client_async.BlobClient: ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rename name
to blob_name
|
||
def __add__(self, other): ... | ||
def __init__(self, read: bool=False, write: bool=False, delete: bool=False, list: bool=False, _str: str=None, *, ContainerPermissions.DELETE, ContainerPermissions.LIST, ContainerPermissions.READ, ContainerPermissions.WRITE): ... | ||
def __or__(self, other): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_add_
and __or__
are doing the same thing,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Breaking change: remove the "enum-like" list parameters.
def values(self): ... | ||
|
||
class CorsRule(Model, object): | ||
allowed_origins:list(str) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can allowed_* be Union[str, list[str]]
instead of list[str]
. It'll be more friendly to pass in one item.
def send(self, request: request) -> ~azure.core.pipeline.PipelineResponse: ... | ||
def sleep(self, settings, transport): ... | ||
|
||
class PageRange(DictMixin, object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add a clear = True|False
to page range
object:bool | ||
_str:str | ||
|
||
def __add__(self, other): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_add_
and _or_
are doing the same thing,
breaking change: remove enum like parameters
_str:str | ||
|
||
def __add__(self, other): ... | ||
def __init__(self, service: bool=False, container: bool=False, object: bool=False, _str: str=None, *, ResourceTypes.CONTAINER, ResourceTypes.OBJECT, ResourceTypes.SERVICE): ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
object
should be more explicit
@johanste - I'm guessing this can be closed now? |
This is the output for a prototype script for API listing for review. Let's see if it makes sense from a review perspective.
Known issues:
+ more
.The alternative is to review the API using the docs. Which we will also have to do.