Skip to content

Commit ce12629

Browse files
authored
bpo-28369: Enhance transport socket check in add_reader/writer (#4365)
1 parent f76231f commit ce12629

File tree

4 files changed

+90
-1
lines changed

4 files changed

+90
-1
lines changed

Lib/asyncio/selector_events.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,16 @@ def _accept_connection2(self, protocol_factory, conn, extra,
246246
self.call_exception_handler(context)
247247

248248
def _ensure_fd_no_transport(self, fd):
249+
fileno = fd
250+
if not isinstance(fileno, int):
251+
try:
252+
fileno = int(fileno.fileno())
253+
except (AttributeError, TypeError, ValueError):
254+
# This code matches selectors._fileobj_to_fd function.
255+
raise ValueError("Invalid file object: "
256+
"{!r}".format(fd)) from None
249257
try:
250-
transport = self._transports[fd]
258+
transport = self._transports[fileno]
251259
except KeyError:
252260
pass
253261
else:

Lib/asyncio/test_utils.py

+7
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,13 @@ def assert_writer(self, fd, callback, *args):
361361
handle._args, args)
362362

363363
def _ensure_fd_no_transport(self, fd):
364+
if not isinstance(fd, int):
365+
try:
366+
fd = int(fd.fileno())
367+
except (AttributeError, TypeError, ValueError):
368+
# This code matches selectors._fileobj_to_fd function.
369+
raise ValueError("Invalid file object: "
370+
"{!r}".format(fd)) from None
364371
try:
365372
transport = self._transports[fd]
366373
except KeyError:

Lib/test/test_asyncio/test_unix_events.py

+70
Original file line numberDiff line numberDiff line change
@@ -1616,5 +1616,75 @@ def test_child_watcher_replace_mainloop_existing(self):
16161616
new_loop.close()
16171617

16181618

1619+
class TestFunctional(unittest.TestCase):
1620+
1621+
def setUp(self):
1622+
self.loop = asyncio.new_event_loop()
1623+
asyncio.set_event_loop(self.loop)
1624+
1625+
def tearDown(self):
1626+
self.loop.close()
1627+
asyncio.set_event_loop(None)
1628+
1629+
def test_add_reader_invalid_argument(self):
1630+
def assert_raises():
1631+
return self.assertRaisesRegex(ValueError, r'Invalid file object')
1632+
1633+
cb = lambda: None
1634+
1635+
with assert_raises():
1636+
self.loop.add_reader(object(), cb)
1637+
with assert_raises():
1638+
self.loop.add_writer(object(), cb)
1639+
1640+
with assert_raises():
1641+
self.loop.remove_reader(object())
1642+
with assert_raises():
1643+
self.loop.remove_writer(object())
1644+
1645+
def test_add_reader_or_writer_transport_fd(self):
1646+
def assert_raises():
1647+
return self.assertRaisesRegex(
1648+
RuntimeError,
1649+
r'File descriptor .* is used by transport')
1650+
1651+
async def runner():
1652+
tr, pr = await self.loop.create_connection(
1653+
lambda: asyncio.Protocol(), sock=rsock)
1654+
1655+
try:
1656+
cb = lambda: None
1657+
1658+
with assert_raises():
1659+
self.loop.add_reader(rsock, cb)
1660+
with assert_raises():
1661+
self.loop.add_reader(rsock.fileno(), cb)
1662+
1663+
with assert_raises():
1664+
self.loop.remove_reader(rsock)
1665+
with assert_raises():
1666+
self.loop.remove_reader(rsock.fileno())
1667+
1668+
with assert_raises():
1669+
self.loop.add_writer(rsock, cb)
1670+
with assert_raises():
1671+
self.loop.add_writer(rsock.fileno(), cb)
1672+
1673+
with assert_raises():
1674+
self.loop.remove_writer(rsock)
1675+
with assert_raises():
1676+
self.loop.remove_writer(rsock.fileno())
1677+
1678+
finally:
1679+
tr.close()
1680+
1681+
rsock, wsock = socket.socketpair()
1682+
try:
1683+
self.loop.run_until_complete(runner())
1684+
finally:
1685+
rsock.close()
1686+
wsock.close()
1687+
1688+
16191689
if __name__ == '__main__':
16201690
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Enhance add_reader/writer check that socket is not used by some transport.
2+
Before, only cases when add_reader/writer were called with an int FD were
3+
supported. Now the check is implemented correctly for all file-like
4+
objects.

0 commit comments

Comments
 (0)