From c1cb23cf4e6b8d51e440c12e38d581456b441908 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sat, 22 May 2021 22:43:22 +0200 Subject: [PATCH 1/2] Add --show-locals which print local variables in tracebacks. --- docs/changes.rst | 1 + docs/reference_guides/configuration.rst | 14 +++++++++++++ docs/tutorials/how_to_debug.rst | 11 ++++++++++ src/_pytask/cli.py | 2 ++ src/_pytask/collect.py | 2 +- src/_pytask/console.py | 27 +++++++++++++++++++++++++ src/_pytask/execute.py | 5 ++++- src/_pytask/resolve_dependencies.py | 17 +++++++++------- tests/test_console.py | 26 ++++++++++++++++++++++++ 9 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 tests/test_console.py diff --git a/docs/changes.rst b/docs/changes.rst index 61ea9432..f843733d 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -21,6 +21,7 @@ all releases are available on `PyPI `_ and - :gh:`88` adds a ``profile`` command to show information on tasks like duration and file size of products. - :gh:`93` fixes the display of parametrized arguments in the console. +- :gh:`94` adds ``--show-locals`` which allows to print local variables in tracebacks. 0.0.14 - 2021-03-23 diff --git a/docs/reference_guides/configuration.rst b/docs/reference_guides/configuration.rst index 9186175e..0aa4223a 100644 --- a/docs/reference_guides/configuration.rst +++ b/docs/reference_guides/configuration.rst @@ -130,6 +130,20 @@ The options pdb = True +.. confval:: show_locals + + If you want to print local variables of each stack frame in the tracebacks, set this + value to true. + + .. code-block:: console + + pytask build --show-locals + + .. code-block:: ini + + show_locals = True + + .. confval:: strict_markers If you want to raise an error for unregistered markers, pass diff --git a/docs/tutorials/how_to_debug.rst b/docs/tutorials/how_to_debug.rst index f5ea1f0f..3ac427cc 100644 --- a/docs/tutorials/how_to_debug.rst +++ b/docs/tutorials/how_to_debug.rst @@ -10,6 +10,17 @@ be more productive and gain more confidence in your code. To facilitate debugging, pytask offers two command-line options. +Tracebacks +---------- + +You can enrich the display of tracebacks by show local variables in each stack frame. +Just execute pytask with + +.. code-block:: console + + $ pytask --show-locals + + Debugging --------- diff --git a/src/_pytask/cli.py b/src/_pytask/cli.py index 380e5134..d638c9eb 100644 --- a/src/_pytask/cli.py +++ b/src/_pytask/cli.py @@ -43,6 +43,7 @@ def pytask_add_hooks(pm): from _pytask import collect from _pytask import collect_command from _pytask import config + from _pytask import console from _pytask import database from _pytask import debugging from _pytask import execute @@ -61,6 +62,7 @@ def pytask_add_hooks(pm): pm.register(collect) pm.register(collect_command) pm.register(config) + pm.register(console) pm.register(database) pm.register(debugging) pm.register(execute) diff --git a/src/_pytask/collect.py b/src/_pytask/collect.py index 07f6c4b1..b61d100f 100644 --- a/src/_pytask/collect.py +++ b/src/_pytask/collect.py @@ -265,7 +265,7 @@ def pytask_collect_log(session, reports, tasks): console.print() - console.print(Traceback.from_exception(*report.exc_info)) + console.print(Traceback.from_exception(*report.exc_info, show_locals=True)) console.print() diff --git a/src/_pytask/console.py b/src/_pytask/console.py index 23a60997..d03a56c2 100644 --- a/src/_pytask/console.py +++ b/src/_pytask/console.py @@ -3,9 +3,14 @@ import sys from typing import List +import click +from _pytask.config import hookimpl +from _pytask.shared import convert_truthy_or_falsy_to_bool +from _pytask.shared import get_first_non_none_value from rich.console import Console from rich.tree import Tree + _IS_WSL = "IS_WSL" in os.environ or "WSL_DISTRO_NAME" in os.environ _IS_WINDOWS_TERMINAL = "WT_SESSION" in os.environ _IS_WINDOWS = sys.platform == "win32" @@ -29,6 +34,28 @@ console = Console(color_system=_COLOR_SYSTEM) +@hookimpl +def pytask_extend_command_line_interface(cli): + show_locals_option = click.Option( + ["--show-locals"], + is_flag=True, + default=None, + help="Show local variables in tracebacks.", + ) + cli.commands["build"].params.append(show_locals_option) + + +@hookimpl +def pytask_parse_config(config, config_from_file, config_from_cli): + config["show_locals"] = get_first_non_none_value( + config_from_cli, + config_from_file, + key="show_locals", + default=False, + callback=convert_truthy_or_falsy_to_bool, + ) + + def format_strings_as_flat_tree(strings: List[str], title: str, icon: str) -> str: """Format list of strings as flat tree.""" tree = Tree(title) diff --git a/src/_pytask/execute.py b/src/_pytask/execute.py index 5bdd7656..68f3e394 100644 --- a/src/_pytask/execute.py +++ b/src/_pytask/execute.py @@ -161,6 +161,7 @@ def pytask_execute_task_log_end(report): @hookimpl def pytask_execute_log_end(session, reports): session.execution_end = time.time() + show_locals = session.config["show_locals"] n_successful = sum(report.success for report in reports) n_failed = len(reports) - n_successful @@ -183,7 +184,9 @@ def pytask_execute_log_end(session, reports): console.print() - console.print(Traceback.from_exception(*report.exc_info)) + console.print( + Traceback.from_exception(*report.exc_info, show_locals=show_locals) + ) console.print() show_capture = session.config["show_capture"] diff --git a/src/_pytask/resolve_dependencies.py b/src/_pytask/resolve_dependencies.py index 82dfaddb..e92813f1 100644 --- a/src/_pytask/resolve_dependencies.py +++ b/src/_pytask/resolve_dependencies.py @@ -119,13 +119,16 @@ def _has_node_changed(task_name: str, node_dict): try: state = node.state() except NodeNotFoundError: - return True - try: - state_in_db = State[task_name, node.name].state - except orm.ObjectNotFound: - return True - - return state != state_in_db + out = True + else: + try: + state_in_db = State[task_name, node.name].state + except orm.ObjectNotFound: + out = True + else: + out = state != state_in_db + + return out def _check_if_dag_has_cycles(dag): diff --git a/tests/test_console.py b/tests/test_console.py new file mode 100644 index 00000000..32bdf885 --- /dev/null +++ b/tests/test_console.py @@ -0,0 +1,26 @@ +import textwrap + +import pytest +from pytask import cli + + +@pytest.mark.end_to_end +def test_printing_of_local_variables(tmp_path, runner): + source = """ + def task_dummy(): + a = 1 + helper() + + def helper(): + b = 2 + raise Exception + """ + tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(source)) + + result = runner.invoke(cli, [tmp_path.as_posix(), "--show-locals"]) + assert result.exit_code == 1 + + captured = result.output + assert " locals " in captured + assert "a = 1" in captured + assert "b = 2" in captured From f78b6bc69d6f312899ce8841c4683962bfba9e86 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Sat, 22 May 2021 22:56:30 +0200 Subject: [PATCH 2/2] fix typo. --- README.rst | 3 ++- docs/tutorials/how_to_debug.rst | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index b7e31991..b01c5faa 100644 --- a/README.rst +++ b/README.rst @@ -193,7 +193,8 @@ and pytest's many contributors, this project would not have been possible. Thank Citation -------- -If you rely on pytask to manage your research project, please cite it with +If you rely on pytask to manage your research project, please cite it with the following +key to help others to discover the tool. .. code-block:: diff --git a/docs/tutorials/how_to_debug.rst b/docs/tutorials/how_to_debug.rst index 3ac427cc..27b11fef 100644 --- a/docs/tutorials/how_to_debug.rst +++ b/docs/tutorials/how_to_debug.rst @@ -41,7 +41,7 @@ prompt will enter the debugger enabling you to discover the source of the except .. tip:: Instead of Python's :mod:`pdb`, use `pdb++ `_ which - is more convenient, colorful has some useful features like the `sticky mode + is more convenient, colorful, and has some useful features like the `sticky mode `_.