PageRenderTime 48ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/module/_io/interp_iobase.py

https://bitbucket.org/pypy/pypy/
Python | 394 lines | 296 code | 68 blank | 30 comment | 76 complexity | b686aef02eb1413976786ee072fc1c88 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from errno import EINTR
  2. from pypy.interpreter.baseobjspace import W_Root
  3. from pypy.interpreter.error import OperationError, oefmt
  4. from pypy.interpreter.typedef import (
  5. TypeDef, GetSetProperty, generic_new_descr, descr_get_dict, descr_set_dict,
  6. make_weakref_descr)
  7. from pypy.interpreter.gateway import interp2app
  8. from rpython.rlib.rstring import StringBuilder
  9. from rpython.rlib import rweakref, rweaklist
  10. DEFAULT_BUFFER_SIZE = 8192
  11. def convert_size(space, w_size):
  12. if space.is_none(w_size):
  13. return -1
  14. else:
  15. return space.int_w(w_size)
  16. def trap_eintr(space, error):
  17. # Return True if an EnvironmentError with errno == EINTR is set
  18. if not error.match(space, space.w_EnvironmentError):
  19. return False
  20. try:
  21. w_value = error.get_w_value(space)
  22. w_errno = space.getattr(w_value, space.wrap("errno"))
  23. return space.eq_w(w_errno, space.wrap(EINTR))
  24. except OperationError:
  25. return False
  26. def unsupported(space, message):
  27. w_exc = space.getattr(space.getbuiltinmodule('_io'),
  28. space.wrap('UnsupportedOperation'))
  29. return OperationError(w_exc, space.wrap(message))
  30. # May be called with any object
  31. def check_readable_w(space, w_obj):
  32. if not space.is_true(space.call_method(w_obj, 'readable')):
  33. raise oefmt(space.w_IOError, "file or stream is not readable")
  34. # May be called with any object
  35. def check_writable_w(space, w_obj):
  36. if not space.is_true(space.call_method(w_obj, 'writable')):
  37. raise oefmt(space.w_IOError, "file or stream is not writable")
  38. # May be called with any object
  39. def check_seekable_w(space, w_obj):
  40. if not space.is_true(space.call_method(w_obj, 'seekable')):
  41. raise oefmt(space.w_IOError, "file or stream is not seekable")
  42. class W_IOBase(W_Root):
  43. def __init__(self, space, add_to_autoflusher=True):
  44. # XXX: IOBase thinks it has to maintain its own internal state in
  45. # `__IOBase_closed` and call flush() by itself, but it is redundant
  46. # with whatever behaviour a non-trivial derived class will implement.
  47. self.space = space
  48. self.w_dict = space.newdict()
  49. self.__IOBase_closed = False
  50. if add_to_autoflusher:
  51. get_autoflusher(space).add(self)
  52. if self.needs_to_finalize:
  53. self.register_finalizer(space)
  54. def getdict(self, space):
  55. return self.w_dict
  56. def _closed(self, space):
  57. # This gets the derived attribute, which is *not* __IOBase_closed
  58. # in most cases!
  59. w_closed = space.findattr(self, space.wrap('closed'))
  60. if w_closed is not None and space.is_true(w_closed):
  61. return True
  62. return False
  63. def _finalize_(self):
  64. space = self.space
  65. w_closed = space.findattr(self, space.wrap('closed'))
  66. try:
  67. # If `closed` doesn't exist or can't be evaluated as bool, then
  68. # the object is probably in an unusable state, so ignore.
  69. if w_closed is not None and not space.is_true(w_closed):
  70. space.call_method(self, "close")
  71. except OperationError:
  72. # Silencing I/O errors is bad, but printing spurious tracebacks is
  73. # equally as bad, and potentially more frequent (because of
  74. # shutdown issues).
  75. pass
  76. needs_to_finalize = True
  77. def _CLOSED(self):
  78. # Use this macro whenever you want to check the internal `closed`
  79. # status of the IOBase object rather than the virtual `closed`
  80. # attribute as returned by whatever subclass.
  81. return self.__IOBase_closed
  82. def _unsupportedoperation(self, space, message):
  83. raise unsupported(space, message)
  84. def _check_closed(self, space, message=None):
  85. if message is None:
  86. message = "I/O operation on closed file"
  87. if self._closed(space):
  88. raise OperationError(
  89. space.w_ValueError, space.wrap(message))
  90. def check_closed_w(self, space):
  91. self._check_closed(space)
  92. def closed_get_w(self, space):
  93. return space.newbool(self.__IOBase_closed)
  94. def close_w(self, space):
  95. if self._CLOSED():
  96. return
  97. try:
  98. space.call_method(self, "flush")
  99. finally:
  100. self.__IOBase_closed = True
  101. def flush_w(self, space):
  102. if self._CLOSED():
  103. raise oefmt(space.w_ValueError, "I/O operation on closed file")
  104. def seek_w(self, space, w_offset, w_whence=None):
  105. self._unsupportedoperation(space, "seek")
  106. def tell_w(self, space):
  107. return space.call_method(self, "seek", space.wrap(0), space.wrap(1))
  108. def truncate_w(self, space, w_size=None):
  109. self._unsupportedoperation(space, "truncate")
  110. def fileno_w(self, space):
  111. self._unsupportedoperation(space, "fileno")
  112. def enter_w(self, space):
  113. self._check_closed(space)
  114. return space.wrap(self)
  115. def exit_w(self, space, __args__):
  116. space.call_method(self, "close")
  117. def iter_w(self, space):
  118. self._check_closed(space)
  119. return space.wrap(self)
  120. def next_w(self, space):
  121. w_line = space.call_method(self, "readline")
  122. if space.len_w(w_line) == 0:
  123. raise OperationError(space.w_StopIteration, space.w_None)
  124. return w_line
  125. def isatty_w(self, space):
  126. self._check_closed(space)
  127. return space.w_False
  128. def readable_w(self, space):
  129. return space.w_False
  130. def writable_w(self, space):
  131. return space.w_False
  132. def seekable_w(self, space):
  133. return space.w_False
  134. # ______________________________________________________________
  135. def readline_w(self, space, w_limit=None):
  136. # For backwards compatibility, a (slowish) readline().
  137. limit = convert_size(space, w_limit)
  138. has_peek = space.findattr(self, space.wrap("peek"))
  139. builder = StringBuilder()
  140. size = 0
  141. while limit < 0 or size < limit:
  142. nreadahead = 1
  143. if has_peek:
  144. try:
  145. w_readahead = space.call_method(self, "peek", space.wrap(1))
  146. except OperationError as e:
  147. if trap_eintr(space, e):
  148. continue
  149. raise
  150. if not space.isinstance_w(w_readahead, space.w_str):
  151. raise oefmt(space.w_IOError,
  152. "peek() should have returned a bytes object, "
  153. "not '%T'", w_readahead)
  154. length = space.len_w(w_readahead)
  155. if length > 0:
  156. n = 0
  157. buf = space.bytes_w(w_readahead)
  158. if limit >= 0:
  159. while True:
  160. if n >= length or n >= limit:
  161. break
  162. n += 1
  163. if buf[n-1] == '\n':
  164. break
  165. else:
  166. while True:
  167. if n >= length:
  168. break
  169. n += 1
  170. if buf[n-1] == '\n':
  171. break
  172. nreadahead = n
  173. try:
  174. w_read = space.call_method(self, "read", space.wrap(nreadahead))
  175. except OperationError as e:
  176. if trap_eintr(space, e):
  177. continue
  178. raise
  179. if not space.isinstance_w(w_read, space.w_str):
  180. raise oefmt(space.w_IOError,
  181. "peek() should have returned a bytes object, not "
  182. "'%T'", w_read)
  183. read = space.bytes_w(w_read)
  184. if not read:
  185. break
  186. size += len(read)
  187. builder.append(read)
  188. if read[-1] == '\n':
  189. break
  190. return space.newbytes(builder.build())
  191. def readlines_w(self, space, w_hint=None):
  192. hint = convert_size(space, w_hint)
  193. if hint <= 0:
  194. return space.newlist(space.unpackiterable(self))
  195. lines_w = []
  196. length = 0
  197. while True:
  198. w_line = space.call_method(self, "readline")
  199. line_length = space.len_w(w_line)
  200. if line_length == 0: # done
  201. break
  202. lines_w.append(w_line)
  203. length += line_length
  204. if length > hint:
  205. break
  206. return space.newlist(lines_w)
  207. def writelines_w(self, space, w_lines):
  208. self._check_closed(space)
  209. w_iterator = space.iter(w_lines)
  210. while True:
  211. try:
  212. w_line = space.next(w_iterator)
  213. except OperationError as e:
  214. if not e.match(space, space.w_StopIteration):
  215. raise
  216. break # done
  217. while True:
  218. try:
  219. space.call_method(self, "write", w_line)
  220. except OperationError as e:
  221. if trap_eintr(space, e):
  222. continue
  223. raise
  224. else:
  225. break
  226. W_IOBase.typedef = TypeDef(
  227. '_io._IOBase',
  228. __new__ = generic_new_descr(W_IOBase),
  229. __enter__ = interp2app(W_IOBase.enter_w),
  230. __exit__ = interp2app(W_IOBase.exit_w),
  231. __iter__ = interp2app(W_IOBase.iter_w),
  232. next = interp2app(W_IOBase.next_w),
  233. close = interp2app(W_IOBase.close_w),
  234. flush = interp2app(W_IOBase.flush_w),
  235. seek = interp2app(W_IOBase.seek_w),
  236. tell = interp2app(W_IOBase.tell_w),
  237. truncate = interp2app(W_IOBase.truncate_w),
  238. fileno = interp2app(W_IOBase.fileno_w),
  239. isatty = interp2app(W_IOBase.isatty_w),
  240. readable = interp2app(W_IOBase.readable_w),
  241. writable = interp2app(W_IOBase.writable_w),
  242. seekable = interp2app(W_IOBase.seekable_w),
  243. _checkReadable = interp2app(check_readable_w),
  244. _checkWritable = interp2app(check_writable_w),
  245. _checkSeekable = interp2app(check_seekable_w),
  246. _checkClosed = interp2app(W_IOBase.check_closed_w),
  247. closed = GetSetProperty(W_IOBase.closed_get_w),
  248. __dict__ = GetSetProperty(descr_get_dict, descr_set_dict, cls=W_IOBase),
  249. __weakref__ = make_weakref_descr(W_IOBase),
  250. readline = interp2app(W_IOBase.readline_w),
  251. readlines = interp2app(W_IOBase.readlines_w),
  252. writelines = interp2app(W_IOBase.writelines_w),
  253. )
  254. class W_RawIOBase(W_IOBase):
  255. # ________________________________________________________________
  256. # Abstract read methods, based on readinto()
  257. def read_w(self, space, w_size=None):
  258. size = convert_size(space, w_size)
  259. if size < 0:
  260. return space.call_method(self, "readall")
  261. w_buffer = space.call_function(space.w_bytearray, w_size)
  262. w_length = space.call_method(self, "readinto", w_buffer)
  263. if space.is_w(w_length, space.w_None):
  264. return w_length
  265. space.delslice(w_buffer, w_length, space.len(w_buffer))
  266. return space.str(w_buffer)
  267. def readall_w(self, space):
  268. builder = StringBuilder()
  269. while True:
  270. try:
  271. w_data = space.call_method(self, "read",
  272. space.wrap(DEFAULT_BUFFER_SIZE))
  273. except OperationError as e:
  274. if trap_eintr(space, e):
  275. continue
  276. raise
  277. if space.is_w(w_data, space.w_None):
  278. if not builder.getlength():
  279. return w_data
  280. break
  281. if not space.isinstance_w(w_data, space.w_str):
  282. raise oefmt(space.w_TypeError, "read() should return bytes")
  283. data = space.bytes_w(w_data)
  284. if not data:
  285. break
  286. builder.append(data)
  287. return space.newbytes(builder.build())
  288. W_RawIOBase.typedef = TypeDef(
  289. '_io._RawIOBase', W_IOBase.typedef,
  290. __new__ = generic_new_descr(W_RawIOBase),
  291. read = interp2app(W_RawIOBase.read_w),
  292. readall = interp2app(W_RawIOBase.readall_w),
  293. )
  294. # ------------------------------------------------------------
  295. # functions to make sure that all streams are flushed on exit
  296. # ------------------------------------------------------------
  297. class AutoFlusher(rweaklist.RWeakListMixin):
  298. def __init__(self, space):
  299. self.initialize()
  300. def add(self, w_iobase):
  301. if rweakref.has_weakref_support():
  302. self.add_handle(w_iobase)
  303. #else:
  304. # no support for weakrefs, so ignore and we
  305. # will not get autoflushing
  306. def flush_all(self, space):
  307. while True:
  308. handles = self.get_all_handles()
  309. self.initialize() # reset the state here
  310. progress = False
  311. for wr in handles:
  312. w_iobase = wr()
  313. if w_iobase is None:
  314. continue
  315. progress = True
  316. try:
  317. space.call_method(w_iobase, 'flush')
  318. except OperationError:
  319. # Silencing all errors is bad, but getting randomly
  320. # interrupted here is equally as bad, and potentially
  321. # more frequent (because of shutdown issues).
  322. pass
  323. if not progress:
  324. break
  325. def get_autoflusher(space):
  326. return space.fromcache(AutoFlusher)