1.. currentmodule:: asyncio 2 3.. _asyncio-dev: 4 5Develop with asyncio 6==================== 7 8Asynchronous programming is different than classical "sequential" programming. 9This page lists common traps and explains how to avoid them. 10 11 12.. _asyncio-debug-mode: 13 14Debug mode of asyncio 15--------------------- 16 17The implementation of :mod:`asyncio` has been written for performance. 18In order to ease the development of asynchronous code, you may wish to 19enable *debug mode*. 20 21To enable all debug checks for an application: 22 23* Enable the asyncio debug mode globally by setting the environment variable 24 :envvar:`PYTHONASYNCIODEBUG` to ``1``, or by calling :meth:`AbstractEventLoop.set_debug`. 25* Set the log level of the :ref:`asyncio logger <asyncio-logger>` to 26 :py:data:`logging.DEBUG`. For example, call 27 ``logging.basicConfig(level=logging.DEBUG)`` at startup. 28* Configure the :mod:`warnings` module to display :exc:`ResourceWarning` 29 warnings. For example, use the ``-Wdefault`` command line option of Python to 30 display them. 31 32Examples debug checks: 33 34* Log :ref:`coroutines defined but never "yielded from" 35 <asyncio-coroutine-not-scheduled>` 36* :meth:`~AbstractEventLoop.call_soon` and :meth:`~AbstractEventLoop.call_at` methods 37 raise an exception if they are called from the wrong thread. 38* Log the execution time of the selector 39* Log callbacks taking more than 100 ms to be executed. The 40 :attr:`AbstractEventLoop.slow_callback_duration` attribute is the minimum 41 duration in seconds of "slow" callbacks. 42* :exc:`ResourceWarning` warnings are emitted when transports and event loops 43 are :ref:`not closed explicitly <asyncio-close-transports>`. 44 45.. seealso:: 46 47 The :meth:`AbstractEventLoop.set_debug` method and the :ref:`asyncio logger 48 <asyncio-logger>`. 49 50 51Cancellation 52------------ 53 54Cancellation of tasks is not common in classic programming. In asynchronous 55programming, not only it is something common, but you have to prepare your 56code to handle it. 57 58Futures and tasks can be cancelled explicitly with their :meth:`Future.cancel` 59method. The :func:`wait_for` function cancels the waited task when the timeout 60occurs. There are many other cases where a task can be cancelled indirectly. 61 62Don't call :meth:`~Future.set_result` or :meth:`~Future.set_exception` method 63of :class:`Future` if the future is cancelled: it would fail with an exception. 64For example, write:: 65 66 if not fut.cancelled(): 67 fut.set_result('done') 68 69Don't schedule directly a call to the :meth:`~Future.set_result` or the 70:meth:`~Future.set_exception` method of a future with 71:meth:`AbstractEventLoop.call_soon`: the future can be cancelled before its method 72is called. 73 74If you wait for a future, you should check early if the future was cancelled to 75avoid useless operations. Example:: 76 77 @coroutine 78 def slow_operation(fut): 79 if fut.cancelled(): 80 return 81 # ... slow computation ... 82 yield from fut 83 # ... 84 85The :func:`shield` function can also be used to ignore cancellation. 86 87 88.. _asyncio-multithreading: 89 90Concurrency and multithreading 91------------------------------ 92 93An event loop runs in a thread and executes all callbacks and tasks in the same 94thread. While a task is running in the event loop, no other task is running in 95the same thread. But when the task uses ``yield from``, the task is suspended 96and the event loop executes the next task. 97 98To schedule a callback from a different thread, the 99:meth:`AbstractEventLoop.call_soon_threadsafe` method should be used. Example:: 100 101 loop.call_soon_threadsafe(callback, *args) 102 103Most asyncio objects are not thread safe. You should only worry if you access 104objects outside the event loop. For example, to cancel a future, don't call 105directly its :meth:`Future.cancel` method, but:: 106 107 loop.call_soon_threadsafe(fut.cancel) 108 109To handle signals and to execute subprocesses, the event loop must be run in 110the main thread. 111 112To schedule a coroutine object from a different thread, the 113:func:`run_coroutine_threadsafe` function should be used. It returns a 114:class:`concurrent.futures.Future` to access the result:: 115 116 future = asyncio.run_coroutine_threadsafe(coro_func(), loop) 117 result = future.result(timeout) # Wait for the result with a timeout 118 119The :meth:`AbstractEventLoop.run_in_executor` method can be used with a thread pool 120executor to execute a callback in different thread to not block the thread of 121the event loop. 122 123.. seealso:: 124 125 The :ref:`Synchronization primitives <asyncio-sync>` section describes ways 126 to synchronize tasks. 127 128 The :ref:`Subprocess and threads <asyncio-subprocess-threads>` section lists 129 asyncio limitations to run subprocesses from different threads. 130 131 132 133 134.. _asyncio-handle-blocking: 135 136Handle blocking functions correctly 137----------------------------------- 138 139Blocking functions should not be called directly. For example, if a function 140blocks for 1 second, other tasks are delayed by 1 second which can have an 141important impact on reactivity. 142 143For networking and subprocesses, the :mod:`asyncio` module provides high-level 144APIs like :ref:`protocols <asyncio-protocol>`. 145 146An executor can be used to run a task in a different thread or even in a 147different process, to not block the thread of the event loop. See the 148:meth:`AbstractEventLoop.run_in_executor` method. 149 150.. seealso:: 151 152 The :ref:`Delayed calls <asyncio-delayed-calls>` section details how the 153 event loop handles time. 154 155 156.. _asyncio-logger: 157 158Logging 159------- 160 161The :mod:`asyncio` module logs information with the :mod:`logging` module in 162the logger ``'asyncio'``. 163 164The default log level for the :mod:`asyncio` module is :py:data:`logging.INFO`. 165For those not wanting such verbosity from :mod:`asyncio` the log level can 166be changed. For example, to change the level to :py:data:`logging.WARNING`: 167 168.. code-block:: none 169 170 logging.getLogger('asyncio').setLevel(logging.WARNING) 171 172 173.. _asyncio-coroutine-not-scheduled: 174 175Detect coroutine objects never scheduled 176---------------------------------------- 177 178When a coroutine function is called and its result is not passed to 179:func:`ensure_future` or to the :meth:`AbstractEventLoop.create_task` method, 180the execution of the coroutine object will never be scheduled which is 181probably a bug. :ref:`Enable the debug mode of asyncio <asyncio-debug-mode>` 182to :ref:`log a warning <asyncio-logger>` to detect it. 183 184Example with the bug:: 185 186 import asyncio 187 188 @asyncio.coroutine 189 def test(): 190 print("never scheduled") 191 192 test() 193 194Output in debug mode:: 195 196 Coroutine test() at test.py:3 was never yielded from 197 Coroutine object created at (most recent call last): 198 File "test.py", line 7, in <module> 199 test() 200 201The fix is to call the :func:`ensure_future` function or the 202:meth:`AbstractEventLoop.create_task` method with the coroutine object. 203 204.. seealso:: 205 206 :ref:`Pending task destroyed <asyncio-pending-task-destroyed>`. 207 208 209Detect exceptions never consumed 210-------------------------------- 211 212Python usually calls :func:`sys.displayhook` on unhandled exceptions. If 213:meth:`Future.set_exception` is called, but the exception is never consumed, 214:func:`sys.displayhook` is not called. Instead, :ref:`a log is emitted 215<asyncio-logger>` when the future is deleted by the garbage collector, with the 216traceback where the exception was raised. 217 218Example of unhandled exception:: 219 220 import asyncio 221 222 @asyncio.coroutine 223 def bug(): 224 raise Exception("not consumed") 225 226 loop = asyncio.get_event_loop() 227 asyncio.ensure_future(bug()) 228 loop.run_forever() 229 loop.close() 230 231Output:: 232 233 Task exception was never retrieved 234 future: <Task finished coro=<coro() done, defined at asyncio/coroutines.py:139> exception=Exception('not consumed',)> 235 Traceback (most recent call last): 236 File "asyncio/tasks.py", line 237, in _step 237 result = next(coro) 238 File "asyncio/coroutines.py", line 141, in coro 239 res = func(*args, **kw) 240 File "test.py", line 5, in bug 241 raise Exception("not consumed") 242 Exception: not consumed 243 244:ref:`Enable the debug mode of asyncio <asyncio-debug-mode>` to get the 245traceback where the task was created. Output in debug mode:: 246 247 Task exception was never retrieved 248 future: <Task finished coro=<bug() done, defined at test.py:3> exception=Exception('not consumed',) created at test.py:8> 249 source_traceback: Object created at (most recent call last): 250 File "test.py", line 8, in <module> 251 asyncio.ensure_future(bug()) 252 Traceback (most recent call last): 253 File "asyncio/tasks.py", line 237, in _step 254 result = next(coro) 255 File "asyncio/coroutines.py", line 79, in __next__ 256 return next(self.gen) 257 File "asyncio/coroutines.py", line 141, in coro 258 res = func(*args, **kw) 259 File "test.py", line 5, in bug 260 raise Exception("not consumed") 261 Exception: not consumed 262 263There are different options to fix this issue. The first option is to chain the 264coroutine in another coroutine and use classic try/except:: 265 266 @asyncio.coroutine 267 def handle_exception(): 268 try: 269 yield from bug() 270 except Exception: 271 print("exception consumed") 272 273 loop = asyncio.get_event_loop() 274 asyncio.ensure_future(handle_exception()) 275 loop.run_forever() 276 loop.close() 277 278Another option is to use the :meth:`AbstractEventLoop.run_until_complete` 279function:: 280 281 task = asyncio.ensure_future(bug()) 282 try: 283 loop.run_until_complete(task) 284 except Exception: 285 print("exception consumed") 286 287.. seealso:: 288 289 The :meth:`Future.exception` method. 290 291 292Chain coroutines correctly 293-------------------------- 294 295When a coroutine function calls other coroutine functions and tasks, they 296should be chained explicitly with ``yield from``. Otherwise, the execution is 297not guaranteed to be sequential. 298 299Example with different bugs using :func:`asyncio.sleep` to simulate slow 300operations:: 301 302 import asyncio 303 304 @asyncio.coroutine 305 def create(): 306 yield from asyncio.sleep(3.0) 307 print("(1) create file") 308 309 @asyncio.coroutine 310 def write(): 311 yield from asyncio.sleep(1.0) 312 print("(2) write into file") 313 314 @asyncio.coroutine 315 def close(): 316 print("(3) close file") 317 318 @asyncio.coroutine 319 def test(): 320 asyncio.ensure_future(create()) 321 asyncio.ensure_future(write()) 322 asyncio.ensure_future(close()) 323 yield from asyncio.sleep(2.0) 324 loop.stop() 325 326 loop = asyncio.get_event_loop() 327 asyncio.ensure_future(test()) 328 loop.run_forever() 329 print("Pending tasks at exit: %s" % asyncio.Task.all_tasks(loop)) 330 loop.close() 331 332Expected output: 333 334.. code-block:: none 335 336 (1) create file 337 (2) write into file 338 (3) close file 339 Pending tasks at exit: set() 340 341Actual output: 342 343.. code-block:: none 344 345 (3) close file 346 (2) write into file 347 Pending tasks at exit: {<Task pending create() at test.py:7 wait_for=<Future pending cb=[Task._wakeup()]>>} 348 Task was destroyed but it is pending! 349 task: <Task pending create() done at test.py:5 wait_for=<Future pending cb=[Task._wakeup()]>> 350 351The loop stopped before the ``create()`` finished, ``close()`` has been called 352before ``write()``, whereas coroutine functions were called in this order: 353``create()``, ``write()``, ``close()``. 354 355To fix the example, tasks must be marked with ``yield from``:: 356 357 @asyncio.coroutine 358 def test(): 359 yield from asyncio.ensure_future(create()) 360 yield from asyncio.ensure_future(write()) 361 yield from asyncio.ensure_future(close()) 362 yield from asyncio.sleep(2.0) 363 loop.stop() 364 365Or without ``asyncio.ensure_future()``:: 366 367 @asyncio.coroutine 368 def test(): 369 yield from create() 370 yield from write() 371 yield from close() 372 yield from asyncio.sleep(2.0) 373 loop.stop() 374 375 376.. _asyncio-pending-task-destroyed: 377 378Pending task destroyed 379---------------------- 380 381If a pending task is destroyed, the execution of its wrapped :ref:`coroutine 382<coroutine>` did not complete. It is probably a bug and so a warning is logged. 383 384Example of log: 385 386.. code-block:: none 387 388 Task was destroyed but it is pending! 389 task: <Task pending coro=<kill_me() done, defined at test.py:5> wait_for=<Future pending cb=[Task._wakeup()]>> 390 391:ref:`Enable the debug mode of asyncio <asyncio-debug-mode>` to get the 392traceback where the task was created. Example of log in debug mode: 393 394.. code-block:: none 395 396 Task was destroyed but it is pending! 397 source_traceback: Object created at (most recent call last): 398 File "test.py", line 15, in <module> 399 task = asyncio.ensure_future(coro, loop=loop) 400 task: <Task pending coro=<kill_me() done, defined at test.py:5> wait_for=<Future pending cb=[Task._wakeup()] created at test.py:7> created at test.py:15> 401 402 403.. seealso:: 404 405 :ref:`Detect coroutine objects never scheduled <asyncio-coroutine-not-scheduled>`. 406 407.. _asyncio-close-transports: 408 409Close transports and event loops 410-------------------------------- 411 412When a transport is no more needed, call its ``close()`` method to release 413resources. Event loops must also be closed explicitly. 414 415If a transport or an event loop is not closed explicitly, a 416:exc:`ResourceWarning` warning will be emitted in its destructor. By default, 417:exc:`ResourceWarning` warnings are ignored. The :ref:`Debug mode of asyncio 418<asyncio-debug-mode>` section explains how to display them. 419