From 26ea4212afd21c2923d8d8e8ece3481c8041cb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Waehner?= Date: Tue, 26 Aug 2025 19:58:14 +0200 Subject: [PATCH 1/2] feat(datastore): added newTransaction option for runQuery (#925) --- datastore/gcloud/aio/datastore/query.py | 18 ++++++--- datastore/tests/integration/smoke_test.py | 47 +++++++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/datastore/gcloud/aio/datastore/query.py b/datastore/gcloud/aio/datastore/query.py index 81775faaf..c8421de39 100644 --- a/datastore/gcloud/aio/datastore/query.py +++ b/datastore/gcloud/aio/datastore/query.py @@ -282,9 +282,11 @@ class QueryResult: query_result_batch_kind = QueryResultBatch def __init__(self, result_batch: Optional[QueryResultBatch] = None, - explain_metrics: Optional[ExplainMetrics] = None): + explain_metrics: Optional[ExplainMetrics] = None, + transaction: Optional[str] = None): self.result_batch = result_batch self.explain_metrics = explain_metrics + self.transaction = transaction def __repr__(self) -> str: return str(self.to_repr()) @@ -293,27 +295,33 @@ def __eq__(self, other: object) -> bool: if not isinstance(other, QueryResult): return False return (self.result_batch == other.result_batch - and self.explain_metrics == other.explain_metrics) + and self.explain_metrics == other.explain_metrics + and self.transaction == other.transaction) @classmethod def from_repr(cls, data: Dict[str, Any]) -> 'QueryResult': result_batch = None explain_metrics = None + transaction = None if 'batch' in data: result_batch = cls.query_result_batch_kind.from_repr(data['batch']) if 'explainMetrics' in data: explain_metrics = ExplainMetrics.from_repr(data['explainMetrics']) + if 'transaction' in data: + transaction = data['transaction'] - return cls(result_batch=result_batch, explain_metrics=explain_metrics) + return cls(result_batch=result_batch, + explain_metrics=explain_metrics, transaction=transaction) def to_repr(self) -> Dict[str, Any]: - result = {} + result: Dict[str, Any] = {} if self.result_batch: result['batch'] = self.result_batch.to_repr() if self.explain_metrics: result['explainMetrics'] = self.explain_metrics.to_repr() - + if self.transaction: + result['transaction'] = self.transaction return result def get_explain_metrics(self) -> Optional[ExplainMetrics]: diff --git a/datastore/tests/integration/smoke_test.py b/datastore/tests/integration/smoke_test.py index 361b9398c..4993f1941 100644 --- a/datastore/tests/integration/smoke_test.py +++ b/datastore/tests/integration/smoke_test.py @@ -135,6 +135,53 @@ async def test_start_transaction_on_lookup(creds: str, actual = await ds.lookup([key], session=s) assert actual['found'][0].entity.properties == {'animal': 'aardvark'} + # Clean up test data + await ds.delete(key, s) + + +@pytest.mark.asyncio +async def test_start_transaction_on_query( + creds: str, kind: str, project: str, +) -> None: + key = Key(project, [PathElement(kind, name=f'test_record_{uuid.uuid4()}')]) + + async with Session() as s: + ds = Datastore(project=project, service_file=creds, session=s) + + # Test query with newTransaction parameter + property_filter = PropertyFilter( + prop='animal', + operator=PropertyFilterOperator.EQUAL, + value=Value('three-toed sloth'), + ) + query = Query(kind=kind, query_filter=Filter(property_filter)) + + # Use newTransaction parameter + options = TransactionOptions(ReadWrite()) + result = await ds.runQuery(query, newTransaction=options, session=s) + assert result.transaction is not None and result.transaction + + mutations = [ + ds.make_mutation( + Operation.INSERT, key, + properties={'animal': 'three-toed sloth'}, + ), + ds.make_mutation( + Operation.UPDATE, key, + properties={'animal': 'aardvark'}, + ), + ] + await ds.commit( + mutations, + transaction=result.transaction, + session=s) + + actual = await ds.lookup([key], session=s) + assert actual['found'][0].entity.properties == {'animal': 'aardvark'} + + # Clean up test data + await ds.delete(key, s) + @pytest.mark.asyncio async def test_transaction(creds: str, kind: str, project: str) -> None: From 04553ff6c19240780ee43cffffb299f8ebe6b01a Mon Sep 17 00:00:00 2001 From: Santiago Fernandez <155993215+santiagofdialpad@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:08:31 -0300 Subject: [PATCH 2/2] chore(datastore): bump version to 9.1.0 (#930) --- datastore/pyproject.rest.toml | 2 +- datastore/pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/pyproject.rest.toml b/datastore/pyproject.rest.toml index c75126be3..b9e31bb19 100644 --- a/datastore/pyproject.rest.toml +++ b/datastore/pyproject.rest.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "gcloud-rest-datastore" -version = "9.0.0" +version = "9.1.0" description = "Python Client for Google Cloud Datastore" readme = "README.rst" diff --git a/datastore/pyproject.toml b/datastore/pyproject.toml index 3430451aa..0d01116de 100644 --- a/datastore/pyproject.toml +++ b/datastore/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "gcloud-aio-datastore" -version = "9.0.0" +version = "9.1.0" description = "Python Client for Google Cloud Datastore" readme = "README.rst"