Coverage Report

Created: 2025-10-12 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Modules/_io/fileio.c
Line
Count
Source
1
/* Author: Daniel Stutzbach */
2
3
#include "Python.h"
4
#include "pycore_fileutils.h"     // _Py_BEGIN_SUPPRESS_IPH
5
#include "pycore_object.h"        // _PyObject_GC_UNTRACK()
6
#include "pycore_pyerrors.h"      // _PyErr_ChainExceptions1()
7
#include "pycore_weakref.h"       // FT_CLEAR_WEAKREFS()
8
9
#include <stdbool.h>              // bool
10
#ifdef HAVE_UNISTD_H
11
#  include <unistd.h>             // lseek()
12
#endif
13
#ifdef HAVE_SYS_TYPES_H
14
#  include <sys/types.h>
15
#endif
16
#ifdef HAVE_IO_H
17
#  include <io.h>
18
#endif
19
#ifdef HAVE_FCNTL_H
20
#  include <fcntl.h>              // open()
21
#endif
22
23
#include "_iomodule.h"
24
25
/*
26
 * Known likely problems:
27
 *
28
 * - Files larger then 2**32-1
29
 * - Files with unicode filenames
30
 * - Passing numbers greater than 2**32-1 when an integer is expected
31
 * - Making it work on Windows and other oddball platforms
32
 *
33
 * To Do:
34
 *
35
 * - autoconfify header file inclusion
36
 */
37
38
#ifdef MS_WINDOWS
39
   // can simulate truncate with Win32 API functions; see file_truncate
40
#  define HAVE_FTRUNCATE
41
#  ifndef WIN32_LEAN_AND_MEAN
42
#    define WIN32_LEAN_AND_MEAN
43
#  endif
44
#  include <windows.h>
45
#endif
46
47
#if BUFSIZ < (8*1024)
48
#  define SMALLCHUNK (8*1024)
49
#elif (BUFSIZ >= (2 << 25))
50
#  error "unreasonable BUFSIZ > 64 MiB defined"
51
#else
52
3
#  define SMALLCHUNK BUFSIZ
53
#endif
54
55
/* Size at which a buffer is considered "large" and behavior should change to
56
   avoid excessive memory allocation */
57
1.02k
#define LARGE_BUFFER_CUTOFF_SIZE 65536
58
59
/*[clinic input]
60
module _io
61
class _io.FileIO "fileio *" "clinic_state()->PyFileIO_Type"
62
[clinic start generated code]*/
63
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ac25ec278f4d6703]*/
64
65
typedef struct {
66
    PyObject_HEAD
67
    int fd;
68
    unsigned int created : 1;
69
    unsigned int readable : 1;
70
    unsigned int writable : 1;
71
    unsigned int appending : 1;
72
    signed int seekable : 2; /* -1 means unknown */
73
    unsigned int closefd : 1;
74
    char finalizing;
75
    /* Stat result which was grabbed at file open, useful for optimizing common
76
       File I/O patterns to be more efficient. This is only guidance / an
77
       estimate, as it is subject to Time-Of-Check to Time-Of-Use (TOCTOU)
78
       issues / bugs. Both the underlying file descriptor and file may be
79
       modified outside of the fileio object / Python (ex. gh-90102, GH-121941,
80
       gh-109523). */
81
    struct _Py_stat_struct *stat_atopen;
82
    PyObject *weakreflist;
83
    PyObject *dict;
84
} fileio;
85
86
#define PyFileIO_Check(state, op) (PyObject_TypeCheck((op), state->PyFileIO_Type))
87
24.9k
#define PyFileIO_CAST(op) ((fileio *)(op))
88
89
/* Forward declarations */
90
static PyObject* portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error);
91
92
int
93
_PyFileIO_closed(PyObject *self)
94
2.07k
{
95
2.07k
    return (PyFileIO_CAST(self)->fd < 0);
96
2.07k
}
97
98
/* Because this can call arbitrary code, it shouldn't be called when
99
   the refcount is 0 (that is, not directly from tp_dealloc unless
100
   the refcount has been temporarily re-incremented). */
101
static PyObject *
102
fileio_dealloc_warn(PyObject *op, PyObject *source)
103
0
{
104
0
    fileio *self = PyFileIO_CAST(op);
105
0
    if (self->fd >= 0 && self->closefd) {
106
0
        PyObject *exc = PyErr_GetRaisedException();
107
0
        if (PyErr_ResourceWarning(source, 1, "unclosed file %R", source)) {
108
            /* Spurious errors can appear at shutdown */
109
0
            if (PyErr_ExceptionMatches(PyExc_Warning)) {
110
0
                PyErr_FormatUnraisable("Exception ignored "
111
0
                                       "while finalizing file %R", self);
112
0
            }
113
0
        }
114
0
        PyErr_SetRaisedException(exc);
115
0
    }
116
0
    Py_RETURN_NONE;
117
0
}
118
119
/* Returns 0 on success, -1 with exception set on failure. */
120
static int
121
internal_close(fileio *self)
122
1.27k
{
123
1.27k
    int err = 0;
124
1.27k
    int save_errno = 0;
125
1.27k
    if (self->fd >= 0) {
126
1.27k
        int fd = self->fd;
127
1.27k
        self->fd = -1;
128
        /* fd is accessible and someone else may have closed it */
129
1.27k
        Py_BEGIN_ALLOW_THREADS
130
1.27k
        _Py_BEGIN_SUPPRESS_IPH
131
1.27k
        err = close(fd);
132
1.27k
        if (err < 0)
133
0
            save_errno = errno;
134
1.27k
        _Py_END_SUPPRESS_IPH
135
1.27k
        Py_END_ALLOW_THREADS
136
1.27k
    }
137
1.27k
    PyMem_Free(self->stat_atopen);
138
1.27k
    self->stat_atopen = NULL;
139
1.27k
    if (err < 0) {
140
0
        errno = save_errno;
141
0
        PyErr_SetFromErrno(PyExc_OSError);
142
0
        return -1;
143
0
    }
144
1.27k
    return 0;
145
1.27k
}
146
147
/*[clinic input]
148
_io.FileIO.close
149
150
    cls: defining_class
151
    /
152
153
Close the file.
154
155
A closed file cannot be used for further I/O operations.  close() may be
156
called more than once without error.
157
[clinic start generated code]*/
158
159
static PyObject *
160
_io_FileIO_close_impl(fileio *self, PyTypeObject *cls)
161
/*[clinic end generated code: output=c30cbe9d1f23ca58 input=70da49e63db7c64d]*/
162
1.27k
{
163
1.27k
    PyObject *res;
164
1.27k
    int rc;
165
1.27k
    _PyIO_State *state = get_io_state_by_cls(cls);
166
1.27k
    res = PyObject_CallMethodOneArg((PyObject*)state->PyRawIOBase_Type,
167
1.27k
                                     &_Py_ID(close), (PyObject *)self);
168
1.27k
    if (!self->closefd) {
169
0
        self->fd = -1;
170
0
        return res;
171
0
    }
172
173
1.27k
    PyObject *exc = NULL;
174
1.27k
    if (res == NULL) {
175
0
        exc = PyErr_GetRaisedException();
176
0
    }
177
1.27k
    if (self->finalizing) {
178
0
        PyObject *r = fileio_dealloc_warn((PyObject*)self, (PyObject *) self);
179
0
        if (r) {
180
0
            Py_DECREF(r);
181
0
        }
182
0
        else {
183
0
            PyErr_Clear();
184
0
        }
185
0
    }
186
1.27k
    rc = internal_close(self);
187
1.27k
    if (res == NULL) {
188
0
        _PyErr_ChainExceptions1(exc);
189
0
    }
190
1.27k
    if (rc < 0) {
191
0
        Py_CLEAR(res);
192
0
    }
193
1.27k
    return res;
194
1.27k
}
195
196
static PyObject *
197
fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
198
1.32k
{
199
1.32k
    assert(type != NULL && type->tp_alloc != NULL);
200
201
1.32k
    fileio *self = (fileio *) type->tp_alloc(type, 0);
202
1.32k
    if (self == NULL) {
203
0
        return NULL;
204
0
    }
205
206
1.32k
    self->fd = -1;
207
1.32k
    self->created = 0;
208
1.32k
    self->readable = 0;
209
1.32k
    self->writable = 0;
210
1.32k
    self->appending = 0;
211
1.32k
    self->seekable = -1;
212
1.32k
    self->stat_atopen = NULL;
213
1.32k
    self->closefd = 1;
214
1.32k
    self->weakreflist = NULL;
215
1.32k
    return (PyObject *) self;
216
1.32k
}
217
218
#ifdef O_CLOEXEC
219
extern int _Py_open_cloexec_works;
220
#endif
221
222
/*[clinic input]
223
_io.FileIO.__init__
224
    file as nameobj: object
225
    mode: str = "r"
226
    closefd: bool = True
227
    opener: object = None
228
229
Open a file.
230
231
The mode can be 'r' (default), 'w', 'x' or 'a' for reading,
232
writing, exclusive creation or appending.  The file will be created if it
233
doesn't exist when opened for writing or appending; it will be truncated
234
when opened for writing.  A FileExistsError will be raised if it already
235
exists when opened for creating. Opening a file for creating implies
236
writing so this mode behaves in a similar way to 'w'.Add a '+' to the mode
237
to allow simultaneous reading and writing. A custom opener can be used by
238
passing a callable as *opener*. The underlying file descriptor for the file
239
object is then obtained by calling opener with (*name*, *flags*).
240
*opener* must return an open file descriptor (passing os.open as *opener*
241
results in functionality similar to passing None).
242
[clinic start generated code]*/
243
244
static int
245
_io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
246
                         int closefd, PyObject *opener)
247
/*[clinic end generated code: output=23413f68e6484bbd input=588aac967e0ba74b]*/
248
1.32k
{
249
#ifdef MS_WINDOWS
250
    wchar_t *widename = NULL;
251
#else
252
1.32k
    const char *name = NULL;
253
1.32k
#endif
254
1.32k
    PyObject *stringobj = NULL;
255
1.32k
    const char *s;
256
1.32k
    int ret = 0;
257
1.32k
    int rwa = 0, plus = 0;
258
1.32k
    int flags = 0;
259
1.32k
    int fd = -1;
260
1.32k
    int fd_is_own = 0;
261
1.32k
#ifdef O_CLOEXEC
262
1.32k
    int *atomic_flag_works = &_Py_open_cloexec_works;
263
#elif !defined(MS_WINDOWS)
264
    int *atomic_flag_works = NULL;
265
#endif
266
1.32k
    int fstat_result;
267
1.32k
    int async_err = 0;
268
269
#ifdef Py_DEBUG
270
    _PyIO_State *state = find_io_state_by_def(Py_TYPE(self));
271
    assert(PyFileIO_Check(state, self));
272
#endif
273
1.32k
    if (self->fd >= 0) {
274
0
        if (self->closefd) {
275
            /* Have to close the existing file first. */
276
0
            if (internal_close(self) < 0) {
277
0
                return -1;
278
0
            }
279
0
        }
280
0
        else
281
0
            self->fd = -1;
282
0
    }
283
284
1.32k
    if (PyBool_Check(nameobj)) {
285
0
        if (PyErr_WarnEx(PyExc_RuntimeWarning,
286
0
                "bool is used as a file descriptor", 1))
287
0
        {
288
0
            return -1;
289
0
        }
290
0
    }
291
1.32k
    fd = PyLong_AsInt(nameobj);
292
1.32k
    if (fd < 0) {
293
1.03k
        if (!PyErr_Occurred()) {
294
0
            PyErr_SetString(PyExc_ValueError,
295
0
                            "negative file descriptor");
296
0
            return -1;
297
0
        }
298
1.03k
        PyErr_Clear();
299
1.03k
    }
300
301
1.32k
    if (fd < 0) {
302
#ifdef MS_WINDOWS
303
        if (!PyUnicode_FSDecoder(nameobj, &stringobj)) {
304
            return -1;
305
        }
306
        widename = PyUnicode_AsWideCharString(stringobj, NULL);
307
        if (widename == NULL)
308
            return -1;
309
#else
310
1.03k
        if (!PyUnicode_FSConverter(nameobj, &stringobj)) {
311
0
            return -1;
312
0
        }
313
1.03k
        name = PyBytes_AS_STRING(stringobj);
314
1.03k
#endif
315
1.03k
    }
316
317
1.32k
    s = mode;
318
2.88k
    while (*s) {
319
1.56k
        switch (*s++) {
320
0
        case 'x':
321
0
            if (rwa) {
322
0
            bad_mode:
323
0
                PyErr_SetString(PyExc_ValueError,
324
0
                                "Must have exactly one of create/read/write/append "
325
0
                                "mode and at most one plus");
326
0
                goto error;
327
0
            }
328
0
            rwa = 1;
329
0
            self->created = 1;
330
0
            self->writable = 1;
331
0
            flags |= O_EXCL | O_CREAT;
332
0
            break;
333
1.05k
        case 'r':
334
1.05k
            if (rwa)
335
0
                goto bad_mode;
336
1.05k
            rwa = 1;
337
1.05k
            self->readable = 1;
338
1.05k
            break;
339
272
        case 'w':
340
272
            if (rwa)
341
0
                goto bad_mode;
342
272
            rwa = 1;
343
272
            self->writable = 1;
344
272
            flags |= O_CREAT | O_TRUNC;
345
272
            break;
346
0
        case 'a':
347
0
            if (rwa)
348
0
                goto bad_mode;
349
0
            rwa = 1;
350
0
            self->writable = 1;
351
0
            self->appending = 1;
352
0
            flags |= O_APPEND | O_CREAT;
353
0
            break;
354
240
        case 'b':
355
240
            break;
356
0
        case '+':
357
0
            if (plus)
358
0
                goto bad_mode;
359
0
            self->readable = self->writable = 1;
360
0
            plus = 1;
361
0
            break;
362
0
        default:
363
0
            PyErr_Format(PyExc_ValueError,
364
0
                         "invalid mode: %.200s", mode);
365
0
            goto error;
366
1.56k
        }
367
1.56k
    }
368
369
1.32k
    if (!rwa)
370
0
        goto bad_mode;
371
372
1.32k
    if (self->readable && self->writable)
373
0
        flags |= O_RDWR;
374
1.32k
    else if (self->readable)
375
1.05k
        flags |= O_RDONLY;
376
272
    else
377
272
        flags |= O_WRONLY;
378
379
#ifdef O_BINARY
380
    flags |= O_BINARY;
381
#endif
382
383
#ifdef MS_WINDOWS
384
    flags |= O_NOINHERIT;
385
#elif defined(O_CLOEXEC)
386
1.32k
    flags |= O_CLOEXEC;
387
1.32k
#endif
388
389
1.32k
    if (PySys_Audit("open", "Osi", nameobj, mode, flags) < 0) {
390
0
        goto error;
391
0
    }
392
393
1.32k
    if (fd >= 0) {
394
288
        self->fd = fd;
395
288
        self->closefd = closefd;
396
288
    }
397
1.03k
    else {
398
1.03k
        self->closefd = 1;
399
1.03k
        if (!closefd) {
400
0
            PyErr_SetString(PyExc_ValueError,
401
0
                "Cannot use closefd=False with file name");
402
0
            goto error;
403
0
        }
404
405
1.03k
        errno = 0;
406
1.03k
        if (opener == Py_None) {
407
1.03k
            do {
408
1.03k
                Py_BEGIN_ALLOW_THREADS
409
#ifdef MS_WINDOWS
410
                self->fd = _wopen(widename, flags, 0666);
411
#else
412
1.03k
                self->fd = open(name, flags, 0666);
413
1.03k
#endif
414
1.03k
                Py_END_ALLOW_THREADS
415
1.03k
            } while (self->fd < 0 && errno == EINTR &&
416
0
                     !(async_err = PyErr_CheckSignals()));
417
418
1.03k
            if (async_err)
419
0
                goto error;
420
421
1.03k
            if (self->fd < 0) {
422
0
                PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
423
0
                goto error;
424
0
            }
425
1.03k
        }
426
0
        else {
427
0
            PyObject *fdobj;
428
429
0
#ifndef MS_WINDOWS
430
            /* the opener may clear the atomic flag */
431
0
            atomic_flag_works = NULL;
432
0
#endif
433
434
0
            fdobj = PyObject_CallFunction(opener, "Oi", nameobj, flags);
435
0
            if (fdobj == NULL)
436
0
                goto error;
437
0
            if (!PyLong_Check(fdobj)) {
438
0
                Py_DECREF(fdobj);
439
0
                PyErr_SetString(PyExc_TypeError,
440
0
                        "expected integer from opener");
441
0
                goto error;
442
0
            }
443
444
0
            self->fd = PyLong_AsInt(fdobj);
445
0
            Py_DECREF(fdobj);
446
0
            if (self->fd < 0) {
447
0
                if (!PyErr_Occurred()) {
448
                    /* The opener returned a negative but didn't set an
449
                       exception.  See issue #27066 */
450
0
                    PyErr_Format(PyExc_ValueError,
451
0
                                 "opener returned %d", self->fd);
452
0
                }
453
0
                goto error;
454
0
            }
455
0
        }
456
1.03k
        fd_is_own = 1;
457
458
1.03k
#ifndef MS_WINDOWS
459
1.03k
        if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0)
460
0
            goto error;
461
1.03k
#endif
462
1.03k
    }
463
464
1.32k
    PyMem_Free(self->stat_atopen);
465
1.32k
    self->stat_atopen = PyMem_New(struct _Py_stat_struct, 1);
466
1.32k
    if (self->stat_atopen == NULL) {
467
0
        PyErr_NoMemory();
468
0
        goto error;
469
0
    }
470
1.32k
    Py_BEGIN_ALLOW_THREADS
471
1.32k
    fstat_result = _Py_fstat_noraise(self->fd, self->stat_atopen);
472
1.32k
    Py_END_ALLOW_THREADS
473
1.32k
    if (fstat_result < 0) {
474
        /* Tolerate fstat() errors other than EBADF.  See Issue #25717, where
475
        an anonymous file on a Virtual Box shared folder filesystem would
476
        raise ENOENT. */
477
#ifdef MS_WINDOWS
478
        if (GetLastError() == ERROR_INVALID_HANDLE) {
479
            PyErr_SetFromWindowsErr(0);
480
#else
481
0
        if (errno == EBADF) {
482
0
            PyErr_SetFromErrno(PyExc_OSError);
483
0
#endif
484
0
            goto error;
485
0
        }
486
487
0
        PyMem_Free(self->stat_atopen);
488
0
        self->stat_atopen = NULL;
489
0
    }
490
1.32k
    else {
491
1.32k
#if defined(S_ISDIR) && defined(EISDIR)
492
        /* On Unix, open will succeed for directories.
493
           In Python, there should be no file objects referring to
494
           directories, so we need a check.  */
495
1.32k
        if (S_ISDIR(self->stat_atopen->st_mode)) {
496
0
            errno = EISDIR;
497
0
            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
498
0
            goto error;
499
0
        }
500
1.32k
#endif /* defined(S_ISDIR) */
501
1.32k
    }
502
503
#if defined(MS_WINDOWS) || defined(__CYGWIN__)
504
    /* don't translate newlines (\r\n <=> \n) */
505
    _setmode(self->fd, O_BINARY);
506
#endif
507
508
1.32k
    if (PyObject_SetAttr((PyObject *)self, &_Py_ID(name), nameobj) < 0)
509
0
        goto error;
510
511
1.32k
    if (self->appending) {
512
        /* For consistent behaviour, we explicitly seek to the
513
           end of file (otherwise, it might be done only on the
514
           first write()). */
515
0
        PyObject *pos = portable_lseek(self, NULL, 2, true);
516
0
        if (pos == NULL)
517
0
            goto error;
518
0
        Py_DECREF(pos);
519
0
    }
520
521
1.32k
    goto done;
522
523
1.32k
 error:
524
0
    ret = -1;
525
0
    if (!fd_is_own)
526
0
        self->fd = -1;
527
0
    if (self->fd >= 0) {
528
0
        PyObject *exc = PyErr_GetRaisedException();
529
0
        internal_close(self);
530
0
        _PyErr_ChainExceptions1(exc);
531
0
    }
532
0
    PyMem_Free(self->stat_atopen);
533
0
    self->stat_atopen = NULL;
534
535
1.32k
 done:
536
#ifdef MS_WINDOWS
537
    PyMem_Free(widename);
538
#endif
539
1.32k
    Py_CLEAR(stringobj);
540
1.32k
    return ret;
541
0
}
542
543
static int
544
fileio_traverse(PyObject *op, visitproc visit, void *arg)
545
14.5k
{
546
14.5k
    fileio *self = PyFileIO_CAST(op);
547
14.5k
    Py_VISIT(Py_TYPE(self));
548
14.5k
    Py_VISIT(self->dict);
549
14.5k
    return 0;
550
14.5k
}
551
552
static int
553
fileio_clear(PyObject *op)
554
1.27k
{
555
1.27k
    fileio *self = PyFileIO_CAST(op);
556
1.27k
    Py_CLEAR(self->dict);
557
1.27k
    return 0;
558
1.27k
}
559
560
static void
561
fileio_dealloc(PyObject *op)
562
1.27k
{
563
1.27k
    fileio *self = PyFileIO_CAST(op);
564
1.27k
    self->finalizing = 1;
565
1.27k
    if (_PyIOBase_finalize(op) < 0) {
566
0
        return;
567
0
    }
568
569
1.27k
    _PyObject_GC_UNTRACK(self);
570
1.27k
    if (self->stat_atopen != NULL) {
571
0
        PyMem_Free(self->stat_atopen);
572
0
        self->stat_atopen = NULL;
573
0
    }
574
1.27k
    FT_CLEAR_WEAKREFS(op, self->weakreflist);
575
1.27k
    (void)fileio_clear(op);
576
577
1.27k
    PyTypeObject *tp = Py_TYPE(op);
578
1.27k
    tp->tp_free(op);
579
1.27k
    Py_DECREF(tp);
580
1.27k
}
581
582
static PyObject *
583
err_closed(void)
584
0
{
585
0
    PyErr_SetString(PyExc_ValueError, "I/O operation on closed file");
586
0
    return NULL;
587
0
}
588
589
static PyObject *
590
err_mode(_PyIO_State *state, const char *action)
591
0
{
592
0
    return PyErr_Format(state->unsupported_operation,
593
0
                        "File not open for %s", action);
594
0
}
595
596
/*[clinic input]
597
_io.FileIO.fileno
598
599
Return the underlying file descriptor (an integer).
600
[clinic start generated code]*/
601
602
static PyObject *
603
_io_FileIO_fileno_impl(fileio *self)
604
/*[clinic end generated code: output=a9626ce5398ece90 input=0b9b2de67335ada3]*/
605
0
{
606
0
    if (self->fd < 0)
607
0
        return err_closed();
608
0
    return PyLong_FromLong((long) self->fd);
609
0
}
610
611
/*[clinic input]
612
_io.FileIO.readable
613
614
True if file was opened in a read mode.
615
[clinic start generated code]*/
616
617
static PyObject *
618
_io_FileIO_readable_impl(fileio *self)
619
/*[clinic end generated code: output=640744a6150fe9ba input=a3fdfed6eea721c5]*/
620
1.06k
{
621
1.06k
    if (self->fd < 0)
622
0
        return err_closed();
623
1.06k
    return PyBool_FromLong((long) self->readable);
624
1.06k
}
625
626
/*[clinic input]
627
_io.FileIO.writable
628
629
True if file was opened in a write mode.
630
[clinic start generated code]*/
631
632
static PyObject *
633
_io_FileIO_writable_impl(fileio *self)
634
/*[clinic end generated code: output=96cefc5446e89977 input=c204a808ca2e1748]*/
635
64
{
636
64
    if (self->fd < 0)
637
0
        return err_closed();
638
64
    return PyBool_FromLong((long) self->writable);
639
64
}
640
641
/*[clinic input]
642
_io.FileIO.seekable
643
644
True if file supports random-access.
645
[clinic start generated code]*/
646
647
static PyObject *
648
_io_FileIO_seekable_impl(fileio *self)
649
/*[clinic end generated code: output=47909ca0a42e9287 input=c8e5554d2fd63c7f]*/
650
54
{
651
54
    if (self->fd < 0)
652
0
        return err_closed();
653
54
    if (self->seekable < 0) {
654
        /* portable_lseek() sets the seekable attribute */
655
0
        PyObject *pos = portable_lseek(self, NULL, SEEK_CUR, false);
656
0
        assert(self->seekable >= 0);
657
0
        if (pos == NULL) {
658
0
            PyErr_Clear();
659
0
        }
660
0
        else {
661
0
            Py_DECREF(pos);
662
0
        }
663
0
    }
664
54
    return PyBool_FromLong((long) self->seekable);
665
54
}
666
667
/*[clinic input]
668
_io.FileIO.readinto
669
    cls: defining_class
670
    buffer: Py_buffer(accept={rwbuffer})
671
    /
672
673
Same as RawIOBase.readinto().
674
[clinic start generated code]*/
675
676
static PyObject *
677
_io_FileIO_readinto_impl(fileio *self, PyTypeObject *cls, Py_buffer *buffer)
678
/*[clinic end generated code: output=97f0f3d69534db34 input=fd20323e18ce1ec8]*/
679
4
{
680
4
    Py_ssize_t n;
681
4
    int err;
682
683
4
    if (self->fd < 0)
684
0
        return err_closed();
685
4
    if (!self->readable) {
686
0
        _PyIO_State *state = get_io_state_by_cls(cls);
687
0
        return err_mode(state, "reading");
688
0
    }
689
690
4
    n = _Py_read(self->fd, buffer->buf, buffer->len);
691
    /* copy errno because PyBuffer_Release() can indirectly modify it */
692
4
    err = errno;
693
694
4
    if (n == -1) {
695
0
        if (err == EAGAIN) {
696
0
            PyErr_Clear();
697
0
            Py_RETURN_NONE;
698
0
        }
699
0
        return NULL;
700
0
    }
701
702
4
    return PyLong_FromSsize_t(n);
703
4
}
704
705
static size_t
706
new_buffersize(fileio *self, size_t currentsize)
707
0
{
708
0
    size_t addend;
709
710
    /* Expand the buffer by an amount proportional to the current size,
711
       giving us amortized linear-time behavior.  For bigger sizes, use a
712
       less-than-double growth factor to avoid excessive allocation. */
713
0
    assert(currentsize <= PY_SSIZE_T_MAX);
714
0
    if (currentsize > LARGE_BUFFER_CUTOFF_SIZE)
715
0
        addend = currentsize >> 3;
716
0
    else
717
0
        addend = 256 + currentsize;
718
0
    if (addend < SMALLCHUNK)
719
        /* Avoid tiny read() calls. */
720
0
        addend = SMALLCHUNK;
721
0
    return addend + currentsize;
722
0
}
723
724
/*[clinic input]
725
@permit_long_docstring_body
726
_io.FileIO.readall
727
728
Read all data from the file, returned as bytes.
729
730
Reads until either there is an error or read() returns size 0 (indicates EOF).
731
If the file is already at EOF, returns an empty bytes object.
732
733
In non-blocking mode, returns as much data as could be read before EAGAIN. If no
734
data is available (EAGAIN is returned before bytes are read) returns None.
735
[clinic start generated code]*/
736
737
static PyObject *
738
_io_FileIO_readall_impl(fileio *self)
739
/*[clinic end generated code: output=faa0292b213b4022 input=10d8b2ec403302dc]*/
740
1.03k
{
741
1.03k
    Py_off_t pos, end;
742
1.03k
    PyBytesWriter *writer;
743
1.03k
    Py_ssize_t bytes_read = 0;
744
1.03k
    Py_ssize_t n;
745
1.03k
    size_t bufsize;
746
747
1.03k
    if (self->fd < 0) {
748
0
        return err_closed();
749
0
    }
750
751
1.03k
    if (self->stat_atopen != NULL && self->stat_atopen->st_size < _PY_READ_MAX) {
752
1.03k
        end = (Py_off_t)self->stat_atopen->st_size;
753
1.03k
    }
754
0
    else {
755
0
        end = -1;
756
0
    }
757
1.03k
    if (end <= 0) {
758
        /* Use a default size and resize as needed. */
759
3
        bufsize = SMALLCHUNK;
760
3
    }
761
1.02k
    else {
762
        /* This is probably a real file. */
763
1.02k
        if (end > _PY_READ_MAX - 1) {
764
0
            bufsize = _PY_READ_MAX;
765
0
        }
766
1.02k
        else {
767
            /* In order to detect end of file, need a read() of at
768
               least 1 byte which returns size 0. Oversize the buffer
769
               by 1 byte so the I/O can be completed with two read()
770
               calls (one for all data, one for EOF) without needing
771
               to resize the buffer. */
772
1.02k
            bufsize = (size_t)end + 1;
773
1.02k
        }
774
775
        /* While a lot of code does open().read() to get the whole contents
776
           of a file it is possible a caller seeks/reads a ways into the file
777
           then calls readall() to get the rest, which would result in allocating
778
           more than required. Guard against that for larger files where we expect
779
           the I/O time to dominate anyways while keeping small files fast. */
780
1.02k
        if (bufsize > LARGE_BUFFER_CUTOFF_SIZE) {
781
59
            Py_BEGIN_ALLOW_THREADS
782
59
            _Py_BEGIN_SUPPRESS_IPH
783
#ifdef MS_WINDOWS
784
            pos = _lseeki64(self->fd, 0L, SEEK_CUR);
785
#else
786
59
            pos = lseek(self->fd, 0L, SEEK_CUR);
787
59
#endif
788
59
            _Py_END_SUPPRESS_IPH
789
59
            Py_END_ALLOW_THREADS
790
791
59
            if (end >= pos && pos >= 0 && (end - pos) < (_PY_READ_MAX - 1)) {
792
59
                bufsize = (size_t)(end - pos) + 1;
793
59
            }
794
59
        }
795
1.02k
    }
796
797
1.03k
    writer = PyBytesWriter_Create(bufsize);
798
1.03k
    if (writer == NULL) {
799
0
        return NULL;
800
0
    }
801
802
2.06k
    while (1) {
803
2.06k
        if (bytes_read >= (Py_ssize_t)bufsize) {
804
0
            bufsize = new_buffersize(self, bytes_read);
805
0
            if (bufsize > PY_SSIZE_T_MAX || bufsize <= 0) {
806
0
                PyErr_SetString(PyExc_OverflowError,
807
0
                                "unbounded read returned more bytes "
808
0
                                "than a Python bytes object can hold");
809
0
                PyBytesWriter_Discard(writer);
810
0
                return NULL;
811
0
            }
812
813
0
            if (PyBytesWriter_GetSize(writer) < (Py_ssize_t)bufsize) {
814
0
                if (PyBytesWriter_Resize(writer, bufsize) < 0)
815
0
                    return NULL;
816
0
            }
817
0
        }
818
819
2.06k
        n = _Py_read(self->fd,
820
2.06k
                     (char*)PyBytesWriter_GetData(writer) + bytes_read,
821
2.06k
                     bufsize - bytes_read);
822
823
2.06k
        if (n == 0)
824
1.03k
            break;
825
1.02k
        if (n == -1) {
826
0
            if (errno == EAGAIN) {
827
0
                PyErr_Clear();
828
0
                if (bytes_read > 0)
829
0
                    break;
830
0
                PyBytesWriter_Discard(writer);
831
0
                Py_RETURN_NONE;
832
0
            }
833
0
            PyBytesWriter_Discard(writer);
834
0
            return NULL;
835
0
        }
836
1.02k
        bytes_read += n;
837
1.02k
    }
838
839
1.03k
    return PyBytesWriter_FinishWithSize(writer, bytes_read);
840
1.03k
}
841
842
/*[clinic input]
843
@permit_long_docstring_body
844
_io.FileIO.read
845
    cls: defining_class
846
    size: Py_ssize_t(accept={int, NoneType}) = -1
847
    /
848
849
Read at most size bytes, returned as bytes.
850
851
If size is less than 0, read all bytes in the file making multiple read calls.
852
See ``FileIO.readall``.
853
854
Attempts to make only one system call, retrying only per PEP 475 (EINTR). This
855
means less data may be returned than requested.
856
857
In non-blocking mode, returns None if no data is available. Return an empty
858
bytes object at EOF.
859
[clinic start generated code]*/
860
861
static PyObject *
862
_io_FileIO_read_impl(fileio *self, PyTypeObject *cls, Py_ssize_t size)
863
/*[clinic end generated code: output=bbd749c7c224143e input=752d1ad3db8564a5]*/
864
0
{
865
0
    if (self->fd < 0)
866
0
        return err_closed();
867
0
    if (!self->readable) {
868
0
        _PyIO_State *state = get_io_state_by_cls(cls);
869
0
        return err_mode(state, "reading");
870
0
    }
871
872
0
    if (size < 0)
873
0
        return _io_FileIO_readall_impl(self);
874
875
0
    if (size > _PY_READ_MAX) {
876
0
        size = _PY_READ_MAX;
877
0
    }
878
879
0
    PyBytesWriter *writer = PyBytesWriter_Create(size);
880
0
    if (writer == NULL) {
881
0
        return NULL;
882
0
    }
883
0
    char *ptr = PyBytesWriter_GetData(writer);
884
885
0
    Py_ssize_t n = _Py_read(self->fd, ptr, size);
886
0
    if (n == -1) {
887
        // copy errno because PyBytesWriter_Discard() can indirectly modify it
888
0
        int err = errno;
889
0
        PyBytesWriter_Discard(writer);
890
0
        if (err == EAGAIN) {
891
0
            PyErr_Clear();
892
0
            Py_RETURN_NONE;
893
0
        }
894
0
        return NULL;
895
0
    }
896
897
0
    return PyBytesWriter_FinishWithSize(writer, n);
898
0
}
899
900
/*[clinic input]
901
_io.FileIO.write
902
    cls: defining_class
903
    b: Py_buffer
904
    /
905
906
Write buffer b to file, return number of bytes written.
907
908
Only makes one system call, so not all of the data may be written.
909
The number of bytes actually written is returned.  In non-blocking mode,
910
returns None if the write would block.
911
[clinic start generated code]*/
912
913
static PyObject *
914
_io_FileIO_write_impl(fileio *self, PyTypeObject *cls, Py_buffer *b)
915
/*[clinic end generated code: output=927e25be80f3b77b input=2776314f043088f5]*/
916
240
{
917
240
    Py_ssize_t n;
918
240
    int err;
919
920
240
    if (self->fd < 0)
921
0
        return err_closed();
922
240
    if (!self->writable) {
923
0
        _PyIO_State *state = get_io_state_by_cls(cls);
924
0
        return err_mode(state, "writing");
925
0
    }
926
927
240
    n = _Py_write(self->fd, b->buf, b->len);
928
    /* copy errno because PyBuffer_Release() can indirectly modify it */
929
240
    err = errno;
930
931
240
    if (n < 0) {
932
0
        if (err == EAGAIN) {
933
0
            PyErr_Clear();
934
0
            Py_RETURN_NONE;
935
0
        }
936
0
        return NULL;
937
0
    }
938
939
240
    return PyLong_FromSsize_t(n);
940
240
}
941
942
/* XXX Windows support below is likely incomplete */
943
944
/* Cribbed from posix_lseek() */
945
static PyObject *
946
portable_lseek(fileio *self, PyObject *posobj, int whence, bool suppress_pipe_error)
947
1.12k
{
948
1.12k
    Py_off_t pos, res;
949
1.12k
    int fd = self->fd;
950
951
1.12k
#ifdef SEEK_SET
952
    /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
953
1.12k
    switch (whence) {
954
#if SEEK_SET != 0
955
    case 0: whence = SEEK_SET; break;
956
#endif
957
#if SEEK_CUR != 1
958
    case 1: whence = SEEK_CUR; break;
959
#endif
960
#if SEEK_END != 2
961
    case 2: whence = SEEK_END; break;
962
#endif
963
1.12k
    }
964
1.12k
#endif /* SEEK_SET */
965
966
1.12k
    if (posobj == NULL) {
967
1.11k
        pos = 0;
968
1.11k
    }
969
6
    else {
970
#if defined(HAVE_LARGEFILE_SUPPORT)
971
        pos = PyLong_AsLongLong(posobj);
972
#else
973
6
        pos = PyLong_AsLong(posobj);
974
6
#endif
975
6
        if (PyErr_Occurred())
976
0
            return NULL;
977
6
    }
978
979
1.12k
    Py_BEGIN_ALLOW_THREADS
980
1.12k
    _Py_BEGIN_SUPPRESS_IPH
981
#ifdef MS_WINDOWS
982
    res = _lseeki64(fd, pos, whence);
983
#else
984
1.12k
    res = lseek(fd, pos, whence);
985
1.12k
#endif
986
1.12k
    _Py_END_SUPPRESS_IPH
987
1.12k
    Py_END_ALLOW_THREADS
988
989
1.12k
    if (self->seekable < 0) {
990
1.08k
        self->seekable = (res >= 0);
991
1.08k
    }
992
993
1.12k
    if (res < 0) {
994
0
        if (suppress_pipe_error && errno == ESPIPE) {
995
0
            res = 0;
996
0
        } else {
997
0
            return PyErr_SetFromErrno(PyExc_OSError);
998
0
        }
999
0
    }
1000
1001
#if defined(HAVE_LARGEFILE_SUPPORT)
1002
    return PyLong_FromLongLong(res);
1003
#else
1004
1.12k
    return PyLong_FromLong(res);
1005
1.12k
#endif
1006
1.12k
}
1007
1008
/*[clinic input]
1009
@permit_long_docstring_body
1010
_io.FileIO.seek
1011
    pos: object
1012
    whence: int = 0
1013
    /
1014
1015
Move to new file position and return the file position.
1016
1017
Argument offset is a byte count.  Optional argument whence defaults to
1018
SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values
1019
are SEEK_CUR or 1 (move relative to current position, positive or negative),
1020
and SEEK_END or 2 (move relative to end of file, usually negative, although
1021
many platforms allow seeking beyond the end of a file).
1022
1023
Note that not all file objects are seekable.
1024
[clinic start generated code]*/
1025
1026
static PyObject *
1027
_io_FileIO_seek_impl(fileio *self, PyObject *pos, int whence)
1028
/*[clinic end generated code: output=c976acdf054e6655 input=f077c492a84c9e62]*/
1029
6
{
1030
6
    if (self->fd < 0)
1031
0
        return err_closed();
1032
1033
6
    return portable_lseek(self, pos, whence, false);
1034
6
}
1035
1036
/*[clinic input]
1037
_io.FileIO.tell
1038
1039
Current file position.
1040
1041
Can raise OSError for non seekable files.
1042
[clinic start generated code]*/
1043
1044
static PyObject *
1045
_io_FileIO_tell_impl(fileio *self)
1046
/*[clinic end generated code: output=ffe2147058809d0b input=807e24ead4cec2f9]*/
1047
1.11k
{
1048
1.11k
    if (self->fd < 0)
1049
0
        return err_closed();
1050
1051
1.11k
    return portable_lseek(self, NULL, 1, false);
1052
1.11k
}
1053
1054
#ifdef HAVE_FTRUNCATE
1055
/*[clinic input]
1056
_io.FileIO.truncate
1057
    cls: defining_class
1058
    size as posobj: object = None
1059
    /
1060
1061
Truncate the file to at most size bytes and return the truncated size.
1062
1063
Size defaults to the current file position, as returned by tell().
1064
The current file position is changed to the value of size.
1065
[clinic start generated code]*/
1066
1067
static PyObject *
1068
_io_FileIO_truncate_impl(fileio *self, PyTypeObject *cls, PyObject *posobj)
1069
/*[clinic end generated code: output=d936732a49e8d5a2 input=c367fb45d6bb2c18]*/
1070
0
{
1071
0
    Py_off_t pos;
1072
0
    int ret;
1073
0
    int fd;
1074
1075
0
    fd = self->fd;
1076
0
    if (fd < 0)
1077
0
        return err_closed();
1078
0
    if (!self->writable) {
1079
0
        _PyIO_State *state = get_io_state_by_cls(cls);
1080
0
        return err_mode(state, "writing");
1081
0
    }
1082
1083
0
    if (posobj == Py_None) {
1084
        /* Get the current position. */
1085
0
        posobj = portable_lseek(self, NULL, 1, false);
1086
0
        if (posobj == NULL)
1087
0
            return NULL;
1088
0
    }
1089
0
    else {
1090
0
        Py_INCREF(posobj);
1091
0
    }
1092
1093
#if defined(HAVE_LARGEFILE_SUPPORT)
1094
    pos = PyLong_AsLongLong(posobj);
1095
#else
1096
0
    pos = PyLong_AsLong(posobj);
1097
0
#endif
1098
0
    if (PyErr_Occurred()){
1099
0
        Py_DECREF(posobj);
1100
0
        return NULL;
1101
0
    }
1102
1103
0
    Py_BEGIN_ALLOW_THREADS
1104
0
    _Py_BEGIN_SUPPRESS_IPH
1105
0
    errno = 0;
1106
#ifdef MS_WINDOWS
1107
    ret = _chsize_s(fd, pos);
1108
#else
1109
0
    ret = ftruncate(fd, pos);
1110
0
#endif
1111
0
    _Py_END_SUPPRESS_IPH
1112
0
    Py_END_ALLOW_THREADS
1113
1114
0
    if (ret != 0) {
1115
0
        PyErr_SetFromErrno(PyExc_OSError);
1116
0
        Py_DECREF(posobj);
1117
0
        return NULL;
1118
0
    }
1119
1120
    /* Since the file was truncated, its size at open is no longer accurate
1121
       as an estimate. Clear out the stat result, and rely on dynamic resize
1122
       code if a readall is requested. */
1123
0
    if (self->stat_atopen != NULL) {
1124
0
        PyMem_Free(self->stat_atopen);
1125
0
        self->stat_atopen = NULL;
1126
0
    }
1127
1128
0
    return posobj;
1129
0
}
1130
#endif /* HAVE_FTRUNCATE */
1131
1132
static const char *
1133
mode_string(fileio *self)
1134
0
{
1135
0
    if (self->created) {
1136
0
        if (self->readable)
1137
0
            return "xb+";
1138
0
        else
1139
0
            return "xb";
1140
0
    }
1141
0
    if (self->appending) {
1142
0
        if (self->readable)
1143
0
            return "ab+";
1144
0
        else
1145
0
            return "ab";
1146
0
    }
1147
0
    else if (self->readable) {
1148
0
        if (self->writable)
1149
0
            return "rb+";
1150
0
        else
1151
0
            return "rb";
1152
0
    }
1153
0
    else
1154
0
        return "wb";
1155
0
}
1156
1157
static PyObject *
1158
fileio_repr(PyObject *op)
1159
0
{
1160
0
    fileio *self = PyFileIO_CAST(op);
1161
0
    const char *type_name = Py_TYPE(self)->tp_name;
1162
1163
0
    if (self->fd < 0) {
1164
0
        return PyUnicode_FromFormat("<%.100s [closed]>", type_name);
1165
0
    }
1166
1167
0
    PyObject *nameobj;
1168
0
    if (PyObject_GetOptionalAttr((PyObject *) self, &_Py_ID(name), &nameobj) < 0) {
1169
0
        return NULL;
1170
0
    }
1171
0
    PyObject *res;
1172
0
    if (nameobj == NULL) {
1173
0
        res = PyUnicode_FromFormat(
1174
0
            "<%.100s fd=%d mode='%s' closefd=%s>",
1175
0
            type_name, self->fd, mode_string(self), self->closefd ? "True" : "False");
1176
0
    }
1177
0
    else {
1178
0
        int status = Py_ReprEnter((PyObject *)self);
1179
0
        res = NULL;
1180
0
        if (status == 0) {
1181
0
            res = PyUnicode_FromFormat(
1182
0
                "<%.100s name=%R mode='%s' closefd=%s>",
1183
0
                type_name, nameobj, mode_string(self), self->closefd ? "True" : "False");
1184
0
            Py_ReprLeave((PyObject *)self);
1185
0
        }
1186
0
        else if (status > 0) {
1187
0
            PyErr_Format(PyExc_RuntimeError,
1188
0
                         "reentrant call inside %.100s.__repr__", type_name);
1189
0
        }
1190
0
        Py_DECREF(nameobj);
1191
0
    }
1192
0
    return res;
1193
0
}
1194
1195
/*[clinic input]
1196
_io.FileIO.isatty
1197
1198
True if the file is connected to a TTY device.
1199
[clinic start generated code]*/
1200
1201
static PyObject *
1202
_io_FileIO_isatty_impl(fileio *self)
1203
/*[clinic end generated code: output=932c39924e9a8070 input=cd94ca1f5e95e843]*/
1204
64
{
1205
64
    long res;
1206
1207
64
    if (self->fd < 0)
1208
0
        return err_closed();
1209
64
    Py_BEGIN_ALLOW_THREADS
1210
64
    _Py_BEGIN_SUPPRESS_IPH
1211
64
    res = isatty(self->fd);
1212
64
    _Py_END_SUPPRESS_IPH
1213
64
    Py_END_ALLOW_THREADS
1214
64
    return PyBool_FromLong(res);
1215
64
}
1216
1217
/* Checks whether the file is a TTY using an open-only optimization.
1218
1219
   TTYs are always character devices. If the interpreter knows a file is
1220
   not a character device when it would call ``isatty``, can skip that
1221
   call. Inside ``open()``  there is a fresh stat result that contains that
1222
   information. Use the stat result to skip a system call. Outside of that
1223
   context TOCTOU issues (the fd could be arbitrarily modified by
1224
   surrounding code). */
1225
static PyObject *
1226
_io_FileIO_isatty_open_only(PyObject *op, PyObject *Py_UNUSED(dummy))
1227
1.08k
{
1228
1.08k
    fileio *self = PyFileIO_CAST(op);
1229
1.08k
    if (self->stat_atopen != NULL && !S_ISCHR(self->stat_atopen->st_mode)) {
1230
1.06k
        Py_RETURN_FALSE;
1231
1.06k
    }
1232
16
    return _io_FileIO_isatty_impl(self);
1233
1.08k
}
1234
1235
#include "clinic/fileio.c.h"
1236
1237
static PyMethodDef fileio_methods[] = {
1238
    _IO_FILEIO_READ_METHODDEF
1239
    _IO_FILEIO_READALL_METHODDEF
1240
    _IO_FILEIO_READINTO_METHODDEF
1241
    _IO_FILEIO_WRITE_METHODDEF
1242
    _IO_FILEIO_SEEK_METHODDEF
1243
    _IO_FILEIO_TELL_METHODDEF
1244
    _IO_FILEIO_TRUNCATE_METHODDEF
1245
    _IO_FILEIO_CLOSE_METHODDEF
1246
    _IO_FILEIO_SEEKABLE_METHODDEF
1247
    _IO_FILEIO_READABLE_METHODDEF
1248
    _IO_FILEIO_WRITABLE_METHODDEF
1249
    _IO_FILEIO_FILENO_METHODDEF
1250
    _IO_FILEIO_ISATTY_METHODDEF
1251
    {"_isatty_open_only", _io_FileIO_isatty_open_only, METH_NOARGS},
1252
    {"_dealloc_warn", fileio_dealloc_warn, METH_O, NULL},
1253
    {"__getstate__", _PyIOBase_cannot_pickle, METH_NOARGS},
1254
    {NULL,           NULL}             /* sentinel */
1255
};
1256
1257
/* 'closed' and 'mode' are attributes for backwards compatibility reasons. */
1258
1259
static PyObject *
1260
fileio_get_closed(PyObject *op, void *closure)
1261
3.58k
{
1262
3.58k
    fileio *self = PyFileIO_CAST(op);
1263
3.58k
    return PyBool_FromLong((long)(self->fd < 0));
1264
3.58k
}
1265
1266
static PyObject *
1267
fileio_get_closefd(PyObject *op, void *closure)
1268
0
{
1269
0
    fileio *self = PyFileIO_CAST(op);
1270
0
    return PyBool_FromLong((long)(self->closefd));
1271
0
}
1272
1273
static PyObject *
1274
fileio_get_mode(PyObject *op, void *closure)
1275
0
{
1276
0
    fileio *self = PyFileIO_CAST(op);
1277
0
    return PyUnicode_FromString(mode_string(self));
1278
0
}
1279
1280
static PyObject *
1281
fileio_get_blksize(PyObject *op, void *closure)
1282
1.08k
{
1283
1.08k
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1284
1.08k
    fileio *self = PyFileIO_CAST(op);
1285
1.08k
    if (self->stat_atopen != NULL && self->stat_atopen->st_blksize > 1) {
1286
1.08k
        return PyLong_FromLong(self->stat_atopen->st_blksize);
1287
1.08k
    }
1288
0
#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1289
0
    return PyLong_FromLong(DEFAULT_BUFFER_SIZE);
1290
1.08k
}
1291
1292
static PyGetSetDef fileio_getsetlist[] = {
1293
    {"closed", fileio_get_closed, NULL, "True if the file is closed"},
1294
    {"closefd", fileio_get_closefd, NULL,
1295
        "True if the file descriptor will be closed by close()."},
1296
    {"mode", fileio_get_mode, NULL, "String giving the file mode"},
1297
    {"_blksize", fileio_get_blksize, NULL, "Stat st_blksize if available"},
1298
    {NULL},
1299
};
1300
1301
static PyMemberDef fileio_members[] = {
1302
    {"_finalizing", Py_T_BOOL, offsetof(fileio, finalizing), 0},
1303
    {"__weaklistoffset__", Py_T_PYSSIZET, offsetof(fileio, weakreflist), Py_READONLY},
1304
    {"__dictoffset__", Py_T_PYSSIZET, offsetof(fileio, dict), Py_READONLY},
1305
    {NULL}
1306
};
1307
1308
static PyType_Slot fileio_slots[] = {
1309
    {Py_tp_dealloc, fileio_dealloc},
1310
    {Py_tp_repr, fileio_repr},
1311
    {Py_tp_doc, (void *)_io_FileIO___init____doc__},
1312
    {Py_tp_traverse, fileio_traverse},
1313
    {Py_tp_clear, fileio_clear},
1314
    {Py_tp_methods, fileio_methods},
1315
    {Py_tp_members, fileio_members},
1316
    {Py_tp_getset, fileio_getsetlist},
1317
    {Py_tp_init, _io_FileIO___init__},
1318
    {Py_tp_new, fileio_new},
1319
    {0, NULL},
1320
};
1321
1322
PyType_Spec fileio_spec = {
1323
    .name = "_io.FileIO",
1324
    .basicsize = sizeof(fileio),
1325
    .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
1326
              Py_TPFLAGS_IMMUTABLETYPE),
1327
    .slots = fileio_slots,
1328
};