]> granicus.if.org Git - python/commitdiff
asyncio, Tulip issue #136: Add get/set_debug() methods to BaseEventLoopTests.
authorVictor Stinner <victor.stinner@gmail.com>
Wed, 19 Feb 2014 22:15:02 +0000 (23:15 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Wed, 19 Feb 2014 22:15:02 +0000 (23:15 +0100)
Add also a PYTHONASYNCIODEBUG environment variable to debug coroutines since
Python startup, to be able to debug coroutines defined directly in the asyncio
module.

Doc/library/asyncio-dev.rst
Doc/library/asyncio-eventloop.rst
Doc/using/cmdline.rst
Lib/asyncio/base_events.py
Lib/asyncio/events.py
Lib/asyncio/tasks.py
Lib/test/test_asyncio/test_base_events.py
Lib/test/test_asyncio/test_tasks.py

index 90bae8440ee74b606bee1318eae86a12348b5761..c5f0d1a3e6f15475317d9a636069b47da1aed777 100644 (file)
@@ -1,5 +1,7 @@
 .. currentmodule:: asyncio
 
+.. _asyncio-dev:
+
 Develop with asyncio
 ====================
 
@@ -81,10 +83,10 @@ Detect coroutine objects never scheduled
 When a coroutine function is called but not passed to :func:`async` or to the
 :class:`Task` constructor, it is not scheduled and it is probably a bug.
 
-To detect such bug, set :data:`asyncio.tasks._DEBUG` to ``True``. When the
-coroutine object is destroyed by the garbage collector, a log will be emitted
-with the traceback where the coroutine function was called. See the
-:ref:`asyncio logger <asyncio-logger>`.
+To detect such bug, set the environment variable :envvar:`PYTHONASYNCIODEBUG`
+to ``1``. When the coroutine object is destroyed by the garbage collector, a
+log will be emitted with the traceback where the coroutine function was called.
+See the :ref:`asyncio logger <asyncio-logger>`.
 
 The debug flag changes the behaviour of the :func:`coroutine` decorator. The
 debug flag value is only used when then coroutine function is defined, not when
index f056cac175884b2660bad2364ef9897c0b4195bc..04b182b9c0c31557d0ce9414d927561e62933b17 100644 (file)
@@ -553,6 +553,22 @@ pool of processes). By default, an event loop uses a thread pool executor
    Set the default executor used by :meth:`run_in_executor`.
 
 
+Debug mode
+----------
+
+.. method:: BaseEventLoop.get_debug()
+
+   Get the debug mode (:class:`bool`) of the event loop.
+
+.. method:: BaseEventLoop.set_debug(enabled: bool)
+
+   Set the debug mode of the event loop.
+
+.. seealso::
+
+   The :ref:`Develop with asyncio <asyncio-dev>` section.
+
+
 Server
 ------
 
index 4807a3688c4322588359eefa51d91af4a0e4ec57..0c3c2037e8ebd88987a1728c9130d7fec7be9154 100644 (file)
@@ -614,6 +614,14 @@ conflict.
    .. versionadded:: 3.4
 
 
+.. envvar:: PYTHONASYNCIODEBUG
+
+   If this environment variable is set to a non-empty string, enable the debug
+   mode of the :mod:`asyncio` module.
+
+   .. versionadded:: 3.4
+
+
 Debug-mode variables
 ~~~~~~~~~~~~~~~~~~~~
 
index b94ba07968484bf2d5f69c16107d6f76fcc6326c..69caa4d723775558cd8bff9eed5233ad5bf0680d 100644 (file)
@@ -123,6 +123,7 @@ class BaseEventLoop(events.AbstractEventLoop):
         self._running = False
         self._clock_resolution = time.get_clock_info('monotonic').resolution
         self._exception_handler = None
+        self._debug = False
 
     def _make_socket_transport(self, sock, protocol, waiter=None, *,
                                extra=None, server=None):
@@ -795,3 +796,9 @@ class BaseEventLoop(events.AbstractEventLoop):
             if not handle._cancelled:
                 handle._run()
         handle = None  # Needed to break cycles when an exception occurs.
+
+    def get_debug(self):
+        return self._debug
+
+    def set_debug(self, enabled):
+        self._debug = enabled
index 1030c045bd8686deb446718b2b0e305ebe564728..5362f056471309ba416959892093e54c340a95aa 100644 (file)
@@ -345,6 +345,14 @@ class AbstractEventLoop:
     def call_exception_handler(self, context):
         raise NotImplementedError
 
+    # Debug flag management.
+
+    def get_debug(self):
+        raise NotImplementedError
+
+    def set_debug(self, enabled):
+        raise NotImplementedError
+
 
 class AbstractEventLoopPolicy:
     """Abstract policy for accessing the event loop."""
index a3e7cdf11e4ba0ef6a8dd7f640fb4b8ff3fed8a9..cf7b54007bbe75bc2f9fc9e9daf05e31489797aa 100644 (file)
@@ -12,6 +12,8 @@ import concurrent.futures
 import functools
 import inspect
 import linecache
+import os
+import sys
 import traceback
 import weakref
 
@@ -28,7 +30,8 @@ from .log import logger
 # 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 = False
+_DEBUG = (not sys.flags.ignore_environment
+          and bool(os.environ.get('PYTHONASYNCIODEBUG')))
 
 
 class CoroWrapper:
index 2eee3be3a87c939e5e3e6631f309c32a9cd963b1..784a39f8fc05d3ca179d2a275faacffabe38d2a1 100644 (file)
@@ -197,6 +197,12 @@ class BaseEventLoopTests(unittest.TestCase):
         self.assertEqual([h2], self.loop._scheduled)
         self.assertTrue(self.loop._process_events.called)
 
+    def test_set_debug(self):
+        self.loop.set_debug(True)
+        self.assertTrue(self.loop.get_debug())
+        self.loop.set_debug(False)
+        self.assertFalse(self.loop.get_debug())
+
     @unittest.mock.patch('asyncio.base_events.time')
     @unittest.mock.patch('asyncio.base_events.logger')
     def test__run_once_logging(self, m_logger, m_time):
index f27b9522c7357c8a2a624f93a134557771757c3c..6d03dc78f9f29d4d1978a4503fe5f74119904dc5 100644 (file)
@@ -1,7 +1,9 @@
 """Tests for tasks.py."""
 
 import gc
+import os.path
 import unittest
+from test.script_helper import assert_python_ok
 
 import asyncio
 from asyncio import test_utils
@@ -1461,6 +1463,32 @@ class GatherTestsBase:
         cb.assert_called_once_with(fut)
         self.assertEqual(fut.result(), [3, 1, exc, exc2])
 
+    def test_env_var_debug(self):
+        path = os.path.dirname(asyncio.__file__)
+        path = os.path.normpath(os.path.join(path, '..'))
+        code = '\n'.join((
+            'import sys',
+            'sys.path.insert(0, %r)' % path,
+            'import asyncio.tasks',
+            'print(asyncio.tasks._DEBUG)'))
+
+        # Test with -E to not fail if the unit test was run with
+        # PYTHONASYNCIODEBUG set to a non-empty string
+        sts, stdout, stderr = assert_python_ok('-E', '-c', code)
+        self.assertEqual(stdout.rstrip(), b'False')
+
+        sts, stdout, stderr = assert_python_ok('-c', code,
+                                               PYTHONASYNCIODEBUG='')
+        self.assertEqual(stdout.rstrip(), b'False')
+
+        sts, stdout, stderr = assert_python_ok('-c', code,
+                                               PYTHONASYNCIODEBUG='1')
+        self.assertEqual(stdout.rstrip(), b'True')
+
+        sts, stdout, stderr = assert_python_ok('-E', '-c', code,
+                                               PYTHONASYNCIODEBUG='1')
+        self.assertEqual(stdout.rstrip(), b'False')
+
 
 class FutureGatherTests(GatherTestsBase, unittest.TestCase):