diff options
author | Carey Metcalfe <carey@cmetcalfe.ca> | 2022-05-03 08:18:18 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-03 21:18:18 +0900 |
commit | 78e70be3318bc2ca57dac188061ed35017a0867c (patch) | |
tree | 25e43cb96acdf6eea461548410087dab85b6ebb2 /Lib/test/test_tempfile.py | |
parent | 52dc9c3066bcdc67a7a45d41cf158ecb1434d5f3 (diff) | |
download | cpython-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.py | 48 |
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) |