I'll have "pdb" set up in Jupyter REPL so it drops into a debugger when I have an error with a function.
In the case of a long running function I want to debug (like the video example at the end) I'll set local variables with the same names as the variables of the function and then execute the function line-by-line/chunk-by-chunk.
The only problem is losing track of global state, but the same thing can happen in Jupyter notebooks.
This type of workflow is really common in Lisp and R.
If I want to integrate documentation and code, I'll use org-babel or R Markdown (which does work with Python).