From 8ed97725e8f8a139e71f503933d05fb953e74937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pasternak?= Date: Tue, 6 Sep 2022 17:50:52 +0200 Subject: [PATCH 1/3] Add ``--liveserver-verbose`` parameter How to see output from LiveServer when it fails? It was a difficult task, as official Django tests make it quiet... until now. --- docs/changelog.rst | 10 ++++++++++ docs/helpers.rst | 4 ++++ pytest_django/fixtures.py | 6 +++++- pytest_django/live_server_helper.py | 15 +++++++++++++-- pytest_django/plugin.py | 8 ++++++++ pytest_django/verbose_live_server.py | 21 +++++++++++++++++++++ 6 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 pytest_django/verbose_live_server.py diff --git a/docs/changelog.rst b/docs/changelog.rst index d120e7cc..60b157f6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,16 @@ Changelog ========= +vunreleased () +-------------- + +Improvements +^^^^^^^^^^^^ + +* ``--liveserver-verbose`` command-line parameter, which allows to see + logs from live_server on the standard output, including tracebacks. + Useful for debugging. + v4.5.2 (2021-12-07) ------------------- diff --git a/docs/helpers.rst b/docs/helpers.rst index 2fc75915..31a725b0 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -371,6 +371,10 @@ depends on the ``transactional_db`` fixture. If tests depend on data created in data migrations, you should add the ``django_db_serialized_rollback`` fixture. +You can also use ``--liveserver-verbose`` command-line argument, to outputs +the liveserver logs to the standard output, including tracebacks. This is +useful for debugging live server behaviour and environment-related problems. + .. note:: Combining database access fixtures. When using multiple database fixtures together, only one of them is diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index a07d165c..9ac12f5f 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -536,7 +536,11 @@ def live_server(request): "DJANGO_LIVE_TEST_SERVER_ADDRESS" ) or "localhost" - server = live_server_helper.LiveServer(addr) + live_server_class = live_server_helper.LiveServer + if request.config.getvalue("liveserver_verbose") is True: + live_server_class = live_server_helper.VerboseLiveServer + + server = live_server_class(addr) request.addfinalizer(server.stop) return server diff --git a/pytest_django/live_server_helper.py b/pytest_django/live_server_helper.py index 644ec9bb..b4d18682 100644 --- a/pytest_django/live_server_helper.py +++ b/pytest_django/live_server_helper.py @@ -8,9 +8,12 @@ class LiveServer: The ``live_server`` fixture handles creation and stopping. """ + def get_live_server_thread_class(self): + from django.test.testcases import LiveServerThread + return LiveServerThread + def __init__(self, addr: str) -> None: from django.db import connections - from django.test.testcases import LiveServerThread from django.test.utils import modify_settings liveserver_kwargs = {} # type: Dict[str, Any] @@ -42,7 +45,9 @@ def __init__(self, addr: str) -> None: host = addr else: liveserver_kwargs["port"] = int(port) - self.thread = LiveServerThread(host, **liveserver_kwargs) + + live_server_thread_class = self.get_live_server_thread_class() + self.thread = live_server_thread_class(host, **liveserver_kwargs) self._live_server_modified_settings = modify_settings( ALLOWED_HOSTS={"append": host} @@ -79,3 +84,9 @@ def __add__(self, other) -> str: def __repr__(self) -> str: return "" % self.url + + +class VerboseLiveServer(LiveServer): + def get_live_server_thread_class(self): + from .verbose_live_server import VerboseLiveServerThread + return VerboseLiveServerThread diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index e2b6f120..5ce81163 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -124,6 +124,14 @@ def pytest_addoption(parser) -> None: parser.addini( SETTINGS_MODULE_ENV, "Django settings module to use by pytest-django." ) + group.addoption( + "--liveserver-verbose", + "--liveserver_verbose", + action="store_true", + dest="liveserver_verbose", + default=False, + help="Enable verbose logging for live_server fixture", + ) parser.addini( "django_find_project", diff --git a/pytest_django/verbose_live_server.py b/pytest_django/verbose_live_server.py new file mode 100644 index 00000000..589dc9d9 --- /dev/null +++ b/pytest_django/verbose_live_server.py @@ -0,0 +1,21 @@ +import sys +import traceback + +from django.core.servers.basehttp import ThreadedWSGIServer, WSGIRequestHandler +from django.test.testcases import LiveServerThread + + +class VerboseWSGIRequestHandler(WSGIRequestHandler): + def handle_uncaught_exception(self, request, resolver, exc_info): + traceback.print_tb(exc_info[2], file=sys.stderr) + return super(VerboseWSGIRequestHandler, self).handle_uncaught_exception( + request, resolver, exc_info) + + +class VerboseLiveServerThread(LiveServerThread): + def _create_server(self): + return ThreadedWSGIServer( + (self.host, self.port), + VerboseWSGIRequestHandler, + allow_reuse_address=False + ) From 6b23d3106972992af938eae23bd6fc73d7ba1173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pasternak?= Date: Tue, 6 Sep 2022 18:05:08 +0200 Subject: [PATCH 2/3] Remind about log suppression. --- docs/helpers.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/helpers.rst b/docs/helpers.rst index 31a725b0..77d12b48 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -374,6 +374,10 @@ created in data migrations, you should add the You can also use ``--liveserver-verbose`` command-line argument, to outputs the liveserver logs to the standard output, including tracebacks. This is useful for debugging live server behaviour and environment-related problems. +Take note, logs will be written to standard output, which is being supressed +by pytest by default, so you will need to use ``-s`` or ``--capture=no`` +parameter too. + .. note:: Combining database access fixtures. From 061a10189e657bfbc49cf2f7e8345a70da816b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pasternak?= Date: Tue, 6 Sep 2022 18:43:02 +0200 Subject: [PATCH 3/3] VerboseDebuggingLiveServer --- docs/helpers.rst | 3 +++ pytest_django/fixtures.py | 3 +++ pytest_django/live_server_helper.py | 6 ++++++ pytest_django/plugin.py | 9 +++++++++ pytest_django/verbose_live_server.py | 18 +++++++++++++++++- 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/helpers.rst b/docs/helpers.rst index 77d12b48..94210329 100644 --- a/docs/helpers.rst +++ b/docs/helpers.rst @@ -378,6 +378,9 @@ Take note, logs will be written to standard output, which is being supressed by pytest by default, so you will need to use ``-s`` or ``--capture=no`` parameter too. +And if your live server still keeps acting weird, there is a switch called +``--liveserver-debug`` which drops into debugger in the console +whenever traceback occurs in a live server page. .. note:: Combining database access fixtures. diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 9ac12f5f..9ea5c5e7 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -540,6 +540,9 @@ def live_server(request): if request.config.getvalue("liveserver_verbose") is True: live_server_class = live_server_helper.VerboseLiveServer + if request.config.getvalue("liveserver_debug") is True: + live_server_class = live_server_helper.VerboseDebuggingLiveServer + server = live_server_class(addr) request.addfinalizer(server.stop) return server diff --git a/pytest_django/live_server_helper.py b/pytest_django/live_server_helper.py index b4d18682..b99d7b17 100644 --- a/pytest_django/live_server_helper.py +++ b/pytest_django/live_server_helper.py @@ -90,3 +90,9 @@ class VerboseLiveServer(LiveServer): def get_live_server_thread_class(self): from .verbose_live_server import VerboseLiveServerThread return VerboseLiveServerThread + + +class VerboseDebuggingLiveServer(LiveServer): + def get_live_server_thread_class(self): + from .verbose_live_server import VerboseDebuggingLiveServerThread + return VerboseDebuggingLiveServerThread diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 5ce81163..dc5fdba8 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -132,6 +132,15 @@ def pytest_addoption(parser) -> None: default=False, help="Enable verbose logging for live_server fixture", ) + group.addoption( + "--liveserver-debug", + "--liveserver_debug", + action="store_true", + dest="liveserver_debug", + default=False, + help="Drops into debugger on tracebacks in live_server " + "(only if used with --liveserver-verbose)" + ) parser.addini( "django_find_project", diff --git a/pytest_django/verbose_live_server.py b/pytest_django/verbose_live_server.py index 589dc9d9..8cd15ac5 100644 --- a/pytest_django/verbose_live_server.py +++ b/pytest_django/verbose_live_server.py @@ -6,16 +6,32 @@ class VerboseWSGIRequestHandler(WSGIRequestHandler): + debug_on_traceback = False + def handle_uncaught_exception(self, request, resolver, exc_info): traceback.print_tb(exc_info[2], file=sys.stderr) + if self.debug_on_traceback: + import pytest + pytest.set_trace() + return super(VerboseWSGIRequestHandler, self).handle_uncaught_exception( request, resolver, exc_info) +class VerboseDebuggingWSGIRequestHandler(VerboseWSGIRequestHandler): + debug_on_traceback = True + + class VerboseLiveServerThread(LiveServerThread): + request_handler_class = VerboseWSGIRequestHandler + def _create_server(self): return ThreadedWSGIServer( (self.host, self.port), - VerboseWSGIRequestHandler, + self.request_handler_class, allow_reuse_address=False ) + + +class VerboseDebuggingLiveServerThread(VerboseLiveServerThread): + request_handler_class = VerboseDebuggingWSGIRequestHandler