Coverage Report

Created: 2026-10-17 06:15

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