Skip to content

Commit 740169c

Browse files
author
Yury Selivanov
committed
Sync asyncio changes from the main repo.
1 parent 37c4f78 commit 740169c

5 files changed

Lines changed: 94 additions & 16 deletions

File tree

Lib/asyncio/base_events.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ def __init__(self):
197197
# exceed this duration in seconds, the slow callback/task is logged.
198198
self.slow_callback_duration = 0.1
199199
self._current_handle = None
200+
self._task_factory = None
200201

201202
def __repr__(self):
202203
return ('<%s running=%s closed=%s debug=%s>'
@@ -209,11 +210,32 @@ def create_task(self, coro):
209210
Return a task object.
210211
"""
211212
self._check_closed()
212-
task = tasks.Task(coro, loop=self)
213-
if task._source_traceback:
214-
del task._source_traceback[-1]
213+
if self._task_factory is None:
214+
task = tasks.Task(coro, loop=self)
215+
if task._source_traceback:
216+
del task._source_traceback[-1]
217+
else:
218+
task = self._task_factory(self, coro)
215219
return task
216220

221+
def set_task_factory(self, factory):
222+
"""Set a task factory that will be used by loop.create_task().
223+
224+
If factory is None the default task factory will be set.
225+
226+
If factory is a callable, it should have a signature matching
227+
'(loop, coro)', where 'loop' will be a reference to the active
228+
event loop, 'coro' will be a coroutine object. The callable
229+
must return a Future.
230+
"""
231+
if factory is not None and not callable(factory):
232+
raise TypeError('task factory must be a callable or None')
233+
self._task_factory = factory
234+
235+
def get_task_factory(self):
236+
"""Return a task factory, or None if the default one is in use."""
237+
return self._task_factory
238+
217239
def _make_socket_transport(self, sock, protocol, waiter=None, *,
218240
extra=None, server=None):
219241
"""Create socket transport."""
@@ -465,25 +487,25 @@ def call_soon_threadsafe(self, callback, *args):
465487
self._write_to_self()
466488
return handle
467489

468-
def run_in_executor(self, executor, callback, *args):
469-
if (coroutines.iscoroutine(callback)
470-
or coroutines.iscoroutinefunction(callback)):
490+
def run_in_executor(self, executor, func, *args):
491+
if (coroutines.iscoroutine(func)
492+
or coroutines.iscoroutinefunction(func)):
471493
raise TypeError("coroutines cannot be used with run_in_executor()")
472494
self._check_closed()
473-
if isinstance(callback, events.Handle):
495+
if isinstance(func, events.Handle):
474496
assert not args
475-
assert not isinstance(callback, events.TimerHandle)
476-
if callback._cancelled:
497+
assert not isinstance(func, events.TimerHandle)
498+
if func._cancelled:
477499
f = futures.Future(loop=self)
478500
f.set_result(None)
479501
return f
480-
callback, args = callback._callback, callback._args
502+
func, args = func._callback, func._args
481503
if executor is None:
482504
executor = self._default_executor
483505
if executor is None:
484506
executor = concurrent.futures.ThreadPoolExecutor(_MAX_WORKERS)
485507
self._default_executor = executor
486-
return futures.wrap_future(executor.submit(callback, *args), loop=self)
508+
return futures.wrap_future(executor.submit(func, *args), loop=self)
487509

488510
def set_default_executor(self, executor):
489511
self._default_executor = executor

Lib/asyncio/events.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def create_task(self, coro):
277277
def call_soon_threadsafe(self, callback, *args):
278278
raise NotImplementedError
279279

280-
def run_in_executor(self, executor, callback, *args):
280+
def run_in_executor(self, executor, func, *args):
281281
raise NotImplementedError
282282

283283
def set_default_executor(self, executor):
@@ -438,6 +438,14 @@ def add_signal_handler(self, sig, callback, *args):
438438
def remove_signal_handler(self, sig):
439439
raise NotImplementedError
440440

441+
# Task factory.
442+
443+
def set_task_factory(self, factory):
444+
raise NotImplementedError
445+
446+
def get_task_factory(self):
447+
raise NotImplementedError
448+
441449
# Error handlers.
442450

443451
def set_exception_handler(self, handler):

Lib/test/test_asyncio/test_base_events.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@
1616
from asyncio import test_utils
1717
try:
1818
from test import support
19-
from test.script_helper import assert_python_ok
2019
except ImportError:
2120
from asyncio import test_support as support
22-
from asyncio.test_support import assert_python_ok
21+
try:
22+
from test.support.script_helper import assert_python_ok
23+
except ImportError:
24+
try:
25+
from test.script_helper import assert_python_ok
26+
except ImportError:
27+
from asyncio.test_support import assert_python_ok
2328

2429

2530
MOCK_ANY = mock.ANY
@@ -623,6 +628,42 @@ def custom_handler(loop, context):
623628
self.assertIs(type(_context['context']['exception']),
624629
ZeroDivisionError)
625630

631+
def test_set_task_factory_invalid(self):
632+
with self.assertRaisesRegex(
633+
TypeError, 'task factory must be a callable or None'):
634+
635+
self.loop.set_task_factory(1)
636+
637+
self.assertIsNone(self.loop.get_task_factory())
638+
639+
def test_set_task_factory(self):
640+
self.loop._process_events = mock.Mock()
641+
642+
class MyTask(asyncio.Task):
643+
pass
644+
645+
@asyncio.coroutine
646+
def coro():
647+
pass
648+
649+
factory = lambda loop, coro: MyTask(coro, loop=loop)
650+
651+
self.assertIsNone(self.loop.get_task_factory())
652+
self.loop.set_task_factory(factory)
653+
self.assertIs(self.loop.get_task_factory(), factory)
654+
655+
task = self.loop.create_task(coro())
656+
self.assertTrue(isinstance(task, MyTask))
657+
self.loop.run_until_complete(task)
658+
659+
self.loop.set_task_factory(None)
660+
self.assertIsNone(self.loop.get_task_factory())
661+
662+
task = self.loop.create_task(coro())
663+
self.assertTrue(isinstance(task, asyncio.Task))
664+
self.assertFalse(isinstance(task, MyTask))
665+
self.loop.run_until_complete(task)
666+
626667
def test_env_var_debug(self):
627668
code = '\n'.join((
628669
'import asyncio',

Lib/test/test_asyncio/test_tasks.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
from asyncio import test_utils
1616
try:
1717
from test import support
18-
from test.script_helper import assert_python_ok
1918
except ImportError:
2019
from asyncio import test_support as support
21-
from asyncio.test_support import assert_python_ok
20+
try:
21+
from test.support.script_helper import assert_python_ok
22+
except ImportError:
23+
try:
24+
from test.script_helper import assert_python_ok
25+
except ImportError:
26+
from asyncio.test_support import assert_python_ok
2227

2328

2429
PY34 = (sys.version_info >= (3, 4))

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ Core and Builtins
4242

4343
- Issue #21354: PyCFunction_New function is exposed by python DLL again.
4444

45+
- asyncio: New event loop APIs: set_task_factory() and get_task_factory()
46+
4547
Library
4648
-------
4749

0 commit comments

Comments
 (0)