diff --git a/CHANGES.rst b/CHANGES.rst index 1f9676fbb..788b2b32c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,8 @@ Unreleased :issue:`1739` - Fix missing local variables in interactive debugger console. :issue:`1746` +- Fix passing file-like objects like ``io.BytesIO`` to + ``FileStorage.save``. :issue:`1733` Version 1.0.0 diff --git a/src/werkzeug/datastructures.py b/src/werkzeug/datastructures.py index 1cda034fc..480d73aec 100644 --- a/src/werkzeug/datastructures.py +++ b/src/werkzeug/datastructures.py @@ -3058,7 +3058,9 @@ def save(self, dst, buffer_size=16384): from shutil import copyfileobj close_dst = False - dst = fspath(dst) + + if hasattr(dst, "__fspath__"): + dst = fspath(dst) if isinstance(dst, string_types): dst = open(dst, "wb") diff --git a/tests/test_datastructures.py b/tests/test_datastructures.py index a64fa9e1b..56f170ba6 100644 --- a/tests/test_datastructures.py +++ b/tests/test_datastructures.py @@ -1243,6 +1243,20 @@ def test_save_to_pathlib_dst(self, tmp_path): storage.save(dst) assert dst.read_text() == "test" + def test_save_to_bytes_io(self): + storage = self.storage_class(io.BytesIO(b"one\ntwo")) + dst = io.BytesIO() + storage.save(dst) + assert dst.getvalue() == b"one\ntwo" + + def test_save_to_file(self, tmp_path): + path = tmp_path / "file.data" + storage = self.storage_class(io.BytesIO(b"one\ntwo")) + with path.open("wb") as dst: + storage.save(dst) + with path.open("rb") as src: + assert src.read() == b"one\ntwo" + @pytest.mark.parametrize("ranges", ([(0, 1), (-5, None)], [(5, None)])) def test_range_to_header(ranges):