Skip to content
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

Closed
wants to merge 1 commit into from

Conversation

johanste
Copy link
Member

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:

  • Enum values are not showing up.
  • Parsing of docstrings to get parameter types is very rudimentary.
  • Type annotations are not picked up.
    + more.

The alternative is to review the API using the docs. Which we will also have to do.

@adxsdk6
Copy link

adxsdk6 commented Sep 20, 2019

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: ...
Copy link
Member Author

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.

Copy link
Member

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

@johanste johanste requested a review from rakshith91 September 24, 2019 00:45
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): ...
Copy link
Member Author

@johanste johanste Sep 24, 2019

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?

Copy link
Contributor

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): ...
Copy link
Member Author

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.

@johanste johanste changed the title Prototype API listing for comments Storage Blobs - prototype API listing for comments Sep 24, 2019
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: ...
Copy link
Member Author

@johanste johanste Sep 24, 2019

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]: ...
Copy link
Contributor

@rakshith91 rakshith91 Sep 24, 2019

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

Copy link
Member

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):
Copy link
Member

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): ...
Copy link
Member

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?)

Copy link
Member Author

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.

Copy link
Member

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: ...
Copy link
Contributor

@rakshith91 rakshith91 Sep 26, 2019

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: ...
Copy link
Contributor

@rakshith91 rakshith91 Sep 26, 2019

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.

Copy link
Member

@annatisch annatisch Sep 27, 2019

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.

Copy link
Contributor

@bryevdv bryevdv Sep 30, 2019

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)

Copy link
Contributor

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): ...
Copy link
Member Author

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)

Copy link
Contributor

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

Copy link
Contributor

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):
Copy link
Member Author

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?

Copy link
Contributor

@rakshith91 rakshith91 Sep 30, 2019

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): ...
Copy link
Member Author

@johanste johanste Sep 27, 2019

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.

Copy link
Contributor

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): ...
Copy link
Member Author

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): ...
Copy link
Member Author

@johanste johanste Sep 27, 2019

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.

Copy link
Contributor

@rakshith91 rakshith91 Sep 30, 2019

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)

Copy link
Contributor

@rakshith91 rakshith91 Sep 30, 2019

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): ...
Copy link
Member Author

@johanste johanste Sep 27, 2019

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.

Copy link
Contributor

@rakshith91 rakshith91 Sep 30, 2019

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): ...
Copy link
Member

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

Copy link
Member

@annatisch annatisch Sep 27, 2019

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)

Copy link
Member

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.

Copy link
Member

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.

Copy link
Member

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: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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

Copy link
Member

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): ...
Copy link
Contributor

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): ...
Copy link
Contributor

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): ...
Copy link
Contributor

@rakshith91 rakshith91 Sep 27, 2019

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

Copy link
Member

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]: ...
Copy link
Contributor

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: ...
Copy link
Member

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)

Copy link
Member

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?

Copy link
Member

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: ...
Copy link
Contributor

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

Copy link
Member

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)): ...
Copy link
Contributor

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.

Copy link
Member

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)): ...
Copy link
Contributor

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

Copy link
Contributor

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

Copy link
Contributor

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 specifies clear == 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]: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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?

Copy link
Member

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"

Copy link
Contributor

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): ...
Copy link
Contributor

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

Copy link
Contributor

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): ...
Copy link
Contributor

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):
Copy link
Contributor

@rakshith91 rakshith91 Sep 29, 2019

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): ...
Copy link
Contributor

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): ...
Copy link
Contributor

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.

Copy link
Contributor

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: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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): ...
Copy link
Contributor

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: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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: ...
Copy link
Contributor

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]: ...
Copy link
Contributor

@rakshith91 rakshith91 Sep 30, 2019

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): ...
Copy link
Contributor

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: ...
Copy link
Contributor

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): ...
Copy link
Contributor

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,

Copy link
Contributor

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)
Copy link
Contributor

@rakshith91 rakshith91 Sep 30, 2019

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):
Copy link
Contributor

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): ...
Copy link
Contributor

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): ...
Copy link
Contributor

@rakshith91 rakshith91 Sep 30, 2019

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

@annatisch
Copy link
Member

@johanste - I'm guessing this can be closed now?
Will you be opening PRs for Queues and Files or will @rakshith91 be doing so?

@johanste johanste closed this Oct 31, 2019
Sign up for free to 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.

6 participants