diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a3fd6d6..28948fd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,17 @@ [1]: https://pypi.org/project/google-cloud-datastore/#history +## [2.1.0](https://www.github.com/googleapis/python-datastore/compare/v2.0.1...v2.1.0) (2020-12-04) + + +### Features + +* support autoconversion of Entity to Key for purposes of delete & delete_multi ([#123](https://www.github.com/googleapis/python-datastore/issues/123)) ([bf1dde6](https://www.github.com/googleapis/python-datastore/commit/bf1dde60b2f42e939c7dfa4a5228c3f41d565ece)) + +### Fix + +* remove six dependency ([#120](https://www.github.com/googleapis/python-datastore/issues/120)) ([b1715e5](https://www.github.com/googleapis/python-datastore/commit/b1715e500f870fd5292bb84232b0039c2ac6be85)) + ### [2.0.1](https://www.github.com/googleapis/python-datastore/compare/v2.0.0...v2.0.1) (2020-11-13) diff --git a/google/cloud/datastore/_gapic.py b/google/cloud/datastore/_gapic.py index 3200ea3c..e901fe6c 100644 --- a/google/cloud/datastore/_gapic.py +++ b/google/cloud/datastore/_gapic.py @@ -15,7 +15,7 @@ """Helpers for making API requests via gapic / gRPC.""" from grpc import insecure_channel -import six +from urllib.parse import urlparse from google.cloud._helpers import make_secure_channel from google.cloud._http import DEFAULT_USER_AGENT @@ -32,7 +32,7 @@ def make_datastore_api(client): :rtype: :class:`.datastore.v1.datastore_client.DatastoreClient` :returns: A datastore API instance with the proper credentials. """ - parse_result = six.moves.urllib_parse.urlparse(client._base_url) + parse_result = urlparse(client._base_url) host = parse_result.netloc if parse_result.scheme == "https": channel = make_secure_channel(client._credentials, DEFAULT_USER_AGENT, host) diff --git a/google/cloud/datastore/client.py b/google/cloud/datastore/client.py index 24b53b54..28d968ce 100644 --- a/google/cloud/datastore/client.py +++ b/google/cloud/datastore/client.py @@ -622,7 +622,8 @@ def delete(self, key, retry=None, timeout=None): The backend API does not make a distinction between a single key or multiple keys in a commit request. - :type key: :class:`google.cloud.datastore.key.Key` + :type key: :class:`google.cloud.datastore.key.Key`, :class:`google.cloud.datastore.entity.Entity` + :param key: The key to be deleted from the datastore. :type retry: :class:`google.api_core.retry.Retry` @@ -643,7 +644,7 @@ def delete(self, key, retry=None, timeout=None): def delete_multi(self, keys, retry=None, timeout=None): """Delete keys from the Cloud Datastore. - :type keys: list of :class:`google.cloud.datastore.key.Key` + :type keys: list of :class:`google.cloud.datastore.key.Key`, :class:`google.cloud.datastore.entity.Entity` :param keys: The keys to be deleted from the Datastore. :type retry: :class:`google.api_core.retry.Retry` @@ -671,6 +672,9 @@ def delete_multi(self, keys, retry=None, timeout=None): current.begin() for key in keys: + if isinstance(key, Entity): + # If the key is in fact an Entity, the key can be extracted. + key = key.key current.delete(key) if not in_batch: diff --git a/google/cloud/datastore/helpers.py b/google/cloud/datastore/helpers.py index f8b32f38..eada5f4f 100644 --- a/google/cloud/datastore/helpers.py +++ b/google/cloud/datastore/helpers.py @@ -22,7 +22,6 @@ from google.protobuf import struct_pb2 from google.type import latlng_pb2 -import six from google.cloud._helpers import _datetime_to_pb_timestamp from google.cloud.datastore_v1.types import datastore as datastore_pb2 @@ -105,7 +104,7 @@ def _property_tuples(entity_pb): :returns: An iterator that yields tuples of a name and ``Value`` corresponding to properties on the entity. """ - return six.iteritems(entity_pb.properties) + return iter(entity_pb.properties.items()) def entity_from_protobuf(pb): @@ -206,7 +205,7 @@ def _set_pb_meaning_from_entity(entity, name, value, value_pb, is_list=False): if is_list: if not isinstance(meaning, list): meaning = itertools.repeat(meaning) - val_iter = six.moves.zip(value_pb.array_value.values, meaning) + val_iter = zip(value_pb.array_value.values, meaning) for sub_value_pb, sub_meaning in val_iter: if sub_meaning is not None: sub_value_pb.meaning = sub_meaning @@ -359,11 +358,11 @@ def _pb_attr_value(val): name, value = "boolean", val elif isinstance(val, float): name, value = "double", val - elif isinstance(val, six.integer_types): + elif isinstance(val, int): name, value = "integer", val - elif isinstance(val, six.text_type): + elif isinstance(val, str): name, value = "string", val - elif isinstance(val, six.binary_type): + elif isinstance(val, bytes): name, value = "blob", val elif isinstance(val, Entity): name, value = "entity", val diff --git a/google/cloud/datastore/key.py b/google/cloud/datastore/key.py index c9beaeb2..98502f9c 100644 --- a/google/cloud/datastore/key.py +++ b/google/cloud/datastore/key.py @@ -16,7 +16,6 @@ import base64 import copy -import six from google.cloud.datastore_v1.types import entity as _entity_pb2 @@ -185,14 +184,14 @@ def _parse_path(path_args): result = [] for kind, id_or_name in zip(kind_list, id_or_name_list): curr_key_part = {} - if isinstance(kind, six.string_types): + if isinstance(kind, str): curr_key_part["kind"] = kind else: raise ValueError(kind, "Kind was not a string.") - if isinstance(id_or_name, six.string_types): + if isinstance(id_or_name, str): curr_key_part["name"] = id_or_name - elif isinstance(id_or_name, six.integer_types): + elif isinstance(id_or_name, int): curr_key_part["id"] = id_or_name elif id_or_name is not partial_ending: raise ValueError(id_or_name, "ID/name was not a string or integer.") @@ -264,9 +263,9 @@ def completed_key(self, id_or_name): if not self.is_partial: raise ValueError("Only a partial key can be completed.") - if isinstance(id_or_name, six.string_types): + if isinstance(id_or_name, str): id_or_name_key = "name" - elif isinstance(id_or_name, six.integer_types): + elif isinstance(id_or_name, int): id_or_name_key = "id" else: raise ValueError(id_or_name, "ID/name was not a string or integer.") diff --git a/google/cloud/datastore/version.py b/google/cloud/datastore/version.py index 956a957b..8b5d3328 100644 --- a/google/cloud/datastore/version.py +++ b/google/cloud/datastore/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -__version__ = "2.0.1" +__version__ = "2.1.0" diff --git a/tests/system/test_system.py b/tests/system/test_system.py index c807781b..a91b99ae 100644 --- a/tests/system/test_system.py +++ b/tests/system/test_system.py @@ -18,7 +18,6 @@ import warnings import requests -import six from google.cloud._helpers import UTC from google.cloud import datastore @@ -73,9 +72,8 @@ def setUpModule(): def tearDownModule(): - keys = [entity.key for entity in Config.TO_DELETE] with Config.CLIENT.transaction(): - Config.CLIENT.delete_multi(keys) + Config.CLIENT.delete_multi(Config.TO_DELETE) class TestDatastore(unittest.TestCase): @@ -84,8 +82,7 @@ def setUp(self): def tearDown(self): with Config.CLIENT.transaction(): - keys = [entity.key for entity in self.case_entities_to_delete] - Config.CLIENT.delete_multi(keys) + Config.CLIENT.delete_multi(self.case_entities_to_delete) class TestDatastoreAllocateIDs(TestDatastore): @@ -294,7 +291,7 @@ def test_limit_queries(self): # Fetch characters. iterator = query.fetch(limit=limit) - page = six.next(iterator.pages) + page = next(iterator.pages) character_entities = list(page) cursor = iterator.next_page_token self.assertEqual(len(character_entities), limit) @@ -442,7 +439,7 @@ def test_query_paginate_with_offset(self): iterator = page_query.fetch(limit=limit, offset=offset) # Fetch characters. - page = six.next(iterator.pages) + page = next(iterator.pages) entities = list(page) cursor = iterator.next_page_token self.assertEqual(len(entities), limit) @@ -466,7 +463,7 @@ def test_query_paginate_with_start_cursor(self): iterator = page_query.fetch(limit=limit, offset=offset) # Fetch characters. - page = six.next(iterator.pages) + page = next(iterator.pages) entities = list(page) cursor = iterator.next_page_token self.assertEqual(len(entities), limit) diff --git a/tests/system/utils/clear_datastore.py b/tests/system/utils/clear_datastore.py index 3438ff89..fa976f60 100644 --- a/tests/system/utils/clear_datastore.py +++ b/tests/system/utils/clear_datastore.py @@ -19,8 +19,6 @@ import os import sys -import six - from google.cloud import datastore @@ -96,7 +94,7 @@ def main(): print_func("This command will remove all entities for " "the following kinds:") print_func("\n".join("- " + val for val in kinds)) - response = six.moves.input("Is this OK [y/n]? ") + response = input("Is this OK [y/n]? ") if response.lower() == "y": diff --git a/tests/system/utils/populate_datastore.py b/tests/system/utils/populate_datastore.py index 223741e8..06b2895a 100644 --- a/tests/system/utils/populate_datastore.py +++ b/tests/system/utils/populate_datastore.py @@ -23,8 +23,6 @@ import time import uuid -import six - from google.cloud import datastore @@ -121,7 +119,7 @@ def add_characters(client=None): # Get a client that uses the test dataset. client = datastore.Client() with client.transaction() as xact: - for key_path, character in six.moves.zip(KEY_PATHS, CHARACTERS): + for key_path, character in zip(KEY_PATHS, CHARACTERS): if key_path[-1] != character["name"]: raise ValueError(("Character and key don't agree", key_path, character)) entity = datastore.Entity(key=client.key(*key_path)) diff --git a/tests/unit/test__http.py b/tests/unit/test__http.py index 700429ff..6048d40b 100644 --- a/tests/unit/test__http.py +++ b/tests/unit/test__http.py @@ -15,7 +15,7 @@ import unittest import mock -from six.moves import http_client +from http import client import requests @@ -72,7 +72,7 @@ def test_failure(self): error.code = code_pb2.FAILED_PRECONDITION http = _make_requests_session( - [_make_response(http_client.BAD_REQUEST, content=error.SerializeToString())] + [_make_response(client.BAD_REQUEST, content=error.SerializeToString())] ) with self.assertRaises(BadRequest) as exc: @@ -808,7 +808,7 @@ def test_allocate_ids_non_empty(self): self.assertEqual(key_before, key_after) -def _make_response(status=http_client.OK, content=b"", headers={}): +def _make_response(status=client.OK, content=b"", headers={}): response = requests.Response() response.status_code = status response._content = content diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 55a45c7f..3c75a5fb 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -961,6 +961,24 @@ def test_delete_multi_w_existing_transaction(self): self.assertEqual(mutated_key, key._key) client._datastore_api_internal.commit.assert_not_called() + def test_delete_multi_w_existing_transaction_entity(self): + from google.cloud.datastore.entity import Entity + + creds = _make_credentials() + client = self._make_one(credentials=creds) + client._datastore_api_internal = _make_datastore_api() + + key = _Key() + entity = Entity(key=key) + + with _NoCommitTransaction(client) as CURR_XACT: + result = client.delete_multi([entity]) + + self.assertIsNone(result) + mutated_key = _mutated_pb(self, CURR_XACT.mutations, "delete") + self.assertEqual(mutated_key, key._key) + client._datastore_api_internal.commit.assert_not_called() + def test_allocate_ids_w_partial_key(self): num_ids = 2