Thanks to visit codestin.com
Credit goes to github.com

Skip to content

gh-82626: Emit a warning when bool is used as a file descriptor #111275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ Other Language Changes
is rejected when the global is used in the :keyword:`else` block.
(Contributed by Irit Katriel in :gh:`111123`.)

* Many functions now emit a warning if a boolean value is passed as
a file descriptor argument.
This can help catch some errors earlier.
(Contributed by Serhiy Storchaka in :gh:`82626`.)

* Added a new environment variable :envvar:`PYTHON_FROZEN_MODULES`. It
determines whether or not frozen modules are ignored by the import machinery,
equivalent of the :option:`-X frozen_modules <-X>` command-line option.
Expand Down
5 changes: 5 additions & 0 deletions Lib/_pyio.py
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,11 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
if isinstance(file, float):
raise TypeError('integer argument expected, got float')
if isinstance(file, int):
if isinstance(file, bool):
import warnings
warnings.warn("bool is used as a file descriptor",
RuntimeWarning, stacklevel=2)
file = int(file)
fd = file
if fd < 0:
raise ValueError('negative file descriptor')
Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_fileio.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,14 @@ def testInvalidFd(self):
import msvcrt
self.assertRaises(OSError, msvcrt.get_osfhandle, make_bad_fd())

def testBooleanFd(self):
for fd in False, True:
with self.assertWarnsRegex(RuntimeWarning,
'bool is used as a file descriptor') as cm:
f = self.FileIO(fd, closefd=False)
f.close()
self.assertEqual(cm.filename, __file__)

def testBadModeArgument(self):
# verify that we get a sensible error message for bad mode argument
bad_mode = "qwerty"
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_genericpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ def test_exists_fd(self):
os.close(w)
self.assertFalse(self.pathmodule.exists(r))

def test_exists_bool(self):
for fd in False, True:
with self.assertWarnsRegex(RuntimeWarning,
'bool is used as a file descriptor'):
self.pathmodule.exists(fd)

def test_isdir(self):
filename = os_helper.TESTFN
bfilename = os.fsencode(filename)
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -2195,12 +2195,15 @@ def test_chmod(self):
class TestInvalidFD(unittest.TestCase):
singles = ["fchdir", "dup", "fdatasync", "fstat",
"fstatvfs", "fsync", "tcgetpgrp", "ttyname"]
singles_fildes = {"fchdir", "fdatasync", "fsync"}
#singles.append("close")
#We omit close because it doesn't raise an exception on some platforms
def get_single(f):
def helper(self):
if hasattr(os, f):
self.check(getattr(os, f))
if f in self.singles_fildes:
self.check_bool(getattr(os, f))
return helper
for f in singles:
locals()["test_"+f] = get_single(f)
Expand All @@ -2214,8 +2217,16 @@ def check(self, f, *args, **kwargs):
self.fail("%r didn't raise an OSError with a bad file descriptor"
% f)

def check_bool(self, f, *args, **kwargs):
with warnings.catch_warnings():
warnings.simplefilter("error", RuntimeWarning)
for fd in False, True:
with self.assertRaises(RuntimeWarning):
f(fd, *args, **kwargs)

def test_fdopen(self):
self.check(os.fdopen, encoding="utf-8")
self.check_bool(os.fdopen, encoding="utf-8")

@unittest.skipUnless(hasattr(os, 'isatty'), 'test needs os.isatty()')
def test_isatty(self):
Expand Down Expand Up @@ -2277,11 +2288,14 @@ def test_fchown(self):
def test_fpathconf(self):
self.check(os.pathconf, "PC_NAME_MAX")
self.check(os.fpathconf, "PC_NAME_MAX")
self.check_bool(os.pathconf, "PC_NAME_MAX")
self.check_bool(os.fpathconf, "PC_NAME_MAX")

@unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()')
def test_ftruncate(self):
self.check(os.truncate, 0)
self.check(os.ftruncate, 0)
self.check_bool(os.truncate, 0)

@unittest.skipUnless(hasattr(os, 'lseek'), 'test needs os.lseek()')
def test_lseek(self):
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,13 @@ def test_stat_dir_fd(self):
self.assertRaises(OverflowError,
posix.stat, name, dir_fd=10**20)

for fd in False, True:
with self.assertWarnsRegex(RuntimeWarning,
'bool is used as a file descriptor') as cm:
with self.assertRaises(OSError):
posix.stat('nonexisting', dir_fd=fd)
self.assertEqual(cm.filename, __file__)

@unittest.skipUnless(os.utime in os.supports_dir_fd, "test needs dir_fd support in os.utime()")
def test_utime_dir_fd(self):
with self.prepare_file() as (dir_fd, name, fullname):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Many functions now emit a warning if a boolean value is passed as a file
descriptor argument.
7 changes: 7 additions & 0 deletions Modules/_io/fileio.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
self->fd = -1;
}

if (PyBool_Check(nameobj)) {
if (PyErr_WarnEx(PyExc_RuntimeWarning,
"bool is used as a file descriptor", 1))
{
return -1;
}
}
fd = PyLong_AsInt(nameobj);
if (fd < 0) {
if (!PyErr_Occurred()) {
Expand Down
7 changes: 7 additions & 0 deletions Modules/faulthandler.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ faulthandler_get_fileno(PyObject **file_ptr)
}
}
else if (PyLong_Check(file)) {
if (PyBool_Check(file)) {
if (PyErr_WarnEx(PyExc_RuntimeWarning,
"bool is used as a file descriptor", 1))
{
return -1;
}
}
fd = PyLong_AsInt(file);
if (fd == -1 && PyErr_Occurred())
return -1;
Expand Down
7 changes: 7 additions & 0 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,13 @@ _fd_converter(PyObject *o, int *p)
int overflow;
long long_value;

if (PyBool_Check(o)) {
if (PyErr_WarnEx(PyExc_RuntimeWarning,
"bool is used as a file descriptor", 1))
{
return 0;
}
}
PyObject *index = _PyNumber_Index(o);
if (index == NULL) {
return 0;
Expand Down
7 changes: 7 additions & 0 deletions Objects/fileobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ PyObject_AsFileDescriptor(PyObject *o)
PyObject *meth;

if (PyLong_Check(o)) {
if (PyBool_Check(o)) {
if (PyErr_WarnEx(PyExc_RuntimeWarning,
"bool is used as a file descriptor", 1))
{
return -1;
}
}
fd = PyLong_AsInt(o);
}
else if (PyObject_GetOptionalAttr(o, &_Py_ID(fileno), &meth) < 0) {
Expand Down