We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Silent error when calling datastore.put() on nonlist
Repro instructions:

```
ent = datastore.Entity(key=datastore.Key('test',1))
datastore.put(ent)
```

Entity is not put in the datastore, and call completes successfully. 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gcloud/datastore/api.py`
Content:
```
1 # Copyright 2014 Google Inc. All rights reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """Methods for interacting with Google Cloud Datastore.
16 
17 Allows interacting with the datastore via user-friendly Key, Entity and
18 Query objects rather than via protobufs.
19 """
20 
21 from gcloud.datastore import _implicit_environ
22 from gcloud.datastore.batch import Batch
23 from gcloud.datastore.transaction import Transaction
24 from gcloud.datastore import helpers
25 
26 
27 _MAX_LOOPS = 128
28 """Maximum number of iterations to wait for deferred keys."""
29 
30 
31 def _require_dataset_id(dataset_id=None, first_key=None):
32     """Infer a dataset ID from the environment, if not passed explicitly.
33 
34     Order of precedence:
35 
36     - Passed `dataset_id` (if not None).
37     - `dataset_id` of current batch / transaction (if current exists).
38     - `dataset_id` of first key
39     - `dataset_id` inferred from the environment (if `set_default_dataset_id`
40       has been called).
41 
42     :type dataset_id: string
43     :param dataset_id: Optional.
44 
45     :type first_key: :class:`gcloud.datastore.key.Key` or None
46     :param first_key: Optional: first key being manipulated.
47 
48     :rtype: string
49     :returns: A dataset ID based on the current environment.
50     :raises: :class:`EnvironmentError` if ``dataset_id`` is ``None``,
51              and cannot be inferred from the environment.
52     """
53     if dataset_id is not None:
54         return dataset_id
55     top = Batch.current()
56     if top is not None:
57         return top.dataset_id
58     if first_key is not None:
59         return first_key.dataset_id
60 
61     dataset_id = _implicit_environ.get_default_dataset_id()
62     if dataset_id is None:
63         raise EnvironmentError('Dataset ID could not be inferred.')
64     return dataset_id
65 
66 
67 def _require_connection(connection=None):
68     """Infer a connection from the environment, if not passed explicitly.
69 
70     :type connection: :class:`gcloud.datastore.connection.Connection`
71     :param connection: Optional.
72 
73     :rtype: :class:`gcloud.datastore.connection.Connection`
74     :returns: A connection based on the current environment.
75     :raises: :class:`EnvironmentError` if ``connection`` is ``None``, and
76              cannot be inferred from the environment.
77     """
78     if connection is None:
79         top = Batch.current()
80         if top is not None:
81             connection = top.connection
82         else:
83             connection = _implicit_environ.get_default_connection()
84             if connection is None:
85                 raise EnvironmentError('Connection could not be inferred.')
86     return connection
87 
88 
89 def _extended_lookup(connection, dataset_id, key_pbs,
90                      missing=None, deferred=None,
91                      eventual=False, transaction_id=None):
92     """Repeat lookup until all keys found (unless stop requested).
93 
94     Helper method for :func:`get`.
95 
96     :type connection: :class:`gcloud.datastore.connection.Connection`
97     :param connection: The connection used to connect to datastore.
98 
99     :type dataset_id: string
100     :param dataset_id: The ID of the dataset of which to make the request.
101 
102     :type key_pbs: list of :class:`gcloud.datastore._datastore_v1_pb2.Key`
103     :param key_pbs: The keys to retrieve from the datastore.
104 
105     :type missing: an empty list or None.
106     :param missing: If a list is passed, the key-only entity protobufs
107                     returned by the backend as "missing" will be copied
108                     into it.  Use only as a keyword param.
109 
110     :type deferred: an empty list or None.
111     :param deferred: If a list is passed, the key protobufs returned
112                      by the backend as "deferred" will be copied into it.
113                      Use only as a keyword param.
114 
115     :type eventual: boolean
116     :param eventual: If False (the default), request ``STRONG`` read
117                      consistency.  If True, request ``EVENTUAL`` read
118                      consistency.
119 
120     :type transaction_id: string
121     :param transaction_id: If passed, make the request in the scope of
122                            the given transaction.  Incompatible with
123                            ``eventual==True``.
124 
125     :rtype: list of :class:`gcloud.datastore._datastore_v1_pb2.Entity`
126     :returns: The requested entities.
127     :raises: :class:`ValueError` if missing / deferred are not null or
128              empty list.
129     """
130     if missing is not None and missing != []:
131         raise ValueError('missing must be None or an empty list')
132 
133     if deferred is not None and deferred != []:
134         raise ValueError('deferred must be None or an empty list')
135 
136     results = []
137 
138     loop_num = 0
139     while loop_num < _MAX_LOOPS:  # loop against possible deferred.
140         loop_num += 1
141 
142         results_found, missing_found, deferred_found = connection.lookup(
143             dataset_id=dataset_id,
144             key_pbs=key_pbs,
145             eventual=eventual,
146             transaction_id=transaction_id,
147         )
148 
149         results.extend(results_found)
150 
151         if missing is not None:
152             missing.extend(missing_found)
153 
154         if deferred is not None:
155             deferred.extend(deferred_found)
156             break
157 
158         if len(deferred_found) == 0:
159             break
160 
161         # We have deferred keys, and the user didn't ask to know about
162         # them, so retry (but only with the deferred ones).
163         key_pbs = deferred_found
164 
165     return results
166 
167 
168 def get(keys, missing=None, deferred=None, connection=None, dataset_id=None):
169     """Retrieves entities, along with their attributes.
170 
171     :type keys: list of :class:`gcloud.datastore.key.Key`
172     :param keys: The keys to be retrieved from the datastore.
173 
174     :type missing: an empty list or None.
175     :param missing: If a list is passed, the key-only entities returned
176                     by the backend as "missing" will be copied into it.
177                     Use only as a keyword param.
178 
179     :type deferred: an empty list or None.
180     :param deferred: If a list is passed, the keys returned
181                      by the backend as "deferred" will be copied into it.
182                      Use only as a keyword param.
183 
184     :type connection: :class:`gcloud.datastore.connection.Connection`
185     :param connection: Optional. The connection used to connect to datastore.
186                        If not passed, inferred from the environment.
187 
188     :type dataset_id: :class:`gcloud.datastore.connection.Connection`
189     :param dataset_id: Optional. The dataset ID used to connect to datastore.
190                        If not passed, inferred from the environment.
191 
192     :rtype: list of :class:`gcloud.datastore.entity.Entity`
193     :returns: The requested entities.
194     :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,
195              and cannot be inferred from the environment.  ValueError if
196              one or more of ``keys`` has a dataset ID which does not match
197              the passed / inferred dataset ID.
198     """
199     if not keys:
200         return []
201 
202     connection = _require_connection(connection)
203     dataset_id = _require_dataset_id(dataset_id, keys[0])
204 
205     if list(set([key.dataset_id for key in keys])) != [dataset_id]:
206         raise ValueError('Keys do not match dataset ID')
207 
208     transaction = Transaction.current()
209 
210     entity_pbs = _extended_lookup(
211         connection,
212         dataset_id=dataset_id,
213         key_pbs=[k.to_protobuf() for k in keys],
214         missing=missing,
215         deferred=deferred,
216         transaction_id=transaction and transaction.id,
217     )
218 
219     if missing is not None:
220         missing[:] = [
221             helpers.entity_from_protobuf(missed_pb)
222             for missed_pb in missing]
223 
224     if deferred is not None:
225         deferred[:] = [
226             helpers.key_from_protobuf(deferred_pb)
227             for deferred_pb in deferred]
228 
229     entities = []
230     for entity_pb in entity_pbs:
231         entities.append(helpers.entity_from_protobuf(entity_pb))
232 
233     return entities
234 
235 
236 def put(entities, connection=None, dataset_id=None):
237     """Save the entities in the Cloud Datastore.
238 
239     :type entities: list of :class:`gcloud.datastore.entity.Entity`
240     :param entities: The entities to be saved to the datastore.
241 
242     :type connection: :class:`gcloud.datastore.connection.Connection`
243     :param connection: Optional connection used to connect to datastore.
244                        If not passed, inferred from the environment.
245 
246     :type dataset_id: :class:`gcloud.datastore.connection.Connection`
247     :param dataset_id: Optional. The dataset ID used to connect to datastore.
248                        If not passed, inferred from the environment.
249 
250     :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,
251              and cannot be inferred from the environment.  ValueError if
252              one or more entities has a key with a dataset ID not matching
253              the passed / inferred dataset ID.
254     """
255     if not entities:
256         return
257 
258     connection = _require_connection(connection)
259     dataset_id = _require_dataset_id(dataset_id, entities[0].key)
260 
261     current = Batch.current()
262     in_batch = current is not None
263     if not in_batch:
264         current = Batch(dataset_id=dataset_id, connection=connection)
265     for entity in entities:
266         current.put(entity)
267     if not in_batch:
268         current.commit()
269 
270 
271 def delete(keys, connection=None, dataset_id=None):
272     """Delete the keys in the Cloud Datastore.
273 
274     :type keys: list of :class:`gcloud.datastore.key.Key`
275     :param keys: The keys to be deleted from the datastore.
276 
277     :type connection: :class:`gcloud.datastore.connection.Connection`
278     :param connection: Optional connection used to connect to datastore.
279                        If not passed, inferred from the environment.
280 
281     :type dataset_id: :class:`gcloud.datastore.connection.Connection`
282     :param dataset_id: Optional. The dataset ID used to connect to datastore.
283                        If not passed, inferred from the environment.
284 
285     :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,
286              and cannot be inferred from the environment.  ValueError if
287              one or more keys has a dataset ID not matching the passed /
288              inferred dataset ID.
289     """
290     if not keys:
291         return
292 
293     connection = _require_connection(connection)
294     dataset_id = _require_dataset_id(dataset_id, keys[0])
295 
296     # We allow partial keys to attempt a delete, the backend will fail.
297     current = Batch.current()
298     in_batch = current is not None
299     if not in_batch:
300         current = Batch(dataset_id=dataset_id, connection=connection)
301     for key in keys:
302         current.delete(key)
303     if not in_batch:
304         current.commit()
305 
306 
307 def allocate_ids(incomplete_key, num_ids, connection=None):
308     """Allocates a list of IDs from a partial key.
309 
310     :type incomplete_key: A :class:`gcloud.datastore.key.Key`
311     :param incomplete_key: Partial key to use as base for allocated IDs.
312 
313     :type num_ids: integer
314     :param num_ids: The number of IDs to allocate.
315 
316     :type connection: :class:`gcloud.datastore.connection.Connection`
317     :param connection: Optional. The connection used to connect to datastore.
318 
319     :rtype: list of :class:`gcloud.datastore.key.Key`
320     :returns: The (complete) keys allocated with ``incomplete_key`` as root.
321     :raises: :class:`ValueError` if ``incomplete_key`` is not a partial key.
322     """
323     connection = _require_connection(connection)
324 
325     if not incomplete_key.is_partial:
326         raise ValueError(('Key is not partial.', incomplete_key))
327 
328     incomplete_key_pb = incomplete_key.to_protobuf()
329     incomplete_key_pbs = [incomplete_key_pb] * num_ids
330 
331     allocated_key_pbs = connection.allocate_ids(incomplete_key.dataset_id,
332                                                 incomplete_key_pbs)
333     allocated_ids = [allocated_key_pb.path_element[-1].id
334                      for allocated_key_pb in allocated_key_pbs]
335     return [incomplete_key.completed_key(allocated_id)
336             for allocated_id in allocated_ids]
337
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/gcloud/datastore/api.py b/gcloud/datastore/api.py
--- a/gcloud/datastore/api.py
+++ b/gcloud/datastore/api.py
@@ -20,6 +20,7 @@
 
 from gcloud.datastore import _implicit_environ
 from gcloud.datastore.batch import Batch
+from gcloud.datastore.entity import Entity
 from gcloud.datastore.transaction import Transaction
 from gcloud.datastore import helpers
 
@@ -252,6 +253,9 @@
              one or more entities has a key with a dataset ID not matching
              the passed / inferred dataset ID.
     """
+    if isinstance(entities, Entity):
+        raise ValueError("Pass a sequence of entities")
+
     if not entities:
         return

{"golden_diff": "diff --git a/gcloud/datastore/api.py b/gcloud/datastore/api.py\n--- a/gcloud/datastore/api.py\n+++ b/gcloud/datastore/api.py\n@@ -20,6 +20,7 @@\n \n from gcloud.datastore import _implicit_environ\n from gcloud.datastore.batch import Batch\n+from gcloud.datastore.entity import Entity\n from gcloud.datastore.transaction import Transaction\n from gcloud.datastore import helpers\n \n@@ -252,6 +253,9 @@\n              one or more entities has a key with a dataset ID not matching\n              the passed / inferred dataset ID.\n     \"\"\"\n+    if isinstance(entities, Entity):\n+        raise ValueError(\"Pass a sequence of entities\")\n+\n     if not entities:\n         return\n", "issue": "Silent error when calling datastore.put() on nonlist\nRepro instructions:\n\n```\nent = datastore.Entity(key=datastore.Key('test',1))\ndatastore.put(ent)\n```\n\nEntity is not put in the datastore, and call completes successfully. \n\n", "before_files": [{"content": "# Copyright 2014 Google Inc. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Methods for interacting with Google Cloud Datastore.\n\nAllows interacting with the datastore via user-friendly Key, Entity and\nQuery objects rather than via protobufs.\n\"\"\"\n\nfrom gcloud.datastore import _implicit_environ\nfrom gcloud.datastore.batch import Batch\nfrom gcloud.datastore.transaction import Transaction\nfrom gcloud.datastore import helpers\n\n\n_MAX_LOOPS = 128\n\"\"\"Maximum number of iterations to wait for deferred keys.\"\"\"\n\n\ndef _require_dataset_id(dataset_id=None, first_key=None):\n    \"\"\"Infer a dataset ID from the environment, if not passed explicitly.\n\n    Order of precedence:\n\n    - Passed `dataset_id` (if not None).\n    - `dataset_id` of current batch / transaction (if current exists).\n    - `dataset_id` of first key\n    - `dataset_id` inferred from the environment (if `set_default_dataset_id`\n      has been called).\n\n    :type dataset_id: string\n    :param dataset_id: Optional.\n\n    :type first_key: :class:`gcloud.datastore.key.Key` or None\n    :param first_key: Optional: first key being manipulated.\n\n    :rtype: string\n    :returns: A dataset ID based on the current environment.\n    :raises: :class:`EnvironmentError` if ``dataset_id`` is ``None``,\n             and cannot be inferred from the environment.\n    \"\"\"\n    if dataset_id is not None:\n        return dataset_id\n    top = Batch.current()\n    if top is not None:\n        return top.dataset_id\n    if first_key is not None:\n        return first_key.dataset_id\n\n    dataset_id = _implicit_environ.get_default_dataset_id()\n    if dataset_id is None:\n        raise EnvironmentError('Dataset ID could not be inferred.')\n    return dataset_id\n\n\ndef _require_connection(connection=None):\n    \"\"\"Infer a connection from the environment, if not passed explicitly.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional.\n\n    :rtype: :class:`gcloud.datastore.connection.Connection`\n    :returns: A connection based on the current environment.\n    :raises: :class:`EnvironmentError` if ``connection`` is ``None``, and\n             cannot be inferred from the environment.\n    \"\"\"\n    if connection is None:\n        top = Batch.current()\n        if top is not None:\n            connection = top.connection\n        else:\n            connection = _implicit_environ.get_default_connection()\n            if connection is None:\n                raise EnvironmentError('Connection could not be inferred.')\n    return connection\n\n\ndef _extended_lookup(connection, dataset_id, key_pbs,\n                     missing=None, deferred=None,\n                     eventual=False, transaction_id=None):\n    \"\"\"Repeat lookup until all keys found (unless stop requested).\n\n    Helper method for :func:`get`.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: The connection used to connect to datastore.\n\n    :type dataset_id: string\n    :param dataset_id: The ID of the dataset of which to make the request.\n\n    :type key_pbs: list of :class:`gcloud.datastore._datastore_v1_pb2.Key`\n    :param key_pbs: The keys to retrieve from the datastore.\n\n    :type missing: an empty list or None.\n    :param missing: If a list is passed, the key-only entity protobufs\n                    returned by the backend as \"missing\" will be copied\n                    into it.  Use only as a keyword param.\n\n    :type deferred: an empty list or None.\n    :param deferred: If a list is passed, the key protobufs returned\n                     by the backend as \"deferred\" will be copied into it.\n                     Use only as a keyword param.\n\n    :type eventual: boolean\n    :param eventual: If False (the default), request ``STRONG`` read\n                     consistency.  If True, request ``EVENTUAL`` read\n                     consistency.\n\n    :type transaction_id: string\n    :param transaction_id: If passed, make the request in the scope of\n                           the given transaction.  Incompatible with\n                           ``eventual==True``.\n\n    :rtype: list of :class:`gcloud.datastore._datastore_v1_pb2.Entity`\n    :returns: The requested entities.\n    :raises: :class:`ValueError` if missing / deferred are not null or\n             empty list.\n    \"\"\"\n    if missing is not None and missing != []:\n        raise ValueError('missing must be None or an empty list')\n\n    if deferred is not None and deferred != []:\n        raise ValueError('deferred must be None or an empty list')\n\n    results = []\n\n    loop_num = 0\n    while loop_num < _MAX_LOOPS:  # loop against possible deferred.\n        loop_num += 1\n\n        results_found, missing_found, deferred_found = connection.lookup(\n            dataset_id=dataset_id,\n            key_pbs=key_pbs,\n            eventual=eventual,\n            transaction_id=transaction_id,\n        )\n\n        results.extend(results_found)\n\n        if missing is not None:\n            missing.extend(missing_found)\n\n        if deferred is not None:\n            deferred.extend(deferred_found)\n            break\n\n        if len(deferred_found) == 0:\n            break\n\n        # We have deferred keys, and the user didn't ask to know about\n        # them, so retry (but only with the deferred ones).\n        key_pbs = deferred_found\n\n    return results\n\n\ndef get(keys, missing=None, deferred=None, connection=None, dataset_id=None):\n    \"\"\"Retrieves entities, along with their attributes.\n\n    :type keys: list of :class:`gcloud.datastore.key.Key`\n    :param keys: The keys to be retrieved from the datastore.\n\n    :type missing: an empty list or None.\n    :param missing: If a list is passed, the key-only entities returned\n                    by the backend as \"missing\" will be copied into it.\n                    Use only as a keyword param.\n\n    :type deferred: an empty list or None.\n    :param deferred: If a list is passed, the keys returned\n                     by the backend as \"deferred\" will be copied into it.\n                     Use only as a keyword param.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional. The connection used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :type dataset_id: :class:`gcloud.datastore.connection.Connection`\n    :param dataset_id: Optional. The dataset ID used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :rtype: list of :class:`gcloud.datastore.entity.Entity`\n    :returns: The requested entities.\n    :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,\n             and cannot be inferred from the environment.  ValueError if\n             one or more of ``keys`` has a dataset ID which does not match\n             the passed / inferred dataset ID.\n    \"\"\"\n    if not keys:\n        return []\n\n    connection = _require_connection(connection)\n    dataset_id = _require_dataset_id(dataset_id, keys[0])\n\n    if list(set([key.dataset_id for key in keys])) != [dataset_id]:\n        raise ValueError('Keys do not match dataset ID')\n\n    transaction = Transaction.current()\n\n    entity_pbs = _extended_lookup(\n        connection,\n        dataset_id=dataset_id,\n        key_pbs=[k.to_protobuf() for k in keys],\n        missing=missing,\n        deferred=deferred,\n        transaction_id=transaction and transaction.id,\n    )\n\n    if missing is not None:\n        missing[:] = [\n            helpers.entity_from_protobuf(missed_pb)\n            for missed_pb in missing]\n\n    if deferred is not None:\n        deferred[:] = [\n            helpers.key_from_protobuf(deferred_pb)\n            for deferred_pb in deferred]\n\n    entities = []\n    for entity_pb in entity_pbs:\n        entities.append(helpers.entity_from_protobuf(entity_pb))\n\n    return entities\n\n\ndef put(entities, connection=None, dataset_id=None):\n    \"\"\"Save the entities in the Cloud Datastore.\n\n    :type entities: list of :class:`gcloud.datastore.entity.Entity`\n    :param entities: The entities to be saved to the datastore.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional connection used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :type dataset_id: :class:`gcloud.datastore.connection.Connection`\n    :param dataset_id: Optional. The dataset ID used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,\n             and cannot be inferred from the environment.  ValueError if\n             one or more entities has a key with a dataset ID not matching\n             the passed / inferred dataset ID.\n    \"\"\"\n    if not entities:\n        return\n\n    connection = _require_connection(connection)\n    dataset_id = _require_dataset_id(dataset_id, entities[0].key)\n\n    current = Batch.current()\n    in_batch = current is not None\n    if not in_batch:\n        current = Batch(dataset_id=dataset_id, connection=connection)\n    for entity in entities:\n        current.put(entity)\n    if not in_batch:\n        current.commit()\n\n\ndef delete(keys, connection=None, dataset_id=None):\n    \"\"\"Delete the keys in the Cloud Datastore.\n\n    :type keys: list of :class:`gcloud.datastore.key.Key`\n    :param keys: The keys to be deleted from the datastore.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional connection used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :type dataset_id: :class:`gcloud.datastore.connection.Connection`\n    :param dataset_id: Optional. The dataset ID used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,\n             and cannot be inferred from the environment.  ValueError if\n             one or more keys has a dataset ID not matching the passed /\n             inferred dataset ID.\n    \"\"\"\n    if not keys:\n        return\n\n    connection = _require_connection(connection)\n    dataset_id = _require_dataset_id(dataset_id, keys[0])\n\n    # We allow partial keys to attempt a delete, the backend will fail.\n    current = Batch.current()\n    in_batch = current is not None\n    if not in_batch:\n        current = Batch(dataset_id=dataset_id, connection=connection)\n    for key in keys:\n        current.delete(key)\n    if not in_batch:\n        current.commit()\n\n\ndef allocate_ids(incomplete_key, num_ids, connection=None):\n    \"\"\"Allocates a list of IDs from a partial key.\n\n    :type incomplete_key: A :class:`gcloud.datastore.key.Key`\n    :param incomplete_key: Partial key to use as base for allocated IDs.\n\n    :type num_ids: integer\n    :param num_ids: The number of IDs to allocate.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional. The connection used to connect to datastore.\n\n    :rtype: list of :class:`gcloud.datastore.key.Key`\n    :returns: The (complete) keys allocated with ``incomplete_key`` as root.\n    :raises: :class:`ValueError` if ``incomplete_key`` is not a partial key.\n    \"\"\"\n    connection = _require_connection(connection)\n\n    if not incomplete_key.is_partial:\n        raise ValueError(('Key is not partial.', incomplete_key))\n\n    incomplete_key_pb = incomplete_key.to_protobuf()\n    incomplete_key_pbs = [incomplete_key_pb] * num_ids\n\n    allocated_key_pbs = connection.allocate_ids(incomplete_key.dataset_id,\n                                                incomplete_key_pbs)\n    allocated_ids = [allocated_key_pb.path_element[-1].id\n                     for allocated_key_pb in allocated_key_pbs]\n    return [incomplete_key.completed_key(allocated_id)\n            for allocated_id in allocated_ids]\n", "path": "gcloud/datastore/api.py"}], "after_files": [{"content": "# Copyright 2014 Google Inc. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Methods for interacting with Google Cloud Datastore.\n\nAllows interacting with the datastore via user-friendly Key, Entity and\nQuery objects rather than via protobufs.\n\"\"\"\n\nfrom gcloud.datastore import _implicit_environ\nfrom gcloud.datastore.batch import Batch\nfrom gcloud.datastore.entity import Entity\nfrom gcloud.datastore.transaction import Transaction\nfrom gcloud.datastore import helpers\n\n\n_MAX_LOOPS = 128\n\"\"\"Maximum number of iterations to wait for deferred keys.\"\"\"\n\n\ndef _require_dataset_id(dataset_id=None, first_key=None):\n    \"\"\"Infer a dataset ID from the environment, if not passed explicitly.\n\n    Order of precedence:\n\n    - Passed `dataset_id` (if not None).\n    - `dataset_id` of current batch / transaction (if current exists).\n    - `dataset_id` of first key\n    - `dataset_id` inferred from the environment (if `set_default_dataset_id`\n      has been called).\n\n    :type dataset_id: string\n    :param dataset_id: Optional.\n\n    :type first_key: :class:`gcloud.datastore.key.Key` or None\n    :param first_key: Optional: first key being manipulated.\n\n    :rtype: string\n    :returns: A dataset ID based on the current environment.\n    :raises: :class:`EnvironmentError` if ``dataset_id`` is ``None``,\n             and cannot be inferred from the environment.\n    \"\"\"\n    if dataset_id is not None:\n        return dataset_id\n    top = Batch.current()\n    if top is not None:\n        return top.dataset_id\n    if first_key is not None:\n        return first_key.dataset_id\n\n    dataset_id = _implicit_environ.get_default_dataset_id()\n    if dataset_id is None:\n        raise EnvironmentError('Dataset ID could not be inferred.')\n    return dataset_id\n\n\ndef _require_connection(connection=None):\n    \"\"\"Infer a connection from the environment, if not passed explicitly.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional.\n\n    :rtype: :class:`gcloud.datastore.connection.Connection`\n    :returns: A connection based on the current environment.\n    :raises: :class:`EnvironmentError` if ``connection`` is ``None``, and\n             cannot be inferred from the environment.\n    \"\"\"\n    if connection is None:\n        top = Batch.current()\n        if top is not None:\n            connection = top.connection\n        else:\n            connection = _implicit_environ.get_default_connection()\n            if connection is None:\n                raise EnvironmentError('Connection could not be inferred.')\n    return connection\n\n\ndef _extended_lookup(connection, dataset_id, key_pbs,\n                     missing=None, deferred=None,\n                     eventual=False, transaction_id=None):\n    \"\"\"Repeat lookup until all keys found (unless stop requested).\n\n    Helper method for :func:`get`.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: The connection used to connect to datastore.\n\n    :type dataset_id: string\n    :param dataset_id: The ID of the dataset of which to make the request.\n\n    :type key_pbs: list of :class:`gcloud.datastore._datastore_v1_pb2.Key`\n    :param key_pbs: The keys to retrieve from the datastore.\n\n    :type missing: an empty list or None.\n    :param missing: If a list is passed, the key-only entity protobufs\n                    returned by the backend as \"missing\" will be copied\n                    into it.  Use only as a keyword param.\n\n    :type deferred: an empty list or None.\n    :param deferred: If a list is passed, the key protobufs returned\n                     by the backend as \"deferred\" will be copied into it.\n                     Use only as a keyword param.\n\n    :type eventual: boolean\n    :param eventual: If False (the default), request ``STRONG`` read\n                     consistency.  If True, request ``EVENTUAL`` read\n                     consistency.\n\n    :type transaction_id: string\n    :param transaction_id: If passed, make the request in the scope of\n                           the given transaction.  Incompatible with\n                           ``eventual==True``.\n\n    :rtype: list of :class:`gcloud.datastore._datastore_v1_pb2.Entity`\n    :returns: The requested entities.\n    :raises: :class:`ValueError` if missing / deferred are not null or\n             empty list.\n    \"\"\"\n    if missing is not None and missing != []:\n        raise ValueError('missing must be None or an empty list')\n\n    if deferred is not None and deferred != []:\n        raise ValueError('deferred must be None or an empty list')\n\n    results = []\n\n    loop_num = 0\n    while loop_num < _MAX_LOOPS:  # loop against possible deferred.\n        loop_num += 1\n\n        results_found, missing_found, deferred_found = connection.lookup(\n            dataset_id=dataset_id,\n            key_pbs=key_pbs,\n            eventual=eventual,\n            transaction_id=transaction_id,\n        )\n\n        results.extend(results_found)\n\n        if missing is not None:\n            missing.extend(missing_found)\n\n        if deferred is not None:\n            deferred.extend(deferred_found)\n            break\n\n        if len(deferred_found) == 0:\n            break\n\n        # We have deferred keys, and the user didn't ask to know about\n        # them, so retry (but only with the deferred ones).\n        key_pbs = deferred_found\n\n    return results\n\n\ndef get(keys, missing=None, deferred=None, connection=None, dataset_id=None):\n    \"\"\"Retrieves entities, along with their attributes.\n\n    :type keys: list of :class:`gcloud.datastore.key.Key`\n    :param keys: The keys to be retrieved from the datastore.\n\n    :type missing: an empty list or None.\n    :param missing: If a list is passed, the key-only entities returned\n                    by the backend as \"missing\" will be copied into it.\n                    Use only as a keyword param.\n\n    :type deferred: an empty list or None.\n    :param deferred: If a list is passed, the keys returned\n                     by the backend as \"deferred\" will be copied into it.\n                     Use only as a keyword param.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional. The connection used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :type dataset_id: :class:`gcloud.datastore.connection.Connection`\n    :param dataset_id: Optional. The dataset ID used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :rtype: list of :class:`gcloud.datastore.entity.Entity`\n    :returns: The requested entities.\n    :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,\n             and cannot be inferred from the environment.  ValueError if\n             one or more of ``keys`` has a dataset ID which does not match\n             the passed / inferred dataset ID.\n    \"\"\"\n    if not keys:\n        return []\n\n    connection = _require_connection(connection)\n    dataset_id = _require_dataset_id(dataset_id, keys[0])\n\n    if list(set([key.dataset_id for key in keys])) != [dataset_id]:\n        raise ValueError('Keys do not match dataset ID')\n\n    transaction = Transaction.current()\n\n    entity_pbs = _extended_lookup(\n        connection,\n        dataset_id=dataset_id,\n        key_pbs=[k.to_protobuf() for k in keys],\n        missing=missing,\n        deferred=deferred,\n        transaction_id=transaction and transaction.id,\n    )\n\n    if missing is not None:\n        missing[:] = [\n            helpers.entity_from_protobuf(missed_pb)\n            for missed_pb in missing]\n\n    if deferred is not None:\n        deferred[:] = [\n            helpers.key_from_protobuf(deferred_pb)\n            for deferred_pb in deferred]\n\n    entities = []\n    for entity_pb in entity_pbs:\n        entities.append(helpers.entity_from_protobuf(entity_pb))\n\n    return entities\n\n\ndef put(entities, connection=None, dataset_id=None):\n    \"\"\"Save the entities in the Cloud Datastore.\n\n    :type entities: list of :class:`gcloud.datastore.entity.Entity`\n    :param entities: The entities to be saved to the datastore.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional connection used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :type dataset_id: :class:`gcloud.datastore.connection.Connection`\n    :param dataset_id: Optional. The dataset ID used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,\n             and cannot be inferred from the environment.  ValueError if\n             one or more entities has a key with a dataset ID not matching\n             the passed / inferred dataset ID.\n    \"\"\"\n    if isinstance(entities, Entity):\n        raise ValueError(\"Pass a sequence of entities\")\n\n    if not entities:\n        return\n\n    connection = _require_connection(connection)\n    dataset_id = _require_dataset_id(dataset_id, entities[0].key)\n\n    current = Batch.current()\n    in_batch = current is not None\n    if not in_batch:\n        current = Batch(dataset_id=dataset_id, connection=connection)\n    for entity in entities:\n        current.put(entity)\n    if not in_batch:\n        current.commit()\n\n\ndef delete(keys, connection=None, dataset_id=None):\n    \"\"\"Delete the keys in the Cloud Datastore.\n\n    :type keys: list of :class:`gcloud.datastore.key.Key`\n    :param keys: The keys to be deleted from the datastore.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional connection used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :type dataset_id: :class:`gcloud.datastore.connection.Connection`\n    :param dataset_id: Optional. The dataset ID used to connect to datastore.\n                       If not passed, inferred from the environment.\n\n    :raises: EnvironmentError if ``connection`` or ``dataset_id`` not passed,\n             and cannot be inferred from the environment.  ValueError if\n             one or more keys has a dataset ID not matching the passed /\n             inferred dataset ID.\n    \"\"\"\n    if not keys:\n        return\n\n    connection = _require_connection(connection)\n    dataset_id = _require_dataset_id(dataset_id, keys[0])\n\n    # We allow partial keys to attempt a delete, the backend will fail.\n    current = Batch.current()\n    in_batch = current is not None\n    if not in_batch:\n        current = Batch(dataset_id=dataset_id, connection=connection)\n    for key in keys:\n        current.delete(key)\n    if not in_batch:\n        current.commit()\n\n\ndef allocate_ids(incomplete_key, num_ids, connection=None):\n    \"\"\"Allocates a list of IDs from a partial key.\n\n    :type incomplete_key: A :class:`gcloud.datastore.key.Key`\n    :param incomplete_key: Partial key to use as base for allocated IDs.\n\n    :type num_ids: integer\n    :param num_ids: The number of IDs to allocate.\n\n    :type connection: :class:`gcloud.datastore.connection.Connection`\n    :param connection: Optional. The connection used to connect to datastore.\n\n    :rtype: list of :class:`gcloud.datastore.key.Key`\n    :returns: The (complete) keys allocated with ``incomplete_key`` as root.\n    :raises: :class:`ValueError` if ``incomplete_key`` is not a partial key.\n    \"\"\"\n    connection = _require_connection(connection)\n\n    if not incomplete_key.is_partial:\n        raise ValueError(('Key is not partial.', incomplete_key))\n\n    incomplete_key_pb = incomplete_key.to_protobuf()\n    incomplete_key_pbs = [incomplete_key_pb] * num_ids\n\n    allocated_key_pbs = connection.allocate_ids(incomplete_key.dataset_id,\n                                                incomplete_key_pbs)\n    allocated_ids = [allocated_key_pb.path_element[-1].id\n                     for allocated_key_pb in allocated_key_pbs]\n    return [incomplete_key.completed_key(allocated_id)\n            for allocated_id in allocated_ids]\n", "path": "gcloud/datastore/api.py"}]}