Coverage Report

Created: 2026-10-31 06:21

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.52k
#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
570k
#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
520k
{
96
520k
    return (PyFileIO_CAST(self)->fd < 0);
97
520k
}
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
22.9k
{
131
22.9k
    int err = 0;
132
22.9k
    int save_errno = 0;
133
22.9k
    if (self->fd >= 0) {
134
22.9k
        int fd = self->fd;
135
22.9k
        self->fd = -1;
136
        /* fd is accessible and someone else may have closed it */
137
22.9k
        Py_BEGIN_ALLOW_THREADS
138
22.9k
        _Py_BEGIN_SUPPRESS_IPH
139
22.9k
        err = close(fd);
140
22.9k
        if (err < 0)
141
0
            save_errno = errno;
142
22.9k
        _Py_END_SUPPRESS_IPH
143
22.9k
        Py_END_ALLOW_THREADS
144
22.9k
    }
145
22.9k
    PyMem_Free(self->stat_atopen);
146
22.9k
    self->stat_atopen = NULL;
147
22.9k
    if (err < 0) {
148
0
        errno = save_errno;
149
0
        PyErr_SetFromErrno(PyExc_OSError);
150
0
        return -1;
151
0
    }
152
22.9k
    return 0;
153
22.9k
}
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
22.9k
{
171
22.9k
    PyObject *res;
172
22.9k
    int rc;
173
22.9k
    _PyIO_State *state = get_io_state_by_cls(cls);
174
22.9k
    res = PyObject_CallMethodOneArg((PyObject*)state->PyRawIOBase_Type,
175
22.9k
                                     &_Py_ID(close), (PyObject *)self);
176
22.9k
    if (!self->closefd) {
177
0
        self->fd = -1;
178
0
        return res;
179
0
    }
180
181
22.9k
    PyObject *exc = NULL;
182
22.9k
    if (res == NULL) {
183
0
        exc = PyErr_GetRaisedException();
184
0
    }
185
22.9k
    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
22.9k
    rc = internal_close(self);
195
22.9k
    if (res == NULL) {
196
0
        _PyErr_ChainExceptions1(exc);
197
0
    }
198
22.9k
    if (rc < 0) {
199
0
        Py_CLEAR(res);
200
0
    }
201
22.9k
    return res;
202
22.9k
}
203
204
static PyObject *
205
fileio_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
206
23.0k
{
207
23.0k
    assert(type != NULL && type->tp_alloc != NULL);
208
209
23.0k
    fileio *self = (fileio *) type->tp_alloc(type, 0);
210
23.0k
    if (self == NULL) {
211
0
        return NULL;
212
0
    }
213
214
23.0k
    self->fd = -1;
215
23.0k
    self->created = 0;
216
23.0k
    self->readable = 0;
217
23.0k
    self->writable = 0;
218
23.0k
    self->appending = 0;
219
23.0k
    self->seekable = -1;
220
23.0k
    self->truncate = 0;
221
23.0k
    self->stat_atopen = NULL;
222
23.0k
    self->closefd = 1;
223
23.0k
    self->weakreflist = NULL;
224
23.0k
    return (PyObject *) self;
225
23.0k
}
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
23.0k
{
261
#ifdef MS_WINDOWS
262
    wchar_t *widename = NULL;
263
#else
264
23.0k
    const char *name = NULL;
265
23.0k
#endif
266
23.0k
    PyObject *stringobj = NULL;
267
23.0k
    const char *s;
268
23.0k
    int ret = 0;
269
23.0k
    int rwa = 0, plus = 0;
270
23.0k
    int flags = 0;
271
23.0k
    int fd = -1;
272
23.0k
    int fd_is_own = 0;
273
23.0k
#ifdef O_CLOEXEC
274
23.0k
    int *atomic_flag_works = &_Py_open_cloexec_works;
275
#elif !defined(MS_WINDOWS)
276
    int *atomic_flag_works = NULL;
277
#endif
278
23.0k
    int fstat_result;
279
23.0k
    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
23.0k
    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
23.0k
    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
23.0k
    fd = PyLong_AsInt(nameobj);
304
23.0k
    if (fd < 0) {
305
22.7k
        if (!PyErr_Occurred()) {
306
0
            PyErr_SetString(PyExc_ValueError,
307
0
                            "negative file descriptor");
308
0
            return -1;
309
0
        }
310
22.7k
        PyErr_Clear();
311
22.7k
    }
312
313
23.0k
    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
22.7k
        if (!PyUnicode_FSConverter(nameobj, &stringobj)) {
323
0
            return -1;
324
0
        }
325
22.7k
        name = PyBytes_AS_STRING(stringobj);
326
22.7k
#endif
327
22.7k
    }
328
329
23.0k
    s = mode;
330
46.1k
    while (*s) {
331
23.0k
        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.59k
        case 'r':
346
5.59k
            if (rwa)
347
0
                goto bad_mode;
348
5.59k
            rwa = 1;
349
5.59k
            self->readable = 1;
350
5.59k
            break;
351
17.4k
        case 'w':
352
17.4k
            if (rwa)
353
0
                goto bad_mode;
354
17.4k
            rwa = 1;
355
17.4k
            self->writable = 1;
356
17.4k
            self->truncate = 1;
357
17.4k
            flags |= O_CREAT | O_TRUNC;
358
17.4k
            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
23.0k
        }
380
23.0k
    }
381
382
23.0k
    if (!rwa)
383
0
        goto bad_mode;
384
385
23.0k
    if (self->readable && self->writable)
386
0
        flags |= O_RDWR;
387
23.0k
    else if (self->readable)
388
5.59k
        flags |= O_RDONLY;
389
17.4k
    else
390
17.4k
        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
23.0k
    flags |= O_CLOEXEC;
400
23.0k
#endif
401
402
23.0k
    if (PySys_Audit("open", "Osi", nameobj, mode, flags) < 0) {
403
0
        goto error;
404
0
    }
405
406
23.0k
    if (fd >= 0) {
407
252
        self->fd = fd;
408
252
        self->closefd = closefd;
409
252
    }
410
22.7k
    else {
411
22.7k
        self->closefd = 1;
412
22.7k
        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
22.7k
        errno = 0;
419
22.7k
        if (opener == Py_None) {
420
22.7k
            do {
421
22.7k
                Py_BEGIN_ALLOW_THREADS
422
#ifdef MS_WINDOWS
423
                self->fd = _wopen(widename, flags, 0666);
424
#else
425
22.7k
                self->fd = open(name, flags, 0666);
426
22.7k
#endif
427
22.7k
                Py_END_ALLOW_THREADS
428
22.7k
            } while (self->fd < 0 && errno == EINTR &&
429
0
                     !(async_err = PyErr_CheckSignals()));
430
431
22.7k
            if (async_err)
432
0
                goto error;
433
434
22.7k
            if (self->fd < 0) {
435
11
                PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, nameobj);
436
11
                goto error;
437
11
            }
438
22.7k
        }
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
22.7k
        fd_is_own = 1;
470
471
22.7k
#ifndef MS_WINDOWS
472
22.7k
        if (_Py_set_inheritable(self->fd, 0, atomic_flag_works) < 0)
473
0
            goto error;
474
22.7k
#endif
475
22.7k
    }
476
477
23.0k
    PyMem_Free(self->stat_atopen);
478
23.0k
    self->stat_atopen = PyMem_New(struct _Py_stat_struct, 1);
479
23.0k
    if (self->stat_atopen == NULL) {
480
0
        PyErr_NoMemory();
481
0
        goto error;
482
0
    }
483
23.0k
    Py_BEGIN_ALLOW_THREADS
484
23.0k
    fstat_result = _Py_fstat_noraise(self->fd, self->stat_atopen);
485
23.0k
    Py_END_ALLOW_THREADS
486
23.0k
    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
23.0k
    else {
504
23.0k
#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
23.0k
        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
23.0k
#endif /* defined(S_ISDIR) */
514
23.0k
    }
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
23.0k
    if (PyObject_SetAttr((PyObject *)self, &_Py_ID(name), nameobj) < 0)
522
0
        goto error;
523
524
23.0k
    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
23.0k
    goto done;
535
536
23.0k
 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
23.0k
 done:
549
#ifdef MS_WINDOWS
550
    PyMem_Free(widename);
551
#endif
552
23.0k
    Py_CLEAR(stringobj);
553
23.0k
    return ret;
554
11
}
555
556
static int
557
fileio_traverse(PyObject *op, visitproc visit, void *arg)
558
4.95k
{
559
4.95k
    fileio *self = PyFileIO_CAST(op);
560
4.95k
    Py_VISIT(Py_TYPE(self));
561
4.95k
    Py_VISIT(self->dict);
562
4.95k
    return 0;
563
4.95k
}
564
565
static int
566
fileio_clear(PyObject *op)
567
22.9k
{
568
22.9k
    fileio *self = PyFileIO_CAST(op);
569
22.9k
    Py_CLEAR(self->dict);
570
22.9k
    return 0;
571
22.9k
}
572
573
static void
574
fileio_dealloc(PyObject *op)
575
22.9k
{
576
22.9k
    fileio *self = PyFileIO_CAST(op);
577
22.9k
    self->finalizing = 1;
578
22.9k
    if (_PyIOBase_finalize(op) < 0) {
579
0
        return;
580
0
    }
581
582
22.9k
    _PyObject_GC_UNTRACK(self);
583
22.9k
    if (self->stat_atopen != NULL) {
584
0
        PyMem_Free(self->stat_atopen);
585
0
        self->stat_atopen = NULL;
586
0
    }
587
22.9k
    FT_CLEAR_WEAKREFS(op, self->weakreflist);
588
22.9k
    (void)fileio_clear(op);
589
590
22.9k
    PyTypeObject *tp = Py_TYPE(op);
591
22.9k
    tp->tp_free(op);
592
22.9k
    Py_DECREF(tp);
593
22.9k
}
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.63k
{
634
5.63k
    if (self->fd < 0)
635
0
        return err_closed();
636
5.63k
    return PyBool_FromLong((long) self->readable);
637
5.63k
}
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
17.5k
{
649
17.5k
    if (self->fd < 0)
650
0
        return err_closed();
651
17.5k
    return PyBool_FromLong((long) self->writable);
652
17.5k
}
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.52k
{
758
5.52k
    Py_off_t pos, end;
759
5.52k
    PyBytesWriter *writer;
760
5.52k
    Py_ssize_t bytes_read = 0;
761
5.52k
    Py_ssize_t n;
762
5.52k
    size_t bufsize;
763
764
5.52k
    if (self->fd < 0) {
765
0
        return err_closed();
766
0
    }
767
5.52k
    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.52k
    if (self->stat_atopen != NULL && self->stat_atopen->st_size < _PY_READ_MAX) {
773
5.52k
        end = (Py_off_t)self->stat_atopen->st_size;
774
5.52k
    }
775
0
    else {
776
0
        end = -1;
777
0
    }
778
5.52k
    if (end <= 0) {
779
        /* Use a default size and resize as needed. */
780
0
        bufsize = SMALLCHUNK;
781
0
    }
782
5.52k
    else {
783
        /* This is probably a real file. */
784
5.52k
        if (end > _PY_READ_MAX - 1) {
785
0
            bufsize = _PY_READ_MAX;
786
0
        }
787
5.52k
        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.52k
            bufsize = (size_t)end + 1;
794
5.52k
        }
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.52k
        if (bufsize > LARGE_BUFFER_CUTOFF_SIZE) {
802
201
            Py_BEGIN_ALLOW_THREADS
803
201
            _Py_BEGIN_SUPPRESS_IPH
804
#ifdef MS_WINDOWS
805
            pos = _lseeki64(self->fd, 0L, SEEK_CUR);
806
#else
807
201
            pos = lseek(self->fd, 0L, SEEK_CUR);
808
201
#endif
809
201
            _Py_END_SUPPRESS_IPH
810
201
            Py_END_ALLOW_THREADS
811
812
201
            if (end >= pos && pos >= 0 && (end - pos) < (_PY_READ_MAX - 1)) {
813
201
                bufsize = (size_t)(end - pos) + 1;
814
201
            }
815
201
        }
816
5.52k
    }
817
818
5.52k
    writer = PyBytesWriter_Create(bufsize);
819
5.52k
    if (writer == NULL) {
820
0
        return NULL;
821
0
    }
822
823
11.0k
    while (1) {
824
11.0k
        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.0k
        n = _Py_read(self->fd,
841
11.0k
                     (char*)PyBytesWriter_GetData(writer) + bytes_read,
842
11.0k
                     bufsize - bytes_read);
843
844
11.0k
        if (n == 0)
845
5.52k
            break;
846
5.52k
        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.52k
        bytes_read += n;
858
5.52k
    }
859
860
5.52k
    return PyBytesWriter_FinishWithSize(writer, bytes_read);
861
5.52k
}
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
161k
{
937
161k
    Py_ssize_t n;
938
161k
    int err;
939
940
161k
    if (self->fd < 0)
941
0
        return err_closed();
942
161k
    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
161k
    n = _Py_write(self->fd, b->buf, b->len);
948
    /* copy errno because PyBuffer_Release() can indirectly modify it */
949
161k
    err = errno;
950
951
161k
    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
161k
    return PyLong_FromSsize_t(n);
960
161k
}
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
23.1k
{
968
23.1k
    Py_off_t pos, res;
969
23.1k
    int fd = self->fd;
970
971
23.1k
#ifdef SEEK_SET
972
    /* Turn 0, 1, 2 into SEEK_{SET,CUR,END} */
973
23.1k
    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
23.1k
    }
984
23.1k
#endif /* SEEK_SET */
985
986
23.1k
    if (posobj == NULL) {
987
23.1k
        pos = 0;
988
23.1k
    }
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
23.1k
    Py_BEGIN_ALLOW_THREADS
1000
23.1k
    _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
23.1k
    res = lseek(fd, pos, whence);
1012
23.1k
#endif
1013
23.1k
    _Py_END_SUPPRESS_IPH
1014
23.1k
    Py_END_ALLOW_THREADS
1015
1016
23.1k
    if (self->seekable < 0) {
1017
23.0k
        self->seekable = (res >= 0);
1018
23.0k
    }
1019
1020
23.1k
    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
23.1k
    return PyLong_FromLong(res);
1032
23.1k
#endif
1033
23.1k
}
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
23.1k
{
1075
23.1k
    if (self->fd < 0)
1076
0
        return err_closed();
1077
1078
23.1k
    return portable_lseek(self, NULL, 1, false);
1079
23.1k
}
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
23.0k
{
1268
23.0k
    if (self->stat_atopen != NULL && !S_ISCHR(self->stat_atopen->st_mode)) {
1269
23.0k
        Py_RETURN_FALSE;
1270
23.0k
    }
1271
37
    return _io_FileIO_isatty_impl(self);
1272
23.0k
}
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
68.8k
{
1309
68.8k
    return self->fd < 0;
1310
68.8k
}
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
23.0k
{
1351
23.0k
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
1352
23.0k
    if (self->stat_atopen != NULL && self->stat_atopen->st_blksize > 1) {
1353
23.0k
        return self->stat_atopen->st_blksize;
1354
23.0k
    }
1355
0
#endif /* HAVE_STRUCT_STAT_ST_BLKSIZE */
1356
0
    return DEFAULT_BUFFER_SIZE;
1357
23.0k
}
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
};