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