diff --git a/flask_annex/s3.py b/flask_annex/s3.py index e833455..01a7758 100644 --- a/flask_annex/s3.py +++ b/flask_annex/s3.py @@ -40,11 +40,16 @@ def delete(self, key): self._client.delete_object(Bucket=self._bucket_name, Key=key) def delete_many(self, keys): + # casting to tuple because keys might be iterable + objects = tuple({'Key': key} for key in keys) + if not objects: + # this is not just an optimization, boto fails if the array + # is empty + return + self._client.delete_objects( Bucket=self._bucket_name, - Delete={ - 'Objects': tuple({'Key': key} for key in keys), - }, + Delete={'Objects': objects}, ) def get_file(self, key, out_file): diff --git a/tests/test_s3.py b/tests/test_s3.py index 67e5a66..348ddca 100644 --- a/tests/test_s3.py +++ b/tests/test_s3.py @@ -2,6 +2,7 @@ from io import BytesIO import json +from mock import Mock import pytest from flask_annex import Annex @@ -107,6 +108,14 @@ def test_get_upload_info_max_content_length(self, app, client): def assert_app_config_content_length_range(self, conditions): assert get_condition(conditions, 'content-length-range') == [0, 100] + def test_delete_many_empty_list(self, annex, monkeypatch): + mock = Mock() + monkeypatch.setattr(annex._client, 'delete_objects', mock) + + annex.delete_many(tuple()) + + mock.assert_not_called() + class TestS3AnnexFromEnv(TestS3Annex): @pytest.fixture diff --git a/tox.ini b/tox.ini index 15789b6..101a817 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,7 @@ usedevelop = True deps = flake8 + mock pytest pytest-cov