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

Expose URL transformation logic for JLab #2800

Closed
DavidJVitale opened this issue Feb 27, 2020 · 13 comments
Closed

Expose URL transformation logic for JLab #2800

DavidJVitale opened this issue Feb 27, 2020 · 13 comments
Assignees
Labels
Custom Widget Issues related to using ipywidgets as a framework for custom widgets
Milestone

Comments

@DavidJVitale
Copy link

DavidJVitale commented Feb 27, 2020

Hello, I am the author of a custom ipywidget. I recently added in functionality that passes a relative image URL from Python -> JS, and display that image in the widget view.

Let's say I have Classic Notebook Server running. I have a notebook open at /some/path/example.ipynb, and I have an image /some/path/smiley.jpg. I pass the relative image path of "./smiley.jpg" from Python -> JS, and the image is able to display correctly in an HTML element there.

This same workflow fails in JupyterLab, because of how JupyterLab is structured and how it assembles URLs. Here is an example of how JupyterLab changes the ./smiley.jpg URL to http://localhost:8889/files/some/path/smiley.jpg?_xsrf=<somerandomstring>

jlab_image_path_construction

It would be great if this functionality was exposed from an ipywidget perspective, so there was a unified programming interface for Classic Notebook Server and JupyterLab. For now, a workaround I will do I will probably implement separate logic for notebooks and jupyterlab, using jupyterlab services and the contentsmanager API.

cc @timkpaine and @jasongrout , we discussed this on gitter

@jasongrout
Copy link
Member

We've thought about exposing some sort of url transformation function from the widget manager, which should be responsible for all environment-specific aspects of the widget. In the jupyterlab widget manager, it would tap into the system url transformation. For the notebook, it would likely be the (default) identity transform.

This has also come up when displaying html in ipywidgets vs using the default jupyterlab html display. JupyterLab automatically converts local links to work in JupyterLab, but the html widget can't. Providing some sort of url transformation mechanism would allow the html widget to be able to do a similar transformation.

@jasongrout jasongrout added this to the 8.0 milestone Feb 27, 2020
@jasongrout
Copy link
Member

To be more specific, what I think would be good to try is:

  1. Introduce a new function in the widget manager that takes in a path and returns a transformed path, perhaps inspired by the IResolver in JupyterLab https://github.com/jupyterlab/jupyterlab/blob/f0153e0258b32674c9aec106383ddf7b618cebab/packages/rendermime-interfaces/src/index.ts#L376
  2. The JupyterLab manager taps into the rendermime registry's resolver to implement these functions. It has access to the rendermime registry, and thus the resolver, at
    this._rendermime = rendermime;
  3. The classic notebook manager does whatever is appropriate there (probably just the identity function)

@jasongrout
Copy link
Member

As a hacky kludgy workaround for now, if a widget is being hosted by a jupyterlab widget manager, it can access the ._rendermime attribute itself to do the resolution. But I'd highly encourage anyone wanting to do that to work on a PR to do it "right" in the public interface of the widget manager.

@snickell
Copy link

snickell commented Apr 21, 2020

Thanks @jasongrout , it looks like I'm doing it the kludge way today 🙈

If anyone is wondering how to access/use the rendermime attribute inside an iypwidgets WidgetView in order to wield this hacky kludge, this example might help them:

import { DOMWidgetView } from '@jupyter-widgets/base'

export class YourWidget extends DOMWidgetView {
  async getDownloadUrlFor(fsPath) {
    const resolver = this.model.widget_manager.rendermime.resolver
    // First we resolve fsPath in case its relative
    const resolvedPath = await resolver.resolveUrl(fsPath)
    // We return a a 'http://{labinstance}/files/___' URL
    return await resolver.getDownloadUrl(resolvedPath)
  }
}

@davidbrochart
Copy link
Member

I was looking into it, but don't we have it already?

/**
* Resolve a URL relative to the current notebook location.
*/
async resolveUrl(url: string): Promise<string> {
const partial = await this.context.urlResolver.resolveUrl(url);
return this.context.urlResolver.getDownloadUrl(partial);
}

@jasongrout
Copy link
Member

Huh, that looks right! Can you use git blame (or the blame view in GitHub) to track down the pr that added that for reference?

@davidbrochart
Copy link
Member

It looks like it started with #1993, and was improved in #2003 and #2043.

@jasongrout
Copy link
Member

Thanks!

@jasongrout
Copy link
Member

Actually, it looks like what is still needed is moving that functionality up to the base manager, so any widget can use it without knowing if it is in JLab, etc.

@jasongrout jasongrout reopened this Feb 18, 2021
@davidbrochart
Copy link
Member

I thought it was how it's working already: because this functionality is in the resolveUrl method of JLab's WidgetManager, it will be called if the widget is in JLab, and if in the classic Notebook the resolveUrl method in ManagerBase is just the identity function.
Am I missing something?

@snickell
Copy link

@davidbrochart / @jasongrout if this is already fixed, its probably "too hard to find" haha, and perhaps a couple lines of code for future people running into the issue might actually weirdly be a high impact documentation fix.

I'll note that in general, I love how awesome a lot of the jupyterlab ecosystem github issues are in terms of completeness BUT, I find SO many issues closed where a one liner by whoever fixed it in the issue showing how to USE the fix would save every developer trying to understand the issue like...... hours

@snickell
Copy link

That is: people find issues when figuring out how to do things, please please please note HOW the fix works when closing issues, it takes the fixer 5 minutes to save everyone else feeling stupid for 3 hrs reading patches and trying to figure out WHAT the fix is

@ianhi ianhi added the Custom Widget Issues related to using ipywidgets as a framework for custom widgets label Apr 1, 2021
@vidartf vidartf assigned vidartf and unassigned jasongrout and SylvainCorlay Aug 31, 2021
@vidartf
Copy link
Member

vidartf commented Sep 2, 2021

This method is already exposed on the widget manager interface. Here in the master branch:

/**
* Resolve a URL relative to the current notebook location.
*
* The default implementation just returns the original url.
*/
resolveUrl(url: string): Promise<string>;

And on the 7.x branch (since 7.2 it looks like), the base manager class also defines it:

/**
* Resolve a URL relative to the current notebook location.
*
* The default implementation just returns the original url.
*/
resolveUrl(url: string): Promise<string> {
return Promise.resolve(url);
}

To use it in a widget method, you can call:

let resolvedUrl = this.widget_manager.resolveUrl("./smiley.jpg");

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Custom Widget Issues related to using ipywidgets as a framework for custom widgets
Projects
None yet
Development

No branches or pull requests

7 participants