|
@@ -80,3 +80,52 @@ class FileResponseTests(SimpleTestCase):
|
|
|
response['Content-Disposition'],
|
|
|
"attachment; filename*=utf-8''%E7%A5%9D%E6%82%A8%E5%B9%B3%E5%AE%89.odt"
|
|
|
)
|
|
|
+
|
|
|
+ def test_file_to_stream_closes_response(self):
|
|
|
+ # Closing file_to_stream calls FileResponse.close(), even when
|
|
|
+ # file-like object doesn't have a close() method.
|
|
|
+ class FileLike:
|
|
|
+ def read(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ class FileLikeWithClose(FileLike):
|
|
|
+ def __init__(self):
|
|
|
+ self.closed = False
|
|
|
+
|
|
|
+ def close(self):
|
|
|
+ self.closed = True
|
|
|
+
|
|
|
+ for filelike_cls in (FileLike, FileLikeWithClose):
|
|
|
+ with self.subTest(filelike_cls=filelike_cls.__name__):
|
|
|
+ filelike = filelike_cls()
|
|
|
+ response = FileResponse(filelike)
|
|
|
+ self.assertFalse(response.closed)
|
|
|
+ # Object with close() is added to the list of closable.
|
|
|
+ if hasattr(filelike, 'closed'):
|
|
|
+ self.assertEqual(response._closable_objects, [filelike])
|
|
|
+ else:
|
|
|
+ self.assertEqual(response._closable_objects, [])
|
|
|
+ file_to_stream = response.file_to_stream
|
|
|
+ file_to_stream.close()
|
|
|
+ if hasattr(filelike, 'closed'):
|
|
|
+ self.assertTrue(filelike.closed)
|
|
|
+ self.assertTrue(response.closed)
|
|
|
+
|
|
|
+ def test_file_to_stream_closes_response_on_error(self):
|
|
|
+ # Closing file_to_stream calls FileResponse.close(), even when
|
|
|
+ # closing file-like raises exceptions.
|
|
|
+ class FileLikeWithRaisingClose:
|
|
|
+ def read(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def close(self):
|
|
|
+ raise RuntimeError()
|
|
|
+
|
|
|
+ filelike = FileLikeWithRaisingClose()
|
|
|
+ response = FileResponse(filelike)
|
|
|
+ self.assertFalse(response.closed)
|
|
|
+ self.assertEqual(response._closable_objects, [filelike])
|
|
|
+ file_to_stream = response.file_to_stream
|
|
|
+ with self.assertRaises(RuntimeError):
|
|
|
+ file_to_stream.close()
|
|
|
+ self.assertTrue(response.closed)
|