mckinsey / vizro

Vizro is a toolkit for creating modular data visualization applications.
https://vizro.readthedocs.io/en/stable/
Apache License 2.0
2.61k stars 116 forks source link

How do I get Vizro to run in a Google Colab Notebook? #175

Open gbabeleda opened 10 months ago

gbabeleda commented 10 months ago

Question

Im using a Google Colab Notebook due to my need to access a BigQuery instance in which I dont have access to other than via login with a google account.

Im not so sure how to make it work. Using the following snippet doesnt work as expected

Vizro().build(dashboard=dashboard).run()

Code/Examples

No response

Other information

No response

vizro version

No response

Python version

No response

OS

No response

Code of Conduct

antonymilne commented 10 months ago

Hello @gbabeleda and thank you for raising the issue. What exactly happens when you run that code? Do you get any error messages or anything else?

Please could you try running this code at the beginning of the notebook also and see what happens?

from dash import jupyter_dash

jupyter_dash.infer_jupyter_proxy_config()
gbabeleda commented 10 months ago

Hello @antonymilne! Thank you for responding.

I ran this in a code cell

from dash import jupyter_dash

jupyter_dash.infer_jupyter_proxy_config()

Followed by this

dashboard = vm.Dashboard(
    pages=[
        landing_page,
        financials_page,
        user_page,
        marketing_funnel_page,
        product_page
    ]
)

Vizro().build(dashboard=dashboard).run()

It results in this error:


LookupError Traceback (most recent call last) Cell In[37], line 11 1 dashboard = vm.Dashboard( 2 pages=[ 3 landing_page, (...) 8 ] 9 ) ---> 11 Vizro().build(dashboard=dashboard).run()

File /usr/local/lib/python3.10/dist-packages/vizro/_vizro.py:62, in Vizro.build(self, dashboard) 53 """Builds the dashboard. 54 55 Args: (...) 59 Vizro: App object 60 """ 61 # Note that model instantiation and pre_build are independent of Dash. ---> 62 self._pre_build() 64 self.dash.layout = dashboard.build() 66 return self

File /usr/local/lib/python3.10/dist-packages/vizro/_vizro.py:91, in Vizro._pre_build() 89 model = model_manager[model_id] 90 if hasattr(model, "pre_build"): ---> 91 model.pre_build()

File /usr/local/lib/python3.10/dist-packages/vizro/models/_models_utils.py:16, in _log_call.._wrapper(self, *args, kwargs) 13 @wraps(method) 14 def _wrapper(self, *args, *kwargs): 15 # We need to run method before logging so that @_log_call works for init. ---> 16 return_value = method(self, args, kwargs) 17 logger.debug("Running %s.%s for model with id %s", self.class.name, method.name, self.id) 18 return return_value

File /usr/local/lib/python3.10/dist-packages/vizro/models/_dashboard.py:77, in Dashboard.pre_build(self) 75 for order, page in enumerate(self.pages): 76 path = page.path if order else "/" ---> 77 dash.register_page( 78 module=page.id, name=page.title, path=path, order=order, layout=partial(self._make_page_layout, page) 79 ) 80 dash.register_page(module=MODULE_PAGE_404, layout=self._make_page_404_layout())

File /usr/local/lib/python3.10/dist-packages/dash/_pages.py:307, in register_page(module, path, path_template, name, order, title, description, image, image_url, redirect_from, layout, kwargs) 174 def register_page( 175 module, 176 path=None, (...) 186 kwargs, 187 ): 188 """ 189 Assigns the variables to dash.page_registry as an OrderedDict 190 (ordered by order). (...) 305 ``` 306 """ --> 307 if context_value.get().get("ignore_register_page"): 308 return 310 _validate.validate_use_pages(CONFIG)

LookupError: <ContextVar name='callback_context' at 0x797eb5a812b0>

antonymilne commented 10 months ago

Interesting, thank you for sharing! Two more things to try out just to debug a bit further:

  1. If you restart your Jupyter kernel and do the above, do you get the same error?
  2. Please could you try running a pure Dash app (with no Vizro) and see what happens? The following example should do it:
from dash import Dash, html, dcc
import plotly.express as px

df = px.data.tips()

app = Dash(__name__)
app.layout = html.Div([
    html.H1('Test App'),
    html.Div([
        dcc.Graph(id='#1 dist', figure=px.histogram(df, x="total_bill")),
        dcc.Graph(id='#2 dist', figure=px.histogram(df, x="tip")),
        dcc.Graph(id='#3 dist', figure=px.histogram(df, x="size"))
    ]),
])
app.run()
antonymilne commented 7 months ago

With many thanks to a user inside QB who helped us troubleshoot this, I think we have made some progress on this.

There seem to be two possible solutions:

  1. ensure that your Vizro() is in the same cell as the build(dashboard) (note that run() can be done in a separate cell if you prefer)
  2. upgrade your version of ipykernel with pip install -U ipykernel (can do inside Jupyter notebook with %pip)

Under the hood, the problem is not Vizro explicitly but rather the use of contextvars in dash.register_page inside a Jupyter notebook. A MWE example here is:

import dash

app = dash.Dash(use_pages=True, pages_folder="")
dash.register_page("x")

If you execute this with the register_page call split into a separate cell, you'll get a LookupError with older version of ipykernel (e.g. ipykernel==5.5.6). Putting it all into one cell or using ipykernel==6.29.0 works fine. See also https://github.com/googlecolab/colabtools/issues/3212.

Possibly this is another reason that Dash Pages officially doesn't work in Jupyter, but I have yet to fully get to the bottom of that. So far it seems easy to fix anyway. See also https://github.com/mckinsey/vizro/issues/109.

lingyielia commented 2 weeks ago

Additional learnings for these 2 working solutions (summarized by @antonymilne above):

  1. Putting all Vizro and Dash code in one cell help avoid error when trigger the notebook first time. In situations when the Vizro/Dash related cell being rerun, the Lookup error is back. To solve the error when rerun the same cell, _callback_context needs reset.

    import dash._callback_context
    from vizro_ai import VizroAI
    from vizro import Vizro
    
    print("Running VizroAI...")
    dash._callback_context.context_value.set({})
    Vizro._reset()
    
    vizro_ai = VizroAI(model=LLM)
    res = vizro_ai._dashboard(dfs, user_input.value, return_elements=True)
    
    vizro_dashboard = Vizro().build(res.dashboard)
  2. Newer version like ipykernel==6.29.0 works fine. As Colab team intentionally don't upgrade dependencies often, to use newer version, the user needs to uninstall and reinstall ipykernel

    %pip uninstall ipykernel -y -q -q -q
    %pip install -U ipykernel -q -q -q

    Then click Runtime -> Restart session to have the new version ipykernel reflected