Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/fileobject.c
Line
Count
Source
1
/* File object implementation (what's left of it -- see io.py) */
2
3
#include "Python.h"
4
#include "pycore_call.h"          // _PyObject_CallNoArgs()
5
#include "pycore_runtime.h"       // _PyRuntime
6
#include "pycore_unicodeobject.h" // _PyUnicode_AsUTF8String()
7
8
#ifdef HAVE_UNISTD_H
9
#  include <unistd.h>             // isatty()
10
#endif
11
12
13
#if defined(HAVE_GETC_UNLOCKED) && !defined(_Py_MEMORY_SANITIZER)
14
   /* clang MemorySanitizer doesn't yet understand getc_unlocked. */
15
0
#  define GETC(f) getc_unlocked(f)
16
0
#  define FLOCKFILE(f) flockfile(f)
17
0
#  define FUNLOCKFILE(f) funlockfile(f)
18
#else
19
#  define GETC(f) getc(f)
20
#  define FLOCKFILE(f)
21
#  define FUNLOCKFILE(f)
22
#endif
23
24
/* Newline flags */
25
#define NEWLINE_UNKNOWN 0       /* No newline seen, yet */
26
#define NEWLINE_CR 1            /* \r newline seen */
27
#define NEWLINE_LF 2            /* \n newline seen */
28
#define NEWLINE_CRLF 4          /* \r\n newline seen */
29
30
/* External C interface */
31
32
PyObject *
33
PyFile_FromFd(int fd, const char *name, const char *mode, int buffering, const char *encoding,
34
              const char *errors, const char *newline, int closefd)
35
0
{
36
0
    PyObject *open, *stream;
37
38
    /* import _io in case we are being used to open io.py */
39
0
    open = PyImport_ImportModuleAttrString("_io", "open");
40
0
    if (open == NULL)
41
0
        return NULL;
42
0
    stream = PyObject_CallFunction(open, "isisssO", fd, mode,
43
0
                                  buffering, encoding, errors,
44
0
                                  newline, closefd ? Py_True : Py_False);
45
0
    Py_DECREF(open);
46
0
    if (stream == NULL)
47
0
        return NULL;
48
    /* ignore name attribute because the name attribute of _BufferedIOMixin
49
       and TextIOWrapper is read only */
50
0
    return stream;
51
0
}
52
53
PyObject *
54
PyFile_GetLine(PyObject *f, int n)
55
0
{
56
0
    PyObject *result;
57
58
0
    if (f == NULL) {
59
0
        PyErr_BadInternalCall();
60
0
        return NULL;
61
0
    }
62
63
0
    if (n <= 0) {
64
0
        result = PyObject_CallMethodNoArgs(f, &_Py_ID(readline));
65
0
    }
66
0
    else {
67
0
        result = _PyObject_CallMethod(f, &_Py_ID(readline), "i", n);
68
0
    }
69
0
    if (result != NULL && !PyBytes_Check(result) &&
70
0
        !PyUnicode_Check(result)) {
71
0
        PyErr_Format(PyExc_TypeError,
72
0
                     "%T.readline() must return a str, not %T", f, result);
73
0
        Py_SETREF(result, NULL);
74
0
    }
75
76
0
    if (n < 0 && result != NULL && PyBytes_Check(result)) {
77
0
        const char *s = PyBytes_AS_STRING(result);
78
0
        Py_ssize_t len = PyBytes_GET_SIZE(result);
79
0
        if (len == 0) {
80
0
            Py_SETREF(result, NULL);
81
0
            PyErr_SetString(PyExc_EOFError,
82
0
                            "EOF when reading a line");
83
0
        }
84
0
        else if (s[len-1] == '\n') {
85
0
            (void) _PyBytes_Resize(&result, len-1);
86
0
        }
87
0
    }
88
0
    if (n < 0 && result != NULL && PyUnicode_Check(result)) {
89
0
        Py_ssize_t len = PyUnicode_GET_LENGTH(result);
90
0
        if (len == 0) {
91
0
            Py_SETREF(result, NULL);
92
0
            PyErr_SetString(PyExc_EOFError,
93
0
                            "EOF when reading a line");
94
0
        }
95
0
        else if (PyUnicode_READ_CHAR(result, len-1) == '\n') {
96
0
            PyObject *v;
97
0
            v = PyUnicode_Substring(result, 0, len-1);
98
0
            Py_SETREF(result, v);
99
0
        }
100
0
    }
101
0
    return result;
102
0
}
103
104
/* Interfaces to write objects/strings to file-like objects */
105
106
int
107
PyFile_WriteObject(PyObject *v, PyObject *f, int flags)
108
0
{
109
0
    if (f == NULL) {
110
0
        PyErr_SetString(PyExc_TypeError, "writeobject with NULL file");
111
0
        return -1;
112
0
    }
113
114
0
    Py_INCREF(f);
115
0
    PyObject *value = (flags & Py_PRINT_RAW) ? PyObject_Str(v) : PyObject_Repr(v);
116
0
    if (value == NULL) {
117
0
        Py_DECREF(f);
118
0
        return -1;
119
0
    }
120
0
    PyObject *result = PyObject_CallMethodOneArg(f, &_Py_ID(write), value);
121
0
    Py_DECREF(value);
122
0
    Py_DECREF(f);
123
0
    if (result == NULL)
124
0
        return -1;
125
0
    Py_DECREF(result);
126
0
    return 0;
127
0
}
128
129
int
130
PyFile_WriteString(const char *s, PyObject *f)
131
0
{
132
0
    if (f == NULL) {
133
        /* Should be caused by a pre-existing error */
134
0
        if (!PyErr_Occurred())
135
0
            PyErr_SetString(PyExc_SystemError,
136
0
                            "null file for PyFile_WriteString");
137
0
        return -1;
138
0
    }
139
0
    else if (!PyErr_Occurred()) {
140
0
        PyObject *v = PyUnicode_FromString(s);
141
0
        int err;
142
0
        if (v == NULL)
143
0
            return -1;
144
0
        err = PyFile_WriteObject(v, f, Py_PRINT_RAW);
145
0
        Py_DECREF(v);
146
0
        return err;
147
0
    }
148
0
    else
149
0
        return -1;
150
0
}
151
152
/* Try to get a file-descriptor from a Python object.  If the object
153
   is an integer, its value is returned.  If not, the
154
   object's fileno() method is called if it exists; the method must return
155
   an integer, which is returned as the file descriptor value.
156
   -1 is returned on failure.
157
*/
158
159
int
160
PyObject_AsFileDescriptor(PyObject *o)
161
0
{
162
0
    int fd;
163
0
    PyObject *meth;
164
165
0
    if (PyLong_Check(o)) {
166
0
        if (PyBool_Check(o)) {
167
0
            if (PyErr_WarnEx(PyExc_RuntimeWarning,
168
0
                    "bool is used as a file descriptor", 1))
169
0
            {
170
0
                return -1;
171
0
            }
172
0
        }
173
0
        fd = PyLong_AsInt(o);
174
0
    }
175
0
    else if (PyObject_GetOptionalAttr(o, &_Py_ID(fileno), &meth) < 0) {
176
0
        return -1;
177
0
    }
178
0
    else if (meth != NULL) {
179
0
        PyObject *fno = _PyObject_CallNoArgs(meth);
180
0
        Py_DECREF(meth);
181
0
        if (fno == NULL)
182
0
            return -1;
183
184
0
        if (PyLong_Check(fno)) {
185
0
            fd = PyLong_AsInt(fno);
186
0
            Py_DECREF(fno);
187
0
        }
188
0
        else {
189
0
            PyErr_Format(PyExc_TypeError,
190
0
                         "%T.fileno() must return an int, not %T", o, fno);
191
0
            Py_DECREF(fno);
192
0
            return -1;
193
0
        }
194
0
    }
195
0
    else {
196
0
        PyErr_SetString(PyExc_TypeError,
197
0
                        "argument must be an int, or have a fileno() method.");
198
0
        return -1;
199
0
    }
200
201
0
    if (fd == -1 && PyErr_Occurred())
202
0
        return -1;
203
0
    if (fd < 0) {
204
0
        PyErr_Format(PyExc_ValueError,
205
0
                     "file descriptor cannot be a negative integer (%i)",
206
0
                     fd);
207
0
        return -1;
208
0
    }
209
0
    return fd;
210
0
}
211
212
int
213
_Py_Off_t_Converter(PyObject *arg, void *addr)
214
0
{
215
#ifdef HAVE_LARGEFILE_SUPPORT
216
    *((Py_off_t *)addr) = PyLong_AsLongLong(arg);
217
#else
218
0
    *((Py_off_t *)addr) = PyLong_AsLong(arg);
219
0
#endif
220
0
    return PyErr_Occurred() ? 0 : 1;
221
0
}
222
223
int
224
_PyLong_FileDescriptor_Converter(PyObject *o, void *ptr)
225
0
{
226
0
    int fd = PyObject_AsFileDescriptor(o);
227
0
    if (fd == -1) {
228
0
        return 0;
229
0
    }
230
0
    *(int *)ptr = fd;
231
0
    return 1;
232
0
}
233
234
char *
235
_Py_UniversalNewlineFgetsWithSize(char *buf, int n, FILE *stream, PyObject *fobj, size_t* size)
236
0
{
237
0
    char *p = buf;
238
0
    int c;
239
240
0
    if (fobj) {
241
0
        errno = ENXIO;          /* What can you do... */
242
0
        return NULL;
243
0
    }
244
0
    FLOCKFILE(stream);
245
0
    while (--n > 0 && (c = GETC(stream)) != EOF ) {
246
0
        if (c == '\r') {
247
            // A \r is translated into a \n, and we skip an adjacent \n, if any.
248
0
            c = GETC(stream);
249
0
            if (c != '\n') {
250
0
                ungetc(c, stream);
251
0
                c = '\n';
252
0
            }
253
0
        }
254
0
        *p++ = c;
255
0
        if (c == '\n') {
256
0
            break;
257
0
        }
258
0
    }
259
0
    FUNLOCKFILE(stream);
260
0
    *p = '\0';
261
0
    if (p == buf) {
262
0
        return NULL;
263
0
    }
264
0
    *size = p - buf;
265
0
    return buf;
266
0
}
267
268
/*
269
** Py_UniversalNewlineFgets is an fgets variation that understands
270
** all of \r, \n and \r\n conventions.
271
** The stream should be opened in binary mode.
272
** The fobj parameter exists solely for legacy reasons and must be NULL.
273
** Note that we need no error handling: fgets() treats error and eof
274
** identically.
275
*/
276
277
char *
278
0
Py_UniversalNewlineFgets(char *buf, int n, FILE *stream, PyObject *fobj) {
279
0
    size_t size;
280
0
    return _Py_UniversalNewlineFgetsWithSize(buf, n, stream, fobj, &size);
281
0
}
282
283
/* **************************** std printer ****************************
284
 * The stdprinter is used during the boot strapping phase as a preliminary
285
 * file like object for sys.stderr.
286
 */
287
288
typedef struct {
289
    PyObject_HEAD
290
    int fd;
291
} PyStdPrinter_Object;
292
293
/*[clinic input]
294
class stdprinter "PyStdPrinter_Object *" "&PyStdPrinter_Type"
295
[clinic start generated code]*/
296
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=031bf557c18c5df5]*/
297
298
#include "clinic/fileobject.c.h"
299
300
PyObject *
301
PyFile_NewStdPrinter(int fd)
302
37
{
303
37
    PyStdPrinter_Object *self;
304
305
37
    if (fd != fileno(stdout) && fd != fileno(stderr)) {
306
        /* not enough infrastructure for PyErr_BadInternalCall() */
307
0
        return NULL;
308
0
    }
309
310
37
    self = PyObject_New(PyStdPrinter_Object,
311
37
                        &PyStdPrinter_Type);
312
37
    if (self != NULL) {
313
37
        self->fd = fd;
314
37
    }
315
37
    return (PyObject*)self;
316
37
}
317
318
/*[clinic input]
319
stdprinter.write
320
321
    text as unicode: unicode
322
    /
323
324
Write the text to the stream.
325
[clinic start generated code]*/
326
327
static PyObject *
328
stdprinter_write_impl(PyStdPrinter_Object *self, PyObject *unicode)
329
/*[clinic end generated code: output=069c9645803fe8ad input=65041d6d7a23aff4]*/
330
0
{
331
0
    PyObject *bytes = NULL;
332
0
    const char *str;
333
0
    Py_ssize_t n;
334
0
    int err;
335
336
    /* The function can clear the current exception */
337
0
    assert(!PyErr_Occurred());
338
339
0
    if (self->fd < 0) {
340
        /* fd might be invalid on Windows
341
         * I can't raise an exception here. It may lead to an
342
         * unlimited recursion in the case stderr is invalid.
343
         */
344
0
        Py_RETURN_NONE;
345
0
    }
346
347
    /* Encode Unicode to UTF-8/backslashreplace */
348
0
    str = PyUnicode_AsUTF8AndSize(unicode, &n);
349
0
    if (str == NULL) {
350
0
        PyErr_Clear();
351
0
        bytes = _PyUnicode_AsUTF8String(unicode, "backslashreplace");
352
0
        if (bytes == NULL)
353
0
            return NULL;
354
0
        str = PyBytes_AS_STRING(bytes);
355
0
        n = PyBytes_GET_SIZE(bytes);
356
0
    }
357
358
0
    n = _Py_write(self->fd, str, n);
359
    /* save errno, it can be modified indirectly by Py_XDECREF() */
360
0
    err = errno;
361
362
0
    Py_XDECREF(bytes);
363
364
0
    if (n == -1) {
365
0
        if (err == EAGAIN) {
366
0
            PyErr_Clear();
367
0
            Py_RETURN_NONE;
368
0
        }
369
0
        return NULL;
370
0
    }
371
372
0
    return PyLong_FromSsize_t(n);
373
0
}
374
375
static PyObject *
376
stdprinter_fileno(PyObject *op, PyObject *Py_UNUSED(ignored))
377
0
{
378
0
    PyStdPrinter_Object *self = (PyStdPrinter_Object*)op;
379
0
    return PyLong_FromLong((long) self->fd);
380
0
}
381
382
static PyObject *
383
stdprinter_repr(PyObject *op)
384
0
{
385
0
    PyStdPrinter_Object *self = (PyStdPrinter_Object*)op;
386
0
    return PyUnicode_FromFormat("<stdprinter(fd=%d) object at %p>",
387
0
                                self->fd, self);
388
0
}
389
390
static PyObject *
391
stdprinter_noop(PyObject *self, PyObject *Py_UNUSED(ignored))
392
0
{
393
0
    Py_RETURN_NONE;
394
0
}
395
396
static PyObject *
397
stdprinter_isatty(PyObject *op, PyObject *Py_UNUSED(ignored))
398
0
{
399
0
    PyStdPrinter_Object *self = (PyStdPrinter_Object*)op;
400
0
    long res;
401
0
    if (self->fd < 0) {
402
0
        Py_RETURN_FALSE;
403
0
    }
404
405
0
    Py_BEGIN_ALLOW_THREADS
406
0
    res = isatty(self->fd);
407
0
    Py_END_ALLOW_THREADS
408
409
0
    return PyBool_FromLong(res);
410
0
}
411
412
static PyMethodDef stdprinter_methods[] = {
413
    {"close", stdprinter_noop, METH_NOARGS, ""},
414
    {"flush", stdprinter_noop, METH_NOARGS, ""},
415
    {"fileno", stdprinter_fileno, METH_NOARGS, ""},
416
    {"isatty", stdprinter_isatty, METH_NOARGS, ""},
417
    STDPRINTER_WRITE_METHODDEF
418
    {NULL,              NULL}  /*sentinel */
419
};
420
421
static PyObject *
422
get_closed(PyObject *self, void *Py_UNUSED(closure))
423
0
{
424
0
    Py_RETURN_FALSE;
425
0
}
426
427
static PyObject *
428
get_mode(PyObject *self, void *Py_UNUSED(closure))
429
0
{
430
0
    return PyUnicode_FromString("w");
431
0
}
432
433
static PyObject *
434
get_encoding(PyObject *self, void *Py_UNUSED(closure))
435
0
{
436
0
    Py_RETURN_NONE;
437
0
}
438
439
static PyGetSetDef stdprinter_getsetlist[] = {
440
    {"closed", get_closed, NULL, "True if the file is closed"},
441
    {"encoding", get_encoding, NULL, "Encoding of the file"},
442
    {"mode", get_mode, NULL, "String giving the file mode"},
443
    {0},
444
};
445
446
PyTypeObject PyStdPrinter_Type = {
447
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
448
    "stderrprinter",                            /* tp_name */
449
    sizeof(PyStdPrinter_Object),                /* tp_basicsize */
450
    0,                                          /* tp_itemsize */
451
    /* methods */
452
    0,                                          /* tp_dealloc */
453
    0,                                          /* tp_vectorcall_offset */
454
    0,                                          /* tp_getattr */
455
    0,                                          /* tp_setattr */
456
    0,                                          /* tp_as_async */
457
    stdprinter_repr,                            /* tp_repr */
458
    0,                                          /* tp_as_number */
459
    0,                                          /* tp_as_sequence */
460
    0,                                          /* tp_as_mapping */
461
    0,                                          /* tp_hash */
462
    0,                                          /* tp_call */
463
    0,                                          /* tp_str */
464
    PyObject_GenericGetAttr,                    /* tp_getattro */
465
    0,                                          /* tp_setattro */
466
    0,                                          /* tp_as_buffer */
467
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION, /* tp_flags */
468
    0,                                          /* tp_doc */
469
    0,                                          /* tp_traverse */
470
    0,                                          /* tp_clear */
471
    0,                                          /* tp_richcompare */
472
    0,                                          /* tp_weaklistoffset */
473
    0,                                          /* tp_iter */
474
    0,                                          /* tp_iternext */
475
    stdprinter_methods,                         /* tp_methods */
476
    0,                                          /* tp_members */
477
    stdprinter_getsetlist,                      /* tp_getset */
478
    0,                                          /* tp_base */
479
    0,                                          /* tp_dict */
480
    0,                                          /* tp_descr_get */
481
    0,                                          /* tp_descr_set */
482
    0,                                          /* tp_dictoffset */
483
    0,                                          /* tp_init */
484
    PyType_GenericAlloc,                        /* tp_alloc */
485
    0,                                          /* tp_new */
486
    PyObject_Free,                              /* tp_free */
487
};
488
489
490
/* ************************** open_code hook ***************************
491
 * The open_code hook allows embedders to override the method used to
492
 * open files that are going to be used by the runtime to execute code
493
 */
494
495
int
496
0
PyFile_SetOpenCodeHook(Py_OpenCodeHookFunction hook, void *userData) {
497
0
    if (Py_IsInitialized() &&
498
0
        PySys_Audit("setopencodehook", NULL) < 0) {
499
0
        return -1;
500
0
    }
501
502
0
    if (_PyRuntime.open_code_hook) {
503
0
        if (Py_IsInitialized()) {
504
0
            PyErr_SetString(PyExc_SystemError,
505
0
                "failed to change existing open_code hook");
506
0
        }
507
0
        return -1;
508
0
    }
509
510
0
    _PyRuntime.open_code_hook = hook;
511
0
    _PyRuntime.open_code_userdata = userData;
512
0
    return 0;
513
0
}
514
515
PyObject *
516
PyFile_OpenCodeObject(PyObject *path)
517
5.52k
{
518
5.52k
    PyObject *f = NULL;
519
520
5.52k
    if (!PyUnicode_Check(path)) {
521
0
        PyErr_Format(PyExc_TypeError, "'path' must be 'str', not '%.200s'",
522
0
                     Py_TYPE(path)->tp_name);
523
0
        return NULL;
524
0
    }
525
526
5.52k
    Py_OpenCodeHookFunction hook = _PyRuntime.open_code_hook;
527
5.52k
    if (hook) {
528
0
        f = hook(path, _PyRuntime.open_code_userdata);
529
5.52k
    } else {
530
5.52k
        PyObject *open = PyImport_ImportModuleAttrString("_io", "open");
531
5.52k
        if (open) {
532
5.52k
            f = PyObject_CallFunction(open, "Os", path, "rb");
533
5.52k
            Py_DECREF(open);
534
5.52k
        }
535
5.52k
    }
536
537
5.52k
    return f;
538
5.52k
}
539
540
PyObject *
541
PyFile_OpenCode(const char *utf8path)
542
0
{
543
0
    PyObject *pathobj = PyUnicode_FromString(utf8path);
544
0
    PyObject *f;
545
0
    if (!pathobj) {
546
0
        return NULL;
547
0
    }
548
0
    f = PyFile_OpenCodeObject(pathobj);
549
0
    Py_DECREF(pathobj);
550
0
    return f;
551
0
}
552
553
554
int
555
_PyFile_Flush(PyObject *file)
556
234k
{
557
234k
    PyObject *tmp = PyObject_CallMethodNoArgs(file, &_Py_ID(flush));
558
234k
    if (tmp == NULL) {
559
0
        return -1;
560
0
    }
561
234k
    Py_DECREF(tmp);
562
234k
    return 0;
563
234k
}