Skip to main content

Modules/posixmodule.c (part 7)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/posixmodule.c

This annotation covers low-level POSIX file descriptor operations. See modules_posix6_detail for os.listdir, os.scandir, os.stat, os.lstat, and os.fstat.

Map

LinesSymbolRole
1-80os.openOpen a file, return an integer fd
81-160os.read / os.readvRead from a file descriptor
161-240os.write / os.writevWrite to a file descriptor
241-360os.closeClose a file descriptor
361-500os.pipe / os.pipe2Create a pipe pair

Reading

os.open

// CPython: Modules/posixmodule.c:4820 os_open_impl
static int
os_open_impl(PyObject *module, path_t *path, int flags, int mode, int dir_fd)
{
int fd;
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_OPENAT
if (dir_fd != DEFAULT_DIR_FD)
fd = openat(dir_fd, path->narrow, flags, mode);
else
#endif
fd = open(path->narrow, flags, mode);
Py_END_ALLOW_THREADS
if (fd < 0) return posix_error();
return fd;
}

os.open returns a raw integer file descriptor, unlike open() which returns a buffered file object. flags uses os.O_RDONLY, os.O_WRONLY, os.O_CREAT, etc. The dir_fd parameter enables openat for directory-relative paths.

os.read

// CPython: Modules/posixmodule.c:5020 os_read_impl
static PyObject *
os_read_impl(PyObject *module, int fd, Py_ssize_t length)
{
PyObject *buffer = PyBytes_FromStringAndSize(NULL, length);
Py_BEGIN_ALLOW_THREADS
n = read(fd, PyBytes_AS_STRING(buffer), length);
Py_END_ALLOW_THREADS
if (n < 0) {
Py_DECREF(buffer);
return posix_error();
}
if (n != length) _PyBytes_Resize(&buffer, n);
return buffer;
}

os.read(fd, 4096) reads up to 4096 bytes and returns a bytes object. The GIL is released during the syscall. If fewer than length bytes are available (EOF or partial read), the returned bytes object is resized.

os.write

// CPython: Modules/posixmodule.c:5060 os_write_impl
static Py_ssize_t
os_write_impl(PyObject *module, int fd, Py_buffer *data)
{
Py_ssize_t size;
Py_BEGIN_ALLOW_THREADS
size = write(fd, data->buf, (size_t)data->len);
Py_END_ALLOW_THREADS
if (size < 0) return posix_error();
return size;
}

os.write(fd, b'hello') returns the number of bytes actually written (may be less than requested on some file types). Accepts any buffer protocol object.

os.close

// CPython: Modules/posixmodule.c:5100 os_close_impl
static PyObject *
os_close_impl(PyObject *module, int fd)
{
int res;
Py_BEGIN_ALLOW_THREADS
res = close(fd);
Py_END_ALLOW_THREADS
if (res < 0) return posix_error();
Py_RETURN_NONE;
}

os.close(fd) must be called once for each file descriptor returned by os.open or os.pipe. Closing an already-closed fd raises OSError: [Errno 9] Bad file descriptor.

os.pipe

// CPython: Modules/posixmodule.c:5140 os_pipe_impl
static PyObject *
os_pipe_impl(PyObject *module)
{
int fds[2];
int res;
Py_BEGIN_ALLOW_THREADS
res = pipe(fds);
Py_END_ALLOW_THREADS
if (res < 0) return posix_error();
return Py_BuildValue("(ii)", fds[0], fds[1]);
}

r, w = os.pipe() returns (read_fd, write_fd). Data written to w can be read from r. Used for inter-process communication: parent keeps one end, child keeps the other. os.pipe2(os.O_CLOEXEC) creates the pipe with O_CLOEXEC set atomically.

gopy notes

os.open is module/os.Open in module/os/module.go. It calls syscall.Open. os.read calls syscall.Read and returns objects.Bytes. os.write calls syscall.Write. os.close calls syscall.Close. os.pipe calls syscall.Pipe and returns a 2-tuple of ints.