@@ -179,6 +179,23 @@ class PyMockFileIO(MockFileIO, pyio.BytesIO):
179179 pass
180180
181181
182+ class MockUnseekableIO :
183+ def seekable (self ):
184+ return False
185+
186+ def seek (self , * args ):
187+ raise self .UnsupportedOperation ("not seekable" )
188+
189+ def tell (self , * args ):
190+ raise self .UnsupportedOperation ("not seekable" )
191+
192+ class CMockUnseekableIO (MockUnseekableIO , io .BytesIO ):
193+ UnsupportedOperation = io .UnsupportedOperation
194+
195+ class PyMockUnseekableIO (MockUnseekableIO , pyio .BytesIO ):
196+ UnsupportedOperation = pyio .UnsupportedOperation
197+
198+
182199class MockNonBlockWriterIO :
183200
184201 def __init__ (self ):
@@ -304,16 +321,26 @@ def large_file_ops(self, f):
304321
305322 def test_invalid_operations (self ):
306323 # Try writing on a file opened in read mode and vice-versa.
324+ exc = self .UnsupportedOperation
307325 for mode in ("w" , "wb" ):
308326 with self .open (support .TESTFN , mode ) as fp :
309- self .assertRaises (IOError , fp .read )
310- self .assertRaises (IOError , fp .readline )
327+ self .assertRaises (exc , fp .read )
328+ self .assertRaises (exc , fp .readline )
329+ with self .open (support .TESTFN , "wb" , buffering = 0 ) as fp :
330+ self .assertRaises (exc , fp .read )
331+ self .assertRaises (exc , fp .readline )
332+ with self .open (support .TESTFN , "rb" , buffering = 0 ) as fp :
333+ self .assertRaises (exc , fp .write , b"blah" )
334+ self .assertRaises (exc , fp .writelines , [b"blah\n " ])
311335 with self .open (support .TESTFN , "rb" ) as fp :
312- self .assertRaises (IOError , fp .write , b"blah" )
313- self .assertRaises (IOError , fp .writelines , [b"blah\n " ])
336+ self .assertRaises (exc , fp .write , b"blah" )
337+ self .assertRaises (exc , fp .writelines , [b"blah\n " ])
314338 with self .open (support .TESTFN , "r" ) as fp :
315- self .assertRaises (IOError , fp .write , "blah" )
316- self .assertRaises (IOError , fp .writelines , ["blah\n " ])
339+ self .assertRaises (exc , fp .write , "blah" )
340+ self .assertRaises (exc , fp .writelines , ["blah\n " ])
341+ # Non-zero seeking from current or end pos
342+ self .assertRaises (exc , fp .seek , 1 , self .SEEK_CUR )
343+ self .assertRaises (exc , fp .seek , - 1 , self .SEEK_END )
317344
318345 def test_raw_file_io (self ):
319346 with self .open (support .TESTFN , "wb" , buffering = 0 ) as f :
@@ -670,6 +697,11 @@ def test_multi_close(self):
670697 b .close ()
671698 self .assertRaises (ValueError , b .flush )
672699
700+ def test_unseekable (self ):
701+ bufio = self .tp (self .MockUnseekableIO (b"A" * 10 ))
702+ self .assertRaises (self .UnsupportedOperation , bufio .tell )
703+ self .assertRaises (self .UnsupportedOperation , bufio .seek , 0 )
704+
673705
674706class BufferedReaderTest (unittest .TestCase , CommonBufferedTests ):
675707 read_mode = "rb"
@@ -1433,6 +1465,9 @@ def test_misbehaved_io(self):
14331465 BufferedReaderTest .test_misbehaved_io (self )
14341466 BufferedWriterTest .test_misbehaved_io (self )
14351467
1468+ # You can't construct a BufferedRandom over a non-seekable stream.
1469+ test_unseekable = None
1470+
14361471class CBufferedRandomTest (BufferedRandomTest ):
14371472 tp = io .BufferedRandom
14381473
@@ -2177,6 +2212,11 @@ def test_multi_close(self):
21772212 txt .close ()
21782213 self .assertRaises (ValueError , txt .flush )
21792214
2215+ def test_unseekable (self ):
2216+ txt = self .TextIOWrapper (self .MockUnseekableIO (self .testdata ))
2217+ self .assertRaises (self .UnsupportedOperation , txt .tell )
2218+ self .assertRaises (self .UnsupportedOperation , txt .seek , 0 )
2219+
21802220class CTextIOWrapperTest (TextIOWrapperTest ):
21812221
21822222 def test_initialization (self ):
@@ -2550,7 +2590,7 @@ def test_main():
25502590 # Put the namespaces of the IO module we are testing and some useful mock
25512591 # classes in the __dict__ of each test.
25522592 mocks = (MockRawIO , MisbehavedRawIO , MockFileIO , CloseFailureIO ,
2553- MockNonBlockWriterIO )
2593+ MockNonBlockWriterIO , MockUnseekableIO )
25542594 all_members = io .__all__ + ["IncrementalNewlineDecoder" ]
25552595 c_io_ns = {name : getattr (io , name ) for name in all_members }
25562596 py_io_ns = {name : getattr (pyio , name ) for name in all_members }
0 commit comments