diff --git a/CHANGES.md b/CHANGES.md index dd4883ae2..001c15b77 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ ### Features * Two new commands, `augur read-file` and `augur write-file`, now allow external programs to do i/o like Augur by piping from/to these new commands. They provide handling of compression formats and newlines consistent with the rest of Augur. [#1562][] (@tsibley) +* A new debugging mode can be enabled by setting the `AUGUR_DEBUG` environment variable to `1` (or another truthy value). Currently the only effect is to print more information about handled (i.e. anticipated) errors. For example, stack traces and parent exceptions in an exception chain are normally omitted for handled errors, but setting this env var includes them. Future debugging and troubleshooting features, like verbose operation logging, will likely also condition on this new debugging mode. [#1577][] (@tsibley) ### Bug Fixes @@ -13,6 +14,7 @@ [#1561]: https://github.com/nextstrain/augur/pull/1561 [#1562]: https://github.com/nextstrain/augur/pull/1562 [#1564]: https://github.com/nextstrain/augur/pull/1564 +[#1577]: https://github.com/nextstrain/augur/pull/1577 diff --git a/augur/__init__.py b/augur/__init__.py index 7b4f2066b..9ef4928dd 100644 --- a/augur/__init__.py +++ b/augur/__init__.py @@ -11,6 +11,7 @@ from types import SimpleNamespace from treetime import TreeTimeError, TreeTimeUnknownError +from .debug import DEBUGGING from .errors import AugurError from .io.print import print_err from .argparse_ import add_command_subparsers, add_default_command @@ -67,21 +68,35 @@ def run(argv): try: return args.__command__.run(args) except AugurError as e: + if DEBUGGING: + traceback.print_exc(file=sys.stderr) print_err(f"ERROR: {e}") sys.exit(2) except RecursionError: + if DEBUGGING: + traceback.print_exc(file=sys.stderr) print_err("FATAL: Maximum recursion depth reached. You can set the env variable AUGUR_RECURSION_LIMIT to adjust this (current limit: {})".format(sys.getrecursionlimit())) sys.exit(2) except FileNotFoundError as e: + if DEBUGGING: + traceback.print_exc(file=sys.stderr) print_err(f"ERROR: {e.strerror}: '{e.filename}'") sys.exit(2) except TreeTimeUnknownError as e: + # TreeTime already prints the traceback (and some other verbiage) in + # TreeTime.run()¹, so don't duplicate it. This is also why the "(see + # above)" in our message below makes sense. + # -trs, 14 Aug 2024 + # + # ¹ print_err(dedent("""\ ERROR from TreeTime: An error occurred in TreeTime (see above). This may be due to an issue with TreeTime or Augur. Please report you are calling TreeTime via Augur. """)) sys.exit(2) except TreeTimeError as e: + if DEBUGGING: + traceback.print_exc(file=sys.stderr) print_err(f"ERROR: {e}") print_err("\n") print_err(dedent("""\ diff --git a/augur/debug.py b/augur/debug.py new file mode 100644 index 000000000..9d36f1f82 --- /dev/null +++ b/augur/debug.py @@ -0,0 +1,12 @@ +""" +Debug flags and utilities. + +.. envvar:: AUGUR_DEBUG + + Set to a truthy value (e.g. 1) to print more information about (handled) + errors. For example, when this is not set or falsey, stack traces and + parent exceptions in an exception chain are omitted from handled errors. +""" +from os import environ + +DEBUGGING = bool(environ.get("AUGUR_DEBUG")) diff --git a/docs/api/developer/augur.debug.rst b/docs/api/developer/augur.debug.rst new file mode 100644 index 000000000..a84b1bc8e --- /dev/null +++ b/docs/api/developer/augur.debug.rst @@ -0,0 +1,7 @@ +augur.debug module +================== + +.. automodule:: augur.debug + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/api/developer/augur.rst b/docs/api/developer/augur.rst index 8d611731b..4ef15e6e3 100644 --- a/docs/api/developer/augur.rst +++ b/docs/api/developer/augur.rst @@ -31,6 +31,7 @@ Submodules augur.ancestral augur.argparse_ augur.clades + augur.debug augur.distance augur.errors augur.export