1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
|
from errno import EINTR
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.typedef import (
TypeDef, GetSetProperty, generic_new_descr, descr_get_dict, descr_set_dict,
make_weakref_descr, unwrap_spec)
from pypy.interpreter.gateway import interp2app
from rpython.rlib.rstring import StringBuilder
from rpython.rlib import rweakref, rweaklist
DEFAULT_BUFFER_SIZE = 8192
def convert_size(space, w_size):
if space.is_none(w_size) or w_size is None:
return -1
else:
return space.int_w(w_size)
def trap_eintr(space, error):
# Return True if an EnvironmentError with errno == EINTR is set
if not error.match(space, space.w_EnvironmentError):
return False
try:
w_value = error.get_w_value(space)
w_errno = space.getattr(w_value, space.newtext("errno"))
return space.eq_w(w_errno, space.newint(EINTR))
except OperationError:
return False
def unsupported(space, message):
w_exc = space.getattr(space.getbuiltinmodule('_io'),
space.newtext('UnsupportedOperation'))
return OperationError(w_exc, space.newtext(message))
# May be called with any object
def check_readable_w(space, w_obj):
if not space.is_true(space.call_method(w_obj, 'readable')):
raise unsupported(space, "File or stream is not readable")
# May be called with any object
def check_writable_w(space, w_obj):
if not space.is_true(space.call_method(w_obj, 'writable')):
raise unsupported(space, "File or stream is not writable")
# May be called with any object
def check_seekable_w(space, w_obj):
if not space.is_true(space.call_method(w_obj, 'seekable')):
raise unsupported(space, "File or stream is not seekable")
class W_IOBase(W_Root):
cffi_fileobj = None # pypy/module/_cffi_backend
def __init__(self, space, add_to_autoflusher=True):
# XXX: IOBase thinks it has to maintain its own internal state in
# `__IOBase_closed` and call flush() by itself, but it is redundant
# with whatever behaviour a non-trivial derived class will implement.
self.space = space
self.w_dict = None
self.__IOBase_closed = False
if add_to_autoflusher:
get_autoflusher(space).add(self)
if self.needs_finalizer():
self.register_finalizer(space)
def getdict(self, space):
if self.w_dict is None:
self.w_dict = space.newdict(instance=True)
return self.w_dict
def getdictvalue(self, space, attr):
if self.w_dict is None:
return None
return space.finditem_str(self.w_dict, attr)
def _closed(self, space):
# This gets the derived attribute, which is *not* __IOBase_closed
# in most cases!
w_closed = space.findattr(self, space.newtext('closed'))
if w_closed is not None and space.is_true(w_closed):
return True
return False
def _finalize_(self):
# Note: there is only this empty _finalize_() method here, but
# we still need register_finalizer() so that descr_del() is
# called. IMPORTANT: this is not the recommended way to have a
# finalizer! It makes the finalizer appear as __del__() from
# app-level, and the user can call __del__() explicitly, or
# override it, with or without calling the parent's __del__().
# This matches 'tp_finalize' in CPython >= 3.4. So far (3.5),
# this is the only built-in class with a 'tp_finalize' slot that
# can be subclassed.
pass
def descr_del(self):
space = self.space
w_closed = space.findattr(self, space.newtext('closed'))
try:
# If `closed` doesn't exist or can't be evaluated as bool, then
# the object is probably in an unusable state, so ignore.
if w_closed is not None and not space.is_true(w_closed):
try:
self._dealloc_warn_w(space, self)
finally:
space.call_method(self, "close")
except OperationError:
# Silencing I/O errors is bad, but printing spurious tracebacks is
# equally as bad, and potentially more frequent (because of
# shutdown issues).
pass
def _CLOSED(self):
# Use this macro whenever you want to check the internal `closed`
# status of the IOBase object rather than the virtual `closed`
# attribute as returned by whatever subclass.
return self.__IOBase_closed
def _unsupportedoperation(self, space, message):
raise unsupported(space, message)
def _check_closed(self, space, message=None):
if message is None:
message = "I/O operation on closed file"
if self._closed(space):
raise OperationError(
space.w_ValueError, space.newtext(message))
def check_closed_w(self, space):
self._check_closed(space)
def closed_get_w(self, space):
return space.newbool(self.__IOBase_closed)
def close_w(self, space):
if self._CLOSED():
return
cffifo = self.cffi_fileobj
self.cffi_fileobj = None
if cffifo is not None:
cffifo.close()
try:
space.call_method(self, "flush")
finally:
self.__IOBase_closed = True
self.maybe_unregister_rpython_finalizer_io(space)
def maybe_unregister_rpython_finalizer_io(self, space):
from rpython.rlib import rgc
if self.user_overridden_class:
return
rgc.may_ignore_finalizer(self)
def needs_finalizer(self):
# can return False if we know that the precise close() method
# of this class will have no effect
return True
def _dealloc_warn_w(self, space, w_source):
"""Called when the io is implicitly closed via the deconstructor"""
pass
def flush_w(self, space):
if self._CLOSED():
raise oefmt(space.w_ValueError, "I/O operation on closed file")
def seek_w(self, space, w_offset, w_whence=None):
self._unsupportedoperation(space, "seek")
def tell_w(self, space):
return space.call_method(self, "seek", space.newint(0), space.newint(1))
def truncate_w(self, space, w_size=None):
self._unsupportedoperation(space, "truncate")
def fileno_w(self, space):
self._unsupportedoperation(space, "fileno")
def enter_w(self, space):
self._check_closed(space)
return self
def exit_w(self, space, __args__):
space.call_method(self, "close")
def iter_w(self, space):
self._check_closed(space)
return self
def next_w(self, space):
w_line = space.call_method(self, "readline")
if space.len_w(w_line) == 0:
raise OperationError(space.w_StopIteration, space.w_None)
return w_line
def isatty_w(self, space):
self._check_closed(space)
return space.w_False
def readable_w(self, space):
return space.w_False
def writable_w(self, space):
return space.w_False
def seekable_w(self, space):
return space.w_False
def getstate_w(self, space):
raise oefmt(space.w_TypeError, "cannot serialize '%T' object", self)
# ______________________________________________________________
def readline_w(self, space, w_limit=None):
# For backwards compatibility, a (slowish) readline().
limit = convert_size(space, w_limit)
has_peek = space.findattr(self, space.newtext("peek"))
builder = StringBuilder()
size = 0
while limit < 0 or size < limit:
nreadahead = 1
if has_peek:
try:
w_readahead = space.call_method(self, "peek", space.newint(1))
except OperationError as e:
if trap_eintr(space, e):
continue
raise
if not space.isinstance_w(w_readahead, space.w_bytes):
raise oefmt(space.w_IOError,
"peek() should have returned a bytes object, "
"not '%T'", w_readahead)
length = space.len_w(w_readahead)
if length > 0:
n = 0
buf = space.bytes_w(w_readahead)
if limit >= 0:
while True:
if n >= length or n >= limit:
break
n += 1
if buf[n-1] == '\n':
break
else:
while True:
if n >= length:
break
n += 1
if buf[n-1] == '\n':
break
nreadahead = n
try:
w_read = space.call_method(self, "read", space.newint(nreadahead))
except OperationError as e:
if trap_eintr(space, e):
continue
raise
if not space.isinstance_w(w_read, space.w_bytes):
raise oefmt(space.w_IOError,
"peek() should have returned a bytes object, not "
"'%T'", w_read)
read = space.bytes_w(w_read)
if not read:
break
size += len(read)
builder.append(read)
if read[-1] == '\n':
break
return space.newbytes(builder.build())
def readlines_w(self, space, w_hint=None):
hint = convert_size(space, w_hint)
if hint <= 0:
return space.newlist(space.unpackiterable(self))
length = 0
lines_w = []
w_iterator = space.iter(self)
while 1:
try:
w_line = space.next(w_iterator)
except OperationError as e:
if not e.match(space, space.w_StopIteration):
raise
break
lines_w.append(w_line)
length += space.len_w(w_line)
if length > hint:
break
return space.newlist(lines_w)
def writelines_w(self, space, w_lines):
self._check_closed(space)
w_iterator = space.iter(w_lines)
while True:
try:
w_line = space.next(w_iterator)
except OperationError as e:
if not e.match(space, space.w_StopIteration):
raise
break # done
while True:
try:
space.call_method(self, "write", w_line)
except OperationError as e:
if trap_eintr(space, e):
continue
raise
else:
break
@staticmethod
def output_slice(space, rwbuffer, target_pos, data):
if target_pos + len(data) > rwbuffer.getlength():
raise oefmt(space.w_RuntimeError,
"target buffer has shrunk during operation")
rwbuffer.setslice(target_pos, data)
W_IOBase.typedef = TypeDef(
'_io._IOBase',
__new__ = generic_new_descr(W_IOBase),
__enter__ = interp2app(W_IOBase.enter_w),
__exit__ = interp2app(W_IOBase.exit_w),
__iter__ = interp2app(W_IOBase.iter_w),
__next__ = interp2app(W_IOBase.next_w),
close = interp2app(W_IOBase.close_w),
flush = interp2app(W_IOBase.flush_w),
seek = interp2app(W_IOBase.seek_w),
tell = interp2app(W_IOBase.tell_w),
truncate = interp2app(W_IOBase.truncate_w),
fileno = interp2app(W_IOBase.fileno_w),
isatty = interp2app(W_IOBase.isatty_w),
readable = interp2app(W_IOBase.readable_w),
writable = interp2app(W_IOBase.writable_w),
seekable = interp2app(W_IOBase.seekable_w),
_checkReadable = interp2app(check_readable_w),
_checkWritable = interp2app(check_writable_w),
_checkSeekable = interp2app(check_seekable_w),
_checkClosed = interp2app(W_IOBase.check_closed_w),
closed = GetSetProperty(W_IOBase.closed_get_w,
doc="True if the file is closed"),
__dict__ = GetSetProperty(descr_get_dict, descr_set_dict, cls=W_IOBase),
__weakref__ = make_weakref_descr(W_IOBase),
__del__ = interp2app(W_IOBase.descr_del),
__confirm_applevel_del__ = True,
readline = interp2app(W_IOBase.readline_w),
readlines = interp2app(W_IOBase.readlines_w),
writelines = interp2app(W_IOBase.writelines_w),
)
class W_RawIOBase(W_IOBase):
pass
# ________________________________________________________________
# Abstract read methods, based on readinto()
# note that the self needs to be W_IOBase here, even though the methods are on
# _RawIOBase. The reason is the applevel_subclasses_base below, which ensures
# that subclasses of _io._RawIOBase are instances of (a subclass of) W_IOBase.
# This is not a problem, because W_RawIOBase does not actually have any
# content the methods and don't expect much from "self". see also comment in
# descr_new_rawiobase below
@unwrap_spec(self=W_IOBase)
def rawiobase_read_w(self, space, w_size=None):
size = convert_size(space, w_size)
if size < 0:
return space.call_method(self, "readall")
w_buffer = space.call_function(space.w_bytearray, w_size)
w_length = space.call_method(self, "readinto", w_buffer)
if space.is_w(w_length, space.w_None):
return w_length
space.delslice(w_buffer, w_length, space.len(w_buffer))
return space.call_function(space.w_bytes, w_buffer)
@unwrap_spec(self=W_IOBase)
def rawiobase_readall_w(self, space):
builder = StringBuilder()
while True:
try:
w_data = space.call_method(self, "read",
space.newint(DEFAULT_BUFFER_SIZE))
except OperationError as e:
if trap_eintr(space, e):
continue
raise
if space.is_w(w_data, space.w_None):
if not builder.getlength():
return w_data
break
if not space.isinstance_w(w_data, space.w_bytes):
raise oefmt(space.w_TypeError, "read() should return bytes")
data = space.bytes_w(w_data)
if not data:
break
builder.append(data)
return space.newbytes(builder.build())
def descr_new_rawiobase(space, w_subtype, __args__):
instance = space.allocate_instance(W_RawIOBase, w_subtype)
# NB: for subclasses of _io._RawIOBase, the instance is not actually an
# instance of W_RawIOBase! this does not matter for the methods at all, of
# course, since W_RawIOBase does not have any extra fields over W_IOBase.
W_IOBase.__init__(instance, space)
return instance
assert W_RawIOBase.__init__.im_func is W_IOBase.__init__.im_func
W_RawIOBase.typedef = TypeDef(
'_io._RawIOBase', W_IOBase.typedef,
__doc__ = "Base class for raw binary I/O.",
__rpython_level_class__ = W_RawIOBase,
__new__ = interp2app(descr_new_rawiobase),
read = interp2app(rawiobase_read_w),
readall = interp2app(rawiobase_readall_w),
)
W_RawIOBase.typedef.applevel_subclasses_base = W_IOBase
# ------------------------------------------------------------
# functions to make sure that all streams are flushed on exit
# ------------------------------------------------------------
class AutoFlusher(rweaklist.RWeakListMixin):
def __init__(self, space):
self.initialize()
def add(self, w_iobase):
if rweakref.has_weakref_support():
self.add_handle(w_iobase)
#else:
# no support for weakrefs, so ignore and we
# will not get autoflushing
def flush_all(self, space):
while True:
handles = self.get_all_handles()
self.initialize() # reset the state here
progress = False
for wr in handles:
w_iobase = wr()
if w_iobase is None:
continue
progress = True
try:
space.call_method(w_iobase, 'flush')
except OperationError:
# Silencing all errors is bad, but getting randomly
# interrupted here is equally as bad, and potentially
# more frequent (because of shutdown issues).
pass
if not progress:
break
def get_autoflusher(space):
return space.fromcache(AutoFlusher)
|