Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@ class AzureSigningError(ClientAuthenticationError):


class _HttpChallenge(object): # pylint:disable=too-few-public-methods
"""Represents a parsed HTTP WWW-Authentication Bearer challenge from a server."""
"""Represents a parsed HTTP WWW-Authentication Bearer challenge from a server.

:param challenge: The WWW-Authenticate header of the challenge response.
:type challenge: str
"""

def __init__(self, challenge):
if not challenge:
Expand Down Expand Up @@ -78,7 +82,6 @@ def __init__(self, challenge):
self.resource = self._parameters.get("resource") or self._parameters.get("resource_id") or ""


# pylint: disable=no-self-use
class SharedKeyCredentialPolicy(SansIOHTTPPolicy):
def __init__(self, credential, is_emulated=False):
self._credential = credential
Expand Down Expand Up @@ -139,7 +142,7 @@ def _add_authorization_header(self, request, string_to_sign):
except Exception as ex:
# Wrap any error that occurred as signing error
# Doing so will clarify/locate the source of problem
raise _wrap_exception(ex, AzureSigningError)
raise _wrap_exception(ex, AzureSigningError) from ex

def on_request(self, request: PipelineRequest) -> None:
self.sign_request(request)
Expand Down Expand Up @@ -171,7 +174,7 @@ class BearerTokenChallengePolicy(BearerTokenCredentialPolicy):

:param credential: The credential.
:type credential: ~azure.core.TokenCredential
:param str scopes: Lets you specify the type of access needed.
:keyword str scopes: Lets you specify the type of access needed.
Comment thread
YalinLi0312 marked this conversation as resolved.
Outdated
:keyword bool discover_tenant: Determines if tenant discovery should be enabled. Defaults to True.
:keyword bool discover_scopes: Determines if scopes from authentication challenges should be provided to token
requests, instead of the scopes given to the policy's constructor, if any are present. Defaults to True.
Expand All @@ -198,6 +201,7 @@ def on_challenge(self, request: PipelineRequest, response: PipelineResponse) ->
:param ~azure.core.pipeline.PipelineRequest request: the request which elicited an authentication challenge
:param ~azure.core.pipeline.PipelineResponse response: the resource provider's response
:returns: a bool indicating whether the policy should send the request
:rtype: bool
"""
if not self._discover_tenant and not self._discover_scopes:
# We can't discover the tenant or use a different scope; the request will fail because it hasn't changed
Expand Down Expand Up @@ -268,5 +272,5 @@ def _configure_credential(
if isinstance(credential, AzureNamedKeyCredential):
return SharedKeyCredentialPolicy(credential)
if credential is not None:
raise TypeError("Unsupported credential: {}".format(credential))
raise TypeError(f"Unsupported credential: {credential}")
return None
62 changes: 31 additions & 31 deletions sdk/tables/azure-data-tables/azure/data/tables/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,7 @@ def get_api_version(kwargs: Dict[str, Any], default: str) -> str:
if api_version and api_version not in _SUPPORTED_API_VERSIONS:
versions = "\n".join(_SUPPORTED_API_VERSIONS)
raise ValueError(
"Unsupported API version '{}'. Please select from:\n{}".format(
api_version, versions
)
f"Unsupported API version '{api_version}'. Please select from:\n{versions}"
)
return api_version or default

Expand All @@ -81,11 +79,11 @@ def __init__(
try:
if not account_url.lower().startswith("http"):
account_url = "https://" + account_url
except AttributeError:
raise ValueError("Account URL must be a string.")
except AttributeError as exc:
raise ValueError("Account URL must be a string.") from exc
parsed_url = urlparse(account_url.rstrip("/"))
if not parsed_url.netloc:
raise ValueError("Invalid URL: {}".format(account_url))
raise ValueError(f"Invalid URL: {account_url}")

_, sas_token = parse_query(parsed_url.query)
if not sas_token and not credential:
Expand Down Expand Up @@ -118,10 +116,8 @@ def __init__(
raise ValueError("Token credential is only supported with HTTPS.")
if hasattr(self.credential, "named_key"):
self.account_name = self.credential.named_key.name # type: ignore
secondary_hostname = "{}-secondary.table.{}".format(
self.credential.named_key.name, # type: ignore
os.getenv("TABLES_STORAGE_ENDPOINT_SUFFIX", DEFAULT_STORAGE_ENDPOINT_SUFFIX)
)
endpoint_suffix = os.getenv("TABLES_STORAGE_ENDPOINT_SUFFIX", DEFAULT_STORAGE_ENDPOINT_SUFFIX)
secondary_hostname = f"{self.account_name}-secondary.table.{endpoint_suffix}"

if not self._hosts:
if len(account) > 1:
Expand All @@ -148,6 +144,8 @@ def url(self):

This could be either the primary endpoint,
or the secondary endpoint depending on the current :func:`location_mode`.

:type: str
Comment thread
annatisch marked this conversation as resolved.
Outdated
"""
return self._format_url(self._hosts[self._location_mode])

Expand Down Expand Up @@ -259,16 +257,24 @@ def _configure_policies(self, **kwargs):
]

def _batch_send(self, table_name: str, *reqs: HttpRequest, **kwargs) -> List[Mapping[str, Any]]:
"""Given a series of request, do a Storage batch call."""
"""Given a series of request, do a Storage batch call.

:param table_name: The table name.
:type table_name: str
:keyword reqs: The HTTP request.
Comment thread
YalinLi0312 marked this conversation as resolved.
Outdated
:paramtype reqs: ~azure.core.pipeline.transport.HttpRequest
:return: A list of batch part metadata in response.
:rtype: list[Mapping[str, Any]]
"""
# Pop it here, so requests doesn't feel bad about additional kwarg
policies = [StorageHeadersPolicy()]

changeset = HttpRequest("POST", None) # type: ignore
changeset.set_multipart_mixed(
*reqs, policies=policies, boundary="changeset_{}".format(uuid4()) # type: ignore
*reqs, policies=policies, boundary=f"changeset_{uuid4()}" # type: ignore
)
request = self._client._client.post( # pylint: disable=protected-access
url="{}://{}/$batch".format(self.scheme, self._primary_hostname),
url=f"{self.scheme}://{self._primary_hostname}/$batch",
headers={
"x-ms-version": self.api_version,
"DataServiceVersion": "3.0",
Expand All @@ -281,7 +287,7 @@ def _batch_send(self, table_name: str, *reqs: HttpRequest, **kwargs) -> List[Map
changeset,
policies=policies,
enforce_https=False,
boundary="batch_{}".format(uuid4()),
boundary=f"batch_{uuid4()}",
)
pipeline_response = self._client._client._pipeline.run(request, **kwargs) # pylint: disable=protected-access
response = pipeline_response.http_response
Expand Down Expand Up @@ -322,6 +328,9 @@ class TransportWrapper(HttpTransport):
"""Wrapper class that ensures that an inner client created
by a `get_client` method does not close the outer transport for the parent
when used in a context manager.

:param transport: The Http Transport instance
:type transport: ~azure.core.pipeline.transport.HttpTransport
"""
def __init__(self, transport):
self._transport = transport
Expand Down Expand Up @@ -352,25 +361,19 @@ def parse_connection_str(conn_str, credential, keyword_args):
if not credential:
try:
credential = AzureNamedKeyCredential(name=conn_settings["accountname"], key=conn_settings["accountkey"])
except KeyError:
except KeyError as exc:
credential = conn_settings.get("sharedaccesssignature", None)
if not credential:
raise ValueError("Connection string missing required connection details.")
raise ValueError("Connection string missing required connection details.") from exc
credential = AzureSasCredential(credential)
primary = conn_settings.get("tableendpoint")
secondary = conn_settings.get("tablesecondaryendpoint")
if not primary:
if secondary:
raise ValueError("Connection string specifies only secondary endpoint.")
try:
primary = "{}://{}.table.{}".format(
conn_settings["defaultendpointsprotocol"],
conn_settings["accountname"],
conn_settings["endpointsuffix"],
)
secondary = "{}-secondary.table.{}".format(
conn_settings["accountname"], conn_settings["endpointsuffix"]
)
primary = f"{conn_settings['defaultendpointsprotocol']}://{conn_settings['accountname']}.table.{conn_settings['endpointsuffix']}" # pylint: disable=line-too-long
secondary = f"{conn_settings['accountname']}-secondary.table.{conn_settings['endpointsuffix']}"
except KeyError:
pass

Expand All @@ -380,12 +383,9 @@ def parse_connection_str(conn_str, credential, keyword_args):
else:
endpoint_suffix = os.getenv("TABLES_STORAGE_ENDPOINT_SUFFIX", DEFAULT_STORAGE_ENDPOINT_SUFFIX)
try:
primary = "https://{}.table.{}".format(
conn_settings["accountname"],
conn_settings.get("endpointsuffix", endpoint_suffix),
)
except KeyError:
raise ValueError("Connection string missing required connection details.")
primary = f"https://{conn_settings['accountname']}.table.{conn_settings.get('endpointsuffix', endpoint_suffix)}" # pylint: disable=line-too-long
except KeyError as exc:
raise ValueError("Connection string missing required connection details.") from exc

if "secondary_hostname" not in keyword_args:
keyword_args["secondary_hostname"] = secondary
Expand Down Expand Up @@ -416,7 +416,7 @@ def parse_query(query_str):
sas_values = QueryStringConstants.to_list()
parsed_query = {k: v[0] for k, v in parse_qs(query_str).items()}
sas_params = [
"{}={}".format(k, quote(v, safe=""))
f"{k}={quote(v, safe='')}"
for k, v in parsed_query.items()
if k in sas_values
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@ class UTC(datetime.tzinfo):
"""Time Zone info for handling UTC"""

def utcoffset(self, dt):
Comment thread
annatisch marked this conversation as resolved.
Outdated
# pylint: disable=docstring-missing-param,docstring-missing-return,docstring-missing-rtype
"""UTF offset for UTC is 0."""
return datetime.timedelta(0)

def tzname(self, dt):
# pylint: disable=docstring-missing-param,docstring-missing-return,docstring-missing-rtype
"""Timestamp representation."""
return "Z"

def dst(self, dt):
# pylint: disable=docstring-missing-param,docstring-missing-return,docstring-missing-rtype
"""No daylight saving for UTC."""
return datetime.timedelta(hours=1)

Expand Down
19 changes: 15 additions & 4 deletions sdk/tables/azure-data-tables/azure/data/tables/_deserialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def _from_entity_datetime(value):
dt_obj = TablesEntityDatetime.strptime(cleaned_value, "%Y-%m-%dT%H:%M:%SZ").replace(
tzinfo=TZ_UTC
)
dt_obj._service_value = value # pylint:disable=protected-access
dt_obj._service_value = value # pylint:disable=protected-access,assigning-non-slot
return dt_obj


Expand Down Expand Up @@ -143,6 +143,11 @@ def _convert_to_entity(entry_element):
"PartitionKey":"my_partition_key",
"RowKey":"my_row_key"
}

:param entry_element: The entity in response.
:type entry_element: Mapping[str, Any]
:return: An entity dict with additional metadata.
:rtype: dict[str, Any]
"""
entity = TableEntity()

Expand Down Expand Up @@ -215,7 +220,13 @@ def _convert_to_entity(entry_element):


def _extract_etag(response):
""" Extracts the etag from the response headers. """
""" Extracts the etag from the response headers.

:param response: The PipelineResponse object.
:type response: ~azure.core.pipeline.PipelineResponse
:return: The etag from the response headers
:rtype: str or None
"""
if response and response.headers:
return response.headers.get("etag")

Expand All @@ -233,8 +244,8 @@ def _extract_continuation_token(continuation_token):
return None, None
try:
return continuation_token.get("PartitionKey"), continuation_token.get("RowKey")
except AttributeError:
raise ValueError("Invalid continuation token format.")
except AttributeError as exc:
raise ValueError("Invalid continuation token format.") from exc


def _normalize_headers(headers):
Expand Down
18 changes: 9 additions & 9 deletions sdk/tables/azure-data-tables/azure/data/tables/_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,11 @@ def _decode_error(response, error_message=None, error_type=None, **kwargs): # p
error_type = HttpResponseError

try:
error_message += "\nErrorCode:{}".format(error_code.value)
error_message += f"\nErrorCode:{error_code.value}"
except AttributeError:
error_message += "\nErrorCode:{}".format(error_code)
error_message += f"\nErrorCode:{error_code}"
for name, info in additional_data.items():
error_message += "\n{}:{}".format(name, info)
error_message += f"\n{name}:{info}"

error = error_type(message=error_message, response=response, **kwargs)
error.error_code = error_code
Expand All @@ -188,16 +188,16 @@ def _reraise_error(decoded_error):
_, _, exc_traceback = sys.exc_info()
try:
raise decoded_error.with_traceback(exc_traceback)
except AttributeError:
except AttributeError as exc:
decoded_error.__traceback__ = exc_traceback
raise decoded_error
raise decoded_error from exc


def _process_table_error(storage_error, table_name=None):
try:
decoded_error = _decode_error(storage_error.response, storage_error.message)
except AttributeError:
raise storage_error
except AttributeError as exc:
raise storage_error from exc
if table_name:
_validate_tablename_error(decoded_error, table_name)
_reraise_error(decoded_error)
Expand Down Expand Up @@ -231,8 +231,8 @@ def _reprocess_error(decoded_error, identifiers=None):
"Too many access policies provided. The server does not support setting more than 5 access policies"\
"on a single resource."
)
except AttributeError:
raise decoded_error
except AttributeError as exc:
raise decoded_error from exc


class TableTransactionError(HttpResponseError):
Expand Down
Loading