Coverage Report

Created: 2025-11-30 06:38

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