#include "Python.h"
#include "pycore_fileutils.h"
#include "pycore_object.h"
#include "structmember.h"
#include <stdbool.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <stddef.h>
#include "_iomodule.h"
#ifdef MS_WINDOWS
#define HAVE_FTRUNCATE
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
#if BUFSIZ < (8*1024)
#define SMALLCHUNK (8*1024)
#elif (BUFSIZ >= (2 << 25))
#error "unreasonable BUFSIZ > 64 MiB defined"
#else
#define SMALLCHUNK BUFSIZ
#endif
typedef struct {
PyObject_HEAD
int fd;
unsigned int created : 1;
unsigned int readable : 1;
unsigned int writable : 1;
unsigned int appending : 1;
signed int seekable : 2;
unsigned int closefd : 1;
char finalizing;
unsigned int blksize;
PyObject *weakreflist;
PyObject *dict;
} fileio;
#define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), state->PyFileIO_Type))
static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error);
int
_PyFileIO_closed(PyObject *self)
{
return ((fileio *)self)->fd < 0;
}
static PyObject *
fileio_dealloc_warn(fileio *self, PyObject *source)
{
if (self->fd >= 0 && self->closefd) {
PyObject *exc = PyErr_GetRaisedException();
if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) {
if (PyErr_ExceptionMatches(PyExc_Warning))
PyErr_WriteUnraisable((PyObject *) self);
}
PyErr_SetRaisedException(exc);
}
Py_RETURN_NONE;
}
static int
internal_close(fileio *self)
{
int err = 0;
int save_errno = 0;
if (self->fd >= 0) {
int fd = self->fd;
self->fd = -1;
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
err = close(fd);
if (err < 0)
save_errno = errno;
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
}
if (err < 0) {
errno = save_errno;
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
return 0;
}
static PyObject *
_io_FileIO_close_impl(fileio *self, PyTypeObject *cls)
{
PyObject *res;
int rc;
_PyIO_State *state = get_io_state_by_cls(cls);
res = PyObject_CallMethodOneArg((PyObject*)state->PyRawIOBase_Type,
&_Py_ID(close), (PyObject *)self);
if (!self->closefd) {
self->fd = -1;
return res;
}
PyObject *exc;
if (res == NULL) {
exc = PyErr_GetRaisedException();
}
if (self->finalizing) {
PyObject *r = fileio_dealloc_warn(self, (PyObject *) self);
if (r) {
Py_DECREF(r);
}
else {
PyErr_Clear();
}
}
rc = internal_close(self);
if (res == NULL) {
_PyErr_ChainExceptions1(exc);
}
if (rc < 0) {
Py_CLEAR(res);
}
return res;
}
static PyObject *
fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
fileio *self;
assert(type != NULL && type->tp_alloc != NULL);
self = (fileio *) type->tp_alloc(type, 0);
if (self != NULL) {
self->fd = -1;
self->created = 0;
self->readable = 0;
self->writable = 0;
self->appending = 0;
self->seekable = -1;
self->blksize = 0;
self->closefd = 1;
self->weakreflist = NULL;
}
return (PyObject *) self;
}
#ifdef O_CLOEXEC
extern int _Py_open_cloexec_works;
#endif
static int
_io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
int closefd, PyObject *opener)
{
#ifdef MS_WINDOWS
wchar_t *widename = NULL;
#else
const char *name = NULL;
#endif
PyObject *stringobj = NULL;
const char *s;
int ret = 0;
int rwa = 0, plus = 0;
int flags = 0;
int fd = -1;
int fd_is_own = 0;
#ifdef O_CLOEXEC
int *atomic_flag_works = &_Py_open_cloexec_works;
#elif !defined(MS_WINDOWS)
int *atomic_flag_works = NULL;
#endif
struct _Py_stat_struct fdfstat;
int fstat_result;
int async_err = 0;
#ifdef Py_DEBUG
_PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
assert(PyFileIO_Check(state, self));
#endif
if (self->fd >= 0) {
if (self->closefd) {
if (internal_close(self) < 0)
return -1;
}
else
self->fd = -1;
}
fd = _PyLong_AsInt(nameobj);
if (fd < 0) {
if (!PyErr_Occurred()) {
PyErr_SetString(PyExc_ValueError,
"negative file descriptor");
return -1;
}
PyErr_Clear();
}
if (fd < 0) {
#ifdef MS_WINDOWS
if (!PyUnicode_FSDecoder(nameobj, &stringobj)) {
return -1;
}
widename = PyUnicode_AsWideCharString(stringobj, NULL);
if (widename == NULL)
return -1;
#else
if (!PyUnicode_FSConverter(nameobj, &stringobj)) {
return -1;
}
name = PyBytes_AS_STRING(stringobj);
#endif
}
s = mode;
while (*s) {
switch (*s++) {
case 'x':
if (rwa) {
bad_mode:
PyErr_SetString(PyExc_ValueError,
"Must have exactly one of create/read/write/append "
"mode and at most one plus");
goto error;
}
rwa = 1;
self->created = 1;
self->writable = 1;
flags |= O_EXCL | O_CREAT;
break;
case 'r':
if (rwa)
goto bad_mode;
rwa = 1;
self->readable = 1;
break;
case 'w':
if (rwa)
goto bad_mode;
rwa = 1;
self->writable = 1;
flags |= O_CREAT | O_TRUNC;
break;
case 'a':
if (rwa)
goto bad_mode;
rwa = 1;
self->writable = 1;
self->appending = 1;
flags |= O_APPEND | O_CREAT;
break;
case 'b':
break;
case '+':
if (plus)
goto bad_mode;
self->readable = self->writable = 1;
plus = 1;
break;
default:
PyErr_Format(PyExc_ValueError,
"invalid mode: %.200s", mode);
goto error;
}
}
if (!rwa)
goto bad_mode;
if (self->readable && self->writable)
flags |= O_RDWR;
else if (self->readable)
flags |= O_RDONLY;
else
flags |= O_WRONLY;
#ifdef O_BINARY
flags |= O_BINARY;
#endif
#ifdef MS_WINDOWS
flags |= O_NOINHERIT;
#elif defined(O_CLOEXEC)
flags |= O_CLOEXEC;
#endif
if (PySys_Audit("open", "Osi", nameobj, mode, flags) < 0) {
goto error;
}
if (fd >= 0) {
self->fd = fd;
self->closefd = closefd;
}
else {
self->closefd = 1;
if (!closefd) {
PyErr_SetString(PyExc_ValueError,
"Cannot use closefd=False with file name");
goto error;
}
errno = 0;
if (opener == Py_None) {
do {
Py_BEGIN_ALLOW_THREADS
#ifdef MS_WINDOWS
self->fd = _wopen(widename, flags, 0666);
#else
self->fd = open(name, flags, 0666);
#endif
Py_END_ALLOW_THREADS
} while (self->fd < 0 && errno == EINTR &&
!(async_err = PyErr_CheckSignals()));
if (async_err)
goto error;
}
else {
PyObject *fdobj;
#ifndef MS_WINDOWS
atomic_flag_works = NULL;
#endif
fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags);
if (fdobj == NULL)
goto error;
if (!PyLong_Check(fdobj)) {
Py_DECREF(fdobj);
PyErr_SetString(PyExc_TypeError,
"expected integer from opener");
goto error;
}
self->fd = _PyLong_AsInt(fdobj);
Py_DECREF(fdobj);
if (self->fd < 0) {
if (!PyErr_Occurred()) {
PyErr_Format(PyExc_ValueError,
"opener returned %d", self->fd);
}
goto error;
}
}
fd_is_own = 1;
if (self->fd < 0) {
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
goto error;
}
#ifndef MS_WINDOWS
if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0)
goto error;
#endif
}
self->blksize = DEFAULT_BUFFER_SIZE;
Py_BEGIN_ALLOW_THREADS
fstat_result = _Py_fstat_noraise(self->fd, &fdfstat);
Py_END_ALLOW_THREADS
if (fstat_result < 0) {
#ifdef MS_WINDOWS
if (GetLastError() == ERROR_INVALID_HANDLE) {
PyErr_SetFromWindowsErr(0);
#else
if (errno == EBADF) {
PyErr_SetFromErrno(PyExc_OSError);
#endif
goto error;
}
}
else {
#if defined(S_ISDIR) && defined(EISDIR)
if (S_ISDIR(fdfstat.st_mode)) {
errno = EISDIR;
PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
goto error;
}
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
if (fdfstat.st_blksize > 1)
self->blksize = fdfstat.st_blksize;
#endif
}
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
_setmode(self->fd, O_BINARY);
#endif
if (PyObject_SetAttr((PyObject *)self, &_Py_ID(name), nameobj) < 0)
goto error;
if (self->appending) {
PyObject *pos = portable_lseek(self, NULL, 2, true);
if (pos == NULL)
goto error;
Py_DECREF(pos);
}
goto done;
error:
ret = -1;
if (!fd_is_own)
self->fd = -1;
if (self->fd >= 0) {
PyObject *exc = PyErr_GetRaisedException();
internal_close(self);
_PyErr_ChainExceptions1(exc);
}
done:
#ifdef MS_WINDOWS
PyMem_Free(widename);
#endif
Py_CLEAR(stringobj);
return ret;
}
static int
fileio_traverse(fileio *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->dict);
return 0;
}
static int
fileio_clear(fileio *self)
{
Py_CLEAR(self->dict);
return 0;
}
static void
fileio_dealloc(fileio *self)
{
PyTypeObject *tp = Py_TYPE(self);
self->finalizing = 1;
if (_PyIOBase_finalize((PyObject *) self) < 0)
return;
_PyObject_GC_UNTRACK(self);
if (self->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) self);
(void)fileio_clear(self);
tp->tp_free((PyObject *)self);
Py_DECREF(tp);
}
static PyObject *
err_closed(void)
{
PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
return NULL;
}
static PyObject *
err_mode(_PyIO_State *state, const char *action)
{
return PyErr_Format(state->unsupported_operation,
"File not open for %s", action);
}
static PyObject *
_io_FileIO_fileno_impl(fileio *self)
{
if (self->fd < 0)
return err_closed();
return PyLong_FromLong((long) self->fd);
}
static PyObject *
_io_FileIO_readable_impl(fileio *self)
{
if (self->fd < 0)
return err_closed();
return PyBool_FromLong((long) self->readable);
}
static PyObject *
_io_FileIO_writable_impl(fileio *self)
{
if (self->fd < 0)
return err_closed();
return PyBool_FromLong((long) self->writable);
}
static PyObject *
_io_FileIO_seekable_impl(fileio *self)
{
if (self->fd < 0)
return err_closed();
if (self->seekable < 0) {
PyObject *pos = portable_lseek(self, NULL, SEEK_CUR, false);
assert(self->seekable >= 0);
if (pos == NULL) {
PyErr_Clear();
}
else {
Py_DECREF(pos);
}
}
return PyBool_FromLong((long) self->seekable);
}
static PyObject *
_io_FileIO_readinto_impl(fileio *self, PyTypeObject *cls, Py_buffer *buffer)
{
Py_ssize_t n;
int err;
if (self->fd < 0)
return err_closed();
if (!self->readable) {
_PyIO_State *state = get_io_state_by_cls(cls);
return err_mode(state, "reading");
}
n = _Py_read(self->fd, buffer->buf, buffer->len);
err = errno;
if (n == -1) {
if (err == EAGAIN) {
PyErr_Clear();
Py_RETURN_NONE;
}
return NULL;
}
return PyLong_FromSsize_t(n);
}
static size_t
new_buffersize(fileio *self, size_t currentsize)
{
size_t addend;
assert(currentsize <= PY_SSIZE_T_MAX);
if (currentsize > 65536)
addend = currentsize >> 3;
else
addend = 256 + currentsize;
if (addend < SMALLCHUNK)
addend = SMALLCHUNK;
return addend + currentsize;
}
static PyObject *
_io_FileIO_readall_impl(fileio *self)
{
struct _Py_stat_struct status;
Py_off_t pos, end;
PyObject *result;
Py_ssize_t bytes_read = 0;
Py_ssize_t n;
size_t bufsize;
int fstat_result;
if (self->fd < 0)
return err_closed();
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
#ifdef MS_WINDOWS
pos = _lseeki64(self->fd, 0L, SEEK_CUR);
#else
pos = lseek(self->fd, 0L, SEEK_CUR);
#endif
_Py_END_SUPPRESS_IPH
fstat_result = _Py_fstat_noraise(self->fd, &status);
Py_END_ALLOW_THREADS
if (fstat_result == 0)
end = status.st_size;
else
end = (Py_off_t)-1;
if (end > 0 && end >= pos && pos >= 0 && end - pos < PY_SSIZE_T_MAX) {
bufsize = (size_t)(end - pos + 1);
} else {
bufsize = SMALLCHUNK;
}
result = PyBytes_FromStringAndSize(NULL, bufsize);
if (result == NULL)
return NULL;
while (1) {
if (bytes_read >= (Py_ssize_t)bufsize) {
bufsize = new_buffersize(self, bytes_read);
if (bufsize > PY_SSIZE_T_MAX || bufsize <= 0) {
PyErr_SetString(PyExc_OverflowError,
"unbounded read returned more bytes "
"than a Python bytes object can hold");
Py_DECREF(result);
return NULL;
}
if (PyBytes_GET_SIZE(result) < (Py_ssize_t)bufsize) {
if (_PyBytes_Resize(&result, bufsize) < 0)
return NULL;
}
}
n = _Py_read(self->fd,
PyBytes_AS_STRING(result) + bytes_read,
bufsize - bytes_read);
if (n == 0)
break;
if (n == -1) {
if (errno == EAGAIN) {
PyErr_Clear();
if (bytes_read > 0)
break;
Py_DECREF(result);
Py_RETURN_NONE;
}
Py_DECREF(result);
return NULL;
}
bytes_read += n;
pos += n;
}
if (PyBytes_GET_SIZE(result) > bytes_read) {
if (_PyBytes_Resize(&result, bytes_read) < 0)
return NULL;
}
return result;
}
static PyObject *
_io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size)
{
char *ptr;
Py_ssize_t n;
PyObject *bytes;
if (self->fd < 0)
return err_closed();
if (!self->readable) {
_PyIO_State *state = get_io_state_by_cls(cls);
return err_mode(state, "reading");
}
if (size < 0)
return _io_FileIO_readall_impl(self);
if (size > _PY_READ_MAX) {
size = _PY_READ_MAX;
}
bytes = PyBytes_FromStringAndSize(NULL, size);
if (bytes == NULL)
return NULL;
ptr = PyBytes_AS_STRING(bytes);
n = _Py_read(self->fd, ptr, size);
if (n == -1) {
int err = errno;
Py_DECREF(bytes);
if (err == EAGAIN) {
PyErr_Clear();
Py_RETURN_NONE;
}
return NULL;
}
if (n != size) {
if (_PyBytes_Resize(&bytes, n) < 0) {
Py_CLEAR(bytes);
return NULL;
}
}
return (PyObject *) bytes;
}
static PyObject *
_io_FileIO_write_impl(fileio *self, PyTypeObject *cls, Py_buffer *b)
{
Py_ssize_t n;
int err;
if (self->fd < 0)
return err_closed();
if (!self->writable) {
_PyIO_State *state = get_io_state_by_cls(cls);
return err_mode(state, "writing");
}
n = _Py_write(self->fd, b->buf, b->len);
err = errno;
if (n < 0) {
if (err == EAGAIN) {
PyErr_Clear();
Py_RETURN_NONE;
}
return NULL;
}
return PyLong_FromSsize_t(n);
}
static PyObject *
portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error)
{
Py_off_t pos, res;
int fd = self->fd;
#ifdef SEEK_SET
switch (whence) {
#if SEEK_SET != 0
case 0: whence = SEEK_SET; break;
#endif
#if SEEK_CUR != 1
case 1: whence = SEEK_CUR; break;
#endif
#if SEEK_END != 2
case 2: whence = SEEK_END; break;
#endif
}
#endif
if (posobj == NULL) {
pos = 0;
}
else {
#if defined(HAVE_LARGEFILE_SUPPORT)
pos = PyLong_AsLongLong(posobj);
#else
pos = PyLong_AsLong(posobj);
#endif
if (PyErr_Occurred())
return NULL;
}
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
#ifdef MS_WINDOWS
res = _lseeki64(fd, pos, whence);
#else
res = lseek(fd, pos, whence);
#endif
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (self->seekable < 0) {
self->seekable = (res >= 0);
}
if (res < 0) {
if (suppress_pipe_error && errno == ESPIPE) {
res = 0;
} else {
return PyErr_SetFromErrno(PyExc_OSError);
}
}
#if defined(HAVE_LARGEFILE_SUPPORT)
return PyLong_FromLongLong(res);
#else
return PyLong_FromLong(res);
#endif
}
static PyObject *
_io_FileIO_seek_impl(fileio *self, PyObject *pos, int whence)
{
if (self->fd < 0)
return err_closed();
return portable_lseek(self, pos, whence, false);
}
static PyObject *
_io_FileIO_tell_impl(fileio *self)
{
if (self->fd < 0)
return err_closed();
return portable_lseek(self, NULL, 1, false);
}
#ifdef HAVE_FTRUNCATE
static PyObject *
_io_FileIO_truncate_impl(fileio *self, PyTypeObject *cls, PyObject *posobj)
{
Py_off_t pos;
int ret;
int fd;
fd = self->fd;
if (fd < 0)
return err_closed();
if (!self->writable) {
_PyIO_State *state = get_io_state_by_cls(cls);
return err_mode(state, "writing");
}
if (posobj == Py_None) {
posobj = portable_lseek(self, NULL, 1, false);
if (posobj == NULL)
return NULL;
}
else {
Py_INCREF(posobj);
}
#if defined(HAVE_LARGEFILE_SUPPORT)
pos = PyLong_AsLongLong(posobj);
#else
pos = PyLong_AsLong(posobj);
#endif
if (PyErr_Occurred()){
Py_DECREF(posobj);
return NULL;
}
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
errno = 0;
#ifdef MS_WINDOWS
ret = _chsize_s(fd, pos);
#else
ret = ftruncate(fd, pos);
#endif
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
if (ret != 0) {
Py_DECREF(posobj);
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
return posobj;
}
#endif
static const char *
mode_string(fileio *self)
{
if (self->created) {
if (self->readable)
return "xb+";
else
return "xb";
}
if (self->appending) {
if (self->readable)
return "ab+";
else
return "ab";
}
else if (self->readable) {
if (self->writable)
return "rb+";
else
return "rb";
}
else
return "wb";
}
static PyObject *
fileio_repr(fileio *self)
{
PyObject *nameobj, *res;
if (self->fd < 0)
return PyUnicode_FromFormat("<_io.FileIO [closed]>");
if (_PyObject_LookupAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 0) {
return NULL;
}
if (nameobj == NULL) {
res = PyUnicode_FromFormat(
"<_io.FileIO fd=%d mode='%s' closefd=%s>",
self->fd, mode_string(self), self->closefd ? "True" : "False");
}
else {
int status = Py_ReprEnter((PyObject *)self);
res = NULL;
if (status == 0) {
res = PyUnicode_FromFormat(
"<_io.FileIO name=%R mode='%s' closefd=%s>",
nameobj, mode_string(self), self->closefd ? "True" : "False");
Py_ReprLeave((PyObject *)self);
}
else if (status > 0) {
PyErr_Format(PyExc_RuntimeError,
"reentrant call inside %s.__repr__",
Py_TYPE(self)->tp_name);
}
Py_DECREF(nameobj);
}
return res;
}
static PyObject *
_io_FileIO_isatty_impl(fileio *self)
{
long res;
if (self->fd < 0)
return err_closed();
Py_BEGIN_ALLOW_THREADS
_Py_BEGIN_SUPPRESS_IPH
res = isatty(self->fd);
_Py_END_SUPPRESS_IPH
Py_END_ALLOW_THREADS
return PyBool_FromLong(res);
}
#include "clinic/fileio.c.h"
static PyMethodDef fileio_methods[] = {
_IO_FILEIO_READ_METHODDEF
_IO_FILEIO_READALL_METHODDEF
_IO_FILEIO_READINTO_METHODDEF
_IO_FILEIO_WRITE_METHODDEF
_IO_FILEIO_SEEK_METHODDEF
_IO_FILEIO_TELL_METHODDEF
_IO_FILEIO_TRUNCATE_METHODDEF
_IO_FILEIO_CLOSE_METHODDEF
_IO_FILEIO_SEEKABLE_METHODDEF
_IO_FILEIO_READABLE_METHODDEF
_IO_FILEIO_WRITABLE_METHODDEF
_IO_FILEIO_FILENO_METHODDEF
_IO_FILEIO_ISATTY_METHODDEF
{"_dealloc_warn", (PyCFunction)fileio_dealloc_warn, METH_O, NULL},
{"__reduce__", _PyIOBase_cannot_pickle, METH_VARARGS},
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_VARARGS},
{NULL, NULL}
};
static PyObject *
get_closed(fileio *self, void *closure)
{
return PyBool_FromLong((long)(self->fd < 0));
}
static PyObject *
get_closefd(fileio *self, void *closure)
{
return PyBool_FromLong((long)(self->closefd));
}
static PyObject *
get_mode(fileio *self, void *closure)
{
return PyUnicode_FromString(mode_string(self));
}
static PyGetSetDef fileio_getsetlist[] = {
{"closed", (getter)get_closed, NULL, "True if the file is closed"},
{"closefd", (getter)get_closefd, NULL,
"True if the file descriptor will be closed by close()."},
{"mode", (getter)get_mode, NULL, "String giving the file mode"},
{NULL},
};
static PyMemberDef fileio_members[] = {
{"_blksize", T_UINT, offsetof(fileio, blksize), 0},
{"_finalizing", T_BOOL, offsetof(fileio, finalizing), 0},
{"__weaklistoffset__", T_PYSSIZET, offsetof(fileio, weakreflist), READONLY},
{"__dictoffset__", T_PYSSIZET, offsetof(fileio, dict), READONLY},
{NULL}
};
static PyType_Slot fileio_slots[] = {
{Py_tp_dealloc, fileio_dealloc},
{Py_tp_repr, fileio_repr},
{Py_tp_doc, (void *)_io_FileIO___init____doc__},
{Py_tp_traverse, fileio_traverse},
{Py_tp_clear, fileio_clear},
{Py_tp_methods, fileio_methods},
{Py_tp_members, fileio_members},
{Py_tp_getset, fileio_getsetlist},
{Py_tp_init, _io_FileIO___init__},
{Py_tp_new, fileio_new},
{0, NULL},
};
PyType_Spec fileio_spec = {
.name = "_io.FileIO",
.basicsize = sizeof(fileio),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_IMMUTABLETYPE),
.slots = fileio_slots,
};