Skip to content
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

Provide mechanism to register callback from JavaScript to specific functions defined in kernel #12252

Open
b3m2a1 opened this issue Mar 21, 2022 · 6 comments
Labels
enhancement tag:Extension Idea Ideas for interesting extensions that live outside of JupyterLab core.

Comments

@b3m2a1
Copy link

b3m2a1 commented Mar 21, 2022

Problem

I'm trying to use JupyterLab for providing simplified access to feature discovery and report generation in a scientific context. My goal is to be able to mimic what Mathematica can do on the desktop.

To this end I would like to be able to create nicely formatted HTML/Markdown reports that can include buttons and other components that can call back to the JupterLab backend to get data or new chunks of HTML/Markdown to render. I would like this to be possible in an ad hoc fashion by generating the HTML/Markdown/JS fragments on the Python side so that things can be easily extensible and my coworkers can play with it.

The widget interface isn't sufficiently powerful for me to do everything I want and I can't easily integrate them with a JavaScript framework like Bootstrap or Vue.

As a concrete very, very minimal example, I would like to be able to provide an object on the python side like

class Results:
    def __init__(self, run_name, **params):
          ...
    def save(self, filename):
          ...
    def _repr_html_(self):
          return Bootstrap.Panel(
                        Bootstrap.PanelHeader(self.run_name),
                        Bootstrap.PanelBody(
                             self._get_report_grid(),
                             Bootstrap.InputGroup(
                                 BootstrapExtensions.FilePicker(id='resultsFile'), 
                                 BootstrapExtensions.Button('Save', self.save('resultsFile')))
                            )
                      )

The biggest thing is that I would like to be able to play with the styling or make other ad hoc interfaces like that on the fly. I don't want to make a custom extension for every type of interaction I want to do just to be able to make the interface look a little bit nicer or to be able to do a slightly more involved interaction. I just want JupyterLab to work as cleanly as Mathematica does for easy, simple things like this.

Proposed Solution

The easiest path forward on this, to my eyes, is to provide a way to register JavaScript callbacks to functions defined in the kernel. For security purposes, it would be sufficient to restrict these to only be possible to define on the kernel side (so that people couldn't just send you a corrupted notebook that executes arbitrary code). This allows a user to communicate with a kernel from the front-end but only through a set of specific callbacks that were loaded by the kernel itself.

Additional context

It would of course still be possible for someone to hide a dangerous callback in a package, but the user would have to install that package themselves and if they're installing a poisioned package there's not much the JupyterLab team can do...

Being able to access specific functions from the front-end would be very powerful and allow me to do actually useful interactive stuff in JupyerLab

@jupyterlab-probot jupyterlab-probot bot added the status:Needs Triage Applied to new issues that need triage label Mar 21, 2022
@welcome
Copy link

welcome bot commented Mar 21, 2022

Thank you for opening your first issue in this project! Engagement like this is essential for open source projects! 🤗

If you haven't done so already, check out Jupyter's Code of Conduct. Also, please try to follow the issue template as it helps other other community members to contribute more effectively.
welcome
You can meet the other Jovyans by joining our Discourse forum. There is also an intro thread there where you can stop by and say Hi! 👋

Welcome to the Jupyter community! 🎉

@b3m2a1
Copy link
Author

b3m2a1 commented Mar 21, 2022

It is possible to get part of the way there by using ipyevents and applying that to an existing HTML widget with the appropriate callbacks but it would be much more powerful to have a proper callback mechanism so I could have a fully HTML layout rather than needing to monkey patch a bunch of widgets together

@b3m2a1
Copy link
Author

b3m2a1 commented Mar 21, 2022

As an example of what would be more straightforward through callbacks, I can create a basic HTML layout with Bootstrap and make a clickable header with ipyevents and stitch everything together by messing with classes and regenerating the body every call:

header = ipywidgets.HTML(Bootstrap.CardHeader(Bootstrap.Heading("Click for new table").add_class('text-success')).tostring())
header.add_class('m-0')
body = ipywidgets.HTML(
        Bootstrap.CardBody(Bootstrap.Table(
                np.random.rand(4, 3).round(3).tolist(),
                headers=["x", "y", "z"]
            )).add_class('table-striped', 'table-hover', 'p-0').tostring()
        )
body.add_class('m-0')
event_listener = ipyevents.Event(source=header, watched_events=['click'])
def handle_event(event):
    body.value = Bootstrap.CardBody(Bootstrap.Table(
                np.random.rand(4, 3).round(3).tolist(),
                headers=["x", "y", "z"]
            )).add_class('table-striped', 'table-hover', 'p-0').tostring()
event_listener.on_dom_event(handle_event)
box = ipywidgets.VBox([
    header,
    body
])
box.add_class('card')
box.add_class('p-0')

image

whereas with a JS callback I could just call into the python function to regenerate the table and update the DOM directly on the JS side

@jasongrout
Copy link
Contributor

Some thoughts after briefly reading the above:

  1. If you are generating your html/js in python, then what you've done with ipyevents certainly is a route. ipyevents provides the generic communication mechanism from the browser back to the kernel, which is the tricky thing to do in your idea.
  2. I'm not familiar with this Bootstrap python library you are using, but perhaps ipyvuetify or ipymaterialui give you some of the more advanced ux controls you want?

@b3m2a1
Copy link
Author

b3m2a1 commented Mar 22, 2022

Both of those libraries could work if I wanted to be bound to one of them (btw the bootstrap library is just a thing I knocked up yesterday), but I think the bigger issue of exposing a more flexible way to communicate with the kernel from the front-end remains.

There's no particular reason I can think of that the front-end should be so impoverished in terms of functionality, especially when the classic notebook interface didn't suffer from that and there are ways to prevent arbitrary code execution without totally locking the FE down.

@JasonWeill JasonWeill added tag:Extension Idea Ideas for interesting extensions that live outside of JupyterLab core. and removed status:Needs Triage Applied to new issues that need triage labels Mar 24, 2022
@JasonWeill
Copy link
Contributor

Triage note: This functionality may be the kernel of an idea for a new extension for JupyterLab. Thank you for your suggestion!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement tag:Extension Idea Ideas for interesting extensions that live outside of JupyterLab core.
Projects
None yet
Development

No branches or pull requests

3 participants