Skip to content

Commit

Permalink
Ensure hvplot.<ext> hooks are run on every import (#1359)
Browse files Browse the repository at this point in the history
Co-authored-by: Simon Høxbro Hansen <simon.hansen@me.com>
Co-authored-by: maximlt <mliquet@anaconda.com>
  • Loading branch information
3 people authored Sep 13, 2024
1 parent 44ed4ed commit cbf8700
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 69 deletions.
30 changes: 28 additions & 2 deletions hvplot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@

import inspect
import os
import sys
import textwrap

import panel as _pn
import holoviews as _hv

from holoviews import Store, render # noqa


from .converter import HoloViewsConverter
from .interactive import Interactive
from .ui import explorer # noqa
Expand Down Expand Up @@ -116,6 +118,28 @@
# the package.
__version__ = '0.0.0+unknown'

# The hvplot.<ext> import mechanism is a convenient way to allow users to have
# to avoid running the holoviews/panel extensions. However since imports are
# cached only the first import actually embeds the extension JS code, meaning
# that if you re-run the cell(s) containing import hvplot.pandas (or some other
# integration) then the JS will no longer be available and on subsequent
# reloads/re-runs of the notebook plots may not appear.
# Here we add an IPython hook which simply deletes the modules before every
# cell execution. This is a big hammer but at least it is restricted to
# IPython environments.
_module_extensions = set()

try:
ip = get_ipython() # noqa

def pre_run_cell(info):
for ext in _module_extensions:
sys.modules.pop(ext, None)

ip.events.register('pre_run_cell', pre_run_cell)
except Exception:
pass

_METHOD_DOCS = {}


Expand Down Expand Up @@ -212,8 +236,10 @@ def help(kind=None, docstring=True, generic=True, style=True):
print(doc)


def post_patch(extension='bokeh', logo=False):
if extension and not getattr(_hv.extension, '_loaded', False):
def post_patch(extension='bokeh', logo=False, check_loaded=False):
if not check_loaded:
hvplot_extension(extension, logo=logo)
elif not getattr(_hv.extension, '_loaded', False):
hvplot_extension(extension, logo=logo)


Expand Down
28 changes: 16 additions & 12 deletions hvplot/cudf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@


def patch(name='hvplot', interactive='interactive', extension='bokeh', logo=False):
from . import hvPlotTabular, post_patch
from . import hvPlotTabular, post_patch, _module_extensions

try:
import cudf
except ImportError:
raise ImportError('Could not patch plotting API onto cuDF. cuDF could not be imported.')
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
plot_prop = property(_patch_plot)
setattr(cudf.DataFrame, name, plot_prop)
setattr(cudf.Series, name, plot_prop)

_patch_interactive = lambda self: Interactive(self) # noqa: E731
_patch_interactive.__doc__ = Interactive.__call__.__doc__
interactive_prop = property(_patch_interactive)
setattr(cudf.DataFrame, interactive, interactive_prop)
setattr(cudf.Series, interactive, interactive_prop)

if 'hvplot.cudf' not in _module_extensions:
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
plot_prop = property(_patch_plot)
setattr(cudf.DataFrame, name, plot_prop)
setattr(cudf.Series, name, plot_prop)

_patch_interactive = lambda self: Interactive(self) # noqa: E731
_patch_interactive.__doc__ = Interactive.__call__.__doc__
interactive_prop = property(_patch_interactive)
setattr(cudf.DataFrame, interactive, interactive_prop)
setattr(cudf.Series, interactive, interactive_prop)

_module_extensions.add('hvplot.cudf')

post_patch(extension, logo)

Expand Down
28 changes: 16 additions & 12 deletions hvplot/dask.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,27 @@ def compute(self):


def patch(name='hvplot', interactive='interactive', extension='bokeh', logo=False):
from . import hvPlotTabular, post_patch
from . import hvPlotTabular, post_patch, _module_extensions

try:
import dask.dataframe as dd
except ImportError:
raise ImportError('Could not patch plotting API onto dask. Dask could not be imported.')
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
plot_prop = property(_patch_plot)
setattr(dd.DataFrame, name, plot_prop)
setattr(dd.Series, name, plot_prop)

_patch_interactive = lambda self: DaskInteractive(self) # noqa: E731
_patch_interactive.__doc__ = DaskInteractive.__call__.__doc__
interactive_prop = property(_patch_interactive)
setattr(dd.DataFrame, interactive, interactive_prop)
setattr(dd.Series, interactive, interactive_prop)

if 'hvplot.dask' not in _module_extensions:
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
plot_prop = property(_patch_plot)
setattr(dd.DataFrame, name, plot_prop)
setattr(dd.Series, name, plot_prop)

_patch_interactive = lambda self: DaskInteractive(self) # noqa: E731
_patch_interactive.__doc__ = DaskInteractive.__call__.__doc__
interactive_prop = property(_patch_interactive)
setattr(dd.DataFrame, interactive, interactive_prop)
setattr(dd.Series, interactive, interactive_prop)

_module_extensions.add('hvplot.dask')

post_patch(extension, logo)

Expand Down
12 changes: 8 additions & 4 deletions hvplot/fugue.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import panel as _pn

from . import hvPlotTabular, post_patch
from . import hvPlotTabular, post_patch, _module_extensions
from .util import _fugue_ipython


Expand Down Expand Up @@ -54,9 +54,13 @@ def process(self, dfs: DataFrames) -> None:

display(col) # in notebook

@parse_outputter.candidate(namespace_candidate(name, lambda x: isinstance(x, str)))
def _parse_hvplot(obj: tuple[str, str]) -> Outputter:
return _Visualize(obj[1])
if 'hvplot.fugue' not in _module_extensions:

@parse_outputter.candidate(namespace_candidate(name, lambda x: isinstance(x, str)))
def _parse_hvplot(obj: tuple[str, str]) -> Outputter:
return _Visualize(obj[1])

_module_extensions.add('hvplot.fugue')

post_patch(extension, logo)

Expand Down
14 changes: 9 additions & 5 deletions hvplot/ibis.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@


def patch(name='hvplot', extension='bokeh', logo=False):
from . import hvPlotTabular, post_patch
from . import hvPlotTabular, post_patch, _module_extensions

try:
import ibis
except ImportError:
raise ImportError('Could not patch plotting API onto ibis. Ibis could not be imported.')
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
patch_property = property(_patch_plot)
setattr(ibis.Expr, name, patch_property)

if 'hvplot.ibis' not in _module_extensions:
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
patch_property = property(_patch_plot)
setattr(ibis.Expr, name, patch_property)

_module_extensions.add('hvplot.ibis')

post_patch(extension, logo)

Expand Down
14 changes: 9 additions & 5 deletions hvplot/intake.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from packaging.version import Version

from . import hvPlot, post_patch
from . import hvPlot, post_patch, _module_extensions


def patch(name='hvplot', extension='bokeh', logo=False):
Expand All @@ -11,10 +11,14 @@ def patch(name='hvplot', extension='bokeh', logo=False):
'Could not patch plotting API onto intake. intake could not be imported.'
)

_patch_plot = lambda self: hvPlot(self) # noqa: E731
_patch_plot.__doc__ = hvPlot.__call__.__doc__
patch_property = property(_patch_plot)
setattr(intake.source.base.DataSource, name, patch_property)
if 'hvplot.intake' not in _module_extensions:
_patch_plot = lambda self: hvPlot(self) # noqa: E731
_patch_plot.__doc__ = hvPlot.__call__.__doc__
patch_property = property(_patch_plot)
setattr(intake.source.base.DataSource, name, patch_property)

_module_extensions.add('hvplot.intake')

post_patch(extension, logo)


Expand Down
27 changes: 15 additions & 12 deletions hvplot/pandas.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,28 @@


def patch(name='hvplot', interactive='interactive', extension='bokeh', logo=False):
from . import hvPlotTabular, post_patch
from . import hvPlotTabular, post_patch, _module_extensions

try:
import pandas as pd
except ImportError:
raise ImportError(
'Could not patch plotting API onto pandas. Pandas could not be imported.'
)
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
plot_prop = property(_patch_plot)
setattr(pd.DataFrame, name, plot_prop)
setattr(pd.Series, name, plot_prop)

_patch_interactive = lambda self: Interactive(self) # noqa: E731
_patch_interactive.__doc__ = Interactive.__call__.__doc__
interactive_prop = property(_patch_interactive)
setattr(pd.DataFrame, interactive, interactive_prop)
setattr(pd.Series, interactive, interactive_prop)

if 'hvplot.pandas' not in _module_extensions:
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
plot_prop = property(_patch_plot)
setattr(pd.DataFrame, name, plot_prop)
setattr(pd.Series, name, plot_prop)

_patch_interactive = lambda self: Interactive(self) # noqa: E731
_patch_interactive.__doc__ = Interactive.__call__.__doc__
interactive_prop = property(_patch_interactive)
setattr(pd.DataFrame, interactive, interactive_prop)
setattr(pd.Series, interactive, interactive_prop)
_module_extensions.add('hvplot.pandas')

post_patch(extension, logo)

Expand Down
10 changes: 6 additions & 4 deletions hvplot/polars.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Adds the `.hvplot` method to pl.DataFrame, pl.LazyFrame and pl.Series"""

from hvplot import post_patch
from hvplot import post_patch, _module_extensions
from hvplot.plotting.core import hvPlotTabularPolars


Expand All @@ -11,9 +11,11 @@ def patch(name='hvplot', extension='bokeh', logo=False):
raise ImportError(
'Could not patch plotting API onto Polars. Polars could not be imported.'
)
pl.api.register_dataframe_namespace(name)(hvPlotTabularPolars)
pl.api.register_series_namespace(name)(hvPlotTabularPolars)
pl.api.register_lazyframe_namespace(name)(hvPlotTabularPolars)
if 'hvplot.polars' not in _module_extensions:
pl.api.register_dataframe_namespace(name)(hvPlotTabularPolars)
pl.api.register_series_namespace(name)(hvPlotTabularPolars)
pl.api.register_lazyframe_namespace(name)(hvPlotTabularPolars)
_module_extensions.add('hvplot.polars')

post_patch(extension, logo)

Expand Down
19 changes: 11 additions & 8 deletions hvplot/streamz.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
def patch(name='hvplot', extension='bokeh', logo=False):
from . import hvPlotTabular, post_patch
from . import hvPlotTabular, post_patch, _module_extensions

try:
import streamz.dataframe as sdf
except ImportError:
raise ImportError(
'Could not patch plotting API onto streamz. Streamz could not be imported.'
)
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
patch_property = property(_patch_plot)
setattr(sdf.DataFrame, name, patch_property)
setattr(sdf.DataFrames, name, patch_property)
setattr(sdf.Series, name, patch_property)
setattr(sdf.Seriess, name, patch_property)
if 'hvplot.streamz' not in _module_extensions:
_patch_plot = lambda self: hvPlotTabular(self) # noqa: E731
_patch_plot.__doc__ = hvPlotTabular.__call__.__doc__
patch_property = property(_patch_plot)
setattr(sdf.DataFrame, name, patch_property)
setattr(sdf.DataFrames, name, patch_property)
setattr(sdf.Series, name, patch_property)
setattr(sdf.Seriess, name, patch_property)

_module_extensions.add('hvplot.streamz')

post_patch(extension, logo)

Expand Down
12 changes: 7 additions & 5 deletions hvplot/xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def isel(self, **kwargs):


def patch(name='hvplot', interactive='interactive', extension='bokeh', logo=False):
from . import hvPlot, post_patch
from . import hvPlot, post_patch, _module_extensions

try:
import xarray as xr
Expand All @@ -51,10 +51,12 @@ def patch(name='hvplot', interactive='interactive', extension='bokeh', logo=Fals
# Remove the class docstring as it very developer focused
XArrayInteractive.__doc__ = ''

xr.register_dataset_accessor(name)(hvPlot)
xr.register_dataarray_accessor(name)(hvPlot)
xr.register_dataset_accessor(interactive)(XArrayInteractive)
xr.register_dataarray_accessor(interactive)(XArrayInteractive)
if 'hvplot.xarray' not in _module_extensions:
xr.register_dataset_accessor(name)(hvPlot)
xr.register_dataarray_accessor(name)(hvPlot)
xr.register_dataset_accessor(interactive)(XArrayInteractive)
xr.register_dataarray_accessor(interactive)(XArrayInteractive)
_module_extensions.add('hvplot.xarray')

post_patch(extension, logo)

Expand Down

0 comments on commit cbf8700

Please sign in to comment.