Coverage Report

Created: 2026-05-30 06:18

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