diff --git a/docs/community/training/debugging/building-your-skills.md b/docs/community/training/debugging/building-your-skills.md index 7e5ae9f6..b35bfc18 100644 --- a/docs/community/training/debugging/building-your-skills.md +++ b/docs/community/training/debugging/building-your-skills.md @@ -8,8 +8,6 @@ ## Identifying errors -**TODO:** explain how to read tracebacks. - Write a failing test case, this allows you to verify that the bug can be reproduced. ## Developing a plan diff --git a/docs/community/training/debugging/stacktrace.R b/docs/community/training/debugging/stacktrace.R new file mode 100755 index 00000000..3dc5607a --- /dev/null +++ b/docs/community/training/debugging/stacktrace.R @@ -0,0 +1,54 @@ +#!/usr/bin/env -S Rscript --vanilla + +options(error = rlang::entrace) + + +main <- function() { + do_big_tasks() + invisible(0) +} + +do_big_tasks <- function(num_tasks = 20, quiet = TRUE) { + for (i in seq_len(num_tasks)) { + prepare_things(i, quiet = quiet) + do_first_step(i, quiet = quiet) + do_second_step(i, quiet = quiet) + if (i > 15) { + do_third_step(i, quiet = quiet) + } + } +} + +prepare_things <- function(task_num, quiet = TRUE) { + if (!quiet) { + cat("Preparing for task #", task_num, "\n", sep = "") + } +} + +do_first_step <- function(task_num, quiet = TRUE) { + if (!quiet) { + cat("Task #", task_num, ": doing step #1\n", sep = "") + } +} + +do_second_step <- function(task_num, quiet = TRUE) { + if (!quiet) { + cat("Task #", task_num, ": doing step #2\n", sep = "") + } +} + +do_third_step <- function(task_num, quiet = TRUE) { + if (!quiet) { + cat("Task #", task_num, ": doing step #3\n", sep = "") + } + try_something() +} + +try_something <- function() { + stop("Whoops, this failed") +} + +if (! interactive()) { + status <- main() + quit(status = status) +} diff --git a/docs/community/training/debugging/stacktrace.py b/docs/community/training/debugging/stacktrace.py new file mode 100755 index 00000000..24e2b6e6 --- /dev/null +++ b/docs/community/training/debugging/stacktrace.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +import sys + + +def main(): + do_big_tasks() + return 0 + + +def do_big_tasks(num_tasks=20, quiet=True): + for i in range(num_tasks): + prepare_things(i, quiet=quiet) + do_first_step(i, quiet=quiet) + do_second_step(i, quiet=quiet) + if i > 15: + do_third_step(i, quiet=quiet) + + +def prepare_things(task_num, quiet=True): + if not quiet: + print(f'Preparing for task #{task_num}') + + +def do_first_step(task_num, quiet=True): + if not quiet: + print(f'Task #{task_num}: doing step #1') + + +def do_second_step(task_num, quiet=True): + if not quiet: + print(f'Task #{task_num}: doing step #2') + + +def do_third_step(task_num, quiet=True): + if not quiet: + print(f'Task #{task_num}: doing step #3') + try_something() + + +def try_something(): + raise ValueError("Whoops, this failed") + + +if __name__ == "__main__": + status = main() + sys.exit(status) diff --git a/docs/community/training/debugging/understanding-error-messages.md b/docs/community/training/debugging/understanding-error-messages.md new file mode 100644 index 00000000..3e3aeef9 --- /dev/null +++ b/docs/community/training/debugging/understanding-error-messages.md @@ -0,0 +1,80 @@ +# Understanding error messages + +!!! tip + + The visible error and its root cause may be located in very different parts of your code. + +If there's an error in your code that causes the program to terminate, **read the error message** and see what it can tell you. + +Most of the time, the error message should allow to identify: + +- **What went wrong?** + For example, did it try to read data from a file that does not exist? + +- **Where did this happen?** + On which line of which file did the error occur? + +## Stack traces + +When an error occurs, one useful piece of information is knowing which functions were called in order to make the error occur. + +Below we have example Python and R scripts that produce an error. + +!!! question + + Can you identify where the error occurred, just by looking at the error message? + +=== "Overview" + + You can download each script and run them on your own computer: + + - [stacktrace.py](stacktrace.py) + - [stacktrace.R](stacktrace.R) + + +=== "Python" + + ### The error message + + ```text + Traceback (most recent call last): + File "stacktrace.py", line 46, in + status = main() + File "stacktrace.py", line 7, in main + do_big_tasks() + File "stacktrace.py", line 17, in do_big_tasks + do_third_step(i, quiet=quiet) + File "stacktrace.py", line 38, in do_third_step + try_something() + File "stacktrace.py", line 42, in try_something + raise ValueError("Whoops, this failed") + ValueError: Whoops, this failed + ``` + + ??? info "Source code" + + ```py title="stacktrace.py" linenums="1" + --8<-- "stacktrace.py" + ``` + +=== "R" + + ### The error message + + ```text + Error in try_something() : Whoops, this failed + Calls: main -> do_big_tasks -> do_third_step -> try_something + Backtrace: + ▆ + 1. └─global main() + 2. └─global do_big_tasks() + 3. └─global do_third_step(i, quiet = quiet) + 4. └─global try_something() + Execution halted + ``` + + ??? info "Source code" + + ```R title="stacktrace.R" linenums="1" + --8<-- "stacktrace.R" + ``` diff --git a/mkdocs.yml b/mkdocs.yml index 45e476b7..8bae400c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -110,6 +110,7 @@ nav: - "Learning objectives": community/training/debugging/learning-objectives.md - "Debugging manifesto": community/training/debugging/manifesto.md - "What is debugging?": community/training/debugging/what-is-debugging.md + - "Understanding error messages": community/training/debugging/understanding-error-messages.md - "Why are debuggers useful?": community/training/debugging/why-are-debuggers-useful.md - "Using a debugger": community/training/debugging/using-a-debugger.md - "Example: Square numbers": community/training/debugging/example-square-numbers.md