mirror of
https://github.com/python/cpython.git
synced 2024-11-25 19:03:49 +08:00
909 lines
26 KiB
C
909 lines
26 KiB
C
/*
|
|
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));
|
|
}
|
|
|
|
static PyObject *
|
|
iobase_get_dict(PyObject *self)
|
|
{
|
|
PyObject **dictptr = _PyObject_GetDictPtr(self);
|
|
PyObject *dict;
|
|
assert(dictptr);
|
|
dict = *dictptr;
|
|
if (dict == NULL)
|
|
dict = *dictptr = PyDict_New();
|
|
Py_XINCREF(dict);
|
|
return dict;
|
|
}
|
|
|
|
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[] = {
|
|
{"__dict__", (getter)iobase_get_dict, NULL, NULL},
|
|
{"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 */
|
|
};
|