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

gr.Plot() injected multiple Bokeh js scripts, breaking Bokeh.Models #5449

Closed
1 task done
relubwu opened this issue Sep 7, 2023 · 8 comments · Fixed by #5795
Closed
1 task done

gr.Plot() injected multiple Bokeh js scripts, breaking Bokeh.Models #5449

relubwu opened this issue Sep 7, 2023 · 8 comments · Fixed by #5795
Assignees
Labels
bug Something isn't working svelte Frontend-related issue (JS)

Comments

@relubwu
Copy link

relubwu commented Sep 7, 2023

Describe the bug

When gr.Plot() is called more than once, load_bokeh() in Plot.svelte would inject Bokeh dependencies multiple times to the frontend document. This causes some conflict on the Bokeh side and subsequently causes 66 models missing from Bokeh.Models._known_models

Have you searched existing issues? 🔎

  • I have searched and found no existing issues

Reproduction

  1. Create a simple gradio app:
def cb():
    return DataTable()

with gr.Blocks() as app: 
    btn = gr.Button()
    plt = gr.Plot()
    btn.click(cb, [], plt)

app.launch()
  1. This snippet works fine. If you check Bokeh.Models._known_models on the frontend, you'll see 366 models loaded.
  2. Now, add another gr.Plot():
with gr.Blocks() as app: 
    btn = gr.Button()
    plt = gr.Plot()
    gr.Plot()
    btn.click(cb, [], plt)

app.launch()

The first plot component would collapse due to Error: could not resolve type 'DataTable', which could be due to a widget or a custom model not being registered before first usage. Now Bokeh.Models._known_models only shows 300 models loaded, missing 66.

  1. Upon closer inspection, you would find bokeh js scripts tags appear multiple times in the frontend document:
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-api-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-api-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-gl-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-api-3.2.2.min.js"></script>
<script src="https://cdn.bokeh.org/bokeh/release/bokeh-3.2.2.min.js"></script>
  1. The function call responsible for injecting these script tags is load_bokeh and load_plugins in js/plot/static/Plot.svelte

Screenshot

No response

Logs

No response

System Info

gradio version: 3.42.0
gradio_client version: 0.5.0

Severity

Blocking usage of gradio (at least for me :( )

@relubwu relubwu added the bug Something isn't working label Sep 7, 2023
@relubwu
Copy link
Author

relubwu commented Sep 7, 2023

Ugly fix:

function load_bokeh(): HTMLScriptElement {
    let script = document.getElementById('gr-bokeh'); 
    if (script !== null) return script;
    script = document.createElement("script");  
    ...
    script.id = 'gr-bokeh'; 
    return script; 
}

and similarly for load_plugins()

@abidlabs abidlabs added this to the Component Cleanup milestone Sep 7, 2023
@abidlabs abidlabs self-assigned this Sep 28, 2023
@abidlabs abidlabs added good first issue Good for newcomers svelte Frontend-related issue (JS) hacktoberfest labels Oct 2, 2023
@abidlabs
Copy link
Member

abidlabs commented Oct 3, 2023

Hi @relubwu I can't reproduce this issue. In your repro, DataTable() is not defined.

I tried creating my own repro by modifying demo/bokeh_plot/run.py to add a second plot:

import gradio as gr
import xyzservices.providers as xyz
from bokeh.tile_providers import get_provider
from bokeh.models import ColumnDataSource, Whisker
from bokeh.plotting import figure
from bokeh.sampledata.autompg2 import autompg2 as df
from bokeh.sampledata.penguins import data
from bokeh.transform import factor_cmap, jitter, factor_mark


def get_plot(plot_type):
    if plot_type == "map":
        tile_provider = get_provider(xyz.OpenStreetMap.Mapnik)
        plot = figure(
            x_range=(-2000000, 6000000),
            y_range=(-1000000, 7000000),
            x_axis_type="mercator",
            y_axis_type="mercator",
        )
        plot.add_tile(tile_provider)
        return plot
    elif plot_type == "whisker":
        classes = list(sorted(df["class"].unique()))

        p = figure(
            height=400,
            x_range=classes,
            background_fill_color="#efefef",
            title="Car class vs HWY mpg with quintile ranges",
        )
        p.xgrid.grid_line_color = None

        g = df.groupby("class")
        upper = g.hwy.quantile(0.80)
        lower = g.hwy.quantile(0.20)
        source = ColumnDataSource(data=dict(base=classes, upper=upper, lower=lower))

        error = Whisker(
            base="base",
            upper="upper",
            lower="lower",
            source=source,
            level="annotation",
            line_width=2,
        )
        error.upper_head.size = 20
        error.lower_head.size = 20
        p.add_layout(error)

        p.circle(
            jitter("class", 0.3, range=p.x_range),
            "hwy",
            source=df,
            alpha=0.5,
            size=13,
            line_color="white",
            color=factor_cmap("class", "Light6", classes),
        )
        return p
    elif plot_type == "scatter":

        SPECIES = sorted(data.species.unique())
        MARKERS = ["hex", "circle_x", "triangle"]

        p = figure(title="Penguin size", background_fill_color="#fafafa")
        p.xaxis.axis_label = "Flipper Length (mm)"
        p.yaxis.axis_label = "Body Mass (g)"

        p.scatter(
            "flipper_length_mm",
            "body_mass_g",
            source=data,
            legend_group="species",
            fill_alpha=0.4,
            size=12,
            marker=factor_mark("species", MARKERS, SPECIES),
            color=factor_cmap("species", "Category10_3", SPECIES),
        )

        p.legend.location = "top_left"
        p.legend.title = "Species"
        return p

with gr.Blocks() as demo:
    with gr.Row():
        plot_type = gr.Radio(value="scatter", choices=["scatter", "whisker", "map"])
        plot = gr.Plot()
        plot2 = gr.Plot()
    plot_type.change(get_plot, inputs=[plot_type], outputs=[plot])
    plot_type.change(get_plot, inputs=[plot_type], outputs=[plot2])
    demo.load(get_plot, inputs=[plot_type], outputs=[plot])


if __name__ == "__main__":
    demo.launch()

but it works fine. Can you advise?

@abidlabs abidlabs added needs repro Awaiting full reproduction and removed good first issue Good for newcomers hacktoberfest labels Oct 3, 2023
@relubwu
Copy link
Author

relubwu commented Oct 3, 2023

Hi @abidlabs , DataTable is part of Bokeh's components, see link

I tried repro the issue with the code you provided in a clean Colab notebook, and the issue persists.

Steps:

  1. Run your code
  2. Open inspector console, Bokeh.Models._known_models is of size 300, instead of 366. DataTable and other components are missing.
  3. In inspector, DOM head section, multiple copies of Bokeh scripts are injected:
image

Please let me know if this helps clarify the issue!

@freddyaboulton
Copy link
Collaborator

Might be a bug in this line. Bokeh should in theory only be loaded once:

if (!_bokeh_loaded) {

@abidlabs abidlabs removed the needs repro Awaiting full reproduction label Oct 4, 2023
@relubwu
Copy link
Author

relubwu commented Oct 4, 2023

Hi @freddyaboulton , great catch! I tried playing around with this flag a bit, it seems not updated in time when Bokeh stuff was injected.

@abidlabs
Copy link
Member

abidlabs commented Oct 4, 2023

Yup that's what I'm seeing as well

@abidlabs
Copy link
Member

abidlabs commented Oct 4, 2023

Here's a PR with the fix @relubwu: #5795

Can you test to see if it fixes it for you? You can install gradio from this branch with:

pip install https://gradio-builds.s3.amazonaws.com/a8c0ef02b71a253db81ddb6bcf07c1467cb8778a/gradio-3.46.1-py3-none-any.whl

@relubwu
Copy link
Author

relubwu commented Oct 4, 2023

Hey @abidlabs ! Can confirm fix works:
image
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working svelte Frontend-related issue (JS)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants