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

feat: Jupyter comms server #145

Merged
merged 18 commits into from
Feb 11, 2025
Merged

feat: Jupyter comms server #145

merged 18 commits into from
Feb 11, 2025

Conversation

manzt
Copy link
Member

@manzt manzt commented Feb 16, 2024

src/higlass/widget.js Outdated Show resolved Hide resolved
@manzt
Copy link
Member Author

manzt commented Feb 16, 2024

Notebooks test code with a widget to collect the Tilesets. A collection of tilesets (should be weakrefable), that handles fulfilling requests from the front end.

See examples/JupyterServer.ipynb for the current Tilesets implementation. I think there are some API decisions that need to be made there, how to transparently setup the server. Ideally this feature feature would likely replace the HTTP server, instead of adding another option. Maintaining both would be really challenging.

@manzt
Copy link
Member Author

manzt commented Jul 21, 2024

Relevant PR that benefits from a similar kind of architecture (and ability to drop jupyter-server-proxy):

@manzt manzt force-pushed the manzt/jupyter-comms branch from 5bb8393 to 4c8689b Compare February 6, 2025 18:46
@manzt
Copy link
Member Author

manzt commented Feb 6, 2025

@nvictus I added a notebook which should be a good starting point for trying this stuff out:

uv run --with=clodius jupyter lab examples/JupyterServer.ipynb
# make sure to wget the test data

deno.json Outdated Show resolved Hide resolved
@@ -0,0 +1,76 @@
// HiGlass's plugin system goes through globals
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HiGlass doesn't provide any types. I've tried to type out the interfaces that we require so we can appropriately type-check the file and also help with autocomplete, etc.

/**
* Transforms the original view config into tracks recognized by the custom data fetcher.
*
* Finds tracks with `server: 'jupyter'`, removes the key, and adds a `data` object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still true?

I don't see this in the example notebook.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Namely I don't see the track's server being set to Jupyter in https://github.com/higlass/higlass-python/blob/manzt/jupyter-comms/examples/JupyterServer.ipynb

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The viewconf I see that's produced contains the following data section.

            "data": {
              "type": "IPY_MODEL_d3b1fccd786440b8a1b9b47bda3cd168",
              "tilesetUid": "abf80f79ec3e9e899d168f869f624aaf"
            },

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, nvm. Ignore that internal monologue. The track does indeed have jupyter as the server:

HeatmapTrack(tilesetUid='abf80f79ec3e9e899d168f869f624aaf', server='jupyter', type='heatmap', uid='8c8c58d9', width=None, height=None, options=None, data=None, position=None, transforms=None)

* @param {{ timeout?: number }} [options]
* @returns {Promise<{ data: T, buffers: DataView[] }>}
*/
function send(model, payload, { timeout = 3000 } = {}) {
Copy link
Member

@pkerpedjiev pkerpedjiev Feb 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I fully grok what this function does or why it's necessary.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two ways to send data between JS and Python: traitlets & custom messages.

Custom messages are normally "fire and forget", so we need some kind of mechanism to fire and "await" a response back from Python. I've implemented an experimental API for this in anywidget, trying to make this pattern easier... but the implementation is pretty straigtht forward and included it here to avoid relying on an experimental API.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I documented the function further to explain this behavior.


# readonly properties
location = t.List(t.Union([t.Float(), t.Tuple()]), read_only=True).tag(sync=True)

def __init__(self, viewconf: dict, **viewer_options):
super().__init__(_viewconf=viewconf, _options=viewer_options)
def __init__(self, viewconf: dict, ts, **viewer_options):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the ts param supposed to be here?

I get that it's the Tilesets object from the example notebook but I'm having a bit of trouble wrapping my head around what it's meant to be conceptually.

" self.ts[ts.tileset.uid] = ts.tileset\n",
" return self\n",
"\n",
" def _handle_custom_msg(self, data, buffers):\n",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is _handle_custom_msg a standard DomWidget method?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We need to add a handler to receive messages from the frontend. It could be named anything. I extracted into a separate function that we now bind in the constructor.

@manzt manzt force-pushed the manzt/jupyter-comms branch 3 times, most recently from cd897ab to 4e3238e Compare February 11, 2025 17:33
@manzt
Copy link
Member Author

manzt commented Feb 11, 2025

This is in a good state now. I refactored the Python code out of the notebook into the codebase.

The top-level tileset functions (e.g., hg.cooler) now return a TrackHelper and register a tileset with a global TilesetRegistry, which the widget queries to resolve tileset_info and tiles requests.

A single TilesetClient is shared across all widgets, simplifying the front end by requiring only one custom data fetcher.

The main open question is handling the old server implementation. I'd prefer to drop HiGlassServer entirely and fully adopt anywidget, avoiding the complexity of maintaining both an HTTP and Jupyter-based backend. Users who need HTTP-based routing can still use hg.remote and spin up their own higlass-server if necessary.

@manzt manzt force-pushed the manzt/jupyter-comms branch 2 times, most recently from 790080b to a409b60 Compare February 11, 2025 20:54
@manzt manzt force-pushed the manzt/jupyter-comms branch from c30cf95 to 265a19c Compare February 11, 2025 21:04
@manzt manzt merged commit b1d8fb4 into main Feb 11, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants