Skip to content

Commit be84f90

Browse files
committed
pythongh-90102: Shortcut isatty in _io open()
1 parent 1a16077 commit be84f90

6 files changed

+26
-4
lines changed

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ struct _Py_global_strings {
248248
STRUCT_FOR_ID(_initializing)
249249
STRUCT_FOR_ID(_io)
250250
STRUCT_FOR_ID(_is_text_encoding)
251+
STRUCT_FOR_ID(_isatty_openonly)
251252
STRUCT_FOR_ID(_layout_)
252253
STRUCT_FOR_ID(_length_)
253254
STRUCT_FOR_ID(_limbo)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/_io/_iomodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,8 @@ _io_open_impl(PyObject *module, PyObject *file, const char *mode,
346346

347347
/* buffering */
348348
if (buffering < 0) {
349-
PyObject *res = PyObject_CallMethodNoArgs(raw, &_Py_ID(isatty));
349+
PyObject *res = PyObject_CallMethodNoArgs(raw,
350+
&_Py_ID(_isatty_openonly));
350351
if (res == NULL)
351352
goto error;
352353
isatty = PyObject_IsTrue(res);

Modules/_io/fileio.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
#ifdef HAVE_SYS_TYPES_H
1313
# include <sys/types.h>
1414
#endif
15-
#ifdef HAVE_SYS_STAT_H
16-
# include <sys/stat.h>
17-
#endif
1815
#ifdef HAVE_IO_H
1916
# include <io.h>
2017
#endif
@@ -1207,6 +1204,22 @@ _io_FileIO_isatty_impl(fileio *self)
12071204
return PyBool_FromLong(res);
12081205
}
12091206

1207+
/* Checks whether the file is a TTY using an open-only optimization.
1208+
1209+
Normally isatty always makes a system call. In the case of open() there
1210+
is a _inside the same python call_ stat result which we can use to
1211+
skip that system call for non-character files. Outsid of that context
1212+
this is subject to TOCTOU issues (the FD has been returned to user code
1213+
and arbitrary syscalls could have happened). */
1214+
static PyObject *
1215+
_io_FileIO_isatty_openonly(fileio *self, void *Py_UNUSED(ignored))
1216+
{
1217+
if (self->stat_atopen != NULL && !S_ISCHR(self->stat_atopen->st_mode)) {
1218+
Py_RETURN_FALSE;
1219+
}
1220+
return _io_FileIO_isatty_impl(self);
1221+
}
1222+
12101223
#include "clinic/fileio.c.h"
12111224

12121225
static PyMethodDef fileio_methods[] = {
@@ -1223,6 +1236,7 @@ static PyMethodDef fileio_methods[] = {
12231236
_IO_FILEIO_WRITABLE_METHODDEF
12241237
_IO_FILEIO_FILENO_METHODDEF
12251238
_IO_FILEIO_ISATTY_METHODDEF
1239+
{"_isatty_openonly", (PyCFunction)_io_FileIO_isatty_openonly, METH_NOARGS},
12261240
{"_dealloc_warn", (PyCFunction)fileio_dealloc_warn, METH_O, NULL},
12271241
{"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
12281242
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},

0 commit comments

Comments
 (0)