From b4031ece01c1de76bcba9ccff38ebe0ca7df98d7 Mon Sep 17 00:00:00 2001 From: antisch Date: Mon, 26 Aug 2019 17:06:09 -0700 Subject: [PATCH 01/46] Updated dependencies --- sdk/cosmos/azure-cosmos/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/setup.py b/sdk/cosmos/azure-cosmos/setup.py index 1f4f8c94d778..a54fe85fb62b 100644 --- a/sdk/cosmos/azure-cosmos/setup.py +++ b/sdk/cosmos/azure-cosmos/setup.py @@ -66,7 +66,7 @@ ), install_requires=[ 'six >=1.6', - 'requests>=2.18.4' + 'azure-core<2.0.0,>=1.0.0b2' ], extras_require={ ":python_version<'3.0'": ["azure-nspkg"], From 9f30dd6c344e92936b2148d65329c59774c9282e Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 14:55:51 -0700 Subject: [PATCH 02/46] Added core pipeline --- .../azure/cosmos/_cosmos_client_connection.py | 40 +++-- .../azure/cosmos/_default_retry_policy.py | 2 +- .../azure/cosmos/_synchronized_request.py | 148 +++++++----------- sdk/cosmos/azure-cosmos/test/crud_tests.py | 4 +- .../azure-cosmos/test/multimaster_tests.py | 4 +- sdk/cosmos/azure-cosmos/test/proxy_tests.py | 4 +- sdk/cosmos/azure-cosmos/test/session_tests.py | 8 +- 7 files changed, 94 insertions(+), 116 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index 6f9fe50a1c86..725f673c043f 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -25,6 +25,16 @@ import six from typing import cast +from azure.core import PipelineClient +from azure.core.pipeline.policies import ( + ContentDecodePolicy, + HeadersPolicy, + UserAgentPolicy, + NetworkTraceLoggingPolicy, + CustomHookPolicy, + ProxyPolicy) +from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy + from . import _base as base from . import documents from . import _constants as constants @@ -68,7 +78,8 @@ def __init__(self, url_connection, auth, connection_policy=None, - consistency_level=documents.ConsistencyLevel.Session): + consistency_level=documents.ConsistencyLevel.Session, + **kwargs): """ :param str url_connection: The URL for connecting to the DB server. @@ -134,15 +145,24 @@ def __init__(self, self._useMultipleWriteLocations = False self._global_endpoint_manager = global_endpoint_manager._GlobalEndpointManager(self) - # creating a requests session used for connection pooling and re-used by all requests - self._requests_session = requests.Session() - + proxies = {} if self.connection_policy.ProxyConfiguration and self.connection_policy.ProxyConfiguration.Host: host = connection_policy.ProxyConfiguration.Host url = six.moves.urllib.parse.urlparse(host) proxy = host if url.port else host + ":" + str(connection_policy.ProxyConfiguration.Port) - proxyDict = {url.scheme : proxy} - self._requests_session.proxies.update(proxyDict) + proxies = {url.scheme : proxy} + + policies = [ + HeadersPolicy(), + ProxyPolicy(proxies=proxies), + UserAgentPolicy(), + ContentDecodePolicy(), + CustomHookPolicy(), + NetworkTraceLoggingPolicy(), + DistributedTracingPolicy() + ] + + self.pipeline_client = PipelineClient(url_connection, "empty-config", policies=policies) # Query compatibility mode. # Allows to specify compatibility mode used by client when making query requests. Should be removed when @@ -2629,7 +2649,7 @@ def __Get(self, path, request, headers): request, self._global_endpoint_manager, self.connection_policy, - self._requests_session, + self.pipeline_client, 'GET', path, None, @@ -2654,7 +2674,7 @@ def __Post(self, path, request, body, headers): request, self._global_endpoint_manager, self.connection_policy, - self._requests_session, + self.pipeline_client, 'POST', path, body, @@ -2679,7 +2699,7 @@ def __Put(self, path, request, body, headers): request, self._global_endpoint_manager, self.connection_policy, - self._requests_session, + self.pipeline_client, 'PUT', path, body, @@ -2703,7 +2723,7 @@ def __Delete(self, path, request, headers): request, self._global_endpoint_manager, self.connection_policy, - self._requests_session, + self.pipeline_client, 'DELETE', path, request_data=None, diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py index 8cb094c1552a..cc35691af6ab 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py @@ -54,7 +54,7 @@ def __init__(self, *args): def needsRetry(self, error_code): if error_code in DefaultRetryPolicy.CONNECTION_ERROR_CODES: if (len(self.args) > 0): - if (self.args[4]['method'] == 'GET') or (http_constants.HttpHeaders.IsQuery in self.args[4]['headers']): + if (self.args[3].method == 'GET') or (http_constants.HttpHeaders.IsQuery in self.args[3].headers): return True return False return True diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index 2ba8c1b2cdd8..2609dc2c532f 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -32,58 +32,28 @@ from . import http_constants from . import _retry_utility -def _IsReadableStream(obj): +def _is_readable_stream(obj): """Checks whether obj is a file-like readable stream. - :rtype: - boolean - + :rtype: boolean """ if (hasattr(obj, 'read') and callable(getattr(obj, 'read'))): return True return False -def _RequestBodyFromData(data): - """Gets request body from data. - - When `data` is dict and list into unicode string; otherwise return `data` - without making any change. - - :param (str, unicode, file-like stream object, dict, list or None) data: - - :rtype: - str, unicode, file-like stream object, or None - - """ - if isinstance(data, six.string_types) or _IsReadableStream(data): - return data - elif isinstance(data, (dict, list, tuple)): - - json_dumped = json.dumps(data, separators=(',',':')) - - if six.PY2: - return json_dumped.decode('utf-8') - else: - return json_dumped - return None - - -def _Request(global_endpoint_manager, request, connection_policy, requests_session, path, request_options, request_body): +def _Request(global_endpoint_manager, request_options, connection_policy, pipeline_client, request): """Makes one http request using the requests module. :param _GlobalEndpointManager global_endpoint_manager: - :param dict request: + :param dict request_options: contains the resourceType, operationType, endpointOverride, useWriteEndpoint, useAlternateWriteEndpoint information :param documents.ConnectionPolicy connection_policy: - :param requests.Session requests_session: - Session object in requests module - :param str resource_url: - The url for the resource - :param dict request_options: - :param str request_body: - Unicode or None + :param azure.core.PipelineClient pipeline_client: + Pipeline client to process the resquest + :param azure.core.HttpRequest request: + The request object to send through the pipeline :return: tuple of (result, headers) @@ -91,7 +61,7 @@ def _Request(global_endpoint_manager, request, connection_policy, requests_sessi tuple of (dict, dict) """ - is_media = request_options['path'].find('media') > -1 + is_media = request.url.find('media') > -1 is_media_stream = is_media and connection_policy.MediaReadMode == documents.MediaReadMode.Streamed connection_timeout = (connection_policy.MediaRequestTimeout @@ -101,21 +71,18 @@ def _Request(global_endpoint_manager, request, connection_policy, requests_sessi # Every request tries to perform a refresh global_endpoint_manager.refresh_endpoint_list(None) - if (request.endpoint_override): - base_url = request.endpoint_override + if (request_options.endpoint_override): + base_url = request_options.endpoint_override else: - base_url = global_endpoint_manager.resolve_service_endpoint(request) + base_url = global_endpoint_manager.resolve_service_endpoint(request_options) + if base_url != pipeline_client._base_url: + request.url = request.url.replace(pipeline_client._base_url, base_url) - if path: - resource_url = base_url + path - else: - resource_url = base_url - - parse_result = urlparse(resource_url) + parse_result = urlparse(request.url) # The requests library now expects header values to be strings only starting 2.11, # and will raise an error on validation if they are not, so casting all header values to strings. - request_options['headers'] = { header: str(value) for header, value in request_options['headers'].items() } + request.headers.update({ header: str(value) for header, value in request.headers.items() }) # We are disabling the SSL verification for local emulator(localhost/127.0.0.1) or if the user # has explicitly specified to disable SSL verification. @@ -124,33 +91,30 @@ def _Request(global_endpoint_manager, request, connection_policy, requests_sessi if connection_policy.SSLConfiguration: ca_certs = connection_policy.SSLConfiguration.SSLCaCerts cert_files = (connection_policy.SSLConfiguration.SSLCertFile, connection_policy.SSLConfiguration.SSLKeyFile) + response = pipeline_client._pipeline.run( + request, + stream=is_media_stream, + connection_timeout=connection_timeout / 1000.0, + connection_verify=ca_certs, + connection_cert=cert_files) - response = requests_session.request(request_options['method'], - resource_url, - data = request_body, - headers = request_options['headers'], - timeout = connection_timeout / 1000.0, - stream = is_media_stream, - verify = ca_certs, - cert = cert_files) else: - response = requests_session.request(request_options['method'], - resource_url, - data = request_body, - headers = request_options['headers'], - timeout = connection_timeout / 1000.0, - stream = is_media_stream, - # If SSL is disabled, verify = false - verify = is_ssl_enabled) - + response = pipeline_client._pipeline.run( + request, + stream=is_media_stream, + connection_timeout=connection_timeout / 1000.0, + # If SSL is disabled, verify = false + connection_verify=is_ssl_enabled) + + response = response.http_response headers = dict(response.headers) # In case of media stream response, return the response to the user and the user # will need to handle reading the response. if is_media_stream: - return (response.raw, headers) + return (response.stream_download(pipeline_client._pipeline), headers) - data = response.content + data = response.body() if not six.PY2: # python 3 compatible: convert data from byte to unicode string data = data.decode('utf-8') @@ -171,10 +135,10 @@ def _Request(global_endpoint_manager, request, connection_policy, requests_sessi return (result, headers) def SynchronizedRequest(client, - request, + request_options, global_endpoint_manager, connection_policy, - requests_session, + pipeline_client, method, path, request_data, @@ -184,11 +148,11 @@ def SynchronizedRequest(client, :param object client: Document client instance - :param dict request: + :param dict request_options: :param _GlobalEndpointManager global_endpoint_manager: :param documents.ConnectionPolicy connection_policy: - :param requests.Session requests_session: - Session object in requests module + :param azure.core.PipelineClient pipeline_client: + PipelineClient to process the request. :param str method: :param str path: :param (str, unicode, file-like stream object, dict, list or None) request_data: @@ -201,28 +165,22 @@ def SynchronizedRequest(client, tuple of (dict dict) """ - request_body = None - if request_data: - request_body = _RequestBodyFromData(request_data) - if not request_body: - raise errors.UnexpectedDataType( - 'parameter data must be a JSON object, string or' + - ' readable stream.') - - request_options = {} - request_options['path'] = path - request_options['method'] = method - if query_params: - request_options['path'] += '?' + urlencode(query_params) - - request_options['headers'] = headers - if request_body and (type(request_body) is str or - type(request_body) is six.text_type): - request_options['headers'][http_constants.HttpHeaders.ContentLength] = ( - len(request_body)) - elif request_body is None: - request_options['headers'][http_constants.HttpHeaders.ContentLength] = 0 + is_stream = _is_readable_stream(request_data) + + request = pipeline_client._request( + method=method, + url=path + '?' + urlencode(query_params) if query_params else path, + params=None, + headers=headers, + content=request_data if not is_stream else None, + form_content=None, + stream_content=request_data if is_stream else None) + + if request.data and isinstance(request.data, six.string_types): + request.headers[http_constants.HttpHeaders.ContentLength] = len(request.data) + elif request.data is None: + request.headers[http_constants.HttpHeaders.ContentLength] = 0 # Pass _Request function with it's parameters to retry_utility's Execute method that wraps the call with retries - return _retry_utility.Execute(client, global_endpoint_manager, _Request, request, connection_policy, requests_session, path, request_options, request_body) + return _retry_utility.Execute(client, global_endpoint_manager, _Request, request_options, connection_policy, pipeline_client, request) diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 77a20f52709c..38c32939928d 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -2566,8 +2566,8 @@ def test_get_resource_with_dictionary_and_object(self): self.assertEquals(read_permission.id, created_permission.id) def _MockExecuteFunction(self, function, *args, **kwargs): - self.last_headers.append(args[5]['headers'][HttpHeaders.PartitionKey] - if HttpHeaders.PartitionKey in args[5]['headers'] else '') + self.last_headers.append(args[4].headers[HttpHeaders.PartitionKey] + if HttpHeaders.PartitionKey in args[4].headers else '') return self.OriginalExecuteFunction(function, *args, **kwargs) if __name__ == '__main__': diff --git a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py index 11c740244dcc..e58dff33a4ae 100644 --- a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py +++ b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py @@ -123,8 +123,8 @@ def _MockExecuteFunction(self, function, *args, **kwargs): return {constants._Constants.EnableMultipleWritableLocations: self.EnableMultipleWritableLocations}, {} else: if len(args) > 0: - self.last_headers.append(HttpHeaders.AllowTentativeWrites in args[5]['headers'] - and args[5]['headers'][HttpHeaders.AllowTentativeWrites] == 'true') + self.last_headers.append(HttpHeaders.AllowTentativeWrites in args[4].headers + and args[4].headers[HttpHeaders.AllowTentativeWrites] == 'true') return self.OriginalExecuteFunction(function, *args, **kwargs) diff --git a/sdk/cosmos/azure-cosmos/test/proxy_tests.py b/sdk/cosmos/azure-cosmos/test/proxy_tests.py index 552f76ee301c..09857ebf84f5 100644 --- a/sdk/cosmos/azure-cosmos/test/proxy_tests.py +++ b/sdk/cosmos/azure-cosmos/test/proxy_tests.py @@ -30,7 +30,7 @@ else: from http.server import BaseHTTPRequestHandler, HTTPServer from threading import Thread -from requests.exceptions import ProxyError +from azure.core.exceptions import ServiceRequestError pytestmark = pytest.mark.cosmosEmulator @@ -104,7 +104,7 @@ def test_failure_with_wrong_proxy(self): client = cosmos_client_connection.CosmosClientConnection(self.host, {'masterKey': self.masterKey}, connection_policy) self.fail("Client instantiation is not expected") except Exception as e: - self.assertTrue(type(e) is ProxyError, msg="Error is not a ProxyError") + self.assertTrue(type(e) is ServiceRequestError, msg="Error is not a ServiceRequestError") if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testName'] diff --git a/sdk/cosmos/azure-cosmos/test/session_tests.py b/sdk/cosmos/azure-cosmos/test/session_tests.py index cdd0799dafd5..f46bbea3077a 100644 --- a/sdk/cosmos/azure-cosmos/test/session_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_tests.py @@ -37,12 +37,12 @@ def setUpClass(cls): cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) - def _MockRequest(self, global_endpoint_manager, request, connection_policy, requests_session, path, request_options, request_body): - if HttpHeaders.SessionToken in request_options['headers']: - self.last_session_token_sent = request_options['headers'][HttpHeaders.SessionToken] + def _MockRequest(self, global_endpoint_manager, request_options, connection_policy, pipeline_client, request): + if HttpHeaders.SessionToken in request.headers: + self.last_session_token_sent = request.headers[HttpHeaders.SessionToken] else: self.last_session_token_sent = None - return self._OriginalRequest(global_endpoint_manager, request, connection_policy, requests_session, path, request_options, request_body) + return self._OriginalRequest(global_endpoint_manager, request_options, connection_policy, pipeline_client, request) def test_session_token_not_sent_for_master_resource_ops (self): self._OriginalRequest = synchronized_request._Request From d2ec4b3ad973e3d406e2d5c1c00b4b3f68148bdd Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 14:56:47 -0700 Subject: [PATCH 03/46] Ignore test config --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2fbde824ef12..792c78318ba6 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,4 @@ sdk/storage/azure-storage-blob/tests/settings_real.py sdk/storage/azure-storage-queue/tests/settings_real.py sdk/storage/azure-storage-file/tests/settings_real.py *.code-workspace +sdk/cosmos/azure-cosmos/test/test_config.py From d7394d720f9721b603164a9a68ac72873b823afe Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 15:45:10 -0700 Subject: [PATCH 04/46] Fixed indexes test --- sdk/cosmos/azure-cosmos/test/crud_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index d8017fc81e49..051d62a8ef66 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -1946,7 +1946,7 @@ def __get_first(array): root_included_path = __get_first([included_path for included_path in indexing_policy['includedPaths'] if included_path['path'] == '/*']) - self.assertFalse('indexes' in root_included_path) + self.assertFalse(root_included_path['indexes']) def test_client_request_timeout(self): connection_policy = documents.ConnectionPolicy() From d628d91bc6922a6a5e84271a5a22be6c6836ac24 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 17:52:08 -0700 Subject: [PATCH 05/46] Refactored request creation --- .../azure/cosmos/_cosmos_client_connection.py | 156 +++++++++--------- .../azure/cosmos/_synchronized_request.py | 63 ++++--- 2 files changed, 120 insertions(+), 99 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index e3c65d938046..a097f946f15a 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -1801,7 +1801,7 @@ def fetch_fn(options): return query_iterable.QueryIterable(self, query, options, fetch_fn) - def ReadMedia(self, media_link): + def ReadMedia(self, media_link, **kwargs): """Reads a media. When self.connection_policy.MediaReadMode == @@ -1825,11 +1825,11 @@ def ReadMedia(self, media_link): headers = base.GetHeaders(self, default_headers, "get", path, attachment_id, "media", {}) # ReadMedia will always use WriteEndpoint since it's not replicated in readable Geo regions - request = _request_object.RequestObject("media", documents._OperationType.Read) - result, self.last_response_headers = self.__Get(path, request, headers) + request_options = _request_object.RequestObject("media", documents._OperationType.Read) + result, self.last_response_headers = self.__Get(path, request_options, headers, **kwargs) return result - def UpdateMedia(self, media_link, readable_stream, options=None): + def UpdateMedia(self, media_link, readable_stream, options=None, **kwargs): """Updates a media and returns it. :param str media_link: @@ -1864,8 +1864,8 @@ def UpdateMedia(self, media_link, readable_stream, options=None): headers = base.GetHeaders(self, initial_headers, "put", path, attachment_id, "media", options) # UpdateMedia will use WriteEndpoint since it uses PUT operation - request = _request_object.RequestObject("media", documents._OperationType.Update) - result, self.last_response_headers = self.__Put(path, request, readable_stream, headers) + request_options = _request_object.RequestObject("media", documents._OperationType.Update) + result, self.last_response_headers = self.__Put(path, request_options, readable_stream, headers, **kwargs) self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result @@ -2014,7 +2014,7 @@ def DeleteUserDefinedFunction(self, udf_link, options=None): udf_id = base.GetResourceIdOrFullNameFromLink(udf_link) return self.DeleteResource(path, "udfs", udf_id, None, options) - def ExecuteStoredProcedure(self, sproc_link, params, options=None): + def ExecuteStoredProcedure(self, sproc_link, params, options=None, **kwargs): """Executes a store procedure. :param str sproc_link: @@ -2044,8 +2044,8 @@ def ExecuteStoredProcedure(self, sproc_link, params, options=None): headers = base.GetHeaders(self, initial_headers, "post", path, sproc_id, "sprocs", options) # ExecuteStoredProcedure will use WriteEndpoint since it uses POST operation - request = _request_object.RequestObject("sprocs", documents._OperationType.ExecuteJavaScript) - result, self.last_response_headers = self.__Post(path, request, params, headers) + request_options = _request_object.RequestObject("sprocs", documents._OperationType.ExecuteJavaScript) + result, self.last_response_headers = self.__Post(path, request_options, params, headers, **kwargs) return result def ReplaceStoredProcedure(self, sproc_link, sproc, options=None): @@ -2194,7 +2194,7 @@ def fetch_fn(options): return query_iterable.QueryIterable(self, query, options, fetch_fn) - def GetDatabaseAccount(self, url_connection=None): + def GetDatabaseAccount(self, url_connection=None, **kwargs): """Gets database account info. :return: @@ -2209,8 +2209,8 @@ def GetDatabaseAccount(self, url_connection=None): initial_headers = dict(self.default_headers) headers = base.GetHeaders(self, initial_headers, "get", "", "", "", {}) # path # id # type - request = _request_object.RequestObject("databaseaccount", documents._OperationType.Read, url_connection) - result, self.last_response_headers = self.__Get("", request, headers) + request_options = _request_object.RequestObject("databaseaccount", documents._OperationType.Read, url_connection) + result, self.last_response_headers = self.__Get("", request_options, headers, **kwargs) database_account = documents.DatabaseAccount() database_account.DatabasesLink = "/dbs/" database_account.MediaLink = "/media/" @@ -2239,7 +2239,7 @@ def GetDatabaseAccount(self, url_connection=None): ) return database_account - def Create(self, body, path, typ, id, initial_headers, options=None): # pylint: disable=redefined-builtin + def Create(self, body, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin """Creates a Azure Cosmos resource and returns it. :param dict body: @@ -2263,14 +2263,14 @@ def Create(self, body, path, typ, id, initial_headers, options=None): # pylint: headers = base.GetHeaders(self, initial_headers, "post", path, id, typ, options) # Create will use WriteEndpoint since it uses POST operation - request = _request_object.RequestObject(typ, documents._OperationType.Create) - result, self.last_response_headers = self.__Post(path, request, body, headers) + request_options = _request_object.RequestObject(typ, documents._OperationType.Create) + result, self.last_response_headers = self.__Post(path, request_options, body, headers, **kwargs) # update session for write request self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result - def Upsert(self, body, path, typ, id, initial_headers, options=None): # pylint: disable=redefined-builtin + def Upsert(self, body, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin """Upserts a Azure Cosmos resource and returns it. :param dict body: @@ -2296,13 +2296,13 @@ def Upsert(self, body, path, typ, id, initial_headers, options=None): # pylint: headers[http_constants.HttpHeaders.IsUpsert] = True # Upsert will use WriteEndpoint since it uses POST operation - request = _request_object.RequestObject(typ, documents._OperationType.Upsert) - result, self.last_response_headers = self.__Post(path, request, body, headers) + request_options = _request_object.RequestObject(typ, documents._OperationType.Upsert) + result, self.last_response_headers = self.__Post(path, request_options, body, headers, **kwargs) # update session for write request self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result - def Replace(self, resource, path, typ, id, initial_headers, options=None): # pylint: disable=redefined-builtin + def Replace(self, resource, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin """Replaces a Azure Cosmos resource and returns it. :param dict resource: @@ -2325,14 +2325,14 @@ def Replace(self, resource, path, typ, id, initial_headers, options=None): # py initial_headers = initial_headers or self.default_headers headers = base.GetHeaders(self, initial_headers, "put", path, id, typ, options) # Replace will use WriteEndpoint since it uses PUT operation - request = _request_object.RequestObject(typ, documents._OperationType.Replace) - result, self.last_response_headers = self.__Put(path, request, resource, headers) + request_options = _request_object.RequestObject(typ, documents._OperationType.Replace) + result, self.last_response_headers = self.__Put(path, request_options, resource, headers, **kwargs) # update session for request mutates data on server side self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result - def Read(self, path, typ, id, initial_headers, options=None): # pylint: disable=redefined-builtin + def Read(self, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin """Reads a Azure Cosmos resource and returns it. :param str path: @@ -2354,11 +2354,11 @@ def Read(self, path, typ, id, initial_headers, options=None): # pylint: disable initial_headers = initial_headers or self.default_headers headers = base.GetHeaders(self, initial_headers, "get", path, id, typ, options) # Read will use ReadEndpoint since it uses GET operation - request = _request_object.RequestObject(typ, documents._OperationType.Read) - result, self.last_response_headers = self.__Get(path, request, headers) + request_options = _request_object.RequestObject(typ, documents._OperationType.Read) + result, self.last_response_headers = self.__Get(path, request_options, headers, **kwargs) return result - def DeleteResource(self, path, typ, id, initial_headers, options=None): # pylint: disable=redefined-builtin + def DeleteResource(self, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin """Deletes a Azure Cosmos resource and returns it. :param str path: @@ -2380,15 +2380,15 @@ def DeleteResource(self, path, typ, id, initial_headers, options=None): # pylin initial_headers = initial_headers or self.default_headers headers = base.GetHeaders(self, initial_headers, "delete", path, id, typ, options) # Delete will use WriteEndpoint since it uses DELETE operation - request = _request_object.RequestObject(typ, documents._OperationType.Delete) - result, self.last_response_headers = self.__Delete(path, request, headers) + request_options = _request_object.RequestObject(typ, documents._OperationType.Delete) + result, self.last_response_headers = self.__Delete(path, request_options, headers, **kwargs) # update session for request mutates data on server side self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result - def __Get(self, path, request, headers): + def __Get(self, path, request_options, headers, **kwargs): """Azure Cosmos 'GET' http request. :params str url: @@ -2401,20 +2401,19 @@ def __Get(self, path, request, headers): tuple of (dict, dict) """ + request = self.pipeline_client.get(url=path, headers=headers) return synchronized_request.SynchronizedRequest( - self, - request, - self._global_endpoint_manager, - self.connection_policy, - self.pipeline_client, - "GET", - path, - None, - None, - headers, + client=self, + request_options=request_options, + global_endpoint_manager=self._global_endpoint_manager, + connection_policy=self.connection_policy, + pipeline_client=self.pipeline_client, + request=request, + request_data=None, + **kwargs ) - def __Post(self, path, request, body, headers): + def __Post(self, path, request_options, body, headers, **kwargs): """Azure Cosmos 'POST' http request. :params str url: @@ -2428,20 +2427,19 @@ def __Post(self, path, request, body, headers): tuple of (dict, dict) """ + request = self.pipeline_client.post(url=path, headers=headers) return synchronized_request.SynchronizedRequest( - self, - request, - self._global_endpoint_manager, - self.connection_policy, - self.pipeline_client, - "POST", - path, - body, - query_params=None, - headers=headers, + client=self, + request_options=request_options, + global_endpoint_manager=self._global_endpoint_manager, + connection_policy=self.connection_policy, + pipeline_client=self.pipeline_client, + request=request, + request_data=body, + **kwargs ) - def __Put(self, path, request, body, headers): + def __Put(self, path, request_options, body, headers, **kwargs): """Azure Cosmos 'PUT' http request. :params str url: @@ -2455,20 +2453,19 @@ def __Put(self, path, request, body, headers): tuple of (dict, dict) """ + request = self.pipeline_client.put(url=path, headers=headers) return synchronized_request.SynchronizedRequest( - self, - request, - self._global_endpoint_manager, - self.connection_policy, - self.pipeline_client, - "PUT", - path, - body, - query_params=None, - headers=headers, + client=self, + request_options=request_options, + global_endpoint_manager=self._global_endpoint_manager, + connection_policy=self.connection_policy, + pipeline_client=self.pipeline_client, + request=request, + request_data=body, + **kwargs ) - def __Delete(self, path, request, headers): + def __Delete(self, path, request_options, headers, **kwargs): """Azure Cosmos 'DELETE' http request. :params str url: @@ -2481,17 +2478,16 @@ def __Delete(self, path, request, headers): tuple of (dict, dict) """ + request = self.pipeline_client.delete(url=path, headers=headers) return synchronized_request.SynchronizedRequest( - self, - request, - self._global_endpoint_manager, - self.connection_policy, - self.pipeline_client, - "DELETE", - path, + client=self, + request_options=request_options, + global_endpoint_manager=self._global_endpoint_manager, + connection_policy=self.connection_policy, + pipeline_client=self.pipeline_client, + request=request, request_data=None, - query_params=None, - headers=headers, + **kwargs ) def QueryFeed(self, path, collection_id, query, options, partition_key_range_id=None): @@ -2525,7 +2521,17 @@ def QueryFeed(self, path, collection_id, query, options, partition_key_range_id= ) def __QueryFeed( - self, path, typ, id_, result_fn, create_fn, query, options=None, partition_key_range_id=None, response_hook=None + self, + path, + typ, + id_, + result_fn, + create_fn, + query, + options=None, + partition_key_range_id=None, + response_hook=None, + **kwargs ): """Query for more than one Azure Cosmos resources. @@ -2564,9 +2570,9 @@ def __GetBodiesFromQueryResult(result): # Copy to make sure that default_headers won't be changed. if query is None: # Query operations will use ReadEndpoint even though it uses GET(for feed requests) - request = _request_object.RequestObject(typ, documents._OperationType.ReadFeed) + request_options = _request_object.RequestObject(typ, documents._OperationType.ReadFeed) headers = base.GetHeaders(self, initial_headers, "get", path, id_, typ, options, partition_key_range_id) - result, self.last_response_headers = self.__Get(path, request, headers) + result, self.last_response_headers = self.__Get(path, request_options, headers, **kwargs) if response_hook: response_hook(self.last_response_headers, result) return __GetBodiesFromQueryResult(result) @@ -2585,9 +2591,9 @@ def __GetBodiesFromQueryResult(result): raise SystemError("Unexpected query compatibility mode.") # Query operations will use ReadEndpoint even though it uses POST(for regular query operations) - request = _request_object.RequestObject(typ, documents._OperationType.SqlQuery) + request_options = _request_object.RequestObject(typ, documents._OperationType.SqlQuery) headers = base.GetHeaders(self, initial_headers, "post", path, id_, typ, options, partition_key_range_id) - result, self.last_response_headers = self.__Post(path, request, query, headers) + result, self.last_response_headers = self.__Post(path, request_options, query, headers, **kwargs) if response_hook: response_hook(self.last_response_headers, result) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index 00b2bd821775..416f41e676d9 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -43,7 +43,32 @@ def _is_readable_stream(obj): return False -def _Request(global_endpoint_manager, request_options, connection_policy, pipeline_client, request): +def _request_body_from_data(data): + """Gets request body from data. + + When `data` is dict and list into unicode string; otherwise return `data` + without making any change. + + :param (str, unicode, file-like stream object, dict, list or None) data: + + :rtype: + str, unicode, file-like stream object, or None + + """ + if data is None or isinstance(data, six.string_types) or _is_readable_stream(data): + return data + elif isinstance(data, (dict, list, tuple)): + + json_dumped = json.dumps(data, separators=(',',':')) + + if six.PY2: + return json_dumped.decode('utf-8') + else: + return json_dumped + return None + + +def _Request(global_endpoint_manager, request_options, connection_policy, pipeline_client, request, **kwargs): """Makes one http request using the requests module. :param _GlobalEndpointManager global_endpoint_manager: @@ -66,6 +91,7 @@ def _Request(global_endpoint_manager, request_options, connection_policy, pipeli is_media_stream = is_media and connection_policy.MediaReadMode == documents.MediaReadMode.Streamed connection_timeout = connection_policy.MediaRequestTimeout if is_media else connection_policy.RequestTimeout + connection_timeout = kwargs.pop('connection_timeout', connection_timeout / 1000.0) # Every request tries to perform a refresh global_endpoint_manager.refresh_endpoint_list(None) @@ -91,23 +117,24 @@ def _Request(global_endpoint_manager, request_options, connection_policy, pipeli and not connection_policy.DisableSSLVerification ) - if connection_policy.SSLConfiguration: + if connection_policy.SSLConfiguration or 'connection_cert' in kwargs: ca_certs = connection_policy.SSLConfiguration.SSLCaCerts cert_files = (connection_policy.SSLConfiguration.SSLCertFile, connection_policy.SSLConfiguration.SSLKeyFile) response = pipeline_client._pipeline.run( request, stream=is_media_stream, - connection_timeout=connection_timeout / 1000.0, - connection_verify=ca_certs, - connection_cert=cert_files + connection_timeout=connection_timeout, + connection_verify=kwargs.pop('connection_verify', ca_certs), + connection_cert=kwargs.pop('connection_cert', cert_files), + ) else: response = pipeline_client._pipeline.run( request, stream=is_media_stream, - connection_timeout=connection_timeout / 1000.0, + connection_timeout=connection_timeout, # If SSL is disabled, verify = false - connection_verify=is_ssl_enabled + connection_verify=kwargs.pop('connection_verify', is_ssl_enabled) ) response = response.http_response @@ -145,11 +172,9 @@ def SynchronizedRequest( global_endpoint_manager, connection_policy, pipeline_client, - method, - path, + request, request_data, - query_params, - headers, + **kwargs ): """Performs one synchronized http request according to the parameters. @@ -172,18 +197,7 @@ def SynchronizedRequest( tuple of (dict dict) """ - is_stream = _is_readable_stream(request_data) - - request = pipeline_client._request( - method=method, - url=path + '?' + urlencode(query_params) if query_params else path, - params=None, - headers=headers, - content=request_data if not is_stream else None, - form_content=None, - stream_content=request_data if is_stream else None - ) - + request.data = _request_body_from_data(request_data) if request.data and isinstance(request.data, six.string_types): request.headers[http_constants.HttpHeaders.ContentLength] = len(request.data) elif request.data is None: @@ -197,5 +211,6 @@ def SynchronizedRequest( request_options, connection_policy, pipeline_client, - request + request, + **kwargs ) From 8c091d807a771c031b9cbc86c6f4eb157ccb9224 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 17:54:04 -0700 Subject: [PATCH 06/46] Fixed index test --- sdk/cosmos/azure-cosmos/test/crud_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 051d62a8ef66..0f093271d7c3 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -1946,7 +1946,7 @@ def __get_first(array): root_included_path = __get_first([included_path for included_path in indexing_policy['includedPaths'] if included_path['path'] == '/*']) - self.assertFalse(root_included_path['indexes']) + self.assertFalse(root_included_path.get('indexes')) def test_client_request_timeout(self): connection_policy = documents.ConnectionPolicy() From abc90303b0572dcec462594dd7ffda6b8ee69bfc Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 18:28:46 -0700 Subject: [PATCH 07/46] Added trace decorators --- .../azure-cosmos/azure/cosmos/container.py | 71 +++++++++++++----- .../azure/cosmos/cosmos_client.py | 27 ++++--- .../azure-cosmos/azure/cosmos/database.py | 72 +++++++++++++------ sdk/cosmos/azure-cosmos/azure/cosmos/user.py | 48 +++++++++---- 4 files changed, 155 insertions(+), 63 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py index 3abe9b3cea93..db929854e529 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py @@ -25,6 +25,7 @@ from typing import Any, Callable, Dict, List, Optional, Union import six +from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection from .errors import HTTPFailure @@ -96,6 +97,7 @@ def _get_conflict_link(self, conflict_or_link): return u"{}/conflicts/{}".format(self.container_link, conflict_or_link) return conflict_or_link["_self"] + @distributed_trace def read( self, session_token=None, @@ -105,6 +107,7 @@ def read( populate_quota_info=None, request_options=None, response_hook=None, + **kwargs ): # type: (str, Dict[str, str], bool, bool, bool, Dict[str, Any], Optional[Callable]) -> Container """ Read the container properties @@ -136,13 +139,14 @@ def read( request_options["populateQuotaInfo"] = populate_quota_info collection_link = self.container_link - self._properties = self.client_connection.ReadContainer(collection_link, options=request_options) + self._properties = self.client_connection.ReadContainer(collection_link, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) return self._properties + @distributed_trace def read_item( self, item, # type: Union[str, Dict[str, Any]] @@ -153,6 +157,7 @@ def read_item( post_trigger_include=None, # type: str request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> Dict[str, str] """ @@ -193,11 +198,12 @@ def read_item( if post_trigger_include: request_options["postTriggerInclude"] = post_trigger_include - result = self.client_connection.ReadItem(document_link=doc_link, options=request_options) + result = self.client_connection.ReadItem(document_link=doc_link, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def read_all_items( self, max_item_count=None, @@ -206,6 +212,7 @@ def read_all_items( populate_query_metrics=None, feed_options=None, response_hook=None, + **kwargs ): # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable """ List all items in the container. @@ -233,12 +240,13 @@ def read_all_items( response_hook.clear() items = self.client_connection.ReadItems( - collection_link=self.container_link, feed_options=feed_options, response_hook=response_hook + collection_link=self.container_link, feed_options=feed_options, response_hook=response_hook, **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, items) return items + @distributed_trace def query_items_change_feed( self, partition_key_range_id=None, @@ -247,6 +255,7 @@ def query_items_change_feed( max_item_count=None, feed_options=None, response_hook=None, + **kwargs ): """ Get a sorted list of items that were changed, in the order in which they were modified. @@ -277,12 +286,13 @@ def query_items_change_feed( response_hook.clear() result = self.client_connection.QueryItemsChangeFeed( - self.container_link, options=feed_options, response_hook=response_hook + self.container_link, options=feed_options, response_hook=response_hook, **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def query_items( self, query, # type: str @@ -296,6 +306,7 @@ def query_items( populate_query_metrics=None, # type: bool feed_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> QueryIterable """Return all results matching the given `query`. @@ -363,11 +374,13 @@ def query_items( options=feed_options, partition_key=partition_key, response_hook=response_hook, + **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, items) return items + @distributed_trace def replace_item( self, item, # type: Union[str, Dict[str, Any]] @@ -380,6 +393,7 @@ def replace_item( post_trigger_include=None, # type: str request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> Dict[str, str] """ Replaces the specified item if it exists in the container. @@ -415,11 +429,14 @@ def replace_item( if post_trigger_include: request_options["postTriggerInclude"] = post_trigger_include - result = self.client_connection.ReplaceItem(document_link=item_link, new_document=body, options=request_options) + result = self.client_connection.ReplaceItem( + document_link=item_link, new_document=body, options=request_options, **kwargs + ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def upsert_item( self, body, # type: Dict[str, Any] @@ -431,6 +448,7 @@ def upsert_item( post_trigger_include=None, # type: str request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> Dict[str, str] """ Insert or update the specified item. @@ -466,11 +484,13 @@ def upsert_item( if post_trigger_include: request_options["postTriggerInclude"] = post_trigger_include - result = self.client_connection.UpsertItem(database_or_Container_link=self.container_link, document=body) + result = self.client_connection.UpsertItem( + database_or_Container_link=self.container_link, document=body, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def create_item( self, body, # type: Dict[str, Any] @@ -483,6 +503,7 @@ def create_item( indexing_directive=None, # type: Any request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> Dict[str, str] """ Create an item in the container. @@ -523,12 +544,13 @@ def create_item( request_options["indexingDirective"] = indexing_directive result = self.client_connection.CreateItem( - database_or_Container_link=self.container_link, document=body, options=request_options + database_or_Container_link=self.container_link, document=body, options=request_options, **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def delete_item( self, item, # type: Union[Dict[str, Any], str] @@ -541,6 +563,7 @@ def delete_item( post_trigger_include=None, # type: str request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> None """ Delete the specified item from the container. @@ -577,11 +600,12 @@ def delete_item( request_options["postTriggerInclude"] = post_trigger_include document_link = self._get_document_link(item) - result = self.client_connection.DeleteItem(document_link=document_link, options=request_options) + result = self.client_connection.DeleteItem(document_link=document_link, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, result) - def read_offer(self, response_hook=None): + @distributed_trace + def read_offer(self, response_hook=None, **kwargs): # type: (Optional[Callable]) -> Offer """ Read the Offer object for this container. @@ -596,7 +620,7 @@ def read_offer(self, response_hook=None): "query": "SELECT * FROM root r WHERE r.resource=@link", "parameters": [{"name": "@link", "value": link}], } - offers = list(self.client_connection.QueryOffers(query_spec)) + offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for container " + self.container_link) @@ -605,7 +629,8 @@ def read_offer(self, response_hook=None): return Offer(offer_throughput=offers[0]["content"]["offerThroughput"], properties=offers[0]) - def replace_throughput(self, throughput, response_hook=None): + @distributed_trace + def replace_throughput(self, throughput, response_hook=None, **kwargs): # type: (int, Optional[Callable]) -> Offer """ Replace the container's throughput @@ -621,19 +646,20 @@ def replace_throughput(self, throughput, response_hook=None): "query": "SELECT * FROM root r WHERE r.resource=@link", "parameters": [{"name": "@link", "value": link}], } - offers = list(self.client_connection.QueryOffers(query_spec)) + offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for container " + self.container_link) new_offer = offers[0].copy() new_offer["content"]["offerThroughput"] = throughput - data = self.client_connection.ReplaceOffer(offer_link=offers[0]["_self"], offer=offers[0]) + data = self.client_connection.ReplaceOffer(offer_link=offers[0]["_self"], offer=offers[0], **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, data) return Offer(offer_throughput=data["content"]["offerThroughput"], properties=data) - def read_all_conflicts(self, max_item_count=None, feed_options=None, response_hook=None): + @distributed_trace + def read_all_conflicts(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): # type: (int, Dict[str, Any], Optional[Callable]) -> QueryIterable """ List all conflicts in the container. @@ -648,11 +674,14 @@ def read_all_conflicts(self, max_item_count=None, feed_options=None, response_ho if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - result = self.client_connection.ReadConflicts(collection_link=self.container_link, feed_options=feed_options) + result = self.client_connection.ReadConflicts( + collection_link=self.container_link, feed_options=feed_options, **kwargs + ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def query_conflicts( self, query, @@ -662,6 +691,7 @@ def query_conflicts( max_item_count=None, feed_options=None, response_hook=None, + **kwargs ): # type: (str, List, bool, Any, int, Dict[str, Any], Optional[Callable]) -> QueryIterable """Return all conflicts matching the given `query`. @@ -691,12 +721,14 @@ def query_conflicts( collection_link=self.container_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, + **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result - def get_conflict(self, conflict, partition_key, request_options=None, response_hook=None): + @distributed_trace + def get_conflict(self, conflict, partition_key, request_options=None, response_hook=None, **kwargs): # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable]) -> Dict[str, str] """ Get the conflict identified by `id`. @@ -714,13 +746,14 @@ def get_conflict(self, conflict, partition_key, request_options=None, response_h request_options["partitionKey"] = self._set_partition_key(partition_key) result = self.client_connection.ReadConflict( - conflict_link=self._get_conflict_link(conflict), options=request_options + conflict_link=self._get_conflict_link(conflict), options=request_options, **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result - def delete_conflict(self, conflict, partition_key, request_options=None, response_hook=None): + @distributed_trace + def delete_conflict(self, conflict, partition_key, request_options=None, response_hook=None, **kwargs): # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable]) -> None """ Delete the specified conflict from the container. @@ -738,7 +771,7 @@ def delete_conflict(self, conflict, partition_key, request_options=None, respons request_options["partitionKey"] = self._set_partition_key(partition_key) result = self.client_connection.DeleteConflict( - conflict_link=self._get_conflict_link(conflict), options=request_options + conflict_link=self._get_conflict_link(conflict), options=request_options, **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 6d62de891fb8..9b0aa1836831 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -25,6 +25,7 @@ from typing import Any, Callable, Dict, Mapping, Optional, Union, cast import six +from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection from .database import Database @@ -80,6 +81,7 @@ def _get_database_link(database_or_id): database_id = cast("Dict[str, str]", database_or_id)["id"] return "dbs/{}".format(database_id) + @distributed_trace def create_database( self, id, # pylint: disable=redefined-builtin @@ -90,6 +92,7 @@ def create_database( offer_throughput=None, request_options=None, response_hook=None, + **kwargs ): # type: (str, str, Dict[str, str], Dict[str, str], bool, int, Dict[str, Any], Optional[Callable]) -> Database """Create a new database with the given ID (name). @@ -128,7 +131,7 @@ def create_database( if offer_throughput is not None: request_options["offerThroughput"] = offer_throughput - result = self.client_connection.CreateDatabase(database=dict(id=id), options=request_options) + result = self.client_connection.CreateDatabase(database=dict(id=id), options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) return Database(self.client_connection, id=result["id"], properties=result) @@ -152,6 +155,7 @@ def get_database_client(self, database): return Database(self.client_connection, id_value) + @distributed_trace def read_all_databases( self, max_item_count=None, @@ -160,6 +164,7 @@ def read_all_databases( populate_query_metrics=None, feed_options=None, response_hook=None, + **kwargs ): # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable """ @@ -185,11 +190,12 @@ def read_all_databases( if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics - result = self.client_connection.ReadDatabases(options=feed_options) + result = self.client_connection.ReadDatabases(options=feed_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) return result + @distributed_trace def query_databases( self, query=None, # type: str @@ -201,6 +207,7 @@ def query_databases( populate_query_metrics=None, # type: bool feed_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> QueryIterable @@ -239,15 +246,15 @@ def query_databases( # (just returning a generator did not initiate the first network call, so # the headers were misleading) # This needs to change for "real" implementation - result = self.client_connection.QueryDatabases( - query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options - ) + query = query if parameters is None else dict(query=query, parameters=parameters) + result = self.client_connection.QueryDatabases(query=query, options=feed_options, **kwargs) else: - result = self.client_connection.ReadDatabases(options=feed_options) + result = self.client_connection.ReadDatabases(options=feed_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) return result + @distributed_trace def delete_database( self, database, # type: Union[str, Database, Dict[str, Any]] @@ -257,6 +264,7 @@ def delete_database( populate_query_metrics=None, # type: bool request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> None """ @@ -285,11 +293,12 @@ def delete_database( request_options["populateQueryMetrics"] = populate_query_metrics database_link = self._get_database_link(database) - self.client_connection.DeleteDatabase(database_link, options=request_options) + self.client_connection.DeleteDatabase(database_link, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) - def get_database_account(self, response_hook=None): + @distributed_trace + def get_database_account(self, response_hook=None, **kwargs): # type: (Optional[Callable]) -> DatabaseAccount """ Retrieve the database account information. @@ -298,7 +307,7 @@ def get_database_account(self, response_hook=None): :returns: A :class:`DatabaseAccount` instance representing the Cosmos DB Database Account. """ - result = self.client_connection.GetDatabaseAccount() + result = self.client_connection.GetDatabaseAccount(**kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) return result diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py index 6d2335986ec0..0df8589b70c2 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py @@ -25,6 +25,7 @@ from typing import Any, List, Dict, Mapping, Union, cast import six +from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection from .container import Container @@ -103,6 +104,7 @@ def _get_properties(self): self.read() return self._properties + @distributed_trace def read( self, session_token=None, @@ -110,6 +112,7 @@ def read( populate_query_metrics=None, request_options=None, response_hook=None, + **kwargs ): # type: (str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Dict[str, Any] """ @@ -139,13 +142,14 @@ def read( if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics - self._properties = self.client_connection.ReadDatabase(database_link, options=request_options) + self._properties = self.client_connection.ReadDatabase(database_link, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) return self._properties + @distributed_trace def create_container( self, id, # type: str # pylint: disable=redefined-builtin @@ -161,6 +165,7 @@ def create_container( conflict_resolution_policy=None, # type: Dict[str, Any] request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> Container """ @@ -228,7 +233,7 @@ def create_container( request_options["offerThroughput"] = offer_throughput data = self.client_connection.CreateContainer( - database_link=self.database_link, collection=definition, options=request_options + database_link=self.database_link, collection=definition, options=request_options, **kwargs ) if response_hook: @@ -236,6 +241,7 @@ def create_container( return Container(self.client_connection, self.database_link, data["id"], properties=data) + @distributed_trace def delete_container( self, container, # type: Union[str, Container, Dict[str, Any]] @@ -245,6 +251,7 @@ def delete_container( populate_query_metrics=None, # type: bool request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> None """ Delete the container @@ -273,7 +280,7 @@ def delete_container( request_options["populateQueryMetrics"] = populate_query_metrics collection_link = self._get_container_link(container) - result = self.client_connection.DeleteContainer(collection_link, options=request_options) + result = self.client_connection.DeleteContainer(collection_link, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, result) @@ -302,6 +309,7 @@ def get_container_client(self, container): return Container(self.client_connection, self.database_link, id_value) + @distributed_trace def read_all_containers( self, max_item_count=None, @@ -310,6 +318,7 @@ def read_all_containers( populate_query_metrics=None, feed_options=None, response_hook=None, + **kwargs ): # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable """ List the containers in the database. @@ -342,11 +351,14 @@ def read_all_containers( if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics - result = self.client_connection.ReadContainers(database_link=self.database_link, options=feed_options) + result = self.client_connection.ReadContainers( + database_link=self.database_link, options=feed_options, **kwargs + ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def query_containers( self, query=None, @@ -357,6 +369,7 @@ def query_containers( populate_query_metrics=None, feed_options=None, response_hook=None, + **kwargs ): # type: (str, List, int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable """List properties for containers in the current database @@ -387,11 +400,13 @@ def query_containers( database_link=self.database_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, + **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result + @distributed_trace def replace_container( self, container, # type: Union[str, Container, Dict[str, Any]] @@ -405,6 +420,7 @@ def replace_container( populate_query_metrics=None, # type: bool request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] + **kwargs ): # type: (...) -> Container """ Reset the properties of the container. Property changes are persisted immediately. @@ -462,7 +478,7 @@ def replace_container( } container_properties = self.client_connection.ReplaceContainer( - container_link, collection=parameters, options=request_options + container_link, collection=parameters, options=request_options, **kwargs ) if response_hook: @@ -472,7 +488,8 @@ def replace_container( self.client_connection, self.database_link, container_properties["id"], properties=container_properties ) - def read_all_users(self, max_item_count=None, feed_options=None, response_hook=None): + @distributed_trace + def read_all_users(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): # type: (int, Dict[str, Any], Optional[Callable]) -> QueryIterable """ List all users in the container. @@ -487,12 +504,15 @@ def read_all_users(self, max_item_count=None, feed_options=None, response_hook=N if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - result = self.client_connection.ReadUsers(database_link=self.database_link, options=feed_options) + result = self.client_connection.ReadUsers( + database_link=self.database_link, options=feed_options, **kwargs + ) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result - def query_users(self, query, parameters=None, max_item_count=None, feed_options=None, response_hook=None): + @distributed_trace + def query_users(self, query, parameters=None, max_item_count=None, feed_options=None, response_hook=None, **kwargs): # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> QueryIterable """Return all users matching the given `query`. @@ -513,6 +533,7 @@ def query_users(self, query, parameters=None, max_item_count=None, feed_options= database_link=self.database_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, + **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) @@ -538,7 +559,8 @@ def get_user_client(self, user): return User(client_connection=self.client_connection, id=id_value, database_link=self.database_link) - def create_user(self, body, request_options=None, response_hook=None): + @distributed_trace + def create_user(self, body, request_options=None, response_hook=None, **kwargs): # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> User """ Create a user in the container. @@ -563,7 +585,8 @@ def create_user(self, body, request_options=None, response_hook=None): if not request_options: request_options = {} # type: Dict[str, Any] - user = self.client_connection.CreateUser(database_link=self.database_link, user=body, options=request_options) + user = self.client_connection.CreateUser( + database_link=self.database_link, user=body, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, user) @@ -572,7 +595,8 @@ def create_user(self, body, request_options=None, response_hook=None): client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) - def upsert_user(self, body, request_options=None, response_hook=None): + @distributed_trace + def upsert_user(self, body, request_options=None, response_hook=None, **kwargs): # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> User """ Insert or update the specified user. @@ -588,7 +612,9 @@ def upsert_user(self, body, request_options=None, response_hook=None): if not request_options: request_options = {} # type: Dict[str, Any] - user = self.client_connection.UpsertUser(database_link=self.database_link, user=body, options=request_options) + user = self.client_connection.UpsertUser( + database_link=self.database_link, user=body, options=request_options, **kwargs + ) if response_hook: response_hook(self.client_connection.last_response_headers, user) @@ -597,7 +623,8 @@ def upsert_user(self, body, request_options=None, response_hook=None): client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) - def replace_user(self, user, body, request_options=None, response_hook=None): + @distributed_trace + def replace_user(self, user, body, request_options=None, response_hook=None, **kwargs): # type: (Union[str, User, Dict[str, Any]], Dict[str, Any], Dict[str, Any], Optional[Callable]) -> User """ Replaces the specified user if it exists in the container. @@ -614,7 +641,7 @@ def replace_user(self, user, body, request_options=None, response_hook=None): request_options = {} # type: Dict[str, Any] user = self.client_connection.ReplaceUser( - user_link=self._get_user_link(user), user=body, options=request_options + user_link=self._get_user_link(user), user=body, options=request_options, **kwargs ) if response_hook: @@ -624,7 +651,8 @@ def replace_user(self, user, body, request_options=None, response_hook=None): client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) - def delete_user(self, user, request_options=None, response_hook=None): + @distributed_trace + def delete_user(self, user, request_options=None, response_hook=None, **kwargs): # type: (Union[str, User, Dict[str, Any]], Dict[str, Any], Optional[Callable]) -> None """ Delete the specified user from the container. @@ -639,11 +667,14 @@ def delete_user(self, user, request_options=None, response_hook=None): if not request_options: request_options = {} # type: Dict[str, Any] - result = self.client_connection.DeleteUser(user_link=self._get_user_link(user), options=request_options) + result = self.client_connection.DeleteUser( + user_link=self._get_user_link(user), options=request_options, **kwargs + ) if response_hook: response_hook(self.client_connection.last_response_headers, result) - def read_offer(self, response_hook=None): + @distributed_trace + def read_offer(self, response_hook=None, **kwargs): # type: (Optional[Callable]) -> Offer """ Read the Offer object for this database. @@ -658,7 +689,7 @@ def read_offer(self, response_hook=None): "query": "SELECT * FROM root r WHERE r.resource=@link", "parameters": [{"name": "@link", "value": link}], } - offers = list(self.client_connection.QueryOffers(query_spec)) + offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for database " + self.database_link) @@ -667,7 +698,8 @@ def read_offer(self, response_hook=None): return Offer(offer_throughput=offers[0]["content"]["offerThroughput"], properties=offers[0]) - def replace_throughput(self, throughput, response_hook=None): + @distributed_trace + def replace_throughput(self, throughput, response_hook=None, **kwargs): # type: (int, Optional[Callable]) -> Offer """ Replace the database level throughput. @@ -688,7 +720,7 @@ def replace_throughput(self, throughput, response_hook=None): raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for collection " + self.database_link) new_offer = offers[0].copy() new_offer["content"]["offerThroughput"] = throughput - data = self.client_connection.ReplaceOffer(offer_link=offers[0]["_self"], offer=offers[0]) + data = self.client_connection.ReplaceOffer(offer_link=offers[0]["_self"], offer=offers[0], **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, data) return Offer(offer_throughput=data["content"]["offerThroughput"], properties=data) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py index ab6a59a2277c..b33a36fc81eb 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py @@ -25,6 +25,7 @@ from typing import Any, List, Dict, Union, cast import six +from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection from .permission import Permission @@ -54,7 +55,8 @@ def _get_properties(self): self.read() return self._properties - def read(self, request_options=None, response_hook=None): + @distributed_trace + def read(self, request_options=None, response_hook=None, **kwargs): # type: (Dict[str, Any], Optional[Callable]) -> User """ Read user propertes. @@ -68,14 +70,15 @@ def read(self, request_options=None, response_hook=None): if not request_options: request_options = {} # type: Dict[str, Any] - self._properties = self.client_connection.ReadUser(user_link=self.user_link, options=request_options) + self._properties = self.client_connection.ReadUser(user_link=self.user_link, options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) return self._properties - def read_all_permissions(self, max_item_count=None, feed_options=None, response_hook=None): + @distributed_trace + def read_all_permissions(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): # type: (int, Dict[str, Any], Optional[Callable]) -> QueryIterable """ List all permission for the user. @@ -90,14 +93,23 @@ def read_all_permissions(self, max_item_count=None, feed_options=None, response_ if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - result = self.client_connection.ReadPermissions(user_link=self.user_link, options=feed_options) + result = self.client_connection.ReadPermissions(user_link=self.user_link, options=feed_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result - def query_permissions(self, query, parameters=None, max_item_count=None, feed_options=None, response_hook=None): + @distributed_trace + def query_permissions( + self, + query, + parameters=None, + max_item_count=None, + feed_options=None, + response_hook=None, + **kwargs + ): # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> QueryIterable """Return all permissions matching the given `query`. @@ -118,6 +130,7 @@ def query_permissions(self, query, parameters=None, max_item_count=None, feed_op user_link=self.user_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, + **kwargs ) if response_hook: @@ -125,7 +138,8 @@ def query_permissions(self, query, parameters=None, max_item_count=None, feed_op return result - def get_permission(self, permission, request_options=None, response_hook=None): + @distributed_trace + def get_permission(self, permission, request_options=None, response_hook=None, **kwargs): # type: (str, Dict[str, Any], Optional[Callable]) -> Permission """ Get the permission identified by `id`. @@ -142,7 +156,7 @@ def get_permission(self, permission, request_options=None, response_hook=None): request_options = {} # type: Dict[str, Any] permission = self.client_connection.ReadPermission( - permission_link=self._get_permission_link(permission), options=request_options + permission_link=self._get_permission_link(permission), options=request_options, **kwargs ) if response_hook: @@ -156,7 +170,8 @@ def get_permission(self, permission, request_options=None, response_hook=None): properties=permission, ) - def create_permission(self, body, request_options=None, response_hook=None): + @distributed_trace + def create_permission(self, body, request_options=None, response_hook=None, **kwargs): # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> Permission """ Create a permission for the user. @@ -173,7 +188,7 @@ def create_permission(self, body, request_options=None, response_hook=None): request_options = {} # type: Dict[str, Any] permission = self.client_connection.CreatePermission( - user_link=self.user_link, permission=body, options=request_options + user_link=self.user_link, permission=body, options=request_options, **kwargs ) if response_hook: @@ -187,7 +202,8 @@ def create_permission(self, body, request_options=None, response_hook=None): properties=permission, ) - def upsert_permission(self, body, request_options=None, response_hook=None): + @distributed_trace + def upsert_permission(self, body, request_options=None, response_hook=None, **kwargs): # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> Permission """ Insert or update the specified permission. @@ -204,7 +220,7 @@ def upsert_permission(self, body, request_options=None, response_hook=None): request_options = {} # type: Dict[str, Any] permission = self.client_connection.UpsertPermission( - user_link=self.user_link, permission=body, options=request_options + user_link=self.user_link, permission=body, options=request_options, **kwargs ) if response_hook: @@ -218,7 +234,8 @@ def upsert_permission(self, body, request_options=None, response_hook=None): properties=permission, ) - def replace_permission(self, permission, body, request_options=None, response_hook=None): + @distributed_trace + def replace_permission(self, permission, body, request_options=None, response_hook=None, **kwargs): # type: (str, Dict[str, Any], Dict[str, Any], Optional[Callable]) -> Permission """ Replaces the specified permission if it exists for the user. @@ -235,7 +252,7 @@ def replace_permission(self, permission, body, request_options=None, response_ho request_options = {} # type: Dict[str, Any] permission = self.client_connection.ReplacePermission( - permission_link=self._get_permission_link(permission), permission=body, options=request_options + permission_link=self._get_permission_link(permission), permission=body, options=request_options, **kwargs ) if response_hook: @@ -249,7 +266,8 @@ def replace_permission(self, permission, body, request_options=None, response_ho properties=permission, ) - def delete_permission(self, permission, request_options=None, response_hook=None): + @distributed_trace + def delete_permission(self, permission, request_options=None, response_hook=None, **kwargs): # type: (str, Dict[str, Any], Optional[Callable]) -> None """ Delete the specified permission from the user. @@ -266,7 +284,7 @@ def delete_permission(self, permission, request_options=None, response_hook=None request_options = {} # type: Dict[str, Any] result = self.client_connection.DeletePermission( - permission_link=self._get_permission_link(permission), options=request_options + permission_link=self._get_permission_link(permission), options=request_options, **kwargs ) if response_hook: From f02ca0f11247df6bb4fa7047df6d5dacd6b53c3d Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 19:38:40 -0700 Subject: [PATCH 08/46] Bumped version --- .../azure-cosmos/azure/cosmos/__init__.py | 2 ++ .../azure-cosmos/azure/cosmos/version.py | 22 +++++++++++++++++++ sdk/cosmos/azure-cosmos/setup.py | 8 +++++-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 sdk/cosmos/azure-cosmos/azure/cosmos/version.py diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py index 19e73780aa50..07f3ca79fb93 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py @@ -37,6 +37,7 @@ from .permission import Permission from .scripts import Scripts from .user import User +from .version import VERSION __all__ = ( "Container", @@ -56,3 +57,4 @@ "TriggerOperation", "TriggerType", ) +__version__ = VERSION \ No newline at end of file diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/version.py b/sdk/cosmos/azure-cosmos/azure/cosmos/version.py new file mode 100644 index 000000000000..ea688752d60b --- /dev/null +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/version.py @@ -0,0 +1,22 @@ +# The MIT License (MIT) +# Copyright (c) 2014 Microsoft Corporation + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +VERSION = "4.0.0b2" diff --git a/sdk/cosmos/azure-cosmos/setup.py b/sdk/cosmos/azure-cosmos/setup.py index a54fe85fb62b..a2bc049eaf9d 100644 --- a/sdk/cosmos/azure-cosmos/setup.py +++ b/sdk/cosmos/azure-cosmos/setup.py @@ -7,7 +7,7 @@ # pylint:disable=missing-docstring import re -import os.path +import os from io import open from setuptools import find_packages, setup @@ -20,6 +20,10 @@ # a-b-c => a.b.c NAMESPACE_NAME = PACKAGE_NAME.replace("-", ".") +# Version extraction inspired from 'requests' +with open(os.path.join(PACKAGE_FOLDER_PATH, 'version.py'), 'r') as fd: + version = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', + fd.read(), re.MULTILINE).group(1) with open("README.md", encoding="utf-8") as f: README = f.read() @@ -28,7 +32,7 @@ setup( name=PACKAGE_NAME, - version='4.0.0b1', + version=version, description="Microsoft Azure {} Client Library for Python".format(PACKAGE_PPRINT_NAME), long_description=README + "\n\n" + HISTORY, long_description_content_type="text/markdown", From c9ae86799d45917f44d01c8b92544e59818c4fe6 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 27 Aug 2019 19:39:21 -0700 Subject: [PATCH 09/46] Updated policies --- .../azure/cosmos/_cosmos_client_connection.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index a097f946f15a..fdb5e6aa12c3 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -24,8 +24,9 @@ """Document client class for the Azure Cosmos database service. """ -import requests +import platform +import requests import six from azure.core import PipelineClient from azure.core.pipeline.policies import ( @@ -50,6 +51,7 @@ from . import _session from . import _utils from .partition_key import _Undefined, _Empty +from .version import VERSION # pylint: disable=protected-access @@ -148,15 +150,19 @@ def __init__( url = six.moves.urllib.parse.urlparse(host) proxy = host if url.port else host + ":" + str(connection_policy.ProxyConfiguration.Port) proxies = {url.scheme : proxy} + user_agent = "azsdk-python-cosmos/{} Python/{} ({})".format( + VERSION, + platform.python_version(), + platform.platform()) policies = [ HeadersPolicy(), ProxyPolicy(proxies=proxies), - UserAgentPolicy(), + UserAgentPolicy(base_user_agent=user_agent), ContentDecodePolicy(), CustomHookPolicy(), + DistributedTracingPolicy(), NetworkTraceLoggingPolicy(), - DistributedTracingPolicy() ] self.pipeline_client = PipelineClient(url_connection, "empty-config", policies=policies) From 095f6f88f8be614366afaf5629c82d6f132c9a7b Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 28 Aug 2019 12:49:30 -0700 Subject: [PATCH 10/46] Renamed request_options -> request_params --- .../azure/cosmos/_cosmos_client_connection.py | 60 +++++++++---------- .../azure/cosmos/_synchronized_request.py | 39 ++++++------ sdk/cosmos/azure-cosmos/test/session_tests.py | 4 +- 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index fdb5e6aa12c3..23c5c4c432d7 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -1831,8 +1831,8 @@ def ReadMedia(self, media_link, **kwargs): headers = base.GetHeaders(self, default_headers, "get", path, attachment_id, "media", {}) # ReadMedia will always use WriteEndpoint since it's not replicated in readable Geo regions - request_options = _request_object.RequestObject("media", documents._OperationType.Read) - result, self.last_response_headers = self.__Get(path, request_options, headers, **kwargs) + request_params = _request_object.RequestObject("media", documents._OperationType.Read) + result, self.last_response_headers = self.__Get(path, request_params, headers, **kwargs) return result def UpdateMedia(self, media_link, readable_stream, options=None, **kwargs): @@ -1870,8 +1870,8 @@ def UpdateMedia(self, media_link, readable_stream, options=None, **kwargs): headers = base.GetHeaders(self, initial_headers, "put", path, attachment_id, "media", options) # UpdateMedia will use WriteEndpoint since it uses PUT operation - request_options = _request_object.RequestObject("media", documents._OperationType.Update) - result, self.last_response_headers = self.__Put(path, request_options, readable_stream, headers, **kwargs) + request_params = _request_object.RequestObject("media", documents._OperationType.Update) + result, self.last_response_headers = self.__Put(path, request_params, readable_stream, headers, **kwargs) self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result @@ -2050,8 +2050,8 @@ def ExecuteStoredProcedure(self, sproc_link, params, options=None, **kwargs): headers = base.GetHeaders(self, initial_headers, "post", path, sproc_id, "sprocs", options) # ExecuteStoredProcedure will use WriteEndpoint since it uses POST operation - request_options = _request_object.RequestObject("sprocs", documents._OperationType.ExecuteJavaScript) - result, self.last_response_headers = self.__Post(path, request_options, params, headers, **kwargs) + request_params = _request_object.RequestObject("sprocs", documents._OperationType.ExecuteJavaScript) + result, self.last_response_headers = self.__Post(path, request_params, params, headers, **kwargs) return result def ReplaceStoredProcedure(self, sproc_link, sproc, options=None): @@ -2215,8 +2215,8 @@ def GetDatabaseAccount(self, url_connection=None, **kwargs): initial_headers = dict(self.default_headers) headers = base.GetHeaders(self, initial_headers, "get", "", "", "", {}) # path # id # type - request_options = _request_object.RequestObject("databaseaccount", documents._OperationType.Read, url_connection) - result, self.last_response_headers = self.__Get("", request_options, headers, **kwargs) + request_params = _request_object.RequestObject("databaseaccount", documents._OperationType.Read, url_connection) + result, self.last_response_headers = self.__Get("", request_params, headers, **kwargs) database_account = documents.DatabaseAccount() database_account.DatabasesLink = "/dbs/" database_account.MediaLink = "/media/" @@ -2269,8 +2269,8 @@ def Create(self, body, path, typ, id, initial_headers, options=None, **kwargs): headers = base.GetHeaders(self, initial_headers, "post", path, id, typ, options) # Create will use WriteEndpoint since it uses POST operation - request_options = _request_object.RequestObject(typ, documents._OperationType.Create) - result, self.last_response_headers = self.__Post(path, request_options, body, headers, **kwargs) + request_params = _request_object.RequestObject(typ, documents._OperationType.Create) + result, self.last_response_headers = self.__Post(path, request_params, body, headers, **kwargs) # update session for write request self._UpdateSessionIfRequired(headers, result, self.last_response_headers) @@ -2302,8 +2302,8 @@ def Upsert(self, body, path, typ, id, initial_headers, options=None, **kwargs): headers[http_constants.HttpHeaders.IsUpsert] = True # Upsert will use WriteEndpoint since it uses POST operation - request_options = _request_object.RequestObject(typ, documents._OperationType.Upsert) - result, self.last_response_headers = self.__Post(path, request_options, body, headers, **kwargs) + request_params = _request_object.RequestObject(typ, documents._OperationType.Upsert) + result, self.last_response_headers = self.__Post(path, request_params, body, headers, **kwargs) # update session for write request self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result @@ -2331,8 +2331,8 @@ def Replace(self, resource, path, typ, id, initial_headers, options=None, **kwar initial_headers = initial_headers or self.default_headers headers = base.GetHeaders(self, initial_headers, "put", path, id, typ, options) # Replace will use WriteEndpoint since it uses PUT operation - request_options = _request_object.RequestObject(typ, documents._OperationType.Replace) - result, self.last_response_headers = self.__Put(path, request_options, resource, headers, **kwargs) + request_params = _request_object.RequestObject(typ, documents._OperationType.Replace) + result, self.last_response_headers = self.__Put(path, request_params, resource, headers, **kwargs) # update session for request mutates data on server side self._UpdateSessionIfRequired(headers, result, self.last_response_headers) @@ -2360,8 +2360,8 @@ def Read(self, path, typ, id, initial_headers, options=None, **kwargs): # pylin initial_headers = initial_headers or self.default_headers headers = base.GetHeaders(self, initial_headers, "get", path, id, typ, options) # Read will use ReadEndpoint since it uses GET operation - request_options = _request_object.RequestObject(typ, documents._OperationType.Read) - result, self.last_response_headers = self.__Get(path, request_options, headers, **kwargs) + request_params = _request_object.RequestObject(typ, documents._OperationType.Read) + result, self.last_response_headers = self.__Get(path, request_params, headers, **kwargs) return result def DeleteResource(self, path, typ, id, initial_headers, options=None, **kwargs): # pylint: disable=redefined-builtin @@ -2386,15 +2386,15 @@ def DeleteResource(self, path, typ, id, initial_headers, options=None, **kwargs) initial_headers = initial_headers or self.default_headers headers = base.GetHeaders(self, initial_headers, "delete", path, id, typ, options) # Delete will use WriteEndpoint since it uses DELETE operation - request_options = _request_object.RequestObject(typ, documents._OperationType.Delete) - result, self.last_response_headers = self.__Delete(path, request_options, headers, **kwargs) + request_params = _request_object.RequestObject(typ, documents._OperationType.Delete) + result, self.last_response_headers = self.__Delete(path, request_params, headers, **kwargs) # update session for request mutates data on server side self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result - def __Get(self, path, request_options, headers, **kwargs): + def __Get(self, path, request_params, headers, **kwargs): """Azure Cosmos 'GET' http request. :params str url: @@ -2410,7 +2410,7 @@ def __Get(self, path, request_options, headers, **kwargs): request = self.pipeline_client.get(url=path, headers=headers) return synchronized_request.SynchronizedRequest( client=self, - request_options=request_options, + request_params=request_params, global_endpoint_manager=self._global_endpoint_manager, connection_policy=self.connection_policy, pipeline_client=self.pipeline_client, @@ -2419,7 +2419,7 @@ def __Get(self, path, request_options, headers, **kwargs): **kwargs ) - def __Post(self, path, request_options, body, headers, **kwargs): + def __Post(self, path, request_params, body, headers, **kwargs): """Azure Cosmos 'POST' http request. :params str url: @@ -2436,7 +2436,7 @@ def __Post(self, path, request_options, body, headers, **kwargs): request = self.pipeline_client.post(url=path, headers=headers) return synchronized_request.SynchronizedRequest( client=self, - request_options=request_options, + request_params=request_params, global_endpoint_manager=self._global_endpoint_manager, connection_policy=self.connection_policy, pipeline_client=self.pipeline_client, @@ -2445,7 +2445,7 @@ def __Post(self, path, request_options, body, headers, **kwargs): **kwargs ) - def __Put(self, path, request_options, body, headers, **kwargs): + def __Put(self, path, request_params, body, headers, **kwargs): """Azure Cosmos 'PUT' http request. :params str url: @@ -2462,7 +2462,7 @@ def __Put(self, path, request_options, body, headers, **kwargs): request = self.pipeline_client.put(url=path, headers=headers) return synchronized_request.SynchronizedRequest( client=self, - request_options=request_options, + request_params=request_params, global_endpoint_manager=self._global_endpoint_manager, connection_policy=self.connection_policy, pipeline_client=self.pipeline_client, @@ -2471,7 +2471,7 @@ def __Put(self, path, request_options, body, headers, **kwargs): **kwargs ) - def __Delete(self, path, request_options, headers, **kwargs): + def __Delete(self, path, request_params, headers, **kwargs): """Azure Cosmos 'DELETE' http request. :params str url: @@ -2487,7 +2487,7 @@ def __Delete(self, path, request_options, headers, **kwargs): request = self.pipeline_client.delete(url=path, headers=headers) return synchronized_request.SynchronizedRequest( client=self, - request_options=request_options, + request_params=request_params, global_endpoint_manager=self._global_endpoint_manager, connection_policy=self.connection_policy, pipeline_client=self.pipeline_client, @@ -2576,9 +2576,9 @@ def __GetBodiesFromQueryResult(result): # Copy to make sure that default_headers won't be changed. if query is None: # Query operations will use ReadEndpoint even though it uses GET(for feed requests) - request_options = _request_object.RequestObject(typ, documents._OperationType.ReadFeed) + request_params = _request_object.RequestObject(typ, documents._OperationType.ReadFeed) headers = base.GetHeaders(self, initial_headers, "get", path, id_, typ, options, partition_key_range_id) - result, self.last_response_headers = self.__Get(path, request_options, headers, **kwargs) + result, self.last_response_headers = self.__Get(path, request_params, headers, **kwargs) if response_hook: response_hook(self.last_response_headers, result) return __GetBodiesFromQueryResult(result) @@ -2597,9 +2597,9 @@ def __GetBodiesFromQueryResult(result): raise SystemError("Unexpected query compatibility mode.") # Query operations will use ReadEndpoint even though it uses POST(for regular query operations) - request_options = _request_object.RequestObject(typ, documents._OperationType.SqlQuery) + request_params = _request_object.RequestObject(typ, documents._OperationType.SqlQuery) headers = base.GetHeaders(self, initial_headers, "post", path, id_, typ, options, partition_key_range_id) - result, self.last_response_headers = self.__Post(path, request_options, query, headers, **kwargs) + result, self.last_response_headers = self.__Post(path, request_params, query, headers, **kwargs) if response_hook: response_hook(self.last_response_headers, result) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index 416f41e676d9..618541feb6b0 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -57,22 +57,21 @@ def _request_body_from_data(data): """ if data is None or isinstance(data, six.string_types) or _is_readable_stream(data): return data - elif isinstance(data, (dict, list, tuple)): - - json_dumped = json.dumps(data, separators=(',',':')) + if isinstance(data, (dict, list, tuple)): + + json_dumped = json.dumps(data, separators=(",", ":")) if six.PY2: - return json_dumped.decode('utf-8') - else: - return json_dumped + return json_dumped.decode("utf-8") + return json_dumped return None -def _Request(global_endpoint_manager, request_options, connection_policy, pipeline_client, request, **kwargs): +def _Request(global_endpoint_manager, request_params, connection_policy, pipeline_client, request, **kwargs): """Makes one http request using the requests module. :param _GlobalEndpointManager global_endpoint_manager: - :param dict request_options: + :param dict request_params: contains the resourceType, operationType, endpointOverride, useWriteEndpoint, useAlternateWriteEndpoint information :param documents.ConnectionPolicy connection_policy: @@ -87,19 +86,19 @@ def _Request(global_endpoint_manager, request_options, connection_policy, pipeli tuple of (dict, dict) """ - is_media = request.url.find('media') > -1 + is_media = request.url.find("media") > -1 is_media_stream = is_media and connection_policy.MediaReadMode == documents.MediaReadMode.Streamed connection_timeout = connection_policy.MediaRequestTimeout if is_media else connection_policy.RequestTimeout - connection_timeout = kwargs.pop('connection_timeout', connection_timeout / 1000.0) + connection_timeout = kwargs.pop("connection_timeout", connection_timeout / 1000.0) # Every request tries to perform a refresh global_endpoint_manager.refresh_endpoint_list(None) - if request_options.endpoint_override: - base_url = request_options.endpoint_override + if request_params.endpoint_override: + base_url = request_params.endpoint_override else: - base_url = global_endpoint_manager.resolve_service_endpoint(request_options) + base_url = global_endpoint_manager.resolve_service_endpoint(request_params) if base_url != pipeline_client._base_url: request.url = request.url.replace(pipeline_client._base_url, base_url) @@ -117,15 +116,15 @@ def _Request(global_endpoint_manager, request_options, connection_policy, pipeli and not connection_policy.DisableSSLVerification ) - if connection_policy.SSLConfiguration or 'connection_cert' in kwargs: + if connection_policy.SSLConfiguration or "connection_cert" in kwargs: ca_certs = connection_policy.SSLConfiguration.SSLCaCerts cert_files = (connection_policy.SSLConfiguration.SSLCertFile, connection_policy.SSLConfiguration.SSLKeyFile) response = pipeline_client._pipeline.run( request, stream=is_media_stream, connection_timeout=connection_timeout, - connection_verify=kwargs.pop('connection_verify', ca_certs), - connection_cert=kwargs.pop('connection_cert', cert_files), + connection_verify=kwargs.pop("connection_verify", ca_certs), + connection_cert=kwargs.pop("connection_cert", cert_files), ) else: @@ -134,7 +133,7 @@ def _Request(global_endpoint_manager, request_options, connection_policy, pipeli stream=is_media_stream, connection_timeout=connection_timeout, # If SSL is disabled, verify = false - connection_verify=kwargs.pop('connection_verify', is_ssl_enabled) + connection_verify=kwargs.pop("connection_verify", is_ssl_enabled) ) response = response.http_response @@ -168,7 +167,7 @@ def _Request(global_endpoint_manager, request_options, connection_policy, pipeli def SynchronizedRequest( client, - request_options, + request_params, global_endpoint_manager, connection_policy, pipeline_client, @@ -180,7 +179,7 @@ def SynchronizedRequest( :param object client: Document client instance - :param dict request_options: + :param dict request_params: :param _GlobalEndpointManager global_endpoint_manager: :param documents.ConnectionPolicy connection_policy: :param azure.core.PipelineClient pipeline_client: @@ -208,7 +207,7 @@ def SynchronizedRequest( client, global_endpoint_manager, _Request, - request_options, + request_params, connection_policy, pipeline_client, request, diff --git a/sdk/cosmos/azure-cosmos/test/session_tests.py b/sdk/cosmos/azure-cosmos/test/session_tests.py index f46bbea3077a..f3e7e3e9fe93 100644 --- a/sdk/cosmos/azure-cosmos/test/session_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_tests.py @@ -37,12 +37,12 @@ def setUpClass(cls): cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) - def _MockRequest(self, global_endpoint_manager, request_options, connection_policy, pipeline_client, request): + def _MockRequest(self, global_endpoint_manager, request_params, connection_policy, pipeline_client, request): if HttpHeaders.SessionToken in request.headers: self.last_session_token_sent = request.headers[HttpHeaders.SessionToken] else: self.last_session_token_sent = None - return self._OriginalRequest(global_endpoint_manager, request_options, connection_policy, pipeline_client, request) + return self._OriginalRequest(global_endpoint_manager, request_params, connection_policy, pipeline_client, request) def test_session_token_not_sent_for_master_resource_ops (self): self._OriginalRequest = synchronized_request._Request From 872cb26d5b31fd0d233c7d6dc3271b5fafe7e08a Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 29 Aug 2019 09:46:10 -0700 Subject: [PATCH 11/46] Renamed clients --- .../azure-cosmos/azure/cosmos/__init__.py | 6 +- .../_execution_context/document_producer.py | 3 +- .../azure/cosmos/_runtime_constants.py | 2 +- .../azure-cosmos/azure/cosmos/_session.py | 2 +- .../{container.py => container_client.py} | 2 +- .../azure/cosmos/cosmos_client.py | 26 +++--- .../{database.py => database_client.py} | 86 +++++++++---------- .../azure/cosmos/http_constants.py | 28 +++--- .../azure-cosmos/azure/cosmos/permission.py | 2 +- .../azure-cosmos/azure/cosmos/scripts.py | 4 +- .../azure/cosmos/{user.py => user_client.py} | 9 +- 11 files changed, 86 insertions(+), 84 deletions(-) rename sdk/cosmos/azure-cosmos/azure/cosmos/{container.py => container_client.py} (99%) rename sdk/cosmos/azure-cosmos/azure/cosmos/{database.py => database_client.py} (92%) rename sdk/cosmos/azure-cosmos/azure/cosmos/{user.py => user_client.py} (98%) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py index 07f3ca79fb93..a2218a14dba9 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py @@ -19,9 +19,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from .container import Container +from .container_client import ContainerClient from .cosmos_client import CosmosClient -from .database import Database +from .database_client import DatabaseClient +from .user_client import UserClient from .documents import ( ConsistencyLevel, DataType, @@ -36,7 +37,6 @@ from .partition_key import PartitionKey from .permission import Permission from .scripts import Scripts -from .user import User from .version import VERSION __all__ = ( diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/document_producer.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/document_producer.py index 19d29ab45866..cb554127c276 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/document_producer.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/document_producer.py @@ -128,7 +128,8 @@ def compare(self, doc_producer1, doc_producer2): # pylint: disable=no-self-use ) -class _OrderByHelper: +class _OrderByHelper(object): + @staticmethod def getTypeOrd(orderby_item): """Returns the ordinal of the value of the item pair in the dictionary. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_runtime_constants.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_runtime_constants.py index 6396de351536..fc9e640b0899 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_runtime_constants.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_runtime_constants.py @@ -23,7 +23,7 @@ """ -class MediaTypes: +class MediaTypes(object): """Constants of media types. http://www.iana.org/assignments/media-types/media-types.xhtml diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py index c80a53c0c5f1..077e56a397a4 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py @@ -204,7 +204,7 @@ def parse_session_token(response_headers): return id_to_sessionlsn -class Session: +class Session(object): """ State of a Azure Cosmos session. This session object can be shared across clients within the same process diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py similarity index 99% rename from sdk/cosmos/azure-cosmos/azure/cosmos/container.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index db929854e529..2bea301cc6a7 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -40,7 +40,7 @@ # pylint: disable=protected-access -class Container: +class ContainerClient(object): """ An Azure Cosmos DB container. A container in an Azure Cosmos DB SQL API database is a collection of documents, diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 9b0aa1836831..78f794661f8c 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -28,14 +28,14 @@ from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection -from .database import Database +from .database_client import DatabaseClient from .documents import ConnectionPolicy, DatabaseAccount from ._query_iterable import QueryIterable __all__ = ("CosmosClient",) -class CosmosClient: +class CosmosClient(object): """ Provides a client-side logical representation of an Azure Cosmos DB account. Use this client to configure and execute requests to the Azure Cosmos DB service. @@ -75,7 +75,7 @@ def _get_database_link(database_or_id): if isinstance(database_or_id, six.string_types): return "dbs/{}".format(database_or_id) try: - return cast("Database", database_or_id).database_link + return cast("DatabaseClient", database_or_id).database_link except AttributeError: pass database_id = cast("Dict[str, str]", database_or_id)["id"] @@ -94,7 +94,7 @@ def create_database( response_hook=None, **kwargs ): - # type: (str, str, Dict[str, str], Dict[str, str], bool, int, Dict[str, Any], Optional[Callable]) -> Database + # type: (str, str, Dict[str, str], Dict[str, str], bool, int, Dict[str, Any], Optional[Callable]) -> DatabaseClient """Create a new database with the given ID (name). :param id: ID (name) of the database to create. @@ -105,7 +105,7 @@ def create_database( :param offer_throughput: The provisioned throughput for this offer. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`Database` instance representing the new database. + :returns: A :class:`DatabaseClient` instance representing the new database. :raises `HTTPFailure`: If database with the given ID already exists. .. literalinclude:: ../../examples/examples.py @@ -134,26 +134,26 @@ def create_database( result = self.client_connection.CreateDatabase(database=dict(id=id), options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) - return Database(self.client_connection, id=result["id"], properties=result) + return DatabaseClient(self.client_connection, id=result["id"], properties=result) def get_database_client(self, database): - # type: (Union[str, Database, Dict[str, Any]]) -> Database + # type: (Union[str, DatabaseClient, Dict[str, Any]]) -> DatabaseClient """ Retrieve an existing database with the ID (name) `id`. - :param database: The ID (name), dict representing the properties or :class:`Database` + :param database: The ID (name), dict representing the properties or :class:`DatabaseClient` instance of the database to read. - :returns: A :class:`Database` instance representing the retrieved database. + :returns: A :class:`DatabaseClient` instance representing the retrieved database. """ - if isinstance(database, Database): + if isinstance(database, DatabaseClient): id_value = database.id elif isinstance(database, Mapping): id_value = database["id"] else: id_value = database - return Database(self.client_connection, id_value) + return DatabaseClient(self.client_connection, id_value) @distributed_trace def read_all_databases( @@ -257,7 +257,7 @@ def query_databases( @distributed_trace def delete_database( self, - database, # type: Union[str, Database, Dict[str, Any]] + database, # type: Union[str, DatabaseClient, Dict[str, Any]] session_token=None, # type: str initial_headers=None, # type: Dict[str, str] access_condition=None, # type: Dict[str, str] @@ -270,7 +270,7 @@ def delete_database( """ Delete the database with the given ID (name). - :param database: The ID (name), dict representing the properties or :class:`Database` + :param database: The ID (name), dict representing the properties or :class:`DatabaseClient` instance of the database to delete. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py similarity index 92% rename from sdk/cosmos/azure-cosmos/azure/cosmos/database.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index 0df8589b70c2..0e45cfbb26d7 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -28,19 +28,19 @@ from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection -from .container import Container +from .container_client import ContainerClient from .offer import Offer from .http_constants import StatusCodes from .errors import HTTPFailure -from .user import User +from .user_client import UserClient from ._query_iterable import QueryIterable -__all__ = ("Database",) +__all__ = ("DatabaseClient",) # pylint: disable=protected-access -class Database(object): +class DatabaseClient(object): """ Represents an Azure Cosmos DB SQL API database. A database contains one or more containers, each of which can contain items, @@ -75,25 +75,25 @@ def __init__(self, client_connection, id, properties=None): # pylint: disable=r @staticmethod def _get_container_id(container_or_id): - # type: (Union[str, Container, Dict[str, Any]]) -> str + # type: (Union[str, ContainerClient, Dict[str, Any]]) -> str if isinstance(container_or_id, six.string_types): return container_or_id try: - return cast("Container", container_or_id).id + return cast("ContainerClient", container_or_id).id except AttributeError: pass return cast("Dict[str, str]", container_or_id)["id"] def _get_container_link(self, container_or_id): - # type: (Union[str, Container, Dict[str, Any]]) -> str + # type: (Union[str, ContainerClient, Dict[str, Any]]) -> str return u"{}/colls/{}".format(self.database_link, self._get_container_id(container_or_id)) def _get_user_link(self, user_or_id): - # type: (Union[User, str, Dict[str, Any]]) -> str + # type: (Union[UserClient, str, Dict[str, Any]]) -> str if isinstance(user_or_id, six.string_types): return u"{}/users/{}".format(self.database_link, user_or_id) try: - return cast("User", user_or_id).user_link + return cast("UserClient", user_or_id).user_link except AttributeError: pass return u"{}/users/{}".format(self.database_link, cast("Dict[str, str]", user_or_id)["id"]) @@ -118,7 +118,7 @@ def read( """ Read the database properties - :param database: The ID (name), dict representing the properties or :class:`Database` + :param database: The ID (name), dict representing the properties or :class:`DatabaseClient` instance of the database to read. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. @@ -167,7 +167,7 @@ def create_container( response_hook=None, # type: Optional[Callable] **kwargs ): - # type: (...) -> Container + # type: (...) -> ContainerClient """ Create a new container with the given ID (name). @@ -186,7 +186,7 @@ def create_container( :param conflict_resolution_policy: The conflict resolution policy to apply to the container. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`Container` instance representing the new container. + :returns: A :class:`ContainerClient` instance representing the new container. :raise HTTPFailure: The container creation failed. @@ -239,12 +239,12 @@ def create_container( if response_hook: response_hook(self.client_connection.last_response_headers, data) - return Container(self.client_connection, self.database_link, data["id"], properties=data) + return ContainerClient(self.client_connection, self.database_link, data["id"], properties=data) @distributed_trace def delete_container( self, - container, # type: Union[str, Container, Dict[str, Any]] + container, # type: Union[str, ContainerClient, Dict[str, Any]] session_token=None, # type: str initial_headers=None, # type: Dict[str, str] access_condition=None, # type: Dict[str, str] @@ -257,7 +257,7 @@ def delete_container( """ Delete the container :param container: The ID (name) of the container to delete. You can either - pass in the ID of the container to delete, a :class:`Container` instance or + pass in the ID of the container to delete, a :class:`ContainerClient` instance or a dict representing the properties of the container. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. @@ -285,10 +285,10 @@ def delete_container( response_hook(self.client_connection.last_response_headers, result) def get_container_client(self, container): - # type: (Union[str, Container, Dict[str, Any]]) -> Container - """ Get the specified `Container`, or a container with specified ID (name). + # type: (Union[str, ContainerClient, Dict[str, Any]]) -> ContainerClient + """ Get the specified `ContainerClient`, or a container with specified ID (name). - :param container: The ID (name) of the container, a :class:`Container` instance, + :param container: The ID (name) of the container, a :class:`ContainerClient` instance, or a dict representing the properties of the container to be retrieved. .. literalinclude:: ../../examples/examples.py @@ -300,14 +300,14 @@ def get_container_client(self, container): :name: get_container """ - if isinstance(container, Container): + if isinstance(container, ContainerClient): id_value = container.id elif isinstance(container, Mapping): id_value = container["id"] else: id_value = container - return Container(self.client_connection, self.database_link, id_value) + return ContainerClient(self.client_connection, self.database_link, id_value) @distributed_trace def read_all_containers( @@ -409,7 +409,7 @@ def query_containers( @distributed_trace def replace_container( self, - container, # type: Union[str, Container, Dict[str, Any]] + container, # type: Union[str, ContainerClient, Dict[str, Any]] partition_key, # type: PartitionKey indexing_policy=None, # type: Dict[str, Any] default_ttl=None, # type: int @@ -422,13 +422,13 @@ def replace_container( response_hook=None, # type: Optional[Callable] **kwargs ): - # type: (...) -> Container + # type: (...) -> ContainerClient """ Reset the properties of the container. Property changes are persisted immediately. Any properties not specified will be reset to their default values. :param container: The ID (name), dict representing the properties or - :class:`Container` instance of the container to be replaced. + :class:`ContainerClient` instance of the container to be replaced. :param partition_key: The partition key to use for the container. :param indexing_policy: The indexing policy to apply to the container. :param default_ttl: Default time to live (TTL) for items in the container. If unspecified, items do not expire. @@ -441,7 +441,7 @@ def replace_container( :param response_hook: a callable invoked with the response metadata :raise `HTTPFailure`: Raised if the container couldn't be replaced. This includes if the container with given id does not exist. - :returns: :class:`Container` instance representing the container after replace completed. + :returns: :class:`ContainerClient` instance representing the container after replace completed. .. literalinclude:: ../../examples/examples.py :start-after: [START reset_container_properties] @@ -484,7 +484,7 @@ def replace_container( if response_hook: response_hook(self.client_connection.last_response_headers, container_properties) - return Container( + return ContainerClient( self.client_connection, self.database_link, container_properties["id"], properties=container_properties ) @@ -540,38 +540,38 @@ def query_users(self, query, parameters=None, max_item_count=None, feed_options= return result def get_user_client(self, user): - # type: (Union[str, User, Dict[str, Any]]) -> User + # type: (Union[str, UserClient, Dict[str, Any]]) -> UserClient """ Get the user identified by `id`. - :param user: The ID (name), dict representing the properties or :class:`User` + :param user: The ID (name), dict representing the properties or :class:`UserClient` instance of the user to be retrieved. - :returns: A :class:`User` instance representing the retrieved user. + :returns: A :class:`UserClient` instance representing the retrieved user. :raise `HTTPFailure`: If the given user couldn't be retrieved. """ - if isinstance(user, User): + if isinstance(user, UserClient): id_value = user.id elif isinstance(user, Mapping): id_value = user["id"] else: id_value = user - return User(client_connection=self.client_connection, id=id_value, database_link=self.database_link) + return UserClient(client_connection=self.client_connection, id=id_value, database_link=self.database_link) @distributed_trace def create_user(self, body, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> User + # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> UserClient """ Create a user in the container. :param body: A dict-like object with an `id` key and value representing the user to be created. The user ID must be unique within the database, and consist of no more than 255 characters. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`User` instance representing the new user. + :returns: A :class:`UserClient` instance representing the new user. :raise `HTTPFailure`: If the given user couldn't be created. - To update or replace an existing user, use the :func:`Container.upsert_user` method. + To update or replace an existing user, use the :func:`ContainerClient.upsert_user` method. .. literalinclude:: ../../examples/examples.py :start-after: [START create_user] @@ -591,19 +591,19 @@ def create_user(self, body, request_options=None, response_hook=None, **kwargs): if response_hook: response_hook(self.client_connection.last_response_headers, user) - return User( + return UserClient( client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) @distributed_trace def upsert_user(self, body, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> User + # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> UserClient """ Insert or update the specified user. :param body: A dict-like object representing the user to update or insert. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`User` instance representing the upserted user. + :returns: A :class:`UserClient` instance representing the upserted user. :raise `HTTPFailure`: If the given user could not be upserted. If the user already exists in the container, it is replaced. If it does not, it is inserted. @@ -619,21 +619,21 @@ def upsert_user(self, body, request_options=None, response_hook=None, **kwargs): if response_hook: response_hook(self.client_connection.last_response_headers, user) - return User( + return UserClient( client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) @distributed_trace def replace_user(self, user, body, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, User, Dict[str, Any]], Dict[str, Any], Dict[str, Any], Optional[Callable]) -> User + # type: (Union[str, UserClient, Dict[str, Any]], Dict[str, Any], Dict[str, Any], Optional[Callable]) -> UserClient """ Replaces the specified user if it exists in the container. - :param user: The ID (name), dict representing the properties or :class:`User` + :param user: The ID (name), dict representing the properties or :class:`UserClient` instance of the user to be replaced. :param body: A dict-like object representing the user to replace. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`User` instance representing the user after replace went through. + :returns: A :class:`UserClient` instance representing the user after replace went through. :raise `HTTPFailure`: If the replace failed or the user with given id does not exist. """ @@ -647,16 +647,16 @@ def replace_user(self, user, body, request_options=None, response_hook=None, **k if response_hook: response_hook(self.client_connection.last_response_headers, user) - return User( + return UserClient( client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) @distributed_trace def delete_user(self, user, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, User, Dict[str, Any]], Dict[str, Any], Optional[Callable]) -> None + # type: (Union[str, UserClient, Dict[str, Any]], Dict[str, Any], Optional[Callable]) -> None """ Delete the specified user from the container. - :param user: The ID (name), dict representing the properties or :class:`User` + :param user: The ID (name), dict representing the properties or :class:`UserClient` instance of the user to be deleted. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py b/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py index b6ea40ee7f45..6dd0450a2434 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py @@ -23,7 +23,7 @@ """ -class HttpMethods: +class HttpMethods(object): """Constants of http methods. """ @@ -35,7 +35,7 @@ class HttpMethods: Options = "OPTIONS" -class HttpHeaders: +class HttpHeaders(object): """Constants of http headers. """ @@ -197,14 +197,14 @@ class HttpHeaders: AllowTentativeWrites = "x-ms-cosmos-allow-tentative-writes" -class HttpHeaderPreferenceTokens: +class HttpHeaderPreferenceTokens(object): """Constants of http header preference tokens. """ PreferUnfilteredQueryResponse = "PreferUnfilteredQueryResponse" -class HttpStatusDescriptions: +class HttpStatusDescriptions(object): """Constants of http status descriptions. """ @@ -234,7 +234,7 @@ class HttpStatusDescriptions: RetryWith = "Retry the request" -class QueryStrings: +class QueryStrings(object): """Constants of query strings. """ @@ -252,14 +252,14 @@ class QueryStrings: Generic = "generic" -class CookieHeaders: +class CookieHeaders(object): """Constants of cookie headers. """ SessionToken = "x-ms-session-token" -class Versions: +class Versions(object): """Constants of versions. """ @@ -268,7 +268,7 @@ class Versions: SDKVersion = "4.0.0a1" -class Delimiters: +class Delimiters(object): """Constants of delimiters. """ @@ -276,7 +276,7 @@ class Delimiters: ClientContinuationFormat = "{0}!!{1}" -class HttpListenerErrorCodes: +class HttpListenerErrorCodes(object): """Constants of http listener error codes. """ @@ -284,14 +284,14 @@ class HttpListenerErrorCodes: ERROR_CONNECTION_INVALID = 1229 -class HttpContextProperties: +class HttpContextProperties(object): """Constants of http context properties. """ SubscriptionId = "SubscriptionId" -class _ErrorCodes: +class _ErrorCodes(object): """Windows Socket Error Codes """ @@ -316,7 +316,7 @@ class _ErrorCodes: LinuxConnectionReset = 131 -class StatusCodes: +class StatusCodes(object): """HTTP status codes returned by the REST operations """ @@ -350,7 +350,7 @@ class StatusCodes: OPERATION_CANCELLED = 1201 -class SubStatusCodes: +class SubStatusCodes(object): """Sub status codes returned by the REST operations specifying the details of the operation """ @@ -385,7 +385,7 @@ class SubStatusCodes: INSUFFICIENT_BINDABLE_PARTITIONS = 1007 -class ResourceType: +class ResourceType(object): """Types of resources in Azure Cosmos """ diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py b/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py index 44a5a7ee2fc3..e2ca6eaa5d46 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py @@ -25,7 +25,7 @@ from .documents import PermissionMode -class Permission: +class Permission(object): def __init__(self, id, user_link, permission_mode, resource_link, properties): # pylint: disable=redefined-builtin # type: (str, str, PermissionMode, str, Dict[str, Any]) -> None self.id = id diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py index a0e889e8b8ae..66ac64282410 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py @@ -33,13 +33,13 @@ # pylint: disable=protected-access -class ScriptType: +class ScriptType(object): StoredProcedure = "sprocs" Trigger = "triggers" UserDefinedFunction = "udfs" -class Scripts: +class Scripts(object): def __init__(self, client_connection, container_link, is_system_key): # type: (CosmosClientConnection, str, bool) -> None self.client_connection = client_connection diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py similarity index 98% rename from sdk/cosmos/azure-cosmos/azure/cosmos/user.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py index b33a36fc81eb..445d13516803 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py @@ -31,7 +31,8 @@ from .permission import Permission -class User: +class UserClient(object): + def __init__(self, client_connection, id, database_link, properties=None): # pylint: disable=redefined-builtin # type: (CosmosClientConnection, str, str, Dict[str, Any]) -> None self.client_connection = client_connection @@ -57,13 +58,13 @@ def _get_properties(self): @distributed_trace def read(self, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Optional[Callable]) -> User + # type: (Dict[str, Any], Optional[Callable]) -> UserClient """ Read user propertes. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`User` instance representing the retrieved user. + :returns: A :class:`UserClient` instance representing the retrieved user. :raise `HTTPFailure`: If the given user couldn't be retrieved. """ @@ -181,7 +182,7 @@ def create_permission(self, body, request_options=None, response_hook=None, **kw :returns: A dict representing the new permission. :raise `HTTPFailure`: If the given permission couldn't be created. - To update or replace an existing permision, use the :func:`User.upsert_permission` method. + To update or replace an existing permision, use the :func:`UserClient.upsert_permission` method. """ if not request_options: From 85ee07d95d3c3ebf9b3b3b819310fe0491eee780 Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 30 Aug 2019 11:05:14 -0700 Subject: [PATCH 12/46] Updated with azure-core errors --- sdk/cosmos/azure-cosmos/README.md | 24 ++-- .../azure-cosmos/azure/cosmos/__init__.py | 4 +- .../azure/cosmos/_default_retry_policy.py | 2 +- .../_endpoint_discovery_retry_policy.py | 2 +- .../execution_dispatcher.py | 8 +- .../azure/cosmos/_global_endpoint_manager.py | 4 +- .../cosmos/_resource_throttle_retry_policy.py | 2 +- .../azure/cosmos/_retry_utility.py | 2 +- .../azure-cosmos/azure/cosmos/_session.py | 8 +- .../azure/cosmos/_session_retry_policy.py | 2 +- .../azure/cosmos/_synchronized_request.py | 16 ++- .../azure/cosmos/_vector_session_token.py | 18 ++- .../azure/cosmos/container_client.py | 34 +++-- .../azure/cosmos/cosmos_client.py | 4 +- .../azure/cosmos/database_client.py | 34 +++-- .../azure-cosmos/azure/cosmos/errors.py | 52 ++++--- .../cosmos/{scripts.py => scripts_client.py} | 28 ++-- .../azure-cosmos/azure/cosmos/user_client.py | 12 +- .../samples/ChangeFeedManagement/Program.py | 25 +--- .../samples/CollectionManagement/Program.py | 79 +++-------- .../samples/DatabaseManagement/Program.py | 23 +-- .../samples/DocumentManagement/Program.py | 23 +-- .../samples/IndexManagement/Program.py | 133 ++++++++---------- .../MultiMasterOperations/ConflictWorker.py | 81 ++++------- .../samples/MultiMasterOperations/Worker.py | 10 +- .../Program.py | 25 +--- .../azure-cosmos/test/aggregate_tests.py | 4 +- sdk/cosmos/azure-cosmos/test/conftest.py | 5 +- sdk/cosmos/azure-cosmos/test/crud_tests.py | 2 +- .../azure-cosmos/test/globaldb_mock_tests.py | 8 +- .../azure-cosmos/test/globaldb_tests.py | 8 +- .../azure-cosmos/test/location_cache_tests.py | 18 ++- .../azure-cosmos/test/retry_policy_tests.py | 24 ++-- sdk/cosmos/azure-cosmos/test/session_tests.py | 12 +- .../test/session_token_unit_tests.py | 6 +- .../test/streaming_failover_test.py | 13 +- sdk/cosmos/azure-cosmos/test/ttl_tests.py | 2 +- 37 files changed, 350 insertions(+), 407 deletions(-) rename sdk/cosmos/azure-cosmos/azure/cosmos/{scripts.py => scripts_client.py} (93%) diff --git a/sdk/cosmos/azure-cosmos/README.md b/sdk/cosmos/azure-cosmos/README.md index b3ca6d2ab67d..c2abbd6d2030 100644 --- a/sdk/cosmos/azure-cosmos/README.md +++ b/sdk/cosmos/azure-cosmos/README.md @@ -63,7 +63,7 @@ export ACCOUNT_KEY=$(az cosmosdb list-keys --resource-group $RES_GROUP --name $A Once you've populated the `ACCOUNT_URI` and `ACCOUNT_KEY` environment variables, you can create the [CosmosClient][ref_cosmosclient]. ```Python -from azure.cosmos import HTTPFailure, CosmosClient, Container, Database, PartitionKey +from azure.cosmos import CosmosClient, Container, Database, PartitionKey, errors import os url = os.environ['ACCOUNT_URI'] @@ -106,9 +106,7 @@ After authenticating your [CosmosClient][ref_cosmosclient], you can work with an database_name = 'testDatabase' try: database = client.create_database(database_name) -except HTTPFailure as e: - if e.status_code != 409: - raise +except errors.CosmosResourceExistsError: database = client.get_database_client(database_name) ``` @@ -120,13 +118,13 @@ This example creates a container with default settings. If a container with the container_name = 'products' try: container = database.create_container(id=container_name, partition_key=PartitionKey(path="/productName")) -except HTTPFailure as e: - if e.status_code != 409: - raise +except errors.CosmosResourceExistsError: container = database.get_container_client(container_name) +except errors.CosmosHttpResponseError: + raise ``` -The preceding snippet also handles the [HTTPFailure][ref_httpfailure] exception if the container creation failed. For more information on error handling and troubleshooting, see the [Troubleshooting](#troubleshooting) section. +The preceding snippet also handles the [CosmosHttpResponseError][ref_httpfailure] exception if the container creation failed. For more information on error handling and troubleshooting, see the [Troubleshooting](#troubleshooting) section. ### Get an existing container @@ -243,13 +241,11 @@ For example, if you try to create a container using an ID (name) that's already ```Python try: database.create_container(id=container_name, partition_key=PartitionKey(path="/productName") -except HTTPFailure as e: - if e.status_code == 409: - print("""Error creating container. +except errors.CosmosResourceExistsError: + print("""Error creating container. HTTP status code 409: The ID (name) provided for the container is already in use. The container name must be unique within the database.""") - else: - raise + ``` ## More sample code @@ -285,7 +281,7 @@ For more extensive documentation on the Cosmos DB service, see the [Azure Cosmos [ref_cosmosclient_create_database]: http://cosmosproto.westus.azurecontainer.io/#azure.cosmos.CosmosClient.create_database [ref_cosmosclient]: http://cosmosproto.westus.azurecontainer.io/#azure.cosmos.CosmosClient [ref_database]: http://cosmosproto.westus.azurecontainer.io/#azure.cosmos.Database -[ref_httpfailure]: https://docs.microsoft.com/python/api/azure-cosmos/azure.cosmos.errors.httpfailure +[ref_httpfailure]: https://docs.microsoft.com/python/api/azure-cosmos/azure.cosmos.errors.CosmosHttpResponseError [ref_item]: http://cosmosproto.westus.azurecontainer.io/#azure.cosmos.Item [sample_database_mgmt]: https://github.com/binderjoe/cosmos-python-prototype/blob/master/examples/databasemanagementsample.py [sample_document_mgmt]: https://github.com/binderjoe/cosmos-python-prototype/blob/master/examples/documentmanagementsample.py diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py index a2218a14dba9..37a04998b43c 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py @@ -36,7 +36,7 @@ ) from .partition_key import PartitionKey from .permission import Permission -from .scripts import Scripts +from .scripts_client import ScriptsClient from .version import VERSION __all__ = ( @@ -45,7 +45,7 @@ "Database", "PartitionKey", "Permission", - "Scripts", + "ScriptsClient", "User", "ConsistencyLevel", "DataType", diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py index 2e07955ab0ea..6b5e52769193 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_default_retry_policy.py @@ -66,7 +66,7 @@ def needsRetry(self, error_code): def ShouldRetry(self, exception): """Returns true if should retry based on the passed-in exception. - :param (errors.HTTPFailure instance) exception: + :param (errors.CosmosHttpResponseError instance) exception: :rtype: boolean diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_endpoint_discovery_retry_policy.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_endpoint_discovery_retry_policy.py index 90422376d450..2f773de8735a 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_endpoint_discovery_retry_policy.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_endpoint_discovery_retry_policy.py @@ -61,7 +61,7 @@ def __init__(self, connection_policy, global_endpoint_manager, *args): def ShouldRetry(self, exception): # pylint: disable=unused-argument """Returns true if should retry based on the passed-in exception. - :param (errors.HTTPFailure instance) exception: + :param (errors.CosmosHttpResponseError instance) exception: :rtype: boolean diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/execution_dispatcher.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/execution_dispatcher.py index b2c1752b40c3..2c37510a63a4 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/execution_dispatcher.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_execution_context/execution_dispatcher.py @@ -24,7 +24,7 @@ import json from six.moves import xrange -from azure.cosmos.errors import HTTPFailure +from azure.cosmos.errors import CosmosHttpResponseError from azure.cosmos._execution_context.base_execution_context import _QueryExecutionContextBase from azure.cosmos._execution_context.base_execution_context import _DefaultQueryExecutionContext from azure.cosmos._execution_context.query_execution_info import _PartitionedQueryExecutionInfo @@ -42,7 +42,7 @@ def _is_partitioned_execution_info(e): def _get_partitioned_execution_info(e): - error_msg = json.loads(e._http_error_message) + error_msg = json.loads(e.http_error_message) return _PartitionedQueryExecutionInfo(json.loads(error_msg["additionalErrorInfo"])) @@ -76,7 +76,7 @@ def next(self): """ try: return next(self._execution_context) - except HTTPFailure as e: + except CosmosHttpResponseError as e: if _is_partitioned_execution_info(e): query_execution_info = _get_partitioned_execution_info(e) self._execution_context = self._create_pipelined_execution_context(query_execution_info) @@ -97,7 +97,7 @@ def fetch_next_block(self): """ try: return self._execution_context.fetch_next_block() - except HTTPFailure as e: + except CosmosHttpResponseError as e: if _is_partitioned_execution_info(e): query_execution_info = _get_partitioned_execution_info(e) self._execution_context = self._create_pipelined_execution_context(query_execution_info) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_global_endpoint_manager.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_global_endpoint_manager.py index b4bb6cda0703..d4dc37ee7533 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_global_endpoint_manager.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_global_endpoint_manager.py @@ -126,13 +126,13 @@ def _GetDatabaseAccount(self): # specified (by creating a locational endpoint) and keeping eating the exception # until we get the database account and return None at the end, if we are not able # to get that info from any endpoints - except errors.HTTPFailure: + except errors.CosmosHttpResponseError: for location_name in self.PreferredLocations: locational_endpoint = _GlobalEndpointManager.GetLocationalEndpoint(self.DefaultEndpoint, location_name) try: database_account = self._GetDatabaseAccountStub(locational_endpoint) return database_account - except errors.HTTPFailure: + except errors.CosmosHttpResponseError: pass return None diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_resource_throttle_retry_policy.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_resource_throttle_retry_policy.py index 8e027e0fcc2e..e21454ec7792 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_resource_throttle_retry_policy.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_resource_throttle_retry_policy.py @@ -36,7 +36,7 @@ def __init__(self, max_retry_attempt_count, fixed_retry_interval_in_milliseconds def ShouldRetry(self, exception): """Returns true if should retry based on the passed-in exception. - :param (errors.HTTPFailure instance) exception: + :param (errors.CosmosHttpResponseError instance) exception: :rtype: boolean diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_retry_utility.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_retry_utility.py index e787857de9e8..df575cb27d36 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_retry_utility.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_retry_utility.py @@ -80,7 +80,7 @@ def Execute(client, global_endpoint_manager, function, *args, **kwargs): ] = resourceThrottle_retry_policy.cummulative_wait_time_in_milliseconds return result - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: retry_policy = None if e.status_code == StatusCodes.FORBIDDEN and e.sub_status == SubStatusCodes.WRITE_FORBIDDEN: retry_policy = endpointDiscovery_retry_policy diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py index 077e56a397a4..dd1a573eaa4a 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_session.py @@ -29,7 +29,7 @@ from . import _base from . import http_constants from ._vector_session_token import VectorSessionToken -from .errors import HTTPFailure +from .errors import CosmosHttpResponseError class SessionContainer(object): @@ -196,9 +196,9 @@ def parse_session_token(response_headers): id_ = tokens[0] sessionToken = VectorSessionToken.create(tokens[1]) if sessionToken is None: - raise HTTPFailure( - http_constants.StatusCodes.INTERNAL_SERVER_ERROR, - "Could not parse the received session token: %s" % tokens[1], + raise CosmosHttpResponseError( + status_code=http_constants.StatusCodes.INTERNAL_SERVER_ERROR, + message="Could not parse the received session token: %s" % tokens[1], ) id_to_sessionlsn[id_] = sessionToken return id_to_sessionlsn diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_session_retry_policy.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_session_retry_policy.py index 9f2c14be6a1f..01ae7778a7f4 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_session_retry_policy.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_session_retry_policy.py @@ -62,7 +62,7 @@ def __init__(self, endpoint_discovery_enable, global_endpoint_manager, *args): def ShouldRetry(self, _exception): """Returns true if should retry based on the passed-in exception. - :param (errors.HTTPFailure instance) exception: + :param (errors.CosmosHttpResponseError instance) exception: :rtype: boolean diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index 618541feb6b0..5368de448b2e 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -26,6 +26,7 @@ from six.moves.urllib.parse import urlparse, urlencode import six +from azure.core.exceptions import DecodeError from . import documents from . import errors @@ -149,8 +150,14 @@ def _Request(global_endpoint_manager, request_params, connection_policy, pipelin # python 3 compatible: convert data from byte to unicode string data = data.decode("utf-8") + if response.status_code == 404: + raise errors.CosmosResourceNotFoundError(message=data, response=response) + if response.status_code == 409: + raise errors.CosmosResourceExistsError(message=data, response=response) + if response.status_code == 412: + raise errors.CosmosResourceModifiedError(message=data, response=response) if response.status_code >= 400: - raise errors.HTTPFailure(response.status_code, data, headers) + raise errors.CosmosHttpResponseError(message=data, response=response) result = None if is_media: @@ -159,8 +166,11 @@ def _Request(global_endpoint_manager, request_params, connection_policy, pipelin if data: try: result = json.loads(data) - except: - raise errors.JSONParseFailure(data) + except Exception as e: + raise DecodeError( + message="Failed to decode JSON data: {}".format(e), + response=response, + error=e) return (result, headers) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_vector_session_token.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_vector_session_token.py index 1c6832f31d3c..675f0801632d 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_vector_session_token.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_vector_session_token.py @@ -120,11 +120,10 @@ def merge(self, other): raise ValueError("Invalid Session Token (should not be None)") if self.version == other.version and len(self.local_lsn_by_region) != len(other.local_lsn_by_region): - raise errors.CosmosError( - Exception( - "Status Code: %s. Compared session tokens '%s' and '%s' have unexpected regions." - % (StatusCodes.INTERNAL_SERVER_ERROR, self.session_token, other.session_token) - ) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.INTERNAL_SERVER_ERROR, + message=("Compared session tokens '%s' and '%s' have unexpected regions." + % (self.session_token, other.session_token)) ) if self.version < other.version: @@ -148,11 +147,10 @@ def merge(self, other): if local_lsn2 is not None: highest_local_lsn_by_region[region_id] = max(local_lsn1, local_lsn2) elif self.version == other.version: - raise errors.CosmosError( - Exception( - "Status Code: %s. Compared session tokens '%s' and '%s' have unexpected regions." - % (StatusCodes.INTERNAL_SERVER_ERROR, self.session_token, other.session_token) - ) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.INTERNAL_SERVER_ERROR, + message=("Compared session tokens '%s' and '%s' have unexpected regions." + % (self.session_token, other.session_token)) ) else: highest_local_lsn_by_region[region_id] = local_lsn1 diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index 2bea301cc6a7..c4f6ff8a9eb7 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -28,10 +28,10 @@ from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection -from .errors import HTTPFailure +from .errors import CosmosResourceNotFoundError from .http_constants import StatusCodes from .offer import Offer -from .scripts import Scripts +from .scripts_client import ScriptsClient from ._query_iterable import QueryIterable from .partition_key import NonePartitionKeyValue @@ -82,7 +82,7 @@ def is_system_key(self): @property def scripts(self): if self._scripts is None: - self._scripts = Scripts(self.client_connection, self.container_link, self.is_system_key) + self._scripts = ScriptsClient(self.client_connection, self.container_link, self.is_system_key) return self._scripts def _get_document_link(self, item_or_link): @@ -120,7 +120,7 @@ def read( :param populate_quota_info: Enable returning collection storage quota information in response headers. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raise `HTTPFailure`: Raised if the container couldn't be retrieved. This includes + :raise `CosmosHttpResponseError`: Raised if the container couldn't be retrieved. This includes if the container does not exist. :returns: :class:`Container` instance representing the retrieved container. @@ -172,7 +172,7 @@ def read_item( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: Dict representing the item to be retrieved. - :raise `HTTPFailure`: If the given item couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given item couldn't be retrieved. .. literalinclude:: ../../examples/examples.py :start-after: [START update_item] @@ -409,7 +409,7 @@ def replace_item( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the item after replace went through. - :raise `HTTPFailure`: If the replace failed or the item with given id does not exist. + :raise `CosmosHttpResponseError`: If the replace failed or the item with given id does not exist. """ item_link = self._get_document_link(item) @@ -463,7 +463,7 @@ def upsert_item( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the upserted item. - :raise `HTTPFailure`: If the given item could not be upserted. + :raise `CosmosHttpResponseError`: If the given item could not be upserted. If the item already exists in the container, it is replaced. If it does not, it is inserted. @@ -519,7 +519,7 @@ def create_item( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the new item. - :raises `HTTPFailure`: If item with the given ID already exists. + :raises `CosmosHttpResponseError`: If item with the given ID already exists. To update or replace an existing item, use the :func:`Container.upsert_item` method. @@ -578,7 +578,7 @@ def delete_item( :param post_trigger_include: trigger id to be used as post operation trigger. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raises `HTTPFailure`: The item wasn't deleted successfully. If the item does not + :raises `CosmosHttpResponseError`: The item wasn't deleted successfully. If the item does not exist in the container, a `404` error is returned. """ @@ -611,7 +611,7 @@ def read_offer(self, response_hook=None, **kwargs): :param response_hook: a callable invoked with the response metadata :returns: Offer for the container. - :raise HTTPFailure: If no offer exists for the container or if the offer could not be retrieved. + :raise CosmosHttpResponseError: If no offer exists for the container or if the offer could not be retrieved. """ properties = self._get_properties() @@ -622,7 +622,9 @@ def read_offer(self, response_hook=None, **kwargs): } offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: - raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for container " + self.container_link) + raise CosmosResourceNotFoundError( + status_code=StatusCodes.NOT_FOUND, + message="Could not find Offer for container " + self.container_link) if response_hook: response_hook(self.client_connection.last_response_headers, offers) @@ -637,7 +639,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): :param throughput: The throughput to be set (an integer). :param response_hook: a callable invoked with the response metadata :returns: Offer for the container, updated with new throughput. - :raise HTTPFailure: If no offer exists for the container or if the offer could not be updated. + :raise CosmosHttpResponseError: If no offer exists for the container or if the offer could not be updated. """ properties = self._get_properties() @@ -648,7 +650,9 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): } offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: - raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for container " + self.container_link) + raise CosmosResourceNotFoundError( + status=StatusCodes.NOT_FOUND, + message="Could not find Offer for container " + self.container_link) new_offer = offers[0].copy() new_offer["content"]["offerThroughput"] = throughput data = self.client_connection.ReplaceOffer(offer_link=offers[0]["_self"], offer=offers[0], **kwargs) @@ -737,7 +741,7 @@ def get_conflict(self, conflict, partition_key, request_options=None, response_h :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the retrieved conflict. - :raise `HTTPFailure`: If the given conflict couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given conflict couldn't be retrieved. """ if not request_options: @@ -761,7 +765,7 @@ def delete_conflict(self, conflict, partition_key, request_options=None, respons :param partition_key: Partition key for the conflict to delete. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raises `HTTPFailure`: The conflict wasn't deleted successfully. If the conflict + :raises `CosmosHttpResponseError`: The conflict wasn't deleted successfully. If the conflict does not exist in the container, a `404` error is returned. """ diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 78f794661f8c..13babf01e771 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -106,7 +106,7 @@ def create_database( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A :class:`DatabaseClient` instance representing the new database. - :raises `HTTPFailure`: If database with the given ID already exists. + :raises `CosmosHttpResponseError`: If database with the given ID already exists. .. literalinclude:: ../../examples/examples.py :start-after: [START create_database] @@ -278,7 +278,7 @@ def delete_database( :param populate_query_metrics: Enable returning query metrics in response headers. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raise HTTPFailure: If the database couldn't be deleted. + :raise CosmosHttpResponseError: If the database couldn't be deleted. """ if not request_options: diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index 0e45cfbb26d7..55d52f966c5a 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -31,7 +31,7 @@ from .container_client import ContainerClient from .offer import Offer from .http_constants import StatusCodes -from .errors import HTTPFailure +from .errors import CosmosResourceNotFoundError from .user_client import UserClient from ._query_iterable import QueryIterable @@ -126,7 +126,7 @@ def read( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: Dict[Str, Any] - :raise `HTTPFailure`: If the given database couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given database couldn't be retrieved. """ # TODO this helper function should be extracted from CosmosClient @@ -171,7 +171,7 @@ def create_container( """ Create a new container with the given ID (name). - If a container with the given ID already exists, an HTTPFailure with status_code 409 is raised. + If a container with the given ID already exists, a CosmosResourceExistsError is raised. :param id: ID (name) of container to create. :param partition_key: The partition key to use for the container. @@ -187,7 +187,7 @@ def create_container( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A :class:`ContainerClient` instance representing the new container. - :raise HTTPFailure: The container creation failed. + :raise CosmosHttpResponseError: The container creation failed. .. literalinclude:: ../../examples/examples.py @@ -265,7 +265,7 @@ def delete_container( :param populate_query_metrics: Enable returning query metrics in response headers. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raise HTTPFailure: If the container couldn't be deleted. + :raise CosmosHttpResponseError: If the container couldn't be deleted. """ if not request_options: @@ -439,7 +439,7 @@ def replace_container( :param populate_query_metrics: Enable returning query metrics in response headers. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raise `HTTPFailure`: Raised if the container couldn't be replaced. This includes + :raise `CosmosHttpResponseError`: Raised if the container couldn't be replaced. This includes if the container with given id does not exist. :returns: :class:`ContainerClient` instance representing the container after replace completed. @@ -547,7 +547,7 @@ def get_user_client(self, user): :param user: The ID (name), dict representing the properties or :class:`UserClient` instance of the user to be retrieved. :returns: A :class:`UserClient` instance representing the retrieved user. - :raise `HTTPFailure`: If the given user couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given user couldn't be retrieved. """ if isinstance(user, UserClient): @@ -569,7 +569,7 @@ def create_user(self, body, request_options=None, response_hook=None, **kwargs): :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A :class:`UserClient` instance representing the new user. - :raise `HTTPFailure`: If the given user couldn't be created. + :raise `CosmosHttpResponseError`: If the given user couldn't be created. To update or replace an existing user, use the :func:`ContainerClient.upsert_user` method. @@ -604,7 +604,7 @@ def upsert_user(self, body, request_options=None, response_hook=None, **kwargs): :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A :class:`UserClient` instance representing the upserted user. - :raise `HTTPFailure`: If the given user could not be upserted. + :raise `CosmosHttpResponseError`: If the given user could not be upserted. If the user already exists in the container, it is replaced. If it does not, it is inserted. @@ -634,7 +634,7 @@ def replace_user(self, user, body, request_options=None, response_hook=None, **k :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A :class:`UserClient` instance representing the user after replace went through. - :raise `HTTPFailure`: If the replace failed or the user with given id does not exist. + :raise `CosmosHttpResponseError`: If the replace failed or the user with given id does not exist. """ if not request_options: @@ -660,7 +660,7 @@ def delete_user(self, user, request_options=None, response_hook=None, **kwargs): instance of the user to be deleted. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raises `HTTPFailure`: The user wasn't deleted successfully. If the user does not + :raises `CosmosHttpResponseError`: The user wasn't deleted successfully. If the user does not exist in the container, a `404` error is returned. """ @@ -680,7 +680,7 @@ def read_offer(self, response_hook=None, **kwargs): :param response_hook: a callable invoked with the response metadata :returns: Offer for the database. - :raise HTTPFailure: If no offer exists for the database or if the offer could not be retrieved. + :raise CosmosHttpResponseError: If no offer exists for the database or if the offer could not be retrieved. """ properties = self._get_properties() @@ -691,7 +691,9 @@ def read_offer(self, response_hook=None, **kwargs): } offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: - raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for database " + self.database_link) + raise CosmosResourceNotFoundError( + status=StatusCodes.NOT_FOUND, + message="Could not find Offer for database " + self.database_link) if response_hook: response_hook(self.client_connection.last_response_headers, offers) @@ -706,7 +708,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): :param throughput: The throughput to be set (an integer). :param response_hook: a callable invoked with the response metadata :returns: Offer for the database, updated with new throughput. - :raise HTTPFailure: If no offer exists for the database or if the offer could not be updated. + :raise CosmosHttpResponseError: If no offer exists for the database or if the offer could not be updated. """ properties = self._get_properties() @@ -717,7 +719,9 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): } offers = list(self.client_connection.QueryOffers(query_spec)) if not offers: - raise HTTPFailure(StatusCodes.NOT_FOUND, "Could not find Offer for collection " + self.database_link) + raise CosmosResourceNotFoundError( + status=StatusCodes.NOT_FOUND, + message="Could not find Offer for collection " + self.database_link) new_offer = offers[0].copy() new_offer["content"]["offerThroughput"] = throughput data = self.client_connection.ReplaceOffer(offer_link=offers[0]["_self"], offer=offers[0], **kwargs) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py index 181136eb93f3..f1b1e3b30418 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py @@ -21,45 +21,51 @@ """PyCosmos Exceptions in the Azure Cosmos database service. """ +from azure.core.exceptions import ( + AzureError, + HttpResponseError, + ResourceExistsError, + ResourceNotFoundError, + ResourceModifiedError +) from . import http_constants -class CosmosError(Exception): - """Base class for all Azure Cosmos errors. - """ - - -class HTTPFailure(CosmosError): +class CosmosHttpResponseError(HttpResponseError): """Raised when a HTTP request to the Azure Cosmos has failed. """ - def __init__(self, status_code, message="", headers=None): + def __init__(self, status_code=None, message=None, response=None, **kwargs): """ :param int status_code: :param str message: """ - if headers is None: - headers = {} - - self.status_code = status_code - self.headers = headers + self.headers = response.headers if response else {} self.sub_status = None - self._http_error_message = message + self.http_error_message = message + status = status_code or (int(response.status_code) if response else 0) + if not status: + raise Exception("Got no status: {}".format(status_code)) + if http_constants.HttpHeaders.SubStatus in self.headers: self.sub_status = int(self.headers[http_constants.HttpHeaders.SubStatus]) - CosmosError.__init__( - self, "Status code: %d Sub-status: %d\n%s" % (self.status_code, self.sub_status, message) - ) + formatted_message = "Status code: %d Sub-status: %d\n%s" % (status, self.sub_status, str(message)) else: - CosmosError.__init__(self, "Status code: %d\n%s" % (self.status_code, message)) + + formatted_message = "Status code: %d\n%s" % (status, str(message)) + super(CosmosHttpResponseError, self).__init__(message=formatted_message, response=response, **kwargs) + self.status_code = status -class JSONParseFailure(CosmosError): - """Raised when fails to parse JSON message. - """ +class CosmosResourceNotFoundError(ResourceNotFoundError, CosmosHttpResponseError): + """An error response with status code 404.""" -class UnexpectedDataType(CosmosError): - """Raised when unexpected data type is provided as parameter. - """ + +class CosmosResourceExistsError(ResourceExistsError, CosmosHttpResponseError): + """An error response with status code 409.""" + + +class CosmosResourceModifiedError(ResourceModifiedError, CosmosHttpResponseError): + """An error response with status code 412.""" diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py similarity index 93% rename from sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py index 66ac64282410..e0f9665d5133 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py @@ -39,7 +39,7 @@ class ScriptType(object): UserDefinedFunction = "udfs" -class Scripts(object): +class ScriptsClient(object): def __init__(self, client_connection, container_link, is_system_key): # type: (CosmosClientConnection, str, bool) -> None self.client_connection = client_connection @@ -98,7 +98,7 @@ def get_stored_procedure(self, sproc, request_options=None): :param sproc: The ID (name) or dict representing stored procedure to retrieve. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the retrieved stored procedure. - :raise `HTTPFailure`: If the given stored procedure couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given stored procedure couldn't be retrieved. """ if not request_options: @@ -115,7 +115,7 @@ def create_stored_procedure(self, body, request_options=None): :param body: A dict-like object representing the sproc to create. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the new stored procedure. - :raise `HTTPFailure`: If the given stored procedure couldn't be created. + :raise `CosmosHttpResponseError`: If the given stored procedure couldn't be created. To replace an existing sproc, use the :func:`Container.scripts.replace_stored_procedure` method. @@ -135,7 +135,7 @@ def replace_stored_procedure(self, sproc, body, request_options=None): :param body: A dict-like object representing the sproc to replace. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the stored procedure after replace went through. - :raise `HTTPFailure`: If the replace failed or the stored procedure with given id does not exist. + :raise `CosmosHttpResponseError`: If the replace failed or the stored procedure with given id does not exist. """ if not request_options: @@ -151,7 +151,7 @@ def delete_stored_procedure(self, sproc, request_options=None): :param sproc: The ID (name) or dict representing stored procedure to be deleted. :param request_options: Dictionary of additional properties to be used for the request. - :raises `HTTPFailure`: The sproc wasn't deleted successfully. If the sproc does not + :raises `CosmosHttpResponseError`: The sproc wasn't deleted successfully. If the sproc does not exist in the container, a `404` error is returned. """ @@ -174,7 +174,7 @@ def execute_stored_procedure( :param partition_key: Specifies the partition key to indicate which partition the sproc should execute on. :param request_options: Dictionary of additional properties to be used for the request. :returns: result of the executed stored procedure for the given parameters. - :raise `HTTPFailure`: If the stored procedure execution failed or if the stored procedure with + :raise `CosmosHttpResponseError`: If the stored procedure execution failed or if the stored procedure with given id does not exists in the container. """ @@ -242,7 +242,7 @@ def get_trigger(self, trigger, request_options=None): :param trigger: The ID (name) or dict representing trigger to retrieve. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the retrieved trigger. - :raise `HTTPFailure`: If the given trigger couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given trigger couldn't be retrieved. """ if not request_options: @@ -259,7 +259,7 @@ def create_trigger(self, body, request_options=None): :param body: A dict-like object representing the trigger to create. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the new trigger. - :raise `HTTPFailure`: If the given trigger couldn't be created. + :raise `CosmosHttpResponseError`: If the given trigger couldn't be created. To replace an existing trigger, use the :func:`Container.scripts.replace_trigger` method. @@ -279,7 +279,7 @@ def replace_trigger(self, trigger, body, request_options=None): :param body: A dict-like object representing the trigger to replace. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the trigger after replace went through. - :raise `HTTPFailure`: If the replace failed or the trigger with given id does not exist. + :raise `CosmosHttpResponseError`: If the replace failed or the trigger with given id does not exist. """ if not request_options: @@ -295,7 +295,7 @@ def delete_trigger(self, trigger, request_options=None): :param trigger: The ID (name) or dict representing trigger to be deleted. :param request_options: Dictionary of additional properties to be used for the request. - :raises `HTTPFailure`: The trigger wasn't deleted successfully. If the trigger does not + :raises `CosmosHttpResponseError`: The trigger wasn't deleted successfully. If the trigger does not exist in the container, a `404` error is returned. """ @@ -354,7 +354,7 @@ def get_user_defined_function(self, udf, request_options=None): :param udf: The ID (name) or dict representing udf to retrieve. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the retrieved user defined function. - :raise `HTTPFailure`: If the given user defined function couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given user defined function couldn't be retrieved. """ if not request_options: @@ -371,7 +371,7 @@ def create_user_defined_function(self, body, request_options=None): :param body: A dict-like object representing the udf to create. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the new user defined function. - :raise `HTTPFailure`: If the given user defined function couldn't be created. + :raise `CosmosHttpResponseError`: If the given user defined function couldn't be created. To replace an existing udf, use the :func:`Container.scripts.replace_user_defined_function` method. @@ -391,7 +391,7 @@ def replace_user_defined_function(self, udf, body, request_options=None): :param body: A dict-like object representing the udf to replace. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the user defined function after replace went through. - :raise `HTTPFailure`: If the replace failed or the user defined function with given id does not exist. + :raise `CosmosHttpResponseError`: If the replace failed or the user defined function with given id does not exist. """ if not request_options: @@ -407,7 +407,7 @@ def delete_user_defined_function(self, udf, request_options=None): :param udf: The ID (name) or dict representing udf to be deleted. :param request_options: Dictionary of additional properties to be used for the request. - :raises `HTTPFailure`: The udf wasn't deleted successfully. If the udf does not + :raises `CosmosHttpResponseError`: The udf wasn't deleted successfully. If the udf does not exist in the container, a `404` error is returned. """ diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py index 445d13516803..6a5feabb2775 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py @@ -65,7 +65,7 @@ def read(self, request_options=None, response_hook=None, **kwargs): :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A :class:`UserClient` instance representing the retrieved user. - :raise `HTTPFailure`: If the given user couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given user couldn't be retrieved. """ if not request_options: @@ -150,7 +150,7 @@ def get_permission(self, permission, request_options=None, response_hook=None, * :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the retrieved permission. - :raise `HTTPFailure`: If the given permission couldn't be retrieved. + :raise `CosmosHttpResponseError`: If the given permission couldn't be retrieved. """ if not request_options: @@ -180,7 +180,7 @@ def create_permission(self, body, request_options=None, response_hook=None, **kw :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the new permission. - :raise `HTTPFailure`: If the given permission couldn't be created. + :raise `CosmosHttpResponseError`: If the given permission couldn't be created. To update or replace an existing permision, use the :func:`UserClient.upsert_permission` method. @@ -212,7 +212,7 @@ def upsert_permission(self, body, request_options=None, response_hook=None, **kw :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the upserted permission. - :raise `HTTPFailure`: If the given permission could not be upserted. + :raise `CosmosHttpResponseError`: If the given permission could not be upserted. If the permission already exists in the container, it is replaced. If it does not, it is inserted. """ @@ -246,7 +246,7 @@ def replace_permission(self, permission, body, request_options=None, response_ho :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the permission after replace went through. - :raise `HTTPFailure`: If the replace failed or the permission with given id does not exist. + :raise `CosmosHttpResponseError`: If the replace failed or the permission with given id does not exist. """ if not request_options: @@ -276,7 +276,7 @@ def delete_permission(self, permission, request_options=None, response_hook=None instance of the permission to be replaced. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :raises `HTTPFailure`: The permission wasn't deleted successfully. If the permission does + :raises `CosmosHttpResponseError`: The permission wasn't deleted successfully. If the permission does not exist for the user, a `404` error is returned. """ diff --git a/sdk/cosmos/azure-cosmos/samples/ChangeFeedManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/ChangeFeedManagement/Program.py index b800f818104e..1056c146bdbd 100644 --- a/sdk/cosmos/azure-cosmos/samples/ChangeFeedManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/ChangeFeedManagement/Program.py @@ -74,12 +74,8 @@ def run_sample(): # setup database for this sample try: db = client.create_database(id=DATABASE_ID) - - except errors.HTTPFailure as e: - if e.status_code == 409: - pass - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + pass # setup container for this sample try: @@ -89,11 +85,8 @@ def run_sample(): ) print('Container with id \'{0}\' created'.format(CONTAINER_ID)) - except errors.HTTPFailure as e: - if e.status_code == 409: - print('Container with id \'{0}\' was found'.format(CONTAINER_ID)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('Container with id \'{0}\' was found'.format(CONTAINER_ID)) ChangeFeedManagement.CreateItems(container, 100) ChangeFeedManagement.ReadChangeFeed(container) @@ -101,14 +94,10 @@ def run_sample(): # cleanup database after sample try: client.delete_database(db) + except errors.CosmosResourceNotFoundError: + pass - except errors.CosmosError as e: - if e.status_code == 404: - pass - else: - raise errors.HTTPFailure(e.status_code) - - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: print('\nrun_sample has caught an error. {0}'.format(e.message)) finally: diff --git a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py index 9bad40d4b0df..0217f2afe900 100644 --- a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py @@ -92,11 +92,8 @@ def create_Container(db, id): db.create_container(id=id, partition_key=partition_key) print('Container with id \'{0}\' created'.format(id)) - except errors.HTTPFailure as e: - if e.status_code == 409: - print('A container with id \'{0}\' already exists'.format(id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A container with id \'{0}\' already exists'.format(id)) print("\n2.2 Create Container - With custom index policy") @@ -118,11 +115,8 @@ def create_Container(db, id): print('IndexPolicy Mode - \'{0}\''.format(container.properties['indexingPolicy']['indexingMode'])) print('IndexPolicy Automatic - \'{0}\''.format(container.properties['indexingPolicy']['automatic'])) - except errors.CosmosError as e: - if e.status_code == 409: - print('A container with id \'{0}\' already exists'.format(container['id'])) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A container with id \'{0}\' already exists'.format(container['id'])) print("\n2.3 Create Container - With custom offer throughput") @@ -135,11 +129,8 @@ def create_Container(db, id): ) print('Container with id \'{0}\' created'.format(container.id)) - except errors.HTTPFailure as e: - if e.status_code == 409: - print('A container with id \'{0}\' already exists'.format(container.id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A container with id \'{0}\' already exists'.format(container.id)) print("\n2.4 Create Container - With Unique keys") @@ -153,11 +144,8 @@ def create_Container(db, id): print('Container with id \'{0}\' created'.format(container.id)) print('Unique Key Paths - \'{0}\', \'{1}\''.format(unique_key_paths[0], unique_key_paths[1])) - except errors.HTTPFailure as e: - if e.status_code == 409: - print('A container with id \'{0}\' already exists'.format(container.id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A container with id \'{0}\' already exists'.format(container.id)) print("\n2.5 Create Collection - With Partition key V2 (Default)") @@ -170,11 +158,8 @@ def create_Container(db, id): print('Container with id \'{0}\' created'.format(container.id)) print('Partition Key - \'{0}\''.format(container.properties['partitionKey'])) - except errors.CosmosError as e: - if e.status_code == 409: - print('A container with id \'{0}\' already exists'.format(container.id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A container with id \'{0}\' already exists'.format(container.id)) print("\n2.6 Create Collection - With Partition key V1") @@ -187,11 +172,8 @@ def create_Container(db, id): print('Container with id \'{0}\' created'.format(container.id)) print('Partition Key - \'{0}\''.format(container.properties['partitionKey'])) - except errors.CosmosError as e: - if e.status_code == 409: - print('A container with id \'{0}\' already exists'.format(container.id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A container with id \'{0}\' already exists'.format(container.id)) @staticmethod def manage_offer_throughput(db, id): @@ -211,11 +193,8 @@ def manage_offer_throughput(db, id): print('Found Offer \'{0}\' for Container \'{1}\' and its throughput is \'{2}\''.format(offer.properties['id'], container.id, offer.properties['content']['offerThroughput'])) - except errors.HTTPFailure as e: - if e.status_code == 404: - print('A container with id \'{0}\' does not exist'.format(id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A container with id \'{0}\' does not exist'.format(id)) print("\n3.2 Change Offer Throughput of Container") @@ -233,11 +212,8 @@ def read_Container(db, id): container = db.get_container_client(id) print('Container with id \'{0}\' was found, it\'s link is {1}'.format(container.id, container.container_link)) - except errors.HTTPFailure as e: - if e.status_code == 404: - print('A container with id \'{0}\' does not exist'.format(id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceNotFoundError: + print('A container with id \'{0}\' does not exist'.format(id)) @staticmethod def list_Containers(db): @@ -262,11 +238,8 @@ def delete_Container(db, id): print('Container with id \'{0}\' was deleted'.format(id)) - except errors.HTTPFailure as e: - if e.status_code == 404: - print('A container with id \'{0}\' does not exist'.format(id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceNotFoundError: + print('A container with id \'{0}\' does not exist'.format(id)) def run_sample(): @@ -276,11 +249,8 @@ def run_sample(): try: db = client.create_database(id=DATABASE_ID) - except errors.HTTPFailure as e: - if e.status_code == 409: - pass - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + pass # query for a container ContainerManagement.find_container(db, CONTAINER_ID) @@ -304,13 +274,10 @@ def run_sample(): try: client.delete_database(db) - except errors.CosmosError as e: - if e.status_code == 404: - pass - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceNotFoundError: + pass - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: print('\nrun_sample has caught an error. {0}'.format(e.message)) finally: diff --git a/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py index f424c98ffa3e..c01013ce73aa 100644 --- a/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py @@ -68,11 +68,8 @@ def create_database(client, id): client.create_database(id=id) print('Database with id \'{0}\' created'.format(id)) - except errors.HTTPFailure as e: - if e.status_code == 409: - print('A database with id \'{0}\' already exists'.format(id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('A database with id \'{0}\' already exists'.format(id)) @staticmethod def read_database(client, id): @@ -82,11 +79,8 @@ def read_database(client, id): database = client.get_database_client(id) print('Database with id \'{0}\' was found, it\'s link is {1}'.format(id, database.database_link)) - except errors.HTTPFailure as e: - if e.status_code == 404: - print('A database with id \'{0}\' does not exist'.format(id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceNotFoundError: + print('A database with id \'{0}\' does not exist'.format(id)) @staticmethod def list_databases(client): @@ -111,11 +105,8 @@ def delete_database(client, id): print('Database with id \'{0}\' was deleted'.format(id)) - except errors.HTTPFailure as e: - if e.status_code == 404: - print('A database with id \'{0}\' does not exist'.format(id)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceNotFoundError: + print('A database with id \'{0}\' does not exist'.format(id)) def run_sample(): with IDisposable(cosmos_client.CosmosClient(HOST, {'masterKey': MASTER_KEY} )) as client: @@ -135,7 +126,7 @@ def run_sample(): # delete database by id DatabaseManagement.delete_database(client, DATABASE_ID) - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: print('\nrun_sample has caught an error. {0}'.format(e.message)) finally: diff --git a/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py index 51754fe8dbf9..31f2953f3dae 100644 --- a/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py @@ -179,22 +179,16 @@ def run_sample(): try: db = client.create_database(id=DATABASE_ID) - except errors.HTTPFailure as e: - if e.status_code == 409: - pass - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + pass # setup container for this sample try: container = db.create_container(id=CONTAINER_ID, partition_key=PartitionKey(path='/id', kind='Hash')) print('Container with id \'{0}\' created'.format(CONTAINER_ID)) - except errors.HTTPFailure as e: - if e.status_code == 409: - print('Container with id \'{0}\' was found'.format(CONTAINER_ID)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('Container with id \'{0}\' was found'.format(CONTAINER_ID)) ItemManagement.CreateItems(container) ItemManagement.ReadItem(container, 'SalesOrder1') @@ -208,13 +202,10 @@ def run_sample(): try: client.delete_database(db) - except errors.CosmosError as e: - if e.status_code == 404: - pass - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceNotFoundError: + pass - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: print('\nrun_sample has caught an error. {0}'.format(e.message)) finally: diff --git a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py index b50b5c684a96..776be548d214 100644 --- a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py @@ -44,6 +44,7 @@ def ObtainClient(): return cosmos_client.CosmosClient(HOST, {'masterKey': MASTER_KEY}, "Session", connection_policy) + # Query for Entity / Entities def Query_Entities(parent, entity_type, id = None): find_entity_by_id_query = { @@ -71,7 +72,7 @@ def Query_Entities(parent, entity_type, id = None): entities = list(parent.read_all_items()) else: entities = list(parent.query_items(find_entity_by_id_query)) - except errors.CosmosError as e: + except errors.AzureError as e: print("The following error occured while querying for the entity / entities ", entity_type, id if id != None else "") print(e) raise @@ -81,36 +82,35 @@ def Query_Entities(parent, entity_type, id = None): return entities[0] return None + def CreateDatabaseIfNotExists(client, database_id): try: database = Query_Entities(client, 'database', id = database_id) if database == None: database = client.create_database(id=database_id) return client.get_database_client(database['id']) - except errors.HTTPFailure as e: - if e.status_code == 409: # Move these constants to an enum - pass - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + pass + def DeleteContainerIfExists(db, collection_id): try: db.delete_container(collection_id) print('Collection with id \'{0}\' was deleted'.format(collection_id)) - except errors.HTTPFailure as e: - if e.status_code == 404: - pass - elif e.status_code == 400: + except errors.CosmosResourceNotFoundError: + pass + except errors.CosmosHttpResponseError as e + if e.status_code == 400: print("Bad request for collection link", collection_id) - raise - else: - raise + raise + def print_dictionary_items(dict): for k, v in dict.items(): print("{:<15}".format(k), v) print() + def FetchAllDatabases(client): databases = Query_Entities(client, 'database') print("-" * 41) @@ -119,6 +119,7 @@ def FetchAllDatabases(client): print_dictionary_items(db) print("-" * 41) + def QueryDocumentsWithCustomQuery(container, query_with_optional_parameters, message = "Document(s) found by query: "): try: results = list(container.query_items(query_with_optional_parameters, enable_cross_partition_query=True)) @@ -126,10 +127,10 @@ def QueryDocumentsWithCustomQuery(container, query_with_optional_parameters, mes for doc in results: print(doc) return results - except errors.HTTPFailure as e: - if e.status_code == 404: - print("Document doesn't exist") - elif e.status_code == 400: + except errors.CosmosResourceNotFoundError: + print("Document doesn't exist") + except errors.CosmosHttpResponseError as e: + if e.status_code == 400: # Can occur when we are trying to query on excluded paths print("Bad Request exception occured: ", e) pass @@ -138,6 +139,7 @@ def QueryDocumentsWithCustomQuery(container, query_with_optional_parameters, mes finally: print() + def ExplicitlyExcludeFromIndex(db): """ The default index policy on a DocumentContainer will AUTOMATICALLY index ALL documents added. There may be scenarios where you want to exclude a specific doc from the index even though all other @@ -190,14 +192,11 @@ def ExplicitlyExcludeFromIndex(db): # Cleanup db.delete_container(created_Container) print("\n") - - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") + def UseManualIndexing(db): """The default index policy on a DocumentContainer will AUTOMATICALLY index ALL documents added. @@ -254,14 +253,11 @@ def UseManualIndexing(db): # Cleanup db.delete_container(created_Container) print("\n") + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise def ExcludePathsFromIndex(db): """The default behavior is for Cosmos to index every attribute in every document automatically. @@ -329,14 +325,11 @@ def ExcludePathsFromIndex(db): # Cleanup db.delete_container(created_Container) print("\n") + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise def RangeScanOnHashIndex(db): """When a range index is not available (i.e. Only hash or no index found on the path), comparisons queries can still @@ -393,13 +386,11 @@ def RangeScanOnHashIndex(db): # Cleanup db.delete_container(created_Container) print("\n") - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") + def UseRangeIndexesOnStrings(db): """Showing how range queries can be performed even on strings. @@ -481,13 +472,11 @@ def UseRangeIndexesOnStrings(db): # Cleanup db.delete_container(created_Container) print("\n") - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") + def PerformIndexTransformations(db): try: @@ -539,13 +528,11 @@ def PerformIndexTransformations(db): # Cleanup db.delete_container(created_Container) print("\n") - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") + def PerformMultiOrderbyQuery(db): try: @@ -632,13 +619,11 @@ def PerformMultiOrderbyQuery(db): # Cleanup db.delete_container(created_container) print("\n") - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") + def PerformMultiOrderbyQuery(client, database_id): try: @@ -728,13 +713,11 @@ def PerformMultiOrderbyQuery(client, database_id): # Cleanup client.DeleteContainer(collection_link) print("\n") - except errors.HTTPFailure as e: - if e.status_code == 409: - print("Entity already exists") - elif e.status_code == 404: - print("Entity doesn't exist") - else: - raise + except errors.CosmosResourceExistsError: + print("Entity already exists") + except errors.CosmosResourceNotFoundError: + print("Entity doesn't exist") + def RunIndexDemo(): try: @@ -769,7 +752,7 @@ def RunIndexDemo(): # 8. Perform Multi Orderby queries using composite indexes PerformMultiOrderbyQuery(client, DATABASE_ID) - except errors.CosmosError as e: + except errors.AzureError as e: raise e if __name__ == '__main__': diff --git a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py index a714278dc71d..21e4b7367746 100644 --- a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py +++ b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py @@ -27,11 +27,8 @@ def initialize_async(self): database = None try: database = create_client.ReadDatabase("dbs/" + self.database_name) - except errors.CosmosError as e: - if e.status_code == StatusCodes.NOT_FOUND: - print("database not found, needs to be created.") - else: - raise e + except errors.CosmosResourceNotFoundError: + print("database not found, needs to be created.") if not database: database = {'id': self.database_name} @@ -122,20 +119,15 @@ def initialize_async(self): } try: lww_sproc = create_client.CreateStoredProcedure("dbs/" + self.database_name+ "/colls/" + self.udp_collection_name, lww_sproc) - except errors.CosmosError as e: - if e.status_code == StatusCodes.CONFLICT: - return - raise e + except errors.CosmosResourceExistsError: + return def try_create_document_collection (self, client, database, collection): read_collection = None try: read_collection = client.ReadContainer("dbs/" + database['id'] + "/colls/" + collection['id']) - except errors.CosmosError as e: - if e.status_code == StatusCodes.NOT_FOUND: - print("collection not found, needs to be created.") - else: - raise errors + except errors.CosmosResourceNotFoundError: + print("collection not found, needs to be created.") if read_collection == None: collection['partitionKey'] = {'paths': ['/id'],'kind': 'Hash'} @@ -481,33 +473,25 @@ def run_delete_conflict_on_UDP_async(self): def try_insert_document(self, client, collection_uri, document): try: return client.CreateItem(collection_uri, document) - except errors.CosmosError as e: - if e.status_code == StatusCodes.CONFLICT: - return None - raise e + except errors.CosmosResourceExistsError: + return None def try_update_document(self, client, collection_uri, document, options): try: options['partitionKey'] = document['id'] return client.ReplaceItem(collection_uri + "/docs/" + document['id'], document, options); - except errors.CosmosError as e: - if (e.status_code == StatusCodes.PRECONDITION_FAILED or - e.status_code == StatusCodes.NOT_FOUND): - # Lost synchronously or no document yet. No conflict is induced. - return None - raise e + except (errors.CosmosResourceNotFoundError, errors.CosmosResourceModifiedError): + # Lost synchronously or no document yet. No conflict is induced. + return None def try_delete_document(self, client, collection_uri, document, options): try: options['partitionKey'] = document['id'] client.DeleteItem(collection_uri + "/docs/" + document['id'], options) return document - except errors.CosmosError as e: - if (e.status_code == StatusCodes.PRECONDITION_FAILED or - e.status_code == StatusCodes.NOT_FOUND): - #Lost synchronously. No conflict is induced. - return None - raise e + except (errors.CosmosResourceNotFoundError, errors.CosmosResourceModifiedError): + #Lost synchronously. No conflict is induced. + return None def try_update_or_delete_document(self, client, collection_uri, conflict_document, options): if int(conflict_document['regionId']) % 2 == 1: @@ -607,16 +591,14 @@ def validate_LWW_async_internal(self, client, conflict_document, has_delete_conf (conflict_document[0]['id'], client.ReadEndpoint)) time.sleep(0.5) - except errors.CosmosError as e: - if e.status_code == StatusCodes.NOT_FOUND: - print("Delete conflict won @ %s" % client.ReadEndpoint) - return - else: - - self.trace_error("Delete conflict for document %s didnt win @ %s" % - (conflict_document[0]['id'], client.ReadEndpoint)) + except errors.CosmosResourceNotFoundError: + print("Delete conflict won @ %s" % client.ReadEndpoint) + return + except errors.CosmosHttpResponseError: + self.trace_error("Delete conflict for document %s didnt win @ %s" % + (conflict_document[0]['id'], client.ReadEndpoint)) - time.sleep(0.5) + time.sleep(0.5) winner_document = None @@ -640,7 +622,7 @@ def validate_LWW_async_internal(self, client, conflict_document, has_delete_conf (int(winner_document["regionId"]), client.WriteEndpoint)) time.sleep(0.5) - except errors.CosmosError as e: + except errors.AzureError as e: self.trace_error("Winner document from region %d is not found @ %s, retrying..." % (int(winner_document["regionId"]), client.WriteEndpoint)) @@ -673,15 +655,13 @@ def validate_UDP_async_internal(self, client, conflict_document, has_delete_conf (conflict_document[0]['id'], client.ReadEndpoint)) time.sleep(0.5) - except errors.CosmosError as e: - if e.status_code == StatusCodes.NOT_FOUND: - print("Delete conflict won @ %s" % client.ReadEndpoint) - return - else: - self.trace_error("Delete conflict for document %s didnt win @ %s" % - (conflict_document[0]['id'], client.ReadEndpoint)) - - time.sleep(0.5) + except errors.CosmosResourceNotFoundError: + print("Delete conflict won @ %s" % client.ReadEndpoint) + return + except errors.CosmosHttpResponseError: + self.trace_error("Delete conflict for document %s didnt win @ %s" % + (conflict_document[0]['id'], client.ReadEndpoint)) + time.sleep(0.5) winner_document = None @@ -705,10 +685,9 @@ def validate_UDP_async_internal(self, client, conflict_document, has_delete_conf (int(winner_document['regionId']), client.WriteEndpoint)) time.sleep(0.5) - except errors.CosmosError as e: + except errors.AzureError: self.trace_error("Winner document from region %d is not found @ %s, retrying..." % (int(winner_document['regionId']), client.WriteEndpoint)) - time.sleep(0.5) def trace_error(self, message): diff --git a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/Worker.py b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/Worker.py index 28eeaefe5291..38af9a920314 100644 --- a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/Worker.py +++ b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/Worker.py @@ -62,10 +62,10 @@ def delete_all_async(self): while doc: try: self.client.DeleteItem(doc['_self'], {'partitionKey': doc['id']}) - except errors.CosmosError as e: - if e.status_code != StatusCodes.NOT_FOUND: - print("Error occurred while deleting document from %s" % self.client.WriteEndpoint) - else: - raise e + except errors.CosmosResourceNotFoundError: + raise + except errors.CosmosHttpResponseError as e: + print("Error occurred while deleting document from %s" % self.client.WriteEndpoint) + doc = next(it, None) print("Deleted all documents from region %s" % self.client.WriteEndpoint) \ No newline at end of file diff --git a/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py b/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py index baaf8f5e41b8..050e24a9b997 100644 --- a/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py @@ -285,23 +285,16 @@ def run_sample(): # setup database for this sample try: db = client.create_database(id=DATABASE_ID) - - except errors.HTTPFailure as e: - if e.status_code == 409: - pass - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + pass # setup container for this sample try: container, document = ItemManagement.CreateNonPartitionedCollection(db) print('Container with id \'{0}\' created'.format(CONTAINER_ID)) - except errors.HTTPFailure as e: - if e.status_code == 409: - print('Container with id \'{0}\' was found'.format(CONTAINER_ID)) - else: - raise errors.HTTPFailure(e.status_code) + except errors.CosmosResourceExistsError: + print('Container with id \'{0}\' was found'.format(CONTAINER_ID)) # Read Item created in non partitioned collection using older API version ItemManagement.ReadItem(container, document['id']) @@ -315,14 +308,10 @@ def run_sample(): # cleanup database after sample try: client.delete_database(db) + except errors.CosmosResourceNotFoundError: + pass - except errors.CosmosError as e: - if e.status_code == 404: - pass - else: - raise errors.HTTPFailure(e.status_code) - - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: print('\nrun_sample has caught an error. {0}'.format(e.message)) finally: diff --git a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py index c8dbdf131109..4f411f84aa50 100644 --- a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py +++ b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py @@ -31,7 +31,7 @@ import azure.cosmos.cosmos_client as cosmos_client import azure.cosmos.documents as documents import test_config -from azure.cosmos.errors import HTTPFailure +from azure.cosmos.errors import CosmosHttpResponseError from azure.cosmos.partition_key import PartitionKey pytestmark = pytest.mark.cosmosEmulator @@ -233,7 +233,7 @@ def invokeNext(): self.assertEqual(result_iterable.fetch_next_block(), []) if isinstance(expected, Exception): - self.assertRaises(HTTPFailure, _verify_result) + self.assertRaises(CosmosHttpResponseError, _verify_result) else: _verify_result() diff --git a/sdk/cosmos/azure-cosmos/test/conftest.py b/sdk/cosmos/azure-cosmos/test/conftest.py index e0548dcc6449..44e90804baf8 100644 --- a/sdk/cosmos/azure-cosmos/test/conftest.py +++ b/sdk/cosmos/azure-cosmos/test/conftest.py @@ -48,9 +48,8 @@ def delete_database(): for database_id in database_ids_to_delete: try: client.delete_database(database_id) - except errors.HTTPFailure as e: - if e.status_code != StatusCodes.NOT_FOUND: - raise e + except errors.CosmosResourceNotFoundError: + pass del database_ids_to_delete[:] print("Clean up completed!") diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 0f093271d7c3..9666de5a50f1 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -84,7 +84,7 @@ def __AssertHTTPFailureWithStatus(self, status_code, func, *args, **kwargs): try: func(*args, **kwargs) self.assertFalse(True, 'function should fail.') - except errors.HTTPFailure as inst: + except errors.CosmosHttpResponseError as inst: self.assertEqual(inst.status_code, status_code) @classmethod diff --git a/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py b/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py index 5c07812625bf..b20b311c4960 100644 --- a/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py +++ b/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py @@ -152,10 +152,14 @@ def MockExecuteFunction(self, function, *args, **kwargs): else: self.endpoint_discovery_retry_count += 1 location_changed = True - raise errors.HTTPFailure(StatusCodes.FORBIDDEN, "Forbidden", {'x-ms-substatus' : 3}) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.FORBIDDEN, + message="Forbidden", + response=test_config.FakeResponse({'x-ms-substatus' : 3})) def MockGetDatabaseAccountStub(self, endpoint): - raise errors.HTTPFailure(StatusCodes.SERVICE_UNAVAILABLE, "Service unavailable") + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.SERVICE_UNAVAILABLE, message="Service unavailable") def MockCreateDatabase(self, client, database): self.OriginalExecuteFunction = _retry_utility.ExecuteFunction diff --git a/sdk/cosmos/azure-cosmos/test/globaldb_tests.py b/sdk/cosmos/azure-cosmos/test/globaldb_tests.py index e0e8eb41db7e..05a31e2c1e28 100644 --- a/sdk/cosmos/azure-cosmos/test/globaldb_tests.py +++ b/sdk/cosmos/azure-cosmos/test/globaldb_tests.py @@ -72,7 +72,7 @@ def __AssertHTTPFailureWithStatus(self, status_code, sub_status, func, *args, ** try: func(*args, **kwargs) self.assertFalse(True, 'function should fail.') - except errors.HTTPFailure as inst: + except errors.CosmosHttpResponseError as inst: self.assertEqual(inst.status_code, status_code) self.assertEqual(inst.sub_status, sub_status) @@ -385,7 +385,11 @@ def test_globaldb_endpoint_discovery_retry_policy_mock(self): _retry_utility.ExecuteFunction = self.OriginalExecuteFunction def _MockExecuteFunction(self, function, *args, **kwargs): - raise errors.HTTPFailure(StatusCodes.FORBIDDEN, "Write Forbidden", {'x-ms-substatus' : SubStatusCodes.WRITE_FORBIDDEN}) + response = test_config.FakeResponse({'x-ms-substatus' : SubStatusCodes.WRITE_FORBIDDEN}) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.FORBIDDEN, + message="Write Forbidden", + response=response) def _MockGetDatabaseAccount(self, url_conection): database_account = documents.DatabaseAccount() diff --git a/sdk/cosmos/azure-cosmos/test/location_cache_tests.py b/sdk/cosmos/azure-cosmos/test/location_cache_tests.py index a2772ce270e8..7b5479bf22bf 100644 --- a/sdk/cosmos/azure-cosmos/test/location_cache_tests.py +++ b/sdk/cosmos/azure-cosmos/test/location_cache_tests.py @@ -11,10 +11,12 @@ import azure.cosmos.errors as errors from azure.cosmos.http_constants import StatusCodes, SubStatusCodes, HttpHeaders from azure.cosmos import _retry_utility +import test_config import six pytestmark = pytest.mark.cosmosEmulator + class RefreshThread(threading.Thread): def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): @@ -87,7 +89,7 @@ def validate_retry_on_session_not_availabe_with_endpoint_discovery_disabled(self else: client.CreateItem("dbs/mydb/colls/mycoll/", {'id':'1'}) self.fail() - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: # not retried self.assertEqual(self.counter, 1) self.counter = 0 @@ -99,7 +101,11 @@ def validate_retry_on_session_not_availabe_with_endpoint_discovery_disabled(self def _MockExecuteFunctionSessionReadFailureOnce(self, function, *args, **kwargs): self.counter += 1 - raise errors.HTTPFailure(StatusCodes.NOT_FOUND, "Read Session not available", {HttpHeaders.SubStatus: SubStatusCodes.READ_SESSION_NOTAVAILABLE}) + response = test_config.FakeResponse({HttpHeaders.SubStatus: SubStatusCodes.READ_SESSION_NOTAVAILABLE}) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.NOT_FOUND, + message="Read Session not available", + response=response) def test_validate_retry_on_session_not_availabe_with_endpoint_discovery_enabled(self): # sequence of chosen endpoints: @@ -128,7 +134,7 @@ def validate_retry_on_session_not_availabe(self, is_preferred_locations_list_emp try: client.ReadItem("dbs/mydb/colls/mycoll/docs/1") - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: # not retried self.assertEqual(self.counter, 4 if use_multiple_write_locations else 2) self.counter = 0 @@ -160,7 +166,11 @@ def _MockExecuteFunctionSessionReadFailureTwice(self, function, *args, **kwargs) self.assertTrue(request.should_clear_session_token_on_session_read_failure) self.assertEqual(expected_endpoint, request.location_endpoint_to_route) self.counter += 1 - raise errors.HTTPFailure(StatusCodes.NOT_FOUND, "Read Session not available", {HttpHeaders.SubStatus: SubStatusCodes.READ_SESSION_NOTAVAILABLE}) + response = test_config.FakeResponse({HttpHeaders.SubStatus: SubStatusCodes.READ_SESSION_NOTAVAILABLE}) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.NOT_FOUND, + message="Read Session not available", + response=response) def test_validate_location_cache(self): self.original_get_database_account = cosmos_client_connection.CosmosClientConnection.GetDatabaseAccount diff --git a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py index f25dfce77e78..1cd872c7b7e1 100644 --- a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py +++ b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py @@ -58,7 +58,7 @@ def __AssertHTTPFailureWithStatus(self, status_code, func, *args, **kwargs): try: func(*args, **kwargs) self.assertFalse(True, 'function should fail.') - except errors.HTTPFailure as inst: + except errors.CosmosHttpResponseError as inst: self.assertEqual(inst.status_code, status_code) @classmethod @@ -88,7 +88,7 @@ def test_resource_throttle_retry_policy_default_retry_after(self): try: self.created_collection.create_item(body=document_definition) - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: self.assertEqual(e.status_code, StatusCodes.TOO_MANY_REQUESTS) self.assertEqual(connection_policy.RetryOptions.MaxRetryAttemptCount, self.created_collection.client_connection.last_response_headers[HttpHeaders.ThrottleRetryCount]) self.assertGreaterEqual( self.created_collection.client_connection.last_response_headers[HttpHeaders.ThrottleRetryWaitTimeInMs], @@ -110,7 +110,7 @@ def test_resource_throttle_retry_policy_fixed_retry_after(self): try: self.created_collection.create_item(body=document_definition) - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: self.assertEqual(e.status_code, StatusCodes.TOO_MANY_REQUESTS) self.assertEqual(connection_policy.RetryOptions.MaxRetryAttemptCount, self.created_collection.client_connection.last_response_headers[HttpHeaders.ThrottleRetryCount]) self.assertGreaterEqual(self.created_collection.client_connection.last_response_headers[HttpHeaders.ThrottleRetryWaitTimeInMs], @@ -133,7 +133,7 @@ def test_resource_throttle_retry_policy_max_wait_time(self): try: self.created_collection.create_item(body=document_definition) - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: self.assertEqual(e.status_code, StatusCodes.TOO_MANY_REQUESTS) self.assertGreaterEqual(self.created_collection.client_connection.last_response_headers[HttpHeaders.ThrottleRetryWaitTimeInMs], connection_policy.RetryOptions.MaxWaitTimeInSeconds * 1000) @@ -162,7 +162,7 @@ def test_resource_throttle_retry_policy_query(self): { 'name':'@id', 'value':document_definition['id'] } ] })) - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: self.assertEqual(e.status_code, StatusCodes.TOO_MANY_REQUESTS) self.assertEqual(connection_policy.RetryOptions.MaxRetryAttemptCount, self.created_collection.client_connection.last_response_headers[HttpHeaders.ThrottleRetryCount]) @@ -233,7 +233,7 @@ def test_default_retry_policy_for_create(self): created_document = {} try : created_document = self.created_collection.create_item(body=document_definition) - except errors.HTTPFailure as err: + except errors.CosmosHttpResponseError as err: self.assertEqual(err.status_code, 10054) self.assertDictEqual(created_document, {}) @@ -244,7 +244,12 @@ def test_default_retry_policy_for_create(self): _retry_utility.ExecuteFunction = original_execute_function def _MockExecuteFunction(self, function, *args, **kwargs): - raise errors.HTTPFailure(StatusCodes.TOO_MANY_REQUESTS, "Request rate is too large", {HttpHeaders.RetryAfterInMilliseconds: self.retry_after_in_milliseconds}) + response = test_config.FakeResponse({HttpHeaders.RetryAfterInMilliseconds: self.retry_after_in_milliseconds}) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.TOO_MANY_REQUESTS, + message="Request rate is too large", + response=response) + class MockExecuteFunctionConnectionReset(object): @@ -257,7 +262,10 @@ def __call__(self, func, *args, **kwargs): if self.counter % 3 == 0: return self.org_func(func, *args, **kwargs) else: - raise errors.HTTPFailure(10054, "Connection was reset", {}) + raise errors.CosmosHttpResponseError( + status_code=10054, + message="Connection was reset", + response=test_config.FakeResponse({})) if __name__ == '__main__': diff --git a/sdk/cosmos/azure-cosmos/test/session_tests.py b/sdk/cosmos/azure-cosmos/test/session_tests.py index f3e7e3e9fe93..de82c70ecb3e 100644 --- a/sdk/cosmos/azure-cosmos/test/session_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_tests.py @@ -57,7 +57,11 @@ def test_session_token_not_sent_for_master_resource_ops (self): synchronized_request._Request = self._OriginalRequest def _MockExecuteFunctionSessionReadFailureOnce(self, function, *args, **kwargs): - raise errors.HTTPFailure(StatusCodes.NOT_FOUND, "Read Session not available", {HttpHeaders.SubStatus: SubStatusCodes.READ_SESSION_NOTAVAILABLE}) + response = test_config.FakeResponse({HttpHeaders.SubStatus: SubStatusCodes.READ_SESSION_NOTAVAILABLE}) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.NOT_FOUND, + message="Read Session not available", + response=response) def test_clear_session_token(self): created_document = self.created_collection.create_item(body={'id': '1' + str(uuid.uuid4()), 'pk': 'mypk'}) @@ -66,7 +70,7 @@ def test_clear_session_token(self): _retry_utility.ExecuteFunction = self._MockExecuteFunctionSessionReadFailureOnce try: self.created_collection.read_item(item=created_document['id'], partition_key='mypk') - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: self.assertEqual(self.client.client_connection.session.get_session_token( 'dbs/' + self.created_db.id + '/colls/' + self.created_collection.id), "") self.assertEqual(e.status_code, StatusCodes.NOT_FOUND) @@ -84,7 +88,7 @@ def test_internal_server_error_raised_for_invalid_session_token_received_from_se try: self.created_collection.create_item(body={'id': '1' + str(uuid.uuid4()), 'pk': 'mypk'}) self.fail() - except errors.HTTPFailure as e: - self.assertEqual(e._http_error_message, "Could not parse the received session token: 2") + except errors.CosmosHttpResponseError as e: + self.assertEqual(e.http_error_message, "Could not parse the received session token: 2") self.assertEqual(e.status_code, StatusCodes.INTERNAL_SERVER_ERROR) _retry_utility.ExecuteFunction = self.OriginalExecuteFunction diff --git a/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py b/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py index 76e9482ade4f..269c33def21b 100644 --- a/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py @@ -1,7 +1,7 @@ import unittest import pytest from azure.cosmos._vector_session_token import VectorSessionToken -from azure.cosmos.errors import CosmosError +from azure.cosmos.errors import CosmosHttpResponseError pytestmark = pytest.mark.cosmosEmulator @@ -76,5 +76,5 @@ def test_validate_session_token_comparison(self): try: session_token1.merge(session_token2) self.fail("Region progress can not be different when version is same") - except CosmosError as e: - self.assertEquals(str(e), "Status Code: 500. Compared session tokens '1#101#1=20#2=5#3=30' and '1#100#1=20#2=5#3=30#4=40' have unexpected regions.") + except CosmosHttpResponseError as e: + self.assertEquals(str(e), "Status code: 500\nCompared session tokens '1#101#1=20#2=5#3=30' and '1#100#1=20#2=5#3=30#4=40' have unexpected regions.") diff --git a/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py b/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py index 73ab656627b2..2f92dd8a92f5 100644 --- a/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py +++ b/sdk/cosmos/azure-cosmos/test/streaming_failover_test.py @@ -3,6 +3,7 @@ import pytest import azure.cosmos.documents as documents import azure.cosmos.errors as errors +import test_config from azure.cosmos.http_constants import HttpHeaders, StatusCodes, SubStatusCodes from azure.cosmos import _retry_utility from azure.cosmos import _endpoint_discovery_retry_policy @@ -83,7 +84,11 @@ def _MockExecuteFunctionEndpointDiscover(self, function, *args, **kwargs): return ({}, {}) else: self.endpoint_sequence.append(args[1].location_endpoint_to_route) - raise errors.HTTPFailure(StatusCodes.FORBIDDEN, "Request is not permitted in this region", {HttpHeaders.SubStatus: SubStatusCodes.WRITE_FORBIDDEN}) + response = test_config.FakeResponse({HttpHeaders.SubStatus: SubStatusCodes.WRITE_FORBIDDEN}) + raise errors.CosmosHttpResponseError( + status_code=StatusCodes.FORBIDDEN, + message="Request is not permitted in this region", + response=response) def test_retry_policy_does_not_mark_null_locations_unavailable(self): self.original_get_database_account = cosmos_client_connection.CosmosClientConnection.GetDatabaseAccount @@ -107,7 +112,8 @@ def test_retry_policy_does_not_mark_null_locations_unavailable(self): self._write_counter = 0 request = RequestObject(http_constants.ResourceType.Document, documents._OperationType.Read) endpointDiscovery_retry_policy = _endpoint_discovery_retry_policy.EndpointDiscoveryRetryPolicy(documents.ConnectionPolicy(), endpoint_manager, request) - endpointDiscovery_retry_policy.ShouldRetry(errors.HTTPFailure(http_constants.StatusCodes.FORBIDDEN)) + endpointDiscovery_retry_policy.ShouldRetry(errors.CosmosHttpResponseError( + status_code=http_constants.StatusCodes.FORBIDDEN)) self.assertEqual(self._read_counter, 0) self.assertEqual(self._write_counter, 0) @@ -115,7 +121,8 @@ def test_retry_policy_does_not_mark_null_locations_unavailable(self): self._write_counter = 0 request = RequestObject(http_constants.ResourceType.Document, documents._OperationType.Create) endpointDiscovery_retry_policy = _endpoint_discovery_retry_policy.EndpointDiscoveryRetryPolicy(documents.ConnectionPolicy(), endpoint_manager, request) - endpointDiscovery_retry_policy.ShouldRetry(errors.HTTPFailure(http_constants.StatusCodes.FORBIDDEN)) + endpointDiscovery_retry_policy.ShouldRetry(errors.CosmosHttpResponseError( + status_code=http_constants.StatusCodes.FORBIDDEN)) self.assertEqual(self._read_counter, 0) self.assertEqual(self._write_counter, 0) diff --git a/sdk/cosmos/azure-cosmos/test/ttl_tests.py b/sdk/cosmos/azure-cosmos/test/ttl_tests.py index 9be94249f36a..a7e4c8cf2f0b 100644 --- a/sdk/cosmos/azure-cosmos/test/ttl_tests.py +++ b/sdk/cosmos/azure-cosmos/test/ttl_tests.py @@ -60,7 +60,7 @@ def __AssertHTTPFailureWithStatus(self, status_code, func, *args, **kwargs): try: func(*args, **kwargs) self.assertFalse(True, 'function should fail.') - except errors.HTTPFailure as inst: + except errors.CosmosHttpResponseError as inst: self.assertEqual(inst.status_code, status_code) @classmethod From 0aacc0b950a51cd3ba32cd9be3598055901bd9ed Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 30 Aug 2019 11:28:53 -0700 Subject: [PATCH 13/46] Fixed test warnings --- .../azure-cosmos/azure/cosmos/errors.py | 2 - sdk/cosmos/azure-cosmos/test/crud_tests.py | 63 +++++++++---------- .../azure-cosmos/test/partition_key_tests.py | 24 +++---- sdk/cosmos/azure-cosmos/test/query_tests.py | 16 ++--- .../test/session_token_unit_tests.py | 4 +- 5 files changed, 53 insertions(+), 56 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py index f1b1e3b30418..6169ef2c7755 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py @@ -45,8 +45,6 @@ def __init__(self, status_code=None, message=None, response=None, **kwargs): self.sub_status = None self.http_error_message = message status = status_code or (int(response.status_code) if response else 0) - if not status: - raise Exception("Got no status: {}".format(status_code)) if http_constants.HttpHeaders.SubStatus in self.headers: self.sub_status = int(self.headers[http_constants.HttpHeaders.SubStatus]) diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 9666de5a50f1..a8ea7b30beb9 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -121,8 +121,7 @@ def test_database_crud(self): {'name': '@id', 'value': database_id} ] })) - self.assert_(databases, - 'number of results for the query should be > 0') + self.assertTrue(databases, 'number of results for the query should be > 0') # read database. self.client.get_database_client(created_db.id) @@ -149,12 +148,12 @@ def test_database_level_offer_throughput(self): # Verify offer throughput for database offer = created_db.read_offer() - self.assertEquals(offer.offer_throughput, offer_throughput) + self.assertEqual(offer.offer_throughput, offer_throughput) # Update database offer throughput new_offer_throughput = 2000 offer = created_db.replace_throughput(new_offer_throughput) - self.assertEquals(offer.offer_throughput, new_offer_throughput) + self.assertEqual(offer.offer_throughput, new_offer_throughput) def test_sql_query_crud(self): # create two databases. @@ -303,7 +302,7 @@ def test_partitioned_collection_partition_key_extraction(self): # create document without partition key being specified created_document = created_collection.create_item(body=document_definition) _retry_utility.ExecuteFunction = self.OriginalExecuteFunction - self.assertEquals(self.last_headers[1], '["WA"]') + self.assertEqual(self.last_headers[1], '["WA"]') del self.last_headers[:] self.assertEqual(created_document.get('id'), document_definition.get('id')) @@ -320,7 +319,7 @@ def test_partitioned_collection_partition_key_extraction(self): # Create document with partitionkey not present as a leaf level property but a dict created_document = created_collection1.create_item(document_definition) _retry_utility.ExecuteFunction = self.OriginalExecuteFunction - self.assertEquals(self.last_headers[1], [{}]) + self.assertEqual(self.last_headers[1], [{}]) del self.last_headers[:] #self.assertEqual(options['partitionKey'], documents.Undefined) @@ -336,7 +335,7 @@ def test_partitioned_collection_partition_key_extraction(self): # Create document with partitionkey not present in the document created_document = created_collection2.create_item(document_definition) _retry_utility.ExecuteFunction = self.OriginalExecuteFunction - self.assertEquals(self.last_headers[1], [{}]) + self.assertEqual(self.last_headers[1], [{}]) del self.last_headers[:] #self.assertEqual(options['partitionKey'], documents.Undefined) @@ -362,7 +361,7 @@ def test_partitioned_collection_partition_key_extraction_special_chars(self): _retry_utility.ExecuteFunction = self._MockExecuteFunction created_document = created_collection1.create_item(body=document_definition) _retry_utility.ExecuteFunction = self.OriginalExecuteFunction - self.assertEquals(self.last_headers[1], '["val1"]') + self.assertEqual(self.last_headers[1], '["val1"]') del self.last_headers[:] collection_definition2 = { @@ -390,7 +389,7 @@ def test_partitioned_collection_partition_key_extraction_special_chars(self): # create document without partition key being specified created_document = created_collection2.create_item(body=document_definition) _retry_utility.ExecuteFunction = self.OriginalExecuteFunction - self.assertEquals(self.last_headers[1], '["val2"]') + self.assertEqual(self.last_headers[1], '["val2"]') del self.last_headers[:] created_db.delete_container(created_collection1.id) @@ -1165,7 +1164,7 @@ def test_permission_crud(self): {'name': '@id', 'value': permission.id} ] )) - self.assert_(results) + self.assertTrue(results) # replace permission change_permission = permission.properties.copy() @@ -1452,7 +1451,7 @@ def test_trigger_crud(self): {'name': '@id', 'value': trigger_definition['id']} ] )) - self.assert_(triggers) + self.assertTrue(triggers) # replace trigger change_trigger = trigger.copy() @@ -1510,7 +1509,7 @@ def test_udf_crud(self): {'name': '@id', 'value': udf_definition['id']} ] )) - self.assert_(results) + self.assertTrue(results) # replace udf change_udf = udf.copy() udf['body'] = 'function() {var x = 20;}' @@ -2452,40 +2451,40 @@ def test_get_resource_with_dictionary_and_object(self): # read database with id read_db = self.client.get_database_client(created_db.id) - self.assertEquals(read_db.id, created_db.id) + self.assertEqual(read_db.id, created_db.id) # read database with instance read_db = self.client.get_database_client(created_db) - self.assertEquals(read_db.id, created_db.id) + self.assertEqual(read_db.id, created_db.id) # read database with properties read_db = self.client.get_database_client(created_db.read()) - self.assertEquals(read_db.id, created_db.id) + self.assertEqual(read_db.id, created_db.id) created_container = self.configs.create_multi_partition_collection_if_not_exist(self.client) # read container with id read_container = created_db.get_container_client(created_container.id) - self.assertEquals(read_container.id, created_container.id) + self.assertEqual(read_container.id, created_container.id) # read container with instance read_container = created_db.get_container_client(created_container) - self.assertEquals(read_container.id, created_container.id) + self.assertEqual(read_container.id, created_container.id) # read container with properties created_properties = created_container.read() read_container = created_db.get_container_client(created_properties) - self.assertEquals(read_container.id, created_container.id) + self.assertEqual(read_container.id, created_container.id) created_item = created_container.create_item({'id':'1' + str(uuid.uuid4())}) # read item with id read_item = created_container.read_item(item=created_item['id'], partition_key=created_item['id']) - self.assertEquals(read_item['id'], created_item['id']) + self.assertEqual(read_item['id'], created_item['id']) # read item with properties read_item = created_container.read_item(item=created_item, partition_key=created_item['id']) - self.assertEquals(read_item['id'], created_item['id']) + self.assertEqual(read_item['id'], created_item['id']) created_sproc = created_container.scripts.create_stored_procedure({ 'id': 'storedProcedure' + str(uuid.uuid4()), @@ -2494,11 +2493,11 @@ def test_get_resource_with_dictionary_and_object(self): # read sproc with id read_sproc = created_container.scripts.get_stored_procedure(created_sproc['id']) - self.assertEquals(read_sproc['id'], created_sproc['id']) + self.assertEqual(read_sproc['id'], created_sproc['id']) # read sproc with properties read_sproc = created_container.scripts.get_stored_procedure(created_sproc) - self.assertEquals(read_sproc['id'], created_sproc['id']) + self.assertEqual(read_sproc['id'], created_sproc['id']) created_trigger = created_container.scripts.create_trigger({ 'id': 'sample trigger' + str(uuid.uuid4()), @@ -2509,11 +2508,11 @@ def test_get_resource_with_dictionary_and_object(self): # read trigger with id read_trigger = created_container.scripts.get_trigger(created_trigger['id']) - self.assertEquals(read_trigger['id'], created_trigger['id']) + self.assertEqual(read_trigger['id'], created_trigger['id']) # read trigger with properties read_trigger = created_container.scripts.get_trigger(created_trigger) - self.assertEquals(read_trigger['id'], created_trigger['id']) + self.assertEqual(read_trigger['id'], created_trigger['id']) created_udf = created_container.scripts.create_user_defined_function({ 'id': 'sample udf' + str(uuid.uuid4()), @@ -2522,11 +2521,11 @@ def test_get_resource_with_dictionary_and_object(self): # read udf with id read_udf = created_container.scripts.get_user_defined_function(created_udf['id']) - self.assertEquals(created_udf['id'], read_udf['id']) + self.assertEqual(created_udf['id'], read_udf['id']) # read udf with properties read_udf = created_container.scripts.get_user_defined_function(created_udf) - self.assertEquals(created_udf['id'], read_udf['id']) + self.assertEqual(created_udf['id'], read_udf['id']) created_user = created_db.create_user({ 'id': 'user' + str(uuid.uuid4()) @@ -2534,16 +2533,16 @@ def test_get_resource_with_dictionary_and_object(self): # read user with id read_user = created_db.get_user_client(created_user.id) - self.assertEquals(read_user.id, created_user.id) + self.assertEqual(read_user.id, created_user.id) # read user with instance read_user = created_db.get_user_client(created_user) - self.assertEquals(read_user.id, created_user.id) + self.assertEqual(read_user.id, created_user.id) # read user with properties created_user_properties = created_user.read() read_user = created_db.get_user_client(created_user_properties) - self.assertEquals(read_user.id, created_user.id) + self.assertEqual(read_user.id, created_user.id) created_permission = created_user.create_permission({ 'id': 'all permission' + str(uuid.uuid4()), @@ -2554,15 +2553,15 @@ def test_get_resource_with_dictionary_and_object(self): # read permission with id read_permission = created_user.get_permission(created_permission.id) - self.assertEquals(read_permission.id, created_permission.id) + self.assertEqual(read_permission.id, created_permission.id) # read permission with instance read_permission = created_user.get_permission(created_permission) - self.assertEquals(read_permission.id, created_permission.id) + self.assertEqual(read_permission.id, created_permission.id) # read permission with properties read_permission = created_user.get_permission(created_permission.properties) - self.assertEquals(read_permission.id, created_permission.id) + self.assertEqual(read_permission.id, created_permission.id) def _MockExecuteFunction(self, function, *args, **kwargs): self.last_headers.append(args[4].headers[HttpHeaders.PartitionKey] diff --git a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py index cccd4ca3ae97..47b47589366e 100644 --- a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py +++ b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py @@ -128,25 +128,25 @@ def test_non_partitioned_collection_operations(self): # Pass partitionKey.Empty as partition key to access documents from a single partition collection with v 2018-12-31 SDK read_item = created_container.read_item(self.created_document['id'], partition_key=partition_key.NonePartitionKeyValue) - self.assertEquals(read_item['id'], self.created_document['id']) + self.assertEqual(read_item['id'], self.created_document['id']) document_definition = {'id': str(uuid.uuid4())} created_item = created_container.create_item(body=document_definition) - self.assertEquals(created_item['id'], document_definition['id']) + self.assertEqual(created_item['id'], document_definition['id']) read_item = created_container.read_item(created_item['id'], partition_key=partition_key.NonePartitionKeyValue) - self.assertEquals(read_item['id'], created_item['id']) + self.assertEqual(read_item['id'], created_item['id']) document_definition_for_replace = {'id': str(uuid.uuid4())} replaced_item = created_container.replace_item(created_item['id'], body=document_definition_for_replace) - self.assertEquals(replaced_item['id'], document_definition_for_replace['id']) + self.assertEqual(replaced_item['id'], document_definition_for_replace['id']) upserted_item = created_container.upsert_item(body=document_definition) - self.assertEquals(upserted_item['id'], document_definition['id']) + self.assertEqual(upserted_item['id'], document_definition['id']) # one document was created during setup, one with create (which was replaced) and one with upsert items = list(created_container.query_items("SELECT * from c", partition_key=partition_key.NonePartitionKeyValue)) - self.assertEquals(len(items), 3) + self.assertEqual(len(items), 3) document_created_by_sproc_id = 'testDoc' sproc = { @@ -170,7 +170,7 @@ def test_non_partitioned_collection_operations(self): # 3 previous items + 1 created from the sproc items = list(created_container.read_all_items()) - self.assertEquals(len(items), 4) + self.assertEqual(len(items), 4) created_container.delete_item(upserted_item['id'], partition_key=partition_key.NonePartitionKeyValue) created_container.delete_item(replaced_item['id'], partition_key=partition_key.NonePartitionKeyValue) @@ -178,13 +178,13 @@ def test_non_partitioned_collection_operations(self): created_container.delete_item(self.created_document['id'], partition_key=partition_key.NonePartitionKeyValue) items = list(created_container.read_all_items()) - self.assertEquals(len(items), 0) + self.assertEqual(len(items), 0) def test_multi_partition_collection_read_document_with_no_pk(self): document_definition = {'id': str(uuid.uuid4())} self.created_collection.create_item(body=document_definition) read_item = self.created_collection.read_item(item=document_definition['id'], partition_key=partition_key.NonePartitionKeyValue) - self.assertEquals(read_item['id'], document_definition['id']) + self.assertEqual(read_item['id'], document_definition['id']) self.created_collection.delete_item(item=document_definition['id'], partition_key=partition_key.NonePartitionKeyValue) def test_hash_v2_partition_key_definition(self): @@ -193,7 +193,7 @@ def test_hash_v2_partition_key_definition(self): partition_key=partition_key.PartitionKey(path="/id", kind="Hash") ) created_container_properties = created_container.read() - self.assertEquals(created_container_properties['partitionKey']['version'], 2) + self.assertEqual(created_container_properties['partitionKey']['version'], 2) self.created_db.delete_container(created_container) created_container = self.created_db.create_container( @@ -201,7 +201,7 @@ def test_hash_v2_partition_key_definition(self): partition_key=partition_key.PartitionKey(path="/id", kind="Hash", version=2) ) created_container_properties = created_container.read() - self.assertEquals(created_container_properties['partitionKey']['version'], 2) + self.assertEqual(created_container_properties['partitionKey']['version'], 2) self.created_db.delete_container(created_container) def test_hash_v1_partition_key_definition(self): @@ -210,5 +210,5 @@ def test_hash_v1_partition_key_definition(self): partition_key=partition_key.PartitionKey(path="/id", kind="Hash", version=1) ) created_container_properties = created_container.read() - self.assertEquals(created_container_properties['partitionKey']['version'], 1) + self.assertEqual(created_container_properties['partitionKey']['version'], 1) self.created_db.delete_container(created_container) diff --git a/sdk/cosmos/azure-cosmos/test/query_tests.py b/sdk/cosmos/azure-cosmos/test/query_tests.py index ca6c8a377fc0..97847fcc292a 100644 --- a/sdk/cosmos/azure-cosmos/test/query_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_tests.py @@ -56,7 +56,7 @@ def test_query_change_feed(self): iter_list = list(query_iterable) self.assertEqual(len(iter_list), 0) self.assertTrue('etag' in created_collection.client_connection.last_response_headers) - self.assertNotEquals(created_collection.client_connection.last_response_headers['etag'], '') + self.assertNotEqual(created_collection.client_connection.last_response_headers['etag'], '') # Read change feed from beginning should return an empty list query_iterable = created_collection.query_items_change_feed( @@ -67,7 +67,7 @@ def test_query_change_feed(self): self.assertEqual(len(iter_list), 0) self.assertTrue('etag' in created_collection.client_connection.last_response_headers) continuation1 = created_collection.client_connection.last_response_headers['etag'] - self.assertNotEquals(continuation1, '') + self.assertNotEqual(continuation1, '') # Create a document. Read change feed should return be able to read that document document_definition = {'pk': 'pk', 'id':'doc1'} @@ -81,8 +81,8 @@ def test_query_change_feed(self): self.assertEqual(iter_list[0]['id'], 'doc1') self.assertTrue('etag' in created_collection.client_connection.last_response_headers) continuation2 = created_collection.client_connection.last_response_headers['etag'] - self.assertNotEquals(continuation2, '') - self.assertNotEquals(continuation2, continuation1) + self.assertNotEqual(continuation2, '') + self.assertNotEqual(continuation2, continuation1) # Create two new documents. Verify that change feed contains the 2 new documents # with page size 1 and page size 100 @@ -117,7 +117,7 @@ def test_query_change_feed(self): all_fetched_res = [] while (True): fetched_res = query_iterable.fetch_next_block() - self.assertEquals(len(fetched_res), min(pageSize, expected_count - count)) + self.assertEqual(len(fetched_res), min(pageSize, expected_count - count)) count += len(fetched_res) all_fetched_res.extend(fetched_res) if len(fetched_res) == 0: @@ -127,7 +127,7 @@ def test_query_change_feed(self): actual_ids += item['id'] + '.' self.assertEqual(actual_ids, expected_ids) # verify there's no more results - self.assertEquals(query_iterable.fetch_next_block(), []) + self.assertEqual(query_iterable.fetch_next_block(), []) # verify reading change feed from the beginning query_iterable = created_collection.query_items_change_feed( @@ -138,7 +138,7 @@ def test_query_change_feed(self): it = query_iterable.__iter__() for i in range(0, len(expected_ids)): doc = next(it) - self.assertEquals(doc['id'], expected_ids[i]) + self.assertEqual(doc['id'], expected_ids[i]) self.assertTrue('etag' in created_collection.client_connection.last_response_headers) continuation3 = created_collection.client_connection.last_response_headers['etag'] @@ -207,7 +207,7 @@ def validate_query_requests_count(self, query_iterable, expected_count): while block: block = query_iterable.fetch_next_block() retry_utility.ExecuteFunction = self.OriginalExecuteFunction - self.assertEquals(self.count, expected_count) + self.assertEqual(self.count, expected_count) self.count = 0 def _MockExecuteFunction(self, function, *args, **kwargs): diff --git a/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py b/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py index 269c33def21b..91fec45ade57 100644 --- a/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_token_unit_tests.py @@ -11,7 +11,7 @@ class SessionTokenUnitTest(unittest.TestCase): def test_validate_successful_session_token_parsing(self): #valid session token session_token = "1#100#1=20#2=5#3=30" - self.assertEquals(VectorSessionToken.create(session_token).convert_to_string(), "1#100#1=20#2=5#3=30") + self.assertEqual(VectorSessionToken.create(session_token).convert_to_string(), "1#100#1=20#2=5#3=30") def test_validate_session_token_parsing_with_invalid_version(self): session_token = "foo#100#1=20#2=5#3=30" @@ -77,4 +77,4 @@ def test_validate_session_token_comparison(self): session_token1.merge(session_token2) self.fail("Region progress can not be different when version is same") except CosmosHttpResponseError as e: - self.assertEquals(str(e), "Status code: 500\nCompared session tokens '1#101#1=20#2=5#3=30' and '1#100#1=20#2=5#3=30#4=40' have unexpected regions.") + self.assertEqual(str(e), "Status code: 500\nCompared session tokens '1#101#1=20#2=5#3=30' and '1#100#1=20#2=5#3=30#4=40' have unexpected regions.") From 943595c00fe6b1f05bd77093af4937b2f57bfcbc Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 30 Aug 2019 11:30:57 -0700 Subject: [PATCH 14/46] Updated config --- sdk/cosmos/azure-cosmos/test/test_config.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/test/test_config.py b/sdk/cosmos/azure-cosmos/test/test_config.py index cb3d053b524a..2f5d6a5c5413 100644 --- a/sdk/cosmos/azure-cosmos/test/test_config.py +++ b/sdk/cosmos/azure-cosmos/test/test_config.py @@ -25,7 +25,7 @@ import azure.cosmos.documents as documents import azure.cosmos.errors as errors from azure.cosmos.http_constants import StatusCodes -from azure.cosmos.database import Database +from azure.cosmos.database_client import DatabaseClient from azure.cosmos.cosmos_client import CosmosClient from azure.cosmos.partition_key import PartitionKey from azure.cosmos.partition_key import NonePartitionKeyValue @@ -86,7 +86,7 @@ def try_delete_database(cls, client): # type: (CosmosClient) -> None try: client.delete_database(cls.TEST_DATABASE_ID) - except errors.HTTPFailure as e: + except errors.CosmosHttpResponseError as e: if e.status_code != StatusCodes.NOT_FOUND: raise e @@ -161,5 +161,12 @@ def remove_all_documents(cls, document_collection, use_custom_partition_key): # sleep to ensure deletes are propagated for multimaster enabled accounts time.sleep(2) break - except errors.HTTPFailure as e: - print("Error occurred while deleting documents:" + str(e) + " \nRetrying...") \ No newline at end of file + except errors.CosmosHttpResponseError as e: + print("Error occurred while deleting documents:" + str(e) + " \nRetrying...") + + +class FakeResponse: + def __init__(self, headers): + self.headers = headers + self.reason = "foo" + self.status_code = "bar" From ff3628adfb7666dcd05ac12ea7c3a6969a619800 Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 30 Aug 2019 11:46:47 -0700 Subject: [PATCH 15/46] PR fixes --- sdk/cosmos/azure-cosmos/README.md | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/README.md b/sdk/cosmos/azure-cosmos/README.md index c2abbd6d2030..ccbbe8a59e78 100644 --- a/sdk/cosmos/azure-cosmos/README.md +++ b/sdk/cosmos/azure-cosmos/README.md @@ -242,7 +242,7 @@ For example, if you try to create a container using an ID (name) that's already try: database.create_container(id=container_name, partition_key=PartitionKey(path="/productName") except errors.CosmosResourceExistsError: - print("""Error creating container. + print("Error creating container.") HTTP status code 409: The ID (name) provided for the container is already in use. The container name must be unique within the database.""") diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index c4f6ff8a9eb7..e834837c48e0 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -651,7 +651,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: raise CosmosResourceNotFoundError( - status=StatusCodes.NOT_FOUND, + status_code=StatusCodes.NOT_FOUND, message="Could not find Offer for container " + self.container_link) new_offer = offers[0].copy() new_offer["content"]["offerThroughput"] = throughput diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index 55d52f966c5a..a473f48ccc7c 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -692,7 +692,7 @@ def read_offer(self, response_hook=None, **kwargs): offers = list(self.client_connection.QueryOffers(query_spec, **kwargs)) if not offers: raise CosmosResourceNotFoundError( - status=StatusCodes.NOT_FOUND, + status_code=StatusCodes.NOT_FOUND, message="Could not find Offer for database " + self.database_link) if response_hook: @@ -720,7 +720,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): offers = list(self.client_connection.QueryOffers(query_spec)) if not offers: raise CosmosResourceNotFoundError( - status=StatusCodes.NOT_FOUND, + status_code=StatusCodes.NOT_FOUND, message="Could not find Offer for collection " + self.database_link) new_offer = offers[0].copy() new_offer["content"]["offerThroughput"] = throughput From 1986464db782f27b7d384ebab5ccad708173d6fc Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 30 Aug 2019 12:03:46 -0700 Subject: [PATCH 16/46] Fixed init user import --- sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py index 64a278402ad8..d5af2e126014 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py @@ -46,7 +46,7 @@ "PartitionKey", "Permission", "ScriptsClient", - "User", + "UserClient", "ConsistencyLevel", "DataType", "IndexKind", From 6366f029100660f296771c92ee906c7b65439c0a Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 30 Aug 2019 12:05:22 -0700 Subject: [PATCH 17/46] Fixed init clients --- sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py index d5af2e126014..eb909e41cb92 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py @@ -40,9 +40,9 @@ from .version import VERSION __all__ = ( - "Container", + "ContainerClient", "CosmosClient", - "Database", + "DatabaseClient", "PartitionKey", "Permission", "ScriptsClient", From 01bb2b4bce8616dc39be7f351d80b78b67187611 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 3 Sep 2019 09:56:53 -0700 Subject: [PATCH 18/46] Started revising constructors --- .../azure/cosmos/_cosmos_client_connection.py | 8 +---- .../azure-cosmos/azure/cosmos/_utils.py | 10 ++---- .../azure/cosmos/cosmos_client.py | 35 +++++++++++++++++-- .../azure/cosmos/http_constants.py | 1 - .../MultiMasterScenario.py | 2 +- .../azure-cosmos/test/aggregate_tests.py | 3 +- sdk/cosmos/azure-cosmos/test/conftest.py | 2 +- sdk/cosmos/azure-cosmos/test/crud_tests.py | 16 ++++----- .../azure-cosmos/test/encoding_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/env_test.py | 2 +- .../azure-cosmos/test/globaldb_mock_tests.py | 4 +-- .../azure-cosmos/test/globaldb_tests.py | 30 ++++++++-------- .../azure-cosmos/test/multiOrderbyTests.py | 2 +- .../azure-cosmos/test/multimaster_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/orderby_tests.py | 2 +- .../azure-cosmos/test/partition_key_tests.py | 2 +- .../test/query_execution_context_tests.py | 6 ++-- sdk/cosmos/azure-cosmos/test/query_tests.py | 2 +- .../azure-cosmos/test/retry_policy_tests.py | 2 +- .../azure-cosmos/test/routing_map_tests.py | 2 +- .../test/session_container_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/session_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/test_config.py | 15 ++++---- sdk/cosmos/azure-cosmos/test/ttl_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/utils_tests.py | 13 ++++--- .../tests/test_common_blob_async.py | 8 ++--- .../azure-storage-blob/tests/testcase.py | 16 +++++++++ 27 files changed, 114 insertions(+), 79 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index 23c5c4c432d7..5acf536b94a2 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -51,7 +51,6 @@ from . import _session from . import _utils from .partition_key import _Undefined, _Empty -from .version import VERSION # pylint: disable=protected-access @@ -121,7 +120,6 @@ def __init__( self.default_headers = { http_constants.HttpHeaders.CacheControl: "no-cache", http_constants.HttpHeaders.Version: http_constants.Versions.CurrentVersion, - http_constants.HttpHeaders.UserAgent: _utils.get_user_agent(), # For single partition query with aggregate functions we would try to accumulate the results on the SDK. # We need to set continuation as not expected. http_constants.HttpHeaders.IsContinuationExpected: False, @@ -150,15 +148,11 @@ def __init__( url = six.moves.urllib.parse.urlparse(host) proxy = host if url.port else host + ":" + str(connection_policy.ProxyConfiguration.Port) proxies = {url.scheme : proxy} - user_agent = "azsdk-python-cosmos/{} Python/{} ({})".format( - VERSION, - platform.python_version(), - platform.platform()) policies = [ HeadersPolicy(), ProxyPolicy(proxies=proxies), - UserAgentPolicy(base_user_agent=user_agent), + UserAgentPolicy(base_user_agent=_utils.get_user_agent()), ContentDecodePolicy(), CustomHookPolicy(), DistributedTracingPolicy(), diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_utils.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_utils.py index 4e24a938d2d7..f44e3a906bda 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_utils.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_utils.py @@ -24,17 +24,13 @@ import platform import re -from . import http_constants +from .version import VERSION def get_user_agent(): - os_name = safe_user_agent_header(platform.system()) - os_version = safe_user_agent_header(platform.release()) + os_name = safe_user_agent_header(platform.platform()) python_version = safe_user_agent_header(platform.python_version()) - - user_agent = "{}/{} Python/{} {}/{}".format( - os_name, os_version, python_version, http_constants.Versions.SDKName, http_constants.Versions.SDKVersion - ) + user_agent = "azsdk-python-cosmos/{} Python/{} ({})".format(VERSION, python_version, os_name) return user_agent diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 13babf01e771..367012bf23f1 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -35,6 +35,18 @@ __all__ = ("CosmosClient",) +def _parse_connection_str(conn_str, credential): + conn_str = conn_str.rstrip(";") + conn_settings = dict( # pylint: disable=consider-using-dict-comprehension + [s.split("=", 1) for s in conn_str.split(";")] + ) + if 'AccountEndpoint' not in conn_settings: + raise ValueError("Connection string missing setting 'AccountEndpoint'.") + if not credential and 'AccountKey' not in conn_settings: + raise ValueError("Connection string missing setting 'AccountKey'.") + return conn_settings + + class CosmosClient(object): """ Provides a client-side logical representation of an Azure Cosmos DB account. @@ -42,13 +54,13 @@ class CosmosClient(object): """ def __init__( - self, url, auth, consistency_level="Session", connection_policy=None + self, url, credential, consistency_level="Session", connection_policy=None ): # pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs,line-too-long # type: (str, Dict[str, str], str, ConnectionPolicy) -> None """ Instantiate a new CosmosClient. :param url: The URL of the Cosmos DB account. - :param auth: + :param credential: Contains 'masterKey' or 'resourceTokens', where auth['masterKey'] is the default authorization key to use to create the client, and auth['resourceTokens'] is the alternative @@ -65,8 +77,25 @@ def __init__( :name: create_client """ + auth = {} + if isinstance(credential, dict): + auth['resourceTokens'] = credential + elif isinstance(credential, list): + auth['permissionFeed'] = credential + else: + auth['masterKey'] = credential self.client_connection = CosmosClientConnection( - url, auth, consistency_level=consistency_level, connection_policy=connection_policy + url, auth=auth, consistency_level=consistency_level, connection_policy=connection_policy + ) + + @classmethod + def from_connection_string(cls, conn_str, credential=None, consistency_level="Session", **kwargs): + settings = _parse_connection_str(conn_str, credential) + return cls( + url=settings['AccountEndpoint'], + credential=credential or settings['AccountKey'], + consistency_level=consistency_level, + **kwargs ) @staticmethod diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py b/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py index 6dd0450a2434..da327a708548 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/http_constants.py @@ -265,7 +265,6 @@ class Versions(object): CurrentVersion = "2018-12-31" SDKName = "azure-cosmos" - SDKVersion = "4.0.0a1" class Delimiters(object): diff --git a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py index 8b97f0899838..b7a9496e795a 100644 --- a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py +++ b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py @@ -27,7 +27,7 @@ def __init__(self): connection_policy.UseMultipleWriteLocations = True connection_policy.PreferredLocations = [region] - client = cosmos_client_connection.CosmosClientConnection(self.account_endpoint, {'masterKey': self.account_key}, connection_policy, documents.ConsistencyLevel.Session) + client = cosmos_client_connection.CosmosClientConnection(self.account_endpoint, self.account_key, connection_policy, documents.ConsistencyLevel.Session) self.workers.append(Worker(client, self.database_name, self.basic_collection_name)) diff --git a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py index 4f411f84aa50..a86994fa91ef 100644 --- a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py +++ b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py @@ -64,8 +64,7 @@ def _setup(): "'masterKey' and 'host' at the top of this class to run the " "tests.") - mcs.client = cosmos_client.CosmosClient(_config.host, - {'masterKey': _config.master_key}, "Session", _config.connection_policy) + mcs.client = cosmos_client.CosmosClient(_config.host, _config.master_key, "Session", _config.connection_policy) created_db = test_config._test_config.create_database_if_not_exist(mcs.client) mcs.created_collection = _create_collection(created_db) diff --git a/sdk/cosmos/azure-cosmos/test/conftest.py b/sdk/cosmos/azure-cosmos/test/conftest.py index 44e90804baf8..697c04994695 100644 --- a/sdk/cosmos/azure-cosmos/test/conftest.py +++ b/sdk/cosmos/azure-cosmos/test/conftest.py @@ -39,7 +39,7 @@ def delete_database(): masterKey = config.masterKey connectionPolicy = config.connectionPolicy try: - client = cosmos_client.CosmosClient(host, {'masterKey': masterKey}, "Session", connectionPolicy) + client = cosmos_client.CosmosClient(host, masterKey, "Session", connectionPolicy) # This is to soft-fail the teardown while cosmos tests are not running automatically except Exception: pass diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index a8ea7b30beb9..eb7f6b8d4d39 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -95,11 +95,11 @@ def setUpClass(cls): "You must specify your Azure Cosmos account values for " "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, connection_policy=cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, connection_policy=cls.connectionPolicy) cls.databaseForTest = cls.configs.create_database_if_not_exist(cls.client) def setUp(self): - self.client = cosmos_client.CosmosClient(self.host, {'masterKey':self.masterKey}, "Session", + self.client = cosmos_client.CosmosClient(self.host, self.masterKey, "Session", self.connectionPolicy) def test_database_crud(self): # read databases. @@ -538,8 +538,7 @@ def test_partitioned_collection_permissions(self): resource_tokens[urllib.quote(all_collection.id)] = (all_permission.properties['_token']) resource_tokens[urllib.quote(read_collection.id)] = (read_permission.properties['_token']) - restricted_client = cosmos_client.CosmosClient( - CRUDTests.host, {'resourceTokens': resource_tokens}, "Session", CRUDTests.connectionPolicy) + restricted_client = cosmos_client.CosmosClient(CRUDTests.host, resource_tokens, "Session", CRUDTests.connectionPolicy) document_definition = {'id': 'document1', 'key': 1 @@ -1355,7 +1354,7 @@ def __SetupEntities(client): client.read_all_databases()) # Client with master key. client = cosmos_client.CosmosClient(CRUDTests.host, - {'masterKey': CRUDTests.masterKey}, + CRUDTests.masterKey, "Session", CRUDTests.connectionPolicy) # setup entities @@ -1365,8 +1364,7 @@ def __SetupEntities(client): entities['permissionOnColl1'].properties['_token']) resource_tokens[entities['doc1']['id']]= ( entities['permissionOnColl1'].properties['_token']) - col1_client = cosmos_client.CosmosClient( - CRUDTests.host, {'resourceTokens': resource_tokens},"Session", CRUDTests.connectionPolicy) + col1_client = cosmos_client.CosmosClient(CRUDTests.host, resource_tokens,"Session", CRUDTests.connectionPolicy) db = entities['db'] old_client_connection = db.client_connection @@ -1396,7 +1394,7 @@ def __SetupEntities(client): 'Expected to read children using parent permissions') col2_client = cosmos_client.CosmosClient( CRUDTests.host, - {'permissionFeed': [entities['permissionOnColl2'].properties]}, "Session", CRUDTests.connectionPolicy) + [entities['permissionOnColl2'].properties], "Session", CRUDTests.connectionPolicy) doc = { 'CustomProperty1': 'BBBBBB', 'customProperty2': 1000, @@ -1953,7 +1951,7 @@ def test_client_request_timeout(self): connection_policy.RequestTimeout = 0 with self.assertRaises(Exception): # client does a getDatabaseAccount on initialization, which will time out - cosmos_client.CosmosClient(CRUDTests.host, {'masterKey': CRUDTests.masterKey}, "Session", connection_policy) + cosmos_client.CosmosClient(CRUDTests.host, CRUDTests.masterKey, "Session", connection_policy) def test_query_iterable_functionality(self): def __create_resources(client): diff --git a/sdk/cosmos/azure-cosmos/test/encoding_tests.py b/sdk/cosmos/azure-cosmos/test/encoding_tests.py index d48f8ef9e58e..1f0c23e7a334 100644 --- a/sdk/cosmos/azure-cosmos/test/encoding_tests.py +++ b/sdk/cosmos/azure-cosmos/test/encoding_tests.py @@ -25,7 +25,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, connection_policy=cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, connection_policy=cls.connectionPolicy) cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) diff --git a/sdk/cosmos/azure-cosmos/test/env_test.py b/sdk/cosmos/azure-cosmos/test/env_test.py index 5210a0bc7ce2..62c0f81e665d 100644 --- a/sdk/cosmos/azure-cosmos/test/env_test.py +++ b/sdk/cosmos/azure-cosmos/test/env_test.py @@ -60,7 +60,7 @@ def setUpClass(cls): os.environ["COSMOS_ENDPOINT"] = cls.host os.environ["COSMOS_KEY"] = cls.masterKey - cls.client = cosmos_client.CosmosClient(url=cls.host, auth={'masterKey': cls.masterKey }, connection_policy=cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(url=cls.host, credential=cls.masterKey, connection_policy=cls.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = test_config._test_config.create_single_partition_collection_if_not_exist(cls.client) diff --git a/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py b/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py index b20b311c4960..886e842be63c 100644 --- a/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py +++ b/sdk/cosmos/azure-cosmos/test/globaldb_mock_tests.py @@ -170,7 +170,7 @@ def test_globaldb_endpoint_discovery_retry_policy(self): connection_policy = documents.ConnectionPolicy() connection_policy.EnableEndpointDiscovery = True - write_location_client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_mock_tests.write_location_host, {'masterKey': Test_globaldb_mock_tests.masterKey}, connection_policy) + write_location_client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_mock_tests.write_location_host, Test_globaldb_mock_tests.masterKey, connection_policy) self.assertEqual(write_location_client._global_endpoint_manager.WriteEndpoint, Test_globaldb_mock_tests.write_location_host) self.MockCreateDatabase(write_location_client, { 'id': 'mock database' }) @@ -181,7 +181,7 @@ def test_globaldb_database_account_unavailable(self): connection_policy = documents.ConnectionPolicy() connection_policy.EnableEndpointDiscovery = True - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_mock_tests.host, {'masterKey': Test_globaldb_mock_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_mock_tests.host, Test_globaldb_mock_tests.masterKey, connection_policy) self.assertEqual(client._global_endpoint_manager.WriteEndpoint, Test_globaldb_mock_tests.write_location_host) self.assertEqual(client._global_endpoint_manager.ReadEndpoint, Test_globaldb_mock_tests.write_location_host) diff --git a/sdk/cosmos/azure-cosmos/test/globaldb_tests.py b/sdk/cosmos/azure-cosmos/test/globaldb_tests.py index 05a31e2c1e28..fc9e5d194f38 100644 --- a/sdk/cosmos/azure-cosmos/test/globaldb_tests.py +++ b/sdk/cosmos/azure-cosmos/test/globaldb_tests.py @@ -86,7 +86,7 @@ def setUpClass(cls): "tests.") def setUp(self): - self.client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}) + self.client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey) # Create the test database only when it's not already present query_iterable = self.client.QueryDatabases('SELECT * FROM root r WHERE r.id=\'' + Test_globaldb_tests.test_database_id + '\'') @@ -114,7 +114,7 @@ def test_globaldb_read_write_endpoints(self): connection_policy = documents.ConnectionPolicy() connection_policy.EnableEndpointDiscovery = False - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) document_definition = { 'id': 'doc', 'name': 'sample document', @@ -140,7 +140,7 @@ def test_globaldb_read_write_endpoints(self): connection_policy.EnableEndpointDiscovery = True document_definition['id'] = 'doc2' - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) # When EnableEndpointDiscovery is True, WriteEndpoint is set to the write endpoint created_document = client.CreateItem(self.test_coll['_self'], document_definition) @@ -163,7 +163,7 @@ def test_globaldb_endpoint_discovery(self): connection_policy = documents.ConnectionPolicy() connection_policy.EnableEndpointDiscovery = False - read_location_client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.read_location_host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + read_location_client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.read_location_host, Test_globaldb_tests.masterKey, connection_policy) document_definition = { 'id': 'doc', 'name': 'sample document', @@ -187,7 +187,7 @@ def test_globaldb_endpoint_discovery(self): })) connection_policy.EnableEndpointDiscovery = True - read_location_client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.read_location_host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + read_location_client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.read_location_host, Test_globaldb_tests.masterKey, connection_policy) # CreateDocument call will go to the WriteEndpoint as EnableEndpointDiscovery is set to True and client will resolve the right endpoint based on the operation created_document = read_location_client.CreateItem(self.test_coll['_self'], document_definition) @@ -197,7 +197,7 @@ def test_globaldb_preferred_locations(self): connection_policy = documents.ConnectionPolicy() connection_policy.EnableEndpointDiscovery = True - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) document_definition = { 'id': 'doc', 'name': 'sample document', @@ -220,7 +220,7 @@ def test_globaldb_preferred_locations(self): self.assertEqual(client.ReadEndpoint, Test_globaldb_tests.write_location_host) connection_policy.PreferredLocations = [Test_globaldb_tests.read_location2] - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) document_definition['id'] = 'doc2' created_document = client.CreateItem(self.test_coll['_self'], document_definition) @@ -242,28 +242,28 @@ def test_globaldb_endpoint_assignments(self): connection_policy = documents.ConnectionPolicy() connection_policy.EnableEndpointDiscovery = False - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) # When EnableEndpointDiscovery is set to False, both Read and Write Endpoints point to endpoint passed while creating the client instance self.assertEqual(client._global_endpoint_manager.WriteEndpoint, Test_globaldb_tests.host) self.assertEqual(client._global_endpoint_manager.ReadEndpoint, Test_globaldb_tests.host) connection_policy.EnableEndpointDiscovery = True - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) # If no preferred locations is set, we return the write endpoint as ReadEndpoint for better latency performance, write endpoint is set as expected self.assertEqual(client._global_endpoint_manager.WriteEndpoint, Test_globaldb_tests.write_location_host) self.assertEqual(client._global_endpoint_manager.ReadEndpoint, Test_globaldb_tests.write_location_host) connection_policy.PreferredLocations = [Test_globaldb_tests.read_location2] - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) # Test that the preferred location is set as ReadEndpoint instead of default write endpoint when no preference is set self.assertEqual(client._global_endpoint_manager.WriteEndpoint, Test_globaldb_tests.write_location_host) self.assertEqual(client._global_endpoint_manager.ReadEndpoint, Test_globaldb_tests.read_location2_host) def test_globaldb_update_locations_cache(self): - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey) writable_locations = [{'name' : Test_globaldb_tests.write_location, 'databaseAccountEndpoint' : Test_globaldb_tests.write_location_host}] readable_locations = [{'name' : Test_globaldb_tests.read_location, 'databaseAccountEndpoint' : Test_globaldb_tests.read_location_host}, {'name' : Test_globaldb_tests.read_location2, 'databaseAccountEndpoint' : Test_globaldb_tests.read_location2_host}] @@ -307,7 +307,7 @@ def test_globaldb_update_locations_cache(self): connection_policy = documents.ConnectionPolicy() connection_policy.PreferredLocations = [Test_globaldb_tests.read_location2] - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) write_endpoint, read_endpoint = client._global_endpoint_manager.UpdateLocationsCache(writable_locations, readable_locations) @@ -321,7 +321,7 @@ def test_globaldb_update_locations_cache(self): connection_policy = documents.ConnectionPolicy() connection_policy.PreferredLocations = [Test_globaldb_tests.read_location2] - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) write_endpoint, read_endpoint = client._global_endpoint_manager.UpdateLocationsCache(writable_locations, readable_locations) @@ -333,7 +333,7 @@ def test_globaldb_update_locations_cache(self): readable_locations = [{'name' : Test_globaldb_tests.read_location, 'databaseAccountEndpoint' : Test_globaldb_tests.read_location_host}, {'name' : Test_globaldb_tests.read_location2, 'databaseAccountEndpoint' : Test_globaldb_tests.read_location2_host}] connection_policy.EnableEndpointDiscovery = False - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}, connection_policy) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey, connection_policy) write_endpoint, read_endpoint = client._global_endpoint_manager.UpdateLocationsCache(writable_locations, readable_locations) @@ -357,7 +357,7 @@ def test_globaldb_locational_endpoint_parser(self): self.assertEqual(locational_endpoint, 'https://contoso-EastUS.documents.azure.com:443/') def test_globaldb_endpoint_discovery_retry_policy_mock(self): - client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, {'masterKey': Test_globaldb_tests.masterKey}) + client = cosmos_client_connection.CosmosClientConnection(Test_globaldb_tests.host, Test_globaldb_tests.masterKey) self.OriginalExecuteFunction = _retry_utility.ExecuteFunction _retry_utility.ExecuteFunction = self._MockExecuteFunction diff --git a/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py b/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py index f10493281e99..eb058c0d8509 100644 --- a/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py +++ b/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py @@ -61,7 +61,7 @@ class MultiOrderbyTests(unittest.TestCase): @classmethod def setUpClass(cls): - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) cls.database = test_config._test_config.create_database_if_not_exist(cls.client) def generate_multi_orderby_item(self): diff --git a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py index e58dff33a4ae..001592585caf 100644 --- a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py +++ b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py @@ -36,7 +36,7 @@ def _validate_tentative_write_headers(self): connectionPolicy = MultiMasterTests.connectionPolicy connectionPolicy.UseMultipleWriteLocations = True - client = cosmos_client.CosmosClient(MultiMasterTests.host, {'masterKey': MultiMasterTests.masterKey}, "Session", + client = cosmos_client.CosmosClient(MultiMasterTests.host, MultiMasterTests.masterKey, "Session", connectionPolicy) created_db = client.create_database(id='multi_master_tests ' + str(uuid.uuid4())) diff --git a/sdk/cosmos/azure-cosmos/test/orderby_tests.py b/sdk/cosmos/azure-cosmos/test/orderby_tests.py index 7b84ed856aaa..f567a374c8e6 100644 --- a/sdk/cosmos/azure-cosmos/test/orderby_tests.py +++ b/sdk/cosmos/azure-cosmos/test/orderby_tests.py @@ -61,7 +61,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = CrossPartitionTopOrderByTest.create_collection(cls.client, cls.created_db) cls.collection_link = cls.GetDocumentCollectionLink(cls.created_db, cls.created_collection) diff --git a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py index 47b47589366e..9b28b5fb12f9 100644 --- a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py +++ b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py @@ -49,7 +49,7 @@ def tearDownClass(cls): @classmethod def setUpClass(cls): - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) diff --git a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py index f8138b6d5a8a..c1cc32ebd1ee 100644 --- a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py @@ -58,9 +58,9 @@ def setUpClass(cls): "tests.") cls.client = cosmos_client.CosmosClient(QueryExecutionContextEndToEndTests.host, - {'masterKey': QueryExecutionContextEndToEndTests.masterKey}, - "Session", - QueryExecutionContextEndToEndTests.connectionPolicy) + QueryExecutionContextEndToEndTests.masterKey, + "Session", + QueryExecutionContextEndToEndTests.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = cls.create_collection(cls.created_db) cls.document_definitions = [] diff --git a/sdk/cosmos/azure-cosmos/test/query_tests.py b/sdk/cosmos/azure-cosmos/test/query_tests.py index 97847fcc292a..9c120b2967d2 100644 --- a/sdk/cosmos/azure-cosmos/test/query_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_tests.py @@ -25,7 +25,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, connection_policy=cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, connection_policy=cls.connectionPolicy) cls.created_db = cls.config.create_database_if_not_exist(cls.client) def test_first_and_last_slashes_trimmed_for_query_string (self): diff --git a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py index 1cd872c7b7e1..f735d1130ad2 100644 --- a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py +++ b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py @@ -70,7 +70,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) cls.created_collection = test_config._test_config.create_single_partition_collection_if_not_exist(cls.client) cls.retry_after_in_milliseconds = 1000 diff --git a/sdk/cosmos/azure-cosmos/test/routing_map_tests.py b/sdk/cosmos/azure-cosmos/test/routing_map_tests.py index f2f396700be2..ac1fc549d175 100644 --- a/sdk/cosmos/azure-cosmos/test/routing_map_tests.py +++ b/sdk/cosmos/azure-cosmos/test/routing_map_tests.py @@ -55,7 +55,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, connection_policy=cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, connection_policy=cls.connectionPolicy) cls.collection_link = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client).container_link def test_read_partition_key_ranges(self): diff --git a/sdk/cosmos/azure-cosmos/test/session_container_tests.py b/sdk/cosmos/azure-cosmos/test/session_container_tests.py index 035df68e94ac..4b2ff5513976 100644 --- a/sdk/cosmos/azure-cosmos/test/session_container_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_container_tests.py @@ -37,7 +37,7 @@ class Test_session_container(unittest.TestCase): connectionPolicy = test_config._test_config.connectionPolicy def setUp(self): - self.client = cosmos_client.CosmosClient(self.host, {'masterKey': self.masterkey}, "Session", connection_policy=self.connectionPolicy) + self.client = cosmos_client.CosmosClient(self.host, self.masterkey, "Session", connection_policy=self.connectionPolicy) self.session = self.client.client_connection.Session def tearDown(self): diff --git a/sdk/cosmos/azure-cosmos/test/session_tests.py b/sdk/cosmos/azure-cosmos/test/session_tests.py index de82c70ecb3e..c74506248f6f 100644 --- a/sdk/cosmos/azure-cosmos/test/session_tests.py +++ b/sdk/cosmos/azure-cosmos/test/session_tests.py @@ -33,7 +33,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, connection_policy=cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, connection_policy=cls.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) diff --git a/sdk/cosmos/azure-cosmos/test/test_config.py b/sdk/cosmos/azure-cosmos/test/test_config.py index 2f5d6a5c5413..b1ca862c2c36 100644 --- a/sdk/cosmos/azure-cosmos/test/test_config.py +++ b/sdk/cosmos/azure-cosmos/test/test_config.py @@ -38,17 +38,18 @@ class _test_config(object): #[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Cosmos DB Emulator Key")] - masterKey = os.getenv('ACCOUNT_KEY', 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==') - host = os.getenv('ACCOUNT_HOST', 'https://localhost:443/') + masterKey = os.getenv('ACCOUNT_KEY', 'Lq2gvNa7V3bTpYmRmOKr7OtUgLWp1HwjUEwqld4A4Fx5roQfM2cbHCSxRsGVFUeEA9Ndmo3cD15TdXtTSPxRoQ==') + host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') + connection_str = os.getenv('ACCOUNT_CONNECTION_STR', 'AccountEndpoint={};AccountKey={};'.format(host, masterKey)) connectionPolicy = documents.ConnectionPolicy() connectionPolicy.DisableSSLVerification = True - global_host = '[YOUR_GLOBAL_ENDPOINT_HERE]' - write_location_host = '[YOUR_WRITE_ENDPOINT_HERE]' - read_location_host = '[YOUR_READ_ENDPOINT_HERE]' - read_location2_host = '[YOUR_READ_ENDPOINT2_HERE]' - global_masterKey = '[YOUR_KEY_HERE]' + global_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') + write_location_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') + read_location_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') + read_location2_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') + global_masterKey = os.getenv('ACCOUNT_KEY', 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==') write_location = '[YOUR_WRITE_LOCATION_HERE]' read_location = '[YOUR_READ_LOCATION_HERE]' diff --git a/sdk/cosmos/azure-cosmos/test/ttl_tests.py b/sdk/cosmos/azure-cosmos/test/ttl_tests.py index a7e4c8cf2f0b..d14a189bf08b 100644 --- a/sdk/cosmos/azure-cosmos/test/ttl_tests.py +++ b/sdk/cosmos/azure-cosmos/test/ttl_tests.py @@ -71,7 +71,7 @@ def setUpClass(cls): "You must specify your Azure Cosmos account values for " "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, {'masterKey': cls.masterKey}, connection_policy=cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, connection_policy=cls.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) def test_collection_and_document_ttl_values(self): diff --git a/sdk/cosmos/azure-cosmos/test/utils_tests.py b/sdk/cosmos/azure-cosmos/test/utils_tests.py index 4349502fe854..1ac9ed48fa83 100644 --- a/sdk/cosmos/azure-cosmos/test/utils_tests.py +++ b/sdk/cosmos/azure-cosmos/test/utils_tests.py @@ -21,9 +21,11 @@ import unittest import pytest +import azure.cosmos import azure.cosmos._utils as _utils import platform import azure.cosmos.http_constants as http_constants +import test_config pytestmark = pytest.mark.cosmosEmulator @@ -35,12 +37,15 @@ class UtilsTests(unittest.TestCase): def test_user_agent(self): user_agent = _utils.get_user_agent() - expected_user_agent = "{}/{} Python/{} azure-cosmos/{}".format( - platform.system(), platform.release(), platform.python_version(), - http_constants.Versions.SDKVersion + expected_user_agent = "azsdk-python-cosmos/{} Python/{} ({})".format( + azure.cosmos.__version__, + platform.python_version(), + platform.platform() ) - self.assertEqual(user_agent, expected_user_agent) + + #def test_connection_string(self): + if __name__ == "__main__": unittest.main() diff --git a/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py b/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py index c77c59749a69..d2eb0dd276ff 100644 --- a/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py @@ -1621,13 +1621,11 @@ def test_account_sas(self): loop.run_until_complete(self._test_account_sas()) async def _test_token_credential(self): - pytest.skip("") if TestMode.need_recording_file(self.test_mode): return await self._setup() - token_credential = self.generate_oauth_token() - get_token = token_credential.get_token + token_credential = self.generate_async_oauth_token() # Action 1: make sure token works service = BlobServiceClient(self._get_oauth_account_url(), credential=token_credential, transport=AiohttpTestTransport()) @@ -1635,7 +1633,7 @@ async def _test_token_credential(self): self.assertIsNotNone(result) # Action 2: change token value to make request fail - fake_credential = self.generate_fake_token() + fake_credential = self.generate_fake_async_token() service = BlobServiceClient(self._get_oauth_account_url(), credential=fake_credential, transport=AiohttpTestTransport()) with self.assertRaises(ClientAuthenticationError): await service.get_service_properties() @@ -1646,7 +1644,7 @@ async def _test_token_credential(self): self.assertIsNotNone(result) @record - def test_token_credential(self): + def test_token_credential_async(self): loop = asyncio.get_event_loop() loop.run_until_complete(self._test_token_credential()) diff --git a/sdk/storage/azure-storage-blob/tests/testcase.py b/sdk/storage/azure-storage-blob/tests/testcase.py index d50d35155dc1..09d5ae6b593f 100644 --- a/sdk/storage/azure-storage-blob/tests/testcase.py +++ b/sdk/storage/azure-storage-blob/tests/testcase.py @@ -417,9 +417,25 @@ def generate_oauth_token(self): self.settings.ACTIVE_DIRECTORY_TENANT_ID ) + def generate_async_oauth_token(self): + from azure.identity.aio import ClientSecretCredential + + return ClientSecretCredential( + self.settings.ACTIVE_DIRECTORY_APPLICATION_ID, + self.settings.ACTIVE_DIRECTORY_APPLICATION_SECRET, + self.settings.ACTIVE_DIRECTORY_TENANT_ID + ) + def generate_fake_token(self): return FakeTokenCredential() + def generate_fake_async_token(self): + class FakeAsyncTokenCredential(object): + async def get_token(self, *args): + return AccessToken("YOU SHALL NOT PASS", 0) + + return FakeAsyncTokenCredential() + def record(test): def recording_test(self): with self.recording(): From 50f9b1220024b096d03ffd12208d81bcd1388859 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 3 Sep 2019 11:58:27 -0700 Subject: [PATCH 19/46] Test conn str constructor --- sdk/cosmos/azure-cosmos/test/utils_tests.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/test/utils_tests.py b/sdk/cosmos/azure-cosmos/test/utils_tests.py index 1ac9ed48fa83..c0dda2c443da 100644 --- a/sdk/cosmos/azure-cosmos/test/utils_tests.py +++ b/sdk/cosmos/azure-cosmos/test/utils_tests.py @@ -44,7 +44,12 @@ def test_user_agent(self): ) self.assertEqual(user_agent, expected_user_agent) - #def test_connection_string(self): + def test_connection_string(self): + client = azure.cosmos.CosmosClient.from_connection_string(test_config._test_config.connection_str) + databases = list(client.read_all_databases()) + assert len(databases) > 0 + assert isinstance(databases[0], dict) + assert databases[0].get('_etag') is not None if __name__ == "__main__": From 8441a6cbd10215dcf9432f7b02cca9dad858e800 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 3 Sep 2019 13:36:51 -0700 Subject: [PATCH 20/46] Update iterables with core paging --- .../azure/cosmos/_cosmos_client_connection.py | 120 ++++++++++++------ .../azure/cosmos/_query_iterable.py | 89 +++++-------- .../azure/cosmos/container_client.py | 6 +- .../azure-cosmos/test/aggregate_tests.py | 8 +- sdk/cosmos/azure-cosmos/test/crud_tests.py | 16 +-- sdk/cosmos/azure-cosmos/test/orderby_tests.py | 28 ++-- sdk/cosmos/azure-cosmos/test/query_tests.py | 16 +-- 7 files changed, 150 insertions(+), 133 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index 5acf536b94a2..69145cf2ea32 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -28,6 +28,7 @@ import requests import six +from azure.core.paging import ItemPaged from azure.core import PipelineClient from azure.core.pipeline.policies import ( ContentDecodePolicy, @@ -307,7 +308,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def ReadContainers(self, database_link, options=None): """Reads all collections in a database. @@ -355,7 +358,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def CreateContainer(self, database_link, collection, options=None): """Creates a collection in a database. @@ -538,7 +543,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def DeleteDatabase(self, database_link, options=None): """Deletes a database. @@ -680,7 +687,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def ReplaceUser(self, user_link, user, options=None): """Replaces a user and return it. @@ -788,10 +797,10 @@ def ReadItems(self, collection_link, feed_options=None, response_hook=None): return self.QueryItems(collection_link, None, feed_options, response_hook=response_hook) - def QueryItems(self, database_or_Container_link, query, options=None, partition_key=None, response_hook=None): + def QueryItems(self, database_or_container_link, query, options=None, partition_key=None, response_hook=None): """Queries documents in a collection. - :param str database_or_Container_link: + :param str database_or_container_link: The link to the database when using partitioning, otherwise link to the document collection. :param (str or dict) query: :param dict options: @@ -807,20 +816,23 @@ def QueryItems(self, database_or_Container_link, query, options=None, partition_ query_iterable.QueryIterable """ - database_or_Container_link = base.TrimBeginningAndEndingSlashes(database_or_Container_link) + database_or_container_link = base.TrimBeginningAndEndingSlashes(database_or_container_link) if options is None: options = {} - if base.IsDatabaseLink(database_or_Container_link): - # Python doesn't have a good way of specifying an overloaded constructor, - # and this is how it's generally overloaded constructors are specified (by - # calling a @classmethod) and returning the 'self' instance - return query_iterable.QueryIterable.PartitioningQueryIterable( - self, query, options, database_or_Container_link, partition_key + if base.IsDatabaseLink(database_or_container_link): + return ItemPaged( + self, + query, + options, + database_link=database_or_container_link, + partition_key=partition_key, + page_iterator_class=query_iterable.QueryIterable ) - path = base.GetPathFromLink(database_or_Container_link, "docs") - collection_id = base.GetResourceIdOrFullNameFromLink(database_or_Container_link) + + path = base.GetPathFromLink(database_or_container_link, "docs") + collection_id = base.GetResourceIdOrFullNameFromLink(database_or_container_link) def fetch_fn(options): return ( @@ -837,7 +849,14 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn, database_or_Container_link) + return ItemPaged( + self, + query, + options, + fetch_function=fetch_fn, + collection_link=database_or_container_link, + page_iterator_class=query_iterable.QueryIterable + ) def QueryItemsChangeFeed(self, collection_link, options=None, response_hook=None): """Queries documents change feed in a collection. @@ -917,7 +936,14 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, None, options, fetch_fn, collection_link) + return ItemPaged( + self, + None, + options, + fetch_function=fetch_fn, + collection_link=collection_link, + page_iterator_class=query_iterable.QueryIterable + ) def _ReadPartitionKeyRanges(self, collection_link, feed_options=None): """Reads Partition Key Ranges. @@ -966,12 +992,14 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) - def CreateItem(self, database_or_Container_link, document, options=None): + def CreateItem(self, database_or_container_link, document, options=None): """Creates a document in a collection. - :param str database_or_Container_link: + :param str database_or_container_link: The link to the database when using partitioning, otherwise link to the document collection. :param dict document: The Azure Cosmos document to create. @@ -999,18 +1027,18 @@ def CreateItem(self, database_or_Container_link, document, options=None): # We check the link to be document collection link since it can be database # link in case of client side partitioning - if base.IsItemContainerLink(database_or_Container_link): - options = self._AddPartitionKey(database_or_Container_link, document, options) + if base.IsItemContainerLink(database_or_container_link): + options = self._AddPartitionKey(database_or_container_link, document, options) collection_id, document, path = self._GetContainerIdWithPathForItem( - database_or_Container_link, document, options + database_or_container_link, document, options ) return self.Create(document, path, "docs", collection_id, None, options) - def UpsertItem(self, database_or_Container_link, document, options=None): + def UpsertItem(self, database_or_container_link, document, options=None): """Upserts a document in a collection. - :param str database_or_Container_link: + :param str database_or_container_link: The link to the database when using partitioning, otherwise link to the document collection. :param dict document: The Azure Cosmos document to upsert. @@ -1038,11 +1066,11 @@ def UpsertItem(self, database_or_Container_link, document, options=None): # We check the link to be document collection link since it can be database # link in case of client side partitioning - if base.IsItemContainerLink(database_or_Container_link): - options = self._AddPartitionKey(database_or_Container_link, document, options) + if base.IsItemContainerLink(database_or_container_link): + options = self._AddPartitionKey(database_or_container_link, document, options) collection_id, document, path = self._GetContainerIdWithPathForItem( - database_or_Container_link, document, options + database_or_container_link, document, options ) return self.Upsert(document, path, "docs", collection_id, None, options) @@ -1054,10 +1082,10 @@ def UpsertItem(self, database_or_Container_link, document, options=None): ) # Gets the collection id and path for the document - def _GetContainerIdWithPathForItem(self, database_or_Container_link, document, options): + def _GetContainerIdWithPathForItem(self, database_or_container_link, document, options): - if not database_or_Container_link: - raise ValueError("database_or_Container_link is None or empty.") + if not database_or_container_link: + raise ValueError("database_or_container_link is None or empty.") if document is None: raise ValueError("document is None.") @@ -1067,10 +1095,10 @@ def _GetContainerIdWithPathForItem(self, database_or_Container_link, document, o if not document.get("id") and not options.get("disableAutomaticIdGeneration"): document["id"] = base.GenerateGuidId() - collection_link = database_or_Container_link + collection_link = database_or_container_link - if base.IsDatabaseLink(database_or_Container_link): - partition_resolver = self.GetPartitionResolver(database_or_Container_link) + if base.IsDatabaseLink(database_or_container_link): + partition_resolver = self.GetPartitionResolver(database_or_container_link) if partition_resolver is not None: collection_link = partition_resolver.ResolveForCreate(document) @@ -1150,7 +1178,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def CreateTrigger(self, collection_link, trigger, options=None): """Creates a trigger in a collection. @@ -1275,7 +1305,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def CreateUserDefinedFunction(self, collection_link, udf, options=None): """Creates a user defined function in a collection. @@ -1400,7 +1432,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def CreateStoredProcedure(self, collection_link, sproc, options=None): """Creates a stored procedure in a collection. @@ -1523,7 +1557,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def ReadConflict(self, conflict_link, options=None): """Reads a conflict. @@ -1799,7 +1835,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def ReadMedia(self, media_link, **kwargs): """Reads a media. @@ -2192,7 +2230,9 @@ def fetch_fn(options): self.last_response_headers, ) - return query_iterable.QueryIterable(self, query, options, fetch_fn) + return ItemPaged( + self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable + ) def GetDatabaseAccount(self, url_connection=None, **kwargs): """Gets database account info. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py index 65f8a8fd2a2d..e44624b73ecb 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py @@ -21,18 +21,29 @@ """Iterable query results in the Azure Cosmos database service. """ +from azure.core.paging import PageIterator from azure.cosmos._execution_context import execution_dispatcher from azure.cosmos._execution_context import base_execution_context # pylint: disable=protected-access -class QueryIterable(object): +class QueryIterable(PageIterator): """Represents an iterable object of the query results. QueryIterable is a wrapper for query execution context. """ - def __init__(self, client, query, options, fetch_function, collection_link=None): + def __init__( + self, + client, + query, + options, + fetch_function=None, + collection_link=None, + database_link=None, + partition_key=None, + continuation_token=None, + ): """ Instantiates a QueryIterable for non-client side partitioning queries. _ProxyQueryExecutionContext will be used as the internal query execution context @@ -56,41 +67,19 @@ def __init__(self, client, query, options, fetch_function, collection_link=None) self.retry_options = client.connection_policy.RetryOptions self._query = query self._options = options + if continuation_token: + self._options['continuation'] = continuation_token self._fetch_function = fetch_function self._collection_link = collection_link - self._ex_context = None - - @classmethod - def PartitioningQueryIterable(cls, client, query, options, database_link, partition_key): - """ - Represents a client side partitioning query iterable. - - This constructor instantiates a QueryIterable for - client side partitioning queries, and sets _MultiCollectionQueryExecutionContext - as the internal execution context. - - :param CosmosClient client: - Instance of document client - :param (str or dict) options: - :param dict options: - The request options for the request. - :param str database_link: - Database self link or ID based link - :param str partition_key: - Partition key for the query - """ - # This will call the base constructor(__init__ method above) - - self = cls(client, query, options, None, None) - self._database_link = database_link # pylint: disable=attribute-defined-outside-init - self._partition_key = partition_key # pylint: disable=attribute-defined-outside-init - - return self + self._database_link = database_link + self._partition_key = partition_key + self._ex_context = self._create_execution_context() + super(QueryIterable, self).__init__(self._fetch_next, self._unpack, continuation_token=continuation_token) def _create_execution_context(self): """instantiates the internal query execution context based. """ - if hasattr(self, "_database_link"): + if self._database_link: # client side partitioning query return base_execution_context._MultiCollectionQueryExecutionContext( self._client, self._options, self._database_link, self._query, self._partition_key @@ -99,29 +88,12 @@ def _create_execution_context(self): self._client, self._collection_link, self._query, self._options, self._fetch_function ) - def __iter__(self): - """Makes this class iterable. - """ - return self.Iterator(self) - - class Iterator(object): - def __init__(self, iterable): - self._iterable = iterable - self._finished = False - self._ex_context = iterable._create_execution_context() - - def __iter__(self): - # Always returns self - return self + def _unpack(self, block): + if block: + self._did_a_call_already = False + return self._ex_context._continuation, block - def __next__(self): - return next(self._ex_context) - - # Also support Python 3.x iteration - def next(self): - return self.__next__() - - def fetch_next_block(self): + def _fetch_next(self, continuation): """Returns a block of results with respecting retry policy. This method only exists for backward compatibility reasons. (Because QueryIterable @@ -132,9 +104,8 @@ def fetch_next_block(self): :rtype: list """ - - if self._ex_context is None: - # initiates execution context for the first time - self._ex_context = self._create_execution_context() - - return self._ex_context.fetch_next_block() + self._ex_context._continuation = continuation + block = self._ex_context.fetch_next_block() + if not block: + raise StopIteration + return block diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index e834837c48e0..69036c1c9e38 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -369,7 +369,7 @@ def query_items( response_hook.clear() items = self.client_connection.QueryItems( - database_or_Container_link=self.container_link, + database_or_container_link=self.container_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, partition_key=partition_key, @@ -485,7 +485,7 @@ def upsert_item( request_options["postTriggerInclude"] = post_trigger_include result = self.client_connection.UpsertItem( - database_or_Container_link=self.container_link, document=body, **kwargs) + database_or_container_link=self.container_link, document=body, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers, result) return result @@ -544,7 +544,7 @@ def create_item( request_options["indexingDirective"] = indexing_directive result = self.client_connection.CreateItem( - database_or_Container_link=self.container_link, document=body, options=request_options, **kwargs + database_or_container_link=self.container_link, document=body, options=request_options, **kwargs ) if response_hook: response_hook(self.client_connection.last_response_headers, result) diff --git a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py index a86994fa91ef..dec8ba2d31a2 100644 --- a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py +++ b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py @@ -220,16 +220,18 @@ def invokeNext(): self.assertRaises(StopIteration, invokeNext) ###################################### - # test fetch_next_block() behavior + # test by_page() behavior ###################################### - fetched_res = result_iterable.fetch_next_block() + page_iter = result_iterable.by_page() + fetched_res = list(next(page_iter)) fetched_size = len(fetched_res) self.assertEqual(fetched_size, 1) self.assertEqual(fetched_res[0], expected) # no more results will be returned - self.assertEqual(result_iterable.fetch_next_block(), []) + with self.assertRaises(StopIteration): + next(page_iter) if isinstance(expected, Exception): self.assertRaises(CosmosHttpResponseError, _verify_result) diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index eb7f6b8d4d39..0fcfc094b70b 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -2010,18 +2010,14 @@ def __create_resources(client): # Get query results page by page. results = resources['coll'].read_all_items(max_item_count=2) - first_block = results.fetch_next_block() - self.assertEqual(2, - len(first_block), - 'First block should have 2 entries.') + page_iter = results.by_page() + first_block = list(next(page_iter)) + self.assertEqual(2, len(first_block), 'First block should have 2 entries.') self.assertEqual(resources['doc1']['id'], first_block[0]['id']) self.assertEqual(resources['doc2']['id'], first_block[1]['id']) - self.assertEqual(1, - len(results.fetch_next_block()), - 'Second block should have 1 entry.') - self.assertEqual(0, - len(results.fetch_next_block()), - 'Then its empty.') + self.assertEqual(1, len(list(next(page_iter))), 'Second block should have 1 entry.') + with self.assertRaises(StopIteration): + next(page_iter) def test_trigger_functionality(self): triggers_in_collection1 = [ diff --git a/sdk/cosmos/azure-cosmos/test/orderby_tests.py b/sdk/cosmos/azure-cosmos/test/orderby_tests.py index f567a374c8e6..f0be608c5ecb 100644 --- a/sdk/cosmos/azure-cosmos/test/orderby_tests.py +++ b/sdk/cosmos/azure-cosmos/test/orderby_tests.py @@ -22,6 +22,7 @@ import unittest import uuid import pytest +from azure.core.paging import ItemPaged import azure.cosmos.documents as documents from azure.cosmos.partition_key import PartitionKey import azure.cosmos.cosmos_client as cosmos_client @@ -431,8 +432,15 @@ def find_docs_by_partition_key_range_id(self): collection_id = base.GetResourceIdOrFullNameFromLink(self.collection_link) def fetch_fn(options): return self.client.client_connection.QueryFeed(path, collection_id, query, options, r['id']) - docResultsIterable = query_iterable.QueryIterable(self.client.client_connection, query, options, fetch_fn, self.collection_link) - + docResultsIterable = ItemPaged( + self.client.client_connection, + query, + options, + fetch_function=fetch_fn, + collection_link=self.collection_link, + page_iterator_class=query_iterable.QueryIterable + ) + docs = list(docResultsIterable) self.assertFalse(r['id'] in docs_by_partition_key_range_id) docs_by_partition_key_range_id[r['id']] = docs @@ -448,7 +456,8 @@ def execute_query_and_validate_results(self, query, expected_ordered_ids): max_item_count=page_size ) - self.assertTrue(isinstance(result_iterable, query_iterable.QueryIterable)) + self.assertTrue(isinstance(result_iterable, ItemPaged)) + self.assertEqual(result_iterable._page_iterator_class, query_iterable.QueryIterable) ###################################### # test next() behavior @@ -466,14 +475,15 @@ def invokeNext(): self.assertRaises(StopIteration, invokeNext) ###################################### - # test fetch_next_block() behavior + # test by_page() behavior ###################################### results = {} cnt = 0 - while True: - fetched_res = result_iterable.fetch_next_block() + page_iter = result_iterable.by_page() + for page in page_iter: + fetched_res = list(page) fetched_size = len(fetched_res) - + for item in fetched_res: self.assertEqual(item['id'], expected_ordered_ids[cnt]) results[cnt] = item @@ -487,12 +497,14 @@ def invokeNext(): else: #cnt > expected_number_of_results self.fail("more results than expected") + # validate the number of collected results self.assertEqual(len(results), len(expected_ordered_ids)) # no more results will be returned - self.assertEqual(result_iterable.fetch_next_block(), []) + with self.assertRaises(StopIteration): + next(page_iter) @classmethod def create_collection(self, client, created_db): diff --git a/sdk/cosmos/azure-cosmos/test/query_tests.py b/sdk/cosmos/azure-cosmos/test/query_tests.py index 9c120b2967d2..862263f07d59 100644 --- a/sdk/cosmos/azure-cosmos/test/query_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_tests.py @@ -105,7 +105,7 @@ def test_query_change_feed(self): actual_ids += item['id'] + '.' self.assertEqual(actual_ids, expected_ids) - # verify fetch_next_block + # verify by_page # the options is not copied, therefore it need to be restored query_iterable = created_collection.query_items_change_feed( partition_key_range_id=pkRangeId, @@ -115,19 +115,16 @@ def test_query_change_feed(self): count = 0 expected_count = 2 all_fetched_res = [] - while (True): - fetched_res = query_iterable.fetch_next_block() + for page in query_iterable.by_page(): + fetched_res = list(page) self.assertEqual(len(fetched_res), min(pageSize, expected_count - count)) count += len(fetched_res) all_fetched_res.extend(fetched_res) - if len(fetched_res) == 0: - break + actual_ids = '' for item in all_fetched_res: actual_ids += item['id'] + '.' self.assertEqual(actual_ids, expected_ids) - # verify there's no more results - self.assertEqual(query_iterable.fetch_next_block(), []) # verify reading change feed from the beginning query_iterable = created_collection.query_items_change_feed( @@ -203,9 +200,8 @@ def validate_query_requests_count(self, query_iterable, expected_count): self.count = 0 self.OriginalExecuteFunction = retry_utility.ExecuteFunction retry_utility.ExecuteFunction = self._MockExecuteFunction - block = query_iterable.fetch_next_block() - while block: - block = query_iterable.fetch_next_block() + for block in query_iterable.by_page(): + assert len(list(block)) != 0 retry_utility.ExecuteFunction = self.OriginalExecuteFunction self.assertEqual(self.count, expected_count) self.count = 0 From ce6c62207ce6d7e2e85dc72a2af284c6e9f657f6 Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 3 Sep 2019 13:39:16 -0700 Subject: [PATCH 21/46] Added context managers --- sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 367012bf23f1..7bfcab01a0d2 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -88,6 +88,13 @@ def __init__( url, auth=auth, consistency_level=consistency_level, connection_policy=connection_policy ) + def __enter__(self): + self.client_connection.pipeline_client.__enter__() + return self + + def __exit__(self, *args): + return self.client_connection.pipeline_client.__exit__(*args) + @classmethod def from_connection_string(cls, conn_str, credential=None, consistency_level="Session", **kwargs): settings = _parse_connection_str(conn_str, credential) From 398d9ddc0a545b2ac97d06ea81cf4cc70cc7425b Mon Sep 17 00:00:00 2001 From: antisch Date: Tue, 3 Sep 2019 15:01:33 -0700 Subject: [PATCH 22/46] Reverted storage changes --- .../tests/test_common_blob_async.py | 8 +++++--- sdk/storage/azure-storage-blob/tests/testcase.py | 16 ---------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py b/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py index d2eb0dd276ff..c77c59749a69 100644 --- a/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py +++ b/sdk/storage/azure-storage-blob/tests/test_common_blob_async.py @@ -1621,11 +1621,13 @@ def test_account_sas(self): loop.run_until_complete(self._test_account_sas()) async def _test_token_credential(self): + pytest.skip("") if TestMode.need_recording_file(self.test_mode): return await self._setup() - token_credential = self.generate_async_oauth_token() + token_credential = self.generate_oauth_token() + get_token = token_credential.get_token # Action 1: make sure token works service = BlobServiceClient(self._get_oauth_account_url(), credential=token_credential, transport=AiohttpTestTransport()) @@ -1633,7 +1635,7 @@ async def _test_token_credential(self): self.assertIsNotNone(result) # Action 2: change token value to make request fail - fake_credential = self.generate_fake_async_token() + fake_credential = self.generate_fake_token() service = BlobServiceClient(self._get_oauth_account_url(), credential=fake_credential, transport=AiohttpTestTransport()) with self.assertRaises(ClientAuthenticationError): await service.get_service_properties() @@ -1644,7 +1646,7 @@ async def _test_token_credential(self): self.assertIsNotNone(result) @record - def test_token_credential_async(self): + def test_token_credential(self): loop = asyncio.get_event_loop() loop.run_until_complete(self._test_token_credential()) diff --git a/sdk/storage/azure-storage-blob/tests/testcase.py b/sdk/storage/azure-storage-blob/tests/testcase.py index 09d5ae6b593f..d50d35155dc1 100644 --- a/sdk/storage/azure-storage-blob/tests/testcase.py +++ b/sdk/storage/azure-storage-blob/tests/testcase.py @@ -417,25 +417,9 @@ def generate_oauth_token(self): self.settings.ACTIVE_DIRECTORY_TENANT_ID ) - def generate_async_oauth_token(self): - from azure.identity.aio import ClientSecretCredential - - return ClientSecretCredential( - self.settings.ACTIVE_DIRECTORY_APPLICATION_ID, - self.settings.ACTIVE_DIRECTORY_APPLICATION_SECRET, - self.settings.ACTIVE_DIRECTORY_TENANT_ID - ) - def generate_fake_token(self): return FakeTokenCredential() - def generate_fake_async_token(self): - class FakeAsyncTokenCredential(object): - async def get_token(self, *args): - return AccessToken("YOU SHALL NOT PASS", 0) - - return FakeAsyncTokenCredential() - def record(test): def recording_test(self): with self.recording(): From dab0703fd02d0991a17ea03a521eeab5f754d3e6 Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 10:15:26 -0700 Subject: [PATCH 23/46] Updated constructor --- sdk/cosmos/azure-cosmos/README.md | 4 +- .../azure/cosmos/_cosmos_client_connection.py | 14 ++-- .../azure/cosmos/cosmos_client.py | 68 ++++++++++++++++--- .../azure-cosmos/test/aggregate_tests.py | 3 +- sdk/cosmos/azure-cosmos/test/conftest.py | 2 +- sdk/cosmos/azure-cosmos/test/crud_tests.py | 18 +++-- .../azure-cosmos/test/multiOrderbyTests.py | 2 +- .../azure-cosmos/test/multimaster_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/orderby_tests.py | 2 +- .../azure-cosmos/test/partition_key_tests.py | 2 +- .../test/query_execution_context_tests.py | 2 +- .../azure-cosmos/test/retry_policy_tests.py | 2 +- 12 files changed, 85 insertions(+), 36 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/README.md b/sdk/cosmos/azure-cosmos/README.md index ccbbe8a59e78..9199973f08dd 100644 --- a/sdk/cosmos/azure-cosmos/README.md +++ b/sdk/cosmos/azure-cosmos/README.md @@ -68,9 +68,7 @@ from azure.cosmos import CosmosClient, Container, Database, PartitionKey, errors import os url = os.environ['ACCOUNT_URI'] key = os.environ['ACCOUNT_KEY'] -client = CosmosClient(url, auth = { - 'masterKey': key -}) +client = CosmosClient(url, credential=key) ``` ## Usage diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index 69145cf2ea32..3d4e3ecb2997 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -81,7 +81,7 @@ class _QueryCompatibilityMode: _DefaultStringRangePrecision = -1 def __init__( - self, url_connection, auth, connection_policy=None, consistency_level=documents.ConsistencyLevel.Session + self, url_connection, auth, connection_policy=None, consistency_level=documents.ConsistencyLevel.Session, **kwargs ): """ :param str url_connection: @@ -143,21 +143,21 @@ def __init__( self._useMultipleWriteLocations = False self._global_endpoint_manager = global_endpoint_manager._GlobalEndpointManager(self) - proxies = {} + proxies = kwargs.pop('proxies', {}) if self.connection_policy.ProxyConfiguration and self.connection_policy.ProxyConfiguration.Host: host = connection_policy.ProxyConfiguration.Host url = six.moves.urllib.parse.urlparse(host) proxy = host if url.port else host + ":" + str(connection_policy.ProxyConfiguration.Port) - proxies = {url.scheme : proxy} + proxies.update({url.scheme : proxy}) policies = [ - HeadersPolicy(), + HeadersPolicy(**kwargs), ProxyPolicy(proxies=proxies), - UserAgentPolicy(base_user_agent=_utils.get_user_agent()), + UserAgentPolicy(base_user_agent=_utils.get_user_agent(), **kwargs), ContentDecodePolicy(), - CustomHookPolicy(), + CustomHookPolicy(**kwargs), DistributedTracingPolicy(), - NetworkTraceLoggingPolicy(), + NetworkTraceLoggingPolicy(**kwargs), ] self.pipeline_client = PipelineClient(url_connection, "empty-config", policies=policies) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 7bfcab01a0d2..9a5873a33f62 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -47,15 +47,66 @@ def _parse_connection_str(conn_str, credential): return conn_settings +def _build_auth(credential): + auth = {} + if isinstance(credential, six.string_types): + auth['masterKey'] = credential + elif isinstance(credential, dict): + if any(k for k in credential.keys() if k in ['masterKey', 'resourceTokens', 'permissionFeed']): + return credential # Backwards compatible + auth['resourceTokens'] = credential + elif hasattr(credential, '__iter__'): + auth['permissionFeed'] = credential + else: + raise TypeError( + "Unrecognized credential type. Please supply the master key as str, " + "or a dictionary or resource tokens, or a list of permissions.") + return auth + + +def _build_connection_policy(kwargs): + policy = kwargs.pop('connection_policy', None) or ConnectionPolicy() + + # Connection config + policy.RequestTimeout = kwargs.pop('request_timeout', None) or \ + kwargs.pop('connection_timeout', None) or \ + policy.RequestTimeout + policy.MediaRequestTimeout = kwargs.pop('media_request_timeout', None) or policy.MediaRequestTimeout + policy.ConnectionMode = kwargs.pop('connection_mode', None) or policy.ConnectionMode + policy.MediaReadMode = kwargs.pop('media_read_mode', None) or policy.MediaReadMode + policy.ProxyConfiguration = kwargs.pop('proxy_config', None) or policy.ProxyConfiguration + policy.EnableEndpointDiscovery = kwargs.pop('enable_endpoint_discovery', None) or policy.EnableEndpointDiscovery + policy.PreferredLocations = kwargs.pop('preferred_locations', None) or policy.PreferredLocations + policy.UseMultipleWriteLocations = kwargs.pop('multiple_write_locations', None) or \ + policy.UseMultipleWriteLocations + + # SSL config + verify = kwargs.pop('connection_verify', None) + policy.DisableSSLVerification = not bool(verify if verify is not None else True) + ssl = kwargs.pop('ssl_config', None) or policy.SSLConfiguration + if ssl: + ssl.SSLCertFile = kwargs.pop('connection_cert', None) or ssl.SSLCertFile + ssl.SSLCaCerts = verify or ssl.SSLCaCerts + policy.SSLConfiguration = ssl + + # Retry config + retry = kwargs.pop('retry_options', None) or policy.RetryOptions + retry._max_retry_attempt_count = kwargs.pop('retry_total', None) or retry._max_retry_attempt_count + retry._fixed_retry_interval_in_milliseconds = kwargs.pop('retry_fixed_interval', None) or \ + retry._fixed_retry_interval_in_milliseconds + retry._max_wait_time_in_seconds = kwargs.pop('retry_backoff_max', None) or retry._max_wait_time_in_seconds + policy.RetryOptions = retry + + return policy + + class CosmosClient(object): """ Provides a client-side logical representation of an Azure Cosmos DB account. Use this client to configure and execute requests to the Azure Cosmos DB service. """ - def __init__( - self, url, credential, consistency_level="Session", connection_policy=None - ): # pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs,line-too-long + def __init__(self, url, credential, consistency_level="Session", **kwargs): # type: (str, Dict[str, str], str, ConnectionPolicy) -> None """ Instantiate a new CosmosClient. @@ -77,15 +128,10 @@ def __init__( :name: create_client """ - auth = {} - if isinstance(credential, dict): - auth['resourceTokens'] = credential - elif isinstance(credential, list): - auth['permissionFeed'] = credential - else: - auth['masterKey'] = credential + auth = _build_auth(credential) + connection_policy = _build_connection_policy(kwargs) self.client_connection = CosmosClientConnection( - url, auth=auth, consistency_level=consistency_level, connection_policy=connection_policy + url, auth=auth, consistency_level=consistency_level, connection_policy=connection_policy, **kwargs ) def __enter__(self): diff --git a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py index dec8ba2d31a2..a10343334234 100644 --- a/sdk/cosmos/azure-cosmos/test/aggregate_tests.py +++ b/sdk/cosmos/azure-cosmos/test/aggregate_tests.py @@ -64,7 +64,8 @@ def _setup(): "'masterKey' and 'host' at the top of this class to run the " "tests.") - mcs.client = cosmos_client.CosmosClient(_config.host, _config.master_key, "Session", _config.connection_policy) + mcs.client = cosmos_client.CosmosClient( + _config.host, _config.master_key, "Session", connection_policy=_config.connection_policy) created_db = test_config._test_config.create_database_if_not_exist(mcs.client) mcs.created_collection = _create_collection(created_db) diff --git a/sdk/cosmos/azure-cosmos/test/conftest.py b/sdk/cosmos/azure-cosmos/test/conftest.py index 697c04994695..cbb2191bc775 100644 --- a/sdk/cosmos/azure-cosmos/test/conftest.py +++ b/sdk/cosmos/azure-cosmos/test/conftest.py @@ -39,7 +39,7 @@ def delete_database(): masterKey = config.masterKey connectionPolicy = config.connectionPolicy try: - client = cosmos_client.CosmosClient(host, masterKey, "Session", connectionPolicy) + client = cosmos_client.CosmosClient(host, masterKey, "Session", connection_policy=connectionPolicy) # This is to soft-fail the teardown while cosmos tests are not running automatically except Exception: pass diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 0fcfc094b70b..6d87734dd2e4 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -100,7 +100,7 @@ def setUpClass(cls): def setUp(self): self.client = cosmos_client.CosmosClient(self.host, self.masterKey, "Session", - self.connectionPolicy) + connection_policy=self.connectionPolicy) def test_database_crud(self): # read databases. databases = list(self.client.read_all_databases()) @@ -538,7 +538,8 @@ def test_partitioned_collection_permissions(self): resource_tokens[urllib.quote(all_collection.id)] = (all_permission.properties['_token']) resource_tokens[urllib.quote(read_collection.id)] = (read_permission.properties['_token']) - restricted_client = cosmos_client.CosmosClient(CRUDTests.host, resource_tokens, "Session", CRUDTests.connectionPolicy) + restricted_client = cosmos_client.CosmosClient( + CRUDTests.host, resource_tokens, "Session", connection_policy=CRUDTests.connectionPolicy) document_definition = {'id': 'document1', 'key': 1 @@ -1348,7 +1349,7 @@ def __SetupEntities(client): return entities # Client without any authorization will fail. - client = cosmos_client.CosmosClient(CRUDTests.host, {}, "Session", CRUDTests.connectionPolicy) + client = cosmos_client.CosmosClient(CRUDTests.host, {}, "Session", connection_policy=CRUDTests.connectionPolicy) self.__AssertHTTPFailureWithStatus(StatusCodes.UNAUTHORIZED, list, client.read_all_databases()) @@ -1356,7 +1357,7 @@ def __SetupEntities(client): client = cosmos_client.CosmosClient(CRUDTests.host, CRUDTests.masterKey, "Session", - CRUDTests.connectionPolicy) + connection_policy=CRUDTests.connectionPolicy) # setup entities entities = __SetupEntities(client) resource_tokens = {} @@ -1364,7 +1365,8 @@ def __SetupEntities(client): entities['permissionOnColl1'].properties['_token']) resource_tokens[entities['doc1']['id']]= ( entities['permissionOnColl1'].properties['_token']) - col1_client = cosmos_client.CosmosClient(CRUDTests.host, resource_tokens,"Session", CRUDTests.connectionPolicy) + col1_client = cosmos_client.CosmosClient( + CRUDTests.host, resource_tokens,"Session", connection_policy=CRUDTests.connectionPolicy) db = entities['db'] old_client_connection = db.client_connection @@ -1394,7 +1396,9 @@ def __SetupEntities(client): 'Expected to read children using parent permissions') col2_client = cosmos_client.CosmosClient( CRUDTests.host, - [entities['permissionOnColl2'].properties], "Session", CRUDTests.connectionPolicy) + [entities['permissionOnColl2'].properties], + "Session", + connection_policy=CRUDTests.connectionPolicy) doc = { 'CustomProperty1': 'BBBBBB', 'customProperty2': 1000, @@ -1951,7 +1955,7 @@ def test_client_request_timeout(self): connection_policy.RequestTimeout = 0 with self.assertRaises(Exception): # client does a getDatabaseAccount on initialization, which will time out - cosmos_client.CosmosClient(CRUDTests.host, CRUDTests.masterKey, "Session", connection_policy) + cosmos_client.CosmosClient(CRUDTests.host, CRUDTests.masterKey, "Session", connection_policy=connection_policy) def test_query_iterable_functionality(self): def __create_resources(client): diff --git a/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py b/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py index eb058c0d8509..b68db27b520e 100644 --- a/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py +++ b/sdk/cosmos/azure-cosmos/test/multiOrderbyTests.py @@ -61,7 +61,7 @@ class MultiOrderbyTests(unittest.TestCase): @classmethod def setUpClass(cls): - cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", connection_policy=cls.connectionPolicy) cls.database = test_config._test_config.create_database_if_not_exist(cls.client) def generate_multi_orderby_item(self): diff --git a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py index 001592585caf..efe8c3f7619d 100644 --- a/sdk/cosmos/azure-cosmos/test/multimaster_tests.py +++ b/sdk/cosmos/azure-cosmos/test/multimaster_tests.py @@ -37,7 +37,7 @@ def _validate_tentative_write_headers(self): connectionPolicy = MultiMasterTests.connectionPolicy connectionPolicy.UseMultipleWriteLocations = True client = cosmos_client.CosmosClient(MultiMasterTests.host, MultiMasterTests.masterKey, "Session", - connectionPolicy) + connection_policy=connectionPolicy) created_db = client.create_database(id='multi_master_tests ' + str(uuid.uuid4())) diff --git a/sdk/cosmos/azure-cosmos/test/orderby_tests.py b/sdk/cosmos/azure-cosmos/test/orderby_tests.py index f0be608c5ecb..ba1eb99b3c17 100644 --- a/sdk/cosmos/azure-cosmos/test/orderby_tests.py +++ b/sdk/cosmos/azure-cosmos/test/orderby_tests.py @@ -62,7 +62,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", connection_policy=cls.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = CrossPartitionTopOrderByTest.create_collection(cls.client, cls.created_db) cls.collection_link = cls.GetDocumentCollectionLink(cls.created_db, cls.created_collection) diff --git a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py index 9b28b5fb12f9..026b1f30ae56 100644 --- a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py +++ b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py @@ -49,7 +49,7 @@ def tearDownClass(cls): @classmethod def setUpClass(cls): - cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", connection_policy=cls.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = test_config._test_config.create_multi_partition_collection_with_custom_pk_if_not_exist(cls.client) diff --git a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py index c1cc32ebd1ee..9b73a4f738d9 100644 --- a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py @@ -60,7 +60,7 @@ def setUpClass(cls): cls.client = cosmos_client.CosmosClient(QueryExecutionContextEndToEndTests.host, QueryExecutionContextEndToEndTests.masterKey, "Session", - QueryExecutionContextEndToEndTests.connectionPolicy) + connection_policy=QueryExecutionContextEndToEndTests.connectionPolicy) cls.created_db = test_config._test_config.create_database_if_not_exist(cls.client) cls.created_collection = cls.create_collection(cls.created_db) cls.document_definitions = [] diff --git a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py index f735d1130ad2..77b50ea7e3d7 100644 --- a/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py +++ b/sdk/cosmos/azure-cosmos/test/retry_policy_tests.py @@ -70,7 +70,7 @@ def setUpClass(cls): "'masterKey' and 'host' at the top of this class to run the " "tests.") - cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", cls.connectionPolicy) + cls.client = cosmos_client.CosmosClient(cls.host, cls.masterKey, "Session", connection_policy=cls.connectionPolicy) cls.created_collection = test_config._test_config.create_single_partition_collection_if_not_exist(cls.client) cls.retry_after_in_milliseconds = 1000 From be457d643bd13789529bc62e58eb6e1d08ef3098 Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 11:05:40 -0700 Subject: [PATCH 24/46] Mypy and Pylint --- .../azure-cosmos/azure/cosmos/__init__.py | 2 +- .../azure/cosmos/_cosmos_client_connection.py | 11 +++-- .../azure/cosmos/_synchronized_request.py | 6 ++- .../azure/cosmos/container_client.py | 31 +++++++------ .../azure/cosmos/cosmos_client.py | 33 +++++++------- .../azure/cosmos/database_client.py | 44 +++++++++++-------- .../azure-cosmos/azure/cosmos/errors.py | 3 +- .../azure/cosmos/scripts_client.py | 19 ++++---- .../azure-cosmos/azure/cosmos/user_client.py | 8 ++-- 9 files changed, 88 insertions(+), 69 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py index eb909e41cb92..6a0c0bea8854 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py @@ -57,4 +57,4 @@ "TriggerOperation", "TriggerType", ) -__version__ = VERSION \ No newline at end of file +__version__ = VERSION diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index 3d4e3ecb2997..d3f3e0ff821f 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -24,9 +24,6 @@ """Document client class for the Azure Cosmos database service. """ -import platform - -import requests import six from azure.core.paging import ItemPaged from azure.core import PipelineClient @@ -81,8 +78,14 @@ class _QueryCompatibilityMode: _DefaultStringRangePrecision = -1 def __init__( - self, url_connection, auth, connection_policy=None, consistency_level=documents.ConsistencyLevel.Session, **kwargs + self, + url_connection, # type: str + auth, # type: Dict[str, Any] + connection_policy=None, # type: Optional[ConnectionPolicy] + consistency_level=documents.ConsistencyLevel.Session, # type: str + **kwargs # type: Any ): + # type: (...) -> None """ :param str url_connection: The URL for connecting to the DB server. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index 5368de448b2e..270ae5c3843f 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -24,7 +24,7 @@ import json -from six.moves.urllib.parse import urlparse, urlencode +from six.moves.urllib.parse import urlparse import six from azure.core.exceptions import DecodeError @@ -87,6 +87,8 @@ def _Request(global_endpoint_manager, request_params, connection_policy, pipelin tuple of (dict, dict) """ + # pylint: disable=protected-access + is_media = request.url.find("media") > -1 is_media_stream = is_media and connection_policy.MediaReadMode == documents.MediaReadMode.Streamed @@ -190,7 +192,7 @@ def SynchronizedRequest( :param object client: Document client instance :param dict request_params: - :param _GlobalEndpointManager global_endpoint_manager: + :param _GlobalEndpointManager global_endpoint_manager: :param documents.ConnectionPolicy connection_policy: :param azure.core.PipelineClient pipeline_client: PipelineClient to process the request. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index 69036c1c9e38..ac6516e5e5bb 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -22,7 +22,7 @@ """Create, read, update and delete items in the Azure Cosmos DB SQL API service. """ -from typing import Any, Callable, Dict, List, Optional, Union +from typing import Any, Callable, Dict, List, Optional, Union, Iterable import six from azure.core.tracing.decorator import distributed_trace @@ -32,12 +32,12 @@ from .http_constants import StatusCodes from .offer import Offer from .scripts_client import ScriptsClient -from ._query_iterable import QueryIterable from .partition_key import NonePartitionKeyValue -__all__ = ("Container",) +__all__ = ("ContainerClient",) # pylint: disable=protected-access +# pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs class ContainerClient(object): @@ -72,6 +72,7 @@ def _get_properties(self): @property def is_system_key(self): + # type: () -> bool if self._is_system_key is None: properties = self._get_properties() self._is_system_key = ( @@ -81,6 +82,7 @@ def is_system_key(self): @property def scripts(self): + # type: () -> ScriptsClient if self._scripts is None: self._scripts = ScriptsClient(self.client_connection, self.container_link, self.is_system_key) return self._scripts @@ -214,7 +216,7 @@ def read_all_items( response_hook=None, **kwargs ): - # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List all items in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -249,14 +251,15 @@ def read_all_items( @distributed_trace def query_items_change_feed( self, - partition_key_range_id=None, - is_start_from_beginning=False, - continuation=None, - max_item_count=None, - feed_options=None, - response_hook=None, - **kwargs + partition_key_range_id=None, # type: Optional[str] + is_start_from_beginning=False, # type: bool + continuation=None, # type: Optional[str] + max_item_count=None, # type: Optional[int] + feed_options=None, # type: Optional[Dict[str, Any]] + response_hook=None, # type: Optional[Callable] + **kwargs # type: Any ): + # type: (...) -> Iterable[Dict[str, Any]] """ Get a sorted list of items that were changed, in the order in which they were modified. :param partition_key_range_id: ChangeFeed requests can be executed against specific partition key ranges. @@ -308,7 +311,7 @@ def query_items( response_hook=None, # type: Optional[Callable] **kwargs ): - # type: (...) -> QueryIterable + # type: (...) -> Iterable[Dict[str, Any]] """Return all results matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -664,7 +667,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): @distributed_trace def read_all_conflicts(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (int, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List all conflicts in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -697,7 +700,7 @@ def query_conflicts( response_hook=None, **kwargs ): - # type: (str, List, bool, Any, int, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (str, List, bool, Any, int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """Return all conflicts matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 9a5873a33f62..6429ab47c1ee 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -22,7 +22,7 @@ """Create, read, and delete databases in the Azure Cosmos DB SQL API service. """ -from typing import Any, Callable, Dict, Mapping, Optional, Union, cast +from typing import Any, Callable, Dict, Mapping, Optional, Union, cast, Iterable import six from azure.core.tracing.decorator import distributed_trace @@ -30,7 +30,6 @@ from ._cosmos_client_connection import CosmosClientConnection from .database_client import DatabaseClient from .documents import ConnectionPolicy, DatabaseAccount -from ._query_iterable import QueryIterable __all__ = ("CosmosClient",) @@ -65,6 +64,7 @@ def _build_auth(credential): def _build_connection_policy(kwargs): + # pylint: disable=protected-access policy = kwargs.pop('connection_policy', None) or ConnectionPolicy() # Connection config @@ -128,7 +128,7 @@ def __init__(self, url, credential, consistency_level="Session", **kwargs): :name: create_client """ - auth = _build_auth(credential) + auth = _build_auth(credential) connection_policy = _build_connection_policy(kwargs) self.client_connection = CosmosClientConnection( url, auth=auth, consistency_level=consistency_level, connection_policy=connection_policy, **kwargs @@ -143,6 +143,7 @@ def __exit__(self, *args): @classmethod def from_connection_string(cls, conn_str, credential=None, consistency_level="Session", **kwargs): + # type: (str, Optional[Any], str, Any) -> CosmosClient settings = _parse_connection_str(conn_str, credential) return cls( url=settings['AccountEndpoint'], @@ -164,19 +165,19 @@ def _get_database_link(database_or_id): return "dbs/{}".format(database_id) @distributed_trace - def create_database( + def create_database( # pylint: disable=redefined-builtin self, - id, # pylint: disable=redefined-builtin - session_token=None, - initial_headers=None, - access_condition=None, - populate_query_metrics=None, - offer_throughput=None, - request_options=None, - response_hook=None, - **kwargs + id, # type: str + session_token=None, # type: Optional[str] + initial_headers=None, # type: Optional[Dict[str, str]] + access_condition=None, # type: Optional[Dict[str, str]] + populate_query_metrics=None, # type: Optional[bool] + offer_throughput=None, # type: Optional[int] + request_options=None, # type: Optional[Dict[str, Any]] + response_hook=None, # type: Optional[Callable] + **kwargs # type: Any ): - # type: (str, str, Dict[str, str], Dict[str, str], bool, int, Dict[str, Any], Optional[Callable]) -> DatabaseClient + # type: (...) -> DatabaseClient """Create a new database with the given ID (name). :param id: ID (name) of the database to create. @@ -248,7 +249,7 @@ def read_all_databases( response_hook=None, **kwargs ): - # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List the databases in a Cosmos DB SQL database account. @@ -291,7 +292,7 @@ def query_databases( response_hook=None, # type: Optional[Callable] **kwargs ): - # type: (...) -> QueryIterable + # type: (...) -> Iterable[Dict[str, Any]] """ Query the databases in a Cosmos DB SQL database account. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index a473f48ccc7c..cdfbe338d164 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -22,7 +22,7 @@ """Create, read, update and delete containers in the Azure Cosmos DB SQL API service. """ -from typing import Any, List, Dict, Mapping, Union, cast +from typing import Any, List, Dict, Mapping, Union, cast, Iterable import six from azure.core.tracing.decorator import distributed_trace @@ -33,11 +33,11 @@ from .http_constants import StatusCodes from .errors import CosmosResourceNotFoundError from .user_client import UserClient -from ._query_iterable import QueryIterable __all__ = ("DatabaseClient",) # pylint: disable=protected-access +# pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs class DatabaseClient(object): @@ -320,7 +320,7 @@ def read_all_containers( response_hook=None, **kwargs ): - # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List the containers in the database. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -361,17 +361,17 @@ def read_all_containers( @distributed_trace def query_containers( self, - query=None, - parameters=None, - max_item_count=None, - session_token=None, - initial_headers=None, - populate_query_metrics=None, - feed_options=None, - response_hook=None, - **kwargs + query=None, # type: Optional[str] + parameters=None, # type: Optional[List[str]] + max_item_count=None, # type: Optional[int] + session_token=None, # type: Optional[str] + initial_headers=None, # type: Optional[Dict[str, str]] + populate_query_metrics=None, # type: Optional[bool] + feed_options=None, # type: Optional[Dict[str, Any]] + response_hook=None, # type: Optional[Callable] + **kwargs # type: Any ): - # type: (str, List, int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (...) -> Iterable[Dict[str, Any]] """List properties for containers in the current database :param query: The Azure Cosmos DB SQL query to execute. @@ -431,7 +431,8 @@ def replace_container( :class:`ContainerClient` instance of the container to be replaced. :param partition_key: The partition key to use for the container. :param indexing_policy: The indexing policy to apply to the container. - :param default_ttl: Default time to live (TTL) for items in the container. If unspecified, items do not expire. + :param default_ttl: Default time to live (TTL) for items in the container. + If unspecified, items do not expire. :param conflict_resolution_policy: The conflict resolution policy to apply to the container. :param session_token: Token for use with Session consistency. :param access_condition: Conditions Associated with the request. @@ -490,7 +491,7 @@ def replace_container( @distributed_trace def read_all_users(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (int, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List all users in the container. :param max_item_count: Max number of users to be returned in the enumeration operation. @@ -513,7 +514,7 @@ def read_all_users(self, max_item_count=None, feed_options=None, response_hook=N @distributed_trace def query_users(self, query, parameters=None, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """Return all users matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -624,8 +625,15 @@ def upsert_user(self, body, request_options=None, response_hook=None, **kwargs): ) @distributed_trace - def replace_user(self, user, body, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, UserClient, Dict[str, Any]], Dict[str, Any], Dict[str, Any], Optional[Callable]) -> UserClient + def replace_user( + self, + user, # type: Union[str, UserClient, Dict[str, Any]] + body, # type: Dict[str, Any] + request_options=None, # type: Optional(Dict[str, Any]) + response_hook=None, # type: Optional[Callable] + **kwargs # type: Any + ): + # type: (...) -> UserClient """ Replaces the specified user if it exists in the container. :param user: The ID (name), dict representing the properties or :class:`UserClient` diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py index 6169ef2c7755..8bcf053773b9 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py @@ -21,7 +21,7 @@ """PyCosmos Exceptions in the Azure Cosmos database service. """ -from azure.core.exceptions import ( +from azure.core.exceptions import ( # pylint: disable=unused-import AzureError, HttpResponseError, ResourceExistsError, @@ -50,7 +50,6 @@ def __init__(self, status_code=None, message=None, response=None, **kwargs): self.sub_status = int(self.headers[http_constants.HttpHeaders.SubStatus]) formatted_message = "Status code: %d Sub-status: %d\n%s" % (status, self.sub_status, str(message)) else: - formatted_message = "Status code: %d\n%s" % (status, str(message)) super(CosmosHttpResponseError, self).__init__(message=formatted_message, response=response, **kwargs) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py index e0f9665d5133..b3e44f349bc6 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py @@ -22,15 +22,15 @@ """Create, read, update and delete and execute scripts in the Azure Cosmos DB SQL API service. """ -from typing import Any, List, Dict, Union +from typing import Any, List, Dict, Union, Iterable import six from azure.cosmos._cosmos_client_connection import CosmosClientConnection from .partition_key import NonePartitionKeyValue -from ._query_iterable import QueryIterable # pylint: disable=protected-access +# pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs class ScriptType(object): @@ -53,7 +53,7 @@ def _get_resource_link(self, script_or_id, typ): return script_or_id["_self"] def list_stored_procedures(self, max_item_count=None, feed_options=None): - # type: (int, Dict[str, Any]) -> QueryIterable + # type: (int, Dict[str, Any]) -> Iterable[Dict[str, Any]] """ List all stored procedures in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -69,7 +69,7 @@ def list_stored_procedures(self, max_item_count=None, feed_options=None): return self.client_connection.ReadStoredProcedures(collection_link=self.container_link, options=feed_options) def query_stored_procedures(self, query, parameters=None, max_item_count=None, feed_options=None): - # type: (str, List, int, Dict[str, Any]) -> QueryIterable + # type: (str, List, int, Dict[str, Any]) -> Iterable[Dict[str, Any]] """Return all stored procedures matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -197,7 +197,7 @@ def execute_stored_procedure( ) def list_triggers(self, max_item_count=None, feed_options=None): - # type: (int, Dict[str, Any]) -> QueryIterable + # type: (int, Dict[str, Any]) -> Iterable[Dict[str, Any]] """ List all triggers in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -213,7 +213,7 @@ def list_triggers(self, max_item_count=None, feed_options=None): return self.client_connection.ReadTriggers(collection_link=self.container_link, options=feed_options) def query_triggers(self, query, parameters=None, max_item_count=None, feed_options=None): - # type: (str, List, int, Dict[str, Any]) -> QueryIterable + # type: (str, List, int, Dict[str, Any]) -> Iterable[Dict[str, Any]] """Return all triggers matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -307,7 +307,7 @@ def delete_trigger(self, trigger, request_options=None): ) def list_user_defined_functions(self, max_item_count=None, feed_options=None): - # type: (int, Dict[str, Any]) -> QueryIterable + # type: (int, Dict[str, Any]) -> Iterable[Dict[str, Any]] """ List all user defined functions in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -325,7 +325,7 @@ def list_user_defined_functions(self, max_item_count=None, feed_options=None): ) def query_user_defined_functions(self, query, parameters=None, max_item_count=None, feed_options=None): - # type: (str, List, int, Dict[str, Any]) -> QueryIterable + # type: (str, List, int, Dict[str, Any]) -> Iterable[Dict[str, Any]] """Return all user defined functions matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -391,7 +391,8 @@ def replace_user_defined_function(self, udf, body, request_options=None): :param body: A dict-like object representing the udf to replace. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the user defined function after replace went through. - :raise `CosmosHttpResponseError`: If the replace failed or the user defined function with given id does not exist. + :raise `CosmosHttpResponseError`: If the replace failed or the user defined function with + given id does not exist. """ if not request_options: diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py index 6a5feabb2775..3422edbb7594 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py @@ -19,10 +19,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +# pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs + """Create, read, update and delete permissions in the Azure Cosmos DB SQL API service. """ -from typing import Any, List, Dict, Union, cast +from typing import Any, List, Dict, Union, cast, Iterable import six from azure.core.tracing.decorator import distributed_trace @@ -80,7 +82,7 @@ def read(self, request_options=None, response_hook=None, **kwargs): @distributed_trace def read_all_permissions(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (int, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List all permission for the user. :param max_item_count: Max number of permissions to be returned in the enumeration operation. @@ -111,7 +113,7 @@ def query_permissions( response_hook=None, **kwargs ): - # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> QueryIterable + # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """Return all permissions matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. From 44f88af158b39ccccbaaa7503c0ef055626cad9e Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 11:33:15 -0700 Subject: [PATCH 25/46] Renamed all listing operations --- .../azure/cosmos/_synchronized_request.py | 5 +- .../azure/cosmos/container_client.py | 4 +- .../azure/cosmos/cosmos_client.py | 2 +- .../azure/cosmos/database_client.py | 4 +- .../azure-cosmos/azure/cosmos/user_client.py | 2 +- .../samples/CollectionManagement/Program.py | 2 +- .../samples/DatabaseManagement/Program.py | 2 +- .../samples/DocumentManagement/Program.py | 2 +- .../samples/IndexManagement/Program.py | 6 +- sdk/cosmos/azure-cosmos/test/crud_tests.py | 70 +++++++++---------- sdk/cosmos/azure-cosmos/test/orderby_tests.py | 2 +- .../azure-cosmos/test/partition_key_tests.py | 4 +- .../test/query_execution_context_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/utils_tests.py | 2 +- 14 files changed, 55 insertions(+), 54 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index 270ae5c3843f..c616eacf65fe 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -128,7 +128,7 @@ def _Request(global_endpoint_manager, request_params, connection_policy, pipelin connection_timeout=connection_timeout, connection_verify=kwargs.pop("connection_verify", ca_certs), connection_cert=kwargs.pop("connection_cert", cert_files), - + **kwargs ) else: response = pipeline_client._pipeline.run( @@ -136,7 +136,8 @@ def _Request(global_endpoint_manager, request_params, connection_policy, pipelin stream=is_media_stream, connection_timeout=connection_timeout, # If SSL is disabled, verify = false - connection_verify=kwargs.pop("connection_verify", is_ssl_enabled) + connection_verify=kwargs.pop("connection_verify", is_ssl_enabled), + **kwargs ) response = response.http_response diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index ac6516e5e5bb..e6af9f79fe51 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -206,7 +206,7 @@ def read_item( return result @distributed_trace - def read_all_items( + def list_items( self, max_item_count=None, session_token=None, @@ -666,7 +666,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): return Offer(offer_throughput=data["content"]["offerThroughput"], properties=data) @distributed_trace - def read_all_conflicts(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): + def list_conflicts(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List all conflicts in the container. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 6429ab47c1ee..255a6d51e6b9 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -239,7 +239,7 @@ def get_database_client(self, database): return DatabaseClient(self.client_connection, id_value) @distributed_trace - def read_all_databases( + def list_databases( self, max_item_count=None, session_token=None, diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index cdfbe338d164..f829309c78b2 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -310,7 +310,7 @@ def get_container_client(self, container): return ContainerClient(self.client_connection, self.database_link, id_value) @distributed_trace - def read_all_containers( + def list_containers( self, max_item_count=None, session_token=None, @@ -490,7 +490,7 @@ def replace_container( ) @distributed_trace - def read_all_users(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): + def list_users(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List all users in the container. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py index 3422edbb7594..7eee49755316 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py @@ -81,7 +81,7 @@ def read(self, request_options=None, response_hook=None, **kwargs): return self._properties @distributed_trace - def read_all_permissions(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): + def list_permissions(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] """ List all permission for the user. diff --git a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py index 0217f2afe900..0683e7171f9d 100644 --- a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py @@ -221,7 +221,7 @@ def list_Containers(db): print('Containers:') - containers = list(db.read_all_containers()) + containers = list(db.list_containers()) if not containers: return diff --git a/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py index c01013ce73aa..f1535c93961b 100644 --- a/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/DatabaseManagement/Program.py @@ -88,7 +88,7 @@ def list_databases(client): print('Databases:') - databases = list(client.read_all_databases()) + databases = list(client.list_databases()) if not databases: return diff --git a/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py index 31f2953f3dae..891317c3ff02 100644 --- a/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py @@ -71,7 +71,7 @@ def ReadItems(container): # NOTE: Use MaxItemCount on Options to control how many items come back per trip to the server # Important to handle throttles whenever you are doing operations such as this that might # result in a 429 (throttled request) - item_list = list(container.read_all_items(max_item_count=10)) + item_list = list(container.list_items(max_item_count=10)) print('Found {0} items'.format(item_list.__len__())) diff --git a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py index 776be548d214..207279152d73 100644 --- a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py @@ -57,19 +57,19 @@ def Query_Entities(parent, entity_type, id = None): try: if entity_type == 'database': if id == None: - entities = list(parent.read_all_databases()) + entities = list(parent.list_databases()) else: entities = list(parent.query_databases(find_entity_by_id_query)) elif entity_type == 'collection': if id == None: - entities = list(parent.read_all_containers()) + entities = list(parent.list_containers()) else: entities = list(parent.query_containers(find_entity_by_id_query)) elif entity_type == 'document': if id == None: - entities = list(parent.read_all_items()) + entities = list(parent.list_items()) else: entities = list(parent.query_items(find_entity_by_id_query)) except errors.AzureError as e: diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 6d87734dd2e4..cf2685cdae79 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -103,14 +103,14 @@ def setUp(self): connection_policy=self.connectionPolicy) def test_database_crud(self): # read databases. - databases = list(self.client.read_all_databases()) + databases = list(self.client.list_databases()) # create a database. before_create_databases_count = len(databases) database_id = str(uuid.uuid4()) created_db = self.client.create_database(database_id) self.assertEqual(created_db.id, database_id) # Read databases after creation. - databases = list(self.client.read_all_databases()) + databases = list(self.client.list_databases()) self.assertEqual(len(databases), before_create_databases_count + 1, 'create should increase the number of databases') @@ -184,7 +184,7 @@ def test_sql_query_crud(self): def test_collection_crud(self): created_db = self.databaseForTest - collections = list(created_db.read_all_containers()) + collections = list(created_db.list_containers()) # create a collection before_create_collections_count = len(collections) collection_id = 'test_collection_crud ' + str(uuid.uuid4()) @@ -204,7 +204,7 @@ def test_collection_crud(self): self.assertEqual('consistent', created_properties['indexingPolicy']['indexingMode']) # read collections after creation - collections = list(created_db.read_all_containers()) + collections = list(created_db.list_containers()) self.assertEqual(len(collections), before_create_collections_count + 1, 'create should increase the number of collections') @@ -436,7 +436,7 @@ def test_partitioned_collection_document_crud_and_query(self): self.assertEqual(read_document.get('key'), created_document.get('key')) # Read document feed doesn't require partitionKey as it's always a cross partition query - documentlist = list(created_collection.read_all_items()) + documentlist = list(created_collection.list_items()) self.assertEqual(1, len(documentlist)) # replace document @@ -458,7 +458,7 @@ def test_partitioned_collection_document_crud_and_query(self): self.assertEqual(upserted_document.get('id'), document_definition.get('id')) self.assertEqual(upserted_document.get('key'), document_definition.get('key')) - documentlist = list(created_collection.read_all_items()) + documentlist = list(created_collection.list_items()) self.assertEqual(2, len(documentlist)) # delete document @@ -692,7 +692,7 @@ def test_partitioned_collection_conflict_crud_and_query(self): ) # Read conflict feed doesn't requires partitionKey to be specified as it's a cross partition thing - conflictlist = list(created_collection.read_all_conflicts()) + conflictlist = list(created_collection.list_conflicts()) self.assertEqual(0, len(conflictlist)) # delete conflict here will return resource not found(404) since there is no conflict here @@ -734,7 +734,7 @@ def test_document_crud(self): # create collection created_collection = self.configs.create_multi_partition_collection_if_not_exist(self.client) # read documents - documents = list(created_collection.read_all_items()) + documents = list(created_collection.list_items()) # create a document before_create_documents_count = len(documents) @@ -755,7 +755,7 @@ def test_document_crud(self): created_collection.create_item, duplicated_definition_with_id) # read documents after creation - documents = list(created_collection.read_all_items()) + documents = list(created_collection.list_items()) self.assertEqual( len(documents), before_create_documents_count + 1, @@ -907,7 +907,7 @@ def test_document_upsert(self): created_collection = self.configs.create_multi_partition_collection_if_not_exist(self.client) # read documents and check count - documents = list(created_collection.read_all_items()) + documents = list(created_collection.list_items()) before_create_documents_count = len(documents) # create document definition @@ -924,7 +924,7 @@ def test_document_upsert(self): document_definition['id']) # read documents after creation and verify updated count - documents = list(created_collection.read_all_items()) + documents = list(created_collection.list_items()) self.assertEqual( len(documents), before_create_documents_count + 1, @@ -951,7 +951,7 @@ def test_document_upsert(self): 'document id should stay the same') # read documents after upsert and verify count doesn't increases again - documents = list(created_collection.read_all_items()) + documents = list(created_collection.list_items()) self.assertEqual( len(documents), before_create_documents_count + 1, @@ -968,7 +968,7 @@ def test_document_upsert(self): 'document id should be same') # read documents after upsert and verify count increases - documents = list(created_collection.read_all_items()) + documents = list(created_collection.list_items()) self.assertEqual( len(documents), before_create_documents_count + 2, @@ -979,7 +979,7 @@ def test_document_upsert(self): created_collection.delete_item(item=new_document, partition_key=new_document['id']) # read documents after delete and verify count is same as original - documents = list(created_collection.read_all_items()) + documents = list(created_collection.list_items()) self.assertEqual( len(documents), before_create_documents_count, @@ -1042,14 +1042,14 @@ def test_user_crud(self): # create database db = self.databaseForTest # list users - users = list(db.read_all_users()) + users = list(db.list_users()) before_create_count = len(users) # create user user_id = 'new user' + str(uuid.uuid4()) user = db.create_user(body={'id': user_id}) self.assertEqual(user.id, user_id, 'user id error') # list users after creation - users = list(db.read_all_users()) + users = list(db.list_users()) self.assertEqual(len(users), before_create_count + 1) # query users results = list(db.query_users( @@ -1086,7 +1086,7 @@ def test_user_upsert(self): db = self.databaseForTest # read users and check count - users = list(db.read_all_users()) + users = list(db.list_users()) before_create_count = len(users) # create user using Upsert API @@ -1097,7 +1097,7 @@ def test_user_upsert(self): self.assertEqual(user.id, user_id, 'user id error') # read users after creation and verify updated count - users = list(db.read_all_users()) + users = list(db.list_users()) self.assertEqual(len(users), before_create_count + 1) # Should replace the user since it already exists, there is no public property to change here @@ -1110,7 +1110,7 @@ def test_user_upsert(self): 'user id should remain same') # read users after upsert and verify count doesn't increases again - users = list(db.read_all_users()) + users = list(db.list_users()) self.assertEqual(len(users), before_create_count + 1) user_properties = user.read() @@ -1124,7 +1124,7 @@ def test_user_upsert(self): self.assertEqual(new_user.id, user.id, 'user id error') # read users after upsert and verify count increases - users = list(db.read_all_users()) + users = list(db.list_users()) self.assertEqual(len(users), before_create_count + 2) # delete users @@ -1132,7 +1132,7 @@ def test_user_upsert(self): db.delete_user(new_user.id) # read users after delete and verify count remains the same - users = list(db.read_all_users()) + users = list(db.list_users()) self.assertEqual(len(users), before_create_count) def test_permission_crud(self): @@ -1142,7 +1142,7 @@ def test_permission_crud(self): # create user user = db.create_user(body={'id': 'new user' + str(uuid.uuid4())}) # list permissions - permissions = list(user.read_all_permissions()) + permissions = list(user.list_permissions()) before_create_count = len(permissions) permission = { 'id': 'new permission', @@ -1155,7 +1155,7 @@ def test_permission_crud(self): 'new permission', 'permission id error') # list permissions after creation - permissions = list(user.read_all_permissions()) + permissions = list(user.list_permissions()) self.assertEqual(len(permissions), before_create_count + 1) # query permissions results = list(user.query_permissions( @@ -1195,7 +1195,7 @@ def test_permission_upsert(self): user = db.create_user(body={'id': 'new user' + str(uuid.uuid4())}) # read permissions and check count - permissions = list(user.read_all_permissions()) + permissions = list(user.list_permissions()) before_create_count = len(permissions) permission_definition = { @@ -1213,7 +1213,7 @@ def test_permission_upsert(self): 'permission id error') # read permissions after creation and verify updated count - permissions = list(user.read_all_permissions()) + permissions = list(user.list_permissions()) self.assertEqual(len(permissions), before_create_count + 1) # update permission mode @@ -1232,7 +1232,7 @@ def test_permission_upsert(self): 'permissionMode should change') # read permissions and verify count doesn't increases again - permissions = list(user.read_all_permissions()) + permissions = list(user.list_permissions()) self.assertEqual(len(permissions), before_create_count + 1) # update permission id @@ -1255,7 +1255,7 @@ def test_permission_upsert(self): 'permission resource should be same') # read permissions and verify count increases - permissions = list(user.read_all_permissions()) + permissions = list(user.list_permissions()) self.assertEqual(len(permissions), before_create_count + 2) # delete permissions @@ -1263,7 +1263,7 @@ def test_permission_upsert(self): user.delete_permission(new_permission.id) # read permissions and verify count remains the same - permissions = list(user.read_all_permissions()) + permissions = list(user.list_permissions()) self.assertEqual(len(permissions), before_create_count) def test_authorization(self): @@ -1352,7 +1352,7 @@ def __SetupEntities(client): client = cosmos_client.CosmosClient(CRUDTests.host, {}, "Session", connection_policy=CRUDTests.connectionPolicy) self.__AssertHTTPFailureWithStatus(StatusCodes.UNAUTHORIZED, list, - client.read_all_databases()) + client.list_databases()) # Client with master key. client = cosmos_client.CosmosClient(CRUDTests.host, CRUDTests.masterKey, @@ -1378,7 +1378,7 @@ def __SetupEntities(client): db.delete_container, success_coll1) # 3. Success-- Use Col1 Permission to Read All Docs - success_documents = list(success_coll1.read_all_items()) + success_documents = list(success_coll1.list_items()) self.assertTrue(success_documents != None, 'error reading documents') self.assertEqual(len(success_documents), @@ -1982,7 +1982,7 @@ def __create_resources(client): # Validate QueryIterable by converting it to a list. resources = __create_resources(self.client) - results = resources['coll'].read_all_items(max_item_count=2) + results = resources['coll'].list_items(max_item_count=2) docs = list(iter(results)) self.assertEqual(3, len(docs), @@ -1993,7 +1993,7 @@ def __create_resources(client): self.assertEqual(resources['doc3']['id'], docs[2]['id']) # Validate QueryIterable iterator with 'for'. - results = resources['coll'].read_all_items(max_item_count=2) + results = resources['coll'].list_items(max_item_count=2) counter = 0 # test QueryIterable with 'for'. for doc in iter(results): @@ -2013,7 +2013,7 @@ def __create_resources(client): self.assertEqual(counter, 3) # Get query results page by page. - results = resources['coll'].read_all_items(max_item_count=2) + results = resources['coll'].list_items(max_item_count=2) page_iter = results.by_page() first_block = list(next(page_iter)) self.assertEqual(2, len(first_block), 'First block should have 2 entries.') @@ -2385,7 +2385,7 @@ def test_id_case_validation(self): collection_id2 = 'SampleCollection ' + uuid_string # Verify that no collections exist - collections = list(created_db.read_all_containers()) + collections = list(created_db.list_containers()) number_of_existing_collections = len(collections) # create 2 collections with different casing of IDs @@ -2401,7 +2401,7 @@ def test_id_case_validation(self): partition_key=PartitionKey(path='/id', kind='Hash') ) - collections = list(created_db.read_all_containers()) + collections = list(created_db.list_containers()) # verify if a total of 2 collections got created self.assertEqual(len(collections), number_of_existing_collections + 2) diff --git a/sdk/cosmos/azure-cosmos/test/orderby_tests.py b/sdk/cosmos/azure-cosmos/test/orderby_tests.py index ba1eb99b3c17..83d8e41c17c4 100644 --- a/sdk/cosmos/azure-cosmos/test/orderby_tests.py +++ b/sdk/cosmos/azure-cosmos/test/orderby_tests.py @@ -94,7 +94,7 @@ def setUp(self): self.assertGreaterEqual(len(partition_key_ranges), 5) # sanity check: read documents after creation - queried_docs = list(self.created_collection.read_all_items()) + queried_docs = list(self.created_collection.list_items()) self.assertEqual( len(queried_docs), len(self.document_definitions), diff --git a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py index 026b1f30ae56..8684b6357915 100644 --- a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py +++ b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py @@ -169,7 +169,7 @@ def test_non_partitioned_collection_operations(self): self.assertEqual(result, 1) # 3 previous items + 1 created from the sproc - items = list(created_container.read_all_items()) + items = list(created_container.list_items()) self.assertEqual(len(items), 4) created_container.delete_item(upserted_item['id'], partition_key=partition_key.NonePartitionKeyValue) @@ -177,7 +177,7 @@ def test_non_partitioned_collection_operations(self): created_container.delete_item(document_created_by_sproc_id, partition_key=partition_key.NonePartitionKeyValue) created_container.delete_item(self.created_document['id'], partition_key=partition_key.NonePartitionKeyValue) - items = list(created_container.read_all_items()) + items = list(created_container.list_items()) self.assertEqual(len(items), 0) def test_multi_partition_collection_read_document_with_no_pk(self): diff --git a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py index 9b73a4f738d9..f22755f0c84f 100644 --- a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py @@ -85,7 +85,7 @@ def setUp(self): self.assertGreaterEqual(len(partition_key_ranges), 1) # sanity check: read documents after creation - queried_docs = list(self.created_collection.read_all_items()) + queried_docs = list(self.created_collection.list_items()) self.assertEqual( len(queried_docs), len(self.document_definitions), diff --git a/sdk/cosmos/azure-cosmos/test/utils_tests.py b/sdk/cosmos/azure-cosmos/test/utils_tests.py index c0dda2c443da..589878fee7b2 100644 --- a/sdk/cosmos/azure-cosmos/test/utils_tests.py +++ b/sdk/cosmos/azure-cosmos/test/utils_tests.py @@ -46,7 +46,7 @@ def test_user_agent(self): def test_connection_string(self): client = azure.cosmos.CosmosClient.from_connection_string(test_config._test_config.connection_str) - databases = list(client.read_all_databases()) + databases = list(client.list_databases()) assert len(databases) > 0 assert isinstance(databases[0], dict) assert databases[0].get('_etag') is not None From c1053ee462c796f14172266c1f3b88b31819d4e9 Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 11:58:29 -0700 Subject: [PATCH 26/46] Some mypy fixes --- .../azure/cosmos/container_client.py | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index e6af9f79fe51..cc3710b93d96 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -102,16 +102,16 @@ def _get_conflict_link(self, conflict_or_link): @distributed_trace def read( self, - session_token=None, - initial_headers=None, - populate_query_metrics=None, - populate_partition_key_range_statistics=None, - populate_quota_info=None, - request_options=None, - response_hook=None, - **kwargs + session_token=None, # type: Optional[str] + initial_headers=None, # type: Optional[Dict[str, str]] + populate_query_metrics=None, # type: Optional[bool] + populate_partition_key_range_statistics=None, # type: Optional[bool] + populate_quota_info=None, # type: Optional[bool] + request_options=None, # type: Optional[Dict[str, Any]] + response_hook=None, # type: Optional[Callable] + **kwargs # type: Any ): - # type: (str, Dict[str, str], bool, bool, bool, Dict[str, Any], Optional[Callable]) -> Container + # type: (...) -> Container """ Read the container properties :param session_token: Token for use with Session consistency. @@ -153,13 +153,13 @@ def read_item( self, item, # type: Union[str, Dict[str, Any]] partition_key, # type: Any - session_token=None, # type: str - initial_headers=None, # type: # type: Dict[str, str] - populate_query_metrics=None, # type: bool - post_trigger_include=None, # type: str - request_options=None, # type: Dict[str, Any] + session_token=None, # type: Optional[str] + initial_headers=None, # type: Optional[Dict[str, str]] + populate_query_metrics=None, # type: Optional[bool] + post_trigger_include=None, # type: Optioanl[str] + request_options=None, # type: Optional[Dict[str, Any]] response_hook=None, # type: Optional[Callable] - **kwargs + **kwargs # type: Any ): # type: (...) -> Dict[str, str] """ @@ -208,15 +208,15 @@ def read_item( @distributed_trace def list_items( self, - max_item_count=None, - session_token=None, - initial_headers=None, - populate_query_metrics=None, - feed_options=None, - response_hook=None, - **kwargs + max_item_count=None, # type: Optional[int] + session_token=None, # type: Optional[str] + initial_headers=None, # type: Optional[Dict[str, str]] + populate_query_metrics=None, # type: Optional[bool] + feed_options=None, # type: Optional[Dict[str, Any]] + response_hook=None, # type: Optional[Callable] + **kwargs # type: Any ): - # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + # type: (...) -> Iterable[Dict[str, Any]] """ List all items in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -299,17 +299,17 @@ def query_items_change_feed( def query_items( self, query, # type: str - parameters=None, # type: List - partition_key=None, # type: Any - enable_cross_partition_query=None, # type: bool - max_item_count=None, # type: int - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - enable_scan_in_query=None, # type: bool - populate_query_metrics=None, # type: bool - feed_options=None, # type: Dict[str, Any] + parameters=None, # type: Optional[List[str]] + partition_key=None, # type: Optional[Any] + enable_cross_partition_query=None, # type: Optional[bool] + max_item_count=None, # type: Optional[int] + session_token=None, # type: Optional[str] + initial_headers=None, # type: Optional[Dict[str, str]] + enable_scan_in_query=None, # type: Optional[bool] + populate_query_metrics=None, # type: Optional[bool] + feed_options=None, # type: Optional[Dict[str, Any]] response_hook=None, # type: Optional[Callable] - **kwargs + **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] """Return all results matching the given `query`. @@ -396,7 +396,7 @@ def replace_item( post_trigger_include=None, # type: str request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] - **kwargs + **kwargs # type: Any ): # type: (...) -> Dict[str, str] """ Replaces the specified item if it exists in the container. @@ -451,7 +451,7 @@ def upsert_item( post_trigger_include=None, # type: str request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] - **kwargs + **kwargs # type: Any ): # type: (...) -> Dict[str, str] """ Insert or update the specified item. @@ -506,7 +506,7 @@ def create_item( indexing_directive=None, # type: Any request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] - **kwargs + **kwargs # type: Any ): # type: (...) -> Dict[str, str] """ Create an item in the container. @@ -566,7 +566,7 @@ def delete_item( post_trigger_include=None, # type: str request_options=None, # type: Dict[str, Any] response_hook=None, # type: Optional[Callable] - **kwargs + **kwargs # type: Any ): # type: (...) -> None """ Delete the specified item from the container. @@ -609,7 +609,7 @@ def delete_item( @distributed_trace def read_offer(self, response_hook=None, **kwargs): - # type: (Optional[Callable]) -> Offer + # type: (Optional[Callable], Any) -> Offer """ Read the Offer object for this container. :param response_hook: a callable invoked with the response metadata @@ -636,7 +636,7 @@ def read_offer(self, response_hook=None, **kwargs): @distributed_trace def replace_throughput(self, throughput, response_hook=None, **kwargs): - # type: (int, Optional[Callable]) -> Offer + # type: (int, Optional[Callable], Any) -> Offer """ Replace the container's throughput :param throughput: The throughput to be set (an integer). @@ -667,7 +667,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): @distributed_trace def list_conflicts(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + # type: (Optional[int], Optional[Dict[str, Any]], Optional[Callable], Any) -> Iterable[Dict[str, Any]] """ List all conflicts in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -691,16 +691,16 @@ def list_conflicts(self, max_item_count=None, feed_options=None, response_hook=N @distributed_trace def query_conflicts( self, - query, - parameters=None, - enable_cross_partition_query=None, - partition_key=None, - max_item_count=None, - feed_options=None, - response_hook=None, - **kwargs + query, # type: str + parameters=None, # type: Optional[List[str]] + enable_cross_partition_query=None, # type: Optional[bool] + partition_key=None, # type: Optional[Any] + max_item_count=None, # type: Optional[int] + feed_options=None, # type: Optional[Dict[str, Any]] + response_hook=None, # type: Optional[Callable] + **kwargs # type: Any ): - # type: (str, List, bool, Any, int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + # type: (...) -> Iterable[Dict[str, Any]] """Return all conflicts matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -736,7 +736,7 @@ def query_conflicts( @distributed_trace def get_conflict(self, conflict, partition_key, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable]) -> Dict[str, str] + # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable], Any) -> Dict[str, str] """ Get the conflict identified by `id`. :param conflict: The ID (name) or dict representing the conflict to retrieve. @@ -761,7 +761,7 @@ def get_conflict(self, conflict, partition_key, request_options=None, response_h @distributed_trace def delete_conflict(self, conflict, partition_key, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable]) -> None + # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable], Any) -> None """ Delete the specified conflict from the container. :param conflict: The ID (name) or dict representing the conflict to be deleted. From 44fb172ed3392cac3afb002eac7964b65645cc78 Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 16:00:27 -0700 Subject: [PATCH 27/46] Cleaned up method signatures --- sdk/cosmos/azure-cosmos/azure/cosmos/_base.py | 39 ++++ .../azure/cosmos/container_client.py | 191 ++++++------------ .../azure/cosmos/cosmos_client.py | 88 +++----- .../azure/cosmos/database_client.py | 183 ++++++----------- .../azure/cosmos/scripts_client.py | 186 ++++++++--------- .../azure-cosmos/azure/cosmos/user_client.py | 67 +++--- sdk/cosmos/azure-cosmos/test/crud_tests.py | 4 +- 7 files changed, 310 insertions(+), 448 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py index 148e73a3a16c..eb79c4bcc5fe 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py @@ -39,6 +39,45 @@ # pylint: disable=protected-access +_COMMON_OPTIONS = { + 'initial_headers': 'initialHeaders', + 'pre_trigger_include': 'preTriggerInclude', + 'post_trigger_include': 'postTriggerInclude', + 'max_item_count': 'maxItemCount', + 'access_condition': 'accessCondition', + 'indexing_directive': 'indexingDirective', + 'consistency_level': 'consistencyLevel', + 'session_token': 'sessionToken', + 'enable_scan_in_query': 'enableScanInQuery', + 'resource_token_expiry_seconds': 'resourceTokenExpirySeconds', + 'offer_type': 'offerType', + 'offer_throughput': 'offerThroughput', + 'partition_key': 'partitionKey', + 'enable_cross_partition_query': 'enableCrossPartitionQuery', + 'populate_query_metrics': 'populateQueryMetrics', + 'enable_script_logging': 'enableScriptLogging', + 'offer_enable_ru_per_minute_throughput': 'offerEnableRUPerMinuteThroughput', + 'disable_ru_per_minute_usage': 'disableRUPerMinuteUsage', + 'change_feed': 'changeFeed', + 'continuation': 'continuation', + 'is_start_from_beginning': 'isStartFromBeginning', + 'populate_partition_key_range_statistics': 'populatePartitionKeyRangeStatistics', + 'populate_quota_info': 'populateQuotaInfo' +} + +def build_options(kwargs): + # type: (Dict[str, Any]) -> Dict[str, Any] + options = kwargs.pop('request_options', None) or kwargs.pop('feed_options', {}) + for key, value in _COMMON_OPTIONS.items(): + if key in kwargs: + options[value] = kwargs.pop(key) + + if 'if_match' in kwargs: + options['accessCondition'] = {'type': 'IfMatch', 'condition': kwargs.pop('if_match')} + if 'if_none_match' in kwargs: + options['accessCondition'] = {'type': 'IfNoneMatch', 'condition': kwargs.pop('if_none_match')} + return options + def GetHeaders( # pylint: disable=too-many-statements,too-many-branches cosmos_client_connection, diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index cc3710b93d96..fda5a440664b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -28,6 +28,7 @@ from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection +from ._base import build_options from .errors import CosmosResourceNotFoundError from .http_constants import StatusCodes from .offer import Offer @@ -99,16 +100,17 @@ def _get_conflict_link(self, conflict_or_link): return u"{}/conflicts/{}".format(self.container_link, conflict_or_link) return conflict_or_link["_self"] + def _set_partition_key(self, partition_key): + if partition_key == NonePartitionKeyValue: + return CosmosClientConnection._return_undefined_or_empty_partition_key(self.is_system_key) + return partition_key + @distributed_trace def read( self, - session_token=None, # type: Optional[str] - initial_headers=None, # type: Optional[Dict[str, str]] populate_query_metrics=None, # type: Optional[bool] populate_partition_key_range_statistics=None, # type: Optional[bool] populate_quota_info=None, # type: Optional[bool] - request_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> Container @@ -127,12 +129,8 @@ def read( :returns: :class:`Container` instance representing the retrieved container. """ - if not request_options: - request_options = {} # type: Dict[str, Any] - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics if populate_partition_key_range_statistics is not None: @@ -153,12 +151,8 @@ def read_item( self, item, # type: Union[str, Dict[str, Any]] partition_key, # type: Any - session_token=None, # type: Optional[str] - initial_headers=None, # type: Optional[Dict[str, str]] populate_query_metrics=None, # type: Optional[bool] post_trigger_include=None, # type: Optioanl[str] - request_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> Dict[str, str] @@ -186,15 +180,11 @@ def read_item( """ doc_link = self._get_document_link(item) + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) - if not request_options: - request_options = {} # type: Dict[str, Any] if partition_key: request_options["partitionKey"] = self._set_partition_key(partition_key) - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics if post_trigger_include: @@ -209,11 +199,7 @@ def read_item( def list_items( self, max_item_count=None, # type: Optional[int] - session_token=None, # type: Optional[str] - initial_headers=None, # type: Optional[Dict[str, str]] populate_query_metrics=None, # type: Optional[bool] - feed_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] @@ -227,14 +213,10 @@ def list_items( :param response_hook: a callable invoked with the response metadata :returns: An Iterable of items (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - if session_token: - feed_options["sessionToken"] = session_token - if initial_headers: - feed_options["initialHeaders"] = initial_headers if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics @@ -255,8 +237,6 @@ def query_items_change_feed( is_start_from_beginning=False, # type: bool continuation=None, # type: Optional[str] max_item_count=None, # type: Optional[int] - feed_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] @@ -274,8 +254,8 @@ def query_items_change_feed( :returns: An Iterable of items (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if partition_key_range_id is not None: feed_options["partitionKeyRangeId"] = partition_key_range_id if is_start_from_beginning is not None: @@ -303,12 +283,8 @@ def query_items( partition_key=None, # type: Optional[Any] enable_cross_partition_query=None, # type: Optional[bool] max_item_count=None, # type: Optional[int] - session_token=None, # type: Optional[str] - initial_headers=None, # type: Optional[Dict[str, str]] enable_scan_in_query=None, # type: Optional[bool] populate_query_metrics=None, # type: Optional[bool] - feed_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] @@ -351,16 +327,12 @@ def query_items( :name: query_items_param """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if enable_cross_partition_query is not None: feed_options["enableCrossPartitionQuery"] = enable_cross_partition_query if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - if session_token: - feed_options["sessionToken"] = session_token - if initial_headers: - feed_options["initialHeaders"] = initial_headers if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics if partition_key is not None: @@ -388,14 +360,9 @@ def replace_item( self, item, # type: Union[str, Dict[str, Any]] body, # type: Dict[str, Any] - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - pre_trigger_include=None, # type: str - post_trigger_include=None, # type: str - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] + populate_query_metrics=None, # type: Optional[bool] + pre_trigger_include=None, # type: Optional[str] + post_trigger_include=None, # type: Optional[str] **kwargs # type: Any ): # type: (...) -> Dict[str, str] @@ -416,15 +383,9 @@ def replace_item( """ item_link = self._get_document_link(item) - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) request_options["disableIdGeneration"] = True - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics if pre_trigger_include: @@ -443,14 +404,9 @@ def replace_item( def upsert_item( self, body, # type: Dict[str, Any] - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - pre_trigger_include=None, # type: str - post_trigger_include=None, # type: str - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] + populate_query_metrics=None, # type: Optional[bool] + pre_trigger_include=None, # type: Optional[str] + post_trigger_include=None, # type: Optional[str] **kwargs # type: Any ): # type: (...) -> Dict[str, str] @@ -471,15 +427,9 @@ def upsert_item( If the item already exists in the container, it is replaced. If it does not, it is inserted. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) request_options["disableIdGeneration"] = True - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics if pre_trigger_include: @@ -497,15 +447,10 @@ def upsert_item( def create_item( self, body, # type: Dict[str, Any] - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - pre_trigger_include=None, # type: str - post_trigger_include=None, # type: str - indexing_directive=None, # type: Any - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] + populate_query_metrics=None, # type: Optional[bool] + pre_trigger_include=None, # type: Optional[str] + post_trigger_include=None, # type: Optional[str] + indexing_directive=None, # type: Optional[Any] **kwargs # type: Any ): # type: (...) -> Dict[str, str] @@ -527,16 +472,10 @@ def create_item( To update or replace an existing item, use the :func:`Container.upsert_item` method. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) request_options["disableAutomaticIdGeneration"] = True - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition if populate_query_metrics: request_options["populateQueryMetrics"] = populate_query_metrics if pre_trigger_include: @@ -558,14 +497,9 @@ def delete_item( self, item, # type: Union[Dict[str, Any], str] partition_key, # type: Any - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - pre_trigger_include=None, # type: str - post_trigger_include=None, # type: str - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] + populate_query_metrics=None, # type: Optional[bool] + pre_trigger_include=None, # type: Optional[str] + post_trigger_include=None, # type: Optional[str] **kwargs # type: Any ): # type: (...) -> None @@ -585,16 +519,10 @@ def delete_item( exist in the container, a `404` error is returned. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if partition_key: request_options["partitionKey"] = self._set_partition_key(partition_key) - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics if pre_trigger_include: @@ -608,8 +536,8 @@ def delete_item( response_hook(self.client_connection.last_response_headers, result) @distributed_trace - def read_offer(self, response_hook=None, **kwargs): - # type: (Optional[Callable], Any) -> Offer + def read_offer(self, **kwargs): + # type: (Any) -> Offer """ Read the Offer object for this container. :param response_hook: a callable invoked with the response metadata @@ -617,6 +545,7 @@ def read_offer(self, response_hook=None, **kwargs): :raise CosmosHttpResponseError: If no offer exists for the container or if the offer could not be retrieved. """ + response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() link = properties["_self"] query_spec = { @@ -635,8 +564,8 @@ def read_offer(self, response_hook=None, **kwargs): return Offer(offer_throughput=offers[0]["content"]["offerThroughput"], properties=offers[0]) @distributed_trace - def replace_throughput(self, throughput, response_hook=None, **kwargs): - # type: (int, Optional[Callable], Any) -> Offer + def replace_throughput(self, throughput, **kwargs): + # type: (int, Any) -> Offer """ Replace the container's throughput :param throughput: The throughput to be set (an integer). @@ -645,6 +574,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): :raise CosmosHttpResponseError: If no offer exists for the container or if the offer could not be updated. """ + response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() link = properties["_self"] query_spec = { @@ -666,8 +596,8 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): return Offer(offer_throughput=data["content"]["offerThroughput"], properties=data) @distributed_trace - def list_conflicts(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (Optional[int], Optional[Dict[str, Any]], Optional[Callable], Any) -> Iterable[Dict[str, Any]] + def list_conflicts(self, max_item_count=None, **kwargs): + # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] """ List all conflicts in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -676,8 +606,8 @@ def list_conflicts(self, max_item_count=None, feed_options=None, response_hook=N :returns: An Iterable of conflicts (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -696,8 +626,6 @@ def query_conflicts( enable_cross_partition_query=None, # type: Optional[bool] partition_key=None, # type: Optional[Any] max_item_count=None, # type: Optional[int] - feed_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] @@ -715,8 +643,8 @@ def query_conflicts( :returns: An Iterable of conflicts (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count if enable_cross_partition_query is not None: @@ -735,8 +663,8 @@ def query_conflicts( return result @distributed_trace - def get_conflict(self, conflict, partition_key, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable], Any) -> Dict[str, str] + def get_conflict(self, conflict, partition_key, **kwargs): + # type: (Union[str, Dict[str, Any]], Any, Any) -> Dict[str, str] """ Get the conflict identified by `id`. :param conflict: The ID (name) or dict representing the conflict to retrieve. @@ -747,8 +675,8 @@ def get_conflict(self, conflict, partition_key, request_options=None, response_h :raise `CosmosHttpResponseError`: If the given conflict couldn't be retrieved. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if partition_key: request_options["partitionKey"] = self._set_partition_key(partition_key) @@ -760,8 +688,8 @@ def get_conflict(self, conflict, partition_key, request_options=None, response_h return result @distributed_trace - def delete_conflict(self, conflict, partition_key, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, Dict[str, Any]], Any, Dict[str, Any], Optional[Callable], Any) -> None + def delete_conflict(self, conflict, partition_key, **kwargs): + # type: (Union[str, Dict[str, Any]], Any, Any) -> None """ Delete the specified conflict from the container. :param conflict: The ID (name) or dict representing the conflict to be deleted. @@ -772,8 +700,8 @@ def delete_conflict(self, conflict, partition_key, request_options=None, respons does not exist in the container, a `404` error is returned. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if partition_key: request_options["partitionKey"] = self._set_partition_key(partition_key) @@ -782,8 +710,3 @@ def delete_conflict(self, conflict, partition_key, request_options=None, respons ) if response_hook: response_hook(self.client_connection.last_response_headers, result) - - def _set_partition_key(self, partition_key): - if partition_key == NonePartitionKeyValue: - return CosmosClientConnection._return_undefined_or_empty_partition_key(self.is_system_key) - return partition_key diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 255a6d51e6b9..e400bd238215 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -28,6 +28,7 @@ from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection +from ._base import build_options from .database_client import DatabaseClient from .documents import ConnectionPolicy, DatabaseAccount @@ -35,6 +36,7 @@ def _parse_connection_str(conn_str, credential): + # type: (str, Optional[Any]) -> Dict[str, str] conn_str = conn_str.rstrip(";") conn_settings = dict( # pylint: disable=consider-using-dict-comprehension [s.split("=", 1) for s in conn_str.split(";")] @@ -47,6 +49,7 @@ def _parse_connection_str(conn_str, credential): def _build_auth(credential): + # type: (Any) -> Dict[str, Any] auth = {} if isinstance(credential, six.string_types): auth['masterKey'] = credential @@ -64,6 +67,7 @@ def _build_auth(credential): def _build_connection_policy(kwargs): + # type: (Dict[str, Any]) -> ConnectionPolicy # pylint: disable=protected-access policy = kwargs.pop('connection_policy', None) or ConnectionPolicy() @@ -168,13 +172,8 @@ def _get_database_link(database_or_id): def create_database( # pylint: disable=redefined-builtin self, id, # type: str - session_token=None, # type: Optional[str] - initial_headers=None, # type: Optional[Dict[str, str]] - access_condition=None, # type: Optional[Dict[str, str]] populate_query_metrics=None, # type: Optional[bool] offer_throughput=None, # type: Optional[int] - request_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> DatabaseClient @@ -201,14 +200,8 @@ def create_database( # pylint: disable=redefined-builtin """ - if not request_options: - request_options = {} # type: Dict[str, Any] - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics if offer_throughput is not None: @@ -241,15 +234,11 @@ def get_database_client(self, database): @distributed_trace def list_databases( self, - max_item_count=None, - session_token=None, - initial_headers=None, - populate_query_metrics=None, - feed_options=None, - response_hook=None, - **kwargs + max_item_count=None, # type: Optional[int] + populate_query_metrics=None, # type: Optional[bool] + **kwargs # type: Any ): - # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + # type: (...) -> Iterable[Dict[str, Any]] """ List the databases in a Cosmos DB SQL database account. @@ -262,14 +251,10 @@ def list_databases( :returns: An Iterable of database properties (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - if session_token: - feed_options["sessionToken"] = session_token - if initial_headers: - feed_options["initialHeaders"] = initial_headers if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics @@ -281,19 +266,14 @@ def list_databases( @distributed_trace def query_databases( self, - query=None, # type: str - parameters=None, # type: List[str] - enable_cross_partition_query=None, # type: bool - max_item_count=None, # type: int - session_token=None, # type: str - initial_headers=None, # type: Dict[str,str] - populate_query_metrics=None, # type: bool - feed_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] - **kwargs + query=None, # type: Optional[str] + parameters=None, # type: Optional[List[str]] + enable_cross_partition_query=None, # type: Optional[bool] + max_item_count=None, # type: Optional[int] + populate_query_metrics=None, # type: Optional[bool] + **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] - """ Query the databases in a Cosmos DB SQL database account. @@ -310,16 +290,12 @@ def query_databases( :returns: An Iterable of database properties (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if enable_cross_partition_query is not None: feed_options["enableCrossPartitionQuery"] = enable_cross_partition_query if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - if session_token: - feed_options["sessionToken"] = session_token - if initial_headers: - feed_options["initialHeaders"] = initial_headers if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics @@ -341,13 +317,8 @@ def query_databases( def delete_database( self, database, # type: Union[str, DatabaseClient, Dict[str, Any]] - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] - **kwargs + populate_query_metrics=None, # type: Optional[bool] + **kwargs # type: Any ): # type: (...) -> None """ @@ -364,14 +335,8 @@ def delete_database( :raise CosmosHttpResponseError: If the database couldn't be deleted. """ - if not request_options: - request_options = {} # type: Dict[str, Any] - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics @@ -381,8 +346,8 @@ def delete_database( response_hook(self.client_connection.last_response_headers) @distributed_trace - def get_database_account(self, response_hook=None, **kwargs): - # type: (Optional[Callable]) -> DatabaseAccount + def get_database_account(self, **kwargs): + # type: (Any) -> DatabaseAccount """ Retrieve the database account information. @@ -390,6 +355,7 @@ def get_database_account(self, response_hook=None, **kwargs): :returns: A :class:`DatabaseAccount` instance representing the Cosmos DB Database Account. """ + response_hook = kwargs.pop('response_hook', None) result = self.client_connection.GetDatabaseAccount(**kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index f829309c78b2..fbb3a10f0863 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -28,6 +28,7 @@ from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection +from ._base import build_options from .container_client import ContainerClient from .offer import Offer from .http_constants import StatusCodes @@ -105,16 +106,8 @@ def _get_properties(self): return self._properties @distributed_trace - def read( - self, - session_token=None, - initial_headers=None, - populate_query_metrics=None, - request_options=None, - response_hook=None, - **kwargs - ): - # type: (str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Dict[str, Any] + def read(self, populate_query_metrics=None, **kwargs): + # type: (Optional[bool], Any) -> Dict[str, Any] """ Read the database properties @@ -133,12 +126,8 @@ def read( from .cosmos_client import CosmosClient database_link = CosmosClient._get_database_link(self) - if not request_options: - request_options = {} # type: Dict[str, Any] - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics @@ -154,18 +143,13 @@ def create_container( self, id, # type: str # pylint: disable=redefined-builtin partition_key, # type: PartitionKey - indexing_policy=None, # type: Dict[str, Any] - default_ttl=None, # type: int - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - offer_throughput=None, # type: int - unique_key_policy=None, # type: Dict[str, Any] - conflict_resolution_policy=None, # type: Dict[str, Any] - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] - **kwargs + indexing_policy=None, # type: Optional[Dict[str, Any]] + default_ttl=None, # type: Optional[int] + populate_query_metrics=None, # type: Optional[bool] + offer_throughput=None, # type: Optional[int] + unique_key_policy=None, # type: Optional[Dict[str, Any]] + conflict_resolution_policy=None, # type: Optional[Dict[str, Any]] + **kwargs # type: Any ): # type: (...) -> ContainerClient """ @@ -219,14 +203,8 @@ def create_container( if conflict_resolution_policy: definition["conflictResolutionPolicy"] = conflict_resolution_policy - if not request_options: - request_options = {} # type: Dict[str, Any] - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics if offer_throughput is not None: @@ -245,13 +223,8 @@ def create_container( def delete_container( self, container, # type: Union[str, ContainerClient, Dict[str, Any]] - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] - **kwargs + populate_query_metrics=None, # type: Optional[bool] + **kwargs # type: Any ): # type: (...) -> None """ Delete the container @@ -268,14 +241,8 @@ def delete_container( :raise CosmosHttpResponseError: If the container couldn't be deleted. """ - if not request_options: - request_options = {} # type: Dict[str, Any] - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics @@ -310,17 +277,8 @@ def get_container_client(self, container): return ContainerClient(self.client_connection, self.database_link, id_value) @distributed_trace - def list_containers( - self, - max_item_count=None, - session_token=None, - initial_headers=None, - populate_query_metrics=None, - feed_options=None, - response_hook=None, - **kwargs - ): - # type: (int, str, Dict[str, str], bool, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + def list_containers(self, max_item_count=None, populate_query_metrics=None, **kwargs): + # type: (Optional[int], Optional[bool], Any) -> Iterable[Dict[str, Any]] """ List the containers in the database. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -340,14 +298,10 @@ def list_containers( :name: list_containers """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - if session_token: - feed_options["sessionToken"] = session_token - if initial_headers: - feed_options["initialHeaders"] = initial_headers if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics @@ -364,11 +318,7 @@ def query_containers( query=None, # type: Optional[str] parameters=None, # type: Optional[List[str]] max_item_count=None, # type: Optional[int] - session_token=None, # type: Optional[str] - initial_headers=None, # type: Optional[Dict[str, str]] populate_query_metrics=None, # type: Optional[bool] - feed_options=None, # type: Optional[Dict[str, Any]] - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] @@ -385,14 +335,10 @@ def query_containers( :returns: An Iterable of container properties (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - if session_token: - feed_options["sessionToken"] = session_token - if initial_headers: - feed_options["initialHeaders"] = initial_headers if populate_query_metrics is not None: feed_options["populateQueryMetrics"] = populate_query_metrics @@ -411,16 +357,11 @@ def replace_container( self, container, # type: Union[str, ContainerClient, Dict[str, Any]] partition_key, # type: PartitionKey - indexing_policy=None, # type: Dict[str, Any] - default_ttl=None, # type: int - conflict_resolution_policy=None, # type: Dict[str, Any] - session_token=None, # type: str - initial_headers=None, # type: Dict[str, str] - access_condition=None, # type: Dict[str, str] - populate_query_metrics=None, # type: bool - request_options=None, # type: Dict[str, Any] - response_hook=None, # type: Optional[Callable] - **kwargs + indexing_policy=None, # type: Optional[Dict[str, Any]] + default_ttl=None, # type: Optional[int] + conflict_resolution_policy=None, # type: Optional[Dict[str, Any]] + populate_query_metrics=None, # type: Optional[bool] + **kwargs # type: Any ): # type: (...) -> ContainerClient """ Reset the properties of the container. Property changes are persisted immediately. @@ -453,14 +394,8 @@ def replace_container( :name: reset_container_properties """ - if not request_options: - request_options = {} # type: Dict[str, Any] - if session_token: - request_options["sessionToken"] = session_token - if initial_headers: - request_options["initialHeaders"] = initial_headers - if access_condition: - request_options["accessCondition"] = access_condition + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics @@ -490,8 +425,8 @@ def replace_container( ) @distributed_trace - def list_users(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + def list_users(self, max_item_count=None, **kwargs): + # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] """ List all users in the container. :param max_item_count: Max number of users to be returned in the enumeration operation. @@ -500,8 +435,8 @@ def list_users(self, max_item_count=None, feed_options=None, response_hook=None, :returns: An Iterable of user properties (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -513,8 +448,8 @@ def list_users(self, max_item_count=None, feed_options=None, response_hook=None, return result @distributed_trace - def query_users(self, query, parameters=None, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + def query_users(self, query, parameters=None, max_item_count=None, **kwargs): + # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] """Return all users matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -525,8 +460,8 @@ def query_users(self, query, parameters=None, max_item_count=None, feed_options= :returns: An Iterable of user properties (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -561,8 +496,8 @@ def get_user_client(self, user): return UserClient(client_connection=self.client_connection, id=id_value, database_link=self.database_link) @distributed_trace - def create_user(self, body, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> UserClient + def create_user(self, body, **kwargs): + # type: (Dict[str, Any], Any) -> UserClient """ Create a user in the container. :param body: A dict-like object with an `id` key and value representing the user to be created. @@ -583,8 +518,8 @@ def create_user(self, body, request_options=None, response_hook=None, **kwargs): :name: create_user """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) user = self.client_connection.CreateUser( database_link=self.database_link, user=body, options=request_options, **kwargs) @@ -597,8 +532,8 @@ def create_user(self, body, request_options=None, response_hook=None, **kwargs): ) @distributed_trace - def upsert_user(self, body, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> UserClient + def upsert_user(self, body, **kwargs): + # type: (Dict[str, Any], Any) -> UserClient """ Insert or update the specified user. :param body: A dict-like object representing the user to update or insert. @@ -610,8 +545,8 @@ def upsert_user(self, body, request_options=None, response_hook=None, **kwargs): If the user already exists in the container, it is replaced. If it does not, it is inserted. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) user = self.client_connection.UpsertUser( database_link=self.database_link, user=body, options=request_options, **kwargs @@ -629,8 +564,6 @@ def replace_user( self, user, # type: Union[str, UserClient, Dict[str, Any]] body, # type: Dict[str, Any] - request_options=None, # type: Optional(Dict[str, Any]) - response_hook=None, # type: Optional[Callable] **kwargs # type: Any ): # type: (...) -> UserClient @@ -645,8 +578,8 @@ def replace_user( :raise `CosmosHttpResponseError`: If the replace failed or the user with given id does not exist. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) user = self.client_connection.ReplaceUser( user_link=self._get_user_link(user), user=body, options=request_options, **kwargs @@ -660,8 +593,8 @@ def replace_user( ) @distributed_trace - def delete_user(self, user, request_options=None, response_hook=None, **kwargs): - # type: (Union[str, UserClient, Dict[str, Any]], Dict[str, Any], Optional[Callable]) -> None + def delete_user(self, user, **kwargs): + # type: (Union[str, UserClient, Dict[str, Any]], Any) -> None """ Delete the specified user from the container. :param user: The ID (name), dict representing the properties or :class:`UserClient` @@ -672,8 +605,8 @@ def delete_user(self, user, request_options=None, response_hook=None, **kwargs): exist in the container, a `404` error is returned. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) result = self.client_connection.DeleteUser( user_link=self._get_user_link(user), options=request_options, **kwargs @@ -682,8 +615,8 @@ def delete_user(self, user, request_options=None, response_hook=None, **kwargs): response_hook(self.client_connection.last_response_headers, result) @distributed_trace - def read_offer(self, response_hook=None, **kwargs): - # type: (Optional[Callable]) -> Offer + def read_offer(self, **kwargs): + # type: (Any) -> Offer """ Read the Offer object for this database. :param response_hook: a callable invoked with the response metadata @@ -691,6 +624,7 @@ def read_offer(self, response_hook=None, **kwargs): :raise CosmosHttpResponseError: If no offer exists for the database or if the offer could not be retrieved. """ + response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() link = properties["_self"] query_spec = { @@ -709,8 +643,8 @@ def read_offer(self, response_hook=None, **kwargs): return Offer(offer_throughput=offers[0]["content"]["offerThroughput"], properties=offers[0]) @distributed_trace - def replace_throughput(self, throughput, response_hook=None, **kwargs): - # type: (int, Optional[Callable]) -> Offer + def replace_throughput(self, throughput, **kwargs): + # type: (Optional[int], Any) -> Offer """ Replace the database level throughput. :param throughput: The throughput to be set (an integer). @@ -719,6 +653,7 @@ def replace_throughput(self, throughput, response_hook=None, **kwargs): :raise CosmosHttpResponseError: If no offer exists for the database or if the offer could not be updated. """ + response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() link = properties["_self"] query_spec = { diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py index b3e44f349bc6..b7cce04c04d2 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py @@ -27,6 +27,7 @@ import six from azure.cosmos._cosmos_client_connection import CosmosClientConnection +from ._base import build_options from .partition_key import NonePartitionKeyValue # pylint: disable=protected-access @@ -52,8 +53,8 @@ def _get_resource_link(self, script_or_id, typ): return u"{}/{}/{}".format(self.container_link, typ, script_or_id) return script_or_id["_self"] - def list_stored_procedures(self, max_item_count=None, feed_options=None): - # type: (int, Dict[str, Any]) -> Iterable[Dict[str, Any]] + def list_stored_procedures(self, max_item_count=None, **kwargs): + # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] """ List all stored procedures in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -61,15 +62,16 @@ def list_stored_procedures(self, max_item_count=None, feed_options=None): :returns: An Iterable of stored procedures (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - return self.client_connection.ReadStoredProcedures(collection_link=self.container_link, options=feed_options) + return self.client_connection.ReadStoredProcedures( + collection_link=self.container_link, options=feed_options, **kwargs + ) - def query_stored_procedures(self, query, parameters=None, max_item_count=None, feed_options=None): - # type: (str, List, int, Dict[str, Any]) -> Iterable[Dict[str, Any]] + def query_stored_procedures(self, query, parameters=None, max_item_count=None, **kwargs): + # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] """Return all stored procedures matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -79,8 +81,7 @@ def query_stored_procedures(self, query, parameters=None, max_item_count=None, f :returns: An Iterable of stored procedures (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -88,10 +89,11 @@ def query_stored_procedures(self, query, parameters=None, max_item_count=None, f collection_link=self.container_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, + **kwargs ) - def get_stored_procedure(self, sproc, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any]) -> Dict[str, Any] + def get_stored_procedure(self, sproc, **kwargs): + # type: (Union[str, Dict[str, Any]], Any) -> Dict[str, Any] """ Get the stored procedure identified by `id`. @@ -101,15 +103,14 @@ def get_stored_procedure(self, sproc, request_options=None): :raise `CosmosHttpResponseError`: If the given stored procedure couldn't be retrieved. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.ReadStoredProcedure( - sproc_link=self._get_resource_link(sproc, ScriptType.StoredProcedure), options=request_options + sproc_link=self._get_resource_link(sproc, ScriptType.StoredProcedure), options=request_options, **kwargs ) - def create_stored_procedure(self, body, request_options=None): - # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] + def create_stored_procedure(self, body, **kwargs): + # type: (Dict[str, Any], Any) -> Dict[str, Any] """ Create a stored procedure in the container. :param body: A dict-like object representing the sproc to create. @@ -120,15 +121,14 @@ def create_stored_procedure(self, body, request_options=None): To replace an existing sproc, use the :func:`Container.scripts.replace_stored_procedure` method. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.CreateStoredProcedure( - collection_link=self.container_link, sproc=body, options=request_options + collection_link=self.container_link, sproc=body, options=request_options, **kwargs ) - def replace_stored_procedure(self, sproc, body, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] + def replace_stored_procedure(self, sproc, body, **kwargs): + # type: (Union[str, Dict[str, Any]], Dict[str, Any], Any) -> Dict[str, Any] """ Replaces the specified stored procedure if it exists in the container. :param sproc: The ID (name) or dict representing stored procedure to be replaced. @@ -138,15 +138,17 @@ def replace_stored_procedure(self, sproc, body, request_options=None): :raise `CosmosHttpResponseError`: If the replace failed or the stored procedure with given id does not exist. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.ReplaceStoredProcedure( - sproc_link=self._get_resource_link(sproc, ScriptType.StoredProcedure), sproc=body, options=request_options + sproc_link=self._get_resource_link(sproc, ScriptType.StoredProcedure), + sproc=body, + options=request_options, + **kwargs ) - def delete_stored_procedure(self, sproc, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any]) -> None + def delete_stored_procedure(self, sproc, **kwargs): + # type: (Union[str, Dict[str, Any]], **kwargs) -> None """ Delete the specified stored procedure from the container. :param sproc: The ID (name) or dict representing stored procedure to be deleted. @@ -155,17 +157,21 @@ def delete_stored_procedure(self, sproc, request_options=None): exist in the container, a `404` error is returned. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) self.client_connection.DeleteStoredProcedure( - sproc_link=self._get_resource_link(sproc, ScriptType.StoredProcedure), options=request_options + sproc_link=self._get_resource_link(sproc, ScriptType.StoredProcedure), options=request_options, **kwargs ) def execute_stored_procedure( - self, sproc, partition_key=None, params=None, enable_script_logging=None, request_options=None + self, + sproc, # type: Union[str, Dict[str, Any]] + partition_key=None, # type: Optional[str] + params=None, # type: Optional[List[Any]] + enable_script_logging=None, # type: Optional[bool] + **kwargs # type: Any ): - # type: (Union[str, Dict[str, Any]], str, List[Any], bool, Dict[str, Any]) -> Any + # type: (...) -> Any """ execute the specified stored procedure. :param sproc: The ID (name) or dict representing stored procedure to be executed. @@ -179,8 +185,7 @@ def execute_stored_procedure( """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) if partition_key is not None: request_options["partitionKey"] = ( CosmosClientConnection._return_undefined_or_empty_partition_key(self.is_system_key) @@ -194,10 +199,11 @@ def execute_stored_procedure( sproc_link=self._get_resource_link(sproc, ScriptType.StoredProcedure), params=params, options=request_options, + **kwargs ) - def list_triggers(self, max_item_count=None, feed_options=None): - # type: (int, Dict[str, Any]) -> Iterable[Dict[str, Any]] + def list_triggers(self, max_item_count=None, **kwargs): + # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] """ List all triggers in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -205,15 +211,16 @@ def list_triggers(self, max_item_count=None, feed_options=None): :returns: An Iterable of triggers (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count - return self.client_connection.ReadTriggers(collection_link=self.container_link, options=feed_options) + return self.client_connection.ReadTriggers( + collection_link=self.container_link, options=feed_options, **kwargs + ) - def query_triggers(self, query, parameters=None, max_item_count=None, feed_options=None): - # type: (str, List, int, Dict[str, Any]) -> Iterable[Dict[str, Any]] + def query_triggers(self, query, parameters=None, max_item_count=None, **kwargs): + # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] """Return all triggers matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -223,8 +230,7 @@ def query_triggers(self, query, parameters=None, max_item_count=None, feed_optio :returns: An Iterable of triggers (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -232,10 +238,11 @@ def query_triggers(self, query, parameters=None, max_item_count=None, feed_optio collection_link=self.container_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, + **kwargs ) - def get_trigger(self, trigger, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any]) -> Dict[str, Any] + def get_trigger(self, trigger, **kwargs): + # type: (Union[str, Dict[str, Any]], Any) -> Dict[str, Any] """ Get the trigger identified by `id`. @@ -245,15 +252,14 @@ def get_trigger(self, trigger, request_options=None): :raise `CosmosHttpResponseError`: If the given trigger couldn't be retrieved. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.ReadTrigger( - trigger_link=self._get_resource_link(trigger, ScriptType.Trigger), options=request_options + trigger_link=self._get_resource_link(trigger, ScriptType.Trigger), options=request_options, **kwargs ) - def create_trigger(self, body, request_options=None): - # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] + def create_trigger(self, body, **kwargs): + # type: (Dict[str, Any], Any) -> Dict[str, Any] """ Create a trigger in the container. :param body: A dict-like object representing the trigger to create. @@ -264,15 +270,14 @@ def create_trigger(self, body, request_options=None): To replace an existing trigger, use the :func:`Container.scripts.replace_trigger` method. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.CreateTrigger( - collection_link=self.container_link, trigger=body, options=request_options + collection_link=self.container_link, trigger=body, options=request_options, **kwargs ) - def replace_trigger(self, trigger, body, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] + def replace_trigger(self, trigger, body, **kwargs): + # type: (Union[str, Dict[str, Any]], Dict[str, Any], Any) -> Dict[str, Any] """ Replaces the specified tigger if it exists in the container. :param trigger: The ID (name) or dict representing trigger to be replaced. @@ -282,15 +287,17 @@ def replace_trigger(self, trigger, body, request_options=None): :raise `CosmosHttpResponseError`: If the replace failed or the trigger with given id does not exist. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.ReplaceTrigger( - trigger_link=self._get_resource_link(trigger, ScriptType.Trigger), trigger=body, options=request_options + trigger_link=self._get_resource_link(trigger, ScriptType.Trigger), + trigger=body, + options=request_options, + **kwargs ) - def delete_trigger(self, trigger, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any]) -> None + def delete_trigger(self, trigger, **kwargs): + # type: (Union[str, Dict[str, Any]], Any) -> None """ Delete the specified trigger from the container. :param trigger: The ID (name) or dict representing trigger to be deleted. @@ -299,15 +306,14 @@ def delete_trigger(self, trigger, request_options=None): exist in the container, a `404` error is returned. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) self.client_connection.DeleteTrigger( - trigger_link=self._get_resource_link(trigger, ScriptType.Trigger), options=request_options + trigger_link=self._get_resource_link(trigger, ScriptType.Trigger), options=request_options, **kwargs ) - def list_user_defined_functions(self, max_item_count=None, feed_options=None): - # type: (int, Dict[str, Any]) -> Iterable[Dict[str, Any]] + def list_user_defined_functions(self, max_item_count=None, **kwargs): + # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] """ List all user defined functions in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. @@ -315,17 +321,16 @@ def list_user_defined_functions(self, max_item_count=None, feed_options=None): :returns: An Iterable of user defined functions (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count return self.client_connection.ReadUserDefinedFunctions( - collection_link=self.container_link, options=feed_options + collection_link=self.container_link, options=feed_options, **kwargs ) - def query_user_defined_functions(self, query, parameters=None, max_item_count=None, feed_options=None): - # type: (str, List, int, Dict[str, Any]) -> Iterable[Dict[str, Any]] + def query_user_defined_functions(self, query, parameters=None, max_item_count=None, **kwargs): + # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] """Return all user defined functions matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -335,8 +340,7 @@ def query_user_defined_functions(self, query, parameters=None, max_item_count=No :returns: An Iterable of user defined functions (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -344,10 +348,11 @@ def query_user_defined_functions(self, query, parameters=None, max_item_count=No collection_link=self.container_link, query=query if parameters is None else dict(query=query, parameters=parameters), options=feed_options, + **kwargs ) - def get_user_defined_function(self, udf, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any]) -> Dict[str, Any] + def get_user_defined_function(self, udf, **kwargs): + # type: (Union[str, Dict[str, Any]], Any) -> Dict[str, Any] """ Get the stored procedure identified by `id`. @@ -357,15 +362,14 @@ def get_user_defined_function(self, udf, request_options=None): :raise `CosmosHttpResponseError`: If the given user defined function couldn't be retrieved. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.ReadUserDefinedFunction( - udf_link=self._get_resource_link(udf, ScriptType.UserDefinedFunction), options=request_options + udf_link=self._get_resource_link(udf, ScriptType.UserDefinedFunction), options=request_options, **kwargs ) - def create_user_defined_function(self, body, request_options=None): - # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] + def create_user_defined_function(self, body, **kwargs): + # type: (Dict[str, Any], Any) -> Dict[str, Any] """ Create a user defined function in the container. :param body: A dict-like object representing the udf to create. @@ -376,15 +380,14 @@ def create_user_defined_function(self, body, request_options=None): To replace an existing udf, use the :func:`Container.scripts.replace_user_defined_function` method. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.CreateUserDefinedFunction( - collection_link=self.container_link, udf=body, options=request_options + collection_link=self.container_link, udf=body, options=request_options, **kwargs ) - def replace_user_defined_function(self, udf, body, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] + def replace_user_defined_function(self, udf, body, **kwargs): + # type: (Union[str, Dict[str, Any]], Dict[str, Any], Any) -> Dict[str, Any] """ Replaces the specified user defined function if it exists in the container. :param udf: The ID (name) or dict representing udf to be replaced. @@ -395,15 +398,17 @@ def replace_user_defined_function(self, udf, body, request_options=None): given id does not exist. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) return self.client_connection.ReplaceUserDefinedFunction( - udf_link=self._get_resource_link(udf, ScriptType.UserDefinedFunction), udf=body, options=request_options + udf_link=self._get_resource_link(udf, ScriptType.UserDefinedFunction), + udf=body, + options=request_options, + **kwargs ) - def delete_user_defined_function(self, udf, request_options=None): - # type: (Union[str, Dict[str, Any]], Dict[str, Any]) -> None + def delete_user_defined_function(self, udf, **kwargs): + # type: (Union[str, Dict[str, Any]], Any) -> None """ Delete the specified user defined function from the container. :param udf: The ID (name) or dict representing udf to be deleted. @@ -412,9 +417,8 @@ def delete_user_defined_function(self, udf, request_options=None): exist in the container, a `404` error is returned. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) self.client_connection.DeleteUserDefinedFunction( - udf_link=self._get_resource_link(udf, ScriptType.UserDefinedFunction), options=request_options + udf_link=self._get_resource_link(udf, ScriptType.UserDefinedFunction), options=request_options, **kwargs ) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py index 7eee49755316..7be0cf19409a 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py @@ -30,6 +30,7 @@ from azure.core.tracing.decorator import distributed_trace from ._cosmos_client_connection import CosmosClientConnection +from ._base import build_options from .permission import Permission @@ -59,8 +60,8 @@ def _get_properties(self): return self._properties @distributed_trace - def read(self, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Optional[Callable]) -> UserClient + def read(self, **kwargs): + # type: (Any) -> UserClient """ Read user propertes. @@ -70,8 +71,8 @@ def read(self, request_options=None, response_hook=None, **kwargs): :raise `CosmosHttpResponseError`: If the given user couldn't be retrieved. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) self._properties = self.client_connection.ReadUser(user_link=self.user_link, options=request_options, **kwargs) @@ -81,8 +82,8 @@ def read(self, request_options=None, response_hook=None, **kwargs): return self._properties @distributed_trace - def list_permissions(self, max_item_count=None, feed_options=None, response_hook=None, **kwargs): - # type: (int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + def list_permissions(self, max_item_count=None, **kwargs): + # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] """ List all permission for the user. :param max_item_count: Max number of permissions to be returned in the enumeration operation. @@ -91,8 +92,8 @@ def list_permissions(self, max_item_count=None, feed_options=None, response_hook :returns: An Iterable of permissions (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -109,11 +110,9 @@ def query_permissions( query, parameters=None, max_item_count=None, - feed_options=None, - response_hook=None, **kwargs ): - # type: (str, List, int, Dict[str, Any], Optional[Callable]) -> Iterable[Dict[str, Any]] + # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] """Return all permissions matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. @@ -124,8 +123,8 @@ def query_permissions( :returns: An Iterable of permissions (dicts). """ - if not feed_options: - feed_options = {} # type: Dict[str, Any] + feed_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) if max_item_count is not None: feed_options["maxItemCount"] = max_item_count @@ -142,8 +141,8 @@ def query_permissions( return result @distributed_trace - def get_permission(self, permission, request_options=None, response_hook=None, **kwargs): - # type: (str, Dict[str, Any], Optional[Callable]) -> Permission + def get_permission(self, permission, **kwargs): + # type: (str, Any) -> Permission """ Get the permission identified by `id`. @@ -155,8 +154,8 @@ def get_permission(self, permission, request_options=None, response_hook=None, * :raise `CosmosHttpResponseError`: If the given permission couldn't be retrieved. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) permission = self.client_connection.ReadPermission( permission_link=self._get_permission_link(permission), options=request_options, **kwargs @@ -174,8 +173,8 @@ def get_permission(self, permission, request_options=None, response_hook=None, * ) @distributed_trace - def create_permission(self, body, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> Permission + def create_permission(self, body, **kwargs): + # type: (Dict[str, Any], Any) -> Permission """ Create a permission for the user. :param body: A dict-like object representing the permission to create. @@ -187,8 +186,8 @@ def create_permission(self, body, request_options=None, response_hook=None, **kw To update or replace an existing permision, use the :func:`UserClient.upsert_permission` method. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) permission = self.client_connection.CreatePermission( user_link=self.user_link, permission=body, options=request_options, **kwargs @@ -206,8 +205,8 @@ def create_permission(self, body, request_options=None, response_hook=None, **kw ) @distributed_trace - def upsert_permission(self, body, request_options=None, response_hook=None, **kwargs): - # type: (Dict[str, Any], Dict[str, Any], Optional[Callable]) -> Permission + def upsert_permission(self, body, **kwargs): + # type: (Dict[str, Any], Any) -> Permission """ Insert or update the specified permission. :param body: A dict-like object representing the permission to update or insert. @@ -218,9 +217,8 @@ def upsert_permission(self, body, request_options=None, response_hook=None, **kw If the permission already exists in the container, it is replaced. If it does not, it is inserted. """ - - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) permission = self.client_connection.UpsertPermission( user_link=self.user_link, permission=body, options=request_options, **kwargs @@ -238,8 +236,8 @@ def upsert_permission(self, body, request_options=None, response_hook=None, **kw ) @distributed_trace - def replace_permission(self, permission, body, request_options=None, response_hook=None, **kwargs): - # type: (str, Dict[str, Any], Dict[str, Any], Optional[Callable]) -> Permission + def replace_permission(self, permission, body, **kwargs): + # type: (str, Dict[str, Any], Any) -> Permission """ Replaces the specified permission if it exists for the user. :param permission: The ID (name), dict representing the properties or :class:`Permission` @@ -251,8 +249,8 @@ def replace_permission(self, permission, body, request_options=None, response_ho :raise `CosmosHttpResponseError`: If the replace failed or the permission with given id does not exist. """ - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) permission = self.client_connection.ReplacePermission( permission_link=self._get_permission_link(permission), permission=body, options=request_options, **kwargs @@ -270,8 +268,8 @@ def replace_permission(self, permission, body, request_options=None, response_ho ) @distributed_trace - def delete_permission(self, permission, request_options=None, response_hook=None, **kwargs): - # type: (str, Dict[str, Any], Optional[Callable]) -> None + def delete_permission(self, permission, **kwargs): + # type: (str, Any) -> None """ Delete the specified permission from the user. :param permission: The ID (name), dict representing the properties or :class:`Permission` @@ -282,9 +280,8 @@ def delete_permission(self, permission, request_options=None, response_hook=None not exist for the user, a `404` error is returned. """ - - if not request_options: - request_options = {} # type: Dict[str, Any] + request_options = build_options(kwargs) + response_hook = kwargs.pop('response_hook', None) result = self.client_connection.DeletePermission( permission_link=self._get_permission_link(permission), options=request_options, **kwargs diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index cf2685cdae79..4db490c7a953 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -808,9 +808,7 @@ def test_document_crud(self): created_collection.replace_item, replaced_document['id'], replaced_document, - None, - None, - {'type': 'IfMatch', 'condition': old_etag}, + if_match=old_etag, ) # should pass for most recent etag From 5d1ad186f089aed0f13e993b22aab95ee5545e72 Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 16:12:19 -0700 Subject: [PATCH 28/46] Fix pylint --- sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index fda5a440664b..9c7f9588fe82 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -22,7 +22,7 @@ """Create, read, update and delete items in the Azure Cosmos DB SQL API service. """ -from typing import Any, Callable, Dict, List, Optional, Union, Iterable +from typing import Any, Dict, List, Optional, Union, Iterable import six from azure.core.tracing.decorator import distributed_trace diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index e400bd238215..bae3e6228b7b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -22,7 +22,7 @@ """Create, read, and delete databases in the Azure Cosmos DB SQL API service. """ -from typing import Any, Callable, Dict, Mapping, Optional, Union, cast, Iterable +from typing import Any, Dict, Mapping, Optional, Union, cast, Iterable import six from azure.core.tracing.decorator import distributed_trace From 70b5ee6490d60588da47b67f5a85b0c7c5633373 Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 21:19:42 -0700 Subject: [PATCH 29/46] Propagate kwargs --- sdk/cosmos/azure-cosmos/azure/__init__.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/_base.py | 1 + .../azure/cosmos/_cosmos_client_connection.py | 317 +++++++++--------- .../azure/cosmos/container_client.py | 6 +- .../azure/cosmos/cosmos_client.py | 2 +- .../azure/cosmos/database_client.py | 6 +- sdk/cosmos/azure-cosmos/azure/cosmos/offer.py | 1 + .../azure-cosmos/azure/cosmos/permission.py | 1 + .../azure/cosmos/scripts_client.py | 4 +- .../azure-cosmos/azure/cosmos/user_client.py | 2 +- 10 files changed, 181 insertions(+), 161 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/__init__.py b/sdk/cosmos/azure-cosmos/azure/__init__.py index 8db66d3d0f0f..d55ccad1f573 100644 --- a/sdk/cosmos/azure-cosmos/azure/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/__init__.py @@ -1 +1 @@ -__path__ = __import__("pkgutil").extend_path(__path__, __name__) +__path__ = __import__("pkgutil").extend_path(__path__, __name__) # type: ignore diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py index eb79c4bcc5fe..5f0143c2bd1b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py @@ -30,6 +30,7 @@ import six from six.moves.urllib.parse import quote as urllib_quote +from typing import Dict, Any from . import auth from . import documents diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index d3f3e0ff821f..8603a7d3e134 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -24,6 +24,7 @@ """Document client class for the Azure Cosmos database service. """ +from typing import Dict, Any, Optional import six from azure.core.paging import ItemPaged from azure.core import PipelineClient @@ -38,6 +39,7 @@ from . import _base as base from . import documents +from .documents import ConnectionPolicy from . import _constants as constants from . import http_constants from . import _query_iterable as query_iterable @@ -115,7 +117,7 @@ def __init__( id_ = resource_parts[-1] self.resource_tokens[id_] = permission_feed["_token"] - self.connection_policy = connection_policy or documents.ConnectionPolicy() + self.connection_policy = connection_policy or ConnectionPolicy() self.partition_resolvers = {} @@ -148,9 +150,9 @@ def __init__( proxies = kwargs.pop('proxies', {}) if self.connection_policy.ProxyConfiguration and self.connection_policy.ProxyConfiguration.Host: - host = connection_policy.ProxyConfiguration.Host + host = self.connection_policy.ProxyConfiguration.Host url = six.moves.urllib.parse.urlparse(host) - proxy = host if url.port else host + ":" + str(connection_policy.ProxyConfiguration.Port) + proxy = host if url.port else host + ":" + str(self.connection_policy.ProxyConfiguration.Port) proxies.update({url.scheme : proxy}) policies = [ @@ -233,7 +235,7 @@ def GetPartitionResolver(self, database_link): return self.partition_resolvers.get(base.TrimBeginningAndEndingSlashes(database_link)) - def CreateDatabase(self, database, options=None): + def CreateDatabase(self, database, options=None, **kwargs): """Creates a database. :param dict database: @@ -251,9 +253,9 @@ def CreateDatabase(self, database, options=None): CosmosClientConnection.__ValidateResource(database) path = "/dbs" - return self.Create(database, path, "dbs", None, None, options) + return self.Create(database, path, "dbs", None, None, options, **kwargs) - def ReadDatabase(self, database_link, options=None): + def ReadDatabase(self, database_link, options=None, **kwargs): """Reads a database. :param str database_link: @@ -271,9 +273,9 @@ def ReadDatabase(self, database_link, options=None): path = base.GetPathFromLink(database_link) database_id = base.GetResourceIdOrFullNameFromLink(database_link) - return self.Read(path, "dbs", database_id, None, options) + return self.Read(path, "dbs", database_id, None, options, **kwargs) - def ReadDatabases(self, options=None): + def ReadDatabases(self, options=None, **kwargs): """Reads all databases. :param dict options: @@ -288,9 +290,9 @@ def ReadDatabases(self, options=None): if options is None: options = {} - return self.QueryDatabases(None, options) + return self.QueryDatabases(None, options, **kwargs) - def QueryDatabases(self, query, options=None): + def QueryDatabases(self, query, options=None, **kwargs): """Queries databases. :param (str or dict) query: @@ -307,7 +309,10 @@ def QueryDatabases(self, query, options=None): def fetch_fn(options): return ( - self.__QueryFeed("/dbs", "dbs", "", lambda r: r["Databases"], lambda _, b: b, query, options), + self.__QueryFeed( + "/dbs", "dbs", "", lambda r: r["Databases"], + lambda _, b: b, query, options, **kwargs + ), self.last_response_headers, ) @@ -315,7 +320,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def ReadContainers(self, database_link, options=None): + def ReadContainers(self, database_link, options=None, **kwargs): """Reads all collections in a database. :param str database_link: @@ -331,9 +336,9 @@ def ReadContainers(self, database_link, options=None): if options is None: options = {} - return self.QueryContainers(database_link, None, options) + return self.QueryContainers(database_link, None, options, **kwargs) - def QueryContainers(self, database_link, query, options=None): + def QueryContainers(self, database_link, query, options=None, **kwargs): """Queries collections in a database. :param str database_link: @@ -356,7 +361,8 @@ def QueryContainers(self, database_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "colls", database_id, lambda r: r["DocumentCollections"], lambda _, body: body, query, options + path, "colls", database_id, lambda r: r["DocumentCollections"], + lambda _, body: body, query, options, **kwargs ), self.last_response_headers, ) @@ -365,7 +371,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def CreateContainer(self, database_link, collection, options=None): + def CreateContainer(self, database_link, collection, options=None, **kwargs): """Creates a collection in a database. :param str database_link: @@ -379,15 +385,16 @@ def CreateContainer(self, database_link, collection, options=None): :rtype: dict """ + print("KWARGS", kwargs) if options is None: options = {} CosmosClientConnection.__ValidateResource(collection) path = base.GetPathFromLink(database_link, "colls") database_id = base.GetResourceIdOrFullNameFromLink(database_link) - return self.Create(collection, path, "colls", database_id, None, options) + return self.Create(collection, path, "colls", database_id, None, options, **kwargs) - def ReplaceContainer(self, collection_link, collection, options=None): + def ReplaceContainer(self, collection_link, collection, options=None, **kwargs): """Replaces a collection and return it. :param str collection_link: @@ -409,9 +416,9 @@ def ReplaceContainer(self, collection_link, collection, options=None): CosmosClientConnection.__ValidateResource(collection) path = base.GetPathFromLink(collection_link) collection_id = base.GetResourceIdOrFullNameFromLink(collection_link) - return self.Replace(collection, path, "colls", collection_id, None, options) + return self.Replace(collection, path, "colls", collection_id, None, options, **kwargs) - def ReadContainer(self, collection_link, options=None): + def ReadContainer(self, collection_link, options=None, **kwargs): """Reads a collection. :param str collection_link: @@ -430,9 +437,9 @@ def ReadContainer(self, collection_link, options=None): path = base.GetPathFromLink(collection_link) collection_id = base.GetResourceIdOrFullNameFromLink(collection_link) - return self.Read(path, "colls", collection_id, None, options) + return self.Read(path, "colls", collection_id, None, options, **kwargs) - def CreateUser(self, database_link, user, options=None): + def CreateUser(self, database_link, user, options=None, **kwargs): """Creates a user. :param str database_link: @@ -452,9 +459,9 @@ def CreateUser(self, database_link, user, options=None): options = {} database_id, path = self._GetDatabaseIdWithPathForUser(database_link, user) - return self.Create(user, path, "users", database_id, None, options) + return self.Create(user, path, "users", database_id, None, options, **kwargs) - def UpsertUser(self, database_link, user, options=None): + def UpsertUser(self, database_link, user, options=None, **kwargs): """Upserts a user. :param str database_link: @@ -472,7 +479,7 @@ def UpsertUser(self, database_link, user, options=None): options = {} database_id, path = self._GetDatabaseIdWithPathForUser(database_link, user) - return self.Upsert(user, path, "users", database_id, None, options) + return self.Upsert(user, path, "users", database_id, None, options, **kwargs) def _GetDatabaseIdWithPathForUser(self, database_link, user): # pylint: disable=no-self-use CosmosClientConnection.__ValidateResource(user) @@ -480,7 +487,7 @@ def _GetDatabaseIdWithPathForUser(self, database_link, user): # pylint: disable database_id = base.GetResourceIdOrFullNameFromLink(database_link) return database_id, path - def ReadUser(self, user_link, options=None): + def ReadUser(self, user_link, options=None, **kwargs): """Reads a user. :param str user_link: @@ -499,9 +506,9 @@ def ReadUser(self, user_link, options=None): path = base.GetPathFromLink(user_link) user_id = base.GetResourceIdOrFullNameFromLink(user_link) - return self.Read(path, "users", user_id, None, options) + return self.Read(path, "users", user_id, None, options, **kwargs) - def ReadUsers(self, database_link, options=None): + def ReadUsers(self, database_link, options=None, **kwargs): """Reads all users in a database. :params str database_link: @@ -517,9 +524,9 @@ def ReadUsers(self, database_link, options=None): if options is None: options = {} - return self.QueryUsers(database_link, None, options) + return self.QueryUsers(database_link, None, options, **kwargs) - def QueryUsers(self, database_link, query, options=None): + def QueryUsers(self, database_link, query, options=None, **kwargs): """Queries users in a database. :param str database_link: @@ -542,7 +549,7 @@ def QueryUsers(self, database_link, query, options=None): def fetch_fn(options): return ( - self.__QueryFeed(path, "users", database_id, lambda r: r["Users"], lambda _, b: b, query, options), + self.__QueryFeed(path, "users", database_id, lambda r: r["Users"], lambda _, b: b, query, options, **kwargs), self.last_response_headers, ) @@ -550,7 +557,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def DeleteDatabase(self, database_link, options=None): + def DeleteDatabase(self, database_link, options=None, **kwargs): """Deletes a database. :param str database_link: @@ -569,9 +576,9 @@ def DeleteDatabase(self, database_link, options=None): path = base.GetPathFromLink(database_link) database_id = base.GetResourceIdOrFullNameFromLink(database_link) - return self.DeleteResource(path, "dbs", database_id, None, options) + return self.DeleteResource(path, "dbs", database_id, None, options, **kwargs) - def CreatePermission(self, user_link, permission, options=None): + def CreatePermission(self, user_link, permission, options=None, **kwargs): """Creates a permission for a user. :param str user_link: @@ -591,9 +598,9 @@ def CreatePermission(self, user_link, permission, options=None): options = {} path, user_id = self._GetUserIdWithPathForPermission(permission, user_link) - return self.Create(permission, path, "permissions", user_id, None, options) + return self.Create(permission, path, "permissions", user_id, None, options, **kwargs) - def UpsertPermission(self, user_link, permission, options=None): + def UpsertPermission(self, user_link, permission, options=None, **kwargs): """Upserts a permission for a user. :param str user_link: @@ -613,7 +620,7 @@ def UpsertPermission(self, user_link, permission, options=None): options = {} path, user_id = self._GetUserIdWithPathForPermission(permission, user_link) - return self.Upsert(permission, path, "permissions", user_id, None, options) + return self.Upsert(permission, path, "permissions", user_id, None, options, **kwargs) def _GetUserIdWithPathForPermission(self, permission, user_link): # pylint: disable=no-self-use CosmosClientConnection.__ValidateResource(permission) @@ -621,7 +628,7 @@ def _GetUserIdWithPathForPermission(self, permission, user_link): # pylint: dis user_id = base.GetResourceIdOrFullNameFromLink(user_link) return path, user_id - def ReadPermission(self, permission_link, options=None): + def ReadPermission(self, permission_link, options=None, **kwargs): """Reads a permission. :param str permission_link: @@ -640,9 +647,9 @@ def ReadPermission(self, permission_link, options=None): path = base.GetPathFromLink(permission_link) permission_id = base.GetResourceIdOrFullNameFromLink(permission_link) - return self.Read(path, "permissions", permission_id, None, options) + return self.Read(path, "permissions", permission_id, None, options, **kwargs) - def ReadPermissions(self, user_link, options=None): + def ReadPermissions(self, user_link, options=None, **kwargs): """Reads all permissions for a user. :param str user_link: @@ -659,9 +666,9 @@ def ReadPermissions(self, user_link, options=None): if options is None: options = {} - return self.QueryPermissions(user_link, None, options) + return self.QueryPermissions(user_link, None, options, **kwargs) - def QueryPermissions(self, user_link, query, options=None): + def QueryPermissions(self, user_link, query, options=None, **kwargs): """Queries permissions for a user. :param str user_link: @@ -685,7 +692,7 @@ def QueryPermissions(self, user_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "permissions", user_id, lambda r: r["Permissions"], lambda _, b: b, query, options + path, "permissions", user_id, lambda r: r["Permissions"], lambda _, b: b, query, options, **kwargs ), self.last_response_headers, ) @@ -694,7 +701,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def ReplaceUser(self, user_link, user, options=None): + def ReplaceUser(self, user_link, user, options=None, **kwargs): """Replaces a user and return it. :param str user_link: @@ -715,9 +722,9 @@ def ReplaceUser(self, user_link, user, options=None): CosmosClientConnection.__ValidateResource(user) path = base.GetPathFromLink(user_link) user_id = base.GetResourceIdOrFullNameFromLink(user_link) - return self.Replace(user, path, "users", user_id, None, options) + return self.Replace(user, path, "users", user_id, None, options, **kwargs) - def DeleteUser(self, user_link, options=None): + def DeleteUser(self, user_link, options=None, **kwargs): """Deletes a user. :param str user_link: @@ -736,9 +743,9 @@ def DeleteUser(self, user_link, options=None): path = base.GetPathFromLink(user_link) user_id = base.GetResourceIdOrFullNameFromLink(user_link) - return self.DeleteResource(path, "users", user_id, None, options) + return self.DeleteResource(path, "users", user_id, None, options, **kwargs) - def ReplacePermission(self, permission_link, permission, options=None): + def ReplacePermission(self, permission_link, permission, options=None, **kwargs): """Replaces a permission and return it. :param str permission_link: @@ -759,9 +766,9 @@ def ReplacePermission(self, permission_link, permission, options=None): CosmosClientConnection.__ValidateResource(permission) path = base.GetPathFromLink(permission_link) permission_id = base.GetResourceIdOrFullNameFromLink(permission_link) - return self.Replace(permission, path, "permissions", permission_id, None, options) + return self.Replace(permission, path, "permissions", permission_id, None, options, **kwargs) - def DeletePermission(self, permission_link, options=None): + def DeletePermission(self, permission_link, options=None, **kwargs): """Deletes a permission. :param str permission_link: @@ -780,9 +787,9 @@ def DeletePermission(self, permission_link, options=None): path = base.GetPathFromLink(permission_link) permission_id = base.GetResourceIdOrFullNameFromLink(permission_link) - return self.DeleteResource(path, "permissions", permission_id, None, options) + return self.DeleteResource(path, "permissions", permission_id, None, options, **kwargs) - def ReadItems(self, collection_link, feed_options=None, response_hook=None): + def ReadItems(self, collection_link, feed_options=None, response_hook=None, **kwargs): """Reads all documents in a collection. :param str collection_link: @@ -798,9 +805,9 @@ def ReadItems(self, collection_link, feed_options=None, response_hook=None): if feed_options is None: feed_options = {} - return self.QueryItems(collection_link, None, feed_options, response_hook=response_hook) + return self.QueryItems(collection_link, None, feed_options, response_hook=response_hook, **kwargs) - def QueryItems(self, database_or_container_link, query, options=None, partition_key=None, response_hook=None): + def QueryItems(self, database_or_container_link, query, options=None, partition_key=None, response_hook=None, **kwargs): """Queries documents in a collection. :param str database_or_container_link: @@ -848,6 +855,7 @@ def fetch_fn(options): query, options, response_hook=response_hook, + **kwargs ), self.last_response_headers, ) @@ -861,7 +869,7 @@ def fetch_fn(options): page_iterator_class=query_iterable.QueryIterable ) - def QueryItemsChangeFeed(self, collection_link, options=None, response_hook=None): + def QueryItemsChangeFeed(self, collection_link, options=None, response_hook=None, **kwargs): """Queries documents change feed in a collection. :param str collection_link: @@ -884,11 +892,11 @@ def QueryItemsChangeFeed(self, collection_link, options=None, response_hook=None partition_key_range_id = options["partitionKeyRangeId"] return self._QueryChangeFeed( - collection_link, "Documents", options, partition_key_range_id, response_hook=response_hook + collection_link, "Documents", options, partition_key_range_id, response_hook=response_hook, **kwargs ) def _QueryChangeFeed( - self, collection_link, resource_type, options=None, partition_key_range_id=None, response_hook=None + self, collection_link, resource_type, options=None, partition_key_range_id=None, response_hook=None, **kwargs ): """Queries change feed of a resource in a collection. @@ -935,6 +943,7 @@ def fetch_fn(options): options, partition_key_range_id, response_hook=response_hook, + **kwargs ), self.last_response_headers, ) @@ -948,7 +957,7 @@ def fetch_fn(options): page_iterator_class=query_iterable.QueryIterable ) - def _ReadPartitionKeyRanges(self, collection_link, feed_options=None): + def _ReadPartitionKeyRanges(self, collection_link, feed_options=None, **kwargs): """Reads Partition Key Ranges. :param str collection_link: @@ -964,9 +973,9 @@ def _ReadPartitionKeyRanges(self, collection_link, feed_options=None): if feed_options is None: feed_options = {} - return self._QueryPartitionKeyRanges(collection_link, None, feed_options) + return self._QueryPartitionKeyRanges(collection_link, None, feed_options, **kwargs) - def _QueryPartitionKeyRanges(self, collection_link, query, options=None): + def _QueryPartitionKeyRanges(self, collection_link, query, options=None, **kwargs): """Queries Partition Key Ranges in a collection. :param str collection_link: @@ -990,7 +999,8 @@ def _QueryPartitionKeyRanges(self, collection_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "pkranges", collection_id, lambda r: r["PartitionKeyRanges"], lambda _, b: b, query, options + path, "pkranges", collection_id, lambda r: r["PartitionKeyRanges"], + lambda _, b: b, query, options, **kwargs ), self.last_response_headers, ) @@ -999,7 +1009,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def CreateItem(self, database_or_container_link, document, options=None): + def CreateItem(self, database_or_container_link, document, options=None, **kwargs): """Creates a document in a collection. :param str database_or_container_link: @@ -1036,9 +1046,9 @@ def CreateItem(self, database_or_container_link, document, options=None): collection_id, document, path = self._GetContainerIdWithPathForItem( database_or_container_link, document, options ) - return self.Create(document, path, "docs", collection_id, None, options) + return self.Create(document, path, "docs", collection_id, None, options, **kwargs) - def UpsertItem(self, database_or_container_link, document, options=None): + def UpsertItem(self, database_or_container_link, document, options=None, **kwargs): """Upserts a document in a collection. :param str database_or_container_link: @@ -1075,7 +1085,7 @@ def UpsertItem(self, database_or_container_link, document, options=None): collection_id, document, path = self._GetContainerIdWithPathForItem( database_or_container_link, document, options ) - return self.Upsert(document, path, "docs", collection_id, None, options) + return self.Upsert(document, path, "docs", collection_id, None, options, **kwargs) PartitionResolverErrorMessage = ( "Couldn't find any partition resolvers for the database link provided. " @@ -1112,7 +1122,7 @@ def _GetContainerIdWithPathForItem(self, database_or_container_link, document, o collection_id = base.GetResourceIdOrFullNameFromLink(collection_link) return collection_id, document, path - def ReadItem(self, document_link, options=None): + def ReadItem(self, document_link, options=None, **kwargs): """Reads a document. :param str document_link: @@ -1131,9 +1141,9 @@ def ReadItem(self, document_link, options=None): path = base.GetPathFromLink(document_link) document_id = base.GetResourceIdOrFullNameFromLink(document_link) - return self.Read(path, "docs", document_id, None, options) + return self.Read(path, "docs", document_id, None, options, **kwargs) - def ReadTriggers(self, collection_link, options=None): + def ReadTriggers(self, collection_link, options=None, **kwargs): """Reads all triggers in a collection. :param str collection_link: @@ -1150,9 +1160,9 @@ def ReadTriggers(self, collection_link, options=None): if options is None: options = {} - return self.QueryTriggers(collection_link, None, options) + return self.QueryTriggers(collection_link, None, options, **kwargs) - def QueryTriggers(self, collection_link, query, options=None): + def QueryTriggers(self, collection_link, query, options=None, **kwargs): """Queries triggers in a collection. :param str collection_link: @@ -1176,7 +1186,7 @@ def QueryTriggers(self, collection_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "triggers", collection_id, lambda r: r["Triggers"], lambda _, b: b, query, options + path, "triggers", collection_id, lambda r: r["Triggers"], lambda _, b: b, query, options, **kwargs ), self.last_response_headers, ) @@ -1185,7 +1195,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def CreateTrigger(self, collection_link, trigger, options=None): + def CreateTrigger(self, collection_link, trigger, options=None, **kwargs): """Creates a trigger in a collection. :param str collection_link: @@ -1204,9 +1214,9 @@ def CreateTrigger(self, collection_link, trigger, options=None): options = {} collection_id, path, trigger = self._GetContainerIdWithPathForTrigger(collection_link, trigger) - return self.Create(trigger, path, "triggers", collection_id, None, options) + return self.Create(trigger, path, "triggers", collection_id, None, options, **kwargs) - def UpsertTrigger(self, collection_link, trigger, options=None): + def UpsertTrigger(self, collection_link, trigger, options=None, **kwargs): """Upserts a trigger in a collection. :param str collection_link: @@ -1225,7 +1235,7 @@ def UpsertTrigger(self, collection_link, trigger, options=None): options = {} collection_id, path, trigger = self._GetContainerIdWithPathForTrigger(collection_link, trigger) - return self.Upsert(trigger, path, "triggers", collection_id, None, options) + return self.Upsert(trigger, path, "triggers", collection_id, None, options, **kwargs) def _GetContainerIdWithPathForTrigger(self, collection_link, trigger): # pylint: disable=no-self-use CosmosClientConnection.__ValidateResource(trigger) @@ -1239,7 +1249,7 @@ def _GetContainerIdWithPathForTrigger(self, collection_link, trigger): # pylint collection_id = base.GetResourceIdOrFullNameFromLink(collection_link) return collection_id, path, trigger - def ReadTrigger(self, trigger_link, options=None): + def ReadTrigger(self, trigger_link, options=None, **kwargs): """Reads a trigger. :param str trigger_link: @@ -1258,9 +1268,9 @@ def ReadTrigger(self, trigger_link, options=None): path = base.GetPathFromLink(trigger_link) trigger_id = base.GetResourceIdOrFullNameFromLink(trigger_link) - return self.Read(path, "triggers", trigger_id, None, options) + return self.Read(path, "triggers", trigger_id, None, options, **kwargs) - def ReadUserDefinedFunctions(self, collection_link, options=None): + def ReadUserDefinedFunctions(self, collection_link, options=None, **kwargs): """Reads all user defined functions in a collection. :param str collection_link: @@ -1277,9 +1287,9 @@ def ReadUserDefinedFunctions(self, collection_link, options=None): if options is None: options = {} - return self.QueryUserDefinedFunctions(collection_link, None, options) + return self.QueryUserDefinedFunctions(collection_link, None, options, **kwargs) - def QueryUserDefinedFunctions(self, collection_link, query, options=None): + def QueryUserDefinedFunctions(self, collection_link, query, options=None, **kwargs): """Queries user defined functions in a collection. :param str collection_link: @@ -1303,7 +1313,8 @@ def QueryUserDefinedFunctions(self, collection_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "udfs", collection_id, lambda r: r["UserDefinedFunctions"], lambda _, b: b, query, options + path, "udfs", collection_id, lambda r: r["UserDefinedFunctions"], + lambda _, b: b, query, options, **kwargs ), self.last_response_headers, ) @@ -1312,7 +1323,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def CreateUserDefinedFunction(self, collection_link, udf, options=None): + def CreateUserDefinedFunction(self, collection_link, udf, options=None, **kwargs): """Creates a user defined function in a collection. :param str collection_link: @@ -1331,9 +1342,9 @@ def CreateUserDefinedFunction(self, collection_link, udf, options=None): options = {} collection_id, path, udf = self._GetContainerIdWithPathForUDF(collection_link, udf) - return self.Create(udf, path, "udfs", collection_id, None, options) + return self.Create(udf, path, "udfs", collection_id, None, options, **kwargs) - def UpsertUserDefinedFunction(self, collection_link, udf, options=None): + def UpsertUserDefinedFunction(self, collection_link, udf, options=None, **kwargs): """Upserts a user defined function in a collection. :param str collection_link: @@ -1352,7 +1363,7 @@ def UpsertUserDefinedFunction(self, collection_link, udf, options=None): options = {} collection_id, path, udf = self._GetContainerIdWithPathForUDF(collection_link, udf) - return self.Upsert(udf, path, "udfs", collection_id, None, options) + return self.Upsert(udf, path, "udfs", collection_id, None, options, **kwargs) def _GetContainerIdWithPathForUDF(self, collection_link, udf): # pylint: disable=no-self-use CosmosClientConnection.__ValidateResource(udf) @@ -1366,7 +1377,7 @@ def _GetContainerIdWithPathForUDF(self, collection_link, udf): # pylint: disabl collection_id = base.GetResourceIdOrFullNameFromLink(collection_link) return collection_id, path, udf - def ReadUserDefinedFunction(self, udf_link, options=None): + def ReadUserDefinedFunction(self, udf_link, options=None, **kwargs): """Reads a user defined function. :param str udf_link: @@ -1385,9 +1396,9 @@ def ReadUserDefinedFunction(self, udf_link, options=None): path = base.GetPathFromLink(udf_link) udf_id = base.GetResourceIdOrFullNameFromLink(udf_link) - return self.Read(path, "udfs", udf_id, None, options) + return self.Read(path, "udfs", udf_id, None, options, **kwargs) - def ReadStoredProcedures(self, collection_link, options=None): + def ReadStoredProcedures(self, collection_link, options=None, **kwargs): """Reads all store procedures in a collection. :param str collection_link: @@ -1404,9 +1415,9 @@ def ReadStoredProcedures(self, collection_link, options=None): if options is None: options = {} - return self.QueryStoredProcedures(collection_link, None, options) + return self.QueryStoredProcedures(collection_link, None, options, **kwargs) - def QueryStoredProcedures(self, collection_link, query, options=None): + def QueryStoredProcedures(self, collection_link, query, options=None, **kwargs): """Queries stored procedures in a collection. :param str collection_link: @@ -1430,7 +1441,8 @@ def QueryStoredProcedures(self, collection_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "sprocs", collection_id, lambda r: r["StoredProcedures"], lambda _, b: b, query, options + path, "sprocs", collection_id, lambda r: r["StoredProcedures"], + lambda _, b: b, query, options, **kwargs ), self.last_response_headers, ) @@ -1439,7 +1451,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def CreateStoredProcedure(self, collection_link, sproc, options=None): + def CreateStoredProcedure(self, collection_link, sproc, options=None, **kwargs): """Creates a stored procedure in a collection. :param str collection_link: @@ -1458,9 +1470,9 @@ def CreateStoredProcedure(self, collection_link, sproc, options=None): options = {} collection_id, path, sproc = self._GetContainerIdWithPathForSproc(collection_link, sproc) - return self.Create(sproc, path, "sprocs", collection_id, None, options) + return self.Create(sproc, path, "sprocs", collection_id, None, options, **kwargs) - def UpsertStoredProcedure(self, collection_link, sproc, options=None): + def UpsertStoredProcedure(self, collection_link, sproc, options=None, **kwargs): """Upserts a stored procedure in a collection. :param str collection_link: @@ -1479,7 +1491,7 @@ def UpsertStoredProcedure(self, collection_link, sproc, options=None): options = {} collection_id, path, sproc = self._GetContainerIdWithPathForSproc(collection_link, sproc) - return self.Upsert(sproc, path, "sprocs", collection_id, None, options) + return self.Upsert(sproc, path, "sprocs", collection_id, None, options, **kwargs) def _GetContainerIdWithPathForSproc(self, collection_link, sproc): # pylint: disable=no-self-use CosmosClientConnection.__ValidateResource(sproc) @@ -1492,7 +1504,7 @@ def _GetContainerIdWithPathForSproc(self, collection_link, sproc): # pylint: di collection_id = base.GetResourceIdOrFullNameFromLink(collection_link) return collection_id, path, sproc - def ReadStoredProcedure(self, sproc_link, options=None): + def ReadStoredProcedure(self, sproc_link, options=None, **kwargs): """Reads a stored procedure. :param str sproc_link: @@ -1511,9 +1523,9 @@ def ReadStoredProcedure(self, sproc_link, options=None): path = base.GetPathFromLink(sproc_link) sproc_id = base.GetResourceIdOrFullNameFromLink(sproc_link) - return self.Read(path, "sprocs", sproc_id, None, options) + return self.Read(path, "sprocs", sproc_id, None, options, **kwargs) - def ReadConflicts(self, collection_link, feed_options=None): + def ReadConflicts(self, collection_link, feed_options=None, **kwargs): """Reads conflicts. :param str collection_link: @@ -1529,9 +1541,9 @@ def ReadConflicts(self, collection_link, feed_options=None): if feed_options is None: feed_options = {} - return self.QueryConflicts(collection_link, None, feed_options) + return self.QueryConflicts(collection_link, None, feed_options, **kwargs) - def QueryConflicts(self, collection_link, query, options=None): + def QueryConflicts(self, collection_link, query, options=None, **kwargs): """Queries conflicts in a collection. :param str collection_link: @@ -1555,7 +1567,8 @@ def QueryConflicts(self, collection_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "conflicts", collection_id, lambda r: r["Conflicts"], lambda _, b: b, query, options + path, "conflicts", collection_id, lambda r: r["Conflicts"], + lambda _, b: b, query, options, **kwargs ), self.last_response_headers, ) @@ -1564,7 +1577,7 @@ def fetch_fn(options): self, query, options, fetch_function=fetch_fn, page_iterator_class=query_iterable.QueryIterable ) - def ReadConflict(self, conflict_link, options=None): + def ReadConflict(self, conflict_link, options=None, **kwargs): """Reads a conflict. :param str conflict_link: @@ -1582,9 +1595,9 @@ def ReadConflict(self, conflict_link, options=None): path = base.GetPathFromLink(conflict_link) conflict_id = base.GetResourceIdOrFullNameFromLink(conflict_link) - return self.Read(path, "conflicts", conflict_id, None, options) + return self.Read(path, "conflicts", conflict_id, None, options, **kwargs) - def DeleteContainer(self, collection_link, options=None): + def DeleteContainer(self, collection_link, options=None, **kwargs): """Deletes a collection. :param str collection_link: @@ -1603,9 +1616,9 @@ def DeleteContainer(self, collection_link, options=None): path = base.GetPathFromLink(collection_link) collection_id = base.GetResourceIdOrFullNameFromLink(collection_link) - return self.DeleteResource(path, "colls", collection_id, None, options) + return self.DeleteResource(path, "colls", collection_id, None, options, **kwargs) - def ReplaceItem(self, document_link, new_document, options=None): + def ReplaceItem(self, document_link, new_document, options=None, **kwargs): """Replaces a document and returns it. :param str document_link: @@ -1638,9 +1651,9 @@ def ReplaceItem(self, document_link, new_document, options=None): collection_link = base.GetItemContainerLink(document_link) options = self._AddPartitionKey(collection_link, new_document, options) - return self.Replace(new_document, path, "docs", document_id, None, options) + return self.Replace(new_document, path, "docs", document_id, None, options, **kwargs) - def DeleteItem(self, document_link, options=None): + def DeleteItem(self, document_link, options=None, **kwargs): """Deletes a document. :param str document_link: @@ -1659,9 +1672,9 @@ def DeleteItem(self, document_link, options=None): path = base.GetPathFromLink(document_link) document_id = base.GetResourceIdOrFullNameFromLink(document_link) - return self.DeleteResource(path, "docs", document_id, None, options) + return self.DeleteResource(path, "docs", document_id, None, options, **kwargs) - def CreateAttachment(self, document_link, attachment, options=None): + def CreateAttachment(self, document_link, attachment, options=None, **kwargs): """Creates an attachment in a document. :param str document_link: @@ -1681,9 +1694,9 @@ def CreateAttachment(self, document_link, attachment, options=None): options = {} document_id, path = self._GetItemIdWithPathForAttachment(attachment, document_link) - return self.Create(attachment, path, "attachments", document_id, None, options) + return self.Create(attachment, path, "attachments", document_id, None, options, **kwargs) - def UpsertAttachment(self, document_link, attachment, options=None): + def UpsertAttachment(self, document_link, attachment, options=None, **kwargs): """Upserts an attachment in a document. :param str document_link: @@ -1703,7 +1716,7 @@ def UpsertAttachment(self, document_link, attachment, options=None): options = {} document_id, path = self._GetItemIdWithPathForAttachment(attachment, document_link) - return self.Upsert(attachment, path, "attachments", document_id, None, options) + return self.Upsert(attachment, path, "attachments", document_id, None, options, **kwargs) def _GetItemIdWithPathForAttachment(self, attachment, document_link): # pylint: disable=no-self-use CosmosClientConnection.__ValidateResource(attachment) @@ -1711,7 +1724,7 @@ def _GetItemIdWithPathForAttachment(self, attachment, document_link): # pylint: document_id = base.GetResourceIdOrFullNameFromLink(document_link) return document_id, path - def CreateAttachmentAndUploadMedia(self, document_link, readable_stream, options=None): + def CreateAttachmentAndUploadMedia(self, document_link, readable_stream, options=None, **kwargs): """Creates an attachment and upload media. :param str document_link: @@ -1730,9 +1743,9 @@ def CreateAttachmentAndUploadMedia(self, document_link, readable_stream, options options = {} document_id, initial_headers, path = self._GetItemIdWithPathForAttachmentMedia(document_link, options) - return self.Create(readable_stream, path, "attachments", document_id, initial_headers, options) + return self.Create(readable_stream, path, "attachments", document_id, initial_headers, options, **kwargs) - def UpsertAttachmentAndUploadMedia(self, document_link, readable_stream, options=None): + def UpsertAttachmentAndUploadMedia(self, document_link, readable_stream, options=None, **kwargs): """Upserts an attachment and upload media. :param str document_link: @@ -1751,7 +1764,7 @@ def UpsertAttachmentAndUploadMedia(self, document_link, readable_stream, options options = {} document_id, initial_headers, path = self._GetItemIdWithPathForAttachmentMedia(document_link, options) - return self.Upsert(readable_stream, path, "attachments", document_id, initial_headers, options) + return self.Upsert(readable_stream, path, "attachments", document_id, initial_headers, options, **kwargs) def _GetItemIdWithPathForAttachmentMedia(self, document_link, options): initial_headers = dict(self.default_headers) @@ -1769,7 +1782,7 @@ def _GetItemIdWithPathForAttachmentMedia(self, document_link, options): document_id = base.GetResourceIdOrFullNameFromLink(document_link) return document_id, initial_headers, path - def ReadAttachment(self, attachment_link, options=None): + def ReadAttachment(self, attachment_link, options=None, **kwargs): """Reads an attachment. :param str attachment_link: @@ -1788,9 +1801,9 @@ def ReadAttachment(self, attachment_link, options=None): path = base.GetPathFromLink(attachment_link) attachment_id = base.GetResourceIdOrFullNameFromLink(attachment_link) - return self.Read(path, "attachments", attachment_id, None, options) + return self.Read(path, "attachments", attachment_id, None, options, **kwargs) - def ReadAttachments(self, document_link, options=None): + def ReadAttachments(self, document_link, options=None, **kwargs): """Reads all attachments in a document. :param str document_link: @@ -1807,9 +1820,9 @@ def ReadAttachments(self, document_link, options=None): if options is None: options = {} - return self.QueryAttachments(document_link, None, options) + return self.QueryAttachments(document_link, None, options, **kwargs) - def QueryAttachments(self, document_link, query, options=None): + def QueryAttachments(self, document_link, query, options=None, **kwargs): """Queries attachments in a document. :param str document_link: @@ -1833,7 +1846,8 @@ def QueryAttachments(self, document_link, query, options=None): def fetch_fn(options): return ( self.__QueryFeed( - path, "attachments", document_id, lambda r: r["Attachments"], lambda _, b: b, query, options + path, "attachments", document_id, lambda r: r["Attachments"], + lambda _, b: b, query, options, **kwargs ), self.last_response_headers, ) @@ -1911,7 +1925,7 @@ def UpdateMedia(self, media_link, readable_stream, options=None, **kwargs): self._UpdateSessionIfRequired(headers, result, self.last_response_headers) return result - def ReplaceAttachment(self, attachment_link, attachment, options=None): + def ReplaceAttachment(self, attachment_link, attachment, options=None, **kwargs): """Replaces an attachment and returns it. :param str attachment_link: @@ -1932,9 +1946,9 @@ def ReplaceAttachment(self, attachment_link, attachment, options=None): CosmosClientConnection.__ValidateResource(attachment) path = base.GetPathFromLink(attachment_link) attachment_id = base.GetResourceIdOrFullNameFromLink(attachment_link) - return self.Replace(attachment, path, "attachments", attachment_id, None, options) + return self.Replace(attachment, path, "attachments", attachment_id, None, options, **kwargs) - def DeleteAttachment(self, attachment_link, options=None): + def DeleteAttachment(self, attachment_link, options=None, **kwargs): """Deletes an attachment. :param str attachment_link: @@ -1953,9 +1967,9 @@ def DeleteAttachment(self, attachment_link, options=None): path = base.GetPathFromLink(attachment_link) attachment_id = base.GetResourceIdOrFullNameFromLink(attachment_link) - return self.DeleteResource(path, "attachments", attachment_id, None, options) + return self.DeleteResource(path, "attachments", attachment_id, None, options, **kwargs) - def ReplaceTrigger(self, trigger_link, trigger, options=None): + def ReplaceTrigger(self, trigger_link, trigger, options=None, **kwargs): """Replaces a trigger and returns it. :param str trigger_link: @@ -1982,9 +1996,9 @@ def ReplaceTrigger(self, trigger_link, trigger, options=None): path = base.GetPathFromLink(trigger_link) trigger_id = base.GetResourceIdOrFullNameFromLink(trigger_link) - return self.Replace(trigger, path, "triggers", trigger_id, None, options) + return self.Replace(trigger, path, "triggers", trigger_id, None, options, **kwargs) - def DeleteTrigger(self, trigger_link, options=None): + def DeleteTrigger(self, trigger_link, options=None, **kwargs): """Deletes a trigger. :param str trigger_link: @@ -2003,9 +2017,9 @@ def DeleteTrigger(self, trigger_link, options=None): path = base.GetPathFromLink(trigger_link) trigger_id = base.GetResourceIdOrFullNameFromLink(trigger_link) - return self.DeleteResource(path, "triggers", trigger_id, None, options) + return self.DeleteResource(path, "triggers", trigger_id, None, options, **kwargs) - def ReplaceUserDefinedFunction(self, udf_link, udf, options=None): + def ReplaceUserDefinedFunction(self, udf_link, udf, options=None, **kwargs): """Replaces a user defined function and returns it. :param str udf_link: @@ -2032,9 +2046,9 @@ def ReplaceUserDefinedFunction(self, udf_link, udf, options=None): path = base.GetPathFromLink(udf_link) udf_id = base.GetResourceIdOrFullNameFromLink(udf_link) - return self.Replace(udf, path, "udfs", udf_id, None, options) + return self.Replace(udf, path, "udfs", udf_id, None, options, **kwargs) - def DeleteUserDefinedFunction(self, udf_link, options=None): + def DeleteUserDefinedFunction(self, udf_link, options=None, **kwargs): """Deletes a user defined function. :param str udf_link: @@ -2053,7 +2067,7 @@ def DeleteUserDefinedFunction(self, udf_link, options=None): path = base.GetPathFromLink(udf_link) udf_id = base.GetResourceIdOrFullNameFromLink(udf_link) - return self.DeleteResource(path, "udfs", udf_id, None, options) + return self.DeleteResource(path, "udfs", udf_id, None, options, **kwargs) def ExecuteStoredProcedure(self, sproc_link, params, options=None, **kwargs): """Executes a store procedure. @@ -2089,7 +2103,7 @@ def ExecuteStoredProcedure(self, sproc_link, params, options=None, **kwargs): result, self.last_response_headers = self.__Post(path, request_params, params, headers, **kwargs) return result - def ReplaceStoredProcedure(self, sproc_link, sproc, options=None): + def ReplaceStoredProcedure(self, sproc_link, sproc, options=None, **kwargs): """Replaces a stored procedure and returns it. :param str sproc_link: @@ -2116,9 +2130,9 @@ def ReplaceStoredProcedure(self, sproc_link, sproc, options=None): path = base.GetPathFromLink(sproc_link) sproc_id = base.GetResourceIdOrFullNameFromLink(sproc_link) - return self.Replace(sproc, path, "sprocs", sproc_id, None, options) + return self.Replace(sproc, path, "sprocs", sproc_id, None, options, **kwargs) - def DeleteStoredProcedure(self, sproc_link, options=None): + def DeleteStoredProcedure(self, sproc_link, options=None, **kwargs): """Deletes a stored procedure. :param str sproc_link: @@ -2137,9 +2151,9 @@ def DeleteStoredProcedure(self, sproc_link, options=None): path = base.GetPathFromLink(sproc_link) sproc_id = base.GetResourceIdOrFullNameFromLink(sproc_link) - return self.DeleteResource(path, "sprocs", sproc_id, None, options) + return self.DeleteResource(path, "sprocs", sproc_id, None, options, **kwargs) - def DeleteConflict(self, conflict_link, options=None): + def DeleteConflict(self, conflict_link, options=None, **kwargs): """Deletes a conflict. :param str conflict_link: @@ -2158,9 +2172,9 @@ def DeleteConflict(self, conflict_link, options=None): path = base.GetPathFromLink(conflict_link) conflict_id = base.GetResourceIdOrFullNameFromLink(conflict_link) - return self.DeleteResource(path, "conflicts", conflict_id, None, options) + return self.DeleteResource(path, "conflicts", conflict_id, None, options, **kwargs) - def ReplaceOffer(self, offer_link, offer): + def ReplaceOffer(self, offer_link, offer, **kwargs): """Replaces an offer and returns it. :param str offer_link: @@ -2176,9 +2190,9 @@ def ReplaceOffer(self, offer_link, offer): CosmosClientConnection.__ValidateResource(offer) path = base.GetPathFromLink(offer_link) offer_id = base.GetResourceIdOrFullNameFromLink(offer_link) - return self.Replace(offer, path, "offers", offer_id, None, None) + return self.Replace(offer, path, "offers", offer_id, None, None, **kwargs) - def ReadOffer(self, offer_link): + def ReadOffer(self, offer_link, **kwargs): """Reads an offer. :param str offer_link: @@ -2192,9 +2206,9 @@ def ReadOffer(self, offer_link): """ path = base.GetPathFromLink(offer_link) offer_id = base.GetResourceIdOrFullNameFromLink(offer_link) - return self.Read(path, "offers", offer_id, None, {}) + return self.Read(path, "offers", offer_id, None, {}, **kwargs) - def ReadOffers(self, options=None): + def ReadOffers(self, options=None, **kwargs): """Reads all offers. :param dict options: @@ -2209,9 +2223,9 @@ def ReadOffers(self, options=None): if options is None: options = {} - return self.QueryOffers(None, options) + return self.QueryOffers(None, options, **kwargs) - def QueryOffers(self, query, options=None): + def QueryOffers(self, query, options=None, **kwargs): """Query for all offers. :param (str or dict) query: @@ -2229,7 +2243,9 @@ def QueryOffers(self, query, options=None): def fetch_fn(options): return ( - self.__QueryFeed("/offers", "offers", "", lambda r: r["Offers"], lambda _, b: b, query, options), + self.__QueryFeed( + "/offers", "offers", "", lambda r: r["Offers"], lambda _, b: b, query, options, **kwargs + ), self.last_response_headers, ) @@ -2533,7 +2549,7 @@ def __Delete(self, path, request_params, headers, **kwargs): **kwargs ) - def QueryFeed(self, path, collection_id, query, options, partition_key_range_id=None): + def QueryFeed(self, path, collection_id, query, options, partition_key_range_id=None, **kwargs): """Query Feed for Document Collection resource. :param str path: @@ -2559,6 +2575,7 @@ def QueryFeed(self, path, collection_id, query, options, partition_key_range_id= query, options, partition_key_range_id, + **kwargs ), self.last_response_headers, ) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index 9c7f9588fe82..53044d63ca84 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -113,7 +113,7 @@ def read( populate_quota_info=None, # type: Optional[bool] **kwargs # type: Any ): - # type: (...) -> Container + # type: (...) -> Dict[str, Any] """ Read the container properties :param session_token: Token for use with Session consistency. @@ -126,7 +126,7 @@ def read( :param response_hook: a callable invoked with the response metadata :raise `CosmosHttpResponseError`: Raised if the container couldn't be retrieved. This includes if the container does not exist. - :returns: :class:`Container` instance representing the retrieved container. + :returns: Dict representing the retrieved container. """ request_options = build_options(kwargs) @@ -152,7 +152,7 @@ def read_item( item, # type: Union[str, Dict[str, Any]] partition_key, # type: Any populate_query_metrics=None, # type: Optional[bool] - post_trigger_include=None, # type: Optioanl[str] + post_trigger_include=None, # type: Optional[str] **kwargs # type: Any ): # type: (...) -> Dict[str, str] diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index bae3e6228b7b..de4f4a2bad1d 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -22,7 +22,7 @@ """Create, read, and delete databases in the Azure Cosmos DB SQL API service. """ -from typing import Any, Dict, Mapping, Optional, Union, cast, Iterable +from typing import Any, Dict, Mapping, Optional, Union, cast, Iterable, List import six from azure.core.tracing.decorator import distributed_trace diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index fbb3a10f0863..a06695d238f1 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -22,7 +22,7 @@ """Create, read, update and delete containers in the Azure Cosmos DB SQL API service. """ -from typing import Any, List, Dict, Mapping, Union, cast, Iterable +from typing import Any, List, Dict, Mapping, Union, cast, Iterable, Optional import six from azure.core.tracing.decorator import distributed_trace @@ -142,7 +142,7 @@ def read(self, populate_query_metrics=None, **kwargs): def create_container( self, id, # type: str # pylint: disable=redefined-builtin - partition_key, # type: PartitionKey + partition_key, # type: Any indexing_policy=None, # type: Optional[Dict[str, Any]] default_ttl=None, # type: Optional[int] populate_query_metrics=None, # type: Optional[bool] @@ -356,7 +356,7 @@ def query_containers( def replace_container( self, container, # type: Union[str, ContainerClient, Dict[str, Any]] - partition_key, # type: PartitionKey + partition_key, # type: Any indexing_policy=None, # type: Optional[Dict[str, Any]] default_ttl=None, # type: Optional[int] conflict_resolution_policy=None, # type: Optional[Dict[str, Any]] diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/offer.py b/sdk/cosmos/azure-cosmos/azure/cosmos/offer.py index 4e7b240d1c8f..c4087542f003 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/offer.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/offer.py @@ -21,6 +21,7 @@ """Represents an offer in the Azure Cosmos DB SQL API service. """ +from typing import Dict, Any class Offer(dict): diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py b/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py index e2ca6eaa5d46..1e81c332eb01 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py @@ -21,6 +21,7 @@ """Represents a Permission object in the Azure Cosmos DB SQL API service. """ +from typing import Dict, Any from .documents import PermissionMode diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py index b7cce04c04d2..b14aa1284a2d 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py @@ -22,7 +22,7 @@ """Create, read, update and delete and execute scripts in the Azure Cosmos DB SQL API service. """ -from typing import Any, List, Dict, Union, Iterable +from typing import Any, List, Dict, Union, Iterable, Optional import six @@ -148,7 +148,7 @@ def replace_stored_procedure(self, sproc, body, **kwargs): ) def delete_stored_procedure(self, sproc, **kwargs): - # type: (Union[str, Dict[str, Any]], **kwargs) -> None + # type: (Union[str, Dict[str, Any]], Any) -> None """ Delete the specified stored procedure from the container. :param sproc: The ID (name) or dict representing stored procedure to be deleted. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py index 7be0cf19409a..88b063233d92 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py @@ -24,7 +24,7 @@ """Create, read, update and delete permissions in the Azure Cosmos DB SQL API service. """ -from typing import Any, List, Dict, Union, cast, Iterable +from typing import Any, List, Dict, Union, cast, Iterable, Optional import six from azure.core.tracing.decorator import distributed_trace From ba45e57f5b107044f3e1514331ef467ccb0c3cd5 Mon Sep 17 00:00:00 2001 From: antisch Date: Wed, 4 Sep 2019 21:37:24 -0700 Subject: [PATCH 30/46] Fix pylint --- sdk/cosmos/azure-cosmos/azure/cosmos/_base.py | 2 +- .../azure/cosmos/_cosmos_client_connection.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py index 5f0143c2bd1b..3ca995657948 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py @@ -27,10 +27,10 @@ import json import uuid import binascii +from typing import Dict, Any import six from six.moves.urllib.parse import quote as urllib_quote -from typing import Dict, Any from . import auth from . import documents diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index 8603a7d3e134..fdd342ea57ca 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -549,7 +549,10 @@ def QueryUsers(self, database_link, query, options=None, **kwargs): def fetch_fn(options): return ( - self.__QueryFeed(path, "users", database_id, lambda r: r["Users"], lambda _, b: b, query, options, **kwargs), + self.__QueryFeed( + path, "users", database_id, lambda r: r["Users"], + lambda _, b: b, query, options, **kwargs + ), self.last_response_headers, ) @@ -807,7 +810,15 @@ def ReadItems(self, collection_link, feed_options=None, response_hook=None, **kw return self.QueryItems(collection_link, None, feed_options, response_hook=response_hook, **kwargs) - def QueryItems(self, database_or_container_link, query, options=None, partition_key=None, response_hook=None, **kwargs): + def QueryItems( + self, + database_or_container_link, + query, + options=None, + partition_key=None, + response_hook=None, + **kwargs + ): """Queries documents in a collection. :param str database_or_container_link: From 338209c18e9b28aca2ed671dde86d42be631e2c0 Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 5 Sep 2019 08:56:49 -0700 Subject: [PATCH 31/46] Some mypy fixes --- .../azure/cosmos/_cosmos_client_connection.py | 6 +++--- .../azure/cosmos/container_client.py | 10 ++++++---- .../azure-cosmos/azure/cosmos/cosmos_client.py | 8 ++++---- .../azure/cosmos/database_client.py | 17 +++++++++++------ .../azure-cosmos/azure/cosmos/permission.py | 4 ++-- .../azure-cosmos/azure/cosmos/user_client.py | 8 ++++---- 6 files changed, 30 insertions(+), 23 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index fdd342ea57ca..58d734201ee7 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -119,9 +119,9 @@ def __init__( self.connection_policy = connection_policy or ConnectionPolicy() - self.partition_resolvers = {} + self.partition_resolvers = {} # type: Dict[str, Any] - self.partition_key_definition_cache = {} + self.partition_key_definition_cache = {} # type: Dict[str, Any] self.default_headers = { http_constants.HttpHeaders.CacheControl: "no-cache", @@ -143,7 +143,7 @@ def __init__( # via setter self.session = _session.Session(self.url_connection) else: - self.session = None + self.session = None # type: ignore self._useMultipleWriteLocations = False self._global_endpoint_manager = global_endpoint_manager._GlobalEndpointManager(self) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py index 53044d63ca84..5826872a2401 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py @@ -63,12 +63,12 @@ def __init__(self, client_connection, database_link, id, properties=None): # py self._properties = properties self.container_link = u"{}/colls/{}".format(database_link, self.id) self._is_system_key = None - self._scripts = None + self._scripts = None # type: ScriptsClient def _get_properties(self): # type: () -> Dict[str, Any] if self._properties is None: - self.read() + self._properties = self.read() return self._properties @property @@ -78,7 +78,7 @@ def is_system_key(self): properties = self._get_properties() self._is_system_key = ( properties["partitionKey"]["systemKey"] if "systemKey" in properties["partitionKey"] else False - ) + ) # type: bool return self._is_system_key @property @@ -139,7 +139,9 @@ def read( request_options["populateQuotaInfo"] = populate_quota_info collection_link = self.container_link - self._properties = self.client_connection.ReadContainer(collection_link, options=request_options, **kwargs) + self._properties = self.client_connection.ReadContainer( + collection_link, options=request_options, **kwargs + ) # type: Dict[str, Any] if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index de4f4a2bad1d..31073ad61e62 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -38,7 +38,7 @@ def _parse_connection_str(conn_str, credential): # type: (str, Optional[Any]) -> Dict[str, str] conn_str = conn_str.rstrip(";") - conn_settings = dict( # pylint: disable=consider-using-dict-comprehension + conn_settings = dict( # pylint: disable=consider-using-dict-comprehension # type: ignore [s.split("=", 1) for s in conn_str.split(";")] ) if 'AccountEndpoint' not in conn_settings: @@ -111,7 +111,7 @@ class CosmosClient(object): """ def __init__(self, url, credential, consistency_level="Session", **kwargs): - # type: (str, Dict[str, str], str, ConnectionPolicy) -> None + # type: (str, Any, str, ConnectionPolicy) -> None """ Instantiate a new CosmosClient. :param url: The URL of the Cosmos DB account. @@ -158,7 +158,7 @@ def from_connection_string(cls, conn_str, credential=None, consistency_level="Se @staticmethod def _get_database_link(database_or_id): - # type: (str) -> str + # type: (Union[DatabaseClient, str, Dict[str, str]]) -> str if isinstance(database_or_id, six.string_types): return "dbs/{}".format(database_or_id) try: @@ -305,7 +305,7 @@ def query_databases( # (just returning a generator did not initiate the first network call, so # the headers were misleading) # This needs to change for "real" implementation - query = query if parameters is None else dict(query=query, parameters=parameters) + query = query if parameters is None else dict(query=query, parameters=parameters) # type: ignore result = self.client_connection.QueryDatabases(query=query, options=feed_options, **kwargs) else: result = self.client_connection.ReadDatabases(options=feed_options, **kwargs) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py index a06695d238f1..c4e9bfbc1919 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py @@ -102,7 +102,7 @@ def _get_user_link(self, user_or_id): def _get_properties(self): # type: () -> Dict[str, Any] if self._properties is None: - self.read() + self._properties = self.read() return self._properties @distributed_trace @@ -131,7 +131,9 @@ def read(self, populate_query_metrics=None, **kwargs): if populate_query_metrics is not None: request_options["populateQueryMetrics"] = populate_query_metrics - self._properties = self.client_connection.ReadDatabase(database_link, options=request_options, **kwargs) + self._properties = self.client_connection.ReadDatabase( + database_link, options=request_options, **kwargs + ) # type: Dict[str, Any] if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) @@ -581,15 +583,18 @@ def replace_user( request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) - user = self.client_connection.ReplaceUser( + replaced_user = self.client_connection.ReplaceUser( user_link=self._get_user_link(user), user=body, options=request_options, **kwargs - ) + ) # type: Dict[str, str] if response_hook: - response_hook(self.client_connection.last_response_headers, user) + response_hook(self.client_connection.last_response_headers, replaced_user) return UserClient( - client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user + client_connection=self.client_connection, + id=replaced_user["id"], + database_link=self.database_link, + properties=replaced_user ) @distributed_trace diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py b/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py index 1e81c332eb01..3432e741de8c 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/permission.py @@ -21,14 +21,14 @@ """Represents a Permission object in the Azure Cosmos DB SQL API service. """ -from typing import Dict, Any +from typing import Dict, Any, Union from .documents import PermissionMode class Permission(object): def __init__(self, id, user_link, permission_mode, resource_link, properties): # pylint: disable=redefined-builtin - # type: (str, str, PermissionMode, str, Dict[str, Any]) -> None + # type: (str, str, Union[str, PermissionMode], str, Dict[str, Any]) -> None self.id = id self.user_link = user_link self.permission_mode = permission_mode diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py index 88b063233d92..45cb8f7711c3 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py @@ -56,12 +56,12 @@ def _get_permission_link(self, permission_or_id): def _get_properties(self): # type: () -> Dict[str, Any] if self._properties is None: - self.read() + self._properties = self.read() return self._properties @distributed_trace def read(self, **kwargs): - # type: (Any) -> UserClient + # type: (Any) -> Dict[str, Any] """ Read user propertes. @@ -159,7 +159,7 @@ def get_permission(self, permission, **kwargs): permission = self.client_connection.ReadPermission( permission_link=self._get_permission_link(permission), options=request_options, **kwargs - ) + ) # type: Dict[str, str] if response_hook: response_hook(self.client_connection.last_response_headers, permission) @@ -254,7 +254,7 @@ def replace_permission(self, permission, body, **kwargs): permission = self.client_connection.ReplacePermission( permission_link=self._get_permission_link(permission), permission=body, options=request_options, **kwargs - ) + ) # type: Dict[str, str] if response_hook: response_hook(self.client_connection.last_response_headers, permission) From e6a427b877711c1427cfae91398652327b5a0b22 Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 5 Sep 2019 10:16:14 -0700 Subject: [PATCH 32/46] Updated readme and release notes --- sdk/cosmos/azure-cosmos/HISTORY.md | 41 ++++++++++++++++++++++++++++++ sdk/cosmos/azure-cosmos/README.md | 16 +++++++----- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/HISTORY.md b/sdk/cosmos/azure-cosmos/HISTORY.md index 28da1d733e79..d04e05dc1c0c 100644 --- a/sdk/cosmos/azure-cosmos/HISTORY.md +++ b/sdk/cosmos/azure-cosmos/HISTORY.md @@ -1,4 +1,45 @@ # Change Log azure-cosmos + +## Version 4.0.0b2: + +Version 4.0.0b2 is the second iteration in our efforts to build a more Python client library. +For more information about this, and preview releases of other Azure SDK libraries, please visit https://aka.ms/azure-sdk-preview1-python. + +**Breaking changes** + +- The client connection has been adapted to consume the HTTP pipeline defined in `azure.core.pipeline`. +- Interactive objects have now been renamed as "clients". This includes: + - `Database` -> `DatabaseClient` + - `User` -> `UserClient` + - `Container` -> `ContainerClient` + - `Scripts` -> `ScriptsClient` +- The constructor of `CosmosClient` has been updated: + - The `auth` parameter has been renamed to `credential` and will now take an authentication type directly. This means the master key value, a dictionary of resource tokens, or a list of permissions can be passed in. However the old dictionary format is still supported. + - The `connection_policy` parameter has been made a keyword only parameter, and while it is still supported, each of the individual attributes of the policy can now be passed in as explicit keyword arguments: + - `request_timeout` + - `media_request_timeout` + - `connection_mode` + - `media_read_mode` + - `proxy_config` + - `enable_endpoint_discovery` + - `preferred_locations` + - `multiple_write_locations` +- A new classmethod constructor has been added to `CosmosClient` to enable creation via a connection string retrieved from the Azure portal. +- All `read_all` operations have been renamed to `list` operations: + - `Container.read_all_conflicts` -> `ContainerClient.list_conflicts` + - `Container.read_all_items` -> `ContainerClient.list_items` + - `CosmosClient.read_all_databases` -> `CosmosClient.list_database` + - `Database.read_all_containers` -> `DatabaseClient.list_containers` + - `Database.read_all_users` -> `DatabaseClient.list_users` + - `User.read_all_permissions` -> `UserClient.list_permissions` +- All operations that take `request_options` or `feed_options` parameters, these have been moved to keyword only parameters. In addition, while these options dictionaries are still supported, each of the individual options within the dictionary are now supported as explicit keyword arguments. +- The error heirarchy is now inherited from `azure.core.AzureError` instead of `CosmosError` which has been removed. + - `HTTPFailure` has been renamed to `CosmosHttpResponseError` + - `JSONParseFailure` has been removed and replaced by `azure.core.DecodeError` + - Added ` CosmosResourceNotFoundError` and `CosmosResourceExistsError` for specific conflict response codes. +- `CosmosClient` can now be run in a context manager to handle closing the client connection. +- Iterable responses (e.g. query responses and list responses) are now of type `azure.core.paging.ItemPaged`. The method `fetch_next_block` has been replaced by a secondary iterator, accessed by the `by_page` method. + ## Version 4.0.0b1: Version 4.0.0b1 is the first preview of our efforts to create a user-friendly and Pythonic client library for Azure Cosmos. For more information about this, and preview releases of other Azure SDK libraries, please visit https://aka.ms/azure-sdk-preview1-python. diff --git a/sdk/cosmos/azure-cosmos/README.md b/sdk/cosmos/azure-cosmos/README.md index 9199973f08dd..b2725f3be229 100644 --- a/sdk/cosmos/azure-cosmos/README.md +++ b/sdk/cosmos/azure-cosmos/README.md @@ -75,9 +75,9 @@ client = CosmosClient(url, credential=key) Once you've initialized a [CosmosClient][ref_cosmosclient], you can interact with the primary resource types in Cosmos DB: -* [Database][ref_database]: A Cosmos DB account can contain multiple databases. When you create a database, you specify the API you'd like to use when interacting with its documents: SQL, MongoDB, Gremlin, Cassandra, or Azure Table. Use the [Database][ref_database] object to manage its containers. +* [Database][ref_database]: A Cosmos DB account can contain multiple databases. When you create a database, you specify the API you'd like to use when interacting with its documents: SQL, MongoDB, Gremlin, Cassandra, or Azure Table. Use the [DatabaseClient][ref_database] object to manage its containers. -* [Container][ref_container]: A container is a collection of JSON documents. You create (insert), read, update, and delete items in a container by using methods on the [Container][ref_container] object. +* [Container][ref_container]: A container is a collection of JSON documents. You create (insert), read, update, and delete items in a container by using methods on the [ContainerClient][ref_container] object. * [Item][ref_item]: An Item is the dictionary-like representation of a JSON document stored in a container. Each Item you add to a container must include an `id` key with a value that uniquely identifies the item within the container. @@ -106,6 +106,8 @@ try: database = client.create_database(database_name) except errors.CosmosResourceExistsError: database = client.get_database_client(database_name) +except errors.CosmosHttpResponseError: + raise ``` ### Create a container @@ -135,7 +137,7 @@ container = database.get_container_client(container_name) ### Insert data -To insert items into a container, pass a dictionary containing your data to [Container.upsert_item][ref_container_upsert_item]. Each item you add to a container must include an `id` key with a value that uniquely identifies the item within the container. +To insert items into a container, pass a dictionary containing your data to [ContainerClient.upsert_item][ref_container_upsert_item]. Each item you add to a container must include an `id` key with a value that uniquely identifies the item within the container. This example inserts several items into the container, each with a unique `id`: @@ -154,7 +156,7 @@ for i in range(1, 10): ### Delete data -To delete items from a container, use [Container.delete_item][ref_container_delete_item]. The SQL API in Cosmos DB does not support the SQL `DELETE` statement. +To delete items from a container, use [ContainerClient.delete_item][ref_container_delete_item]. The SQL API in Cosmos DB does not support the SQL `DELETE` statement. ```Python for item in container.query_items(query='SELECT * FROM products p WHERE p.productModel = "DISCONTINUED"', @@ -164,7 +166,7 @@ for item in container.query_items(query='SELECT * FROM products p WHERE p.produc ### Query the database -A Cosmos DB SQL API database supports querying the items in a container with [Container.query_items][ref_container_query_items] using SQL-like syntax. +A Cosmos DB SQL API database supports querying the items in a container with [ContainerClient.query_items][ref_container_query_items] using SQL-like syntax. This example queries a container for items with a specific `id`: @@ -182,7 +184,7 @@ for item in container.query_items( > NOTE: Although you can specify any value for the container name in the `FROM` clause, we recommend you use the container name for consistency. -Perform parameterized queries by passing a dictionary containing the parameters and their values to [Container.query_items][ref_container_query_items]: +Perform parameterized queries by passing a dictionary containing the parameters and their values to [ContainerClient.query_items][ref_container_query_items]: ```Python discontinued_items = container.query_items( @@ -240,7 +242,7 @@ For example, if you try to create a container using an ID (name) that's already try: database.create_container(id=container_name, partition_key=PartitionKey(path="/productName") except errors.CosmosResourceExistsError: - print("Error creating container.") + print("""Error creating container HTTP status code 409: The ID (name) provided for the container is already in use. The container name must be unique within the database.""") From 404dae07bc0fd13db09595dfca07d37386b36ac2 Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 5 Sep 2019 10:39:00 -0700 Subject: [PATCH 33/46] Fix for passing in extra headers --- .../azure/cosmos/_cosmos_client_connection.py | 28 +++++++++---------- sdk/cosmos/azure-cosmos/test/crud_tests.py | 6 +++- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index 58d734201ee7..d74c9c422268 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -2458,12 +2458,12 @@ def DeleteResource(self, path, typ, id, initial_headers, options=None, **kwargs) return result - def __Get(self, path, request_params, headers, **kwargs): + def __Get(self, path, request_params, req_headers, **kwargs): """Azure Cosmos 'GET' http request. :params str url: :params str path: - :params dict headers: + :params dict req_headers: :return: Tuple of (result, headers). @@ -2471,7 +2471,7 @@ def __Get(self, path, request_params, headers, **kwargs): tuple of (dict, dict) """ - request = self.pipeline_client.get(url=path, headers=headers) + request = self.pipeline_client.get(url=path, headers=req_headers) return synchronized_request.SynchronizedRequest( client=self, request_params=request_params, @@ -2483,13 +2483,13 @@ def __Get(self, path, request_params, headers, **kwargs): **kwargs ) - def __Post(self, path, request_params, body, headers, **kwargs): + def __Post(self, path, request_params, body, req_headers, **kwargs): """Azure Cosmos 'POST' http request. :params str url: :params str path: :params (str, unicode, dict) body: - :params dict headers: + :params dict req_headers: :return: Tuple of (result, headers). @@ -2497,7 +2497,7 @@ def __Post(self, path, request_params, body, headers, **kwargs): tuple of (dict, dict) """ - request = self.pipeline_client.post(url=path, headers=headers) + request = self.pipeline_client.post(url=path, headers=req_headers) return synchronized_request.SynchronizedRequest( client=self, request_params=request_params, @@ -2509,13 +2509,13 @@ def __Post(self, path, request_params, body, headers, **kwargs): **kwargs ) - def __Put(self, path, request_params, body, headers, **kwargs): + def __Put(self, path, request_params, body, req_headers, **kwargs): """Azure Cosmos 'PUT' http request. :params str url: :params str path: :params (str, unicode, dict) body: - :params dict headers: + :params dict req_headers: :return: Tuple of (result, headers). @@ -2523,7 +2523,7 @@ def __Put(self, path, request_params, body, headers, **kwargs): tuple of (dict, dict) """ - request = self.pipeline_client.put(url=path, headers=headers) + request = self.pipeline_client.put(url=path, headers=req_headers) return synchronized_request.SynchronizedRequest( client=self, request_params=request_params, @@ -2535,12 +2535,12 @@ def __Put(self, path, request_params, body, headers, **kwargs): **kwargs ) - def __Delete(self, path, request_params, headers, **kwargs): + def __Delete(self, path, request_params, req_headers, **kwargs): """Azure Cosmos 'DELETE' http request. :params str url: :params str path: - :params dict headers: + :params dict req_headers: :return: Tuple of (result, headers). @@ -2548,7 +2548,7 @@ def __Delete(self, path, request_params, headers, **kwargs): tuple of (dict, dict) """ - request = self.pipeline_client.delete(url=path, headers=headers) + request = self.pipeline_client.delete(url=path, headers=req_headers) return synchronized_request.SynchronizedRequest( client=self, request_params=request_params, @@ -2663,8 +2663,8 @@ def __GetBodiesFromQueryResult(result): # Query operations will use ReadEndpoint even though it uses POST(for regular query operations) request_params = _request_object.RequestObject(typ, documents._OperationType.SqlQuery) - headers = base.GetHeaders(self, initial_headers, "post", path, id_, typ, options, partition_key_range_id) - result, self.last_response_headers = self.__Post(path, request_params, query, headers, **kwargs) + req_headers = base.GetHeaders(self, initial_headers, "post", path, id_, typ, options, partition_key_range_id) + result, self.last_response_headers = self.__Post(path, request_params, query, req_headers, **kwargs) if response_hook: response_hook(self.last_response_headers, result) diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index 4db490c7a953..d35445e4320a 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -1854,7 +1854,11 @@ def test_create_indexing_policy_with_composite_and_spatial_indexes(self): created_container = db.create_container( id='composite_index_spatial_index' + str(uuid.uuid4()), indexing_policy=indexing_policy, - partition_key=PartitionKey(path='/id', kind='Hash') + partition_key=PartitionKey(path='/id', kind='Hash'), + headers={"Foo":"bar"}, + user_agent="blah", + user_agent_overwrite=True, + logging_enable=True ) created_properties = created_container.read() read_indexing_policy = created_properties['indexingPolicy'] From 11509db9c5f4cb60bd0fac5204c9c5a8f1e9914d Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 5 Sep 2019 12:23:55 -0700 Subject: [PATCH 34/46] Reverted credentials --- .../azure/cosmos/_cosmos_client_connection.py | 1 - sdk/cosmos/azure-cosmos/test/test_config.py | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index d74c9c422268..ef2e45c0abea 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -385,7 +385,6 @@ def CreateContainer(self, database_link, collection, options=None, **kwargs): :rtype: dict """ - print("KWARGS", kwargs) if options is None: options = {} diff --git a/sdk/cosmos/azure-cosmos/test/test_config.py b/sdk/cosmos/azure-cosmos/test/test_config.py index b1ca862c2c36..ddea65ee64d5 100644 --- a/sdk/cosmos/azure-cosmos/test/test_config.py +++ b/sdk/cosmos/azure-cosmos/test/test_config.py @@ -38,18 +38,18 @@ class _test_config(object): #[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Cosmos DB Emulator Key")] - masterKey = os.getenv('ACCOUNT_KEY', 'Lq2gvNa7V3bTpYmRmOKr7OtUgLWp1HwjUEwqld4A4Fx5roQfM2cbHCSxRsGVFUeEA9Ndmo3cD15TdXtTSPxRoQ==') - host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') + masterKey = os.getenv('ACCOUNT_KEY', 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==') + host = os.getenv('ACCOUNT_HOST', 'https://localhost:443/') connection_str = os.getenv('ACCOUNT_CONNECTION_STR', 'AccountEndpoint={};AccountKey={};'.format(host, masterKey)) connectionPolicy = documents.ConnectionPolicy() connectionPolicy.DisableSSLVerification = True - global_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') - write_location_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') - read_location_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') - read_location2_host = os.getenv('ACCOUNT_HOST', 'https://pytest.documents.azure.com:443/') - global_masterKey = os.getenv('ACCOUNT_KEY', 'C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==') + global_host = '[YOUR_GLOBAL_ENDPOINT_HERE]' + write_location_host = '[YOUR_WRITE_ENDPOINT_HERE]' + read_location_host = '[YOUR_READ_ENDPOINT_HERE]' + read_location2_host = '[YOUR_READ_ENDPOINT2_HERE]' + global_masterKey = '[YOUR_KEY_HERE]' write_location = '[YOUR_WRITE_LOCATION_HERE]' read_location = '[YOUR_READ_LOCATION_HERE]' From 3f7a7d0b67078e0c6f99436d32405a83e3f93160 Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 5 Sep 2019 14:30:09 -0700 Subject: [PATCH 35/46] Review feedback --- sdk/cosmos/azure-cosmos/HISTORY.md | 29 +++--- sdk/cosmos/azure-cosmos/README.md | 12 +-- .../azure-cosmos/azure/cosmos/__init__.py | 16 ++-- sdk/cosmos/azure-cosmos/azure/cosmos/_base.py | 2 +- .../azure/cosmos/_synchronized_request.py | 2 +- .../{container_client.py => container.py} | 14 +-- .../azure/cosmos/cosmos_client.py | 28 +++--- .../{database_client.py => database.py} | 88 +++++++++---------- .../azure-cosmos/azure/cosmos/errors.py | 5 +- .../cosmos/{scripts_client.py => scripts.py} | 2 +- .../azure/cosmos/{user_client.py => user.py} | 6 +- .../samples/DocumentManagement/Program.py | 2 +- .../samples/IndexManagement/Program.py | 2 +- .../MultiMasterOperations/ConflictWorker.py | 4 +- .../Program.py | 2 +- sdk/cosmos/azure-cosmos/test/crud_tests.py | 26 +++--- sdk/cosmos/azure-cosmos/test/orderby_tests.py | 2 +- .../azure-cosmos/test/partition_key_tests.py | 4 +- .../test/query_execution_context_tests.py | 2 +- sdk/cosmos/azure-cosmos/test/test_config.py | 2 +- 20 files changed, 125 insertions(+), 125 deletions(-) rename sdk/cosmos/azure-cosmos/azure/cosmos/{container_client.py => container.py} (99%) rename sdk/cosmos/azure-cosmos/azure/cosmos/{database_client.py => database.py} (91%) rename sdk/cosmos/azure-cosmos/azure/cosmos/{scripts_client.py => scripts.py} (99%) rename sdk/cosmos/azure-cosmos/azure/cosmos/{user_client.py => user.py} (98%) diff --git a/sdk/cosmos/azure-cosmos/HISTORY.md b/sdk/cosmos/azure-cosmos/HISTORY.md index d04e05dc1c0c..1780ead7c5cd 100644 --- a/sdk/cosmos/azure-cosmos/HISTORY.md +++ b/sdk/cosmos/azure-cosmos/HISTORY.md @@ -2,17 +2,16 @@ ## Version 4.0.0b2: -Version 4.0.0b2 is the second iteration in our efforts to build a more Python client library. -For more information about this, and preview releases of other Azure SDK libraries, please visit https://aka.ms/azure-sdk-preview1-python. +Version 4.0.0b2 is the second iteration in our efforts to build a more Pythonic client library. **Breaking changes** - The client connection has been adapted to consume the HTTP pipeline defined in `azure.core.pipeline`. -- Interactive objects have now been renamed as "clients". This includes: - - `Database` -> `DatabaseClient` - - `User` -> `UserClient` - - `Container` -> `ContainerClient` - - `Scripts` -> `ScriptsClient` +- Interactive objects have now been renamed as proxies. This includes: + - `Database` -> `DatabaseProxy` + - `User` -> `UserProxy` + - `Container` -> `ContainerProxy` + - `Scripts` -> `ScriptsProxy` - The constructor of `CosmosClient` has been updated: - The `auth` parameter has been renamed to `credential` and will now take an authentication type directly. This means the master key value, a dictionary of resource tokens, or a list of permissions can be passed in. However the old dictionary format is still supported. - The `connection_policy` parameter has been made a keyword only parameter, and while it is still supported, each of the individual attributes of the policy can now be passed in as explicit keyword arguments: @@ -25,18 +24,20 @@ For more information about this, and preview releases of other Azure SDK librari - `preferred_locations` - `multiple_write_locations` - A new classmethod constructor has been added to `CosmosClient` to enable creation via a connection string retrieved from the Azure portal. -- All `read_all` operations have been renamed to `list` operations: - - `Container.read_all_conflicts` -> `ContainerClient.list_conflicts` - - `Container.read_all_items` -> `ContainerClient.list_items` +- Some `read_all` operations have been renamed to `list` operations: - `CosmosClient.read_all_databases` -> `CosmosClient.list_database` - - `Database.read_all_containers` -> `DatabaseClient.list_containers` - - `Database.read_all_users` -> `DatabaseClient.list_users` - - `User.read_all_permissions` -> `UserClient.list_permissions` + - `Container.read_all_conflicts` -> `ContainerProxy.list_conflicts` + - `Database.read_all_containers` -> `DatabaseProxy.list_containers` + - `Database.read_all_users` -> `DatabaseProxy.list_users` + - `User.read_all_permissions` -> `UserProxy.list_permissions` - All operations that take `request_options` or `feed_options` parameters, these have been moved to keyword only parameters. In addition, while these options dictionaries are still supported, each of the individual options within the dictionary are now supported as explicit keyword arguments. - The error heirarchy is now inherited from `azure.core.AzureError` instead of `CosmosError` which has been removed. - `HTTPFailure` has been renamed to `CosmosHttpResponseError` - `JSONParseFailure` has been removed and replaced by `azure.core.DecodeError` - - Added ` CosmosResourceNotFoundError` and `CosmosResourceExistsError` for specific conflict response codes. + - Added additional errors for specific response codes: + - `CosmosResourceNotFoundError` for status 404 + - `CosmosResourceExistsError` for status 409 + - `CosmosAccessConditionFailedError` for status 412 - `CosmosClient` can now be run in a context manager to handle closing the client connection. - Iterable responses (e.g. query responses and list responses) are now of type `azure.core.paging.ItemPaged`. The method `fetch_next_block` has been replaced by a secondary iterator, accessed by the `by_page` method. diff --git a/sdk/cosmos/azure-cosmos/README.md b/sdk/cosmos/azure-cosmos/README.md index b2725f3be229..f9ac265aa87e 100644 --- a/sdk/cosmos/azure-cosmos/README.md +++ b/sdk/cosmos/azure-cosmos/README.md @@ -75,9 +75,9 @@ client = CosmosClient(url, credential=key) Once you've initialized a [CosmosClient][ref_cosmosclient], you can interact with the primary resource types in Cosmos DB: -* [Database][ref_database]: A Cosmos DB account can contain multiple databases. When you create a database, you specify the API you'd like to use when interacting with its documents: SQL, MongoDB, Gremlin, Cassandra, or Azure Table. Use the [DatabaseClient][ref_database] object to manage its containers. +* [Database][ref_database]: A Cosmos DB account can contain multiple databases. When you create a database, you specify the API you'd like to use when interacting with its documents: SQL, MongoDB, Gremlin, Cassandra, or Azure Table. Use the [DatabaseProxy][ref_database] object to manage its containers. -* [Container][ref_container]: A container is a collection of JSON documents. You create (insert), read, update, and delete items in a container by using methods on the [ContainerClient][ref_container] object. +* [Container][ref_container]: A container is a collection of JSON documents. You create (insert), read, update, and delete items in a container by using methods on the [ContainerProxy][ref_container] object. * [Item][ref_item]: An Item is the dictionary-like representation of a JSON document stored in a container. Each Item you add to a container must include an `id` key with a value that uniquely identifies the item within the container. @@ -137,7 +137,7 @@ container = database.get_container_client(container_name) ### Insert data -To insert items into a container, pass a dictionary containing your data to [ContainerClient.upsert_item][ref_container_upsert_item]. Each item you add to a container must include an `id` key with a value that uniquely identifies the item within the container. +To insert items into a container, pass a dictionary containing your data to [ContainerProxy.upsert_item][ref_container_upsert_item]. Each item you add to a container must include an `id` key with a value that uniquely identifies the item within the container. This example inserts several items into the container, each with a unique `id`: @@ -156,7 +156,7 @@ for i in range(1, 10): ### Delete data -To delete items from a container, use [ContainerClient.delete_item][ref_container_delete_item]. The SQL API in Cosmos DB does not support the SQL `DELETE` statement. +To delete items from a container, use [ContainerProxy.delete_item][ref_container_delete_item]. The SQL API in Cosmos DB does not support the SQL `DELETE` statement. ```Python for item in container.query_items(query='SELECT * FROM products p WHERE p.productModel = "DISCONTINUED"', @@ -166,7 +166,7 @@ for item in container.query_items(query='SELECT * FROM products p WHERE p.produc ### Query the database -A Cosmos DB SQL API database supports querying the items in a container with [ContainerClient.query_items][ref_container_query_items] using SQL-like syntax. +A Cosmos DB SQL API database supports querying the items in a container with [ContainerProxy.query_items][ref_container_query_items] using SQL-like syntax. This example queries a container for items with a specific `id`: @@ -184,7 +184,7 @@ for item in container.query_items( > NOTE: Although you can specify any value for the container name in the `FROM` clause, we recommend you use the container name for consistency. -Perform parameterized queries by passing a dictionary containing the parameters and their values to [ContainerClient.query_items][ref_container_query_items]: +Perform parameterized queries by passing a dictionary containing the parameters and their values to [ContainerProxy.query_items][ref_container_query_items]: ```Python discontinued_items = container.query_items( diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py index 6a0c0bea8854..93920f280f41 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/__init__.py @@ -19,11 +19,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from .container_client import ContainerClient +from .container import ContainerProxy from .cosmos_client import CosmosClient -from .database_client import DatabaseClient -from .user_client import UserClient -from .scripts_client import ScriptsClient +from .database import DatabaseProxy +from .user import UserProxy +from .scripts import ScriptsProxy from .documents import ( ConsistencyLevel, DataType, @@ -40,13 +40,13 @@ from .version import VERSION __all__ = ( - "ContainerClient", "CosmosClient", - "DatabaseClient", + "DatabaseProxy", + "ContainerProxy", "PartitionKey", "Permission", - "ScriptsClient", - "UserClient", + "ScriptsProxy", + "UserProxy", "ConsistencyLevel", "DataType", "IndexKind", diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py index 3ca995657948..265d108e178b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_base.py @@ -68,7 +68,7 @@ def build_options(kwargs): # type: (Dict[str, Any]) -> Dict[str, Any] - options = kwargs.pop('request_options', None) or kwargs.pop('feed_options', {}) + options = kwargs.pop('request_options', kwargs.pop('feed_options', {})) for key, value in _COMMON_OPTIONS.items(): if key in kwargs: options[value] = kwargs.pop(key) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index c616eacf65fe..111c2157e67f 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -158,7 +158,7 @@ def _Request(global_endpoint_manager, request_params, connection_policy, pipelin if response.status_code == 409: raise errors.CosmosResourceExistsError(message=data, response=response) if response.status_code == 412: - raise errors.CosmosResourceModifiedError(message=data, response=response) + raise errors.CosmosAccessConditionFailedError(message=data, response=response) if response.status_code >= 400: raise errors.CosmosHttpResponseError(message=data, response=response) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py similarity index 99% rename from sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/container.py index 5826872a2401..880a7afefefb 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py @@ -32,16 +32,16 @@ from .errors import CosmosResourceNotFoundError from .http_constants import StatusCodes from .offer import Offer -from .scripts_client import ScriptsClient +from .scripts import ScriptsProxy from .partition_key import NonePartitionKeyValue -__all__ = ("ContainerClient",) +__all__ = ("ContainerProxy",) # pylint: disable=protected-access # pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs -class ContainerClient(object): +class ContainerProxy(object): """ An Azure Cosmos DB container. A container in an Azure Cosmos DB SQL API database is a collection of documents, @@ -63,7 +63,7 @@ def __init__(self, client_connection, database_link, id, properties=None): # py self._properties = properties self.container_link = u"{}/colls/{}".format(database_link, self.id) self._is_system_key = None - self._scripts = None # type: ScriptsClient + self._scripts = None # type: ScriptsProxy def _get_properties(self): # type: () -> Dict[str, Any] @@ -83,9 +83,9 @@ def is_system_key(self): @property def scripts(self): - # type: () -> ScriptsClient + # type: () -> ScriptsProxy if self._scripts is None: - self._scripts = ScriptsClient(self.client_connection, self.container_link, self.is_system_key) + self._scripts = ScriptsProxy(self.client_connection, self.container_link, self.is_system_key) return self._scripts def _get_document_link(self, item_or_link): @@ -198,7 +198,7 @@ def read_item( return result @distributed_trace - def list_items( + def read_all_items( self, max_item_count=None, # type: Optional[int] populate_query_metrics=None, # type: Optional[bool] diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 31073ad61e62..b8d5453ce80b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -29,7 +29,7 @@ from ._cosmos_client_connection import CosmosClientConnection from ._base import build_options -from .database_client import DatabaseClient +from .database import DatabaseProxy from .documents import ConnectionPolicy, DatabaseAccount __all__ = ("CosmosClient",) @@ -39,7 +39,7 @@ def _parse_connection_str(conn_str, credential): # type: (str, Optional[Any]) -> Dict[str, str] conn_str = conn_str.rstrip(";") conn_settings = dict( # pylint: disable=consider-using-dict-comprehension # type: ignore - [s.split("=", 1) for s in conn_str.split(";")] + s.split("=", 1) for s in conn_str.split(";") ) if 'AccountEndpoint' not in conn_settings: raise ValueError("Connection string missing setting 'AccountEndpoint'.") @@ -158,11 +158,11 @@ def from_connection_string(cls, conn_str, credential=None, consistency_level="Se @staticmethod def _get_database_link(database_or_id): - # type: (Union[DatabaseClient, str, Dict[str, str]]) -> str + # type: (Union[DatabaseProxy, str, Dict[str, str]]) -> str if isinstance(database_or_id, six.string_types): return "dbs/{}".format(database_or_id) try: - return cast("DatabaseClient", database_or_id).database_link + return cast("DatabaseProxy", database_or_id).database_link except AttributeError: pass database_id = cast("Dict[str, str]", database_or_id)["id"] @@ -176,7 +176,7 @@ def create_database( # pylint: disable=redefined-builtin offer_throughput=None, # type: Optional[int] **kwargs # type: Any ): - # type: (...) -> DatabaseClient + # type: (...) -> DatabaseProxy """Create a new database with the given ID (name). :param id: ID (name) of the database to create. @@ -187,7 +187,7 @@ def create_database( # pylint: disable=redefined-builtin :param offer_throughput: The provisioned throughput for this offer. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`DatabaseClient` instance representing the new database. + :returns: A :class:`DatabaseProxy` instance representing the new database. :raises `CosmosHttpResponseError`: If database with the given ID already exists. .. literalinclude:: ../../examples/examples.py @@ -210,26 +210,26 @@ def create_database( # pylint: disable=redefined-builtin result = self.client_connection.CreateDatabase(database=dict(id=id), options=request_options, **kwargs) if response_hook: response_hook(self.client_connection.last_response_headers) - return DatabaseClient(self.client_connection, id=result["id"], properties=result) + return DatabaseProxy(self.client_connection, id=result["id"], properties=result) def get_database_client(self, database): - # type: (Union[str, DatabaseClient, Dict[str, Any]]) -> DatabaseClient + # type: (Union[str, DatabaseProxy, Dict[str, Any]]) -> DatabaseProxy """ Retrieve an existing database with the ID (name) `id`. - :param database: The ID (name), dict representing the properties or :class:`DatabaseClient` + :param database: The ID (name), dict representing the properties or :class:`DatabaseProxy` instance of the database to read. - :returns: A :class:`DatabaseClient` instance representing the retrieved database. + :returns: A :class:`DatabaseProxy` instance representing the retrieved database. """ - if isinstance(database, DatabaseClient): + if isinstance(database, DatabaseProxy): id_value = database.id elif isinstance(database, Mapping): id_value = database["id"] else: id_value = database - return DatabaseClient(self.client_connection, id_value) + return DatabaseProxy(self.client_connection, id_value) @distributed_trace def list_databases( @@ -316,7 +316,7 @@ def query_databases( @distributed_trace def delete_database( self, - database, # type: Union[str, DatabaseClient, Dict[str, Any]] + database, # type: Union[str, DatabaseProxy, Dict[str, Any]] populate_query_metrics=None, # type: Optional[bool] **kwargs # type: Any ): @@ -324,7 +324,7 @@ def delete_database( """ Delete the database with the given ID (name). - :param database: The ID (name), dict representing the properties or :class:`DatabaseClient` + :param database: The ID (name), dict representing the properties or :class:`DatabaseProxy` instance of the database to delete. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py similarity index 91% rename from sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/database.py index c4e9bfbc1919..6901d85e8535 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py @@ -29,19 +29,19 @@ from ._cosmos_client_connection import CosmosClientConnection from ._base import build_options -from .container_client import ContainerClient +from .container import ContainerProxy from .offer import Offer from .http_constants import StatusCodes from .errors import CosmosResourceNotFoundError -from .user_client import UserClient +from .user import UserProxy -__all__ = ("DatabaseClient",) +__all__ = ("DatabaseProxy",) # pylint: disable=protected-access # pylint: disable=missing-client-constructor-parameter-credential,missing-client-constructor-parameter-kwargs -class DatabaseClient(object): +class DatabaseProxy(object): """ Represents an Azure Cosmos DB SQL API database. A database contains one or more containers, each of which can contain items, @@ -76,25 +76,25 @@ def __init__(self, client_connection, id, properties=None): # pylint: disable=r @staticmethod def _get_container_id(container_or_id): - # type: (Union[str, ContainerClient, Dict[str, Any]]) -> str + # type: (Union[str, ContainerProxy, Dict[str, Any]]) -> str if isinstance(container_or_id, six.string_types): return container_or_id try: - return cast("ContainerClient", container_or_id).id + return cast("ContainerProxy", container_or_id).id except AttributeError: pass return cast("Dict[str, str]", container_or_id)["id"] def _get_container_link(self, container_or_id): - # type: (Union[str, ContainerClient, Dict[str, Any]]) -> str + # type: (Union[str, ContainerProxy, Dict[str, Any]]) -> str return u"{}/colls/{}".format(self.database_link, self._get_container_id(container_or_id)) def _get_user_link(self, user_or_id): - # type: (Union[UserClient, str, Dict[str, Any]]) -> str + # type: (Union[UserProxy, str, Dict[str, Any]]) -> str if isinstance(user_or_id, six.string_types): return u"{}/users/{}".format(self.database_link, user_or_id) try: - return cast("UserClient", user_or_id).user_link + return cast("UserProxy", user_or_id).user_link except AttributeError: pass return u"{}/users/{}".format(self.database_link, cast("Dict[str, str]", user_or_id)["id"]) @@ -111,7 +111,7 @@ def read(self, populate_query_metrics=None, **kwargs): """ Read the database properties - :param database: The ID (name), dict representing the properties or :class:`DatabaseClient` + :param database: The ID (name), dict representing the properties or :class:`DatabaseProxy` instance of the database to read. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. @@ -153,7 +153,7 @@ def create_container( conflict_resolution_policy=None, # type: Optional[Dict[str, Any]] **kwargs # type: Any ): - # type: (...) -> ContainerClient + # type: (...) -> ContainerProxy """ Create a new container with the given ID (name). @@ -172,7 +172,7 @@ def create_container( :param conflict_resolution_policy: The conflict resolution policy to apply to the container. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`ContainerClient` instance representing the new container. + :returns: A :class:`ContainerProxy` instance representing the new container. :raise CosmosHttpResponseError: The container creation failed. @@ -219,12 +219,12 @@ def create_container( if response_hook: response_hook(self.client_connection.last_response_headers, data) - return ContainerClient(self.client_connection, self.database_link, data["id"], properties=data) + return ContainerProxy(self.client_connection, self.database_link, data["id"], properties=data) @distributed_trace def delete_container( self, - container, # type: Union[str, ContainerClient, Dict[str, Any]] + container, # type: Union[str, ContainerProxy, Dict[str, Any]] populate_query_metrics=None, # type: Optional[bool] **kwargs # type: Any ): @@ -232,7 +232,7 @@ def delete_container( """ Delete the container :param container: The ID (name) of the container to delete. You can either - pass in the ID of the container to delete, a :class:`ContainerClient` instance or + pass in the ID of the container to delete, a :class:`ContainerProxy` instance or a dict representing the properties of the container. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. @@ -254,10 +254,10 @@ def delete_container( response_hook(self.client_connection.last_response_headers, result) def get_container_client(self, container): - # type: (Union[str, ContainerClient, Dict[str, Any]]) -> ContainerClient - """ Get the specified `ContainerClient`, or a container with specified ID (name). + # type: (Union[str, ContainerProxy, Dict[str, Any]]) -> ContainerProxy + """ Get the specified `ContainerProxy`, or a container with specified ID (name). - :param container: The ID (name) of the container, a :class:`ContainerClient` instance, + :param container: The ID (name) of the container, a :class:`ContainerProxy` instance, or a dict representing the properties of the container to be retrieved. .. literalinclude:: ../../examples/examples.py @@ -269,14 +269,14 @@ def get_container_client(self, container): :name: get_container """ - if isinstance(container, ContainerClient): + if isinstance(container, ContainerProxy): id_value = container.id elif isinstance(container, Mapping): id_value = container["id"] else: id_value = container - return ContainerClient(self.client_connection, self.database_link, id_value) + return ContainerProxy(self.client_connection, self.database_link, id_value) @distributed_trace def list_containers(self, max_item_count=None, populate_query_metrics=None, **kwargs): @@ -357,7 +357,7 @@ def query_containers( @distributed_trace def replace_container( self, - container, # type: Union[str, ContainerClient, Dict[str, Any]] + container, # type: Union[str, ContainerProxy, Dict[str, Any]] partition_key, # type: Any indexing_policy=None, # type: Optional[Dict[str, Any]] default_ttl=None, # type: Optional[int] @@ -365,13 +365,13 @@ def replace_container( populate_query_metrics=None, # type: Optional[bool] **kwargs # type: Any ): - # type: (...) -> ContainerClient + # type: (...) -> ContainerProxy """ Reset the properties of the container. Property changes are persisted immediately. Any properties not specified will be reset to their default values. :param container: The ID (name), dict representing the properties or - :class:`ContainerClient` instance of the container to be replaced. + :class:`ContainerProxy` instance of the container to be replaced. :param partition_key: The partition key to use for the container. :param indexing_policy: The indexing policy to apply to the container. :param default_ttl: Default time to live (TTL) for items in the container. @@ -385,7 +385,7 @@ def replace_container( :param response_hook: a callable invoked with the response metadata :raise `CosmosHttpResponseError`: Raised if the container couldn't be replaced. This includes if the container with given id does not exist. - :returns: :class:`ContainerClient` instance representing the container after replace completed. + :returns: :class:`ContainerProxy` instance representing the container after replace completed. .. literalinclude:: ../../examples/examples.py :start-after: [START reset_container_properties] @@ -422,7 +422,7 @@ def replace_container( if response_hook: response_hook(self.client_connection.last_response_headers, container_properties) - return ContainerClient( + return ContainerProxy( self.client_connection, self.database_link, container_properties["id"], properties=container_properties ) @@ -478,38 +478,38 @@ def query_users(self, query, parameters=None, max_item_count=None, **kwargs): return result def get_user_client(self, user): - # type: (Union[str, UserClient, Dict[str, Any]]) -> UserClient + # type: (Union[str, UserProxy, Dict[str, Any]]) -> UserProxy """ Get the user identified by `id`. - :param user: The ID (name), dict representing the properties or :class:`UserClient` + :param user: The ID (name), dict representing the properties or :class:`UserProxy` instance of the user to be retrieved. - :returns: A :class:`UserClient` instance representing the retrieved user. + :returns: A :class:`UserProxy` instance representing the retrieved user. :raise `CosmosHttpResponseError`: If the given user couldn't be retrieved. """ - if isinstance(user, UserClient): + if isinstance(user, UserProxy): id_value = user.id elif isinstance(user, Mapping): id_value = user["id"] else: id_value = user - return UserClient(client_connection=self.client_connection, id=id_value, database_link=self.database_link) + return UserProxy(client_connection=self.client_connection, id=id_value, database_link=self.database_link) @distributed_trace def create_user(self, body, **kwargs): - # type: (Dict[str, Any], Any) -> UserClient + # type: (Dict[str, Any], Any) -> UserProxy """ Create a user in the container. :param body: A dict-like object with an `id` key and value representing the user to be created. The user ID must be unique within the database, and consist of no more than 255 characters. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`UserClient` instance representing the new user. + :returns: A :class:`UserProxy` instance representing the new user. :raise `CosmosHttpResponseError`: If the given user couldn't be created. - To update or replace an existing user, use the :func:`ContainerClient.upsert_user` method. + To update or replace an existing user, use the :func:`ContainerProxy.upsert_user` method. .. literalinclude:: ../../examples/examples.py :start-after: [START create_user] @@ -529,19 +529,19 @@ def create_user(self, body, **kwargs): if response_hook: response_hook(self.client_connection.last_response_headers, user) - return UserClient( + return UserProxy( client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) @distributed_trace def upsert_user(self, body, **kwargs): - # type: (Dict[str, Any], Any) -> UserClient + # type: (Dict[str, Any], Any) -> UserProxy """ Insert or update the specified user. :param body: A dict-like object representing the user to update or insert. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`UserClient` instance representing the upserted user. + :returns: A :class:`UserProxy` instance representing the upserted user. :raise `CosmosHttpResponseError`: If the given user could not be upserted. If the user already exists in the container, it is replaced. If it does not, it is inserted. @@ -557,26 +557,26 @@ def upsert_user(self, body, **kwargs): if response_hook: response_hook(self.client_connection.last_response_headers, user) - return UserClient( + return UserProxy( client_connection=self.client_connection, id=user["id"], database_link=self.database_link, properties=user ) @distributed_trace def replace_user( self, - user, # type: Union[str, UserClient, Dict[str, Any]] + user, # type: Union[str, UserProxy, Dict[str, Any]] body, # type: Dict[str, Any] **kwargs # type: Any ): - # type: (...) -> UserClient + # type: (...) -> UserProxy """ Replaces the specified user if it exists in the container. - :param user: The ID (name), dict representing the properties or :class:`UserClient` + :param user: The ID (name), dict representing the properties or :class:`UserProxy` instance of the user to be replaced. :param body: A dict-like object representing the user to replace. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`UserClient` instance representing the user after replace went through. + :returns: A :class:`UserProxy` instance representing the user after replace went through. :raise `CosmosHttpResponseError`: If the replace failed or the user with given id does not exist. """ @@ -590,7 +590,7 @@ def replace_user( if response_hook: response_hook(self.client_connection.last_response_headers, replaced_user) - return UserClient( + return UserProxy( client_connection=self.client_connection, id=replaced_user["id"], database_link=self.database_link, @@ -599,10 +599,10 @@ def replace_user( @distributed_trace def delete_user(self, user, **kwargs): - # type: (Union[str, UserClient, Dict[str, Any]], Any) -> None + # type: (Union[str, UserProxy, Dict[str, Any]], Any) -> None """ Delete the specified user from the container. - :param user: The ID (name), dict representing the properties or :class:`UserClient` + :param user: The ID (name), dict representing the properties or :class:`UserProxy` instance of the user to be deleted. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py index 8bcf053773b9..55f29b60384f 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py @@ -25,8 +25,7 @@ AzureError, HttpResponseError, ResourceExistsError, - ResourceNotFoundError, - ResourceModifiedError + ResourceNotFoundError ) from . import http_constants @@ -64,5 +63,5 @@ class CosmosResourceExistsError(ResourceExistsError, CosmosHttpResponseError): """An error response with status code 409.""" -class CosmosResourceModifiedError(ResourceModifiedError, CosmosHttpResponseError): +class CosmosAccessConditionFailedError(CosmosHttpResponseError): """An error response with status code 412.""" diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py similarity index 99% rename from sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py index b14aa1284a2d..ea91f8e0982b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py @@ -40,7 +40,7 @@ class ScriptType(object): UserDefinedFunction = "udfs" -class ScriptsClient(object): +class ScriptsProxy(object): def __init__(self, client_connection, container_link, is_system_key): # type: (CosmosClientConnection, str, bool) -> None self.client_connection = client_connection diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py similarity index 98% rename from sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py rename to sdk/cosmos/azure-cosmos/azure/cosmos/user.py index 45cb8f7711c3..0e5ea04fa57f 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py @@ -34,7 +34,7 @@ from .permission import Permission -class UserClient(object): +class UserProxy(object): def __init__(self, client_connection, id, database_link, properties=None): # pylint: disable=redefined-builtin # type: (CosmosClientConnection, str, str, Dict[str, Any]) -> None @@ -67,7 +67,7 @@ def read(self, **kwargs): :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`UserClient` instance representing the retrieved user. + :returns: A :class:`UserProxy` instance representing the retrieved user. :raise `CosmosHttpResponseError`: If the given user couldn't be retrieved. """ @@ -183,7 +183,7 @@ def create_permission(self, body, **kwargs): :returns: A dict representing the new permission. :raise `CosmosHttpResponseError`: If the given permission couldn't be created. - To update or replace an existing permision, use the :func:`UserClient.upsert_permission` method. + To update or replace an existing permision, use the :func:`UserProxy.upsert_permission` method. """ request_options = build_options(kwargs) diff --git a/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py index 891317c3ff02..31f2953f3dae 100644 --- a/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/DocumentManagement/Program.py @@ -71,7 +71,7 @@ def ReadItems(container): # NOTE: Use MaxItemCount on Options to control how many items come back per trip to the server # Important to handle throttles whenever you are doing operations such as this that might # result in a 429 (throttled request) - item_list = list(container.list_items(max_item_count=10)) + item_list = list(container.read_all_items(max_item_count=10)) print('Found {0} items'.format(item_list.__len__())) diff --git a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py index 207279152d73..bf83329216c4 100644 --- a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py @@ -69,7 +69,7 @@ def Query_Entities(parent, entity_type, id = None): elif entity_type == 'document': if id == None: - entities = list(parent.list_items()) + entities = list(parent.read_all_items()) else: entities = list(parent.query_items(find_entity_by_id_query)) except errors.AzureError as e: diff --git a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py index 21e4b7367746..40f64026ebec 100644 --- a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py +++ b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/ConflictWorker.py @@ -480,7 +480,7 @@ def try_update_document(self, client, collection_uri, document, options): try: options['partitionKey'] = document['id'] return client.ReplaceItem(collection_uri + "/docs/" + document['id'], document, options); - except (errors.CosmosResourceNotFoundError, errors.CosmosResourceModifiedError): + except (errors.CosmosResourceNotFoundError, errors.CosmosAccessConditionFailedError): # Lost synchronously or no document yet. No conflict is induced. return None @@ -489,7 +489,7 @@ def try_delete_document(self, client, collection_uri, document, options): options['partitionKey'] = document['id'] client.DeleteItem(collection_uri + "/docs/" + document['id'], options) return document - except (errors.CosmosResourceNotFoundError, errors.CosmosResourceModifiedError): + except (errors.CosmosResourceNotFoundError, errors.CosmosAccessConditionFailedError): #Lost synchronously. No conflict is induced. return None diff --git a/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py b/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py index 050e24a9b997..3afcf4cf1d4c 100644 --- a/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py @@ -175,7 +175,7 @@ def ReadItems(container): # NOTE: Use MaxItemCount on Options to control how many items come back per trip to the server # Important to handle throttles whenever you are doing operations such as this that might # result in a 429 (throttled request) - item_list = list(container.list_items(max_item_count=10)) + item_list = list(container.read_all_items(max_item_count=10)) print('Found {0} items'.format(item_list.__len__())) diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index d35445e4320a..b1d40cd4df9f 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -436,7 +436,7 @@ def test_partitioned_collection_document_crud_and_query(self): self.assertEqual(read_document.get('key'), created_document.get('key')) # Read document feed doesn't require partitionKey as it's always a cross partition query - documentlist = list(created_collection.list_items()) + documentlist = list(created_collection.read_all_items()) self.assertEqual(1, len(documentlist)) # replace document @@ -458,7 +458,7 @@ def test_partitioned_collection_document_crud_and_query(self): self.assertEqual(upserted_document.get('id'), document_definition.get('id')) self.assertEqual(upserted_document.get('key'), document_definition.get('key')) - documentlist = list(created_collection.list_items()) + documentlist = list(created_collection.read_all_items()) self.assertEqual(2, len(documentlist)) # delete document @@ -734,7 +734,7 @@ def test_document_crud(self): # create collection created_collection = self.configs.create_multi_partition_collection_if_not_exist(self.client) # read documents - documents = list(created_collection.list_items()) + documents = list(created_collection.read_all_items()) # create a document before_create_documents_count = len(documents) @@ -755,7 +755,7 @@ def test_document_crud(self): created_collection.create_item, duplicated_definition_with_id) # read documents after creation - documents = list(created_collection.list_items()) + documents = list(created_collection.read_all_items()) self.assertEqual( len(documents), before_create_documents_count + 1, @@ -905,7 +905,7 @@ def test_document_upsert(self): created_collection = self.configs.create_multi_partition_collection_if_not_exist(self.client) # read documents and check count - documents = list(created_collection.list_items()) + documents = list(created_collection.read_all_items()) before_create_documents_count = len(documents) # create document definition @@ -922,7 +922,7 @@ def test_document_upsert(self): document_definition['id']) # read documents after creation and verify updated count - documents = list(created_collection.list_items()) + documents = list(created_collection.read_all_items()) self.assertEqual( len(documents), before_create_documents_count + 1, @@ -949,7 +949,7 @@ def test_document_upsert(self): 'document id should stay the same') # read documents after upsert and verify count doesn't increases again - documents = list(created_collection.list_items()) + documents = list(created_collection.read_all_items()) self.assertEqual( len(documents), before_create_documents_count + 1, @@ -966,7 +966,7 @@ def test_document_upsert(self): 'document id should be same') # read documents after upsert and verify count increases - documents = list(created_collection.list_items()) + documents = list(created_collection.read_all_items()) self.assertEqual( len(documents), before_create_documents_count + 2, @@ -977,7 +977,7 @@ def test_document_upsert(self): created_collection.delete_item(item=new_document, partition_key=new_document['id']) # read documents after delete and verify count is same as original - documents = list(created_collection.list_items()) + documents = list(created_collection.read_all_items()) self.assertEqual( len(documents), before_create_documents_count, @@ -1376,7 +1376,7 @@ def __SetupEntities(client): db.delete_container, success_coll1) # 3. Success-- Use Col1 Permission to Read All Docs - success_documents = list(success_coll1.list_items()) + success_documents = list(success_coll1.read_all_items()) self.assertTrue(success_documents != None, 'error reading documents') self.assertEqual(len(success_documents), @@ -1984,7 +1984,7 @@ def __create_resources(client): # Validate QueryIterable by converting it to a list. resources = __create_resources(self.client) - results = resources['coll'].list_items(max_item_count=2) + results = resources['coll'].read_all_items(max_item_count=2) docs = list(iter(results)) self.assertEqual(3, len(docs), @@ -1995,7 +1995,7 @@ def __create_resources(client): self.assertEqual(resources['doc3']['id'], docs[2]['id']) # Validate QueryIterable iterator with 'for'. - results = resources['coll'].list_items(max_item_count=2) + results = resources['coll'].read_all_items(max_item_count=2) counter = 0 # test QueryIterable with 'for'. for doc in iter(results): @@ -2015,7 +2015,7 @@ def __create_resources(client): self.assertEqual(counter, 3) # Get query results page by page. - results = resources['coll'].list_items(max_item_count=2) + results = resources['coll'].read_all_items(max_item_count=2) page_iter = results.by_page() first_block = list(next(page_iter)) self.assertEqual(2, len(first_block), 'First block should have 2 entries.') diff --git a/sdk/cosmos/azure-cosmos/test/orderby_tests.py b/sdk/cosmos/azure-cosmos/test/orderby_tests.py index 83d8e41c17c4..ba1eb99b3c17 100644 --- a/sdk/cosmos/azure-cosmos/test/orderby_tests.py +++ b/sdk/cosmos/azure-cosmos/test/orderby_tests.py @@ -94,7 +94,7 @@ def setUp(self): self.assertGreaterEqual(len(partition_key_ranges), 5) # sanity check: read documents after creation - queried_docs = list(self.created_collection.list_items()) + queried_docs = list(self.created_collection.read_all_items()) self.assertEqual( len(queried_docs), len(self.document_definitions), diff --git a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py index 8684b6357915..026b1f30ae56 100644 --- a/sdk/cosmos/azure-cosmos/test/partition_key_tests.py +++ b/sdk/cosmos/azure-cosmos/test/partition_key_tests.py @@ -169,7 +169,7 @@ def test_non_partitioned_collection_operations(self): self.assertEqual(result, 1) # 3 previous items + 1 created from the sproc - items = list(created_container.list_items()) + items = list(created_container.read_all_items()) self.assertEqual(len(items), 4) created_container.delete_item(upserted_item['id'], partition_key=partition_key.NonePartitionKeyValue) @@ -177,7 +177,7 @@ def test_non_partitioned_collection_operations(self): created_container.delete_item(document_created_by_sproc_id, partition_key=partition_key.NonePartitionKeyValue) created_container.delete_item(self.created_document['id'], partition_key=partition_key.NonePartitionKeyValue) - items = list(created_container.list_items()) + items = list(created_container.read_all_items()) self.assertEqual(len(items), 0) def test_multi_partition_collection_read_document_with_no_pk(self): diff --git a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py index f22755f0c84f..9b73a4f738d9 100644 --- a/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py +++ b/sdk/cosmos/azure-cosmos/test/query_execution_context_tests.py @@ -85,7 +85,7 @@ def setUp(self): self.assertGreaterEqual(len(partition_key_ranges), 1) # sanity check: read documents after creation - queried_docs = list(self.created_collection.list_items()) + queried_docs = list(self.created_collection.read_all_items()) self.assertEqual( len(queried_docs), len(self.document_definitions), diff --git a/sdk/cosmos/azure-cosmos/test/test_config.py b/sdk/cosmos/azure-cosmos/test/test_config.py index ddea65ee64d5..82dcafff145a 100644 --- a/sdk/cosmos/azure-cosmos/test/test_config.py +++ b/sdk/cosmos/azure-cosmos/test/test_config.py @@ -25,7 +25,7 @@ import azure.cosmos.documents as documents import azure.cosmos.errors as errors from azure.cosmos.http_constants import StatusCodes -from azure.cosmos.database_client import DatabaseClient +from azure.cosmos.database import DatabaseProxy from azure.cosmos.cosmos_client import CosmosClient from azure.cosmos.partition_key import PartitionKey from azure.cosmos.partition_key import NonePartitionKeyValue From 191eb6f17d4244cce69313fbb4f77c68ebf3eae4 Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 5 Sep 2019 14:43:25 -0700 Subject: [PATCH 36/46] Fix pylint --- sdk/cosmos/azure-cosmos/azure/cosmos/container.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py index 880a7afefefb..7b3a5aa7989e 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py @@ -22,7 +22,7 @@ """Create, read, update and delete items in the Azure Cosmos DB SQL API service. """ -from typing import Any, Dict, List, Optional, Union, Iterable +from typing import Any, Dict, List, Optional, Union, Iterable # pylint: disable=unused-import import six from azure.core.tracing.decorator import distributed_trace From 90b006455b29567ec2941c975eebe4b6b21e12c5 Mon Sep 17 00:00:00 2001 From: antisch Date: Thu, 5 Sep 2019 17:01:26 -0700 Subject: [PATCH 37/46] Fixed samples --- .../samples/CollectionManagement/Program.py | 28 ++-- .../samples/IndexManagement/Program.py | 146 ++++-------------- .../MultiMasterScenario.py | 7 +- .../Program.py | 14 +- 4 files changed, 57 insertions(+), 138 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py index 0683e7171f9d..fa877125c4e0 100644 --- a/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/CollectionManagement/Program.py @@ -111,12 +111,13 @@ def create_Container(db, id): partition_key=partition_key, indexing_policy=coll['indexingPolicy'] ) + properties = container.read() print('Container with id \'{0}\' created'.format(container.id)) - print('IndexPolicy Mode - \'{0}\''.format(container.properties['indexingPolicy']['indexingMode'])) - print('IndexPolicy Automatic - \'{0}\''.format(container.properties['indexingPolicy']['automatic'])) + print('IndexPolicy Mode - \'{0}\''.format(properties['indexingPolicy']['indexingMode'])) + print('IndexPolicy Automatic - \'{0}\''.format(properties['indexingPolicy']['automatic'])) except errors.CosmosResourceExistsError: - print('A container with id \'{0}\' already exists'.format(container['id'])) + print('A container with id \'{0}\' already exists'.format(coll['id'])) print("\n2.3 Create Container - With custom offer throughput") @@ -130,7 +131,7 @@ def create_Container(db, id): print('Container with id \'{0}\' created'.format(container.id)) except errors.CosmosResourceExistsError: - print('A container with id \'{0}\' already exists'.format(container.id)) + print('A container with id \'{0}\' already exists'.format(coll['id'])) print("\n2.4 Create Container - With Unique keys") @@ -140,12 +141,13 @@ def create_Container(db, id): partition_key=partition_key, unique_key_policy={'uniqueKeys': [{'paths': ['/field1/field2', '/field3']}]} ) - unique_key_paths = container.properties['uniqueKeyPolicy']['uniqueKeys'][0]['paths'] + properties = container.read() + unique_key_paths = properties['uniqueKeyPolicy']['uniqueKeys'][0]['paths'] print('Container with id \'{0}\' created'.format(container.id)) print('Unique Key Paths - \'{0}\', \'{1}\''.format(unique_key_paths[0], unique_key_paths[1])) except errors.CosmosResourceExistsError: - print('A container with id \'{0}\' already exists'.format(container.id)) + print('A container with id \'container_unique_keys\' already exists') print("\n2.5 Create Collection - With Partition key V2 (Default)") @@ -154,12 +156,12 @@ def create_Container(db, id): id="collection_partition_key_v2", partition_key=PartitionKey(path='/id', kind='Hash') ) - + properties = container.read() print('Container with id \'{0}\' created'.format(container.id)) - print('Partition Key - \'{0}\''.format(container.properties['partitionKey'])) + print('Partition Key - \'{0}\''.format(properties['partitionKey'])) except errors.CosmosResourceExistsError: - print('A container with id \'{0}\' already exists'.format(container.id)) + print('A container with id \'collection_partition_key_v2\' already exists') print("\n2.6 Create Collection - With Partition key V1") @@ -168,12 +170,12 @@ def create_Container(db, id): id="collection_partition_key_v1", partition_key=PartitionKey(path='/id', kind='Hash', version=1) ) - + properties = container.read() print('Container with id \'{0}\' created'.format(container.id)) - print('Partition Key - \'{0}\''.format(container.properties['partitionKey'])) + print('Partition Key - \'{0}\''.format(properties['partitionKey'])) except errors.CosmosResourceExistsError: - print('A container with id \'{0}\' already exists'.format(container.id)) + print('A container with id \'collection_partition_key_v1\' already exists') @staticmethod def manage_offer_throughput(db, id): @@ -250,7 +252,7 @@ def run_sample(): db = client.create_database(id=DATABASE_ID) except errors.CosmosResourceExistsError: - pass + db = client.get_database_client(DATABASE_ID) # query for a container ContainerManagement.find_container(db, CONTAINER_ID) diff --git a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py index bf83329216c4..d4a945ac699b 100644 --- a/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/IndexManagement/Program.py @@ -42,7 +42,7 @@ def ObtainClient(): urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) connection_policy.SSLConfiguration.SSLCaCerts = False - return cosmos_client.CosmosClient(HOST, {'masterKey': MASTER_KEY}, "Session", connection_policy) + return cosmos_client.CosmosClient(HOST, MASTER_KEY, "Session", connection_policy=connection_policy) # Query for Entity / Entities @@ -87,8 +87,9 @@ def CreateDatabaseIfNotExists(client, database_id): try: database = Query_Entities(client, 'database', id = database_id) if database == None: - database = client.create_database(id=database_id) - return client.get_database_client(database['id']) + return client.create_database(id=database_id) + else: + return client.get_database_client(database_id) except errors.CosmosResourceExistsError: pass @@ -99,7 +100,7 @@ def DeleteContainerIfExists(db, collection_id): print('Collection with id \'{0}\' was deleted'.format(collection_id)) except errors.CosmosResourceNotFoundError: pass - except errors.CosmosHttpResponseError as e + except errors.CosmosHttpResponseError as e: if e.status_code == 400: print("Bad request for collection link", collection_id) raise @@ -155,7 +156,8 @@ def ExplicitlyExcludeFromIndex(db): print(created_Container) print("\n" + "-" * 25 + "\n1. Collection created with index policy") - print_dictionary_items(created_Container.properties["indexingPolicy"]) + properties = created_Container.read() + print_dictionary_items(properties["indexingPolicy"]) # Create a document and query on it immediately. # Will work as automatic indexing is still True @@ -213,10 +215,11 @@ def UseManualIndexing(db): indexing_policy={"automatic" : False}, partition_key=PARTITION_KEY ) + properties = created_Container.read() print(created_Container) print("\n" + "-" * 25 + "\n2. Collection created with index policy") - print_dictionary_items(created_Container.properties["indexingPolicy"]) + print_dictionary_items(properties["indexingPolicy"]) # Create a document # Then query for that document @@ -296,9 +299,10 @@ def ExcludePathsFromIndex(db): indexing_policy=collection_to_create['indexingPolicy'], partition_key=PARTITION_KEY ) + properties = created_Container.read() print(created_Container) print("\n" + "-" * 25 + "\n4. Collection created with index policy") - print_dictionary_items(created_Container.properties["indexingPolicy"]) + print_dictionary_items(properties["indexingPolicy"]) # The effect of the above IndexingPolicy is that only id, foo, and the subDoc/searchable are indexed doc = created_Container.create_item(body=doc_with_nested_structures) @@ -358,9 +362,10 @@ def RangeScanOnHashIndex(db): indexing_policy=collection_to_create['indexingPolicy'], partition_key=PARTITION_KEY ) + properties = created_Container.read() print(created_Container) print("\n" + "-" * 25 + "\n5. Collection created with index policy") - print_dictionary_items(created_Container.properties["indexingPolicy"]) + print_dictionary_items(properties["indexingPolicy"]) doc1 = created_Container.create_item(body={ "id" : "dyn1", "length" : 10, "width" : 5, "height" : 15 }) doc2 = created_Container.create_item(body={ "id" : "dyn2", "length" : 7, "width" : 15 }) @@ -449,9 +454,10 @@ def UseRangeIndexesOnStrings(db): indexing_policy=collection_definition['indexingPolicy'], partition_key=PARTITION_KEY ) + properties = created_Container.read() print(created_Container) print("\n" + "-" * 25 + "\n6. Collection created with index policy") - print_dictionary_items(created_Container.properties["indexingPolicy"]) + print_dictionary_items(properties["indexingPolicy"]) created_Container.create_item(body={ "id" : "doc1", "region" : "USA" }) created_Container.create_item(body={ "id" : "doc2", "region" : "UK" }) @@ -484,21 +490,22 @@ def PerformIndexTransformations(db): # Create a collection with default indexing policy created_Container = db.create_container(id=CONTAINER_ID, partition_key=PARTITION_KEY) + properties = created_Container.read() print(created_Container) print("\n" + "-" * 25 + "\n7. Collection created with index policy") - print_dictionary_items(created_Container.properties["indexingPolicy"]) + print_dictionary_items(properties["indexingPolicy"]) # Insert some documents doc1 = created_Container.create_item(body={ "id" : "dyn1", "length" : 10, "width" : 5, "height" : 15 }) doc2 = created_Container.create_item(body={ "id" : "dyn2", "length" : 7, "width" : 15 }) doc3 = created_Container.create_item(body={ "id" : "dyn3", "length" : 2 }) - print("Three docs created with ids : ", doc1["id"], doc2["id"], doc3["id"], " with indexing mode", created_Container.properties['indexingPolicy']['indexingMode']) + print("Three docs created with ids : ", doc1["id"], doc2["id"], doc3["id"], " with indexing mode", properties['indexingPolicy']['indexingMode']) # Switch to use string & number range indexing with maximum precision. print("Changing to string & number range indexing with maximum precision (needed for Order By).") - created_Container.properties['indexingPolicy']['includedPaths'][0]['indexes'] = [{ + properties['indexingPolicy']['includedPaths'][0]['indexes'] = [{ 'kind': documents.IndexKind.Range, 'dataType': documents.DataType.String, 'precision': -1 @@ -507,23 +514,25 @@ def PerformIndexTransformations(db): created_Container = db.replace_container( container=created_Container.id, partition_key=PARTITION_KEY, - indexing_policy=created_Container.properties['indexingPolicy'] + indexing_policy=properties['indexingPolicy'] ) + properties = created_Container.read() # Check progress and wait for completion - should be instantaneous since we have only a few documents, but larger # collections will take time. - print_dictionary_items(created_Container.properties["indexingPolicy"]) + print_dictionary_items(properties["indexingPolicy"]) # Now exclude a path from indexing to save on storage space. print("Now excluding the path /length/ to save on storage space") - created_Container.properties['indexingPolicy']['excludedPaths'] = [{"path" : "/length/*"}] + properties['indexingPolicy']['excludedPaths'] = [{"path" : "/length/*"}] created_Container = db.replace_container( container=created_Container.id, partition_key=PARTITION_KEY, - indexing_policy=created_Container.properties['indexingPolicy'] + indexing_policy=properties['indexingPolicy'] ) - print_dictionary_items(created_Container.properties["indexingPolicy"]) + properties = created_Container.read() + print_dictionary_items(properties["indexingPolicy"]) # Cleanup db.delete_container(created_Container) @@ -577,11 +586,11 @@ def PerformMultiOrderbyQuery(db): indexing_policy=indexing_policy, partition_key=PARTITION_KEY ) - + properties = created_container.read() print(created_container) print("\n" + "-" * 25 + "\n8. Collection created with index policy") - print_dictionary_items(created_container.properties["indexingPolicy"]) + print_dictionary_items(properties["indexingPolicy"]) # Insert some documents doc1 = created_container.create_item(body={"id": "doc1", "numberField": 1, "stringField": "1", "numberField2": 1, "stringField2": "1"}) @@ -625,100 +634,6 @@ def PerformMultiOrderbyQuery(db): print("Entity doesn't exist") -def PerformMultiOrderbyQuery(client, database_id): - try: - DeleteContainerIfExists(client, database_id, COLLECTION_ID) - database_link = GetDatabaseLink(database_id) - - # Create a collection with composite indexes - indexingPolicy = { - "compositeIndexes": [ - [ - { - "path": "/numberField", - "order": "ascending" - }, - { - "path": "/stringField", - "order": "descending" - } - ], - [ - { - "path": "/numberField", - "order": "descending" - }, - { - "path": "/stringField", - "order": "ascending" - }, - { - "path": "/numberField2", - "order": "descending" - }, - { - "path": "/stringField2", - "order": "ascending" - } - ] - ] - } - - container_definition = { - 'id': COLLECTION_ID, - 'indexingPolicy': indexingPolicy - } - - created_container = client.CreateContainer(database_link, container_definition) - - print(created_container) - - print("\n" + "-" * 25 + "\n8. Collection created with index policy") - print_dictionary_items(created_container["indexingPolicy"]) - - # Insert some documents - collection_link = GetContainerLink(database_id, COLLECTION_ID) - doc1 = client.CreateItem(collection_link, {"id": "doc1", "numberField": 1, "stringField": "1", "numberField2": 1, "stringField2": "1"}) - doc2 = client.CreateItem(collection_link, {"id": "doc2", "numberField": 1, "stringField": "1", "numberField2": 1, "stringField2": "2"}) - doc3 = client.CreateItem(collection_link, {"id": "doc3", "numberField": 1, "stringField": "1", "numberField2": 2, "stringField2": "1"}) - doc4 = client.CreateItem(collection_link, {"id": "doc4", "numberField": 1, "stringField": "1", "numberField2": 2, "stringField2": "2"}) - doc5 = client.CreateItem(collection_link, {"id": "doc5", "numberField": 1, "stringField": "2", "numberField2": 1, "stringField2": "1"}) - doc6 = client.CreateItem(collection_link, {"id": "doc6", "numberField": 1, "stringField": "2", "numberField2": 1, "stringField2": "2"}) - doc7 = client.CreateItem(collection_link, {"id": "doc7", "numberField": 1, "stringField": "2", "numberField2": 2, "stringField2": "1"}) - doc8 = client.CreateItem(collection_link, {"id": "doc8", "numberField": 1, "stringField": "2", "numberField2": 2, "stringField2": "2"}) - doc9 = client.CreateItem(collection_link, {"id": "doc9", "numberField": 2, "stringField": "1", "numberField2": 1, "stringField2": "1"}) - doc10 = client.CreateItem(collection_link, {"id": "doc10", "numberField": 2, "stringField": "1", "numberField2": 1, "stringField2": "2"}) - doc11 = client.CreateItem(collection_link, {"id": "doc11", "numberField": 2, "stringField": "1", "numberField2": 2, "stringField2": "1"}) - doc12 = client.CreateItem(collection_link, {"id": "doc12", "numberField": 2, "stringField": "1", "numberField2": 2, "stringField2": "2"}) - doc13 = client.CreateItem(collection_link, {"id": "doc13", "numberField": 2, "stringField": "2", "numberField2": 1, "stringField2": "1"}) - doc14 = client.CreateItem(collection_link, {"id": "doc14", "numberField": 2, "stringField": "2", "numberField2": 1, "stringField2": "2"}) - doc15 = client.CreateItem(collection_link, {"id": "doc15", "numberField": 2, "stringField": "2", "numberField2": 2, "stringField2": "1"}) - doc16 = client.CreateItem(collection_link, {"id": "doc16", "numberField": 2, "stringField": "2", "numberField2": 2, "stringField2": "2"}) - - print("Query documents and Order by 1st composite index: Ascending numberField and Descending stringField:") - - query = { - "query": "SELECT * FROM r ORDER BY r.numberField ASC, r.stringField DESC", - } - QueryDocumentsWithCustomQuery(client, collection_link, query) - - print("Query documents and Order by inverted 2nd composite index -") - print("Ascending numberField, Descending stringField, Ascending numberField2, Descending stringField2") - - query = { - "query": "SELECT * FROM r ORDER BY r.numberField ASC, r.stringField DESC, r.numberField2 ASC, r.stringField2 DESC", - } - QueryDocumentsWithCustomQuery(client, collection_link, query) - - # Cleanup - client.DeleteContainer(collection_link) - print("\n") - except errors.CosmosResourceExistsError: - print("Entity already exists") - except errors.CosmosResourceNotFoundError: - print("Entity doesn't exist") - - def RunIndexDemo(): try: client = ObtainClient() @@ -749,9 +664,6 @@ def RunIndexDemo(): # 8. Perform Multi Orderby queries using composite indexes PerformMultiOrderbyQuery(created_db) - # 8. Perform Multi Orderby queries using composite indexes - PerformMultiOrderbyQuery(client, DATABASE_ID) - except errors.AzureError as e: raise e @@ -760,4 +672,4 @@ def RunIndexDemo(): RunIndexDemo() except Exception as e: - print("Top level Error: args:{0}, message:N/A".format(e.args)) + print("Top level Error: args:{0}, message:N/A".format(e.args)) diff --git a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py index b7a9496e795a..453f8caa9fe9 100644 --- a/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py +++ b/sdk/cosmos/azure-cosmos/samples/MultiMasterOperations/MultiMasterScenario.py @@ -27,7 +27,12 @@ def __init__(self): connection_policy.UseMultipleWriteLocations = True connection_policy.PreferredLocations = [region] - client = cosmos_client_connection.CosmosClientConnection(self.account_endpoint, self.account_key, connection_policy, documents.ConsistencyLevel.Session) + client = cosmos_client_connection.CosmosClientConnection( + self.account_endpoint, + {'masterKey': self.account_key}, + connection_policy, + documents.ConsistencyLevel.Session + ) self.workers.append(Worker(client, self.database_name, self.basic_collection_name)) diff --git a/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py b/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py index 3afcf4cf1d4c..5be4ad1345a9 100644 --- a/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py +++ b/sdk/cosmos/azure-cosmos/samples/NonPartitionedCollectionOperations/Program.py @@ -97,7 +97,7 @@ def CreateNonPartitionedCollection(db): # python 3 compatible: convert data from byte to unicode string data = data.decode('utf-8') data = json.loads(data) - created_collection = db.get_container_client(data['id']) + created_collection = db.get_container_client("mycoll") # Create a document in the non partitioned collection using the rest API and older version resource_url = base_url_split[0] + ":" + base_url_split[1] + ":" + base_url_split[2].split("/")[0] \ @@ -122,7 +122,7 @@ def CreateNonPartitionedCollection(db): data = data.decode('utf-8') data = json.loads(data) created_document = data - return created_collection, created_document + return created_collection, "SaledOrder0" @staticmethod def get_authorization(client, verb, resource_id_or_fullname, resource_type, headers): @@ -162,7 +162,7 @@ def ReadItem(container, doc_id): print('\n1.2 Reading Item by Id\n') # Note that Reads require a partition key to be spcified. - response = container.read_item(id=doc_id, partition_key=partition_key.NonePartitionKeyValue) + response = container.read_item(doc_id, partition_key=partition_key.NonePartitionKeyValue) print('Item read by Id {0}'.format(doc_id)) print('Account Number: {0}'.format(response.get('account_number'))) @@ -201,7 +201,7 @@ def QueryItems(container, doc_id): def ReplaceItem(container, doc_id): print('\n1.5 Replace an Item\n') - read_item = container.read_item(id=doc_id, partition_key=partition_key.NonePartitionKeyValue) + read_item = container.read_item(doc_id, partition_key=partition_key.NonePartitionKeyValue) read_item['subtotal'] = read_item['subtotal'] + 1 response = container.replace_item(item=read_item, body=read_item) @@ -211,7 +211,7 @@ def ReplaceItem(container, doc_id): def UpsertItem(container, doc_id): print('\n1.6 Upserting an item\n') - read_item = container.read_item(id=doc_id, partition_key=partition_key.NonePartitionKeyValue) + read_item = container.read_item(doc_id, partition_key=partition_key.NonePartitionKeyValue) read_item['subtotal'] = read_item['subtotal'] + 1 response = container.upsert_item(body=read_item) @@ -286,7 +286,7 @@ def run_sample(): try: db = client.create_database(id=DATABASE_ID) except errors.CosmosResourceExistsError: - pass + db = client.get_database_client(DATABASE_ID) # setup container for this sample try: @@ -297,7 +297,7 @@ def run_sample(): print('Container with id \'{0}\' was found'.format(CONTAINER_ID)) # Read Item created in non partitioned collection using older API version - ItemManagement.ReadItem(container, document['id']) + ItemManagement.ReadItem(container, document) ItemManagement.CreateItems(container) ItemManagement.ReadItems(container) ItemManagement.QueryItems(container, 'SalesOrder1') From cfa1b8ce93c82117c3eee80cae1f984fe3c8591d Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 6 Sep 2019 10:19:20 -0700 Subject: [PATCH 38/46] Updated docstrings --- .../azure-cosmos/azure/cosmos/container.py | 98 ++++++----- .../azure/cosmos/cosmos_client.py | 156 +++++++++++------- .../azure-cosmos/azure/cosmos/database.py | 97 ++++++----- .../azure-cosmos/azure/cosmos/errors.py | 8 +- .../azure-cosmos/azure/cosmos/scripts.py | 106 +++++++----- sdk/cosmos/azure-cosmos/azure/cosmos/user.py | 43 +++-- 6 files changed, 299 insertions(+), 209 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py index 7b3a5aa7989e..ef98dc6e67a2 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py @@ -42,7 +42,9 @@ class ContainerProxy(object): - """ An Azure Cosmos DB container. + """ + An interface to interact with a specific DB Container. + This class should not be instantiated directly, use :func:`DatabaseProxy.get_container_client` method. A container in an Azure Cosmos DB SQL API database is a collection of documents, each of which represented as an Item. @@ -114,7 +116,8 @@ def read( **kwargs # type: Any ): # type: (...) -> Dict[str, Any] - """ Read the container properties + """ + Read the container properties :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. @@ -127,7 +130,7 @@ def read( :raise `CosmosHttpResponseError`: Raised if the container couldn't be retrieved. This includes if the container does not exist. :returns: Dict representing the retrieved container. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -159,7 +162,7 @@ def read_item( ): # type: (...) -> Dict[str, str] """ - Get the item identified by `id`. + Get the item identified by `item`. :param item: The ID (name) or dict representing item to retrieve. :param partition_key: Partition key for the item to retrieve. @@ -171,6 +174,7 @@ def read_item( :param response_hook: a callable invoked with the response metadata :returns: Dict representing the item to be retrieved. :raise `CosmosHttpResponseError`: If the given item couldn't be retrieved. + :rtype: dict[str, Any] .. literalinclude:: ../../examples/examples.py :start-after: [START update_item] @@ -205,7 +209,8 @@ def read_all_items( **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] - """ List all items in the container. + """ + List all items in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. :param session_token: Token for use with Session consistency. @@ -214,6 +219,7 @@ def read_all_items( :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of items (dicts). + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -242,19 +248,19 @@ def query_items_change_feed( **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] - """ Get a sorted list of items that were changed, in the order in which they were modified. + """ + Get a sorted list of items that were changed, in the order in which they were modified. :param partition_key_range_id: ChangeFeed requests can be executed against specific partition key ranges. - This is used to process the change feed in parallel across multiple consumers. + This is used to process the change feed in parallel across multiple consumers. :param is_start_from_beginning: Get whether change feed should start from - beginning (true) or from current (false). - By default it's start from current (false). + beginning (true) or from current (false). By default it's start from current (false). :param continuation: e_tag value to be used as continuation for reading change feed. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of items (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -290,14 +296,19 @@ def query_items( **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] - """Return all results matching the given `query`. + """ + Return all results matching the given `query`. + + You can use any value for the container name in the FROM clause, but typically the container name is used. + In the examples below, the container name is "products," and is aliased as "p" for easier referencing + in the WHERE clause. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. :param partition_key: Specifies the partition key value for the item. :param enable_cross_partition_query: Allows sending of more than one request to execute the query in the Azure Cosmos DB service. - More than one request is necessary if the query is not scoped to single partition key value. + More than one request is necessary if the query is not scoped to single partition key value. :param max_item_count: Max number of items to be returned in the enumeration operation. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. @@ -307,10 +318,7 @@ def query_items( :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of items (dicts). - - You can use any value for the container name in the FROM clause, but typically the container name is used. - In the examples below, the container name is "products," and is aliased as "p" for easier referencing - in the WHERE clause. + :rtype: Iterable[dict[str, Any]] .. literalinclude:: ../../examples/examples.py :start-after: [START query_items] @@ -368,7 +376,8 @@ def replace_item( **kwargs # type: Any ): # type: (...) -> Dict[str, str] - """ Replaces the specified item if it exists in the container. + """ + Replaces the specified item if it exists in the container. :param item: The ID (name) or dict representing item to be replaced. :param body: A dict-like object representing the item to replace. @@ -382,7 +391,7 @@ def replace_item( :param response_hook: a callable invoked with the response metadata :returns: A dict representing the item after replace went through. :raise `CosmosHttpResponseError`: If the replace failed or the item with given id does not exist. - + :rtype: dict[str, Any] """ item_link = self._get_document_link(item) request_options = build_options(kwargs) @@ -412,7 +421,9 @@ def upsert_item( **kwargs # type: Any ): # type: (...) -> Dict[str, str] - """ Insert or update the specified item. + """ + Insert or update the specified item. + If the item already exists in the container, it is replaced. If it does not, it is inserted. :param body: A dict-like object representing the item to update or insert. :param session_token: Token for use with Session consistency. @@ -425,9 +436,7 @@ def upsert_item( :param response_hook: a callable invoked with the response metadata :returns: A dict representing the upserted item. :raise `CosmosHttpResponseError`: If the given item could not be upserted. - - If the item already exists in the container, it is replaced. If it does not, it is inserted. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -456,7 +465,9 @@ def create_item( **kwargs # type: Any ): # type: (...) -> Dict[str, str] - """ Create an item in the container. + """ + Create an item in the container. + To update or replace an existing item, use the :func:`ContainerProxy.upsert_item` method. :param body: A dict-like object representing the item to create. :param session_token: Token for use with Session consistency. @@ -470,9 +481,7 @@ def create_item( :param response_hook: a callable invoked with the response metadata :returns: A dict representing the new item. :raises `CosmosHttpResponseError`: If item with the given ID already exists. - - To update or replace an existing item, use the :func:`Container.upsert_item` method. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -505,7 +514,8 @@ def delete_item( **kwargs # type: Any ): # type: (...) -> None - """ Delete the specified item from the container. + """ + Delete the specified item from the container. :param item: The ID (name) or dict representing item to be deleted. :param partition_key: Specifies the partition key value for the item. @@ -519,7 +529,7 @@ def delete_item( :param response_hook: a callable invoked with the response metadata :raises `CosmosHttpResponseError`: The item wasn't deleted successfully. If the item does not exist in the container, a `404` error is returned. - + :rtype: None """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -540,12 +550,13 @@ def delete_item( @distributed_trace def read_offer(self, **kwargs): # type: (Any) -> Offer - """ Read the Offer object for this container. + """ + Read the Offer object for this container. :param response_hook: a callable invoked with the response metadata :returns: Offer for the container. :raise CosmosHttpResponseError: If no offer exists for the container or if the offer could not be retrieved. - + :rtype: ~azure.cosmos.offer.Offer """ response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() @@ -568,13 +579,14 @@ def read_offer(self, **kwargs): @distributed_trace def replace_throughput(self, throughput, **kwargs): # type: (int, Any) -> Offer - """ Replace the container's throughput + """ + Replace the container's throughput :param throughput: The throughput to be set (an integer). :param response_hook: a callable invoked with the response metadata :returns: Offer for the container, updated with new throughput. :raise CosmosHttpResponseError: If no offer exists for the container or if the offer could not be updated. - + :rtype: ~azure.cosmos.offer.Offer """ response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() @@ -600,13 +612,14 @@ def replace_throughput(self, throughput, **kwargs): @distributed_trace def list_conflicts(self, max_item_count=None, **kwargs): # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] - """ List all conflicts in the container. + """ + List all conflicts in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of conflicts (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -631,19 +644,20 @@ def query_conflicts( **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] - """Return all conflicts matching the given `query`. + """ + Return all conflicts matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. :param partition_key: Specifies the partition key value for the item. :param enable_cross_partition_query: Allows sending of more than one request to execute the query in the Azure Cosmos DB service. - More than one request is necessary if the query is not scoped to single partition key value. + More than one request is necessary if the query is not scoped to single partition key value. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of conflicts (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -667,7 +681,8 @@ def query_conflicts( @distributed_trace def get_conflict(self, conflict, partition_key, **kwargs): # type: (Union[str, Dict[str, Any]], Any, Any) -> Dict[str, str] - """ Get the conflict identified by `id`. + """ + Get the conflict identified by `conflict`. :param conflict: The ID (name) or dict representing the conflict to retrieve. :param partition_key: Partition key for the conflict to retrieve. @@ -675,7 +690,7 @@ def get_conflict(self, conflict, partition_key, **kwargs): :param response_hook: a callable invoked with the response metadata :returns: A dict representing the retrieved conflict. :raise `CosmosHttpResponseError`: If the given conflict couldn't be retrieved. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -692,7 +707,8 @@ def get_conflict(self, conflict, partition_key, **kwargs): @distributed_trace def delete_conflict(self, conflict, partition_key, **kwargs): # type: (Union[str, Dict[str, Any]], Any, Any) -> None - """ Delete the specified conflict from the container. + """ + Delete the specified conflict from the container. :param conflict: The ID (name) or dict representing the conflict to be deleted. :param partition_key: Partition key for the conflict to delete. @@ -700,7 +716,7 @@ def delete_conflict(self, conflict, partition_key, **kwargs): :param response_hook: a callable invoked with the response metadata :raises `CosmosHttpResponseError`: The conflict wasn't deleted successfully. If the conflict does not exist in the container, a `404` error is returned. - + :rtype: None """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index b8d5453ce80b..dd9ca892b4df 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -108,30 +108,48 @@ class CosmosClient(object): """ Provides a client-side logical representation of an Azure Cosmos DB account. Use this client to configure and execute requests to the Azure Cosmos DB service. + + :param str url: The URL of the Cosmos DB account. + :param credential: + Can be the account key, or a dictionary of resource tokens. + :type credential: str or dict(str, str) + :param str consistency_level: + Consistency level to use for the session. The default value is "Session". + + **Keyword arguments:** + + *request_timeout* - The HTTP request timeout in seconds. + *media_request_timeout* - The media request timeout in seconds. + *connection_mode* - The connection mode for the client - currently only supports 'Gateway'. + *media_read_mode* - The mode for use with downloading attachment content - default value is `Buffered`. + *proxy_config* - Instance of ~azure.cosmos.documents.ProxyConfiguration + *ssl_config* - Instance of ~azure.cosmos.documents.SSLConfiguration + *connection_verify* - Whether to verify the connection, default value is True. + *connection_cert* - An alternative certificate to verify the connection. + *retry_total* - Maximum retry attempts. + *retry_backoff_max* - Maximum retry wait time in seconds. + *retry_fixed_interval* - Fixed retry interval in milliseconds. + *enable_endpoint_discovery* - Enable endpoint discovery for geo-replicated database accounts. Default is True. + *preferred_locations* - The preferred locations for geo-replicated database accounts. + When `enable_endpoint_discovery` is true and `preferred_locations` is non-empty, + the client will use this list to evaluate the final location, taking into consideration + the order specified in `preferred_locations` list. The locations in this list are specified + as the names of the azure Cosmos locations like, 'West US', 'East US', 'Central India' + and so on. + *connection_policy* - An instance of ~azure.cosmos.documents.ConnectionPolicy + + .. literalinclude:: ../../examples/examples.py + :start-after: [START create_client] + :end-before: [END create_client] + :language: python + :dedent: 0 + :caption: Create a new instance of the Cosmos DB client: + :name: create_client """ def __init__(self, url, credential, consistency_level="Session", **kwargs): - # type: (str, Any, str, ConnectionPolicy) -> None - """ Instantiate a new CosmosClient. - - :param url: The URL of the Cosmos DB account. - :param credential: - Contains 'masterKey' or 'resourceTokens', where - auth['masterKey'] is the default authorization key to use to - create the client, and auth['resourceTokens'] is the alternative - authorization key. - :param consistency_level: Consistency level to use for the session. - :param connection_policy: Connection policy to use for the session. - - .. literalinclude:: ../../examples/examples.py - :start-after: [START create_client] - :end-before: [END create_client] - :language: python - :dedent: 0 - :caption: Create a new instance of the Cosmos DB client: - :name: create_client - - """ + # type: (str, Any, str, Any) -> None + """ Instantiate a new CosmosClient.""" auth = _build_auth(credential) connection_policy = _build_connection_policy(kwargs) self.client_connection = CosmosClientConnection( @@ -148,6 +166,18 @@ def __exit__(self, *args): @classmethod def from_connection_string(cls, conn_str, credential=None, consistency_level="Session", **kwargs): # type: (str, Optional[Any], str, Any) -> CosmosClient + """ + Create CosmosClient from a connection string. + + This can be retrieved from the Azure portal.For full list of optional keyword + arguments, see the CosmosClient constructor. + + :param str conn_str: The connection string. + :param credential: Alternative credentials to use instead of the key provided in the + connection string. + :type credential: str or dict(str, str) + :param str consistency_level: Consistency level to use for the session. The default value is "Session". + """ settings = _parse_connection_str(conn_str, credential) return cls( url=settings['AccountEndpoint'], @@ -177,18 +207,20 @@ def create_database( # pylint: disable=redefined-builtin **kwargs # type: Any ): # type: (...) -> DatabaseProxy - """Create a new database with the given ID (name). + """ + Create a new database with the given ID (name). :param id: ID (name) of the database to create. - :param session_token: Token for use with Session consistency. - :param initial_headers: Initial headers to be sent as part of the request. - :param access_condition: Conditions Associated with the request. - :param populate_query_metrics: Enable returning query metrics in response headers. - :param offer_throughput: The provisioned throughput for this offer. - :param request_options: Dictionary of additional properties to be used for the request. - :param response_hook: a callable invoked with the response metadata - :returns: A :class:`DatabaseProxy` instance representing the new database. - :raises `CosmosHttpResponseError`: If database with the given ID already exists. + :param str session_token: Token for use with Session consistency. + :param dict(str, str) initial_headers: Initial headers to be sent as part of the request. + :param dict(str, str) access_condition: Conditions Associated with the request. + :param bool populate_query_metrics: Enable returning query metrics in response headers. + :param int offer_throughput: The provisioned throughput for this offer. + :param dict(str, Any) request_options: Dictionary of additional properties to be used for the request. + :param Callable response_hook: a callable invoked with the response metadata + :returns: A DatabaseProxy instance representing the new database. + :rtype: ~azure.cosmos.database.DatabaseProxy + :raises `CosmosResourceExistsError`: If database with the given ID already exists. .. literalinclude:: ../../examples/examples.py :start-after: [START create_database] @@ -217,10 +249,11 @@ def get_database_client(self, database): """ Retrieve an existing database with the ID (name) `id`. - :param database: The ID (name), dict representing the properties or :class:`DatabaseProxy` + :param database: The ID (name), dict representing the properties or `DatabaseProxy` instance of the database to read. - :returns: A :class:`DatabaseProxy` instance representing the retrieved database. - + :type database: str or dict(str, str) or ~azure.cosmos.database.DatabaseProxy + :returns: A `DatabaseProxy` instance representing the retrieved database. + :rtype: ~azure.cosmos.database.DatabaseProxy """ if isinstance(database, DatabaseProxy): id_value = database.id @@ -242,14 +275,14 @@ def list_databases( """ List the databases in a Cosmos DB SQL database account. - :param max_item_count: Max number of items to be returned in the enumeration operation. - :param session_token: Token for use with Session consistency. - :param initial_headers: Initial headers to be sent as part of the request. - :param populate_query_metrics: Enable returning query metrics in response headers. - :param feed_options: Dictionary of additional properties to be used for the request. - :param response_hook: a callable invoked with the response metadata + :param int max_item_count: Max number of items to be returned in the enumeration operation. + :param str session_token: Token for use with Session consistency. + :param dict[str, str] initial_headers: Initial headers to be sent as part of the request. + :param bool populate_query_metrics: Enable returning query metrics in response headers. + :param dict[str, str] feed_options: Dictionary of additional properties to be used for the request. + :param Callable response_hook: a callable invoked with the response metadata :returns: An Iterable of database properties (dicts). - + :rtype: Iterable[dict[str, str]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -277,18 +310,18 @@ def query_databases( """ Query the databases in a Cosmos DB SQL database account. - :param query: The Azure Cosmos DB SQL query to execute. - :param parameters: Optional array of parameters to the query. Ignored if no query is provided. - :param enable_cross_partition_query: Allow scan on the queries which couldn't be + :param str query: The Azure Cosmos DB SQL query to execute. + :param list[str] parameters: Optional array of parameters to the query. Ignored if no query is provided. + :param bool enable_cross_partition_query: Allow scan on the queries which couldn't be served as indexing was opted out on the requested paths. - :param max_item_count: Max number of items to be returned in the enumeration operation. - :param session_token: Token for use with Session consistency. - :param initial_headers: Initial headers to be sent as part of the request. - :param populate_query_metrics: Enable returning query metrics in response headers. - :param feed_options: Dictionary of additional properties to be used for the request. - :param response_hook: a callable invoked with the response metadata + :param int max_item_count: Max number of items to be returned in the enumeration operation. + :param str session_token: Token for use with Session consistency. + :param dict[str, str] initial_headers: Initial headers to be sent as part of the request. + :param bool populate_query_metrics: Enable returning query metrics in response headers. + :param dict[str, Any] feed_options: Dictionary of additional properties to be used for the request. + :param Callable response_hook: a callable invoked with the response metadata :returns: An Iterable of database properties (dicts). - + :rtype: Iterable[dict[str, str]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -326,14 +359,15 @@ def delete_database( :param database: The ID (name), dict representing the properties or :class:`DatabaseProxy` instance of the database to delete. - :param session_token: Token for use with Session consistency. - :param initial_headers: Initial headers to be sent as part of the request. - :param access_condition: Conditions Associated with the request. - :param populate_query_metrics: Enable returning query metrics in response headers. - :param request_options: Dictionary of additional properties to be used for the request. - :param response_hook: a callable invoked with the response metadata + :type database: str or dict(str, str) or ~azure.cosmos.database.DatabaseProxy + :param str session_token: Token for use with Session consistency. + :param dict[str, str] initial_headers: Initial headers to be sent as part of the request. + :param dict[str, str] access_condition: Conditions Associated with the request. + :param bool populate_query_metrics: Enable returning query metrics in response headers. + :param dict[str, str] request_options: Dictionary of additional properties to be used for the request. + :param Callable response_hook: a callable invoked with the response metadata :raise CosmosHttpResponseError: If the database couldn't be deleted. - + :rtype: None """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -351,9 +385,9 @@ def get_database_account(self, **kwargs): """ Retrieve the database account information. - :param response_hook: a callable invoked with the response metadata - :returns: A :class:`DatabaseAccount` instance representing the Cosmos DB Database Account. - + :param Callable response_hook: a callable invoked with the response metadata + :returns: A `DatabaseAccount` instance representing the Cosmos DB Database Account. + :rtype: ~azure.cosmos.documents.DatabaseAccount """ response_hook = kwargs.pop('response_hook', None) result = self.client_connection.GetDatabaseAccount(**kwargs) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py index 6901d85e8535..57bbad81dd09 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py @@ -42,7 +42,9 @@ class DatabaseProxy(object): - """ Represents an Azure Cosmos DB SQL API database. + """ + An interface to interact with a specific database. + This class should not be instantiated directly, use :func:`CosmosClient.get_database_client` method. A database contains one or more containers, each of which can contain items, stored procedures, triggers, and user-defined functions. @@ -109,18 +111,17 @@ def _get_properties(self): def read(self, populate_query_metrics=None, **kwargs): # type: (Optional[bool], Any) -> Dict[str, Any] """ - Read the database properties + Read the database properties. :param database: The ID (name), dict representing the properties or :class:`DatabaseProxy` instance of the database to read. :param session_token: Token for use with Session consistency. :param initial_headers: Initial headers to be sent as part of the request. - :param populate_query_metrics: Enable returning query metrics in response headers. + :param bool populate_query_metrics: Enable returning query metrics in response headers. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: Dict[Str, Any] + :rtype: Dict[Str, Any] :raise `CosmosHttpResponseError`: If the given database couldn't be retrieved. - """ # TODO this helper function should be extracted from CosmosClient from .cosmos_client import CosmosClient @@ -172,9 +173,9 @@ def create_container( :param conflict_resolution_policy: The conflict resolution policy to apply to the container. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`ContainerProxy` instance representing the new container. + :returns: A `ContainerProxy` instance representing the new container. :raise CosmosHttpResponseError: The container creation failed. - + :rtype: ~azure.cosmos.container.ContainerProxy .. literalinclude:: ../../examples/examples.py :start-after: [START create_container] @@ -229,7 +230,8 @@ def delete_container( **kwargs # type: Any ): # type: (...) -> None - """ Delete the container + """ + Delete the container :param container: The ID (name) of the container to delete. You can either pass in the ID of the container to delete, a :class:`ContainerProxy` instance or @@ -241,7 +243,7 @@ def delete_container( :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :raise CosmosHttpResponseError: If the container couldn't be deleted. - + :rtype: None """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -255,10 +257,12 @@ def delete_container( def get_container_client(self, container): # type: (Union[str, ContainerProxy, Dict[str, Any]]) -> ContainerProxy - """ Get the specified `ContainerProxy`, or a container with specified ID (name). + """ + Get the specified `ContainerProxy`, or a container with specified ID (name). :param container: The ID (name) of the container, a :class:`ContainerProxy` instance, or a dict representing the properties of the container to be retrieved. + :rtype: ~azure.cosmos.container.ContainerProxy .. literalinclude:: ../../examples/examples.py :start-after: [START get_container] @@ -281,7 +285,8 @@ def get_container_client(self, container): @distributed_trace def list_containers(self, max_item_count=None, populate_query_metrics=None, **kwargs): # type: (Optional[int], Optional[bool], Any) -> Iterable[Dict[str, Any]] - """ List the containers in the database. + """ + List the containers in the database. :param max_item_count: Max number of items to be returned in the enumeration operation. :param session_token: Token for use with Session consistency. @@ -290,6 +295,7 @@ def list_containers(self, max_item_count=None, populate_query_metrics=None, **kw :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of container properties (dicts). + :rtype: Iterable[dict[str, Any]] .. literalinclude:: ../../examples/examples.py :start-after: [START list_containers] @@ -324,7 +330,8 @@ def query_containers( **kwargs # type: Any ): # type: (...) -> Iterable[Dict[str, Any]] - """List properties for containers in the current database + """ + List properties for containers in the current database. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. @@ -335,7 +342,7 @@ def query_containers( :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of container properties (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -366,8 +373,8 @@ def replace_container( **kwargs # type: Any ): # type: (...) -> ContainerProxy - """ Reset the properties of the container. Property changes are persisted immediately. - + """ + Reset the properties of the container. Property changes are persisted immediately. Any properties not specified will be reset to their default values. :param container: The ID (name), dict representing the properties or @@ -385,7 +392,8 @@ def replace_container( :param response_hook: a callable invoked with the response metadata :raise `CosmosHttpResponseError`: Raised if the container couldn't be replaced. This includes if the container with given id does not exist. - :returns: :class:`ContainerProxy` instance representing the container after replace completed. + :returns: A `ContainerProxy` instance representing the container after replace completed. + :rtype: ~azure.cosmos.container.ContainerProxy .. literalinclude:: ../../examples/examples.py :start-after: [START reset_container_properties] @@ -429,13 +437,14 @@ def replace_container( @distributed_trace def list_users(self, max_item_count=None, **kwargs): # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] - """ List all users in the container. + """ + List all users in the container. :param max_item_count: Max number of users to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of user properties (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -452,7 +461,8 @@ def list_users(self, max_item_count=None, **kwargs): @distributed_trace def query_users(self, query, parameters=None, max_item_count=None, **kwargs): # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] - """Return all users matching the given `query`. + """ + Return all users matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. @@ -460,7 +470,7 @@ def query_users(self, query, parameters=None, max_item_count=None, **kwargs): :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of user properties (dicts). - + :rtype: Iterable[str, Any] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -480,13 +490,13 @@ def query_users(self, query, parameters=None, max_item_count=None, **kwargs): def get_user_client(self, user): # type: (Union[str, UserProxy, Dict[str, Any]]) -> UserProxy """ - Get the user identified by `id`. + Get the user identified by `user`. :param user: The ID (name), dict representing the properties or :class:`UserProxy` instance of the user to be retrieved. - :returns: A :class:`UserProxy` instance representing the retrieved user. + :returns: A `UserProxy` instance representing the retrieved user. :raise `CosmosHttpResponseError`: If the given user couldn't be retrieved. - + :rtype: ~azure.cosmos.user.UserProxy """ if isinstance(user, UserProxy): id_value = user.id @@ -500,16 +510,17 @@ def get_user_client(self, user): @distributed_trace def create_user(self, body, **kwargs): # type: (Dict[str, Any], Any) -> UserProxy - """ Create a user in the container. + """ + Create a user in the container. + To update or replace an existing user, use the :func:`ContainerProxy.upsert_user` method. :param body: A dict-like object with an `id` key and value representing the user to be created. The user ID must be unique within the database, and consist of no more than 255 characters. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`UserProxy` instance representing the new user. + :returns: A `UserProxy` instance representing the new user. :raise `CosmosHttpResponseError`: If the given user couldn't be created. - - To update or replace an existing user, use the :func:`ContainerProxy.upsert_user` method. + :rtype: ~azure.cosmos.user.UserProxy .. literalinclude:: ../../examples/examples.py :start-after: [START create_user] @@ -536,16 +547,16 @@ def create_user(self, body, **kwargs): @distributed_trace def upsert_user(self, body, **kwargs): # type: (Dict[str, Any], Any) -> UserProxy - """ Insert or update the specified user. + """ + Insert or update the specified user. + If the user already exists in the container, it is replaced. If it does not, it is inserted. :param body: A dict-like object representing the user to update or insert. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`UserProxy` instance representing the upserted user. + :returns: A `UserProxy` instance representing the upserted user. :raise `CosmosHttpResponseError`: If the given user could not be upserted. - - If the user already exists in the container, it is replaced. If it does not, it is inserted. - + :rtype: ~azure.cosmos.user.UserProxy """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -569,16 +580,17 @@ def replace_user( **kwargs # type: Any ): # type: (...) -> UserProxy - """ Replaces the specified user if it exists in the container. + """ + Replaces the specified user if it exists in the container. :param user: The ID (name), dict representing the properties or :class:`UserProxy` instance of the user to be replaced. :param body: A dict-like object representing the user to replace. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata - :returns: A :class:`UserProxy` instance representing the user after replace went through. + :returns: A `UserProxy` instance representing the user after replace went through. :raise `CosmosHttpResponseError`: If the replace failed or the user with given id does not exist. - + :rtype: ~azure.cosmos.user.UserProxy """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -600,7 +612,8 @@ def replace_user( @distributed_trace def delete_user(self, user, **kwargs): # type: (Union[str, UserProxy, Dict[str, Any]], Any) -> None - """ Delete the specified user from the container. + """ + Delete the specified user from the container. :param user: The ID (name), dict representing the properties or :class:`UserProxy` instance of the user to be deleted. @@ -608,7 +621,7 @@ def delete_user(self, user, **kwargs): :param response_hook: a callable invoked with the response metadata :raises `CosmosHttpResponseError`: The user wasn't deleted successfully. If the user does not exist in the container, a `404` error is returned. - + :rtype: None """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -622,12 +635,13 @@ def delete_user(self, user, **kwargs): @distributed_trace def read_offer(self, **kwargs): # type: (Any) -> Offer - """ Read the Offer object for this database. + """ + Read the Offer object for this database. :param response_hook: a callable invoked with the response metadata :returns: Offer for the database. :raise CosmosHttpResponseError: If no offer exists for the database or if the offer could not be retrieved. - + :rtype: ~azure.cosmos.offer.Offer """ response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() @@ -650,13 +664,14 @@ def read_offer(self, **kwargs): @distributed_trace def replace_throughput(self, throughput, **kwargs): # type: (Optional[int], Any) -> Offer - """ Replace the database level throughput. + """ + Replace the database level throughput. :param throughput: The throughput to be set (an integer). :param response_hook: a callable invoked with the response metadata :returns: Offer for the database, updated with new throughput. :raise CosmosHttpResponseError: If no offer exists for the database or if the offer could not be updated. - + :rtype: ~azure.cosmos.offer.Offer """ response_hook = kwargs.pop('response_hook', None) properties = self._get_properties() diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py index 55f29b60384f..e10d17efb320 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py @@ -31,14 +31,12 @@ class CosmosHttpResponseError(HttpResponseError): - """Raised when a HTTP request to the Azure Cosmos has failed. - """ + """Raised when a HTTP request to the Azure Cosmos has failed.""" def __init__(self, status_code=None, message=None, response=None, **kwargs): """ - :param int status_code: - :param str message: - + :param int status_code: HTTP response code. + :param str message: Error message. """ self.headers = response.headers if response else {} self.sub_status = None diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py index ea91f8e0982b..d7208f918c4c 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/scripts.py @@ -41,6 +41,11 @@ class ScriptType(object): class ScriptsProxy(object): + """ + An interface to interact with stored procedures. + This class should not be instantiated directly, use :func:`ContainerProxy.scripts` attribute. + """ + def __init__(self, client_connection, container_link, is_system_key): # type: (CosmosClientConnection, str, bool) -> None self.client_connection = client_connection @@ -55,12 +60,13 @@ def _get_resource_link(self, script_or_id, typ): def list_stored_procedures(self, max_item_count=None, **kwargs): # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] - """ List all stored procedures in the container. + """ + List all stored procedures in the container. - :param max_item_count: Max number of items to be returned in the enumeration operation. + :param int max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :returns: An Iterable of stored procedures (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) if max_item_count is not None: @@ -72,14 +78,15 @@ def list_stored_procedures(self, max_item_count=None, **kwargs): def query_stored_procedures(self, query, parameters=None, max_item_count=None, **kwargs): # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] - """Return all stored procedures matching the given `query`. + """ + Return all stored procedures matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :returns: An Iterable of stored procedures (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) if max_item_count is not None: @@ -101,7 +108,7 @@ def get_stored_procedure(self, sproc, **kwargs): :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the retrieved stored procedure. :raise `CosmosHttpResponseError`: If the given stored procedure couldn't be retrieved. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -111,15 +118,15 @@ def get_stored_procedure(self, sproc, **kwargs): def create_stored_procedure(self, body, **kwargs): # type: (Dict[str, Any], Any) -> Dict[str, Any] - """ Create a stored procedure in the container. + """ + Create a stored procedure in the container. + To replace an existing sproc, use the :func:`Container.scripts.replace_stored_procedure` method. :param body: A dict-like object representing the sproc to create. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the new stored procedure. :raise `CosmosHttpResponseError`: If the given stored procedure couldn't be created. - - To replace an existing sproc, use the :func:`Container.scripts.replace_stored_procedure` method. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -129,14 +136,15 @@ def create_stored_procedure(self, body, **kwargs): def replace_stored_procedure(self, sproc, body, **kwargs): # type: (Union[str, Dict[str, Any]], Dict[str, Any], Any) -> Dict[str, Any] - """ Replaces the specified stored procedure if it exists in the container. + """ + Replaces the specified stored procedure if it exists in the container. :param sproc: The ID (name) or dict representing stored procedure to be replaced. :param body: A dict-like object representing the sproc to replace. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the stored procedure after replace went through. :raise `CosmosHttpResponseError`: If the replace failed or the stored procedure with given id does not exist. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -149,13 +157,14 @@ def replace_stored_procedure(self, sproc, body, **kwargs): def delete_stored_procedure(self, sproc, **kwargs): # type: (Union[str, Dict[str, Any]], Any) -> None - """ Delete the specified stored procedure from the container. + """ + Delete the specified stored procedure from the container. :param sproc: The ID (name) or dict representing stored procedure to be deleted. :param request_options: Dictionary of additional properties to be used for the request. :raises `CosmosHttpResponseError`: The sproc wasn't deleted successfully. If the sproc does not exist in the container, a `404` error is returned. - + :rtype: None """ request_options = build_options(kwargs) @@ -172,17 +181,18 @@ def execute_stored_procedure( **kwargs # type: Any ): # type: (...) -> Any - """ execute the specified stored procedure. + """ + Execute the specified stored procedure. :param sproc: The ID (name) or dict representing stored procedure to be executed. :param params: List of parameters to be passed to the stored procedure to be executed. - :param enable_script_logging: Enables or disables script logging for the current request. + :param bool enable_script_logging: Enables or disables script logging for the current request. :param partition_key: Specifies the partition key to indicate which partition the sproc should execute on. :param request_options: Dictionary of additional properties to be used for the request. - :returns: result of the executed stored procedure for the given parameters. + :returns: Result of the executed stored procedure for the given parameters. :raise `CosmosHttpResponseError`: If the stored procedure execution failed or if the stored procedure with given id does not exists in the container. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -204,12 +214,13 @@ def execute_stored_procedure( def list_triggers(self, max_item_count=None, **kwargs): # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] - """ List all triggers in the container. + """ + List all triggers in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :returns: An Iterable of triggers (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) if max_item_count is not None: @@ -221,14 +232,15 @@ def list_triggers(self, max_item_count=None, **kwargs): def query_triggers(self, query, parameters=None, max_item_count=None, **kwargs): # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] - """Return all triggers matching the given `query`. + """ + Return all triggers matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :returns: An Iterable of triggers (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) if max_item_count is not None: @@ -250,7 +262,7 @@ def get_trigger(self, trigger, **kwargs): :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the retrieved trigger. :raise `CosmosHttpResponseError`: If the given trigger couldn't be retrieved. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -260,15 +272,15 @@ def get_trigger(self, trigger, **kwargs): def create_trigger(self, body, **kwargs): # type: (Dict[str, Any], Any) -> Dict[str, Any] - """ Create a trigger in the container. + """ + Create a trigger in the container. + To replace an existing trigger, use the :func:`ContainerProxy.scripts.replace_trigger` method. :param body: A dict-like object representing the trigger to create. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the new trigger. :raise `CosmosHttpResponseError`: If the given trigger couldn't be created. - - To replace an existing trigger, use the :func:`Container.scripts.replace_trigger` method. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -278,14 +290,15 @@ def create_trigger(self, body, **kwargs): def replace_trigger(self, trigger, body, **kwargs): # type: (Union[str, Dict[str, Any]], Dict[str, Any], Any) -> Dict[str, Any] - """ Replaces the specified tigger if it exists in the container. + """ + Replaces the specified tigger if it exists in the container. :param trigger: The ID (name) or dict representing trigger to be replaced. :param body: A dict-like object representing the trigger to replace. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the trigger after replace went through. :raise `CosmosHttpResponseError`: If the replace failed or the trigger with given id does not exist. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -298,13 +311,14 @@ def replace_trigger(self, trigger, body, **kwargs): def delete_trigger(self, trigger, **kwargs): # type: (Union[str, Dict[str, Any]], Any) -> None - """ Delete the specified trigger from the container. + """ + Delete the specified trigger from the container. :param trigger: The ID (name) or dict representing trigger to be deleted. :param request_options: Dictionary of additional properties to be used for the request. :raises `CosmosHttpResponseError`: The trigger wasn't deleted successfully. If the trigger does not exist in the container, a `404` error is returned. - + :rtype: None """ request_options = build_options(kwargs) @@ -314,12 +328,13 @@ def delete_trigger(self, trigger, **kwargs): def list_user_defined_functions(self, max_item_count=None, **kwargs): # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] - """ List all user defined functions in the container. + """ + List all user defined functions in the container. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :returns: An Iterable of user defined functions (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) if max_item_count is not None: @@ -331,14 +346,15 @@ def list_user_defined_functions(self, max_item_count=None, **kwargs): def query_user_defined_functions(self, query, parameters=None, max_item_count=None, **kwargs): # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] - """Return all user defined functions matching the given `query`. + """ + Return all user defined functions matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. :param max_item_count: Max number of items to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :returns: An Iterable of user defined functions (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) if max_item_count is not None: @@ -360,7 +376,7 @@ def get_user_defined_function(self, udf, **kwargs): :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the retrieved user defined function. :raise `CosmosHttpResponseError`: If the given user defined function couldn't be retrieved. - + :rtype: Iterable[dict[str, Any]] """ request_options = build_options(kwargs) @@ -370,15 +386,15 @@ def get_user_defined_function(self, udf, **kwargs): def create_user_defined_function(self, body, **kwargs): # type: (Dict[str, Any], Any) -> Dict[str, Any] - """ Create a user defined function in the container. + """ + Create a user defined function in the container. + To replace an existing udf, use the :func:`ContainerProxy.scripts.replace_user_defined_function` method. :param body: A dict-like object representing the udf to create. :param request_options: Dictionary of additional properties to be used for the request. :returns: A dict representing the new user defined function. :raise `CosmosHttpResponseError`: If the given user defined function couldn't be created. - - To replace an existing udf, use the :func:`Container.scripts.replace_user_defined_function` method. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -388,7 +404,8 @@ def create_user_defined_function(self, body, **kwargs): def replace_user_defined_function(self, udf, body, **kwargs): # type: (Union[str, Dict[str, Any]], Dict[str, Any], Any) -> Dict[str, Any] - """ Replaces the specified user defined function if it exists in the container. + """ + Replaces the specified user defined function if it exists in the container. :param udf: The ID (name) or dict representing udf to be replaced. :param body: A dict-like object representing the udf to replace. @@ -396,7 +413,7 @@ def replace_user_defined_function(self, udf, body, **kwargs): :returns: A dict representing the user defined function after replace went through. :raise `CosmosHttpResponseError`: If the replace failed or the user defined function with given id does not exist. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) @@ -409,13 +426,14 @@ def replace_user_defined_function(self, udf, body, **kwargs): def delete_user_defined_function(self, udf, **kwargs): # type: (Union[str, Dict[str, Any]], Any) -> None - """ Delete the specified user defined function from the container. + """ + Delete the specified user defined function from the container. :param udf: The ID (name) or dict representing udf to be deleted. :param request_options: Dictionary of additional properties to be used for the request. :raises `CosmosHttpResponseError`: The udf wasn't deleted successfully. If the udf does not exist in the container, a `404` error is returned. - + :rtype: None """ request_options = build_options(kwargs) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py index 0e5ea04fa57f..51d3badda16b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py @@ -35,6 +35,10 @@ class UserProxy(object): + """ + An interface to interact with a specific user. + This class should not be instantiated directly, use :func:`DatabaseProxy.get_user_client` method. + """ def __init__(self, client_connection, id, database_link, properties=None): # pylint: disable=redefined-builtin # type: (CosmosClientConnection, str, str, Dict[str, Any]) -> None @@ -69,7 +73,7 @@ def read(self, **kwargs): :param response_hook: a callable invoked with the response metadata :returns: A :class:`UserProxy` instance representing the retrieved user. :raise `CosmosHttpResponseError`: If the given user couldn't be retrieved. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -84,13 +88,14 @@ def read(self, **kwargs): @distributed_trace def list_permissions(self, max_item_count=None, **kwargs): # type: (Optional[int], Any) -> Iterable[Dict[str, Any]] - """ List all permission for the user. + """ + List all permission for the user. :param max_item_count: Max number of permissions to be returned in the enumeration operation. :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of permissions (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -113,7 +118,8 @@ def query_permissions( **kwargs ): # type: (str, Optional[List[str]], Optional[int], Any) -> Iterable[Dict[str, Any]] - """Return all permissions matching the given `query`. + """ + Return all permissions matching the given `query`. :param query: The Azure Cosmos DB SQL query to execute. :param parameters: Optional array of parameters to the query. Ignored if no query is provided. @@ -121,7 +127,7 @@ def query_permissions( :param feed_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: An Iterable of permissions (dicts). - + :rtype: Iterable[dict[str, Any]] """ feed_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -152,7 +158,7 @@ def get_permission(self, permission, **kwargs): :param response_hook: a callable invoked with the response metadata :returns: A dict representing the retrieved permission. :raise `CosmosHttpResponseError`: If the given permission couldn't be retrieved. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -175,16 +181,16 @@ def get_permission(self, permission, **kwargs): @distributed_trace def create_permission(self, body, **kwargs): # type: (Dict[str, Any], Any) -> Permission - """ Create a permission for the user. + """ + Create a permission for the user. + To update or replace an existing permision, use the :func:`UserProxy.upsert_permission` method. :param body: A dict-like object representing the permission to create. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the new permission. :raise `CosmosHttpResponseError`: If the given permission couldn't be created. - - To update or replace an existing permision, use the :func:`UserProxy.upsert_permission` method. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -207,15 +213,16 @@ def create_permission(self, body, **kwargs): @distributed_trace def upsert_permission(self, body, **kwargs): # type: (Dict[str, Any], Any) -> Permission - """ Insert or update the specified permission. + """ + Insert or update the specified permission. + If the permission already exists in the container, it is replaced. If it does not, it is inserted. :param body: A dict-like object representing the permission to update or insert. :param request_options: Dictionary of additional properties to be used for the request. :param response_hook: a callable invoked with the response metadata :returns: A dict representing the upserted permission. :raise `CosmosHttpResponseError`: If the given permission could not be upserted. - - If the permission already exists in the container, it is replaced. If it does not, it is inserted. + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -238,7 +245,8 @@ def upsert_permission(self, body, **kwargs): @distributed_trace def replace_permission(self, permission, body, **kwargs): # type: (str, Dict[str, Any], Any) -> Permission - """ Replaces the specified permission if it exists for the user. + """ + Replaces the specified permission if it exists for the user. :param permission: The ID (name), dict representing the properties or :class:`Permission` instance of the permission to be replaced. @@ -247,7 +255,7 @@ def replace_permission(self, permission, body, **kwargs): :param response_hook: a callable invoked with the response metadata :returns: A dict representing the permission after replace went through. :raise `CosmosHttpResponseError`: If the replace failed or the permission with given id does not exist. - + :rtype: dict[str, Any] """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) @@ -270,7 +278,8 @@ def replace_permission(self, permission, body, **kwargs): @distributed_trace def delete_permission(self, permission, **kwargs): # type: (str, Any) -> None - """ Delete the specified permission from the user. + """ + Delete the specified permission from the user. :param permission: The ID (name), dict representing the properties or :class:`Permission` instance of the permission to be replaced. @@ -278,7 +287,7 @@ def delete_permission(self, permission, **kwargs): :param response_hook: a callable invoked with the response metadata :raises `CosmosHttpResponseError`: The permission wasn't deleted successfully. If the permission does not exist for the user, a `404` error is returned. - + :rtype: None """ request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) From 9f3ea2f9e8e9b151c082ab2da4074516a0a78dbf Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 6 Sep 2019 10:36:59 -0700 Subject: [PATCH 39/46] Fixed whitespace and imports --- .../azure/cosmos/_cosmos_client_connection.py | 8 ++++---- sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py | 2 +- .../azure-cosmos/azure/cosmos/_synchronized_request.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/container.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py | 4 ++-- sdk/cosmos/azure-cosmos/azure/cosmos/database.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/errors.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/user.py | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py index ef2e45c0abea..1fa3d3db9934 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_cosmos_client_connection.py @@ -26,16 +26,16 @@ """ from typing import Dict, Any, Optional import six -from azure.core.paging import ItemPaged -from azure.core import PipelineClient -from azure.core.pipeline.policies import ( +from azure.core.paging import ItemPaged # type: ignore +from azure.core import PipelineClient # type: ignore +from azure.core.pipeline.policies import ( # type: ignore ContentDecodePolicy, HeadersPolicy, UserAgentPolicy, NetworkTraceLoggingPolicy, CustomHookPolicy, ProxyPolicy) -from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy +from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy # type: ignore from . import _base as base from . import documents diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py index e44624b73ecb..ac8aa982013d 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py @@ -21,7 +21,7 @@ """Iterable query results in the Azure Cosmos database service. """ -from azure.core.paging import PageIterator +from azure.core.paging import PageIterator # type: ignore from azure.cosmos._execution_context import execution_dispatcher from azure.cosmos._execution_context import base_execution_context diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py index 111c2157e67f..7f1b900303ca 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_synchronized_request.py @@ -26,7 +26,7 @@ from six.moves.urllib.parse import urlparse import six -from azure.core.exceptions import DecodeError +from azure.core.exceptions import DecodeError # type: ignore from . import documents from . import errors diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py index ef98dc6e67a2..d15230e1d1a8 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py @@ -25,7 +25,7 @@ from typing import Any, Dict, List, Optional, Union, Iterable # pylint: disable=unused-import import six -from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator import distributed_trace # type: ignore from ._cosmos_client_connection import CosmosClientConnection from ._base import build_options diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index dd9ca892b4df..702e9a1492ae 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -25,7 +25,7 @@ from typing import Any, Dict, Mapping, Optional, Union, cast, Iterable, List import six -from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator import distributed_trace # type: ignore from ._cosmos_client_connection import CosmosClientConnection from ._base import build_options @@ -168,7 +168,7 @@ def from_connection_string(cls, conn_str, credential=None, consistency_level="Se # type: (str, Optional[Any], str, Any) -> CosmosClient """ Create CosmosClient from a connection string. - + This can be retrieved from the Azure portal.For full list of optional keyword arguments, see the CosmosClient constructor. diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py index 57bbad81dd09..7ffa683b9525 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py @@ -25,7 +25,7 @@ from typing import Any, List, Dict, Mapping, Union, cast, Iterable, Optional import six -from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator import distributed_trace # type: ignore from ._cosmos_client_connection import CosmosClientConnection from ._base import build_options diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py index e10d17efb320..8a0bb830ce35 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py @@ -21,7 +21,7 @@ """PyCosmos Exceptions in the Azure Cosmos database service. """ -from azure.core.exceptions import ( # pylint: disable=unused-import +from azure.core.exceptions import ( # pylint: disable=unused-import # type: ignore AzureError, HttpResponseError, ResourceExistsError, diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py index 51d3badda16b..be2a7454ca7e 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py @@ -27,7 +27,7 @@ from typing import Any, List, Dict, Union, cast, Iterable, Optional import six -from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator import distributed_trace # type: ignore from ._cosmos_client_connection import CosmosClientConnection from ._base import build_options From 4fad699ffbdd9b66a0c610d54d3035a2d16fe801 Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 6 Sep 2019 11:01:56 -0700 Subject: [PATCH 40/46] Some mypy fixes --- .../azure-cosmos/azure/cosmos/container.py | 12 ++++----- .../azure/cosmos/cosmos_client.py | 4 +-- .../azure-cosmos/azure/cosmos/database.py | 4 +-- .../azure-cosmos/azure/cosmos/errors.py | 2 +- sdk/cosmos/azure-cosmos/azure/cosmos/user.py | 26 +++++++++---------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py index d15230e1d1a8..11689ff4f369 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py @@ -22,7 +22,7 @@ """Create, read, update and delete items in the Azure Cosmos DB SQL API service. """ -from typing import Any, Dict, List, Optional, Union, Iterable # pylint: disable=unused-import +from typing import Any, Dict, List, Optional, Union, Iterable, cast # pylint: disable=unused-import import six from azure.core.tracing.decorator import distributed_trace # type: ignore @@ -65,7 +65,7 @@ def __init__(self, client_connection, database_link, id, properties=None): # py self._properties = properties self.container_link = u"{}/colls/{}".format(database_link, self.id) self._is_system_key = None - self._scripts = None # type: ScriptsProxy + self._scripts = None def _get_properties(self): # type: () -> Dict[str, Any] @@ -80,8 +80,8 @@ def is_system_key(self): properties = self._get_properties() self._is_system_key = ( properties["partitionKey"]["systemKey"] if "systemKey" in properties["partitionKey"] else False - ) # type: bool - return self._is_system_key + ) + return cast('bool', self._is_system_key) @property def scripts(self): @@ -144,12 +144,12 @@ def read( collection_link = self.container_link self._properties = self.client_connection.ReadContainer( collection_link, options=request_options, **kwargs - ) # type: Dict[str, Any] + ) if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) - return self._properties + return cast('Dict[str, Any]', self._properties) @distributed_trace def read_item( diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py index 702e9a1492ae..9387163ce416 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/cosmos_client.py @@ -38,7 +38,7 @@ def _parse_connection_str(conn_str, credential): # type: (str, Optional[Any]) -> Dict[str, str] conn_str = conn_str.rstrip(";") - conn_settings = dict( # pylint: disable=consider-using-dict-comprehension # type: ignore + conn_settings = dict( # type: ignore # pylint: disable=consider-using-dict-comprehension s.split("=", 1) for s in conn_str.split(";") ) if 'AccountEndpoint' not in conn_settings: @@ -56,7 +56,7 @@ def _build_auth(credential): elif isinstance(credential, dict): if any(k for k in credential.keys() if k in ['masterKey', 'resourceTokens', 'permissionFeed']): return credential # Backwards compatible - auth['resourceTokens'] = credential + auth['resourceTokens'] = credential # type: ignore elif hasattr(credential, '__iter__'): auth['permissionFeed'] = credential else: diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py index 7ffa683b9525..a1ee733918b5 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/database.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/database.py @@ -134,12 +134,12 @@ def read(self, populate_query_metrics=None, **kwargs): self._properties = self.client_connection.ReadDatabase( database_link, options=request_options, **kwargs - ) # type: Dict[str, Any] + ) if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) - return self._properties + return cast('Dict[str, Any]', self._properties) @distributed_trace def create_container( diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py index 8a0bb830ce35..5fcb514959c1 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/errors.py @@ -21,7 +21,7 @@ """PyCosmos Exceptions in the Azure Cosmos database service. """ -from azure.core.exceptions import ( # pylint: disable=unused-import # type: ignore +from azure.core.exceptions import ( # type: ignore # pylint: disable=unused-import AzureError, HttpResponseError, ResourceExistsError, diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py index be2a7454ca7e..9328548206b8 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/user.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/user.py @@ -83,7 +83,7 @@ def read(self, **kwargs): if response_hook: response_hook(self.client_connection.last_response_headers, self._properties) - return self._properties + return cast('Dict[str, Any]', self._properties) @distributed_trace def list_permissions(self, max_item_count=None, **kwargs): @@ -163,19 +163,19 @@ def get_permission(self, permission, **kwargs): request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) - permission = self.client_connection.ReadPermission( + permission_resp = self.client_connection.ReadPermission( permission_link=self._get_permission_link(permission), options=request_options, **kwargs ) # type: Dict[str, str] if response_hook: - response_hook(self.client_connection.last_response_headers, permission) + response_hook(self.client_connection.last_response_headers, permission_resp) return Permission( - id=permission["id"], + id=permission_resp["id"], user_link=self.user_link, - permission_mode=permission["permissionMode"], - resource_link=permission["resource"], - properties=permission, + permission_mode=permission_resp["permissionMode"], + resource_link=permission_resp["resource"], + properties=permission_resp, ) @distributed_trace @@ -260,19 +260,19 @@ def replace_permission(self, permission, body, **kwargs): request_options = build_options(kwargs) response_hook = kwargs.pop('response_hook', None) - permission = self.client_connection.ReplacePermission( + permission_resp = self.client_connection.ReplacePermission( permission_link=self._get_permission_link(permission), permission=body, options=request_options, **kwargs ) # type: Dict[str, str] if response_hook: - response_hook(self.client_connection.last_response_headers, permission) + response_hook(self.client_connection.last_response_headers, permission_resp) return Permission( - id=permission["id"], + id=permission_resp["id"], user_link=self.user_link, - permission_mode=permission["permissionMode"], - resource_link=permission["resource"], - properties=permission, + permission_mode=permission_resp["permissionMode"], + resource_link=permission_resp["resource"], + properties=permission_resp, ) @distributed_trace From 34246ba182c1f3326fd5d9f17606e8a7bfd49aca Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 6 Sep 2019 11:16:47 -0700 Subject: [PATCH 41/46] Mypy fixes --- sdk/cosmos/azure-cosmos/azure/cosmos/container.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py index 11689ff4f369..bb94879c7923 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/container.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/container.py @@ -65,7 +65,7 @@ def __init__(self, client_connection, database_link, id, properties=None): # py self._properties = properties self.container_link = u"{}/colls/{}".format(database_link, self.id) self._is_system_key = None - self._scripts = None + self._scripts = None # type: Optional[ScriptsProxy] def _get_properties(self): # type: () -> Dict[str, Any] @@ -88,7 +88,7 @@ def scripts(self): # type: () -> ScriptsProxy if self._scripts is None: self._scripts = ScriptsProxy(self.client_connection, self.container_link, self.is_system_key) - return self._scripts + return cast('ScriptsProxy', self._scripts) def _get_document_link(self, item_or_link): # type: (Union[Dict[str, Any], str]) -> str From 56c341e6fb93ce9e7f91fe4f62e9e35c03a6c7d5 Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 6 Sep 2019 11:59:32 -0700 Subject: [PATCH 42/46] Removed continuation token support --- .../azure-cosmos/azure/cosmos/_query_iterable.py | 11 +++++------ sdk/cosmos/azure-cosmos/test/crud_tests.py | 4 ++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py index ac8aa982013d..0af18a8bcff0 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py @@ -63,18 +63,18 @@ def __init__( >>> return result['Databases'] """ + if continuation_token: + raise ValueError("Continuation token not supported by Query iterator.") self._client = client self.retry_options = client.connection_policy.RetryOptions self._query = query self._options = options - if continuation_token: - self._options['continuation'] = continuation_token self._fetch_function = fetch_function self._collection_link = collection_link self._database_link = database_link self._partition_key = partition_key self._ex_context = self._create_execution_context() - super(QueryIterable, self).__init__(self._fetch_next, self._unpack, continuation_token=continuation_token) + super(QueryIterable, self).__init__(self._fetch_next, self._unpack) def _create_execution_context(self): """instantiates the internal query execution context based. @@ -91,9 +91,9 @@ def _create_execution_context(self): def _unpack(self, block): if block: self._did_a_call_already = False - return self._ex_context._continuation, block + return None, block - def _fetch_next(self, continuation): + def _fetch_next(self, *args): """Returns a block of results with respecting retry policy. This method only exists for backward compatibility reasons. (Because QueryIterable @@ -104,7 +104,6 @@ def _fetch_next(self, continuation): :rtype: list """ - self._ex_context._continuation = continuation block = self._ex_context.fetch_next_block() if not block: raise StopIteration diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index b1d40cd4df9f..eba231ed5d1a 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -2016,6 +2016,10 @@ def __create_resources(client): # Get query results page by page. results = resources['coll'].read_all_items(max_item_count=2) + + with self.assertRaises(ValueError): + page_iter = results.by_page(continuation_token="foo") + page_iter = results.by_page() first_block = list(next(page_iter)) self.assertEqual(2, len(first_block), 'First block should have 2 entries.') From d36e04bdddbe1dc917f06403a3994cd16723919b Mon Sep 17 00:00:00 2001 From: antisch Date: Fri, 6 Sep 2019 12:21:32 -0700 Subject: [PATCH 43/46] Pylint fix --- sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py index 0af18a8bcff0..3729f87318b0 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py @@ -93,7 +93,7 @@ def _unpack(self, block): self._did_a_call_already = False return None, block - def _fetch_next(self, *args): + def _fetch_next(self, *args): # pylint: disable=unused-argument """Returns a block of results with respecting retry policy. This method only exists for backward compatibility reasons. (Because QueryIterable From f74ec891e97b700a1a2d05aa6ce8ca5690538ede Mon Sep 17 00:00:00 2001 From: antisch Date: Mon, 9 Sep 2019 08:53:13 -0700 Subject: [PATCH 44/46] Docs tweaks --- sdk/cosmos/azure-cosmos/HISTORY.md | 2 +- sdk/cosmos/azure-cosmos/README.md | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/HISTORY.md b/sdk/cosmos/azure-cosmos/HISTORY.md index 1780ead7c5cd..a72609a1be0a 100644 --- a/sdk/cosmos/azure-cosmos/HISTORY.md +++ b/sdk/cosmos/azure-cosmos/HISTORY.md @@ -25,7 +25,7 @@ Version 4.0.0b2 is the second iteration in our efforts to build a more Pythonic - `multiple_write_locations` - A new classmethod constructor has been added to `CosmosClient` to enable creation via a connection string retrieved from the Azure portal. - Some `read_all` operations have been renamed to `list` operations: - - `CosmosClient.read_all_databases` -> `CosmosClient.list_database` + - `CosmosClient.read_all_databases` -> `CosmosClient.list_databases` - `Container.read_all_conflicts` -> `ContainerProxy.list_conflicts` - `Database.read_all_containers` -> `DatabaseProxy.list_containers` - `Database.read_all_users` -> `DatabaseProxy.list_users` diff --git a/sdk/cosmos/azure-cosmos/README.md b/sdk/cosmos/azure-cosmos/README.md index f9ac265aa87e..84bf724222dd 100644 --- a/sdk/cosmos/azure-cosmos/README.md +++ b/sdk/cosmos/azure-cosmos/README.md @@ -106,8 +106,6 @@ try: database = client.create_database(database_name) except errors.CosmosResourceExistsError: database = client.get_database_client(database_name) -except errors.CosmosHttpResponseError: - raise ``` ### Create a container From 0ba76abdcaba50d88bd759af9765beda82afce1c Mon Sep 17 00:00:00 2001 From: antisch Date: Mon, 9 Sep 2019 13:27:19 -0700 Subject: [PATCH 45/46] Updated continuation token --- .../azure-cosmos/azure/cosmos/_query_iterable.py | 11 ++++++----- sdk/cosmos/azure-cosmos/test/crud_tests.py | 3 --- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py index 3729f87318b0..d23d107fe40b 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py @@ -63,18 +63,18 @@ def __init__( >>> return result['Databases'] """ - if continuation_token: - raise ValueError("Continuation token not supported by Query iterator.") self._client = client self.retry_options = client.connection_policy.RetryOptions self._query = query self._options = options + if continuation_token: + options['continuation'] = continuation_token self._fetch_function = fetch_function self._collection_link = collection_link self._database_link = database_link self._partition_key = partition_key self._ex_context = self._create_execution_context() - super(QueryIterable, self).__init__(self._fetch_next, self._unpack) + super(QueryIterable, self).__init__(self._fetch_next, self._unpack, continuation_token=continuation_token) def _create_execution_context(self): """instantiates the internal query execution context based. @@ -89,9 +89,10 @@ def _create_execution_context(self): ) def _unpack(self, block): - if block: + continuation = self._client.last_response_headers.get('etag') if self._client.last_response_headers else None + if block and not continuation: self._did_a_call_already = False - return None, block + return continuation, block def _fetch_next(self, *args): # pylint: disable=unused-argument """Returns a block of results with respecting retry policy. diff --git a/sdk/cosmos/azure-cosmos/test/crud_tests.py b/sdk/cosmos/azure-cosmos/test/crud_tests.py index eba231ed5d1a..14529aede445 100644 --- a/sdk/cosmos/azure-cosmos/test/crud_tests.py +++ b/sdk/cosmos/azure-cosmos/test/crud_tests.py @@ -2017,9 +2017,6 @@ def __create_resources(client): # Get query results page by page. results = resources['coll'].read_all_items(max_item_count=2) - with self.assertRaises(ValueError): - page_iter = results.by_page(continuation_token="foo") - page_iter = results.by_page() first_block = list(next(page_iter)) self.assertEqual(2, len(first_block), 'First block should have 2 entries.') From da5f0f90668200c9b69b210452988142c1725d3f Mon Sep 17 00:00:00 2001 From: antisch Date: Mon, 9 Sep 2019 13:38:47 -0700 Subject: [PATCH 46/46] Updated response header --- sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py index d23d107fe40b..d1cf600be217 100644 --- a/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py +++ b/sdk/cosmos/azure-cosmos/azure/cosmos/_query_iterable.py @@ -89,8 +89,11 @@ def _create_execution_context(self): ) def _unpack(self, block): - continuation = self._client.last_response_headers.get('etag') if self._client.last_response_headers else None - if block and not continuation: + continuation = None + if self._client.last_response_headers: + continuation = self._client.last_response_headers.get("x-ms-continuation") or \ + self._client.last_response_headers.get('etag') + if block: self._did_a_call_already = False return continuation, block