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

Skip to content

Commit 652fbf8

Browse files
gh-82626: Emit a warning when bool is used as a file descriptor (GH-111275)
1 parent 09096a1 commit 652fbf8

File tree

11 files changed

+75
-0
lines changed

11 files changed

+75
-0
lines changed

Doc/whatsnew/3.13.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ Other Language Changes
145145
is rejected when the global is used in the :keyword:`else` block.
146146
(Contributed by Irit Katriel in :gh:`111123`.)
147147

148+
* Many functions now emit a warning if a boolean value is passed as
149+
a file descriptor argument.
150+
This can help catch some errors earlier.
151+
(Contributed by Serhiy Storchaka in :gh:`82626`.)
152+
148153
* Added a new environment variable :envvar:`PYTHON_FROZEN_MODULES`. It
149154
determines whether or not frozen modules are ignored by the import machinery,
150155
equivalent of the :option:`-X frozen_modules <-X>` command-line option.

Lib/_pyio.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,11 @@ def __init__(self, file, mode='r', closefd=True, opener=None):
14951495
if isinstance(file, float):
14961496
raise TypeError('integer argument expected, got float')
14971497
if isinstance(file, int):
1498+
if isinstance(file, bool):
1499+
import warnings
1500+
warnings.warn("bool is used as a file descriptor",
1501+
RuntimeWarning, stacklevel=2)
1502+
file = int(file)
14981503
fd = file
14991504
if fd < 0:
15001505
raise ValueError('negative file descriptor')

Lib/test/test_fileio.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,14 @@ def testInvalidFd(self):
484484
import msvcrt
485485
self.assertRaises(OSError, msvcrt.get_osfhandle, make_bad_fd())
486486

487+
def testBooleanFd(self):
488+
for fd in False, True:
489+
with self.assertWarnsRegex(RuntimeWarning,
490+
'bool is used as a file descriptor') as cm:
491+
f = self.FileIO(fd, closefd=False)
492+
f.close()
493+
self.assertEqual(cm.filename, __file__)
494+
487495
def testBadModeArgument(self):
488496
# verify that we get a sensible error message for bad mode argument
489497
bad_mode = "qwerty"

Lib/test/test_genericpath.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,12 @@ def test_exists_fd(self):
165165
os.close(w)
166166
self.assertFalse(self.pathmodule.exists(r))
167167

168+
def test_exists_bool(self):
169+
for fd in False, True:
170+
with self.assertWarnsRegex(RuntimeWarning,
171+
'bool is used as a file descriptor'):
172+
self.pathmodule.exists(fd)
173+
168174
def test_isdir(self):
169175
filename = os_helper.TESTFN
170176
bfilename = os.fsencode(filename)

Lib/test/test_os.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2195,12 +2195,15 @@ def test_chmod(self):
21952195
class TestInvalidFD(unittest.TestCase):
21962196
singles = ["fchdir", "dup", "fdatasync", "fstat",
21972197
"fstatvfs", "fsync", "tcgetpgrp", "ttyname"]
2198+
singles_fildes = {"fchdir", "fdatasync", "fsync"}
21982199
#singles.append("close")
21992200
#We omit close because it doesn't raise an exception on some platforms
22002201
def get_single(f):
22012202
def helper(self):
22022203
if hasattr(os, f):
22032204
self.check(getattr(os, f))
2205+
if f in self.singles_fildes:
2206+
self.check_bool(getattr(os, f))
22042207
return helper
22052208
for f in singles:
22062209
locals()["test_"+f] = get_single(f)
@@ -2214,8 +2217,16 @@ def check(self, f, *args, **kwargs):
22142217
self.fail("%r didn't raise an OSError with a bad file descriptor"
22152218
% f)
22162219

2220+
def check_bool(self, f, *args, **kwargs):
2221+
with warnings.catch_warnings():
2222+
warnings.simplefilter("error", RuntimeWarning)
2223+
for fd in False, True:
2224+
with self.assertRaises(RuntimeWarning):
2225+
f(fd, *args, **kwargs)
2226+
22172227
def test_fdopen(self):
22182228
self.check(os.fdopen, encoding="utf-8")
2229+
self.check_bool(os.fdopen, encoding="utf-8")
22192230

22202231
@unittest.skipUnless(hasattr(os, 'isatty'), 'test needs os.isatty()')
22212232
def test_isatty(self):
@@ -2277,11 +2288,14 @@ def test_fchown(self):
22772288
def test_fpathconf(self):
22782289
self.check(os.pathconf, "PC_NAME_MAX")
22792290
self.check(os.fpathconf, "PC_NAME_MAX")
2291+
self.check_bool(os.pathconf, "PC_NAME_MAX")
2292+
self.check_bool(os.fpathconf, "PC_NAME_MAX")
22802293

22812294
@unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()')
22822295
def test_ftruncate(self):
22832296
self.check(os.truncate, 0)
22842297
self.check(os.ftruncate, 0)
2298+
self.check_bool(os.truncate, 0)
22852299

22862300
@unittest.skipUnless(hasattr(os, 'lseek'), 'test needs os.lseek()')
22872301
def test_lseek(self):

Lib/test/test_posix.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,13 @@ def test_stat_dir_fd(self):
15141514
self.assertRaises(OverflowError,
15151515
posix.stat, name, dir_fd=10**20)
15161516

1517+
for fd in False, True:
1518+
with self.assertWarnsRegex(RuntimeWarning,
1519+
'bool is used as a file descriptor') as cm:
1520+
with self.assertRaises(OSError):
1521+
posix.stat('nonexisting', dir_fd=fd)
1522+
self.assertEqual(cm.filename, __file__)
1523+
15171524
@unittest.skipUnless(os.utime in os.supports_dir_fd, "test needs dir_fd support in os.utime()")
15181525
def test_utime_dir_fd(self):
15191526
with self.prepare_file() as (dir_fd, name, fullname):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Many functions now emit a warning if a boolean value is passed as a file
2+
descriptor argument.

Modules/_io/fileio.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,13 @@ _io_FileIO___init___impl(fileio *self, PyObject *nameobj, const char *mode,
269269
self->fd = -1;
270270
}
271271

272+
if (PyBool_Check(nameobj)) {
273+
if (PyErr_WarnEx(PyExc_RuntimeWarning,
274+
"bool is used as a file descriptor", 1))
275+
{
276+
return -1;
277+
}
278+
}
272279
fd = PyLong_AsInt(nameobj);
273280
if (fd < 0) {
274281
if (!PyErr_Occurred()) {

Modules/faulthandler.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ faulthandler_get_fileno(PyObject **file_ptr)
119119
}
120120
}
121121
else if (PyLong_Check(file)) {
122+
if (PyBool_Check(file)) {
123+
if (PyErr_WarnEx(PyExc_RuntimeWarning,
124+
"bool is used as a file descriptor", 1))
125+
{
126+
return -1;
127+
}
128+
}
122129
fd = PyLong_AsInt(file);
123130
if (fd == -1 && PyErr_Occurred())
124131
return -1;

Modules/posixmodule.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,13 @@ _fd_converter(PyObject *o, int *p)
969969
int overflow;
970970
long long_value;
971971

972+
if (PyBool_Check(o)) {
973+
if (PyErr_WarnEx(PyExc_RuntimeWarning,
974+
"bool is used as a file descriptor", 1))
975+
{
976+
return 0;
977+
}
978+
}
972979
PyObject *index = _PyNumber_Index(o);
973980
if (index == NULL) {
974981
return 0;

Objects/fileobject.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,13 @@ PyObject_AsFileDescriptor(PyObject *o)
174174
PyObject *meth;
175175

176176
if (PyLong_Check(o)) {
177+
if (PyBool_Check(o)) {
178+
if (PyErr_WarnEx(PyExc_RuntimeWarning,
179+
"bool is used as a file descriptor", 1))
180+
{
181+
return -1;
182+
}
183+
}
177184
fd = PyLong_AsInt(o);
178185
}
179186
else if (PyObject_GetOptionalAttr(o, &_Py_ID(fileno), &meth) < 0) {

0 commit comments

Comments
 (0)