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

[Tables] Misc client updates #18462

Merged
merged 10 commits into from
May 6, 2021
7 changes: 4 additions & 3 deletions sdk/tables/azure-data-tables/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 12.0.0b7 (Unreleased)
**Breaking**
* The `account_url` parameter in the client constructors has been renamed to `endpoint`.
* The `TableEntity` object now acts exclusively like a dictionary, and no longer supports key access via attributes.
* Metadata of an entity is now accessed via `TableEntity.metadata` attribute rather than a method.
* Removed explicit `LinearRetry` and `ExponentialRetry` in favor of keyword parameter.
Expand All @@ -12,14 +13,13 @@
* `TableClient.send_batch` has been renamed to `TableClient.submit_transaction`.
* Removed `BatchTransactionResult` object in favor of returning an iterable of batched entities with returned metadata.
* Removed Batching context-manager behavior
* Changed optional `value` and `type` arguments of `EntityProperty` to required.
* `EntityProperty` is now a NampedTuple, and can be represented by a tuple of `(entity, EdmType)`.
* Renamed `EntityProperty.type` to `EntityProperty.edm_type`.
* `BatchErrorException` has been renamed to `TableTransactionError`.
* The `location_mode` is no longer a public attribute on the Clients.
* The only supported credentials are `AzureNamedKeyCredential`, `AzureSasCredential`, or authentication by connection string
* `EntityProperty` is now a tuple.
* Removed `date` and `api_version` from the `TableItem` class.


**Fixes**
* Fixed issue with Cosmos merge operations.
* Removed legacy Storage policies from pipeline.
Expand All @@ -28,6 +28,7 @@
* Added support for Azurite storage emulator
* Throws a `RequestTooLargeError` on transaction requests that return a 413 error code
* Added support for Int64 and Binary types in query filters
* Added support for `select` keyword parameter to `TableClient.get_entity()`.
* On `update_entity` and `delete_entity` if no `etag` is supplied via kwargs, the `etag` in the entity will be used if it is in the entity.

## 12.0.0b6 (2021-04-06)
Expand Down
8 changes: 4 additions & 4 deletions sdk/tables/azure-data-tables/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pip install --pre azure-data-tables
The Azure Data Tables library allows you to interact with two types of resources:
* the tables in your account
* the entities within those tables.
Interaction with these resources starts with an instance of a [client](#clients). To create a client object, you will need the account's table service endpoint URL and a credential that allows you to access the account. The `account_url` can be found on the page for your storage account in the [Azure Portal][azure_portal_account_url] under the "Access Keys" section or by running the following Azure CLI command:
Interaction with these resources starts with an instance of a [client](#clients). To create a client object, you will need the account's table service endpoint URL and a credential that allows you to access the account. The `endpoint` can be found on the page for your storage account in the [Azure Portal][azure_portal_account_url] under the "Access Keys" section or by running the following Azure CLI command:

```bash
# Get the table service URL for the account
Expand All @@ -39,7 +39,7 @@ az storage account show -n mystorageaccount -g MyResourceGroup --query "primaryE
Once you have the account URL, it can be used to create the service client:
```python
from azure.data.tables import TableServiceClient
service = TableServiceClient(account_url="https://<my_account_name>.table.core.windows.net/", credential=credential)
service = TableServiceClient(endpoint="https://<my_account_name>.table.core.windows.net/", credential=credential)
```

For more information about table service URL's and how to configure custom domain names for Azure Storage check out the [official documentation][azure_portal_account_url]
Expand All @@ -60,7 +60,7 @@ az storage account keys list -g MyResourceGroup -n MyStorageAccount
Use the key as the credential parameter to authenticate the client:
```python
from azure.data.tables import TableServiceClient
service = TableServiceClient(account_url="https://<my_account_name>.table.core.windows.net", credential="<account_access_key>")
service = TableServiceClient(endpoint="https://<my_account_name>.table.core.windows.net", credential="<account_access_key>")
```

##### Creating the client from a connection string
Expand Down Expand Up @@ -93,7 +93,7 @@ To use a [shared access signature (SAS) token][azure_sas_token], provide the tok
expiry=datetime.utcnow() + timedelta(hours=1)
)

table_service_client = TableServiceClient(account_url="https://<my_account_name>.table.core.windows.net", credential=sas_token)
table_service_client = TableServiceClient(endpoint="https://<my_account_name>.table.core.windows.net", credential=sas_token)
```


Expand Down
15 changes: 2 additions & 13 deletions sdk/tables/azure-data-tables/azure/data/tables/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,17 +188,6 @@ def _secondary_hostname(self):
"""
return self._hosts[LocationMode.SECONDARY]

@property
def location_mode(self):
"""The location mode that the client is currently using.

By default this will be "primary". Options include "primary" and "secondary".

:type: str
"""

return self._location_mode

@property
def api_version(self):
"""The version of the Storage API used for requests.
Expand All @@ -212,12 +201,12 @@ class TablesBaseClient(AccountHostsMixin):

def __init__(
self,
account_url, # type: str
endpoint, # type: str
credential=None, # type: str
**kwargs # type: Any
):
# type: (...) -> None
super(TablesBaseClient, self).__init__(account_url, credential=credential, **kwargs)
super(TablesBaseClient, self).__init__(endpoint, credential=credential, **kwargs)
self._client = AzureTable(
self.url,
policies=kwargs.pop('policies', self._policies),
Expand Down
22 changes: 7 additions & 15 deletions sdk/tables/azure-data-tables/azure/data/tables/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,7 @@ class TablePropertiesPaged(PageIterator):
:param callable command: Function to retrieve the next page of items.
:keyword int results_per_page: The maximum number of results retrieved per API call.
:keyword str filter: The filter to apply to results.
:keyword str select: The select filter to apply to results.
:keyword str continuation_token: An opaque continuation token.
:keyword str location_mode: The location mode being used to list results. The available
options include "primary" and "secondary".
"""

def __init__(self, command, **kwargs):
Expand All @@ -296,25 +293,22 @@ def __init__(self, command, **kwargs):
self._response = None
self.results_per_page = kwargs.get("results_per_page")
self.filter = kwargs.get("filter")
self.select = kwargs.get("select")
self.location_mode = None
self._location_mode = None

def _get_next_cb(self, continuation_token, **kwargs):
query_options = QueryOptions(
top=self.results_per_page, select=self.select, filter=self.filter
)
query_options = QueryOptions(top=self.results_per_page, filter=self.filter)
try:
return self._command(
query_options=query_options,
next_table_name=continuation_token or None,
cls=kwargs.pop("cls", None) or _return_context_and_deserialized,
use_location=self.location_mode,
use_location=self._location_mode,
)
except HttpResponseError as error:
_process_table_error(error)

def _extract_data_cb(self, get_next_return):
self.location_mode, self._response, self._headers = get_next_return
self._location_mode, self._response, self._headers = get_next_return
props_list = [
TableItem._from_generated(t, **self._headers) for t in self._response.value # pylint: disable=protected-access
]
Expand All @@ -330,8 +324,6 @@ class TableEntityPropertiesPaged(PageIterator):
:keyword str filter: The filter to apply to results.
:keyword str select: The select filter to apply to results.
:keyword str continuation_token: An opaque continuation token.
:keyword str location_mode: The location mode being used to list results. The available
options include "primary" and "secondary".
"""

def __init__(self, command, table, **kwargs):
Expand All @@ -347,7 +339,7 @@ def __init__(self, command, table, **kwargs):
self.results_per_page = kwargs.get("results_per_page")
self.filter = kwargs.get("filter")
self.select = kwargs.get("select")
self.location_mode = None
self._location_mode = None

def _get_next_cb(self, continuation_token, **kwargs):
next_partition_key, next_row_key = _extract_continuation_token(
Expand All @@ -363,13 +355,13 @@ def _get_next_cb(self, continuation_token, **kwargs):
next_partition_key=next_partition_key,
table=self.table,
cls=kwargs.pop("cls", None) or _return_context_and_deserialized,
use_location=self.location_mode,
use_location=self._location_mode,
)
except HttpResponseError as error:
_process_table_error(error)

def _extract_data_cb(self, get_next_return):
self.location_mode, self._response, self._headers = get_next_return
self._location_mode, self._response, self._headers = get_next_return
props_list = [_convert_to_entity(t) for t in self._response.value]
next_entity = {}
if self._headers[NEXT_PARTITION_KEY] or self._headers[NEXT_ROW_KEY]:
Expand Down
52 changes: 27 additions & 25 deletions sdk/tables/azure-data-tables/azure/data/tables/_table_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from ._generated.models import (
SignedIdentifier,
TableProperties,
QueryOptions
)
from ._serialize import _get_match_headers, _add_entity_properties
from ._base_client import parse_connection_str, TablesBaseClient
Expand All @@ -41,26 +42,25 @@


class TableClient(TablesBaseClient):
"""
:ivar str account_name: Name of the storage account (Cosmos or Azure)
:ivar str table_name: The name of the table
"""A client to interact with a specific Table in an Azure Tables account.

:ivar str account_name: The name of the Tables account.
:ivar str table_name: The name of the table.
:ivar str url: The full URL to the Tables account.
"""

def __init__(
self,
account_url, # type: str
endpoint, # type: str
table_name, # type: str
credential=None, # type: Union[AzureNamedKeyCredential, AzureSasCredential]
**kwargs # type: Any
):
# type: (...) -> None
"""Create TableClient from a Credential.

:param account_url:
A url to an Azure Storage account.
:type account_url: str
:param table_name: The table name.
:type table_name: str
:param str endpoint: A URL to an Azure Tables account.
:param str table_name: The table name.
:param credential:
The credentials with which to authenticate. This is optional if the
account URL already has a SAS token, or the connection string already has shared
Expand All @@ -75,7 +75,7 @@ def __init__(
raise ValueError("Please specify a table name.")
_validate_table_name(table_name)
self.table_name = table_name
super(TableClient, self).__init__(account_url, credential=credential, **kwargs)
super(TableClient, self).__init__(endpoint, credential=credential, **kwargs)

def _format_url(self, hostname):
"""Format the endpoint URL according to the current location
Expand All @@ -93,11 +93,8 @@ def from_connection_string(
# type: (...) -> TableClient
"""Create TableClient from a Connection String.

:param conn_str:
A connection string to an Azure Storage or Cosmos account.
:type conn_str: str
:param table_name: The table name.
:type table_name: str
:param str conn_str: A connection string to an Azure Tables account.
:param str table_name: The table name.
:returns: A table client.
:rtype: :class:`~azure.data.tables.TableClient`

Expand All @@ -110,10 +107,10 @@ def from_connection_string(
:dedent: 8
:caption: Authenticating a TableServiceClient from a connection_string
"""
account_url, credential = parse_connection_str(
endpoint, credential = parse_connection_str(
conn_str=conn_str, credential=None, keyword_args=kwargs
)
return cls(account_url, table_name=table_name, credential=credential, **kwargs)
return cls(endpoint, table_name=table_name, credential=credential, **kwargs)

@classmethod
def from_table_url(cls, table_url, credential=None, **kwargs):
Expand Down Expand Up @@ -143,7 +140,7 @@ def from_table_url(cls, table_url, credential=None, **kwargs):
account_path = ""
if len(table_path) > 1:
account_path = "/" + "/".join(table_path[:-1])
account_url = "{}://{}{}?{}".format(
endpoint = "{}://{}{}?{}".format(
parsed_url.scheme,
parsed_url.netloc.rstrip("/"),
account_path,
Expand All @@ -154,7 +151,7 @@ def from_table_url(cls, table_url, credential=None, **kwargs):
raise ValueError(
"Invalid URL. Please provide a URL with a valid table name"
)
return cls(account_url, table_name=table_name, credential=credential, **kwargs)
return cls(endpoint, table_name=table_name, credential=credential, **kwargs)

@distributed_trace
def get_table_access_policy(
Expand Down Expand Up @@ -483,8 +480,8 @@ def list_entities(
# type: (...) -> ItemPaged[TableEntity]
"""Lists entities in a table.

:keyword int results_per_page: Number of entities per page in return ItemPaged
:keyword select: Specify desired properties of an entity to return certain entities
:keyword int results_per_page: Number of entities returned per service request.
:keyword select: Specify desired properties of an entity to return.
:paramtype select: str or List[str]
:return: ItemPaged[:class:`~azure.data.tables.TableEntity`]
:rtype: ~azure.core.paging.ItemPaged
Expand Down Expand Up @@ -523,8 +520,8 @@ def query_entities(
"""Lists entities in a table.

:param str query_filter: Specify a filter to return certain entities
:keyword int results_per_page: Number of entities per page in return ItemPaged
:keyword select: Specify desired properties of an entity to return certain entities
:keyword int results_per_page: Number of entities returned per service request.
:keyword select: Specify desired properties of an entity to return.
:paramtype select: str or List[str]
:keyword Dict[str, Any] parameters: Dictionary for formatting query with additional, user defined parameters
:return: ItemPaged[:class:`~azure.data.tables.TableEntity`]
Expand All @@ -547,7 +544,7 @@ def query_entities(
top = kwargs.pop("results_per_page", None)
user_select = kwargs.pop("select", None)
if user_select and not isinstance(user_select, str):
user_select = ", ".join(user_select)
user_select = ",".join(user_select)

command = functools.partial(self._client.table.query_entities, **kwargs)
return ItemPaged(
Expand All @@ -573,6 +570,8 @@ def get_entity(
:type partition_key: str
:param row_key: The row key of the entity.
:type row_key: str
:keyword select: Specify desired properties of an entity to return.
:paramtype select: str or List[str]
:return: Dictionary mapping operation metadata returned from the service
:rtype: :class:`~azure.data.tables.TableEntity`
:raises: :class:`~azure.core.exceptions.HttpResponseError`
Expand All @@ -586,14 +585,17 @@ def get_entity(
:dedent: 8
:caption: Get a single entity from a table
"""
user_select = kwargs.pop("select", None)
if user_select and not isinstance(user_select, str):
user_select = ",".join(user_select)
try:
entity = self._client.table.query_entity_with_partition_and_row_key(
table=self.table_name,
partition_key=partition_key,
row_key=row_key,
query_options=QueryOptions(select=user_select),
**kwargs
)

properties = _convert_to_entity(entity)
return properties
except HttpResponseError as error:
Expand Down
Loading