ipywidgets icon indicating copy to clipboard operation
ipywidgets copied to clipboard

ipywidgets.Output() context suppresses exceptions

Open yumasheta opened this issue 3 years ago • 10 comments

ipywidgets.Output() context manager supresses exceptions and prevents correct exit code

Description

For example take this code snippet and save it to a cell of a *.ipynb notebook.

import ipywidgets
o = ipywidgets.Output()
with o:
    raise Exception()
display(o)
print("execution continues")

Then execute the notebook with ipython:

$ ipython path/to/notebook.ipynb

The output will be

---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
/tmp/ipywidgets_bug/example.ipynb in <module>
      2 o = ipywidgets.Output()
      3 with o:
----> 4     raise Exception()
      5 display(o)
      6 print("execution continues")

Exception: 
Output()
execution continues

Expected behavior

The call to ipython should fail. If it is not aborted at the time when the exception is raised, at least it should exit with exit code other than 0.

Actual Behavior

The exception raised in the output context is suppressed. Execution continues and the ipython call exits with code 0.

Also the stderr output from the exception is not displayed unless display(o) is called explicitly. But this I reckon is expected behavior.

Context

  • ipywidgets version
    8.0.0a4
  • Operating System and version:
    archlinux kernel 5.12.7-arch1-1
  • Browser and version:
    not used
Troubleshoot Output
$PATH:
	/tmp/ipywidgets_bug/.venv/bin
	/usr/local/bin
	/usr/bin
	/usr/local/sbin
	/opt/cuda/bin
	/opt/cuda/nsight_compute
	/opt/cuda/nsight_systems/bin
	/usr/lib/jvm/default/bin
	/usr/bin/site_perl
	/usr/bin/vendor_perl
	/usr/bin/core_perl

sys.path: /tmp/ipywidgets_bug/.venv/bin /usr/lib/python39.zip /usr/lib/python3.9 /usr/lib/python3.9/lib-dynload /tmp/ipywidgets_bug/.venv/lib/python3.9/site-packages

sys.executable: /tmp/ipywidgets_bug/.venv/bin/python

sys.version: 3.9.5 (default, May 24 2021, 12:50:35) [GCC 11.1.0]

platform.platform(): Linux-5.12.7-arch1-1-x86_64-with-glibc2.33

which -a jupyter: /tmp/ipywidgets_bug/.venv/bin/jupyter /usr/bin/jupyter

pip list: Package Version ------------------ ------- attrs 21.2.0 backcall 0.2.0 decorator 5.0.9 ipykernel 5.5.5 ipython 7.24.1 ipython-genutils 0.2.0 ipywidgets 8.0.0a4 jedi 0.18.0 jsonschema 3.2.0 jupyter-client 6.1.12 jupyter-core 4.7.1 jupyterlab-widgets 2.0.0a1 matplotlib-inline 0.1.2 nbformat 5.1.3 parso 0.8.2 pexpect 4.8.0 pickleshare 0.7.5 pip 21.1.1 prompt-toolkit 3.0.18 ptyprocess 0.7.0 Pygments 2.9.0 pyrsistent 0.17.3 python-dateutil 2.8.1 pyzmq 22.1.0 setuptools 56.0.0 six 1.16.0 tornado 6.1 traitlets 5.0.5 wcwidth 0.2.5 widgetsnbextension 4.0.0a2

Command Line Output
[TerminalIPythonApp] IPYTHONDIR set to: /home/XXXX/.ipython
[TerminalIPythonApp] Using existing profile dir: '/home/XXXX/.ipython/profile_default'
[TerminalIPythonApp] Searching path ['/tmp/ipywidgets_bug', '/home/XXXX/.ipython/profile_default', '/tmp/ipywidgets_bug/.venv/etc/ipython', '/usr/local/etc/ipython', '/etc/ipython'] for config files
[TerminalIPythonApp] Attempting to load config file: ipython_config.py
[TerminalIPythonApp] Looking for ipython_config in /etc/ipython
[TerminalIPythonApp] Looking for ipython_config in /usr/local/etc/ipython
[TerminalIPythonApp] Looking for ipython_config in /tmp/ipywidgets_bug/.venv/etc/ipython
[TerminalIPythonApp] Looking for ipython_config in /home/XXXX/.ipython/profile_default
[TerminalIPythonApp] Looking for ipython_config in /tmp/ipywidgets_bug

[TerminalIPythonApp] Loading IPython extensions... [TerminalIPythonApp] Loading IPython extension: storemagic [TerminalIPythonApp] Running file in user namespace: /tmp/ipywidgets_bug/example.ipynb

Exception Traceback (most recent call last) /tmp/ipywidgets_bug/example.ipynb in 2 o = ipywidgets.Output() 3 with o: ----> 4 raise Exception() 5 display(o) 6 print("execution continues")

Exception: Output() execution continues [TerminalIPythonApp] IPython not interactive...

yumasheta avatar Jun 02 '21 22:06 yumasheta

Thanks @yumasheta.

Just to double check, is this only happening in the 8.0 alpha release? Or is it also an issue with 7.x?

jtpio avatar Jun 15 '21 17:06 jtpio

Yes, it does happen with the current version (7.6.3) on PYPI as well. Just verified it.

yumasheta avatar Jun 15 '21 17:06 yumasheta

ok thanks for checking :+1:

jtpio avatar Jun 15 '21 17:06 jtpio

Here is the relevant code:

https://github.com/jupyter-widgets/ipywidgets/blob/4132414e5a4615a78cc6b849824a6df0155b26e9/ipywidgets/widgets/widget_output.py#L132-L134

Can you say more about your situation, and why you want the exception propagated out of the context handler rather than just handled and displayed, as the code above indicates?

jasongrout avatar Jun 18 '21 17:06 jasongrout

Ok, so maybe I'm missing something. I've been struggling a bit with how the output context works and is meant to work. I'd like a way to handle exceptions that occur inside the output context in the surrounding code. But if that exception never leaves the context that's not possible. So basically:

output = ipywidgets.Output()

try:
    with output:
        raise Exception
except:
    print("error")

But this doesn't work.

If you don't mind me talking about my implementation: I have some custom ipywidgets that use the Output context so that they can clear it and replace content in the same output. So it might not be a problem when using the jupyter notebook interactively (that is: exceptions silently fail), because you'll see the output anyway. However, it'd be nice to have an options to handle those exceptions. In particular I have unit tests that execute pre-defined notebook code (via ipython) that goes through the features of the widgets and has to fail on exceptions. But as you can imagine, this doesn't happen.

TL,DR: I want to "unit test" custom widgets for jupyter. I need a way to execute (and manipulate) my widgets programatically and test for errors. The widgets put their output in Output contexts, such that exceptions within those don't lead to failed tests.

yumasheta avatar Jun 18 '21 19:06 yumasheta

This would be, in my opinion, a breaking change on the behavior of ipywidgets.Output() -- so I think it would be worth considering shipping in 8.0 rather than 8.1. @vidartf, Any potential possibilities of including this earlier?

To me, the current behavior sounds really strange. It was not supposed to suppress exceptions, and the use of Output can change the control flow of user program.

(Example code: suppressed as the rationale is same as the OP)

More specifically,

def foo():
    with output:  # block 1
        print("Hello")
        data = some_logic_that_throws_an_exception()
        print("this will not be executed")
        
    with output:  # block 2
        print("data = ", data)

This will first show a stacktrace for the Exception from the first block. The rest of the code, especially block 2 should NOT be executed, but it does, and another stacktrace will be shown for an exception thrown when accessing data (UnboundLocalError: local variable 'data' referenced before assignment).

In addition, whether ipython is involved or not does change the behavior and flow of the same code.

Alternatively, we may have a configurable option whether to suppress exception or not if we want to keep a backward compatibility), but I suggest we should correct the behavior as we deliver a major release.

wookayin avatar Mar 16 '22 00:03 wookayin

I submitted a PR #3417 that would fix this one. My proposal is to change the behavior on whether exceptions won't be suppressed by default, but this would be up to core developers' decision. Thank you for your considerations.

wookayin avatar Mar 16 '22 08:03 wookayin

Thanks for the PR @wookayin . As a placeholder, you can also create a new widget by inheritance (no action in ipywidgets repo needed):

class NocatchOutput(ipywidgets.Output):
    def __exit__(self, *args, **kwargs):
        super().__exit__(*args, **kwargs)

vidartf avatar Mar 17 '22 11:03 vidartf

@vidartf many thanks for the workaround. This seems to be printing the Traceback in red inside the Output, disregarding if the exception is later handled. Is there any way to avoid this?

DiegoF90 avatar Sep 21 '23 18:09 DiegoF90

So this weird behavior has not been fixed yet? I am still trying to understand why the user would expect or want an output context to essentially redirect stderr to /dev/null. It is the definition of failing ungracefully. Any updates?

xguse avatar Nov 09 '23 14:11 xguse