Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cc9b9be

Browse filesBrowse files
cmaloneyvstinner
andauthored
gh-90102: Remove isatty call during regular open (#124922)
Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 6e3c70c commit cc9b9be
Copy full SHA for cc9b9be

File tree

Expand file treeCollapse file tree

9 files changed

+47
-5
lines changed
Filter options
Expand file treeCollapse file tree

9 files changed

+47
-5
lines changed

‎Include/internal/pycore_global_objects_fini_generated.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_global_objects_fini_generated.h
+1Lines 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

Copy file name to clipboardExpand all lines: Include/internal/pycore_global_strings.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ struct _Py_global_strings {
246246
STRUCT_FOR_ID(_initializing)
247247
STRUCT_FOR_ID(_io)
248248
STRUCT_FOR_ID(_is_text_encoding)
249+
STRUCT_FOR_ID(_isatty_open_only)
249250
STRUCT_FOR_ID(_length_)
250251
STRUCT_FOR_ID(_limbo)
251252
STRUCT_FOR_ID(_lock_unlock_module)

‎Include/internal/pycore_runtime_init_generated.h

Copy file name to clipboardExpand all lines: Include/internal/pycore_runtime_init_generated.h
+1Lines 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

Copy file name to clipboardExpand all lines: Include/internal/pycore_unicodeobject_generated.h
+4Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Lib/_pyio.py

Copy file name to clipboardExpand all lines: Lib/_pyio.py
+16-1Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ def open(file, mode="r", buffering=-1, encoding=None, errors=None,
238238
result = raw
239239
try:
240240
line_buffering = False
241-
if buffering == 1 or buffering < 0 and raw.isatty():
241+
if buffering == 1 or buffering < 0 and raw._isatty_open_only():
242242
buffering = -1
243243
line_buffering = True
244244
if buffering < 0:
@@ -1794,6 +1794,21 @@ def isatty(self):
17941794
self._checkClosed()
17951795
return os.isatty(self._fd)
17961796

1797+
def _isatty_open_only(self):
1798+
"""Checks whether the file is a TTY using an open-only optimization.
1799+
1800+
TTYs are always character devices. If the interpreter knows a file is
1801+
not a character device when it would call ``isatty``, can skip that
1802+
call. Inside ``open()`` there is a fresh stat result that contains that
1803+
information. Use the stat result to skip a system call. Outside of that
1804+
context TOCTOU issues (the fd could be arbitrarily modified by
1805+
surrounding code).
1806+
"""
1807+
if (self._stat_atopen is not None
1808+
and not stat.S_ISCHR(self._stat_atopen.st_mode)):
1809+
return True
1810+
return os.isatty(self._fd)
1811+
17971812
@property
17981813
def closefd(self):
17991814
"""True if the file descriptor will be closed by close()."""
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Skip the ``isatty`` system call during open() when the file is known to not
2+
be a character device. This provides a slight performance improvement when
3+
reading whole files.

‎Modules/_io/_iomodule.c

Copy file name to clipboardExpand all lines: Modules/_io/_iomodule.c
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ _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, &_Py_ID(_isatty_open_only));
350350
if (res == NULL)
351351
goto error;
352352
isatty = PyObject_IsTrue(res);

‎Modules/_io/fileio.c

Copy file name to clipboardExpand all lines: Modules/_io/fileio.c
+19-3Lines changed: 19 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
@@ -1218,6 +1215,24 @@ _io_FileIO_isatty_impl(fileio *self)
12181215
return PyBool_FromLong(res);
12191216
}
12201217

1218+
/* Checks whether the file is a TTY using an open-only optimization.
1219+
1220+
TTYs are always character devices. If the interpreter knows a file is
1221+
not a character device when it would call ``isatty``, can skip that
1222+
call. Inside ``open()`` there is a fresh stat result that contains that
1223+
information. Use the stat result to skip a system call. Outside of that
1224+
context TOCTOU issues (the fd could be arbitrarily modified by
1225+
surrounding code). */
1226+
static PyObject *
1227+
_io_FileIO_isatty_open_only(PyObject *op, PyObject *Py_UNUSED(ignored))
1228+
{
1229+
fileio *self = _PyFileIO_CAST(op);
1230+
if (self->stat_atopen != NULL && !S_ISCHR(self->stat_atopen->st_mode)) {
1231+
Py_RETURN_FALSE;
1232+
}
1233+
return _io_FileIO_isatty_impl(self);
1234+
}
1235+
12211236
#include "clinic/fileio.c.h"
12221237

12231238
static PyMethodDef fileio_methods[] = {
@@ -1234,6 +1249,7 @@ static PyMethodDef fileio_methods[] = {
12341249
_IO_FILEIO_WRITABLE_METHODDEF
12351250
_IO_FILEIO_FILENO_METHODDEF
12361251
_IO_FILEIO_ISATTY_METHODDEF
1252+
{"_isatty_open_only", _io_FileIO_isatty_open_only, METH_NOARGS},
12371253
{"_dealloc_warn", fileio_dealloc_warn, METH_O, NULL},
12381254
{"__reduce__", _PyIOBase_cannot_pickle, METH_NOARGS},
12391255
{"__reduce_ex__", _PyIOBase_cannot_pickle, METH_O},

‎Modules/_io/winconsoleio.c

Copy file name to clipboardExpand all lines: Modules/_io/winconsoleio.c
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,7 @@ static PyMethodDef winconsoleio_methods[] = {
11281128
_IO__WINDOWSCONSOLEIO_WRITABLE_METHODDEF
11291129
_IO__WINDOWSCONSOLEIO_FILENO_METHODDEF
11301130
_IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF
1131+
{"_isatty_open_only", (PyCFunction)_io__WindowsConsoleIO_isatty, METH_NOARGS},
11311132
{NULL, NULL} /* sentinel */
11321133
};
11331134

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.