| /* |
| An implementation of the I/O abstract base classes hierarchy |
| as defined by PEP 3116 - "New I/O" |
| |
| Classes defined here: IOBase, RawIOBase. |
| |
| Written by Amaury Forgeot d'Arc and Antoine Pitrou |
| */ |
| |
| |
| #define PY_SSIZE_T_CLEAN |
| #include "Python.h" |
| #include "structmember.h" |
| #include "_iomodule.h" |
| |
| /* |
| * IOBase class, an abstract class |
| */ |
| |
| typedef struct { |
| PyObject_HEAD |
| |
| PyObject *dict; |
| PyObject *weakreflist; |
| } iobase; |
| |
| PyDoc_STRVAR(iobase_doc, |
| "The abstract base class for all I/O classes, acting on streams of\n" |
| "bytes. There is no public constructor.\n" |
| "\n" |
| "This class provides dummy implementations for many methods that\n" |
| "derived classes can override selectively; the default implementations\n" |
| "represent a file that cannot be read, written or seeked.\n" |
| "\n" |
| "Even though IOBase does not declare read, readinto, or write because\n" |
| "their signatures will vary, implementations and clients should\n" |
| "consider those methods part of the interface. Also, implementations\n" |
| "may raise UnsupportedOperation when operations they do not support are\n" |
| "called.\n" |
| "\n" |
| "The basic type used for binary data read from or written to a file is\n" |
| "bytes. bytearrays are accepted too, and in some cases (such as\n" |
| "readinto) needed. Text I/O classes work with str data.\n" |
| "\n" |
| "Note that calling any method (even inquiries) on a closed stream is\n" |
| "undefined. Implementations may raise IOError in this case.\n" |
| "\n" |
| "IOBase (and its subclasses) support the iterator protocol, meaning\n" |
| "that an IOBase object can be iterated over yielding the lines in a\n" |
| "stream.\n" |
| "\n" |
| "IOBase also supports the :keyword:`with` statement. In this example,\n" |
| "fp is closed after the suite of the with statement is complete:\n" |
| "\n" |
| "with open('spam.txt', 'r') as fp:\n" |
| " fp.write('Spam and eggs!')\n"); |
| |
| /* 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. */ |
| |
| #define IS_CLOSED(self) \ |
| PyObject_HasAttrString(self, "__IOBase_closed") |
| |
| /* Internal methods */ |
| static PyObject * |
| iobase_unsupported(const char *message) |
| { |
| PyErr_SetString(IO_STATE->unsupported_operation, message); |
| return NULL; |
| } |
| |
| /* Positionning */ |
| |
| PyDoc_STRVAR(iobase_seek_doc, |
| "Change stream position.\n" |
| "\n" |
| "Change the stream position to byte offset offset. offset is\n" |
| "interpreted relative to the position indicated by whence. Values\n" |
| "for whence are:\n" |
| "\n" |
| "* 0 -- start of stream (the default); offset should be zero or positive\n" |
| "* 1 -- current stream position; offset may be negative\n" |
| "* 2 -- end of stream; offset is usually negative\n" |
| "\n" |
| "Return the new absolute position."); |
| |
| static PyObject * |
| iobase_seek(PyObject *self, PyObject *args) |
| { |
| return iobase_unsupported("seek"); |
| } |
| |
| PyDoc_STRVAR(iobase_tell_doc, |
| "Return current stream position."); |
| |
| static PyObject * |
| iobase_tell(PyObject *self, PyObject *args) |
| { |
| return PyObject_CallMethod(self, "seek", "ii", 0, 1); |
| } |
| |
| PyDoc_STRVAR(iobase_truncate_doc, |
| "Truncate file to size bytes.\n" |
| "\n" |
| "File pointer is left unchanged. Size defaults to the current IO\n" |
| "position as reported by tell(). Returns the new size."); |
| |
| static PyObject * |
| iobase_truncate(PyObject *self, PyObject *args) |
| { |
| return iobase_unsupported("truncate"); |
| } |
| |
| /* Flush and close methods */ |
| |
| PyDoc_STRVAR(iobase_flush_doc, |
| "Flush write buffers, if applicable.\n" |
| "\n" |
| "This is not implemented for read-only and non-blocking streams.\n"); |
| |
| static PyObject * |
| iobase_flush(PyObject *self, PyObject *args) |
| { |
| /* XXX Should this return the number of bytes written??? */ |
| if (IS_CLOSED(self)) { |
| PyErr_SetString(PyExc_ValueError, "I/O operation on closed file."); |
| return NULL; |
| } |
| Py_RETURN_NONE; |
| } |
| |
| PyDoc_STRVAR(iobase_close_doc, |
| "Flush and close the IO object.\n" |
| "\n" |
| "This method has no effect if the file is already closed.\n"); |
| |
| static int |
| iobase_closed(PyObject *self) |
| { |
| PyObject *res; |
| int closed; |
| /* This gets the derived attribute, which is *not* __IOBase_closed |
| in most cases! */ |
| res = PyObject_GetAttr(self, _PyIO_str_closed); |
| if (res == NULL) |
| return 0; |
| closed = PyObject_IsTrue(res); |
| Py_DECREF(res); |
| return closed; |
| } |
| |
| static PyObject * |
| iobase_closed_get(PyObject *self, void *context) |
| { |
| return PyBool_FromLong(IS_CLOSED(self)); |
| } |
| |
| PyObject * |
| _PyIOBase_check_closed(PyObject *self, PyObject *args) |
| { |
| if (iobase_closed(self)) { |
| PyErr_SetString(PyExc_ValueError, "I/O operation on closed file."); |
| return NULL; |
| } |
| if (args == Py_True) |
| return Py_None; |
| else |
| Py_RETURN_NONE; |
| } |
| |
| /* 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. */ |
| |
| static PyObject * |
| iobase_close(PyObject *self, PyObject *args) |
| { |
| PyObject *res; |
| |
| if (IS_CLOSED(self)) |
| Py_RETURN_NONE; |
| |
| res = PyObject_CallMethodObjArgs(self, _PyIO_str_flush, NULL); |
| PyObject_SetAttrString(self, "__IOBase_closed", Py_True); |
| if (res == NULL) { |
| return NULL; |
| } |
| Py_XDECREF(res); |
| Py_RETURN_NONE; |
| } |
| |
| /* Finalization and garbage collection support */ |
| |
| int |
| _PyIOBase_finalize(PyObject *self) |
| { |
| PyObject *res; |
| PyObject *tp, *v, *tb; |
| int closed = 1; |
| int is_zombie; |
| |
| /* If _PyIOBase_finalize() is called from a destructor, we need to |
| resurrect the object as calling close() can invoke arbitrary code. */ |
| is_zombie = (Py_REFCNT(self) == 0); |
| if (is_zombie) { |
| ++Py_REFCNT(self); |
| } |
| PyErr_Fetch(&tp, &v, &tb); |
| /* If `closed` doesn't exist or can't be evaluated as bool, then the |
| object is probably in an unusable state, so ignore. */ |
| res = PyObject_GetAttr(self, _PyIO_str_closed); |
| if (res == NULL) |
| PyErr_Clear(); |
| else { |
| closed = PyObject_IsTrue(res); |
| Py_DECREF(res); |
| if (closed == -1) |
| PyErr_Clear(); |
| } |
| if (closed == 0) { |
| res = PyObject_CallMethodObjArgs((PyObject *) self, _PyIO_str_close, |
| NULL); |
| /* Silencing I/O errors is bad, but printing spurious tracebacks is |
| equally as bad, and potentially more frequent (because of |
| shutdown issues). */ |
| if (res == NULL) |
| PyErr_Clear(); |
| else |
| Py_DECREF(res); |
| } |
| PyErr_Restore(tp, v, tb); |
| if (is_zombie) { |
| if (--Py_REFCNT(self) != 0) { |
| /* The object lives again. The following code is taken from |
| slot_tp_del in typeobject.c. */ |
| Py_ssize_t refcnt = Py_REFCNT(self); |
| _Py_NewReference(self); |
| Py_REFCNT(self) = refcnt; |
| /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so |
| * we need to undo that. */ |
| _Py_DEC_REFTOTAL; |
| /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object |
| * chain, so no more to do there. |
| * If COUNT_ALLOCS, the original decref bumped tp_frees, and |
| * _Py_NewReference bumped tp_allocs: both of those need to be |
| * undone. |
| */ |
| #ifdef COUNT_ALLOCS |
| --Py_TYPE(self)->tp_frees; |
| --Py_TYPE(self)->tp_allocs; |
| #endif |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| iobase_traverse(iobase *self, visitproc visit, void *arg) |
| { |
| Py_VISIT(self->dict); |
| return 0; |
| } |
| |
| static int |
| iobase_clear(iobase *self) |
| { |
| if (_PyIOBase_finalize((PyObject *) self) < 0) |
| return -1; |
| Py_CLEAR(self->dict); |
| return 0; |
| } |
| |
| /* Destructor */ |
| |
| static void |
| iobase_dealloc(iobase *self) |
| { |
| /* NOTE: since IOBaseObject has its own dict, Python-defined attributes |
| are still available here for close() to use. |
| However, if the derived class declares a __slots__, those slots are |
| already gone. |
| */ |
| if (_PyIOBase_finalize((PyObject *) self) < 0) { |
| /* When called from a heap type's dealloc, the type will be |
| decref'ed on return (see e.g. subtype_dealloc in typeobject.c). */ |
| if (PyType_HasFeature(Py_TYPE(self), Py_TPFLAGS_HEAPTYPE)) |
| Py_INCREF(Py_TYPE(self)); |
| return; |
| } |
| _PyObject_GC_UNTRACK(self); |
| if (self->weakreflist != NULL) |
| PyObject_ClearWeakRefs((PyObject *) self); |
| Py_CLEAR(self->dict); |
| Py_TYPE(self)->tp_free((PyObject *) self); |
| } |
| |
| /* Inquiry methods */ |
| |
| PyDoc_STRVAR(iobase_seekable_doc, |
| "Return whether object supports random access.\n" |
| "\n" |
| "If False, seek(), tell() and truncate() will raise UnsupportedOperation.\n" |
| "This method may need to do a test seek()."); |
| |
| static PyObject * |
| iobase_seekable(PyObject *self, PyObject *args) |
| { |
| Py_RETURN_FALSE; |
| } |
| |
| PyObject * |
| _PyIOBase_check_seekable(PyObject *self, PyObject *args) |
| { |
| PyObject *res = PyObject_CallMethodObjArgs(self, _PyIO_str_seekable, NULL); |
| if (res == NULL) |
| return NULL; |
| if (res != Py_True) { |
| Py_CLEAR(res); |
| iobase_unsupported("File or stream is not seekable."); |
| return NULL; |
| } |
| if (args == Py_True) { |
| Py_DECREF(res); |
| } |
| return res; |
| } |
| |
| PyDoc_STRVAR(iobase_readable_doc, |
| "Return whether object was opened for reading.\n" |
| "\n" |
| "If False, read() will raise UnsupportedOperation."); |
| |
| static PyObject * |
| iobase_readable(PyObject *self, PyObject *args) |
| { |
| Py_RETURN_FALSE; |
| } |
| |
| /* May be called with any object */ |
| PyObject * |
| _PyIOBase_check_readable(PyObject *self, PyObject *args) |
| { |
| PyObject *res = PyObject_CallMethodObjArgs(self, _PyIO_str_readable, NULL); |
| if (res == NULL) |
| return NULL; |
| if (res != Py_True) { |
| Py_CLEAR(res); |
| iobase_unsupported("File or stream is not readable."); |
| return NULL; |
| } |
| if (args == Py_True) { |
| Py_DECREF(res); |
| } |
| return res; |
| } |
| |
| PyDoc_STRVAR(iobase_writable_doc, |
| "Return whether object was opened for writing.\n" |
| "\n" |
| "If False, write() will raise UnsupportedOperation."); |
| |
| static PyObject * |
| iobase_writable(PyObject *self, PyObject *args) |
| { |
| Py_RETURN_FALSE; |
| } |
| |
| /* May be called with any object */ |
| PyObject * |
| _PyIOBase_check_writable(PyObject *self, PyObject *args) |
| { |
| PyObject *res = PyObject_CallMethodObjArgs(self, _PyIO_str_writable, NULL); |
| if (res == NULL) |
| return NULL; |
| if (res != Py_True) { |
| Py_CLEAR(res); |
| iobase_unsupported("File or stream is not writable."); |
| return NULL; |
| } |
| if (args == Py_True) { |
| Py_DECREF(res); |
| } |
| return res; |
| } |
| |
| /* Context manager */ |
| |
| static PyObject * |
| iobase_enter(PyObject *self, PyObject *args) |
| { |
| if (_PyIOBase_check_closed(self, Py_True) == NULL) |
| return NULL; |
| |
| Py_INCREF(self); |
| return self; |
| } |
| |
| static PyObject * |
| iobase_exit(PyObject *self, PyObject *args) |
| { |
| return PyObject_CallMethodObjArgs(self, _PyIO_str_close, NULL); |
| } |
| |
| /* Lower-level APIs */ |
| |
| /* XXX Should these be present even if unimplemented? */ |
| |
| PyDoc_STRVAR(iobase_fileno_doc, |
| "Returns underlying file descriptor if one exists.\n" |
| "\n" |
| "An IOError is raised if the IO object does not use a file descriptor.\n"); |
| |
| static PyObject * |
| iobase_fileno(PyObject *self, PyObject *args) |
| { |
| return iobase_unsupported("fileno"); |
| } |
| |
| PyDoc_STRVAR(iobase_isatty_doc, |
| "Return whether this is an 'interactive' stream.\n" |
| "\n" |
| "Return False if it can't be determined.\n"); |
| |
| static PyObject * |
| iobase_isatty(PyObject *self, PyObject *args) |
| { |
| if (_PyIOBase_check_closed(self, Py_True) == NULL) |
| return NULL; |
| Py_RETURN_FALSE; |
| } |
| |
| /* Readline(s) and writelines */ |
| |
| PyDoc_STRVAR(iobase_readline_doc, |
| "Read and return a line from the stream.\n" |
| "\n" |
| "If limit is specified, at most limit bytes will be read.\n" |
| "\n" |
| "The line terminator is always b'\n' for binary files; for text\n" |
| "files, the newlines argument to open can be used to select the line\n" |
| "terminator(s) recognized.\n"); |
| |
| static PyObject * |
| iobase_readline(PyObject *self, PyObject *args) |
| { |
| /* For backwards compatibility, a (slowish) readline(). */ |
| |
| Py_ssize_t limit = -1; |
| int has_peek = 0; |
| PyObject *buffer, *result; |
| Py_ssize_t old_size = -1; |
| |
| if (!PyArg_ParseTuple(args, "|O&:readline", &_PyIO_ConvertSsize_t, &limit)) { |
| return NULL; |
| } |
| |
| if (PyObject_HasAttrString(self, "peek")) |
| has_peek = 1; |
| |
| buffer = PyByteArray_FromStringAndSize(NULL, 0); |
| if (buffer == NULL) |
| return NULL; |
| |
| while (limit < 0 || Py_SIZE(buffer) < limit) { |
| Py_ssize_t nreadahead = 1; |
| PyObject *b; |
| |
| if (has_peek) { |
| PyObject *readahead = PyObject_CallMethod(self, "peek", "i", 1); |
| if (readahead == NULL) |
| goto fail; |
| if (!PyBytes_Check(readahead)) { |
| PyErr_Format(PyExc_IOError, |
| "peek() should have returned a bytes object, " |
| "not '%.200s'", Py_TYPE(readahead)->tp_name); |
| Py_DECREF(readahead); |
| goto fail; |
| } |
| if (PyBytes_GET_SIZE(readahead) > 0) { |
| Py_ssize_t n = 0; |
| const char *buf = PyBytes_AS_STRING(readahead); |
| if (limit >= 0) { |
| do { |
| if (n >= PyBytes_GET_SIZE(readahead) || n >= limit) |
| break; |
| if (buf[n++] == '\n') |
| break; |
| } while (1); |
| } |
| else { |
| do { |
| if (n >= PyBytes_GET_SIZE(readahead)) |
| break; |
| if (buf[n++] == '\n') |
| break; |
| } while (1); |
| } |
| nreadahead = n; |
| } |
| Py_DECREF(readahead); |
| } |
| |
| b = PyObject_CallMethod(self, "read", "n", nreadahead); |
| if (b == NULL) |
| goto fail; |
| if (!PyBytes_Check(b)) { |
| PyErr_Format(PyExc_IOError, |
| "read() should have returned a bytes object, " |
| "not '%.200s'", Py_TYPE(b)->tp_name); |
| Py_DECREF(b); |
| goto fail; |
| } |
| if (PyBytes_GET_SIZE(b) == 0) { |
| Py_DECREF(b); |
| break; |
| } |
| |
| old_size = PyByteArray_GET_SIZE(buffer); |
| PyByteArray_Resize(buffer, old_size + PyBytes_GET_SIZE(b)); |
| memcpy(PyByteArray_AS_STRING(buffer) + old_size, |
| PyBytes_AS_STRING(b), PyBytes_GET_SIZE(b)); |
| |
| Py_DECREF(b); |
| |
| if (PyByteArray_AS_STRING(buffer)[PyByteArray_GET_SIZE(buffer) - 1] == '\n') |
| break; |
| } |
| |
| result = PyBytes_FromStringAndSize(PyByteArray_AS_STRING(buffer), |
| PyByteArray_GET_SIZE(buffer)); |
| Py_DECREF(buffer); |
| return result; |
| fail: |
| Py_DECREF(buffer); |
| return NULL; |
| } |
| |
| static PyObject * |
| iobase_iter(PyObject *self) |
| { |
| if (_PyIOBase_check_closed(self, Py_True) == NULL) |
| return NULL; |
| |
| Py_INCREF(self); |
| return self; |
| } |
| |
| static PyObject * |
| iobase_iternext(PyObject *self) |
| { |
| PyObject *line = PyObject_CallMethodObjArgs(self, _PyIO_str_readline, NULL); |
| |
| if (line == NULL) |
| return NULL; |
| |
| if (PyObject_Size(line) == 0) { |
| Py_DECREF(line); |
| return NULL; |
| } |
| |
| return line; |
| } |
| |
| PyDoc_STRVAR(iobase_readlines_doc, |
| "Return a list of lines from the stream.\n" |
| "\n" |
| "hint can be specified to control the number of lines read: no more\n" |
| "lines will be read if the total size (in bytes/characters) of all\n" |
| "lines so far exceeds hint."); |
| |
| static PyObject * |
| iobase_readlines(PyObject *self, PyObject *args) |
| { |
| Py_ssize_t hint = -1, length = 0; |
| PyObject *result; |
| |
| if (!PyArg_ParseTuple(args, "|O&:readlines", &_PyIO_ConvertSsize_t, &hint)) { |
| return NULL; |
| } |
| |
| result = PyList_New(0); |
| if (result == NULL) |
| return NULL; |
| |
| if (hint <= 0) { |
| /* XXX special-casing this made sense in the Python version in order |
| to remove the bytecode interpretation overhead, but it could |
| probably be removed here. */ |
| PyObject *ret = PyObject_CallMethod(result, "extend", "O", self); |
| if (ret == NULL) { |
| Py_DECREF(result); |
| return NULL; |
| } |
| Py_DECREF(ret); |
| return result; |
| } |
| |
| while (1) { |
| PyObject *line = PyIter_Next(self); |
| if (line == NULL) { |
| if (PyErr_Occurred()) { |
| Py_DECREF(result); |
| return NULL; |
| } |
| else |
| break; /* StopIteration raised */ |
| } |
| |
| if (PyList_Append(result, line) < 0) { |
| Py_DECREF(line); |
| Py_DECREF(result); |
| return NULL; |
| } |
| length += PyObject_Size(line); |
| Py_DECREF(line); |
| |
| if (length > hint) |
| break; |
| } |
| return result; |
| } |
| |
| static PyObject * |
| iobase_writelines(PyObject *self, PyObject *args) |
| { |
| PyObject *lines, *iter, *res; |
| |
| if (!PyArg_ParseTuple(args, "O:writelines", &lines)) { |
| return NULL; |
| } |
| |
| if (_PyIOBase_check_closed(self, Py_True) == NULL) |
| return NULL; |
| |
| iter = PyObject_GetIter(lines); |
| if (iter == NULL) |
| return NULL; |
| |
| while (1) { |
| PyObject *line = PyIter_Next(iter); |
| if (line == NULL) { |
| if (PyErr_Occurred()) { |
| Py_DECREF(iter); |
| return NULL; |
| } |
| else |
| break; /* Stop Iteration */ |
| } |
| |
| res = PyObject_CallMethodObjArgs(self, _PyIO_str_write, line, NULL); |
| Py_DECREF(line); |
| if (res == NULL) { |
| Py_DECREF(iter); |
| return NULL; |
| } |
| Py_DECREF(res); |
| } |
| Py_DECREF(iter); |
| Py_RETURN_NONE; |
| } |
| |
| static PyMethodDef iobase_methods[] = { |
| {"seek", iobase_seek, METH_VARARGS, iobase_seek_doc}, |
| {"tell", iobase_tell, METH_NOARGS, iobase_tell_doc}, |
| {"truncate", iobase_truncate, METH_VARARGS, iobase_truncate_doc}, |
| {"flush", iobase_flush, METH_NOARGS, iobase_flush_doc}, |
| {"close", iobase_close, METH_NOARGS, iobase_close_doc}, |
| |
| {"seekable", iobase_seekable, METH_NOARGS, iobase_seekable_doc}, |
| {"readable", iobase_readable, METH_NOARGS, iobase_readable_doc}, |
| {"writable", iobase_writable, METH_NOARGS, iobase_writable_doc}, |
| |
| {"_checkClosed", _PyIOBase_check_closed, METH_NOARGS}, |
| {"_checkSeekable", _PyIOBase_check_seekable, METH_NOARGS}, |
| {"_checkReadable", _PyIOBase_check_readable, METH_NOARGS}, |
| {"_checkWritable", _PyIOBase_check_writable, METH_NOARGS}, |
| |
| {"fileno", iobase_fileno, METH_NOARGS, iobase_fileno_doc}, |
| {"isatty", iobase_isatty, METH_NOARGS, iobase_isatty_doc}, |
| |
| {"__enter__", iobase_enter, METH_NOARGS}, |
| {"__exit__", iobase_exit, METH_VARARGS}, |
| |
| {"readline", iobase_readline, METH_VARARGS, iobase_readline_doc}, |
| {"readlines", iobase_readlines, METH_VARARGS, iobase_readlines_doc}, |
| {"writelines", iobase_writelines, METH_VARARGS}, |
| |
| {NULL, NULL} |
| }; |
| |
| static PyGetSetDef iobase_getset[] = { |
| {"closed", (getter)iobase_closed_get, NULL, NULL}, |
| {NULL} |
| }; |
| |
| |
| PyTypeObject PyIOBase_Type = { |
| PyVarObject_HEAD_INIT(NULL, 0) |
| "_io._IOBase", /*tp_name*/ |
| sizeof(iobase), /*tp_basicsize*/ |
| 0, /*tp_itemsize*/ |
| (destructor)iobase_dealloc, /*tp_dealloc*/ |
| 0, /*tp_print*/ |
| 0, /*tp_getattr*/ |
| 0, /*tp_setattr*/ |
| 0, /*tp_compare */ |
| 0, /*tp_repr*/ |
| 0, /*tp_as_number*/ |
| 0, /*tp_as_sequence*/ |
| 0, /*tp_as_mapping*/ |
| 0, /*tp_hash */ |
| 0, /*tp_call*/ |
| 0, /*tp_str*/ |
| 0, /*tp_getattro*/ |
| 0, /*tp_setattro*/ |
| 0, /*tp_as_buffer*/ |
| Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
| | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ |
| iobase_doc, /* tp_doc */ |
| (traverseproc)iobase_traverse, /* tp_traverse */ |
| (inquiry)iobase_clear, /* tp_clear */ |
| 0, /* tp_richcompare */ |
| offsetof(iobase, weakreflist), /* tp_weaklistoffset */ |
| iobase_iter, /* tp_iter */ |
| iobase_iternext, /* tp_iternext */ |
| iobase_methods, /* tp_methods */ |
| 0, /* tp_members */ |
| iobase_getset, /* tp_getset */ |
| 0, /* tp_base */ |
| 0, /* tp_dict */ |
| 0, /* tp_descr_get */ |
| 0, /* tp_descr_set */ |
| offsetof(iobase, dict), /* tp_dictoffset */ |
| 0, /* tp_init */ |
| 0, /* tp_alloc */ |
| PyType_GenericNew, /* tp_new */ |
| }; |
| |
| |
| /* |
| * RawIOBase class, Inherits from IOBase. |
| */ |
| PyDoc_STRVAR(rawiobase_doc, |
| "Base class for raw binary I/O."); |
| |
| /* |
| * The read() method is implemented by calling readinto(); derived classes |
| * that want to support read() only need to implement readinto() as a |
| * primitive operation. In general, readinto() can be more efficient than |
| * read(). |
| * |
| * (It would be tempting to also provide an implementation of readinto() in |
| * terms of read(), in case the latter is a more suitable primitive operation, |
| * but that would lead to nasty recursion in case a subclass doesn't implement |
| * either.) |
| */ |
| |
| static PyObject * |
| rawiobase_read(PyObject *self, PyObject *args) |
| { |
| Py_ssize_t n = -1; |
| PyObject *b, *res; |
| |
| if (!PyArg_ParseTuple(args, "|n:read", &n)) { |
| return NULL; |
| } |
| |
| if (n < 0) |
| return PyObject_CallMethod(self, "readall", NULL); |
| |
| /* TODO: allocate a bytes object directly instead and manually construct |
| a writable memoryview pointing to it. */ |
| b = PyByteArray_FromStringAndSize(NULL, n); |
| if (b == NULL) |
| return NULL; |
| |
| res = PyObject_CallMethodObjArgs(self, _PyIO_str_readinto, b, NULL); |
| if (res == NULL || res == Py_None) { |
| Py_DECREF(b); |
| return res; |
| } |
| |
| n = PyNumber_AsSsize_t(res, PyExc_ValueError); |
| Py_DECREF(res); |
| if (n == -1 && PyErr_Occurred()) { |
| Py_DECREF(b); |
| return NULL; |
| } |
| |
| res = PyBytes_FromStringAndSize(PyByteArray_AsString(b), n); |
| Py_DECREF(b); |
| return res; |
| } |
| |
| |
| PyDoc_STRVAR(rawiobase_readall_doc, |
| "Read until EOF, using multiple read() call."); |
| |
| static PyObject * |
| rawiobase_readall(PyObject *self, PyObject *args) |
| { |
| int r; |
| PyObject *chunks = PyList_New(0); |
| PyObject *result; |
| |
| if (chunks == NULL) |
| return NULL; |
| |
| while (1) { |
| PyObject *data = PyObject_CallMethod(self, "read", |
| "i", DEFAULT_BUFFER_SIZE); |
| if (!data) { |
| Py_DECREF(chunks); |
| return NULL; |
| } |
| if (data == Py_None) { |
| if (PyList_GET_SIZE(chunks) == 0) { |
| Py_DECREF(chunks); |
| return data; |
| } |
| Py_DECREF(data); |
| break; |
| } |
| if (!PyBytes_Check(data)) { |
| Py_DECREF(chunks); |
| Py_DECREF(data); |
| PyErr_SetString(PyExc_TypeError, "read() should return bytes"); |
| return NULL; |
| } |
| if (PyBytes_GET_SIZE(data) == 0) { |
| /* EOF */ |
| Py_DECREF(data); |
| break; |
| } |
| r = PyList_Append(chunks, data); |
| Py_DECREF(data); |
| if (r < 0) { |
| Py_DECREF(chunks); |
| return NULL; |
| } |
| } |
| result = _PyBytes_Join(_PyIO_empty_bytes, chunks); |
| Py_DECREF(chunks); |
| return result; |
| } |
| |
| static PyMethodDef rawiobase_methods[] = { |
| {"read", rawiobase_read, METH_VARARGS}, |
| {"readall", rawiobase_readall, METH_NOARGS, rawiobase_readall_doc}, |
| {NULL, NULL} |
| }; |
| |
| PyTypeObject PyRawIOBase_Type = { |
| PyVarObject_HEAD_INIT(NULL, 0) |
| "_io._RawIOBase", /*tp_name*/ |
| 0, /*tp_basicsize*/ |
| 0, /*tp_itemsize*/ |
| 0, /*tp_dealloc*/ |
| 0, /*tp_print*/ |
| 0, /*tp_getattr*/ |
| 0, /*tp_setattr*/ |
| 0, /*tp_compare */ |
| 0, /*tp_repr*/ |
| 0, /*tp_as_number*/ |
| 0, /*tp_as_sequence*/ |
| 0, /*tp_as_mapping*/ |
| 0, /*tp_hash */ |
| 0, /*tp_call*/ |
| 0, /*tp_str*/ |
| 0, /*tp_getattro*/ |
| 0, /*tp_setattro*/ |
| 0, /*tp_as_buffer*/ |
| Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ |
| rawiobase_doc, /* tp_doc */ |
| 0, /* tp_traverse */ |
| 0, /* tp_clear */ |
| 0, /* tp_richcompare */ |
| 0, /* tp_weaklistoffset */ |
| 0, /* tp_iter */ |
| 0, /* tp_iternext */ |
| rawiobase_methods, /* tp_methods */ |
| 0, /* tp_members */ |
| 0, /* tp_getset */ |
| &PyIOBase_Type, /* tp_base */ |
| 0, /* tp_dict */ |
| 0, /* tp_descr_get */ |
| 0, /* tp_descr_set */ |
| 0, /* tp_dictoffset */ |
| 0, /* tp_init */ |
| 0, /* tp_alloc */ |
| 0, /* tp_new */ |
| }; |