blob: 1146a37323c9bfe229ce973f67450faae82cf4bc [file] [log] [blame]
Martin v. Löwisf90ae202002-06-11 06:22:31 +00001import sys
Fred Drake2ec80fa2000-10-23 16:59:35 +00002import os
Thomas Wouters73e5a5b2006-06-08 15:35:45 +00003import unittest
Neal Norwitz62f5a9d2002-04-01 00:09:00 +00004from array import array
Raymond Hettingercb87bc82004-05-31 00:35:52 +00005from weakref import proxy
Fred Drake2ec80fa2000-10-23 16:59:35 +00006
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +00007import io
8import _pyio as pyio
9
Serhiy Storchaka462c1f02021-09-08 18:08:57 +030010from test.support import gc_collect
Hai Shia089d212020-07-06 17:15:08 +080011from test.support.os_helper import TESTFN
12from test.support import os_helper
13from test.support import warnings_helper
Raymond Hettinger53dbe392008-02-12 20:03:09 +000014from collections import UserList
Marc-André Lemburgfa44d792000-08-25 22:37:31 +000015
Ezio Melotti3a03d2e2013-02-15 19:17:53 +020016class AutoFileTests:
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000017 # file tests for which a test file is automatically set up
Raymond Hettingercb87bc82004-05-31 00:35:52 +000018
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000019 def setUp(self):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +000020 self.f = self.open(TESTFN, 'wb')
Tim Peters015dd822003-05-04 04:16:52 +000021
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000022 def tearDown(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +000023 if self.f:
24 self.f.close()
Hai Shia089d212020-07-06 17:15:08 +080025 os_helper.unlink(TESTFN)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000026
27 def testWeakRefs(self):
28 # verify weak references
29 p = proxy(self.f)
Guido van Rossume22905a2007-08-27 23:09:25 +000030 p.write(b'teststring')
Ezio Melottib3aedd42010-11-20 19:04:17 +000031 self.assertEqual(self.f.tell(), p.tell())
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000032 self.f.close()
33 self.f = None
Serhiy Storchaka462c1f02021-09-08 18:08:57 +030034 gc_collect() # For PyPy or other GCs.
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000035 self.assertRaises(ReferenceError, getattr, p, 'tell')
36
37 def testAttributes(self):
38 # verify expected attributes exist
39 f = self.f
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000040 f.name # merely shouldn't blow up
41 f.mode # ditto
42 f.closed # ditto
43
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000044 def testReadinto(self):
45 # verify readinto
Guido van Rossume22905a2007-08-27 23:09:25 +000046 self.f.write(b'12')
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000047 self.f.close()
Guido van Rossum7165cb12007-07-10 06:54:34 +000048 a = array('b', b'x'*10)
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +000049 self.f = self.open(TESTFN, 'rb')
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000050 n = self.f.readinto(a)
Ezio Melottib3aedd42010-11-20 19:04:17 +000051 self.assertEqual(b'12', a.tobytes()[:n])
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000052
53 def testReadinto_text(self):
54 # verify readinto refuses text files
Guido van Rossum7165cb12007-07-10 06:54:34 +000055 a = array('b', b'x'*10)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000056 self.f.close()
Inada Naoki55f31be2021-04-01 11:23:03 +090057 self.f = self.open(TESTFN, encoding="utf-8")
Guido van Rossum7165cb12007-07-10 06:54:34 +000058 if hasattr(self.f, "readinto"):
59 self.assertRaises(TypeError, self.f.readinto, a)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000060
61 def testWritelinesUserList(self):
62 # verify writelines with instance sequence
Guido van Rossum7165cb12007-07-10 06:54:34 +000063 l = UserList([b'1', b'2'])
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000064 self.f.writelines(l)
65 self.f.close()
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +000066 self.f = self.open(TESTFN, 'rb')
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000067 buf = self.f.read()
Ezio Melottib3aedd42010-11-20 19:04:17 +000068 self.assertEqual(buf, b'12')
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000069
70 def testWritelinesIntegers(self):
71 # verify writelines with integers
72 self.assertRaises(TypeError, self.f.writelines, [1, 2, 3])
73
74 def testWritelinesIntegersUserList(self):
75 # verify writelines with integers in UserList
76 l = UserList([1,2,3])
77 self.assertRaises(TypeError, self.f.writelines, l)
78
79 def testWritelinesNonString(self):
80 # verify writelines with non-string object
Thomas Wouters0e3f5912006-08-11 14:57:12 +000081 class NonString:
82 pass
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000083
Thomas Wouters0e3f5912006-08-11 14:57:12 +000084 self.assertRaises(TypeError, self.f.writelines,
85 [NonString(), NonString()])
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000086
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000087 def testErrors(self):
88 f = self.f
Ezio Melottib3aedd42010-11-20 19:04:17 +000089 self.assertEqual(f.name, TESTFN)
Serhiy Storchaka0dcd80a2015-08-02 15:17:49 +030090 self.assertFalse(f.isatty())
91 self.assertFalse(f.closed)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000092
Guido van Rossum7165cb12007-07-10 06:54:34 +000093 if hasattr(f, "readinto"):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +020094 self.assertRaises((OSError, TypeError), f.readinto, "")
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000095 f.close()
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000096 self.assertTrue(f.closed)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +000097
98 def testMethods(self):
Guido van Rossum4b5386f2007-07-10 09:12:49 +000099 methods = [('fileno', ()),
100 ('flush', ()),
101 ('isatty', ()),
102 ('__next__', ()),
103 ('read', ()),
104 ('write', (b"",)),
105 ('readline', ()),
106 ('readlines', ()),
107 ('seek', (0,)),
108 ('tell', ()),
109 ('write', (b"",)),
110 ('writelines', ([],)),
111 ('__iter__', ()),
112 ]
Antoine Pitrou6103ab12009-10-24 20:11:21 +0000113 methods.append(('truncate', ()))
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000114
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000115 # __exit__ should close the file
116 self.f.__exit__(None, None, None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000117 self.assertTrue(self.f.closed)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000118
Guido van Rossum4b5386f2007-07-10 09:12:49 +0000119 for methodname, args in methods:
120 method = getattr(self.f, methodname)
121 # should raise on closed file
122 self.assertRaises(ValueError, method, *args)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000123
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000124 # file is closed, __exit__ shouldn't do anything
Ezio Melottib3aedd42010-11-20 19:04:17 +0000125 self.assertEqual(self.f.__exit__(None, None, None), None)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000126 # it must also return None if an exception was given
127 try:
128 1/0
129 except:
Ezio Melottib3aedd42010-11-20 19:04:17 +0000130 self.assertEqual(self.f.__exit__(*sys.exc_info()), None)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000131
Skip Montanaro80072cb2008-12-23 03:51:14 +0000132 def testReadWhenWriting(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200133 self.assertRaises(OSError, self.f.read)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000134
Ezio Melotti3a03d2e2013-02-15 19:17:53 +0200135class CAutoFileTests(AutoFileTests, unittest.TestCase):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000136 open = io.open
137
Ezio Melotti3a03d2e2013-02-15 19:17:53 +0200138class PyAutoFileTests(AutoFileTests, unittest.TestCase):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000139 open = staticmethod(pyio.open)
140
141
Ezio Melotti3a03d2e2013-02-15 19:17:53 +0200142class OtherFileTests:
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000143
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300144 def tearDown(self):
Hai Shia089d212020-07-06 17:15:08 +0800145 os_helper.unlink(TESTFN)
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300146
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000147 def testModeStrings(self):
148 # check invalid mode strings
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300149 self.open(TESTFN, 'wb').close()
Robert Collinsc94a1dc2015-07-26 06:43:13 +1200150 for mode in ("", "aU", "wU+", "U+", "+U", "rU+"):
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000151 try:
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000152 f = self.open(TESTFN, mode)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000153 except ValueError:
154 pass
155 else:
156 f.close()
157 self.fail('%r is an invalid file mode' % mode)
158
Miss Islington (bot)4e0147e2021-08-10 07:31:00 -0700159 def testStdin(self):
160 if sys.platform == 'osf1V5':
161 # This causes the interpreter to exit on OSF1 v5.1.
162 self.skipTest(
163 ' sys.stdin.seek(-1) may crash the interpreter on OSF1.'
164 ' Test manually.')
165
166 if not sys.stdin.isatty():
167 # Issue 14853: stdin becomes seekable when redirected to a file
168 self.skipTest('stdin must be a TTY in this test')
169
170 with self.assertRaises((IOError, ValueError)):
171 sys.stdin.seek(-1)
172 with self.assertRaises((IOError, ValueError)):
173 sys.stdin.truncate()
174
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000175 def testBadModeArgument(self):
176 # verify that we get a sensible error message for bad mode argument
177 bad_mode = "qwerty"
Tim Peterscffcfed2006-02-14 17:41:18 +0000178 try:
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000179 f = self.open(TESTFN, bad_mode)
Guido van Rossumb940e112007-01-10 16:19:56 +0000180 except ValueError as msg:
Brett Cannonca477b22007-03-21 22:26:20 +0000181 if msg.args[0] != 0:
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000182 s = str(msg)
Ezio Melotti7fb4da72010-03-18 12:29:13 +0000183 if TESTFN in s or bad_mode not in s:
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000184 self.fail("bad error message for invalid mode: %s" % s)
Georg Brandl50da60c2008-01-06 21:38:54 +0000185 # if msg.args[0] == 0, we're probably on Windows where there may be
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000186 # no obvious way to discover why open() failed.
187 else:
188 f.close()
189 self.fail("no error for invalid mode: %s" % bad_mode)
190
Alexey Izbysheva2670562018-10-20 03:22:31 +0300191 def _checkBufferSize(self, s):
192 try:
193 f = self.open(TESTFN, 'wb', s)
194 f.write(str(s).encode("ascii"))
195 f.close()
196 f.close()
197 f = self.open(TESTFN, 'rb', s)
198 d = int(f.read().decode("ascii"))
199 f.close()
200 f.close()
201 except OSError as msg:
202 self.fail('error setting buffer size %d: %s' % (s, str(msg)))
203 self.assertEqual(d, s)
204
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000205 def testSetBufferSize(self):
206 # make sure that explicitly setting the buffer size doesn't cause
207 # misbehaviour especially with repeated close() calls
Alexey Izbysheva2670562018-10-20 03:22:31 +0300208 for s in (-1, 0, 512):
Hai Shia089d212020-07-06 17:15:08 +0800209 with warnings_helper.check_no_warnings(self,
Alexey Izbysheva2670562018-10-20 03:22:31 +0300210 message='line buffering',
211 category=RuntimeWarning):
212 self._checkBufferSize(s)
213
214 # test that attempts to use line buffering in binary mode cause
215 # a warning
216 with self.assertWarnsRegex(RuntimeWarning, 'line buffering'):
217 self._checkBufferSize(1)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000218
219 def testTruncateOnWindows(self):
Guido van Rossum79b79ee2007-10-25 23:21:03 +0000220 # SF bug <http://www.python.org/sf/801631>
221 # "file.truncate fault on windows"
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000222
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000223 f = self.open(TESTFN, 'wb')
Guido van Rossum79b79ee2007-10-25 23:21:03 +0000224
225 try:
Guido van Rossum7165cb12007-07-10 06:54:34 +0000226 f.write(b'12345678901') # 11 bytes
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000227 f.close()
228
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000229 f = self.open(TESTFN,'rb+')
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000230 data = f.read(5)
Guido van Rossum7165cb12007-07-10 06:54:34 +0000231 if data != b'12345':
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000232 self.fail("Read on file opened for update failed %r" % data)
233 if f.tell() != 5:
234 self.fail("File pos after read wrong %d" % f.tell())
235
236 f.truncate()
237 if f.tell() != 5:
238 self.fail("File pos after ftruncate wrong %d" % f.tell())
239
240 f.close()
241 size = os.path.getsize(TESTFN)
242 if size != 5:
243 self.fail("File size after ftruncate wrong %d" % size)
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000244 finally:
Guido van Rossum79b79ee2007-10-25 23:21:03 +0000245 f.close()
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000246
247 def testIteration(self):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000248 # Test the complex interaction when mixing file-iteration and the
Guido van Rossum7165cb12007-07-10 06:54:34 +0000249 # various read* methods.
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000250 dataoffset = 16384
Guido van Rossume22905a2007-08-27 23:09:25 +0000251 filler = b"ham\n"
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000252 assert not dataoffset % len(filler), \
253 "dataoffset must be multiple of len(filler)"
254 nchunks = dataoffset // len(filler)
255 testlines = [
Guido van Rossum7165cb12007-07-10 06:54:34 +0000256 b"spam, spam and eggs\n",
257 b"eggs, spam, ham and spam\n",
258 b"saussages, spam, spam and eggs\n",
259 b"spam, ham, spam and eggs\n",
260 b"spam, spam, spam, spam, spam, ham, spam\n",
261 b"wonderful spaaaaaam.\n"
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000262 ]
263 methods = [("readline", ()), ("read", ()), ("readlines", ()),
Guido van Rossum7165cb12007-07-10 06:54:34 +0000264 ("readinto", (array("b", b" "*100),))]
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000265
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300266 # Prepare the testfile
267 bag = self.open(TESTFN, "wb")
268 bag.write(filler * nchunks)
269 bag.writelines(testlines)
270 bag.close()
271 # Test for appropriate errors mixing read* and iteration
272 for methodname, args in methods:
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000273 f = self.open(TESTFN, 'rb')
Bradley Laney6b490b52018-07-10 05:46:44 -0400274 self.assertEqual(next(f), filler)
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300275 meth = getattr(f, methodname)
276 meth(*args) # This simply shouldn't fail
Antoine Pitroua6e95022010-10-30 14:22:43 +0000277 f.close()
278
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300279 # Test to see if harmless (by accident) mixing of read* and
280 # iteration still works. This depends on the size of the internal
281 # iteration buffer (currently 8192,) but we can test it in a
282 # flexible manner. Each line in the bag o' ham is 4 bytes
283 # ("h", "a", "m", "\n"), so 4096 lines of that should get us
284 # exactly on the buffer boundary for any power-of-2 buffersize
285 # between 4 and 16384 (inclusive).
286 f = self.open(TESTFN, 'rb')
287 for i in range(nchunks):
288 next(f)
289 testline = testlines.pop(0)
290 try:
291 line = f.readline()
292 except ValueError:
293 self.fail("readline() after next() with supposedly empty "
294 "iteration-buffer failed anyway")
295 if line != testline:
296 self.fail("readline() after next() with empty buffer "
297 "failed. Got %r, expected %r" % (line, testline))
298 testline = testlines.pop(0)
299 buf = array("b", b"\x00" * len(testline))
300 try:
301 f.readinto(buf)
302 except ValueError:
303 self.fail("readinto() after next() with supposedly empty "
304 "iteration-buffer failed anyway")
305 line = buf.tobytes()
306 if line != testline:
307 self.fail("readinto() after next() with empty buffer "
308 "failed. Got %r, expected %r" % (line, testline))
309
310 testline = testlines.pop(0)
311 try:
312 line = f.read(len(testline))
313 except ValueError:
314 self.fail("read() after next() with supposedly empty "
315 "iteration-buffer failed anyway")
316 if line != testline:
317 self.fail("read() after next() with empty buffer "
318 "failed. Got %r, expected %r" % (line, testline))
319 try:
320 lines = f.readlines()
321 except ValueError:
322 self.fail("readlines() after next() with supposedly empty "
323 "iteration-buffer failed anyway")
324 if lines != testlines:
325 self.fail("readlines() after next() with empty buffer "
326 "failed. Got %r, expected %r" % (line, testline))
327 f.close()
328
329 # Reading after iteration hit EOF shouldn't hurt either
330 f = self.open(TESTFN, 'rb')
331 try:
332 for line in f:
333 pass
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000334 try:
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300335 f.readline()
336 f.readinto(buf)
337 f.read()
338 f.readlines()
339 except ValueError:
340 self.fail("read* failed after next() consumed file")
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000341 finally:
Serhiy Storchakac2745d22018-06-05 19:55:41 +0300342 f.close()
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000343
Ezio Melotti3a03d2e2013-02-15 19:17:53 +0200344class COtherFileTests(OtherFileTests, unittest.TestCase):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000345 open = io.open
346
Ezio Melotti3a03d2e2013-02-15 19:17:53 +0200347class PyOtherFileTests(OtherFileTests, unittest.TestCase):
Benjamin Peterson4fa88fa2009-03-04 00:14:51 +0000348 open = staticmethod(pyio.open)
349
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000350
Thomas Wouters73e5a5b2006-06-08 15:35:45 +0000351if __name__ == '__main__':
Ezio Melotti3a03d2e2013-02-15 19:17:53 +0200352 unittest.main()