From bd465b032b00474f339acbc97907d41772e8424f Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Sun, 23 Oct 2022 11:23:03 +0000 Subject: [PATCH] S3: Ensure we can copy deleted keys (#5589) --- moto/s3/models.py | 7 ++++++- tests/test_s3/test_s3_file_handles.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/moto/s3/models.py b/moto/s3/models.py index 6f219e8be..2d3d1b368 100644 --- a/moto/s3/models.py +++ b/moto/s3/models.py @@ -305,7 +305,12 @@ class FakeKey(BaseModel, ManagedState): # https://docs.python.org/3/library/pickle.html#handling-stateful-objects def __getstate__(self): state = self.__dict__.copy() - state["value"] = self.value + try: + state["value"] = self.value + except ValueError: + # Buffer is already closed, so we can't reach the data + # Only happens if the key was deleted + state["value"] = "" del state["_value_buffer"] del state["lock"] return state diff --git a/tests/test_s3/test_s3_file_handles.py b/tests/test_s3/test_s3_file_handles.py index 97500ad6b..984cbbdac 100644 --- a/tests/test_s3/test_s3_file_handles.py +++ b/tests/test_s3/test_s3_file_handles.py @@ -1,3 +1,4 @@ +import copy import gc import warnings from functools import wraps @@ -186,3 +187,21 @@ class TestS3FileHandleClosures(TestCase): self.s3.delete_object( bucket_name="my-bucket", key_name="my-key", version_id=key._version_id ) + + +def test_verify_key_can_be_copied_after_disposing(): + # https://github.com/spulec/moto/issues/5588 + # Multithreaded bug where: + # - User: calls list_object_versions + # - Moto creates a list of all keys + # - User: deletes a key + # - Moto iterates over the previously created list, that contains a now-deleted key, and creates a copy of it + # + # This test verifies the copy-operation succeeds, it will just not have any data + key = s3model.FakeKey(name="test", bucket_name="bucket", value="sth") + assert not key._value_buffer.closed + key.dispose() + assert key._value_buffer.closed + + copied_key = copy.copy(key) + copied_key.value.should.equal(b"")