From 8dea344c646f39aa06ed70f715164abd119871a8 Mon Sep 17 00:00:00 2001 From: Maxime Liquet <35924738+maximlt@users.noreply.github.com> Date: Mon, 6 Feb 2023 21:51:39 +0100 Subject: [PATCH] pandas becomes a (lazy) runtime dependency (#4411) --- panel/tests/conftest.py | 2 +- panel/tests/io/test_cache.py | 6 +----- panel/tests/pane/test_markup.py | 11 ++--------- panel/tests/ui/widgets/test_tabulator.py | 6 +----- panel/tests/util.py | 6 ------ panel/tests/widgets/test_tables.py | 16 +++++----------- panel/tests/widgets/test_tqdm.py | 7 ++----- panel/widgets/tables.py | 20 ++++++++------------ setup.py | 1 + 9 files changed, 21 insertions(+), 54 deletions(-) diff --git a/panel/tests/conftest.py b/panel/tests/conftest.py index ca1d22ef0b..7a2a8f36a6 100644 --- a/panel/tests/conftest.py +++ b/panel/tests/conftest.py @@ -14,6 +14,7 @@ from contextlib import contextmanager from subprocess import PIPE, Popen +import pandas as pd import pytest from bokeh.client import pull_session @@ -160,7 +161,6 @@ def port(): @pytest.fixture def dataframe(): - import pandas as pd return pd.DataFrame({ 'int': [1, 2, 3], 'float': [3.14, 6.28, 9.42], diff --git a/panel/tests/io/test_cache.py b/panel/tests/io/test_cache.py index ccc0fc38bc..1ad0cb48d5 100644 --- a/panel/tests/io/test_cache.py +++ b/panel/tests/io/test_cache.py @@ -4,6 +4,7 @@ import time import numpy as np +import pandas as pd import param import pytest @@ -14,7 +15,6 @@ diskcache_available = pytest.mark.skipif(diskcache is None, reason="requires diskcache") from panel.io.cache import _find_hash_func, cache -from panel.tests.util import pd_available ################ # Test hashing # @@ -125,17 +125,13 @@ def test_ndarray_hash(): np.array([2, 1, 0]) ) -@pd_available def test_dataframe_hash(): - import pandas as pd df1, df2 = pd._testing.makeMixedDataFrame(), pd._testing.makeMixedDataFrame() assert hashes_equal(df1, df2) df2['A'] = df2['A'].values[::-1] assert not hashes_equal(df1, df2) -@pd_available def test_series_hash(): - import pandas as pd series1 = pd._testing.makeStringSeries() series2 = series1.copy() assert hashes_equal(series1, series2) diff --git a/panel/tests/pane/test_markup.py b/panel/tests/pane/test_markup.py index 522a51368a..b8db0f50b4 100644 --- a/panel/tests/pane/test_markup.py +++ b/panel/tests/pane/test_markup.py @@ -2,27 +2,24 @@ import json import numpy as np +import pandas as pd import panel as pn from panel.pane import ( HTML, JSON, DataFrame, Markdown, PaneBase, Str, ) -from panel.tests.util import pd_available, streamz_available +from panel.tests.util import streamz_available def test_get_markdown_pane_type(): assert PaneBase.get_pane_type("**Markdown**") is Markdown -@pd_available def test_get_dataframe_pane_type(): - import pandas as pd df = pd._testing.makeDataFrame() assert PaneBase.get_pane_type(df) is DataFrame -@pd_available def test_get_series_pane_type(): - import pandas as pd df = pd._testing.makeDataFrame() assert PaneBase.get_pane_type(df.iloc[:, 0]) is DataFrame @@ -114,9 +111,7 @@ def test_html_pane(document, comm): assert pane._models == {} -@pd_available def test_dataframe_pane_pandas(document, comm): - import pandas as pd pane = DataFrame(pd._testing.makeDataFrame()) # Create pane @@ -135,9 +130,7 @@ def test_dataframe_pane_pandas(document, comm): pane._cleanup(model) assert pane._models == {} -@pd_available def test_dataframe_pane_supports_escape(document, comm): - import pandas as pd url = "Panel" df = pd.DataFrame({"url": [url]}) pane = DataFrame(df) diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py index 48ce60b965..5fbfcd70f3 100644 --- a/panel/tests/ui/widgets/test_tabulator.py +++ b/panel/tests/ui/widgets/test_tabulator.py @@ -4,6 +4,7 @@ import time import numpy as np +import pandas as pd import param import pytest @@ -21,11 +22,6 @@ pytestmark = pytest.mark.ui -try: - import pandas as pd -except ImportError: - pytestmark = pytest.mark.skip('pandas not available') - from panel import state from panel.depends import bind from panel.io.server import serve diff --git a/panel/tests/util.py b/panel/tests/util.py index 5e709c00e5..ee904c7161 100644 --- a/panel/tests/util.py +++ b/panel/tests/util.py @@ -41,12 +41,6 @@ mpl = None mpl_available = pytest.mark.skipif(mpl is None, reason="requires matplotlib") -try: - import pandas as pd -except Exception: - pd = None -pd_available = pytest.mark.skipif(pd is None, reason="requires pandas") - try: import streamz except Exception: diff --git a/panel/tests/widgets/test_tables.py b/panel/tests/widgets/test_tables.py index ef5aa460f9..647b71f3e4 100644 --- a/panel/tests/widgets/test_tables.py +++ b/panel/tests/widgets/test_tables.py @@ -2,25 +2,19 @@ import time import numpy as np +import pandas as pd import pytest import requests -from packaging.version import Version - -try: - import pandas as pd - - from pandas._testing import ( - makeCustomDataframe, makeMixedDataFrame, makeTimeDataFrame, - ) -except ImportError: - pytestmark = pytest.mark.skip('pandas not available') - from bokeh.models.widgets.tables import ( AvgAggregator, CellEditor, CheckboxEditor, DataCube, DateEditor, DateFormatter, IntEditor, MinAggregator, NumberEditor, NumberFormatter, SelectEditor, StringEditor, StringFormatter, SumAggregator, ) +from packaging.version import Version +from pandas._testing import ( + makeCustomDataframe, makeMixedDataFrame, makeTimeDataFrame, +) from panel.depends import bind from panel.io.server import serve diff --git a/panel/tests/widgets/test_tqdm.py b/panel/tests/widgets/test_tqdm.py index c23e1eb3a1..e7debeb3fa 100644 --- a/panel/tests/widgets/test_tqdm.py +++ b/panel/tests/widgets/test_tqdm.py @@ -1,6 +1,8 @@ """Tests of the Tqdm indicator""" import time +import numpy as np +import pandas as pd import pytest from tqdm.contrib.concurrent import process_map @@ -60,11 +62,6 @@ def test_tqdm_color(): def get_tqdm_app(): - import time - - import numpy as np - import pandas as pd - tqdm = Tqdm(layout="row", sizing_mode="stretch_width") def run(*events): diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py index 1c6463aa94..44479ae8cc 100644 --- a/panel/widgets/tables.py +++ b/panel/widgets/tables.py @@ -7,7 +7,7 @@ from types import FunctionType, MethodType from typing import ( TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Mapping, Optional, - Tuple, Type, TypeVar, + Tuple, Type, ) import numpy as np @@ -37,11 +37,7 @@ from .input import TextInput if TYPE_CHECKING: - try: - import pandas as pd - DataFrameType = pd.DataFrame - except Exception: - DataFrameType = TypeVar('DataFrameType') + import pandas as pd from bokeh.document import Document from bokeh.models.sources import DataDict @@ -155,7 +151,7 @@ def _get_columns(self) -> List[TableColumn]: df = self.value.reset_index() if len(indexes) > 1 else self.value return self._get_column_definitions(fields, df) - def _get_column_definitions(self, col_names: List[str], df: DataFrameType) -> List[TableColumn]: + def _get_column_definitions(self, col_names: List[str], df: pd.DataFrame) -> List[TableColumn]: import pandas as pd indexes = self.indexes columns = [] @@ -326,7 +322,7 @@ def _manual_update( else: self._update_columns(event, model) - def _sort_df(self, df: DataFrameType) -> DataFrameType: + def _sort_df(self, df: pd.DataFrame) -> pd.DataFrame: if not self.sorters: return df fields = [self._renamed_cols.get(s['field'], s['field']) for s in self.sorters] @@ -367,7 +363,7 @@ def tabulator_sorter(col): df_sorted.drop(columns=['_index_'], inplace=True) return df_sorted - def _filter_dataframe(self, df: DataFrameType) -> DataFrameType: + def _filter_dataframe(self, df: pd.DataFrame) -> pd.DataFrame: """ Filter the DataFrame. @@ -555,10 +551,10 @@ def _process_column(self, values): return [str(v) for v in values] return values - def _get_data(self) -> Tuple[DataFrameType, DataDict]: + def _get_data(self) -> Tuple[pd.DataFrame, DataDict]: return self._process_df_and_convert_to_cds(self.value) - def _process_df_and_convert_to_cds(self, df: DataFrameType) -> Tuple[DataFrameType, DataDict]: + def _process_df_and_convert_to_cds(self, df: pd.DataFrame) -> Tuple[pd.DataFrame, DataDict]: import pandas as pd df = self._filter_dataframe(df) if df is None: @@ -1896,7 +1892,7 @@ def on_click(self, callback: Callable[[CellClickEvent], None], column: Optional[ self._on_click_callbacks[column].append(callback) @property - def current_view(self) -> DataFrameType: + def current_view(self) -> pd.DataFrame: """ Returns the current view of the table after filtering and sorting are applied. diff --git a/setup.py b/setup.py index 904b87ddf8..ef85f683cf 100644 --- a/setup.py +++ b/setup.py @@ -113,6 +113,7 @@ def run(self): 'bleach', 'setuptools >=42', 'typing_extensions', + 'pandas >=1.2', ] _recommended = [