Skip to main content

Objects/fileobject.c

cpython 3.14 @ ab2d84fe1023/Objects/fileobject.c

File descriptor helpers. Objects/fileobject.c is not the implementation of Python's open() or the _io module types. Those live in Modules/_io/. This file is a smaller set of C-API helpers that sit between the interpreter core and the I/O layer: PyFile_FromFd wraps a raw file descriptor in a Python io.open object, PyObject_AsFileDescriptor extracts an int fd from any object that implements the fileno() protocol, PyFile_WriteObject and PyFile_WriteString write to an arbitrary file-like object using the str conversion and write method, and PyFile_GetLine reads a single line.

The last section of the file provides the sys.stdin, sys.stdout, sys.stderr accessors (_PySys_GetObjectId-style lookups) that the interpreter uses when printing tracebacks, running print() without an explicit file=, and handling input().

Map

LinesSymbolRolegopy
1-100PyFile_FromFdCalls io.open(fd, mode, ...) and returns the resulting file object; used by sys.stdin/stdout/stderr bootstrapping.objects/file.go:NewFile
100-250PyObject_AsFileDescriptorExtracts an int file descriptor from an object; tries ob_as_number->nb_index first, then falls back to calling .fileno().objects/file.go:AsFileDescriptor
250-400PyFile_WriteObject, PyFile_WriteStringConverts an object to str (or repr if flags & Py_PRINT_RAW is clear) then calls .write() on the target file.objects/file.go:FileWriteObject, FileWriteString
400-450PyFile_GetLine, sys_stdin, sys_stdout, sys_stderr helper lookupsGetLine reads one line from a file-like via .readline(); the stdio helpers fetch sys.stdin/stdout/stderr from the thread-state's interp.module/sys/module.go:Stdin, Stdout, Stderr

Reading

fileno() protocol (lines 100 to 250)

cpython 3.14 @ ab2d84fe1023/Objects/fileobject.c#L100-250

PyObject_AsFileDescriptor implements the duck-typed file-descriptor protocol: given any Python object, produce an int fd or raise TypeError. The steps are:

  1. If the object is already a Python int, return it directly.
  2. Otherwise, look up the .fileno() method and call it.
  3. If the result of .fileno() is not an int, raise TypeError.
  4. Validate that the fd is non-negative.
int
PyObject_AsFileDescriptor(PyObject *o)
{
int fd;
PyObject *meth;

if (PyLong_Check(o)) {
fd = _PyLong_AsInt(o);
}
else if ((meth = PyObject_GetAttr(o, &_Py_ID(fileno))) != NULL) {
PyObject *fno = PyObject_CallNoArgs(meth);
Py_DECREF(meth);
if (fno == NULL)
return -1;
if (!PyLong_Check(fno)) {
PyErr_SetString(PyExc_TypeError,
"fileno() returned a non-integer");
Py_DECREF(fno);
return -1;
}
fd = _PyLong_AsInt(fno);
Py_DECREF(fno);
}
else {
PyErr_SetString(PyExc_TypeError,
"argument must be an int, or have a fileno() method.");
return -1;
}

if (fd < 0) {
PyErr_Format(PyExc_ValueError,
"file descriptor cannot be a negative integer (%d)", fd);
return -1;
}
return fd;
}

The fileno() protocol is the standard way CPython bridges Python file objects to OS-level file descriptors. It is used by os.dup, os.fstat, select.select, and many other system-call wrappers that accept either an integer or a file-like object as their first argument.

PyFile_WriteObject encoding (lines 250 to 400)

cpython 3.14 @ ab2d84fe1023/Objects/fileobject.c#L250-400

PyFile_WriteObject is the workhorse behind print() and traceback output. It converts the object to a string, then calls the file's .write() method. The flags argument controls whether str() or repr() is used for the conversion:

int
PyFile_WriteObject(PyObject *v, PyObject *f, int flags)
{
if (f == NULL || f == Py_None) {
/* Discard the output silently; matches CPython print behavior
when sys.stdout is None (frozen applications). */
return 0;
}

PyObject *writer = PyObject_GetAttr(f, &_Py_ID(write));
if (writer == NULL)
return -1;

PyObject *value;
if (flags & Py_PRINT_RAW) {
value = PyObject_Str(v);
}
else {
value = PyObject_Repr(v);
}
if (value == NULL) {
Py_DECREF(writer);
return -1;
}

PyObject *result = PyObject_CallOneArg(writer, value);
Py_DECREF(value);
Py_DECREF(writer);
if (result == NULL)
return -1;
Py_DECREF(result);
return 0;
}

The Py_PRINT_RAW flag is set by print() for its normal operands so that print(42) outputs 42 rather than repr(42). The flag is cleared when writing exception tracebacks so that unprintable objects still produce diagnostic output via their repr.

PyFile_WriteString is a thin wrapper around PyFile_WriteObject that accepts a C const char * string, converts it to a Python str, and delegates:

int
PyFile_WriteString(const char *s, PyObject *f)
{
PyObject *str = PyUnicode_FromString(s);
if (str == NULL) return -1;
int err = PyFile_WriteObject(str, f, Py_PRINT_RAW);
Py_DECREF(str);
return err;
}

sys.stdin/stdout/stderr accessors (lines 400 to 450)

cpython 3.14 @ ab2d84fe1023/Objects/fileobject.c#L400-450

The stdio accessors look up sys.stdin, sys.stdout, and sys.stderr from the interpreter state. This is a two-step process: first look in the current interpreter's sys module for the name, then fall back to the original __stdin__ / __stdout__ / __stderr__ values if the runtime replacement is None. The fallback exists so that C-level traceback printing remains functional even when user code replaces the standard streams:

PyObject *
_PySys_GetObjectId(PyInterpreterState *interp, _Py_Identifier *key)
{
PyObject *sd = interp->sysdict;
if (sd == NULL) return NULL;
return _PyDict_GetItemIdWithError(sd, key);
}

The interpreter calls these accessors in Python/traceback.c, Python/errors.c, and the print builtin in Python/bltinmodule.c. Any of them can return NULL when running in a subinterpreter that has not yet initialized its sys module, which is why each call site checks for NULL before using the result.

gopy mirror

objects/file.go for File, NewFile, FileType, fileGetattr, fileSetattr, fileRepr, fileIter, fileIterNext, fileMethod, and the Read, Write, Readline, Close, Flush methods. The fileno() protocol helper (AsFileDescriptor) and PyFile_WriteObject analog (FileWriteObject) are also in objects/file.go.

The sys.stdin/stdout/stderr accessors are exposed through module/sys/module.go as Stdin(), Stdout(), and Stderr() functions that look up the module attributes by name. This matches the CPython accessor pattern without requiring a direct pointer into interpreter state.

CPython 3.14 changes

Objects/fileobject.c has been stable since Python 2. PyFile_FromFd was added in 3.0 as part of the io module overhaul; before that, PyFile_FromString created a C FILE *-based file object. PyObject_AsFileDescriptor dates to Python 2 and has been unchanged since. PyFile_WriteObject grew the Py_PRINT_RAW flag in 2.6 when print became a function. No 3.14-specific changes affect this file.