Skip to content

Add @capture decorator to output widget #1934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

pbugnion
Copy link
Member

@pbugnion pbugnion commented Jan 26, 2018

This PR adds a @out.capture decorator that pushes all the output of a function to an output widget. It addresses issue #1846 .

For instance,

out = widgets.Output()

@out.capture
def f(arg):
    print(arg)

f('argument')

out.outputs
# => ({'name': 'stdout', 'output_type': 'stream', 'text': 'argument'},)

This is useful for capturing output from widget callbacks:

b = widgets.Button()
b.on_click(f)
# everytime someone clicks on `b`, `out.outputs` gets appended to.

To do:

- [x] Documentation on output widget (probably in a separate PR) (moved to issue #1935)

  • Use functools.wraps to avoid losing the function name and docstring (https://docs.python.org/3.6/library/functools.html#functools.wraps)
    - [ ] Is there a better name than out.capture? (I'm comfortable with out.capture).
    - [ ] Should current outputs be cleared on entry? (I think that would be more confusing -- we could add a recipe to the documentation for creating an output widget with a clear button).

@pbugnion pbugnion changed the title Add @capture decorator to output widget [WIP] Add @capture decorator to output widget Jan 26, 2018
@maartenbreddels
Copy link
Member

I like it 👍. I think capture is a good name. An alternative would be redirect, coming from redirecting stdout/stderr, but I like it less than capture.

By using functools.wraps, we avoid overwriting the __repr__ and __doc__ of the
wrapped callback.
@pbugnion
Copy link
Member Author

Thanks!

I think capture is a good name

I tend to agree. Jason isn't so keen. An alternative could be slurp, but it's pretty ugly.

@maartenbreddels
Copy link
Member

What about making it callable?

@out
def f(arg):
    print(arg)

@pbugnion
Copy link
Member Author

What about making it callable?

So replacing .capture with .__call__? I personally think that's a bit less explicit, but happy to implement it that way if we think that's the right way.

@pbugnion
Copy link
Member Author

Another possible question is -- we could clear the current outputs at each new entrance to func. This brings it a bit closer to how an output cell works: the user just sees the result of the last execution of func.

@maartenbreddels
Copy link
Member

Indeed, I think the name capture is self documenting, maybe @out.redirect_output even more. It's longer, but we have a tab key for a reason :).
Now that I think about it, do we want to redirect stderr/stdout to seperate places maybe, how does the output widget do it now? From a user point of view this may seen nice:

  • @out.redirect_all
  • @out.redirect_stderr
  • @out.redirect_stdout

@pbugnion
Copy link
Member Author

pbugnion commented Jan 26, 2018

how does the output widget do it now

The outputs trait is a tuple of each output type -- you get entries for display_data, error, output streams etc:

@out.capture
def f():
    """hello"""
    print('hi')
    display(HTML('<p>hello</p>'))
    raise Exception('bla')

f()

# =>

({'name': 'stdout', 'output_type': 'stream', 'text': 'hi\n'},
 {'data': {'text/html': '<p>hello</p>',
   'text/plain': '<IPython.core.display.HTML object>'},
  'metadata': {},
  'output_type': 'display_data'},
 {'ename': 'Exception',
  'evalue': 'bla',
  'output_type': 'error',
  'traceback': ['some long traceback']})

Given the number of output types, I'm not sure there'd be a strong use case for redirecting some outputs but not others. That behaviour would also be substantially different to the current context manager.

@pbugnion pbugnion changed the title [WIP] Add @capture decorator to output widget Add @capture decorator to output widget Jan 26, 2018
@pbugnion
Copy link
Member Author

I think this is ready for review and merging as is.

@jasongrout
Copy link
Member

What if we made it output.capture(clear=True) for the automatic clearing case? We could even put in the automatic matplotlib magic that @interact does.

We could support both @output.capture and @output.capture(clear=True) by checking the first argument. We could also change things to be @output.capture() for the base case. Or we could do something like what @jdemeyer did for interact, and do

@output.capture.options(clear=True)
def f():
    ...

@maartenbreddels
Copy link
Member

I don't like the .option, but the optional keyword seems pretty clean!

@jasongrout
Copy link
Member

Once we move to python 3, it will be even cleaner since we can insist that clear=True is a keyword argument.

@maartenbreddels
Copy link
Member

Is it maybe a plan to put in comments in the code, that we can grep to make that easy? # PY3: ... orso

@pbugnion
Copy link
Member Author

Thanks for the feedback -- I'll add a clear=True keyword to the decorator, with a PY3 style comment to remember to switch to enforcing a keyword argument when we move to python 3.

@pbugnion
Copy link
Member Author

pbugnion commented Feb 25, 2018

The decorator now supports clear_output=True. Setting this to True clears the output with every new invocation. -- I went for clear_output as the argument name, instead of clear, to remain consistent with the clear_output method name. Any extra *args and **kwargs passed to the decorator are passed onto the clear_output call, so users can use wait=True.

By default, clear_output is False -- I think this is more consistent with the behaviour of the context manager, which doesn't clear the output every time it is entered.

capture-decorator-example

This is ready for another review.

This is more consistent with the behaviour of the context manager, which does
not clear the output every time it is entered.
@jasongrout jasongrout merged commit 3526180 into jupyter-widgets:master Mar 14, 2018
@jasongrout
Copy link
Member

Thanks!

@pbugnion pbugnion deleted the output-widget-add-capture-decorator branch March 14, 2018 05:42
@pbugnion
Copy link
Member Author

Great, thank you!

@maartenbreddels
Copy link
Member

Yeah, great work!

@jasongrout jasongrout modified the milestones: Minor release, 7.2 Mar 19, 2018
@github-actions github-actions bot added the resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion. label Feb 7, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 7, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants