Coverage Report

Created: 2026-10-17 06:15

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