[3.6] bpo-32356: idempotent pause_/resume_reading (GH-4914) (GH-7629) · python/cpython@142e3c0 (original) (raw)

7 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -124,11 +124,19 @@ ReadTransport
124 124 the protocol's :meth:`data_received` method until :meth:`resume_reading`
125 125 is called.
126 126
127 + .. versionchanged:: 3.6.7
128 + The method is idempotent, i.e. it can be called when the
129 + transport is already paused or closed.
130 +
127 131 .. method:: resume_reading()
128 132
129 133 Resume the receiving end. The protocol's :meth:`data_received` method
130 134 will be called once again if some data is available for reading.
131 135
136 + .. versionchanged:: 3.6.7
137 + The method is idempotent, i.e. it can be called when the
138 + transport is already reading.
139 +
132 140
133 141 WriteTransport
134 142 --------------
Original file line number Diff line number Diff line change
@@ -160,20 +160,16 @@ def __init__(self, loop, sock, protocol, waiter=None,
160 160 self._loop.call_soon(self._loop_reading)
161 161
162 162 def pause_reading(self):
163 -if self._closing:
164 -raise RuntimeError('Cannot pause_reading() when closing')
165 -if self._paused:
166 -raise RuntimeError('Already paused')
163 +if self._closing or self._paused:
164 +return
167 165 self._paused = True
168 166 if self._loop.get_debug():
169 167 logger.debug("%r pauses reading", self)
170 168
171 169 def resume_reading(self):
172 -if not self._paused:
173 -raise RuntimeError('Not paused')
174 -self._paused = False
175 -if self._closing:
170 +if self._closing or not self._paused:
176 171 return
172 +self._paused = False
177 173 if self._reschedule_on_resume:
178 174 self._loop.call_soon(self._loop_reading, self._read_fut)
179 175 self._reschedule_on_resume = False
Original file line number Diff line number Diff line change
@@ -703,18 +703,16 @@ def __init__(self, loop, sock, protocol, waiter=None,
703 703 waiter, None)
704 704
705 705 def pause_reading(self):
706 -if self._closing:
707 -raise RuntimeError('Cannot pause_reading() when closing')
708 -if self._paused:
709 -raise RuntimeError('Already paused')
706 +if self._closing or self._paused:
707 +return
710 708 self._paused = True
711 709 self._loop._remove_reader(self._sock_fd)
712 710 if self._loop.get_debug():
713 711 logger.debug("%r pauses reading", self)
714 712
715 713 def resume_reading(self):
716 -if not self._paused:
717 -raise RuntimeError('Not paused')
714 +if self._closing or not self._paused:
715 +return
718 716 self._paused = False
719 717 self._add_reader(self._sock_fd, self._read_ready)
720 718 if self._loop.get_debug():
Original file line number Diff line number Diff line change
@@ -335,12 +335,19 @@ def _remove_reader(self, fd):
335 335 return False
336 336
337 337 def assert_reader(self, fd, callback, *args):
338 -assert fd in self.readers, 'fd {} is not registered'.format(fd)
338 +if fd not in self.readers:
339 +raise AssertionError(f'fd {fd} is not registered')
339 340 handle = self.readers[fd]
340 -assert handle._callback == callback, '{!r} != {!r}'.format(
341 -handle._callback, callback)
342 -assert handle._args == args, '{!r} != {!r}'.format(
343 -handle._args, args)
341 +if handle._callback != callback:
342 +raise AssertionError(
343 +f'unexpected callback: {handle._callback} != {callback}')
344 +if handle._args != args:
345 +raise AssertionError(
346 +f'unexpected callback args: {handle._args} != {args}')
347 +
348 +def assert_no_reader(self, fd):
349 +if fd in self.readers:
350 +raise AssertionError(f'fd {fd} is registered')
344 351
345 352 def _add_writer(self, fd, callback, *args):
346 353 self.writers[fd] = events.Handle(callback, args, self)
Original file line number Diff line number Diff line change
@@ -334,18 +334,23 @@ def test_pause_resume_reading(self):
334 334 f = asyncio.Future(loop=self.loop)
335 335 f.set_result(msg)
336 336 futures.append(f)
337 +
337 338 self.loop._proactor.recv.side_effect = futures
338 339 self.loop._run_once()
339 340 self.assertFalse(tr._paused)
340 341 self.loop._run_once()
341 342 self.protocol.data_received.assert_called_with(b'data1')
342 343 self.loop._run_once()
343 344 self.protocol.data_received.assert_called_with(b'data2')
345 +
346 +tr.pause_reading()
344 347 tr.pause_reading()
345 348 self.assertTrue(tr._paused)
346 349 for i in range(10):
347 350 self.loop._run_once()
348 351 self.protocol.data_received.assert_called_with(b'data2')
352 +
353 +tr.resume_reading()
349 354 tr.resume_reading()
350 355 self.assertFalse(tr._paused)
351 356 self.loop._run_once()
Original file line number Diff line number Diff line change
@@ -81,6 +81,7 @@ def test_make_ssl_transport(self):
81 81 with test_utils.disable_logger():
82 82 transport = self.loop._make_ssl_transport(
83 83 m, asyncio.Protocol(), m, waiter)
84 +
84 85 # execute the handshake while the logger is disabled
85 86 # to ignore SSL handshake failure
86 87 test_utils.run_briefly(self.loop)
@@ -884,14 +885,19 @@ def test_pause_resume_reading(self):
884 885 test_utils.run_briefly(self.loop)
885 886 self.assertFalse(tr._paused)
886 887 self.loop.assert_reader(7, tr._read_ready)
888 +
889 +tr.pause_reading()
887 890 tr.pause_reading()
888 891 self.assertTrue(tr._paused)
889 -self.assertFalse(7 in self.loop.readers)
892 +self.loop.assert_no_reader(7)
893 +
894 +tr.resume_reading()
890 895 tr.resume_reading()
891 896 self.assertFalse(tr._paused)
892 897 self.loop.assert_reader(7, tr._read_ready)
893 -with self.assertRaises(RuntimeError):
894 -tr.resume_reading()
898 +
899 +tr.close()
900 +self.loop.assert_no_reader(7)
895 901
896 902 def test_read_ready(self):
897 903 transport = self.socket_transport()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1 +asyncio.transport.resume_reading() and pause_reading() are now idempotent.