# Jupyter Community Workshop - The Future of Jupyter Widgets
## Tuesday 18th of October - Friday 21st of October, London
## Week Agenda:
[Google Docs Agenda](https://docs.google.com/spreadsheets/d/1KMB3Mda1MNVXzjSm30eNO7qIW7Oj2z3nYBFY5l-sZ0A/edit#gid=998190500)
## Friday 2022-10-21
Zoom link for remote attendees: https://bloomberg.zoom.us/j/95137727255?pwd=OGpmYmI0RkJZelk1V2ErUWoyc2w4QT09
* 8:30-9:30: Breakfast
* 9:30-12:00: Knowledge sharing / Hacking
* 12:00-13:00: Lunch
* 13:00-18:00: Socials / Hacking!
ipywidgets overview
- package overview:
- js
- base/widget.ts -> basic widget class
- base-manager -> inherts from base
- html manager -> based on base manager
- schema -> useful documenation around
- python
-
## Thursday 2022-10-20
Zoom link for remote attendees: https://bloomberg.zoom.us/j/92527562243?pwd=ODFuR2VlZjVia0xLcCtPemRhUkFPUT09
* 8:30-9:30: Breakfast
* 9:30-12:00: Building larger applications using ipywidgets (with discussion of performance and developer experience)
* 12:00-13:00: Lunch
* 13:00-17:00: Hacking!
* 17:00-18:00: Demos
* 18:30 (optional): Impromptu drinks/dinner
## Wednesday 2022-10-19
Zoom link for remote attendees: https://bloomberg.zoom.us/j/97006618705?pwd=SnM3OXdWYUtDbWUwbFZZenY5S2ZWZz09
* 8:30-9:30: Breakfast
* 9:30-12:00: Reviewing the entire ipywidgets stack
* 12:00-13:00: Lunch
* 13:00-17:00: Hacking!
* 17:00-18:00: Demos
* 18:30 (optional): Dinner @ Apulia (https://apuliarestaurant.co.uk/)
## Tuesday 2022-10-18
Zoom link for remote attendees: https://bloomberg.zoom.us/j/93920480098?pwd=SnkvNGlCWTZiUGY4b3EzWlNMMHEydz09
15 Attendees representing academia and industry, with core developers, third-party widget developers, widget users, etc.
* 8:30-9:30: Breakfast
* 9:30-12:00: Introductions and demos
* 12:00-13:00: Lunch
* 13:00-17:00: Discussion about widgets in various frontends
* 17:00-18:00: Demos
* 18:30 (optional): Drinks at The Last Talisman
### Demos
* Maarten Breddels on [Reacton](https://github.com/widgetti/reacton)
* Provides an answer for how to build large applications in ipywidgets, basically a port of React to Python, which uses ipywidgets, etc., as the equivalent of the DOM elements
* Wrapped ipywidgets, and can autogenerate wrapper as long as constructor arguments match the traits. Currently ipywidgets, ipycanvas, bqplot, wrappers are shipped with reacton
* Compare with idom: idom is driven by react. Reacton does everything in Python
* How to create a new wrapped react component? Create a widget, then generate the wrapper in Reacton.
* How do we embed an ipywidget into a React application?
* Maarten does embed into Vue
* We could have better examples for embedding an ipywidgets into existing web applications
* You can embed reacton inside an ipywidgets app, and embed ipywidgets inside a reacton app
* [Max Fordham](https://www.maxfordham.com/): [ipyautoui](https://github.com/maxfordham/ipyautoui) project
* Generates a ui based on pydantic types
* Nate Rush on [mito](https://www.trymito.io/)
* Jeremy Tuloup, Martin Renou:
* JupyterLite
* Jupyter Notebook 7 (based on JupyterLab components)
* [JupyterLab App Cookiecutter](https://github.com/jupyterlab-contrib/jupyterlab-app-cookiecutter)
* https://xeus-python-kernel.readthedocs.io/en/latest
### Discussion: Widgets in Other Locations
* Don Jayamanne on ipywidgets in VS Code
* https://github.com/microsoft/vscode-jupyter/wiki/IPyWidget-Architecture-Challenges
* Details of how VS code handles message passing from frontend to kernel
* Widget echos: https://github.com/jupyter-widgets/ipywidgets/pull/3195
* Pete Blois on colab:
* How colab handles this: https://github.com/googlecolab/colabtools/blob/main/google/colab/output/_widgets.py
* [Finding all dependencies of a set of widgets including jslink](https://github.com/jupyter-widgets/ipywidgets/pull/3114/files#diff-346aff842b126a18502cf33a55d587dbfe6c963c9991cf9f9bb56e205b88e41eR299)
* Issue in ipycanvas where there was a missing ref between widgets https://github.com/martinRenou/ipycanvas/pull/290 resulting in iframed canvases failing to render.
### Issues for working groups
* iframe isolation for widgets
* lightweight kernel manager that can support multiple versions of the widget manager
* output widgets, and how outputs are updated (kernel vs frontend)
* ipywidgets 7+8
* Want to support ipywidgets 7 core controls in ipywidgets 8 widget manager
* Layering the apis in the frontend to separate concerns:
* synchronization
* model instantiation
* view rendering
* Clearly documenting the entry points for AMD modules (like in JLab has metadata in package.json)
* Provide a RESTful end point to expose the list of widgets and their corresponding entry points in JLab and Jupyter Notebook.
* Rich outputs without Widgets (HTML & Comms)
* Freezing part of the stack to give predictability and prevent breakageshttps://github.com/jpmorganchase/jupyterlab_templates
* Testing
* currently have unit tests and some Galata ui tests
* expand to more comprehensive protocol end-to-end protocol tests
*
* ipywidgets in other non-notebook contexts: streamlit, solara, shiny
* ipywidgets as a protocol?
* Moving the documentation (which exists in an npm package somewhere) to the docs
* Should the kernel handle the Output widgets redirection?
* error handling for comm messages that don't get through
* Handling the "comm target does not exist". Should we [improve the jupyter kernel protocol](https://github.com/jupyter/enhancement-proposals/issues/86)?
* [Remove the ipykernel and IPython dependencies](https://github.com/jupyter-widgets/ipywidgets/issues/3514):
* Rely on a Comm modules that's separated from ipykernel: https://github.com/martinRenou/comm / https://github.com/jupyter-widgets/ipywidgets/pull/3533 / https://github.com/ipython/ipykernel/pull/973 / https://github.com/jupyter-xeus/xeus-python/issues/342
* ipywidget author education:
* Finding documentation in the first place
* Custom widget dev tutorials
* Checking for broken links / tutorials
* Run codespell on the docs/code-base
* Linting: https://github.com/jupyter-widgets/ipywidgets/pull/3566
* Packaging: https://github.com/jupyter-widgets/ipywidgets/pull/3587
* async/await: https://github.com/jupyter-widgets/ipywidgets/pull/2779
* What problems are we solving with widgets vs. what we set out to solve:
* devs who rely on the packaging aspect: widgets as a ways to distribute to multiple UIs
* users who rely on widgets: quick and dirty prototypes vs. full applications (or, as a gateway from one to the other)
### End of day Demos
* Matt Craig: Demoed
* Templating extensions:
* https://pypi.org/project/jupyterlab-multicontents-templates/
* https://github.com/jpmorganchase/jupyterlab_templates ([pypi](https://pypi.org/project/jupyterlab-templates/))
* https://github.com/trungleduc/jupyter_app_launcher
* https://jupyterstarters.readthedocs.io/en/stable/
## Wednesday, Oct 19
### Full ipywidgets stack discussion
#### Traitlets discussion
- pydantic (typing) -> value: Syncable[Int]
- Can we rebuild trailets on top of pydantic?
- Outside our ecosystem, so is it worth the risk?
- Perhaps the best bet is just improving traitlets
- We don't have to abondon trailets for widget authors; we can let widget users use pydantic
- Refactor traitlets:
- Performance:
- Creating traitlets with static default values can be improved 20x (maarten has working PR)
- Event processing: a lot of time appears to be spent in `bunch`
- Exposing typing to static typers
- Removing the metaclass that attaches the name to each traitlet
- [pep 487](https://peps.python.org/pep-0487/)
- [Adding typing using dataclass transform](https://peps.python.org/pep-0681/)
#### JS Stack
#### Backbone
- Uses:
- Eventing
- Framework for instantiating and syncing
- Equality comparison (deciding if you need to sync something); _.isEqual
- Lifycycle
- State of Backbone as of October 2022:
- There is a pinned issue on the repo stating the project is still maintained: https://github.com/jashkenas/backbone/issues/4244#issuecomment-991381707
- There was a release on February 26th 2022: https://backbonejs.org/#changelog
- So there *might* not be a need to fork and maintain a custom Backbone *for now*
- Issues with backbone:
- jquery: exposed as part of the API for backwards compatibilty
- Would be nice to remove this as it's not used by jupyter
- How to remove this without breaking custom widgets, unclear
- $ as a property, first time you access it, it loads?
- We don't fit the syncronization model, have had to strip a lot of this
- Maintainablity: it's fossilized, hard to get changes merged.
- Maarten had issue getting equality checking for typed arrays merged
- The goal perhaps is removing jquery
- It's very hard to do a lazy-load of jquery, as these are syncronous APIs
- Removing jquery is far less of a breaking change than ipywidgets 7 -> ipywidgets 8
- It's like 50kb for 2022. The modern browser versions are super small, as they punt to the browser for everything
- What's even the goal of getting rid of jquery?
- The biggest thing you get is the a simpler interface - reimplementing widgets is easier
- Options:
- Fork backbone, have more control, and we can get rid of jquery
- Leave in all the syncing stuff, etc - in case people rely on this
- Could remove the collections, etc
- What would it take to drop backbone?
- If we had lumino widget class from the start, would we have used backbone?
- Backbone has a more nuianced event system: e.g. the value change events, there's more advanced logic here
- Changing the event handling is high-risk
- e.g. ipythreejs relies on specific event ordering
- Forking the portion of backbone that does this event handling?
- Issues with typing? Only for when widget authors upgrade, so not the worst sort of breaking change
- Could we just have a strategy for when backbone fails us?
- E.g. if you could put in a custom equals, this typed array equality comparison would be ok
- Deprication messages?
- When you use $ on the page
- In webpack, we could log a warning
- On ipywidgets 7 -> 8:
- This could be a test case for if widget authors are willing to upgrade
- Potentially, the upgrade fatigue will just build up
- Maybe we should insist on being future compatible and backward compatible
#### Lumino
- lumino widget
- processLuminoMessage
- Can we rely on the browser APIs?
- before attach, after attach
- onresize, presumably you could shim this to the the browsers resize event w/ ResizeObserver
- Do widgets need to use lumino?
- Not necessarily
- Questions around keeping the API backward compatible + a smaller subset stable API?
- A working group looks at the API, decides what a minimal API looks like for building widgets. Can we build a large portion of the widgets going forward just with this smaller API?
- Two approaches:
- Slimming down the widgets API
- Starting from scratch
- Lumino is going to be around for as long as we use it to build the frontends ; there's less concern than JQuery
- Working group on widget API changes:
- Option 1: removing things (e.g. jquery, Lumino) from the API
- Option 2: reccomending a specific subset of the API that will be long-term maintained
- Option 3: keeping the API the same, and then adding a new API that is long-term maintained
- Etc.
- Dicsussion on the above working group:
- removing things from existing API gets less pressing if we introduce a new minimal subset API that users can rely on
- although one of the things we're loosing is composability of widgets if we just have this minimal API
- How much composition of indivigual outputs really occurs?
- The layout widgets are extremly common
- if different people do it different ways, then there will be libraries that effectively do this widget management
- The hard part of the minimal API is the composability of the widgets:
- But if both widgets are syncing back to Python, then you can still do the side-by-side widgets
- But this is where jslink becomes an issue, but maybe this can be exposed as an API
- How do widgets communicate on the frontend:
- Pete argues that this different frameworks do this differently, and trying to be opinioned on this in a long term contract is a loosing game.
- Don points out that this means that widgets interoperating requires widget authors to opt-in in ways that they don't
- Plan: punt the discussion to this afternoon, and come up with a concrete proposal
- A group of people have a more concrete proposal:
- Here's the API
- Here's how we handle embeding a widget inside another widget
- Here's how they talk to eacother on the front-end / backend
- Here's how a widget would work with the current stack
- Here's how a widget that doesn't work with the current stack would work (and here's how it communicates with the current stack)
#### Webpack
- This is an internal detail of widgets:
- We could benefit from using another technology
- esbuild:
- Does es6 module output
- We put a checkmark on this, and move on. We're not gonna consider this one now
#### AMD modules
- Current cookiecutter, there are currently a federated module for jupyterlab, and then two amd modules (for notebooks and for cdn) that look pretty much the same.
- Options and benefits:
- es6 modules are the modern one, most things are moving to this. The primary benefit is that it's a bit more standarized.
- When loading as an es6 module, async dependencies, this is standardized.
- This is the fully standardized API.
- Potentially prefering this becuase this is the browser standard
- What would a managed transition to es6 look like?
- What steps are the breaking changes, how to make them less painful?
- If we're going to go to the minimal API, then we can leave AMD modules in the old API
- Aka, one option is just adding the es6 as another module to the webpack
- What is the carrot for widget authors? This allows them to load these modules from a CDN (jlab would need to support this?)
- This question is about: how do widget managers pull in the widgets?
- Don, pete agree that the important this is that we all agree on how to load the widgets
- What non-jupyter notebooks do:
- Collab has their own impl of require js that avoids polluting the global namespace
- Microsoft used to have their own require js for .ipynb, but moved to just having standard require js in the webview last week
- Working group for looking into ES6
- Answering two questions:
- What are the benefits, if any?
- Is it feasible to do it?
- Major question around external modules - need some external configuration in the bundler or something
- If there are benefits, and it is feasible, then could consider adding es6 modules as another bundle?
- Grant Nestor had a demo of widget modules as es6 modules
- What would you love to see in widgets?
### Minimal Interactive Outputs API
- Previous brainstorming: https://github.com/Quansight-Labs/jupyter-output-spec
- Colab's stable API: https://github.com/googlecolab/colabtools/blob/main/packages/outputframe/lib/index.d.ts
- How can this be integrated into an improved `%%javascript` magic? https://github.com/ipython/ipython/issues/13376#issuecomment-989040562
- Examples: https://github.com/blois/js-module-renderer/blob/master/tests.ipynb
### Working Groups
#### Minimal API
Attendee: Pete, Vidar, Paul, Jeremy, Jason
An es6 module that exports a render method taking a context argument.
A context has a discovery function and functions for:
- comms
- update output state
-
Colab has a comms api:
- `open("target"): Promise<Comm>`
An example mimebundle:
```json
{
'application/vnd.jupyter.es6-rich-output': 'a string that will be the src of a script tag, i.e., a url, data url, etc.',
'application/vnd.jupyter.datagrid+json': {'data': ['some', 'data', 'in', 'a', 'structure', 'which', 'the', 'js', 'can', 'access']},
'text/html': 'fallback rendering of your data',
'text/plain': 'fallback rendering of your data'
}
```
The mimebundle renderer for `application/vnd.jupyter.es6-rich-output` does a System.import of the url as an object, then calls the render method if it exists with a context object, awaiting the returned `Promise<void>` before moving to the next output.
The context object has mandatory fields:
- `output`: {'data': {...}, 'metadata': {...}} (which is the display_data or execute_result message data/metadata fields). Changes are not respected - treat it as immutable.
- Should the `transient` data also be passed in???
- `element`: HTMLDivElement
Optional fields:
- `render(output, element): Promise<void>`:
- `output`: a kernel iopub stream, display_data, execute_result, error message (i.e., inputs into the rendering system)
- Should these be formatted for nbformat (e.g., transient field is not included)??? Should they include the trusted field that is passed in from JLab???
- `element`: an HTMLDivElement
- Promise resolves when the rendering callback returns
- `comms`: See Colab interface...
- `Comm` interface
- `open("target")`:
- `sharedState`: Map([@@Symbol]: Any) - shared state that might span render calls or contexts. This is considered mutable, in that a renderer can share things with other render calls, replace implementations, etc.
- Let's explore exactly the scope of shared state (e.g., per kernel, per document, etc.). Does there need to be different scopes, or a way to introspect the scopes, etc.???
-
JupyterLab branch for prototype implementation: https://github.com/jupyterlab/jupyterlab/tree/es6-rich-output
#### Traitlet improvements
- Integration with more natural typing
- ?
- Removing the metaclass
- ?
- Performance improvements
https://github.com/ipython/traitlets/pull/788
### Thursday
#### Building larger applications with ipywidgets
- Initial load performance?
- There are hard limits on the
- Server-side rendering
- Doing some things client-side with a wasm runtime, doing what you need on the backend
- Could we statically figure out which is which?
- Performance:
- Users sometimes end up with hundreds to thousands of widgets, pushing memory usage (mostly on Python?)
- Where are the performance bottlenecks with hundreds of widgets?
- Where is the time spent with creating composition lots of widgets?
- How does creating 100 button widgets compare to creating 100 normal html buttons -- what is the overhead?
- Solara:
- Viola slower than and solara on initial load, due to starting a different Python process per
- Potential benchmarks:
- 100 widget buttons vs. 100 HTML buttons
- Where is memory used for hudreds/thousands of widgets that are all visible?
- On scaling application complexity:
- Why do applications get complex?
- No seperation of concerns
- n^2 communication problem
- Callback hell
- There are no state management libraries for Python
- It's also not clear that these Python users are looking for state management solutions
- What are the user profiles who are using ipywidgets?
- Recent college grad: goes to a bank, starts writing Pythonm uses ipywidgets to create an application. Not a software engineer, etc - it grows organically.
- This is an iterative proces. Takes week if not months to work through issues.
- E.g. memory that hangs around, this is really slow. There is no notion of lifecycle, etc.
- More typical quants: they are using Jupyter for more prototyping stuff
- This conversation is very similar to notebook vs. raw Pyhton files. It's very easy to get to a place in your code development where it's extremly hard to figure out what state you have vs. not, and then u.r.done....
- Potential documentation changes:
- Should we have a section on the application page like: you built your first app.
- Perhaps we should think about it as "how can we allow users to just copy and paste and it just works"
- Potentially a clever opinionated way of a way of building applications:
- Sometimes this gets built internally within companies -
- This could be something that we distribute, and allow users to use
- Ideas:
- Class with inheritance?
- Reacton
- Providing a visual programming interface? We could give a GUI builder
- Concerns about degree of customizability
- Doc changes:
- More complex examples of building complex widget applications
- Testing and typing:
- Good practices for testing (although people might not use it)
- Static typing wherever possible
- Working groups:
- Proper state management for
- Perhaps form input, simple output, that you regenerate
- Show an example of building small components that then transitions to composing them together
- Create a `contrib` organization on GitHub, similar to `jupyterlab-contrib`:
- There could be a list of custom widgets like on the `jupyterlab-contrib` website: https://jupyterlab-contrib.github.io/extensions.html
- The [Jovyan](https://github.com/jovyan) github org is also a place for Jupyter community projects. See https://github.com/jupyter/docker-stacks/issues/358 for more info about the name "Jovyan".
- The name `jupyter-widgets-contrib` could make it more explicit about what it is about? And easier to find via search engines. It would also be similar to the existing `jupyterlab-contrib`.
### Friday
#### Contributing
+ Walk-through of fixing a [bug in ipywidgets](https://github.com/jupyter-widgets/ipywidgets/issues/3597).
+ Installation along the way: wrong (old) version of jupyterlab installed when creating conda environment. Known issues, see: https://github.com/mamba-org/mamba/issues/1581
#### Yjs investigations
+ https://github.com/jupyter-widgets/ipywidgets/pull/3195 already helped quite significantly to keep the state synced between the kernel and multiple frontends
+ *Enabled* by default in 8.x: https://github.com/jupyter-widgets/ipywidgets/blob/e3d8fe576334dc84d24ba8b20b68423e9e72ffe7/python/ipywidgets/ipywidgets/widgets/widget.py#L42
+ *Disabled* by default in 7.x: https://github.com/jupyter-widgets/ipywidgets/blob/892166dd0524e93b912d8826a6da26c09afd4366/ipywidgets/widgets/widget.py#L49
+ Looking into using `yjs` / `ypy` for widgets:
+ probably the simplest for now would be to start a completely separate project rather than trying to replace the internals of ipywidgets directly
+ There could be an ephemeral `YDoc` created in the kernel
+ A first step could be to create a new `Websocket` connection between the web clients (JS) and the Python kernel. This would bypass the current contract of message ordering but could be fine for now for testing. There could be a simple JupyterLab extensions exposing a simple widget like the `IntSlider` via a mimerender extension
+ Widgets state are managed in the kernel via the `YDoc`
+ Ideally use a custom websocket provider over the `comms`?