summaryrefslogtreecommitdiff
path: root/Lib/test/test_tempfile.py
diff options
context:
space:
mode:
authorCarey Metcalfe <carey@cmetcalfe.ca>2022-05-03 08:18:18 -0400
committerGitHub <noreply@github.com>2022-05-03 21:18:18 +0900
commit78e70be3318bc2ca57dac188061ed35017a0867c (patch)
tree25e43cb96acdf6eea461548410087dab85b6ebb2 /Lib/test/test_tempfile.py
parent52dc9c3066bcdc67a7a45d41cf158ecb1434d5f3 (diff)
downloadcpython-git-78e70be3318bc2ca57dac188061ed35017a0867c.tar.gz
gh-70363: Implement `io.IOBase` interface for `SpooledTemporaryFile` (GH-29560)
Since the underlying file-like objects (either `io.BytesIO`, or a true file object) all implement the `io.IOBase` interface, the `SpooledTemporaryFile` should as well. Additionally, since the underlying file object will either be an instance of an `io.BufferedIOBase` (for binary mode) or an `io.TextIOBase` (for text mode), methods for these classes were also implemented. In every case, the required methods and properties are simply delegated to the underlying file object. Co-authored-by: Gary Fernie <Gary.Fernie@skyscanner.net> Co-authored-by: Inada Naoki <songofacandy@gmail.com>
Diffstat (limited to 'Lib/test/test_tempfile.py')
-rw-r--r--Lib/test/test_tempfile.py48
1 files changed, 48 insertions, 0 deletions
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index a6847189dc..07a54028ec 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -1061,6 +1061,30 @@ class TestSpooledTemporaryFile(BaseTestCase):
f = self.do_create(max_size=100, pre="a", suf=".txt")
self.assertFalse(f._rolled)
+ def test_is_iobase(self):
+ # SpooledTemporaryFile should implement io.IOBase
+ self.assertIsInstance(self.do_create(), io.IOBase)
+
+ def test_iobase_interface(self):
+ # SpooledTemporaryFile should implement the io.IOBase interface.
+ # Ensure it has all the required methods and properties.
+ iobase_attrs = {
+ # From IOBase
+ 'fileno', 'seek', 'truncate', 'close', 'closed', '__enter__',
+ '__exit__', 'flush', 'isatty', '__iter__', '__next__', 'readable',
+ 'readline', 'readlines', 'seekable', 'tell', 'writable',
+ 'writelines',
+ # From BufferedIOBase (binary mode) and TextIOBase (text mode)
+ 'detach', 'read', 'read1', 'write', 'readinto', 'readinto1',
+ 'encoding', 'errors', 'newlines',
+ }
+ spooledtempfile_attrs = set(dir(tempfile.SpooledTemporaryFile))
+ missing_attrs = iobase_attrs - spooledtempfile_attrs
+ self.assertFalse(
+ missing_attrs,
+ 'SpooledTemporaryFile missing attributes from IOBase/BufferedIOBase/TextIOBase'
+ )
+
def test_del_on_close(self):
# A SpooledTemporaryFile is deleted when closed
dir = tempfile.mkdtemp()
@@ -1076,6 +1100,30 @@ class TestSpooledTemporaryFile(BaseTestCase):
finally:
os.rmdir(dir)
+ def test_del_unrolled_file(self):
+ # The unrolled SpooledTemporaryFile should raise a ResourceWarning
+ # when deleted since the file was not explicitly closed.
+ f = self.do_create(max_size=10)
+ f.write(b'foo')
+ self.assertEqual(f.name, None) # Unrolled so no filename/fd
+ with self.assertWarns(ResourceWarning):
+ f.__del__()
+
+ def test_del_rolled_file(self):
+ # The rolled file should be deleted when the SpooledTemporaryFile
+ # object is deleted. This should raise a ResourceWarning since the file
+ # was not explicitly closed.
+ f = self.do_create(max_size=2)
+ f.write(b'foo')
+ name = f.name # This is a fd on posix+cygwin, a filename everywhere else
+ self.assertTrue(os.path.exists(name))
+ with self.assertWarns(ResourceWarning):
+ f.__del__()
+ self.assertFalse(
+ os.path.exists(name),
+ "Rolled SpooledTemporaryFile (name=%s) exists after delete" % name
+ )
+
def test_rewrite_small(self):
# A SpooledTemporaryFile can be written to multiple within the max_size
f = self.do_create(max_size=30)