/pypy/module/_io/interp_fileio.py

https://bitbucket.org/pypy/pypy/ · Python · 455 lines · 376 code · 64 blank · 15 comment · 103 complexity · ddfd41ed4792daca8762731011b23a26 MD5 · raw file

  1. from pypy.interpreter.typedef import TypeDef, interp_attrproperty, GetSetProperty
  2. from pypy.interpreter.gateway import interp2app, unwrap_spec
  3. from pypy.interpreter.error import (
  4. OperationError, oefmt, wrap_oserror, wrap_oserror2)
  5. from rpython.rlib.rarithmetic import r_longlong
  6. from rpython.rlib.rstring import StringBuilder
  7. from os import O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC
  8. import sys, os, stat, errno
  9. from pypy.module._io.interp_iobase import W_RawIOBase, convert_size
  10. def interp_member_w(name, cls, doc=None):
  11. "NOT_RPYTHON: initialization-time only"
  12. def fget(space, obj):
  13. w_value = getattr(obj, name)
  14. if w_value is None:
  15. raise OperationError(space.w_AttributeError, space.wrap(name))
  16. else:
  17. return w_value
  18. def fset(space, obj, w_value):
  19. setattr(obj, name, w_value)
  20. def fdel(space, obj):
  21. w_value = getattr(obj, name)
  22. if w_value is None:
  23. raise OperationError(space.w_AttributeError, space.wrap(name))
  24. setattr(obj, name, None)
  25. return GetSetProperty(fget, fset, fdel, cls=cls, doc=doc)
  26. O_BINARY = getattr(os, "O_BINARY", 0)
  27. O_APPEND = getattr(os, "O_APPEND", 0)
  28. def _bad_mode(space):
  29. raise oefmt(space.w_ValueError,
  30. "Must have exactly one of read/write/append mode")
  31. def decode_mode(space, mode):
  32. flags = 0
  33. rwa = False
  34. readable = False
  35. writable = False
  36. append = False
  37. plus = False
  38. for s in mode:
  39. if s == 'r':
  40. if rwa:
  41. _bad_mode(space)
  42. rwa = True
  43. readable = True
  44. elif s == 'w':
  45. if rwa:
  46. _bad_mode(space)
  47. rwa = True
  48. writable = True
  49. flags |= O_CREAT | O_TRUNC
  50. elif s == 'a':
  51. if rwa:
  52. _bad_mode(space)
  53. rwa = True
  54. writable = True
  55. append = True
  56. flags |= O_APPEND | O_CREAT
  57. elif s == 'b':
  58. pass
  59. elif s == '+':
  60. if plus:
  61. _bad_mode(space)
  62. readable = writable = True
  63. plus = True
  64. else:
  65. raise oefmt(space.w_ValueError, "invalid mode: %s", mode)
  66. if not rwa:
  67. _bad_mode(space)
  68. if readable and writable:
  69. flags |= O_RDWR
  70. elif readable:
  71. flags |= O_RDONLY
  72. else:
  73. flags |= O_WRONLY
  74. flags |= O_BINARY
  75. return readable, writable, append, flags
  76. SMALLCHUNK = 8 * 1024
  77. BIGCHUNK = 512 * 1024
  78. def new_buffersize(fd, currentsize):
  79. try:
  80. st = os.fstat(fd)
  81. end = st.st_size
  82. pos = os.lseek(fd, 0, 1)
  83. except OSError:
  84. pass
  85. else:
  86. # Files claiming a size smaller than SMALLCHUNK may
  87. # actually be streaming pseudo-files. In this case, we
  88. # apply the more aggressive algorithm below.
  89. if end >= SMALLCHUNK and end >= pos:
  90. # Add 1 so if the file were to grow we'd notice.
  91. return currentsize + end - pos + 1
  92. if currentsize > SMALLCHUNK:
  93. # Keep doubling until we reach BIGCHUNK;
  94. # then keep adding BIGCHUNK.
  95. if currentsize <= BIGCHUNK:
  96. return currentsize + currentsize
  97. else:
  98. return currentsize + BIGCHUNK
  99. return currentsize + SMALLCHUNK
  100. class W_FileIO(W_RawIOBase):
  101. def __init__(self, space):
  102. W_RawIOBase.__init__(self, space)
  103. self.fd = -1
  104. self.readable = False
  105. self.writable = False
  106. self.appending = False
  107. self.seekable = -1
  108. self.closefd = True
  109. self.w_name = None
  110. def descr_new(space, w_subtype, __args__):
  111. self = space.allocate_instance(W_FileIO, w_subtype)
  112. W_FileIO.__init__(self, space)
  113. return space.wrap(self)
  114. @unwrap_spec(mode=str, closefd=int)
  115. def descr_init(self, space, w_name, mode='r', closefd=True):
  116. if space.isinstance_w(w_name, space.w_float):
  117. raise oefmt(space.w_TypeError,
  118. "integer argument expected, got float")
  119. fd = -1
  120. try:
  121. fd = space.c_int_w(w_name)
  122. except OperationError as e:
  123. pass
  124. else:
  125. if fd < 0:
  126. raise oefmt(space.w_ValueError, "negative file descriptor")
  127. self.readable, self.writable, self.appending, flags = decode_mode(space, mode)
  128. fd_is_own = False
  129. try:
  130. if fd >= 0:
  131. try:
  132. os.fstat(fd)
  133. except OSError as e:
  134. if e.errno == errno.EBADF:
  135. raise wrap_oserror(space, e)
  136. # else: pass
  137. self.fd = fd
  138. self.closefd = bool(closefd)
  139. else:
  140. self.closefd = True
  141. if not closefd:
  142. raise oefmt(space.w_ValueError,
  143. "Cannot use closefd=False with file name")
  144. from pypy.module.posix.interp_posix import (
  145. dispatch_filename, rposix)
  146. try:
  147. self.fd = dispatch_filename(rposix.open)(
  148. space, w_name, flags, 0666)
  149. except OSError as e:
  150. raise wrap_oserror2(space, e, w_name,
  151. exception_name='w_IOError')
  152. finally:
  153. fd_is_own = True
  154. self._dircheck(space, w_name)
  155. space.setattr(self, space.wrap("name"), w_name)
  156. if self.appending:
  157. # For consistent behaviour, we explicitly seek to the end of file
  158. # (otherwise, it might be done only on the first write()).
  159. try:
  160. os.lseek(self.fd, 0, os.SEEK_END)
  161. except OSError as e:
  162. raise wrap_oserror(space, e, exception_name='w_IOError')
  163. except:
  164. if not fd_is_own:
  165. self.fd = -1
  166. raise
  167. def _mode(self):
  168. if self.appending:
  169. if self.readable:
  170. return 'ab+'
  171. else:
  172. return 'ab'
  173. elif self.readable:
  174. if self.writable:
  175. return 'rb+'
  176. else:
  177. return 'rb'
  178. else:
  179. return 'wb'
  180. def descr_get_mode(self, space):
  181. return space.wrap(self._mode())
  182. def _closed(self, space):
  183. return self.fd < 0
  184. def _check_closed(self, space, message=None):
  185. if message is None:
  186. message = "I/O operation on closed file"
  187. if self.fd < 0:
  188. raise OperationError(space.w_ValueError, space.wrap(message))
  189. def _check_readable(self, space):
  190. if not self.readable:
  191. raise oefmt(space.w_ValueError, "file not open for reading")
  192. def _check_writable(self, space):
  193. if not self.writable:
  194. raise oefmt(space.w_ValueError, "file not open for writing")
  195. def _close(self, space):
  196. if self.fd < 0:
  197. return
  198. fd = self.fd
  199. self.fd = -1
  200. try:
  201. os.close(fd)
  202. except OSError as e:
  203. raise wrap_oserror(space, e,
  204. exception_name='w_IOError')
  205. def close_w(self, space):
  206. try:
  207. W_RawIOBase.close_w(self, space)
  208. except OperationError:
  209. if not self.closefd:
  210. self.fd = -1
  211. raise
  212. self._close(space)
  213. raise
  214. if not self.closefd:
  215. self.fd = -1
  216. return
  217. self._close(space)
  218. def _dircheck(self, space, w_filename):
  219. # On Unix, fopen will succeed for directories.
  220. # In Python, there should be no file objects referring to
  221. # directories, so we need a check.
  222. if self.fd < 0:
  223. return
  224. try:
  225. st = os.fstat(self.fd)
  226. except OSError:
  227. return
  228. if stat.S_ISDIR(st.st_mode):
  229. raise wrap_oserror2(space, OSError(errno.EISDIR, "fstat"),
  230. w_filename, exception_name='w_IOError')
  231. @unwrap_spec(pos=r_longlong, whence=int)
  232. def seek_w(self, space, pos, whence=0):
  233. self._check_closed(space)
  234. try:
  235. pos = os.lseek(self.fd, pos, whence)
  236. except OSError as e:
  237. raise wrap_oserror(space, e,
  238. exception_name='w_IOError')
  239. return space.wrap(pos)
  240. def tell_w(self, space):
  241. self._check_closed(space)
  242. try:
  243. pos = os.lseek(self.fd, 0, 1)
  244. except OSError as e:
  245. raise wrap_oserror(space, e,
  246. exception_name='w_IOError')
  247. return space.wrap(pos)
  248. def readable_w(self, space):
  249. self._check_closed(space)
  250. return space.wrap(self.readable)
  251. def writable_w(self, space):
  252. self._check_closed(space)
  253. return space.wrap(self.writable)
  254. def seekable_w(self, space):
  255. self._check_closed(space)
  256. if self.seekable < 0:
  257. try:
  258. os.lseek(self.fd, 0, os.SEEK_CUR)
  259. except OSError:
  260. self.seekable = 0
  261. else:
  262. self.seekable = 1
  263. return space.newbool(self.seekable == 1)
  264. # ______________________________________________
  265. def fileno_w(self, space):
  266. self._check_closed(space)
  267. return space.wrap(self.fd)
  268. def isatty_w(self, space):
  269. self._check_closed(space)
  270. try:
  271. res = os.isatty(self.fd)
  272. except OSError as e:
  273. raise wrap_oserror(space, e, exception_name='w_IOError')
  274. return space.wrap(res)
  275. def repr_w(self, space):
  276. if self.fd < 0:
  277. return space.wrap("<_io.FileIO [closed]>")
  278. if self.w_name is None:
  279. return space.wrap(
  280. "<_io.FileIO fd=%d mode='%s'>" % (
  281. self.fd, self._mode()))
  282. else:
  283. w_repr = space.repr(self.w_name)
  284. return space.wrap(
  285. "<_io.FileIO name=%s mode='%s'>" % (
  286. space.str_w(w_repr), self._mode()))
  287. # ______________________________________________
  288. def write_w(self, space, w_data):
  289. self._check_closed(space)
  290. self._check_writable(space)
  291. data = space.getarg_w('s*', w_data).as_str()
  292. try:
  293. n = os.write(self.fd, data)
  294. except OSError as e:
  295. if e.errno == errno.EAGAIN:
  296. return space.w_None
  297. raise wrap_oserror(space, e,
  298. exception_name='w_IOError')
  299. return space.wrap(n)
  300. def read_w(self, space, w_size=None):
  301. self._check_closed(space)
  302. self._check_readable(space)
  303. size = convert_size(space, w_size)
  304. if size < 0:
  305. return self.readall_w(space)
  306. try:
  307. s = os.read(self.fd, size)
  308. except OSError as e:
  309. if e.errno == errno.EAGAIN:
  310. return space.w_None
  311. raise wrap_oserror(space, e,
  312. exception_name='w_IOError')
  313. return space.newbytes(s)
  314. def readinto_w(self, space, w_buffer):
  315. self._check_closed(space)
  316. self._check_readable(space)
  317. rwbuffer = space.getarg_w('w*', w_buffer)
  318. length = rwbuffer.getlength()
  319. try:
  320. buf = os.read(self.fd, length)
  321. except OSError as e:
  322. if e.errno == errno.EAGAIN:
  323. return space.w_None
  324. raise wrap_oserror(space, e,
  325. exception_name='w_IOError')
  326. rwbuffer.setslice(0, buf)
  327. return space.wrap(len(buf))
  328. def readall_w(self, space):
  329. self._check_closed(space)
  330. self._check_readable(space)
  331. total = 0
  332. builder = StringBuilder()
  333. while True:
  334. newsize = int(new_buffersize(self.fd, total))
  335. try:
  336. chunk = os.read(self.fd, newsize - total)
  337. except OSError as e:
  338. if e.errno == errno.EINTR:
  339. space.getexecutioncontext().checksignals()
  340. continue
  341. if total > 0:
  342. # return what we've got so far
  343. break
  344. if e.errno == errno.EAGAIN:
  345. return space.w_None
  346. raise wrap_oserror(space, e,
  347. exception_name='w_IOError')
  348. if not chunk:
  349. break
  350. builder.append(chunk)
  351. total += len(chunk)
  352. return space.newbytes(builder.build())
  353. if sys.platform == "win32":
  354. def _truncate(self, size):
  355. from rpython.rlib.streamio import ftruncate_win32
  356. ftruncate_win32(self.fd, size)
  357. else:
  358. def _truncate(self, size):
  359. os.ftruncate(self.fd, size)
  360. def truncate_w(self, space, w_size=None):
  361. self._check_closed(space)
  362. self._check_writable(space)
  363. if space.is_none(w_size):
  364. w_size = self.tell_w(space)
  365. try:
  366. self._truncate(space.r_longlong_w(w_size))
  367. except OSError as e:
  368. raise wrap_oserror(space, e, exception_name='w_IOError')
  369. return w_size
  370. W_FileIO.typedef = TypeDef(
  371. '_io.FileIO', W_RawIOBase.typedef,
  372. __new__ = interp2app(W_FileIO.descr_new.im_func),
  373. __init__ = interp2app(W_FileIO.descr_init),
  374. __repr__ = interp2app(W_FileIO.repr_w),
  375. seek = interp2app(W_FileIO.seek_w),
  376. tell = interp2app(W_FileIO.tell_w),
  377. write = interp2app(W_FileIO.write_w),
  378. read = interp2app(W_FileIO.read_w),
  379. readinto = interp2app(W_FileIO.readinto_w),
  380. readall = interp2app(W_FileIO.readall_w),
  381. truncate = interp2app(W_FileIO.truncate_w),
  382. close = interp2app(W_FileIO.close_w),
  383. readable = interp2app(W_FileIO.readable_w),
  384. writable = interp2app(W_FileIO.writable_w),
  385. seekable = interp2app(W_FileIO.seekable_w),
  386. fileno = interp2app(W_FileIO.fileno_w),
  387. isatty = interp2app(W_FileIO.isatty_w),
  388. name = interp_member_w('w_name', cls=W_FileIO),
  389. closefd = interp_attrproperty('closefd', cls=W_FileIO),
  390. mode = GetSetProperty(W_FileIO.descr_get_mode),
  391. )