/pypy/module/_file/test/test_file_extra.py

https://bitbucket.org/pypy/pypy/ · Python · 669 lines · 541 code · 95 blank · 33 comment · 63 complexity · ed3cafe4e314e7d389649241b30d03a3 MD5 · raw file

  1. import os, random, sys
  2. import rpython.tool.udir
  3. import py
  4. udir = rpython.tool.udir.udir.ensure('test_file_extra', dir=1)
  5. # XXX this file is a random test. It may only fail occasionally
  6. # depending on the details of the random string SAMPLE.
  7. SAMPLE = ''.join([chr(random.randrange(0, 256)) for i in range(12487)])
  8. for extra in ['\r\r', '\r\n', '\n\r', '\n\n']:
  9. for i in range(20):
  10. j = random.randrange(0, len(SAMPLE)+1)
  11. SAMPLE = SAMPLE[:j] + extra + SAMPLE[j:]
  12. if random.random() < 0.1:
  13. SAMPLE += extra # occasionally, also test strings ending in an EOL
  14. def setup_module(mod):
  15. udir.join('sample').write(SAMPLE, 'wb')
  16. class BaseROTests:
  17. sample = SAMPLE
  18. def get_expected_lines(self):
  19. lines = self.sample.split('\n')
  20. for i in range(len(lines)-1):
  21. lines[i] += '\n'
  22. # if self.sample ends exactly in '\n', the split() gives a
  23. # spurious empty line at the end. Fix it:
  24. if lines[-1] == '':
  25. del lines[-1]
  26. return lines
  27. def test_simple_tell(self):
  28. assert self.file.tell() == 0
  29. def test_plain_read(self):
  30. data1 = self.file.read()
  31. assert data1 == self.sample
  32. def test_readline(self):
  33. lines = self.expected_lines
  34. for sampleline in lines:
  35. inputline = self.file.readline()
  36. assert inputline == sampleline
  37. for i in range(5):
  38. inputline = self.file.readline()
  39. assert inputline == ""
  40. def test_readline_max(self):
  41. import random
  42. i = 0
  43. stop = 0
  44. while stop < 5:
  45. max = random.randrange(0, 100)
  46. sampleline = self.sample[i:i+max]
  47. nexteol = sampleline.find('\n')
  48. if nexteol >= 0:
  49. sampleline = sampleline[:nexteol+1]
  50. inputline = self.file.readline(max)
  51. assert inputline == sampleline
  52. i += len(sampleline)
  53. if i == len(self.sample):
  54. stop += 1
  55. def test_iter(self):
  56. inputlines = list(self.file)
  57. assert inputlines == self.expected_lines
  58. def test_isatty(self):
  59. assert not self.file.isatty()
  60. def test_next(self):
  61. lines = self.expected_lines
  62. for sampleline in lines:
  63. inputline = self.file.next()
  64. assert inputline == sampleline
  65. for i in range(5):
  66. raises(StopIteration, self.file.next)
  67. def test_read(self):
  68. import random
  69. i = 0
  70. stop = 0
  71. while stop < 5:
  72. max = random.randrange(0, 100)
  73. samplebuf = self.sample[i:i+max]
  74. inputbuf = self.file.read(max)
  75. assert inputbuf == samplebuf
  76. i += len(samplebuf)
  77. if i == len(self.sample):
  78. stop += 1
  79. def test_readlines(self):
  80. lines = self.file.readlines()
  81. assert lines == self.expected_lines
  82. def test_readlines_max(self):
  83. import random
  84. i = 0
  85. stop = 0
  86. samplelines = self.expected_lines
  87. while stop < 5:
  88. morelines = self.file.readlines(random.randrange(1, 300))
  89. for inputline in morelines:
  90. assert inputline == samplelines[0]
  91. samplelines.pop(0)
  92. if not samplelines:
  93. stop += 1
  94. else:
  95. assert len(morelines) >= 1 # otherwise, this test (and
  96. # real programs) would be prone
  97. # to endless loops
  98. def test_seek(self):
  99. import random
  100. for i in range(100):
  101. position = random.randrange(0, len(self.sample))
  102. self.file.seek(position)
  103. inputchar = self.file.read(1)
  104. assert inputchar == self.sample[position]
  105. for i in range(100):
  106. position = random.randrange(0, len(self.sample))
  107. self.file.seek(position - len(self.sample), 2)
  108. inputchar = self.file.read(1)
  109. assert inputchar == self.sample[position]
  110. prevpos = position + 1
  111. for i in range(100):
  112. position = random.randrange(0, len(self.sample))
  113. self.file.seek(position - prevpos, 1)
  114. inputchar = self.file.read(1)
  115. assert inputchar == self.sample[position]
  116. prevpos = position + 1
  117. def test_tell(self):
  118. import random
  119. for i in range(100):
  120. position = random.randrange(0, len(self.sample)+1)
  121. self.file.seek(position)
  122. told = self.file.tell()
  123. assert told == position
  124. for i in range(100):
  125. position = random.randrange(0, len(self.sample)+1)
  126. self.file.seek(position - len(self.sample), 2)
  127. told = self.file.tell()
  128. assert told == position
  129. prevpos = position
  130. for i in range(100):
  131. position = random.randrange(0, len(self.sample)+1)
  132. self.file.seek(position - prevpos, 1)
  133. told = self.file.tell()
  134. assert told == position
  135. prevpos = position
  136. def test_tell_and_seek_back(self):
  137. import random
  138. i = 0
  139. stop = 0
  140. secondpass = []
  141. while stop < 5:
  142. max = random.randrange(0, 100)
  143. samplebuf = self.sample[i:i+max]
  144. secondpass.append((self.file.tell(), i))
  145. inputbuf = self.file.read(max)
  146. assert inputbuf == samplebuf
  147. i += len(samplebuf)
  148. if i == len(self.sample):
  149. stop += 1
  150. for i in range(100):
  151. saved_position, i = random.choice(secondpass)
  152. max = random.randrange(0, 100)
  153. samplebuf = self.sample[i:i+max]
  154. self.file.seek(saved_position)
  155. inputbuf = self.file.read(max)
  156. assert inputbuf == samplebuf
  157. def test_xreadlines(self):
  158. assert self.file.xreadlines() is self.file
  159. def test_attr(self):
  160. f = self.file
  161. if self.expected_filename is not None:
  162. assert f.name == self.expected_filename
  163. if self.expected_mode is not None:
  164. assert f.mode == self.expected_mode
  165. assert f.closed == False
  166. assert not f.softspace
  167. raises((TypeError, AttributeError), 'f.name = 42')
  168. raises((TypeError, AttributeError), 'f.name = "stuff"')
  169. raises((TypeError, AttributeError), 'f.mode = "r"')
  170. raises((TypeError, AttributeError), 'f.closed = True')
  171. f.softspace = True
  172. assert f.softspace
  173. f.softspace = False
  174. assert not f.softspace
  175. f.close()
  176. assert f.closed == True
  177. def test_repr(self):
  178. import sys
  179. if '__pypy__' not in sys.builtin_module_names and \
  180. sys.version_info < (2, 7, 4):
  181. skip("see cpython issue14161")
  182. assert repr(self.file).startswith(
  183. "<open file %r, mode '%s' at 0x" %
  184. (self.expected_filename, self.expected_mode))
  185. self.file.close()
  186. assert repr(self.file).startswith(
  187. "<closed file %r, mode '%s' at 0x" %
  188. (self.expected_filename, self.expected_mode))
  189. # ____________________________________________________________
  190. #
  191. # Basic 'rb' mode
  192. class AppTestFile(BaseROTests):
  193. expected_filename = str(udir.join('sample'))
  194. expected_mode = 'rb'
  195. extra_args = ()
  196. spaceconfig = {'usemodules': ['binascii', 'time', 'struct']}
  197. def setup_method(self, method):
  198. space = self.space
  199. if hasattr(space, 'gettypeobject'):
  200. from pypy.module._file.interp_file import W_File
  201. w_filetype = space.gettypeobject(W_File.typedef)
  202. else:
  203. w_filetype = file # TinyObjSpace, for "py.test -A"
  204. self.w_file = space.call_function(
  205. w_filetype,
  206. space.wrap(self.expected_filename),
  207. space.wrap(self.expected_mode),
  208. *[space.wrap(a) for a in self.extra_args])
  209. self.w_sample = space.wrap(self.sample)
  210. self.w_expected_filename = space.wrap(self.expected_filename)
  211. self.w_expected_mode = space.wrap(self.expected_mode)
  212. self.w_expected_lines = space.wrap(self.get_expected_lines())
  213. def teardown_method(self, method):
  214. self.space.call_method(self.w_file, 'close')
  215. class AppTestUnbufferedFile(AppTestFile):
  216. extra_args = (0,)
  217. class AppTestLineBufferedFile(AppTestFile):
  218. extra_args = (1,)
  219. class AppTestLargeBufferFile(AppTestFile):
  220. extra_args = (len(SAMPLE),)
  221. # ____________________________________________________________
  222. #
  223. # Check on top of CPython
  224. class TestWithCPython(BaseROTests):
  225. expected_filename = str(udir.join('sample'))
  226. expected_mode = 'rb'
  227. def setup_method(self, method):
  228. self.file = open(self.expected_filename, self.expected_mode)
  229. self.expected_lines = self.get_expected_lines()
  230. def teardown_method(self, method):
  231. self.file.close()
  232. # ____________________________________________________________
  233. #
  234. # Files built with fdopen()
  235. class AppTestFdOpen(BaseROTests):
  236. expected_filename = '<fdopen>'
  237. expected_mode = 'rb'
  238. extra_args = ()
  239. spaceconfig = {'usemodules': ['binascii', 'time', 'struct']}
  240. def setup_method(self, method):
  241. space = self.space
  242. O_BINARY = getattr(os, "O_BINARY", 0)
  243. if hasattr(space, 'gettypeobject'):
  244. from pypy.module._file.interp_file import W_File
  245. w_filetype = space.gettypeobject(W_File.typedef)
  246. else:
  247. w_filetype = os # TinyObjSpace, for "py.test -A"
  248. # (CPython has no file.fdopen, only os.fdopen)
  249. fd = os.open(AppTestFile.expected_filename, os.O_RDONLY | O_BINARY)
  250. self.w_file = space.call_method(
  251. w_filetype,
  252. 'fdopen',
  253. space.wrap(fd),
  254. space.wrap(self.expected_mode),
  255. *[space.wrap(a) for a in self.extra_args])
  256. self.w_fd = space.wrap(fd)
  257. self.w_sample = space.wrap(self.sample)
  258. self.w_expected_filename = space.wrap(self.expected_filename)
  259. self.w_expected_mode = space.wrap(self.expected_mode)
  260. self.w_expected_lines = space.wrap(self.get_expected_lines())
  261. def teardown_method(self, method):
  262. self.space.call_method(self.w_file, 'close')
  263. def test_fileno(self):
  264. assert self.file.fileno() == self.fd
  265. class AppTestUnbufferedFdOpen(AppTestFdOpen):
  266. extra_args = (0,)
  267. class AppTestLineBufferedFdOpen(AppTestFdOpen):
  268. extra_args = (1,)
  269. class AppTestLargeBufferFdOpen(AppTestFdOpen):
  270. extra_args = (len(SAMPLE),)
  271. # ____________________________________________________________
  272. #
  273. # Universal newlines
  274. class AppTestUniversalNewlines(AppTestFile):
  275. expected_mode = 'rU'
  276. sample = '\n'.join((SAMPLE+'X').splitlines(False))[:-1]
  277. # ^^^ if SAMPLE ends in any end-of-line character combination, read()ing
  278. # it in 'rU' mode gives a final '\n', but splitlines(False) doesn't give
  279. # a final empty line. Adding and removing an extra 'X' avoids this
  280. # corner case.
  281. def test_seek(self):
  282. skip("does not apply in universal newlines mode")
  283. test_tell = test_seek
  284. class AppTestUnbufferedUniversal(AppTestUniversalNewlines):
  285. extra_args = (0,)
  286. class AppTestLineBufferedUniversal(AppTestUniversalNewlines):
  287. extra_args = (1,)
  288. class AppTestLargeBufferUniversal(AppTestUniversalNewlines):
  289. extra_args = (len(SAMPLE),)
  290. # ____________________________________________________________
  291. #
  292. # A few extra tests
  293. class AppTestAFewExtra:
  294. spaceconfig = {'usemodules': ['_socket', 'array', 'binascii', 'time',
  295. 'struct']}
  296. def setup_method(self, method):
  297. fn = str(udir.join('temptestfile'))
  298. self.w_temptestfile = self.space.wrap(fn)
  299. def test_case_readonly(self):
  300. fn = self.temptestfile
  301. f = file(fn, 'w')
  302. assert f.name == fn
  303. assert f.mode == 'w'
  304. assert f.closed == False
  305. assert f.encoding == None # Fix when we find out what this is
  306. raises((TypeError, AttributeError), setattr, f, 'name', 42)
  307. def test_readlines(self):
  308. fn = self.temptestfile
  309. lines = ['line%d\n' % i for i in range(1000)]
  310. f = file(fn, 'w')
  311. f.writelines(lines)
  312. f.close()
  313. assert open(fn, 'r').readlines() == lines
  314. assert file(fn, 'r').readlines() == lines
  315. somelines = file(fn, 'r').readlines(2000)
  316. assert len(somelines) > 200
  317. assert somelines == lines[:len(somelines)]
  318. def test_writelines(self):
  319. import array
  320. import sys
  321. fn = self.temptestfile
  322. with file(fn, 'w') as f:
  323. f.writelines(['abc'])
  324. f.writelines([u'def'])
  325. exc = raises(TypeError, f.writelines, [array.array('c', 'ghi')])
  326. assert str(exc.value) == "writelines() argument must be a sequence of strings"
  327. exc = raises(TypeError, f.writelines, [memoryview('jkl')])
  328. assert str(exc.value) == "writelines() argument must be a sequence of strings"
  329. assert open(fn, 'r').readlines() == ['abcdef']
  330. with file(fn, 'wb') as f:
  331. f.writelines(['abc'])
  332. f.writelines([u'def'])
  333. f.writelines([array.array('c', 'ghi')])
  334. exc = raises(TypeError, f.writelines, [memoryview('jkl')])
  335. assert str(exc.value) == "writelines() argument must be a sequence of strings"
  336. out = open(fn, 'rb').readlines()[0]
  337. if sys.byteorder == 'big':
  338. assert out[0:7] == 'abc\x00\x00\x00d'
  339. else:
  340. assert out[0:5] == 'abcd\x00'
  341. assert out[-3:] == 'ghi'
  342. with file(fn, 'wb') as f:
  343. exc = raises(TypeError, f.writelines, ['abc', memoryview('def')])
  344. assert str(exc.value) == "writelines() argument must be a sequence of strings"
  345. assert open(fn, 'rb').readlines() == []
  346. def test_nasty_writelines(self):
  347. # The stream lock should be released between writes
  348. fn = self.temptestfile
  349. f = file(fn, 'w')
  350. def nasty():
  351. for i in range(5):
  352. if i == 3:
  353. # should not raise because of acquired lock
  354. f.close()
  355. yield str(i)
  356. exc = raises(ValueError, f.writelines, nasty())
  357. assert exc.value.message == "I/O operation on closed file"
  358. f.close()
  359. def test_rw_bin(self):
  360. import random
  361. flags = 'w+b'
  362. checkflags = 'rb'
  363. eolstyles = [('', ''), ('\n', '\n'),
  364. ('\r', '\r'), ('\r\n', '\r\n')]
  365. fn = self.temptestfile
  366. f = file(fn, flags)
  367. expected = ''
  368. pos = 0
  369. for i in range(5000):
  370. x = random.random()
  371. if x < 0.4:
  372. l = int(x*100)
  373. buf = f.read(l)
  374. assert buf == expected[pos:pos+l]
  375. pos += len(buf)
  376. elif x < 0.75:
  377. writeeol, expecteol = random.choice(eolstyles)
  378. x = str(x)
  379. buf1 = x+writeeol
  380. buf2 = x+expecteol
  381. f.write(buf1)
  382. expected = expected[:pos] + buf2 + expected[pos+len(buf2):]
  383. pos += len(buf2)
  384. elif x < 0.80:
  385. pos = random.randint(0, len(expected))
  386. f.seek(pos)
  387. elif x < 0.85:
  388. pos = random.randint(0, len(expected))
  389. f.seek(pos - len(expected), 2)
  390. elif x < 0.90:
  391. currentpos = pos
  392. pos = random.randint(0, len(expected))
  393. f.seek(pos - currentpos, 1)
  394. elif x < 0.95:
  395. assert f.tell() == pos
  396. else:
  397. f.flush()
  398. g = open(fn, checkflags)
  399. buf = g.read()
  400. g.close()
  401. assert buf == expected
  402. f.close()
  403. g = open(fn, checkflags)
  404. buf = g.read()
  405. g.close()
  406. assert buf == expected
  407. def test_rw(self):
  408. fn = self.temptestfile
  409. f = file(fn, 'w+')
  410. f.write('hello\nworld\n')
  411. f.seek(0)
  412. assert f.read() == 'hello\nworld\n'
  413. f.close()
  414. def test_r_universal(self):
  415. fn = self.temptestfile
  416. f = open(fn, 'wb')
  417. f.write('hello\r\nworld\r\n')
  418. f.close()
  419. f = file(fn, 'rU')
  420. assert f.read() == 'hello\nworld\n'
  421. f.close()
  422. def test_flush(self):
  423. import os
  424. fn = self.temptestfile
  425. f = file(fn, 'w', 0)
  426. f.write('x')
  427. assert os.stat(fn).st_size == 1
  428. f.close()
  429. f = file(fn, 'wb', 1)
  430. f.write('x')
  431. assert os.stat(fn).st_size == 0
  432. f.write('\n')
  433. assert os.stat(fn).st_size == 2
  434. f.write('x')
  435. assert os.stat(fn).st_size == 2
  436. f.flush()
  437. assert os.stat(fn).st_size == 3
  438. f.close()
  439. assert os.stat(fn).st_size == 3
  440. f = file(fn, 'wb', 1000)
  441. f.write('x')
  442. assert os.stat(fn).st_size == 0
  443. f.write('\n')
  444. assert os.stat(fn).st_size == 0
  445. f.write('x')
  446. assert os.stat(fn).st_size == 0
  447. f.flush()
  448. assert os.stat(fn).st_size == 3
  449. f.close()
  450. assert os.stat(fn).st_size == 3
  451. def test_isatty(self):
  452. try:
  453. f = file('/dev/tty')
  454. except IOError:
  455. pass
  456. else:
  457. assert f.isatty()
  458. f.close()
  459. def test_truncate(self):
  460. fn = self.temptestfile
  461. f = open(fn, 'w+b')
  462. f.write('hello world')
  463. f.seek(7)
  464. f.truncate()
  465. f.seek(0)
  466. data = f.read()
  467. assert data == 'hello w'
  468. f.seek(0, 2)
  469. assert f.tell() == 7
  470. f.seek(0)
  471. f.truncate(3)
  472. data = f.read(123)
  473. assert data == 'hel'
  474. f.close()
  475. import errno, sys
  476. f = open(fn)
  477. exc = raises(IOError, f.truncate, 3)
  478. assert str(exc.value) == "File not open for writing"
  479. f.close()
  480. def test_readinto(self):
  481. from array import array
  482. a = array('c')
  483. a.fromstring('0123456789')
  484. fn = self.temptestfile
  485. f = open(fn, 'w+b')
  486. f.write('foobar')
  487. f.seek(0)
  488. n = f.readinto(a)
  489. f.close()
  490. assert n == 6
  491. assert len(a) == 10
  492. assert a.tostring() == 'foobar6789'
  493. @py.test.mark.skipif("os.name != 'posix'")
  494. def test_readinto_error(self):
  495. import _socket, posix, array
  496. s = _socket.socket()
  497. buff = array.array("c", "X" * 65)
  498. fh = posix.fdopen(posix.dup(s.fileno()), 'rb')
  499. # "Transport endpoint is not connected"
  500. raises(IOError, fh.readinto, buff)
  501. fh.close()
  502. s.close()
  503. def test_weakref(self):
  504. """Files are weakrefable."""
  505. import weakref
  506. fn = self.temptestfile
  507. f = open(fn, 'wb')
  508. ref = weakref.ref(f)
  509. ref().write('hello')
  510. assert f.tell() == 5
  511. f.close()
  512. def test_weakref_dies_before_file_closes(self):
  513. # Hard-to-reproduce failure (which should now be fixed).
  514. # I think that this is how lib-python/modified-2.5.2/test_file.py
  515. # sometimes failed on a Boehm pypy-c.
  516. import weakref, gc
  517. fn = self.temptestfile
  518. f = open(fn, 'wb')
  519. f.close()
  520. f = open(fn, 'rb')
  521. ref = weakref.ref(f)
  522. attempts = range(10)
  523. del f
  524. for i in attempts:
  525. f1 = ref()
  526. if f1 is None:
  527. break # all gone
  528. assert not f1.closed # if still reachable, should be still open
  529. del f1
  530. gc.collect()
  531. def test_ValueError(self):
  532. fn = self.temptestfile
  533. f = open(fn, 'wb')
  534. f.close()
  535. raises(ValueError, f.fileno)
  536. raises(ValueError, f.flush)
  537. raises(ValueError, f.isatty)
  538. raises(ValueError, f.next)
  539. raises(ValueError, f.read)
  540. raises(ValueError, f.readline)
  541. raises(ValueError, f.readlines)
  542. raises(ValueError, f.seek, 0)
  543. raises(ValueError, f.tell)
  544. raises(ValueError, f.truncate)
  545. raises(ValueError, f.write, "")
  546. raises(ValueError, f.writelines, [])
  547. raises(ValueError, iter, f)
  548. raises(ValueError, f.xreadlines)
  549. raises(ValueError, f.__enter__)
  550. f.close() # accepted as a no-op
  551. def test_docstrings(self):
  552. assert file.closed.__doc__ == 'True if the file is closed'
  553. def test_repr_unicode_filename(self):
  554. with open(unicode(self.temptestfile), 'w') as f:
  555. assert repr(f).startswith("<open file " +
  556. repr(unicode(self.temptestfile)))
  557. def test_repr_escape_filename(self):
  558. import sys
  559. fname = 'xx\rxx\nxx\'xx"xx' if sys.platform != "win32" else "xx'xx"
  560. fname = self.temptestfile + fname
  561. with open(fname, 'w') as f:
  562. assert repr(f).startswith("<open file %r, mode 'w' at" % fname)
  563. @py.test.mark.skipif("os.name != 'posix'")
  564. def test_EAGAIN(self):
  565. import _socket, posix
  566. s1, s2 = _socket.socketpair()
  567. s2.setblocking(False)
  568. s1.send("hello")
  569. f2 = posix.fdopen(posix.dup(s2.fileno()), 'rb', 0)
  570. data = f2.read(12)
  571. assert data == "hello"
  572. f2.close()
  573. s2.close()
  574. s1.close()