Skip to content

Commit 22feeb8

Browse files
authored
bpo-32643: Drop support for a few private Task and Future APIs. (#5293)
Specifically, it's not possible to subclass Task/Future classes and override the following methods: * Future._schedule_callbacks * Task._step * Task._wakeup
1 parent 8ded5b8 commit 22feeb8

File tree

7 files changed

+33
-229
lines changed

7 files changed

+33
-229
lines changed

Lib/asyncio/futures.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,10 @@ def cancel(self):
131131
if self._state != _PENDING:
132132
return False
133133
self._state = _CANCELLED
134-
self._schedule_callbacks()
134+
self.__schedule_callbacks()
135135
return True
136136

137-
def _schedule_callbacks(self):
137+
def __schedule_callbacks(self):
138138
"""Internal: Ask the event loop to call all callbacks.
139139
140140
The callbacks are scheduled to be called as soon as possible. Also
@@ -234,7 +234,7 @@ def set_result(self, result):
234234
raise InvalidStateError('{}: {!r}'.format(self._state, self))
235235
self._result = result
236236
self._state = _FINISHED
237-
self._schedule_callbacks()
237+
self.__schedule_callbacks()
238238

239239
def set_exception(self, exception):
240240
"""Mark the future done and set an exception.
@@ -251,7 +251,7 @@ def set_exception(self, exception):
251251
"and cannot be raised into a Future")
252252
self._exception = exception
253253
self._state = _FINISHED
254-
self._schedule_callbacks()
254+
self.__schedule_callbacks()
255255
self.__log_traceback = True
256256

257257
def __await__(self):

Lib/asyncio/tasks.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def __init__(self, coro, *, loop=None):
9999
self._coro = coro
100100
self._context = contextvars.copy_context()
101101

102-
self._loop.call_soon(self._step, context=self._context)
102+
self._loop.call_soon(self.__step, context=self._context)
103103
_register_task(self)
104104

105105
def __del__(self):
@@ -185,11 +185,11 @@ def cancel(self):
185185
# catches and ignores the cancellation so we may have
186186
# to cancel it again later.
187187
return True
188-
# It must be the case that self._step is already scheduled.
188+
# It must be the case that self.__step is already scheduled.
189189
self._must_cancel = True
190190
return True
191191

192-
def _step(self, exc=None):
192+
def __step(self, exc=None):
193193
if self.done():
194194
raise futures.InvalidStateError(
195195
f'_step(): already done: {self!r}, {exc!r}')
@@ -232,17 +232,17 @@ def _step(self, exc=None):
232232
f'Task {self!r} got Future '
233233
f'{result!r} attached to a different loop')
234234
self._loop.call_soon(
235-
self._step, new_exc, context=self._context)
235+
self.__step, new_exc, context=self._context)
236236
elif blocking:
237237
if result is self:
238238
new_exc = RuntimeError(
239239
f'Task cannot await on itself: {self!r}')
240240
self._loop.call_soon(
241-
self._step, new_exc, context=self._context)
241+
self.__step, new_exc, context=self._context)
242242
else:
243243
result._asyncio_future_blocking = False
244244
result.add_done_callback(
245-
self._wakeup, context=self._context)
245+
self.__wakeup, context=self._context)
246246
self._fut_waiter = result
247247
if self._must_cancel:
248248
if self._fut_waiter.cancel():
@@ -252,41 +252,41 @@ def _step(self, exc=None):
252252
f'yield was used instead of yield from '
253253
f'in task {self!r} with {result!r}')
254254
self._loop.call_soon(
255-
self._step, new_exc, context=self._context)
255+
self.__step, new_exc, context=self._context)
256256

257257
elif result is None:
258258
# Bare yield relinquishes control for one event loop iteration.
259-
self._loop.call_soon(self._step, context=self._context)
259+
self._loop.call_soon(self.__step, context=self._context)
260260
elif inspect.isgenerator(result):
261261
# Yielding a generator is just wrong.
262262
new_exc = RuntimeError(
263263
f'yield was used instead of yield from for '
264264
f'generator in task {self!r} with {result}')
265265
self._loop.call_soon(
266-
self._step, new_exc, context=self._context)
266+
self.__step, new_exc, context=self._context)
267267
else:
268268
# Yielding something else is an error.
269269
new_exc = RuntimeError(f'Task got bad yield: {result!r}')
270270
self._loop.call_soon(
271-
self._step, new_exc, context=self._context)
271+
self.__step, new_exc, context=self._context)
272272
finally:
273273
_leave_task(self._loop, self)
274274
self = None # Needed to break cycles when an exception occurs.
275275

276-
def _wakeup(self, future):
276+
def __wakeup(self, future):
277277
try:
278278
future.result()
279279
except Exception as exc:
280280
# This may also be a cancellation.
281-
self._step(exc)
281+
self.__step(exc)
282282
else:
283283
# Don't pass the value of `future.result()` explicitly,
284284
# as `Future.__iter__` and `Future.__await__` don't need it.
285285
# If we call `_step(value, None)` instead of `_step()`,
286286
# Python eval loop would use `.send(value)` method call,
287287
# instead of `__next__()`, which is slower for futures
288288
# that return non-generator iterators from their `__iter__`.
289-
self._step()
289+
self.__step()
290290
self = None # Needed to break cycles when an exception occurs.
291291

292292

@@ -513,7 +513,7 @@ def __sleep0():
513513
514514
This is a private helper for 'asyncio.sleep()', used
515515
when the 'delay' is set to 0. It uses a bare 'yield'
516-
expression (which Task._step knows how to handle)
516+
expression (which Task.__step knows how to handle)
517517
instead of creating a Future object.
518518
"""
519519
yield

Lib/test/test_asyncio/test_futures.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,6 @@ def test_uninitialized(self):
175175
with self.assertRaises((RuntimeError, AttributeError)):
176176
fut.remove_done_callback(lambda f: None)
177177

178-
fut = self.cls.__new__(self.cls, loop=self.loop)
179-
with self.assertRaises((RuntimeError, AttributeError)):
180-
fut._schedule_callbacks()
181-
182178
fut = self.cls.__new__(self.cls, loop=self.loop)
183179
try:
184180
repr(fut)

Lib/test/test_asyncio/test_tasks.py

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,17 +1401,6 @@ def wait_for_future():
14011401
self.assertTrue(t.done())
14021402
self.assertIsNone(t.result())
14031403

1404-
def test_step_with_baseexception(self):
1405-
@asyncio.coroutine
1406-
def notmutch():
1407-
raise BaseException()
1408-
1409-
task = self.new_task(self.loop, notmutch())
1410-
self.assertRaises(BaseException, task._step)
1411-
1412-
self.assertTrue(task.done())
1413-
self.assertIsInstance(task.exception(), BaseException)
1414-
14151404
def test_baseexception_during_cancel(self):
14161405

14171406
def gen():
@@ -2275,22 +2264,12 @@ def __init__(self, *args, **kwargs):
22752264
self.calls = collections.defaultdict(lambda: 0)
22762265
super().__init__(*args, **kwargs)
22772266

2278-
def _schedule_callbacks(self):
2279-
self.calls['_schedule_callbacks'] += 1
2280-
return super()._schedule_callbacks()
2281-
22822267
def add_done_callback(self, *args, **kwargs):
22832268
self.calls['add_done_callback'] += 1
22842269
return super().add_done_callback(*args, **kwargs)
22852270

22862271
class Task(CommonFuture, BaseTask):
2287-
def _step(self, *args):
2288-
self.calls['_step'] += 1
2289-
return super()._step(*args)
2290-
2291-
def _wakeup(self, *args):
2292-
self.calls['_wakeup'] += 1
2293-
return super()._wakeup(*args)
2272+
pass
22942273

22952274
class Future(CommonFuture, BaseFuture):
22962275
pass
@@ -2310,12 +2289,11 @@ async def func():
23102289

23112290
self.assertEqual(
23122291
dict(task.calls),
2313-
{'_step': 2, '_wakeup': 1, 'add_done_callback': 1,
2314-
'_schedule_callbacks': 1})
2292+
{'add_done_callback': 1})
23152293

23162294
self.assertEqual(
23172295
dict(fut.calls),
2318-
{'add_done_callback': 1, '_schedule_callbacks': 1})
2296+
{'add_done_callback': 1})
23192297

23202298
# Add patched Task & Future back to the test case
23212299
cls.Task = Task
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make Task._step, Task._wakeup and Future._schedule_callbacks methods
2+
private.

0 commit comments

Comments
 (0)