• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import sys
2import os
3import unittest
4from array import array
5from weakref import proxy
6
7import io
8import _pyio as pyio
9
10from test.support import TESTFN
11from test import support
12from collections import UserList
13
14class AutoFileTests:
15    # file tests for which a test file is automatically set up
16
17    def setUp(self):
18        self.f = self.open(TESTFN, 'wb')
19
20    def tearDown(self):
21        if self.f:
22            self.f.close()
23        support.unlink(TESTFN)
24
25    def testWeakRefs(self):
26        # verify weak references
27        p = proxy(self.f)
28        p.write(b'teststring')
29        self.assertEqual(self.f.tell(), p.tell())
30        self.f.close()
31        self.f = None
32        self.assertRaises(ReferenceError, getattr, p, 'tell')
33
34    def testAttributes(self):
35        # verify expected attributes exist
36        f = self.f
37        f.name     # merely shouldn't blow up
38        f.mode     # ditto
39        f.closed   # ditto
40
41    def testReadinto(self):
42        # verify readinto
43        self.f.write(b'12')
44        self.f.close()
45        a = array('b', b'x'*10)
46        self.f = self.open(TESTFN, 'rb')
47        n = self.f.readinto(a)
48        self.assertEqual(b'12', a.tobytes()[:n])
49
50    def testReadinto_text(self):
51        # verify readinto refuses text files
52        a = array('b', b'x'*10)
53        self.f.close()
54        self.f = self.open(TESTFN, 'r')
55        if hasattr(self.f, "readinto"):
56            self.assertRaises(TypeError, self.f.readinto, a)
57
58    def testWritelinesUserList(self):
59        # verify writelines with instance sequence
60        l = UserList([b'1', b'2'])
61        self.f.writelines(l)
62        self.f.close()
63        self.f = self.open(TESTFN, 'rb')
64        buf = self.f.read()
65        self.assertEqual(buf, b'12')
66
67    def testWritelinesIntegers(self):
68        # verify writelines with integers
69        self.assertRaises(TypeError, self.f.writelines, [1, 2, 3])
70
71    def testWritelinesIntegersUserList(self):
72        # verify writelines with integers in UserList
73        l = UserList([1,2,3])
74        self.assertRaises(TypeError, self.f.writelines, l)
75
76    def testWritelinesNonString(self):
77        # verify writelines with non-string object
78        class NonString:
79            pass
80
81        self.assertRaises(TypeError, self.f.writelines,
82                          [NonString(), NonString()])
83
84    def testErrors(self):
85        f = self.f
86        self.assertEqual(f.name, TESTFN)
87        self.assertFalse(f.isatty())
88        self.assertFalse(f.closed)
89
90        if hasattr(f, "readinto"):
91            self.assertRaises((OSError, TypeError), f.readinto, "")
92        f.close()
93        self.assertTrue(f.closed)
94
95    def testMethods(self):
96        methods = [('fileno', ()),
97                   ('flush', ()),
98                   ('isatty', ()),
99                   ('__next__', ()),
100                   ('read', ()),
101                   ('write', (b"",)),
102                   ('readline', ()),
103                   ('readlines', ()),
104                   ('seek', (0,)),
105                   ('tell', ()),
106                   ('write', (b"",)),
107                   ('writelines', ([],)),
108                   ('__iter__', ()),
109                   ]
110        methods.append(('truncate', ()))
111
112        # __exit__ should close the file
113        self.f.__exit__(None, None, None)
114        self.assertTrue(self.f.closed)
115
116        for methodname, args in methods:
117            method = getattr(self.f, methodname)
118            # should raise on closed file
119            self.assertRaises(ValueError, method, *args)
120
121        # file is closed, __exit__ shouldn't do anything
122        self.assertEqual(self.f.__exit__(None, None, None), None)
123        # it must also return None if an exception was given
124        try:
125            1/0
126        except:
127            self.assertEqual(self.f.__exit__(*sys.exc_info()), None)
128
129    def testReadWhenWriting(self):
130        self.assertRaises(OSError, self.f.read)
131
132class CAutoFileTests(AutoFileTests, unittest.TestCase):
133    open = io.open
134
135class PyAutoFileTests(AutoFileTests, unittest.TestCase):
136    open = staticmethod(pyio.open)
137
138
139class OtherFileTests:
140
141    def tearDown(self):
142        support.unlink(TESTFN)
143
144    def testModeStrings(self):
145        # check invalid mode strings
146        self.open(TESTFN, 'wb').close()
147        for mode in ("", "aU", "wU+", "U+", "+U", "rU+"):
148            try:
149                f = self.open(TESTFN, mode)
150            except ValueError:
151                pass
152            else:
153                f.close()
154                self.fail('%r is an invalid file mode' % mode)
155
156    def testBadModeArgument(self):
157        # verify that we get a sensible error message for bad mode argument
158        bad_mode = "qwerty"
159        try:
160            f = self.open(TESTFN, bad_mode)
161        except ValueError as msg:
162            if msg.args[0] != 0:
163                s = str(msg)
164                if TESTFN in s or bad_mode not in s:
165                    self.fail("bad error message for invalid mode: %s" % s)
166            # if msg.args[0] == 0, we're probably on Windows where there may be
167            # no obvious way to discover why open() failed.
168        else:
169            f.close()
170            self.fail("no error for invalid mode: %s" % bad_mode)
171
172    def _checkBufferSize(self, s):
173        try:
174            f = self.open(TESTFN, 'wb', s)
175            f.write(str(s).encode("ascii"))
176            f.close()
177            f.close()
178            f = self.open(TESTFN, 'rb', s)
179            d = int(f.read().decode("ascii"))
180            f.close()
181            f.close()
182        except OSError as msg:
183            self.fail('error setting buffer size %d: %s' % (s, str(msg)))
184        self.assertEqual(d, s)
185
186    def testSetBufferSize(self):
187        # make sure that explicitly setting the buffer size doesn't cause
188        # misbehaviour especially with repeated close() calls
189        for s in (-1, 0, 512):
190            with support.check_no_warnings(self,
191                                           message='line buffering',
192                                           category=RuntimeWarning):
193                self._checkBufferSize(s)
194
195        # test that attempts to use line buffering in binary mode cause
196        # a warning
197        with self.assertWarnsRegex(RuntimeWarning, 'line buffering'):
198            self._checkBufferSize(1)
199
200    def testTruncateOnWindows(self):
201        # SF bug <http://www.python.org/sf/801631>
202        # "file.truncate fault on windows"
203
204        f = self.open(TESTFN, 'wb')
205
206        try:
207            f.write(b'12345678901')   # 11 bytes
208            f.close()
209
210            f = self.open(TESTFN,'rb+')
211            data = f.read(5)
212            if data != b'12345':
213                self.fail("Read on file opened for update failed %r" % data)
214            if f.tell() != 5:
215                self.fail("File pos after read wrong %d" % f.tell())
216
217            f.truncate()
218            if f.tell() != 5:
219                self.fail("File pos after ftruncate wrong %d" % f.tell())
220
221            f.close()
222            size = os.path.getsize(TESTFN)
223            if size != 5:
224                self.fail("File size after ftruncate wrong %d" % size)
225        finally:
226            f.close()
227
228    def testIteration(self):
229        # Test the complex interaction when mixing file-iteration and the
230        # various read* methods.
231        dataoffset = 16384
232        filler = b"ham\n"
233        assert not dataoffset % len(filler), \
234            "dataoffset must be multiple of len(filler)"
235        nchunks = dataoffset // len(filler)
236        testlines = [
237            b"spam, spam and eggs\n",
238            b"eggs, spam, ham and spam\n",
239            b"saussages, spam, spam and eggs\n",
240            b"spam, ham, spam and eggs\n",
241            b"spam, spam, spam, spam, spam, ham, spam\n",
242            b"wonderful spaaaaaam.\n"
243        ]
244        methods = [("readline", ()), ("read", ()), ("readlines", ()),
245                   ("readinto", (array("b", b" "*100),))]
246
247        # Prepare the testfile
248        bag = self.open(TESTFN, "wb")
249        bag.write(filler * nchunks)
250        bag.writelines(testlines)
251        bag.close()
252        # Test for appropriate errors mixing read* and iteration
253        for methodname, args in methods:
254            f = self.open(TESTFN, 'rb')
255            self.assertEqual(next(f), filler)
256            meth = getattr(f, methodname)
257            meth(*args)  # This simply shouldn't fail
258            f.close()
259
260        # Test to see if harmless (by accident) mixing of read* and
261        # iteration still works. This depends on the size of the internal
262        # iteration buffer (currently 8192,) but we can test it in a
263        # flexible manner.  Each line in the bag o' ham is 4 bytes
264        # ("h", "a", "m", "\n"), so 4096 lines of that should get us
265        # exactly on the buffer boundary for any power-of-2 buffersize
266        # between 4 and 16384 (inclusive).
267        f = self.open(TESTFN, 'rb')
268        for i in range(nchunks):
269            next(f)
270        testline = testlines.pop(0)
271        try:
272            line = f.readline()
273        except ValueError:
274            self.fail("readline() after next() with supposedly empty "
275                        "iteration-buffer failed anyway")
276        if line != testline:
277            self.fail("readline() after next() with empty buffer "
278                        "failed. Got %r, expected %r" % (line, testline))
279        testline = testlines.pop(0)
280        buf = array("b", b"\x00" * len(testline))
281        try:
282            f.readinto(buf)
283        except ValueError:
284            self.fail("readinto() after next() with supposedly empty "
285                        "iteration-buffer failed anyway")
286        line = buf.tobytes()
287        if line != testline:
288            self.fail("readinto() after next() with empty buffer "
289                        "failed. Got %r, expected %r" % (line, testline))
290
291        testline = testlines.pop(0)
292        try:
293            line = f.read(len(testline))
294        except ValueError:
295            self.fail("read() after next() with supposedly empty "
296                        "iteration-buffer failed anyway")
297        if line != testline:
298            self.fail("read() after next() with empty buffer "
299                        "failed. Got %r, expected %r" % (line, testline))
300        try:
301            lines = f.readlines()
302        except ValueError:
303            self.fail("readlines() after next() with supposedly empty "
304                        "iteration-buffer failed anyway")
305        if lines != testlines:
306            self.fail("readlines() after next() with empty buffer "
307                        "failed. Got %r, expected %r" % (line, testline))
308        f.close()
309
310        # Reading after iteration hit EOF shouldn't hurt either
311        f = self.open(TESTFN, 'rb')
312        try:
313            for line in f:
314                pass
315            try:
316                f.readline()
317                f.readinto(buf)
318                f.read()
319                f.readlines()
320            except ValueError:
321                self.fail("read* failed after next() consumed file")
322        finally:
323            f.close()
324
325class COtherFileTests(OtherFileTests, unittest.TestCase):
326    open = io.open
327
328class PyOtherFileTests(OtherFileTests, unittest.TestCase):
329    open = staticmethod(pyio.open)
330
331
332if __name__ == '__main__':
333    unittest.main()
334