From 4df01941f19e6c06ac69a8434cf9d99b6463e745 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Wed, 30 Oct 2024 11:36:01 -0700 Subject: [PATCH] refactor into util and test --- hvplot/converter.py | 16 +++++----------- hvplot/tests/testui.py | 4 +++- hvplot/tests/testutil.py | 21 +++++++++++++++++++++ hvplot/ui.py | 14 +++----------- hvplot/util.py | 18 ++++++++++++++++++ 5 files changed, 50 insertions(+), 23 deletions(-) diff --git a/hvplot/converter.py b/hvplot/converter.py index 0f90fc800..f09b237b0 100644 --- a/hvplot/converter.py +++ b/hvplot/converter.py @@ -75,6 +75,7 @@ process_derived_datetime_pandas, _convert_col_names_to_str, import_datashader, + geoviews_is_available, ) from .utilities import hvplot_extension @@ -645,6 +646,10 @@ def __init__( self.dynamic = dynamic self.geo = any([geo, crs, global_extent, projection, project, coastline, features]) + # Try importing geoviews if geo-features requested + if self.geo or self.datatype == 'geopandas': + geoviews_is_available(raise_error=True) + self.crs = self._process_crs(data, crs) if self.geo else None self.output_projection = self.crs self.project = project @@ -654,17 +659,6 @@ def __init__( self.tiles_opts = tiles_opts or {} self.sort_date = sort_date - # Import geoviews if geo-features requested - if self.geo or self.datatype == 'geopandas': - try: - import geoviews # noqa - except ImportError: - raise ImportError( - 'In order to use geo-related features ' - 'the geoviews library must be available. ' - 'It can be installed with:\n conda ' - 'install geoviews' - ) if self.geo: if self.kind not in self._geo_types: param.main.param.warning( diff --git a/hvplot/tests/testui.py b/hvplot/tests/testui.py index 2cd9fb624..68bc46cd4 100644 --- a/hvplot/tests/testui.py +++ b/hvplot/tests/testui.py @@ -1,5 +1,6 @@ import re from textwrap import dedent +from unittest.mock import patch import numpy as np import holoviews as hv @@ -419,4 +420,5 @@ def test_max_rows_sample(): def test_explorer_geo_no_import_error_when_false(): da = ds_air_temperature['air'].isel(time=0) - assert hvplot.explorer(da, x='lon', y='lat', geo=False) + with patch('hvplot.util.geoviews_is_available', return_value=False): + assert hvplot.explorer(da, x='lon', y='lat', geo=False) diff --git a/hvplot/tests/testutil.py b/hvplot/tests/testutil.py index 4f9d39ef8..71dc7c42a 100644 --- a/hvplot/tests/testutil.py +++ b/hvplot/tests/testutil.py @@ -2,6 +2,7 @@ Tests utilities to convert data and projections """ +from unittest.mock import patch import numpy as np import pandas as pd import panel as pn @@ -18,6 +19,7 @@ from hvplot.util import ( check_crs, + geoviews_is_available, is_list_like, process_crs, process_xarray, @@ -383,3 +385,22 @@ def test_is_geodataframe_spatialpandas_dask(): def test_is_geodataframe_classic_dataframe(): df = pd.DataFrame({'geometry': [None, None], 'name': ['A', 'B']}) assert not is_geodataframe(df) + + +@pytest.mark.geo +def test_geoviews_is_available(): + assert geoviews_is_available(raise_error=True) + + +def test_geoviews_is_available_no_raise(): + with patch('hvplot.util.geoviews_is_available', side_effect=ImportError): + result = geoviews_is_available(raise_error=False) + assert result is False + + +def test_geoviews_is_available_with_raise(): + with patch('hvplot.util.geoviews_is_available', side_effect=ImportError): + with pytest.raises( + ImportError, match='GeoViews must be installed to enable the geographic options.' + ): + geoviews_is_available(raise_error=True) diff --git a/hvplot/ui.py b/hvplot/ui.py index 808cb4f95..b6256ecef 100644 --- a/hvplot/ui.py +++ b/hvplot/ui.py @@ -10,7 +10,7 @@ from .converter import HoloViewsConverter as _hvConverter from .plotting import hvPlot as _hvPlot -from .util import is_geodataframe, is_xarray, instantiate_crs_str +from .util import is_geodataframe, is_xarray, instantiate_crs_str, geoviews_is_available # Defaults KINDS = { @@ -361,17 +361,9 @@ class Geographic(Controls): _widgets_kwargs = {'geo': {'type': pn.widgets.Toggle}} def __init__(self, data, **params): - gv_available = False - try: - import geoviews # noqa - - gv_available = True - except ImportError: - pass - geo_params = GEO_KEYS + ['geo'] - if not gv_available and any(params.get(p) for p in geo_params): - raise ImportError('GeoViews must be installed to enable the geographic options.') + gv_available = geoviews_is_available(raise_error=any(params.get(p) for p in geo_params)) + super().__init__(data, **params) if not gv_available: for p in geo_params: diff --git a/hvplot/util.py b/hvplot/util.py index 8a8c1079c..fc426b3c7 100644 --- a/hvplot/util.py +++ b/hvplot/util.py @@ -735,3 +735,21 @@ def relabel_redim(hv_obj, relabel_kwargs, redim_kwargs): if redim_kwargs: hv_obj = hv_obj.redim(**redim_kwargs) return hv_obj + + +def geoviews_is_available(raise_error: bool = False): + """ + Check if GeoViews is available and raise an ImportError if not. + """ + try: + import geoviews # noqa + + gv_available = True + except ImportError: + gv_available = False + + if not raise_error: + return gv_available + + if not gv_available and raise_error: + raise ImportError('GeoViews must be installed to enable the geographic options.')