From dd3c7b55036b08fe019daec32c47185d72c6fd65 Mon Sep 17 00:00:00 2001 From: Martin Durant Date: Fri, 6 Oct 2023 15:09:53 -0400 Subject: [PATCH] Fix small transaction (#586) --- gcsfs/core.py | 9 ++++----- gcsfs/retry.py | 3 ++- gcsfs/tests/test_core.py | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/gcsfs/core.py b/gcsfs/core.py index f33f6e8c..2c1afd42 100644 --- a/gcsfs/core.py +++ b/gcsfs/core.py @@ -1736,7 +1736,7 @@ def _upload_chunk(self, final=False): head = {} l = len(data) - if (l < GCS_MIN_BLOCK_SIZE) and not final: + if (l < GCS_MIN_BLOCK_SIZE) and (not final or not self.autocommit): # either flush() was called, but we don't have enough to # push, or we split a big upload, and have less left than one # block. If this is the final part, OK to violate those @@ -1818,13 +1818,12 @@ def discard(self): """ if self.location is None: return - uid = re.findall("upload_id=([^&=?]+)", self.location) self.gcsfs.call( "DELETE", - f"{self.fs._location}/upload/storage/v1/b/{quote(self.bucket)}/o", - params={"uploadType": "resumable", "upload_id": uid}, - json_out=True, + self.location, ) + self.location = None + self.closed = True def _simple_upload(self): """One-shot upload, less than 5MB""" diff --git a/gcsfs/retry.py b/gcsfs/retry.py index 9ae823ba..4bb860ba 100644 --- a/gcsfs/retry.py +++ b/gcsfs/retry.py @@ -82,7 +82,8 @@ def validate_response(status, content, path, args=None): r: requests response object path: associated URL path, for error messages """ - if status >= 400: + if status >= 400 and status != 499: + # 499 is special "upload was cancelled" status if args: from .core import quote diff --git a/gcsfs/tests/test_core.py b/gcsfs/tests/test_core.py index 89dd1760..665106a0 100644 --- a/gcsfs/tests/test_core.py +++ b/gcsfs/tests/test_core.py @@ -1424,6 +1424,29 @@ def test_copy_cache_invalidated(gcs): assert gcs.isfile(target_file2) +def test_transaction(gcs): + # https://github.com/fsspec/gcsfs/issues/389 + if not gcs.on_google: + pytest.skip() + try: + with gcs.transaction: + with gcs.open(f"{TEST_BUCKET}/foo", "wb") as f: + f.write(b"This is a test string") + f.discard() + assert not gcs.exists(f"{TEST_BUCKET}/foo") + raise ZeroDivisionError + except ZeroDivisionError: + ... + assert not gcs.exists(f"{TEST_BUCKET}/foo") + + with gcs.transaction: + with gcs.open(f"{TEST_BUCKET}/foo", "wb") as f: + f.write(b"This is a test string") + assert not gcs.exists(f"{TEST_BUCKET}/foo") + + assert gcs.cat(f"{TEST_BUCKET}/foo") == b"This is a test string" + + def test_find_maxdepth(gcs): assert gcs.find(f"{TEST_BUCKET}/nested", maxdepth=None) == [ f"{TEST_BUCKET}/nested/file1",