From 54d3cd587d17534b9d969855c7019567d48aa528 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sat, 30 Jun 2018 18:09:06 -0700 Subject: [PATCH 01/18] Adding the --trace option. --- src/_pytest/debugging.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 3fc3eb552..f983edd13 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -5,6 +5,8 @@ import sys import os from doctest import UnexpectedException +from _pytest.config import hookimpl + try: from builtins import breakpoint # noqa @@ -12,7 +14,7 @@ try: except ImportError: SUPPORTS_BREAKPOINT_BUILTIN = False - +immediately_break = False def pytest_addoption(parser): group = parser.getgroup("general") group._addoption( @@ -28,6 +30,12 @@ def pytest_addoption(parser): help="start a custom interactive Python debugger on errors. " "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", ) + group._addoption( + "--trace", + dest="trace", + action="store_true", + help="Immediately break when running each test.", + ) def pytest_configure(config): @@ -63,6 +71,23 @@ def pytest_configure(config): config._cleanup.append(fin) + +@hookimpl(hookwrapper=True) +def pytest_pyfunc_call(pyfuncitem): + if immediately_break: + pytestPDB.set_trace(set_break=False) + testfunction = pyfuncitem.obj + pyfuncitem.obj = pdb.runcall + if pyfuncitem._isyieldedfunction(): + pyfuncitem.args = [testfunction, pyfuncitem._args] + else: + pyfuncitem.funcargs['func'] = testfunction + new_list = list(pyfuncitem._fixtureinfo.argnames) + new_list.append('func') + pyfuncitem._fixtureinfo.argnames = tuple(new_list) + outcome = yield + + class pytestPDB(object): """ Pseudo PDB that defers to the real pdb. """ @@ -71,7 +96,7 @@ class pytestPDB(object): _pdb_cls = pdb.Pdb @classmethod - def set_trace(cls): + def set_trace(cls, set_break=True): """ invoke PDB set_trace debugging, dropping any IO capturing. """ import _pytest.config @@ -84,7 +109,8 @@ class pytestPDB(object): tw.line() tw.sep(">", "PDB set_trace (IO-capturing turned off)") cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config) - cls._pdb_cls().set_trace(frame) + if set_break: + cls._pdb_cls().set_trace(frame) class PdbInvoke(object): From 952bbefaac3836e4c4a8ae6e0919dabcb90a448a Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sat, 30 Jun 2018 18:26:58 -0700 Subject: [PATCH 02/18] Add initial test. --- testing/test_pdb.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 08be812a2..7bb19d20f 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -696,3 +696,20 @@ class TestDebuggingBreakpoints(object): assert "1 failed" in rest assert "reading from stdin while output" not in rest TestPDB.flush(child) + +class TestTraceOption(): + def test_trace_sets_breakpoint(self, testdir): + p1 = testdir.makepyfile( + """ + def test_1(): + assert 1 + """ + ) + child = testdir.spawn_pytest("--trace " + str(p1)) + child.expect("test_1") + child.expect("(Pdb)") + child.sendeof() + rest = child.read().decode("utf8") + assert "1 failed" in rest + assert "reading from stdin while output" not in rest + TestPDB.flush(child) \ No newline at end of file From a46b94950c7574f94315a29b661f38e4176e72b9 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sat, 30 Jun 2018 21:32:25 -0700 Subject: [PATCH 03/18] Properly set immediately_break value --- src/_pytest/debugging.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index f983edd13..d4c5747da 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -46,6 +46,8 @@ def pytest_configure(config): else: pdb_cls = pdb.Pdb + global immediately_break + immediately_break = config.getvalue("trace") if config.getvalue("usepdb"): config.pluginmanager.register(PdbInvoke(), "pdbinvoke") From 533f4cc10cf95933c532bac78e60bed38001ee52 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sat, 30 Jun 2018 21:36:27 -0700 Subject: [PATCH 04/18] Fix test to pass --- testing/test_pdb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 7bb19d20f..c7f542c26 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -702,7 +702,7 @@ class TestTraceOption(): p1 = testdir.makepyfile( """ def test_1(): - assert 1 + assert True """ ) child = testdir.spawn_pytest("--trace " + str(p1)) @@ -710,6 +710,6 @@ class TestTraceOption(): child.expect("(Pdb)") child.sendeof() rest = child.read().decode("utf8") - assert "1 failed" in rest + assert "1 passed" in rest assert "reading from stdin while output" not in rest TestPDB.flush(child) \ No newline at end of file From 57198d477b5f8f6299c8c56fd075603194063868 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 1 Jul 2018 12:14:35 -0700 Subject: [PATCH 05/18] Adding changelog entry for the --trace option. --- changelog/3610.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/3610.feature.rst diff --git a/changelog/3610.feature.rst b/changelog/3610.feature.rst new file mode 100644 index 000000000..546f2be56 --- /dev/null +++ b/changelog/3610.feature.rst @@ -0,0 +1 @@ +Added the `--trace` option to enter the debugger at the start of a test. \ No newline at end of file From 713d32c4da34abaaea77fec3e1211f28206e154f Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 1 Jul 2018 18:49:39 -0700 Subject: [PATCH 06/18] Adding documentation for the --trace option. --- doc/en/usage.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 25be54395..b1085c9e8 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -171,6 +171,16 @@ for example:: >>> sys.last_value AssertionError('assert result == "ok"',) +.. _trace-option: + +Dropping to PDB_ (Python Debugger) at the start of a test + +``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option:: + + pytest --trace + + This will invoke the Python debugger at the start of every test. + .. _breakpoints: Setting breakpoints From 66fa6bb42ed511fb7e3628a61233f050597fb913 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 1 Jul 2018 18:50:57 -0700 Subject: [PATCH 07/18] Fix flake8 issues. --- src/_pytest/debugging.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index d4c5747da..7d7bb0ad2 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -14,7 +14,10 @@ try: except ImportError: SUPPORTS_BREAKPOINT_BUILTIN = False + immediately_break = False + + def pytest_addoption(parser): group = parser.getgroup("general") group._addoption( @@ -73,7 +76,6 @@ def pytest_configure(config): config._cleanup.append(fin) - @hookimpl(hookwrapper=True) def pytest_pyfunc_call(pyfuncitem): if immediately_break: @@ -83,11 +85,13 @@ def pytest_pyfunc_call(pyfuncitem): if pyfuncitem._isyieldedfunction(): pyfuncitem.args = [testfunction, pyfuncitem._args] else: + if 'func' in pyfuncitem._fixtureinfo.argnames: + raise ValueError("--trace can't be used with a fixture named func!") pyfuncitem.funcargs['func'] = testfunction new_list = list(pyfuncitem._fixtureinfo.argnames) new_list.append('func') pyfuncitem._fixtureinfo.argnames = tuple(new_list) - outcome = yield + yield class pytestPDB(object): From a604a71185f8a63e21a32e2619c5fc3fc93159f6 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 1 Jul 2018 18:53:18 -0700 Subject: [PATCH 08/18] Fixing usage.rst title. --- doc/en/usage.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/en/usage.rst b/doc/en/usage.rst index b1085c9e8..469602146 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -174,6 +174,8 @@ for example:: .. _trace-option: Dropping to PDB_ (Python Debugger) at the start of a test +----------------------------------------------- + ``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option:: From 88011622751fd6280d8aad02ca924aae066b6df8 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 1 Jul 2018 18:54:04 -0700 Subject: [PATCH 09/18] Fixing tabbing in usage.rst. --- doc/en/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 469602146..83e7bd158 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -181,7 +181,7 @@ Dropping to PDB_ (Python Debugger) at the start of a test pytest --trace - This will invoke the Python debugger at the start of every test. +This will invoke the Python debugger at the start of every test. .. _breakpoints: From 0b704779309332de3937bfa94eb3eb0b43b055c3 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 1 Jul 2018 20:18:00 -0700 Subject: [PATCH 10/18] Fix linting issues. --- changelog/3610.feature.rst | 2 +- doc/en/usage.rst | 2 +- src/_pytest/debugging.py | 6 +++--- testing/test_pdb.py | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/changelog/3610.feature.rst b/changelog/3610.feature.rst index 546f2be56..fd044b4b2 100644 --- a/changelog/3610.feature.rst +++ b/changelog/3610.feature.rst @@ -1 +1 @@ -Added the `--trace` option to enter the debugger at the start of a test. \ No newline at end of file +Added the `--trace` option to enter the debugger at the start of a test. diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 83e7bd158..a5418df22 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -174,7 +174,7 @@ for example:: .. _trace-option: Dropping to PDB_ (Python Debugger) at the start of a test ------------------------------------------------ +---------------------------------------------------------- ``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option:: diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 7d7bb0ad2..cf21cb6c4 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -85,11 +85,11 @@ def pytest_pyfunc_call(pyfuncitem): if pyfuncitem._isyieldedfunction(): pyfuncitem.args = [testfunction, pyfuncitem._args] else: - if 'func' in pyfuncitem._fixtureinfo.argnames: + if "func" in pyfuncitem._fixtureinfo.argnames: raise ValueError("--trace can't be used with a fixture named func!") - pyfuncitem.funcargs['func'] = testfunction + pyfuncitem.funcargs["func"] = testfunction new_list = list(pyfuncitem._fixtureinfo.argnames) - new_list.append('func') + new_list.append("func") pyfuncitem._fixtureinfo.argnames = tuple(new_list) yield diff --git a/testing/test_pdb.py b/testing/test_pdb.py index c7f542c26..dab8b28c4 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -697,7 +697,7 @@ class TestDebuggingBreakpoints(object): assert "reading from stdin while output" not in rest TestPDB.flush(child) -class TestTraceOption(): +class TestTraceOption: def test_trace_sets_breakpoint(self, testdir): p1 = testdir.makepyfile( """ @@ -712,4 +712,4 @@ class TestTraceOption(): rest = child.read().decode("utf8") assert "1 passed" in rest assert "reading from stdin while output" not in rest - TestPDB.flush(child) \ No newline at end of file + TestPDB.flush(child) From bc268a58d13ab398fac9c3c7d651296426d2fbb2 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Sun, 1 Jul 2018 20:22:50 -0700 Subject: [PATCH 11/18] Adding needed newline --- testing/test_pdb.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index dab8b28c4..59aa183ad 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -697,6 +697,7 @@ class TestDebuggingBreakpoints(object): assert "reading from stdin while output" not in rest TestPDB.flush(child) + class TestTraceOption: def test_trace_sets_breakpoint(self, testdir): p1 = testdir.makepyfile( From b75320ba95e2033f7c12b400c0d587e989667d08 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Mon, 2 Jul 2018 19:08:41 -0700 Subject: [PATCH 12/18] Fix --trace option with yield tests. --- src/_pytest/debugging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index cf21cb6c4..cd6656d18 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -83,7 +83,7 @@ def pytest_pyfunc_call(pyfuncitem): testfunction = pyfuncitem.obj pyfuncitem.obj = pdb.runcall if pyfuncitem._isyieldedfunction(): - pyfuncitem.args = [testfunction, pyfuncitem._args] + pyfuncitem._args = [testfunction, *pyfuncitem._args] else: if "func" in pyfuncitem._fixtureinfo.argnames: raise ValueError("--trace can't be used with a fixture named func!") From 10a8691ecade7ee3ebbf7223a9873bf83935f473 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Mon, 2 Jul 2018 19:46:26 -0700 Subject: [PATCH 13/18] Add support for yielded functions. --- src/_pytest/debugging.py | 47 ++++++++++++++++++++-------------------- testing/test_pdb.py | 21 ++++++++++++++++++ 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index cd6656d18..2bf290de4 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -15,9 +15,6 @@ except ImportError: SUPPORTS_BREAKPOINT_BUILTIN = False -immediately_break = False - - def pytest_addoption(parser): group = parser.getgroup("general") group._addoption( @@ -49,8 +46,8 @@ def pytest_configure(config): else: pdb_cls = pdb.Pdb - global immediately_break - immediately_break = config.getvalue("trace") + if config.getvalue("trace"): + config.pluginmanager.register(PdbTrace(), "pdbtrace") if config.getvalue("usepdb"): config.pluginmanager.register(PdbInvoke(), "pdbinvoke") @@ -76,24 +73,6 @@ def pytest_configure(config): config._cleanup.append(fin) -@hookimpl(hookwrapper=True) -def pytest_pyfunc_call(pyfuncitem): - if immediately_break: - pytestPDB.set_trace(set_break=False) - testfunction = pyfuncitem.obj - pyfuncitem.obj = pdb.runcall - if pyfuncitem._isyieldedfunction(): - pyfuncitem._args = [testfunction, *pyfuncitem._args] - else: - if "func" in pyfuncitem._fixtureinfo.argnames: - raise ValueError("--trace can't be used with a fixture named func!") - pyfuncitem.funcargs["func"] = testfunction - new_list = list(pyfuncitem._fixtureinfo.argnames) - new_list.append("func") - pyfuncitem._fixtureinfo.argnames = tuple(new_list) - yield - - class pytestPDB(object): """ Pseudo PDB that defers to the real pdb. """ @@ -136,6 +115,28 @@ class PdbInvoke(object): post_mortem(tb) +class PdbTrace(object): + @hookimpl(hookwrapper=True) + def pytest_pyfunc_call(self, pyfuncitem): + _test_pytest_function(pyfuncitem) + yield + + +def _test_pytest_function(pyfuncitem): + pytestPDB.set_trace(set_break=False) + testfunction = pyfuncitem.obj + pyfuncitem.obj = pdb.runcall + if pyfuncitem._isyieldedfunction(): + pyfuncitem._args = [testfunction, *pyfuncitem._args] + else: + if "func" in pyfuncitem._fixtureinfo.argnames: + raise ValueError("--trace can't be used with a fixture named func!") + pyfuncitem.funcargs["func"] = testfunction + new_list = list(pyfuncitem._fixtureinfo.argnames) + new_list.append("func") + pyfuncitem._fixtureinfo.argnames = tuple(new_list) + + def _enter_pdb(node, excinfo, rep): # XXX we re-use the TerminalReporter's terminalwriter # because this seems to avoid some encoding related troubles diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 59aa183ad..316b033fe 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -714,3 +714,24 @@ class TestTraceOption: assert "1 passed" in rest assert "reading from stdin while output" not in rest TestPDB.flush(child) + + def test_trace_against_yield_test(self, testdir): + p1 = testdir.makepyfile( + """ + def is_equal(a, b): + assert a == b + + def test_1(): + assert is_equal, 1, 1 + """ + ) + child = testdir.spawn_pytest("--trace " + str(p1)) + child.expect("test_1") + child.expect("(Pdb)") + child.sendline("c") + child.expect("(Pdb)") + child.sendeof() + rest = child.read().decode("utf8") + assert "1 passed" in rest + assert "reading from stdin while output" not in rest + TestPDB.flush(child) From 6cc4fe2412047dcea25bf2735db5896684680284 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Mon, 2 Jul 2018 19:53:46 -0700 Subject: [PATCH 14/18] Fixing bad indentation --- testing/test_pdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index 316b033fe..f82bccce6 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -725,7 +725,7 @@ class TestTraceOption: assert is_equal, 1, 1 """ ) - child = testdir.spawn_pytest("--trace " + str(p1)) + child = testdir.spawn_pytest("--trace " + str(p1)) child.expect("test_1") child.expect("(Pdb)") child.sendline("c") From 2f1a2cf07f521abb6a226d01205a124ae516a334 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Mon, 2 Jul 2018 19:55:08 -0700 Subject: [PATCH 15/18] Fixing --trace test. --- testing/test_pdb.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index f82bccce6..dff507f1b 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -728,8 +728,6 @@ class TestTraceOption: child = testdir.spawn_pytest("--trace " + str(p1)) child.expect("test_1") child.expect("(Pdb)") - child.sendline("c") - child.expect("(Pdb)") child.sendeof() rest = child.read().decode("utf8") assert "1 passed" in rest From 4afb8c428b2541afae9a80983191a351e0edc3dd Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Mon, 2 Jul 2018 20:20:42 -0700 Subject: [PATCH 16/18] Fix python 2 issues --- src/_pytest/debugging.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index 2bf290de4..9991307d0 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -127,7 +127,9 @@ def _test_pytest_function(pyfuncitem): testfunction = pyfuncitem.obj pyfuncitem.obj = pdb.runcall if pyfuncitem._isyieldedfunction(): - pyfuncitem._args = [testfunction, *pyfuncitem._args] + arg_list = list(pyfuncitem._args) + arg_list.insert(0, testfunction) + pyfuncitem._args = tuple(arg_list) else: if "func" in pyfuncitem._fixtureinfo.argnames: raise ValueError("--trace can't be used with a fixture named func!") From 4a925ef5e9334c12ac6ad8b8146dec6d664b9945 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Mon, 2 Jul 2018 20:29:59 -0700 Subject: [PATCH 17/18] Fixing bug in test. --- testing/test_pdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index dff507f1b..b550cca60 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -722,7 +722,7 @@ class TestTraceOption: assert a == b def test_1(): - assert is_equal, 1, 1 + yield is_equal, 1, 1 """ ) child = testdir.spawn_pytest("--trace " + str(p1)) From 067de257e140ec4ec716368c69c80f9278743fb7 Mon Sep 17 00:00:00 2001 From: Jeffrey Rackauckas Date: Mon, 2 Jul 2018 21:03:21 -0700 Subject: [PATCH 18/18] Fix test_pdb.py with pexpect --- testing/test_pdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_pdb.py b/testing/test_pdb.py index b550cca60..43a78908c 100644 --- a/testing/test_pdb.py +++ b/testing/test_pdb.py @@ -726,7 +726,7 @@ class TestTraceOption: """ ) child = testdir.spawn_pytest("--trace " + str(p1)) - child.expect("test_1") + child.expect("is_equal") child.expect("(Pdb)") child.sendeof() rest = child.read().decode("utf8")