From 1ba94930419e4f033110ec58102b7ffda36efc8f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 16 Nov 2017 12:30:22 +0100 Subject: [PATCH 1/2] bpo-32047: -X dev enables asyncio debug mode The new -X dev command line option now also enables asyncio debug mode. --- Doc/library/asyncio-dev.rst | 9 ++++++- Doc/using/cmdline.rst | 10 +++++--- Lib/asyncio/base_events.py | 3 +-- Lib/asyncio/coroutines.py | 30 ++++++++++++++--------- Lib/test/test_asyncio/test_base_events.py | 4 +++ Lib/test/test_asyncio/test_tasks.py | 4 +++ 6 files changed, 42 insertions(+), 18 deletions(-) diff --git a/Doc/library/asyncio-dev.rst b/Doc/library/asyncio-dev.rst index 1838eb95a7d522..b2ad87b5d02057 100644 --- a/Doc/library/asyncio-dev.rst +++ b/Doc/library/asyncio-dev.rst @@ -21,7 +21,9 @@ enable *debug mode*. To enable all debug checks for an application: * Enable the asyncio debug mode globally by setting the environment variable - :envvar:`PYTHONASYNCIODEBUG` to ``1``, or by calling :meth:`AbstractEventLoop.set_debug`. + :envvar:`PYTHONASYNCIODEBUG` to ``1``, using ``-X dev`` command line option + (see the :option:`-X` option), or by calling + :meth:`AbstractEventLoop.set_debug`. * Set the log level of the :ref:`asyncio logger ` to :py:data:`logging.DEBUG`. For example, call ``logging.basicConfig(level=logging.DEBUG)`` at startup. @@ -42,6 +44,11 @@ Examples debug checks: * :exc:`ResourceWarning` warnings are emitted when transports and event loops are :ref:`not closed explicitly `. +.. versionchanged:: 3.7 + + The new ``-X dev`` command line option can now also be used to enable + the debug mode. + .. seealso:: The :meth:`AbstractEventLoop.set_debug` method and the :ref:`asyncio logger diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 01869d117f7cd8..bf27c1e2b56c4c 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -414,16 +414,18 @@ Miscellaneous options application. Typical usage is ``python3 -X importtime -c 'import asyncio'``. See also :envvar:`PYTHONPROFILEIMPORTTIME`. * ``-X dev`` enables the "developer mode": enable debug checks at runtime. - In short, ``python3 -X dev ...`` behaves as ``PYTHONMALLOC=debug python3 + In short, ``python3 -X dev ...`` behaves as ``PYTHONMALLOC=debug PYTHONASYNCIODEBUG=1 python3 -W default -X faulthandler ...``, except that the :envvar:`PYTHONMALLOC` - environment variable is not set in practice. Developer mode: + and :envvar:`PYTHONASYNCIODEBUG` environment variables are not set in + practice. Developer mode: * Add ``default`` warnings option. For example, display :exc:`DeprecationWarning` and :exc:`ResourceWarning` warnings. - * Install debug hooks on memory allocators as if :envvar:`PYTHONMALLOC` - is set to ``debug``. + * Install debug hooks on memory allocators: see the + :c:func:`PyMem_SetupDebugHooks` C function. * Enable the :mod:`faulthandler` module to dump the Python traceback on a crash. + * Enable :ref:`asyncio debug mode `. It also allows passing arbitrary values and retrieving them through the :data:`sys._xoptions` dictionary. diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index b94856c9393618..a782c51467d7b0 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -244,8 +244,7 @@ def __init__(self): self._thread_id = None self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None - self.set_debug((not sys.flags.ignore_environment - and bool(os.environ.get('PYTHONASYNCIODEBUG')))) + self.set_debug(coroutines._debug_mode()) # In debug mode, if the execution of a callback or a step of a task # exceed this duration in seconds, the slow callback/task is logged. self.slow_callback_duration = 0.1 diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index 520a309f846049..f7cf7717be3d39 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -19,17 +19,25 @@ # Opcode of "yield from" instruction _YIELD_FROM = opcode.opmap['YIELD_FROM'] -# If you set _DEBUG to true, @coroutine will wrap the resulting -# generator objects in a CoroWrapper instance (defined below). That -# instance will log a message when the generator is never iterated -# over, which may happen when you forget to use "yield from" with a -# coroutine call. Note that the value of the _DEBUG flag is taken -# when the decorator is used, so to be of any use it must be set -# before you define your coroutines. A downside of using this feature -# is that tracebacks show entries for the CoroWrapper.__next__ method -# when _DEBUG is true. -_DEBUG = (not sys.flags.ignore_environment and - bool(os.environ.get('PYTHONASYNCIODEBUG'))) + +def _debug_mode(): + # If you set _DEBUG to true, @coroutine will wrap the resulting + # generator objects in a CoroWrapper instance (defined below). That + # instance will log a message when the generator is never iterated + # over, which may happen when you forget to use "yield from" with a + # coroutine call. Note that the value of the _DEBUG flag is taken + # when the decorator is used, so to be of any use it must be set + # before you define your coroutines. A downside of using this feature + # is that tracebacks show entries for the CoroWrapper.__next__ method + # when _DEBUG is true. + debug = (not sys.flags.ignore_environment and + bool(os.environ.get('PYTHONASYNCIODEBUG'))) + if hasattr(sys, '_xoptions') and 'dev' in sys._xoptions: + debug = True + return debug + + +_DEBUG = _debug_mode() try: diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index a25069efb5cdcb..98f2aef5627662 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -822,6 +822,10 @@ def test_env_var_debug(self): PYTHONASYNCIODEBUG='1') self.assertEqual(stdout.rstrip(), b'False') + sts, stdout, stderr = assert_python_ok('-E', '-X', 'dev', + '-c', code) + self.assertEqual(stdout.rstrip(), b'True') + def test_create_task(self): class MyTask(asyncio.Task): pass diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py index 1d77f9f2c4d180..f66f7f1e1706fb 100644 --- a/Lib/test/test_asyncio/test_tasks.py +++ b/Lib/test/test_asyncio/test_tasks.py @@ -2350,6 +2350,10 @@ def test_env_var_debug(self): PYTHONPATH=aio_path) self.assertEqual(stdout.rstrip(), b'False') + sts, stdout, stderr = assert_python_ok('-E', '-X', 'dev', + '-c', code) + self.assertEqual(stdout.rstrip(), b'True') + class FutureGatherTests(GatherTestsBase, test_utils.TestCase): From 0a18fcc5d10c61fb68739aeb38fd1d621a485559 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 16 Nov 2017 14:59:42 +0100 Subject: [PATCH 2/2] Rename added function to _is_debug_mode() --- Lib/asyncio/base_events.py | 2 +- Lib/asyncio/coroutines.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index a782c51467d7b0..bb04aef76e874e 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -244,7 +244,7 @@ def __init__(self): self._thread_id = None self._clock_resolution = time.get_clock_info('monotonic').resolution self._exception_handler = None - self.set_debug(coroutines._debug_mode()) + self.set_debug(coroutines._is_debug_mode()) # In debug mode, if the execution of a callback or a step of a task # exceed this duration in seconds, the slow callback/task is logged. self.slow_callback_duration = 0.1 diff --git a/Lib/asyncio/coroutines.py b/Lib/asyncio/coroutines.py index f7cf7717be3d39..a87c9f9b0b6b57 100644 --- a/Lib/asyncio/coroutines.py +++ b/Lib/asyncio/coroutines.py @@ -20,7 +20,7 @@ _YIELD_FROM = opcode.opmap['YIELD_FROM'] -def _debug_mode(): +def _is_debug_mode(): # If you set _DEBUG to true, @coroutine will wrap the resulting # generator objects in a CoroWrapper instance (defined below). That # instance will log a message when the generator is never iterated @@ -37,7 +37,7 @@ def _debug_mode(): return debug -_DEBUG = _debug_mode() +_DEBUG = _is_debug_mode() try: