Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ jobs:
with:
environments: ${{ matrix.environment }}
opengl: true
- name: PATCH # HACK: Should never be merged into main
run: |
pixi run -e ${{ matrix.environment }} bash scripts/no_pandas_bokeh.sh
pixi run -e ${{ matrix.environment }} pip uninstall pandas -y
- name: Test unit
run: |
pixi run -e ${{ matrix.environment }} test-unit $COV
Expand Down
1 change: 1 addition & 0 deletions doc/how_to/custom_components/examples/plot_viewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
```{pyodide}
import param
import panel as pn
import pandas as pd

from bokeh.sampledata.iris import flowers
from panel.viewable import Viewer
Expand Down
52 changes: 52 additions & 0 deletions examples/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,58 @@
]



if find_spec("pandas") is None:
collect_ignore_glob += [ # TODO: Need to be double checked
"gallery/altair_brushing.ipynb",
"gallery/deckgl_game_of_life.ipynb",
"gallery/gapminders.ipynb",
"gallery/glaciers.ipynb",
"gallery/hvplot_explorer.ipynb",
"gallery/iris_kmeans.ipynb",
"gallery/nyc_deckgl.ipynb",
"gallery/penguin_crossfilter.ipynb",
"gallery/penguin_kmeans.ipynb",
"gallery/portfolio_analyzer.ipynb",
"gallery/portfolio_optimizer.ipynb",
"gallery/vtk_interactive.ipynb",
"gallery/vtk_slicer.ipynb",
"gallery/vtk_warp.ipynb",
"gallery/windturbines.ipynb",
"gallery/xgboost_classifier.ipynb",
"reference/chat/ChatMessage.ipynb",
"reference/indicators/Tqdm.ipynb",
"reference/indicators/Trend.ipynb",
"reference/layouts/Swipe.ipynb",
"reference/panes/Bokeh.ipynb",
"reference/panes/DataFrame.ipynb",
"reference/panes/ECharts.ipynb",
"reference/panes/HTML.ipynb",
"reference/panes/HoloViews.ipynb",
"reference/panes/IPyWidget.ipynb",
"reference/panes/Matplotlib.ipynb",
"reference/panes/Param.ipynb",
"reference/panes/Perspective.ipynb",
"reference/panes/Plotly.ipynb",
"reference/panes/ReactiveExpr.ipynb",
"reference/panes/Reacton.ipynb",
"reference/panes/Streamz.ipynb",
"reference/panes/Vizzu.ipynb",
"reference/templates/Bootstrap.ipynb",
"reference/templates/EditableTemplate.ipynb",
"reference/templates/FastGridTemplate.ipynb",
"reference/templates/FastListTemplate.ipynb",
"reference/templates/GoldenLayout.ipynb",
"reference/templates/Material.ipynb",
"reference/templates/React.ipynb",
"reference/templates/Slides.ipynb",
"reference/templates/Vanilla.ipynb",
"reference/widgets/DataFrame.ipynb",
"reference/widgets/FileDownload.ipynb",
"reference/widgets/Tabulator.ipynb",
]


def pytest_runtest_makereport(item, call):
"""
Skip tests that fail because "the kernel died before replying to kernel_info"
Expand Down
4 changes: 2 additions & 2 deletions panel/pane/vega.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ def ds_as_cds(dataset):
"""
Converts Vega dataset into Bokeh ColumnDataSource data
"""
import pandas as pd
if isinstance(dataset, pd.DataFrame):
pd = sys.modules.get("pandas")
if pd and isinstance(dataset, pd.DataFrame):
return {k: dataset[k].values for k in dataset.columns}
if len(dataset) == 0:
return {}
Expand Down
4 changes: 2 additions & 2 deletions panel/pane/vizzu.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def _get_data(self):
return data, {str(k): v for k, v in cols.items()}

def _get_columns(self):
import pandas as pd
pd = sys.modules.get("pandas")

columns = []
for col, array in self._data.items():
Expand All @@ -118,7 +118,7 @@ def _get_columns(self):
value = array[0]
if isinstance(value, dt.date):
columns.append({'name': col, 'type': 'datetime'})
elif isdatetime(value) or isinstance(value, pd.Period):
elif isdatetime(value) or pd and isinstance(value, pd.Period):
columns.append({'name': col, 'type': 'datetime'})
elif isinstance(value, str):
columns.append({'name': col, 'type': 'dimension'})
Expand Down
3 changes: 2 additions & 1 deletion panel/tests/chat/test_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from io import BytesIO
from zoneinfo import ZoneInfo

import pandas as pd
import pytest

from panel import Param, bind
Expand Down Expand Up @@ -388,6 +387,7 @@ def test_serialize_audio(self):
assert message.serialize() == f"Audio='{ASSETS / 'mp3.mp3'}'"

def test_serialize_dataframe(self):
pd = pytest.importorskip("pandas")
message = ChatMessage(DataFrame(pd.DataFrame({'a': [1, 2, 3]})))
assert message.serialize() == "DataFrame= a\n0 1\n1 2\n2 3"

Expand All @@ -396,5 +396,6 @@ def test_repr(self):
assert repr(message) == "ChatMessage(object='Hello', user='User', reactions=['favorite'])"

def test_repr_dataframe(self):
pd = pytest.importorskip("pandas")
message = ChatMessage(pd.DataFrame({'a': [1, 2, 3]}), avatar="D")
assert repr(message) == "ChatMessage(object= a\n0 1\n1 2\n2 3, user='User', reactions=[])"
2 changes: 2 additions & 0 deletions panel/tests/command/test_serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def test_autoreload_app_local_module(py_files):

@linux_only
def test_serve_admin(py_file):
pytest.importorskip("pandas") # TODO: Investigate if we can remove this
app = "import panel as pn; pn.Row('# Example').servable(title='A')"
write_file(app, py_file.file)

Expand All @@ -72,6 +73,7 @@ def test_serve_admin(py_file):

@linux_only
def test_serve_admin_custom_endpoint(py_file):
pytest.importorskip("pandas") # TODO: Investigate if we can remove this
app = "import panel as pn; pn.Row('# Example').servable(title='A')"
write_file(app, py_file.file)

Expand Down
5 changes: 4 additions & 1 deletion panel/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from functools import cache
from subprocess import PIPE, Popen

import pandas as pd
import pytest

from bokeh.client import pull_session
Expand Down Expand Up @@ -272,6 +271,7 @@ def port():

@pytest.fixture
def dataframe():
pd = pytest.importorskip("pandas")
return pd.DataFrame({
'int': [1, 2, 3],
'float': [3.14, 6.28, 9.42],
Expand All @@ -281,6 +281,7 @@ def dataframe():

@pytest.fixture
def df_mixed():
pd = pytest.importorskip("pandas")
df = pd.DataFrame({
'int': [1, 2, 3, 4],
'float': [3.14, 6.28, 9.42, -2.45],
Expand All @@ -294,6 +295,7 @@ def df_mixed():

@pytest.fixture
def df_multiindex(df_mixed):
pd = pytest.importorskip("pandas")
df_mi = df_mixed.copy()
df_mi.index = pd.MultiIndex.from_tuples([
('group0', 'subgroup0'),
Expand Down Expand Up @@ -573,6 +575,7 @@ def eh(exception):

@pytest.fixture
def df_strings():
pd = pytest.importorskip("pandas")
descr = [
'Under the Weather',
'Top Drawer',
Expand Down
34 changes: 24 additions & 10 deletions panel/tests/io/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import time

from collections import Counter
from importlib.util import find_spec
from typing import Any

import numpy as np
import pandas as pd
import param
import pytest
import requests
Expand Down Expand Up @@ -138,6 +139,7 @@ def test_ndarray_hash():
)

def test_dataframe_hash():
pd = pytest.importorskip("pandas")
data = {
"A": [0.0, 1.0, 2.0, 3.0, 4.0],
"B": [0.0, 1.0, 0.0, 1.0, 0.0],
Expand All @@ -150,6 +152,7 @@ def test_dataframe_hash():
assert not hashes_equal(df1, df2)

def test_series_hash():
pd = pytest.importorskip("pandas")
series1 = pd.Series([0.0, 1.0, 2.0, 3.0, 4.0])
series2 = series1.copy()
assert hashes_equal(series1, series2)
Expand Down Expand Up @@ -383,20 +386,31 @@ def expensive_calculation(self, value):

assert model.executions == 2

DF1 = pd.DataFrame({"x": [1]})
DF2 = pd.DataFrame({"y": [1]})

def test_hash_on_simple_dataframes():
assert _generate_hash(DF1)!=_generate_hash(DF2)

@pytest.mark.parametrize(["value", "other", "expected"], [
is_equal_parameterized: list[tuple[Any, Any, bool]] = [
(None, None, True),
(True, False, False), (False, True, False), (False, False, True), (True, True, True),
(None, 1, False), (1, None, False), (1, 1, True), (1,2,False),
(None, "a", False), ("a", None, False), ("a", "a", True), ("a","b",False),
(1,"1", False),
(None, DF1, False), (DF1, None, False), (DF1, DF1, True), (DF1, DF1.copy(), True), (DF1,DF2,False),
])
]

if find_spec("pandas"):
import pandas as pd
DF1 = pd.DataFrame({"x": [1]})
DF2 = pd.DataFrame({"y": [1]})
is_equal_parameterized.extend([
(None, DF1, False,),
(DF1, None, False,),
(DF1, DF1, True,),
(DF1, DF1.copy(), True,),
(DF1, DF2, False,),
])

def test_hash_on_simple_dataframes():
pytest.importorskip("pandas")
assert _generate_hash(DF1)!=_generate_hash(DF2)

@pytest.mark.parametrize(["value", "other", "expected"], is_equal_parameterized)
def test_is_equal(value, other, expected):
assert is_equal(value, other)==expected

Expand Down
9 changes: 7 additions & 2 deletions panel/tests/io/test_location.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pandas as pd
from importlib.util import find_spec

import param
import pytest

Expand Down Expand Up @@ -27,7 +28,8 @@ class SyncParameterized(param.Parameterized):

string = param.String(default=None)

dataframe = param.DataFrame(default=None)
if find_spec("pandas") is not None:
dataframe = param.DataFrame(default=None)


def test_location_update_query(location):
Expand Down Expand Up @@ -170,6 +172,7 @@ def test_iframe_srcdoc_location():

@pytest.fixture
def dataframe():
pd = pytest.importorskip("pandas")
return pd.DataFrame({"x": [1]})

def test_location_sync_from_dataframe(location, dataframe):
Expand All @@ -178,12 +181,14 @@ def test_location_sync_from_dataframe(location, dataframe):
assert location.search == "?dataframe=%5B%7B%22x%22%3A+1%7D%5D"

def test_location_sync_to_dataframe(location, dataframe):
pd = pytest.importorskip("pandas")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we convert these to decorators, i.e.:

pd_available = pytest.mark.skipif(pd is None, reason="requires pandas")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will do this before a merge. For now this is easier to do.

p = SyncParameterized()
location.search = "?dataframe=%5B%7B%22x%22%3A+1%7D%5D"
location.sync(p)
pd.testing.assert_frame_equal(p.dataframe, dataframe)

def test_location_sync_to_dataframe_with_initial_value(location, dataframe):
pd = pytest.importorskip("pandas")
p = SyncParameterized(dataframe=pd.DataFrame({"y": [2]}))
location.search = "?dataframe=%5B%7B%22x%22%3A+1%7D%5D"
location.sync(p)
Expand Down
4 changes: 1 addition & 3 deletions panel/tests/io/test_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
from io import StringIO
from pathlib import Path

import numpy as np

from bokeh.resources import Resources

from panel.config import config
Expand Down Expand Up @@ -66,7 +64,7 @@ def test_save_cdn_resources():
def test_static_path_in_holoviews_save(tmpdir):
import holoviews as hv
hv.Store.set_current_backend('bokeh')
plot = hv.Curve(np.random.seed(42))
plot = hv.Curve([])
res = Resources(mode='server', root_url='/')
out_file = Path(tmpdir) / 'plot.html'
hv.save(plot, out_file, resources=res)
Expand Down
5 changes: 4 additions & 1 deletion panel/tests/pane/test_markup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from unittest.mock import patch

import numpy as np
import pandas as pd
import pytest

from panel import config
Expand All @@ -21,10 +20,12 @@ def test_get_markdown_pane_type():
assert PaneBase.get_pane_type("**Markdown**") is Markdown

def test_get_dataframe_pane_type():
pd = pytest.importorskip("pandas")
df = pd.DataFrame({"A": [1, 2, 3]})
assert PaneBase.get_pane_type(df) is DataFrame

def test_get_series_pane_type():
pd = pytest.importorskip("pandas")
ser = pd.Series([1, 2, 3])
assert PaneBase.get_pane_type(ser) is DataFrame

Expand Down Expand Up @@ -246,6 +247,7 @@ def test_html_pane_sanitize_html(document, comm):
assert model.text.endswith('<h1><strong>HTML</h1></strong>')

def test_dataframe_pane_pandas(document, comm):
pd = pytest.importorskip("pandas")
pane = DataFrame(pd.DataFrame({"A": [1, 2, 3]}))

# Create pane
Expand All @@ -265,6 +267,7 @@ def test_dataframe_pane_pandas(document, comm):
assert pane._models == {}

def test_dataframe_pane_supports_escape(document, comm):
pd = pytest.importorskip("pandas")
url = "<a href='https://panel.holoviz.org/'>Panel</a>"
df = pd.DataFrame({"url": [url]})
pane = DataFrame(df)
Expand Down
4 changes: 3 additions & 1 deletion panel/tests/pane/test_plotly.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
plotly_available = pytest.mark.skipif(plotly is None, reason="requires plotly")

import numpy as np
import pandas as pd

from panel.models.plotly import PlotlyPlot
from panel.pane import PaneBase, Plotly
Expand Down Expand Up @@ -216,6 +215,7 @@ def test_clean_relayout_data():
@pytest.mark.skipif(not find_spec("scipy"), reason="requires scipy")
@plotly_available
def test_plotly_swap_traces(document, comm):
pd = pytest.importorskip("pandas")
data_bar = pd.DataFrame({'Count': [1, 2, 3, 4], 'Category': ["A", "B", "C", "D"]})
data_cts = np.random.randn(1000)

Expand Down Expand Up @@ -248,6 +248,7 @@ def test_plotly_swap_traces(document, comm):
@plotly_available
def test_plotly_shape_datetime_converted(document, comm):
# see https://github.com/holoviz/panel/issues/5252
pd = pytest.importorskip("pandas")
start = pd.Timestamp('2022-05-11 0:00:00', tz=dt.timezone.utc)
date_range = pd.date_range(start=start, periods=20, freq='h')

Expand All @@ -269,6 +270,7 @@ def test_plotly_shape_datetime_converted(document, comm):
@plotly_available
def test_plotly_datetime_converted_2d_array(document, comm):
# see https://github.com/holoviz/panel/issues/7309
pd = pytest.importorskip("pandas")
n_points = 3
data = pd.DataFrame({
'timestamp': pd.date_range(start='2023-01-01', periods=n_points, freq='min'),
Expand Down
Loading
Loading