From b452c97574829ca3b6ba6a7e4fc3e6941fd037d4 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Thu, 12 Mar 2026 01:38:58 +0530 Subject: [PATCH 01/16] feat: add Polars support with version-locked dependencies --- pyopenms_viz/__init__.py | 2 +- pyopenms_viz/_polars_accessor.py | 25 +++++++++++++++++++++++++ requirements.txt | 2 ++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 pyopenms_viz/_polars_accessor.py diff --git a/pyopenms_viz/__init__.py b/pyopenms_viz/__init__.py index 7d24a4725..17bcfc4fe 100644 --- a/pyopenms_viz/__init__.py +++ b/pyopenms_viz/__init__.py @@ -1,7 +1,7 @@ """ init """ - +from . import _polars_accessor from pandas.plotting._core import PlotAccessor from pandas.core.frame import DataFrame from typing import Any diff --git a/pyopenms_viz/_polars_accessor.py b/pyopenms_viz/_polars_accessor.py new file mode 100644 index 000000000..044f736f5 --- /dev/null +++ b/pyopenms_viz/_polars_accessor.py @@ -0,0 +1,25 @@ +""" +Polars namespace extension for pyopenms_viz. +Provides a thin wrapper to bridge Polars DataFrames with the existing +Pandas-based plotting architecture. +""" +import warnings + +try: + import polars as pl + POLARS_AVAILABLE = True +except ImportError: + POLARS_AVAILABLE = False + +if POLARS_AVAILABLE: + @pl.api.register_dataframe_namespace("ms_plot") + class PolarsPlotAccessor: + """ + Registers the '.ms_plot' namespace for Polars DataFrames. + """ + def __init__(self, df: pl.DataFrame): + self._df = df + + def __call__(self, *args, **kwargs): + pandas_df = self._df.to_pandas() + return pandas_df.plot(*args, **kwargs) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 28e8c040c..a8bbf9c30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -307,3 +307,5 @@ webencodings==0.5.1 # tinycss2 xyzservices==2025.11.0 # via bokeh +polars==1.38.1 +pyarrow==23.0.1 \ No newline at end of file From 75f34fa11b5b6369d55be627be2def215c764b49 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Thu, 12 Mar 2026 02:11:07 +0530 Subject: [PATCH 02/16] chore: address bot feedback on unused imports, arrow conversion, and formatting --- pyopenms_viz/_polars_accessor.py | 8 +++++--- requirements.txt | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/pyopenms_viz/_polars_accessor.py b/pyopenms_viz/_polars_accessor.py index 044f736f5..6aad7e788 100644 --- a/pyopenms_viz/_polars_accessor.py +++ b/pyopenms_viz/_polars_accessor.py @@ -3,8 +3,6 @@ Provides a thin wrapper to bridge Polars DataFrames with the existing Pandas-based plotting architecture. """ -import warnings - try: import polars as pl POLARS_AVAILABLE = True @@ -21,5 +19,9 @@ def __init__(self, df: pl.DataFrame): self._df = df def __call__(self, *args, **kwargs): - pandas_df = self._df.to_pandas() + # Convert to Pandas using Arrow-backed extension arrays for + # better performance and null handling + pandas_df = self._df.to_pandas(use_pyarrow_extension_array=True) + + # Delegate to Pandas' native plot method return pandas_df.plot(*args, **kwargs) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index a8bbf9c30..dda5d3d9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -177,6 +177,8 @@ plotly==5.24.1 # via pyopenms_viz (pyproject.toml) pluggy==1.6.0 # via pytest +polars==1.38.1 + prompt-toolkit==3.0.52 # via ipython psutil==7.2.2 @@ -185,6 +187,8 @@ ptyprocess==0.7.0 # via pexpect pure-eval==0.2.3 # via stack-data +pyarrow==23.0.1 + # via polars pycparser==3.0 # via cffi pydata-sphinx-theme==0.16.1 @@ -215,6 +219,7 @@ pytz==2025.2 # via pandas pyyaml==6.0.3 # via bokeh + pyzmq==27.1.0 # via # ipykernel @@ -307,5 +312,4 @@ webencodings==0.5.1 # tinycss2 xyzservices==2025.11.0 # via bokeh -polars==1.38.1 -pyarrow==23.0.1 \ No newline at end of file + From 55da70d7398144708116265f3a0f31ee71e3cbf3 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Thu, 12 Mar 2026 06:19:01 +0530 Subject: [PATCH 03/16] refactor: implement pyarrow safety catch and internal PlotAccessor routing --- pyopenms_viz/_polars_accessor.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/pyopenms_viz/_polars_accessor.py b/pyopenms_viz/_polars_accessor.py index 6aad7e788..04e17a8d1 100644 --- a/pyopenms_viz/_polars_accessor.py +++ b/pyopenms_viz/_polars_accessor.py @@ -1,7 +1,5 @@ """ Polars namespace extension for pyopenms_viz. -Provides a thin wrapper to bridge Polars DataFrames with the existing -Pandas-based plotting architecture. """ try: import polars as pl @@ -12,16 +10,22 @@ if POLARS_AVAILABLE: @pl.api.register_dataframe_namespace("ms_plot") class PolarsPlotAccessor: - """ - Registers the '.ms_plot' namespace for Polars DataFrames. - """ def __init__(self, df: pl.DataFrame): self._df = df def __call__(self, *args, **kwargs): - # Convert to Pandas using Arrow-backed extension arrays for - # better performance and null handling - pandas_df = self._df.to_pandas(use_pyarrow_extension_array=True) + try: + # 1. Safety Catch: Use Arrow-backed arrays with a clear error fallback + # This prevents cryptic crashes if pyarrow is missing. + pandas_df = self._df.to_pandas(use_pyarrow_extension_array=True) + except (ImportError, ModuleNotFoundError) as exc: + raise RuntimeError( + "Converting a Polars DataFrame to pandas failed because required " + "optional dependencies are missing. Please install 'pyarrow':\n" + " pip install pyarrow" + ) from exc - # Delegate to Pandas' native plot method - return pandas_df.plot(*args, **kwargs) \ No newline at end of file + # 2. Correct Routing: Use the library's internal PlotAccessor. + # This ensures custom backends like 'ms_plotly' are found automatically. + from pyopenms_viz._core import PlotAccessor + return PlotAccessor(pandas_df)(*args, **kwargs) \ No newline at end of file From 7932b36ead9450df08bdffbc0c14ec0a203276c5 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Thu, 12 Mar 2026 06:32:02 +0530 Subject: [PATCH 04/16] test: add pytest coverage for Polars ms_plot accessor --- test/test_polars_accessor.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/test_polars_accessor.py diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py new file mode 100644 index 000000000..7d35db70b --- /dev/null +++ b/test/test_polars_accessor.py @@ -0,0 +1,30 @@ +import pytest + +# Skip this entire test if the user doesn't have Polars or PyArrow installed +pl = pytest.importorskip("polars") +pytest.importorskip("pyarrow") + +import pyopenms_viz + +def test_polars_ms_plot_accessor(): + """ + Test that the ms_plot namespace is properly registered for Polars DataFrames + and successfully delegates to the underlying pandas plotting backend. + """ + # 1. Create a tiny dummy Polars DataFrame + df_pl = pl.DataFrame({ + "m/z": [100.1, 100.2, 105.0, 105.1], + "intensity": [10.0, 50.0, 100.0, 90.0] + }) + + # 2. Call our brand new accessor! + fig = df_pl.ms_plot( + kind="spectrum", + x="m/z", + y="intensity", + backend="ms_plotly" + ) + + # 3. Assert that the delegation worked and returned a valid Figure object + assert fig is not None + assert type(fig).__name__ == "Figure" \ No newline at end of file From d0a60d88b7c8fc39343a47edad82f4d1f9e8dde1 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Thu, 12 Mar 2026 08:33:54 +0530 Subject: [PATCH 05/16] style: fix trailing whitespace and standardise missing dependency exception --- pyopenms_viz/_polars_accessor.py | 11 +++++------ test/test_polars_accessor.py | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pyopenms_viz/_polars_accessor.py b/pyopenms_viz/_polars_accessor.py index 04e17a8d1..a3fb8883c 100644 --- a/pyopenms_viz/_polars_accessor.py +++ b/pyopenms_viz/_polars_accessor.py @@ -16,16 +16,15 @@ def __init__(self, df: pl.DataFrame): def __call__(self, *args, **kwargs): try: # 1. Safety Catch: Use Arrow-backed arrays with a clear error fallback - # This prevents cryptic crashes if pyarrow is missing. pandas_df = self._df.to_pandas(use_pyarrow_extension_array=True) except (ImportError, ModuleNotFoundError) as exc: - raise RuntimeError( + raise ImportError( "Converting a Polars DataFrame to pandas failed because required " "optional dependencies are missing. Please install 'pyarrow':\n" " pip install pyarrow" ) from exc - # 2. Correct Routing: Use the library's internal PlotAccessor. - # This ensures custom backends like 'ms_plotly' are found automatically. - from pyopenms_viz._core import PlotAccessor - return PlotAccessor(pandas_df)(*args, **kwargs) \ No newline at end of file + # 2. Delegate to pandas' plotting API. + # This lets pandas resolve ms_* backends via entry points and respect + # the global `pd.options.plotting.backend` setting. + return pandas_df.plot(*args, **kwargs) \ No newline at end of file diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 7d35db70b..1c2fd42f7 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -4,7 +4,7 @@ pl = pytest.importorskip("polars") pytest.importorskip("pyarrow") -import pyopenms_viz +import pyopenms_viz def test_polars_ms_plot_accessor(): """ From 12dfd3464a54f75b8ac8ed00b05c5790e613e70f Mon Sep 17 00:00:00 2001 From: Sanjith1013 Date: Thu, 12 Mar 2026 06:58:05 +0530 Subject: [PATCH 06/16] Update test/test_polars_accessor.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- test/test_polars_accessor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 1c2fd42f7..05eb0766e 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -22,7 +22,7 @@ def test_polars_ms_plot_accessor(): kind="spectrum", x="m/z", y="intensity", - backend="ms_plotly" + backend="plotly" ) # 3. Assert that the delegation worked and returned a valid Figure object From a65adcdbcfa07f0a64b63f3c369cb8e008f0f327 Mon Sep 17 00:00:00 2001 From: Sanjith1013 Date: Thu, 12 Mar 2026 08:20:01 +0530 Subject: [PATCH 07/16] Update test/test_polars_accessor.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test/test_polars_accessor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 05eb0766e..1c2fd42f7 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -22,7 +22,7 @@ def test_polars_ms_plot_accessor(): kind="spectrum", x="m/z", y="intensity", - backend="plotly" + backend="ms_plotly" ) # 3. Assert that the delegation worked and returned a valid Figure object From 866d0198466137eceda410be11d8bbcda00e9f67 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Thu, 12 Mar 2026 21:56:38 +0530 Subject: [PATCH 08/16] test: add snapshot test comparing polars and pandas plotly output --- README.md | 13 ++++++++++++ test/test_polars_accessor.py | 40 +++++++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 6afda21ed..af26eee66 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,19 @@ pyOpenMS-Viz is a Python library that provides a simple interface for extending | PeakMap 3D | x, y, z | peakmap (plot3d=True) | ✓ | | ✓ | +### Note on Polars DataFrames +`pyopenms_viz` fully supports [Polars](https://pola.rs/) DataFrames via an Arrow-backed zero-copy conversion. However, due to API differences, you must use the `.ms_plot()` namespace instead of `.plot()` when working with Polars. + +**Pandas:** +```python +df_pandas.plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly") +``` + +**Polars:** +```python +df_polars.ms_plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly") +``` + ## (Recommended) Pip Installation The recommended way of installing pyopenms_viz is through the Python Package Index (PyPI). We recommend installing pyopenms_viz in its own virtual environment using Anaconda to avoid packaging conflicts. diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 1c2fd42f7..41dbb55b5 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -1,6 +1,7 @@ import pytest +import pandas as pd -# Skip this entire test if the user doesn't have Polars or PyArrow installed +# Skip these tests if the user doesn't have Polars or PyArrow installed pl = pytest.importorskip("polars") pytest.importorskip("pyarrow") @@ -11,20 +12,39 @@ def test_polars_ms_plot_accessor(): Test that the ms_plot namespace is properly registered for Polars DataFrames and successfully delegates to the underlying pandas plotting backend. """ - # 1. Create a tiny dummy Polars DataFrame df_pl = pl.DataFrame({ "m/z": [100.1, 100.2, 105.0, 105.1], "intensity": [10.0, 50.0, 100.0, 90.0] }) - - # 2. Call our brand new accessor! + fig = df_pl.ms_plot( - kind="spectrum", - x="m/z", - y="intensity", + kind="spectrum", + x="m/z", + y="intensity", backend="ms_plotly" ) - - # 3. Assert that the delegation worked and returned a valid Figure object + assert fig is not None - assert type(fig).__name__ == "Figure" \ No newline at end of file + assert type(fig).__name__ == "Figure" + +def test_polars_vs_pandas_snapshot(): + """ + Test that Polars ms_plot() and Pandas plot() generate the exact same + underlying Plotly figure data (snapshot test). + """ + data = { + "m/z": [100.1, 100.2, 105.0, 105.1], + "intensity": [10.0, 50.0, 100.0, 90.0] + } + + # Create identical DataFrames + df_pd = pd.DataFrame(data) + df_pl = pl.DataFrame(data) + + # Generate figures + fig_pd = df_pd.plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly") + fig_pl = df_pl.ms_plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly") + + # Compare the underlying JSON structure of the figures. + # This guarantees the Polars delegation yields 100% identical Plotly output. + assert fig_pd.to_json() == fig_pl.to_json() From d73cc2a6d509e75cc496934b87f388660f492233 Mon Sep 17 00:00:00 2001 From: Sanjith1013 Date: Thu, 12 Mar 2026 22:07:17 +0530 Subject: [PATCH 09/16] Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af26eee66..8c0651f09 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ pyOpenMS-Viz is a Python library that provides a simple interface for extending ### Note on Polars DataFrames -`pyopenms_viz` fully supports [Polars](https://pola.rs/) DataFrames via an Arrow-backed zero-copy conversion. However, due to API differences, you must use the `.ms_plot()` namespace instead of `.plot()` when working with Polars. +`pyopenms_viz` can work with [Polars](https://pola.rs/) DataFrames by internally converting them to pandas, often via an Arrow-backed representation where possible. This conversion may involve data copies depending on data types and configuration, and plotting is ultimately delegated to the pandas-based plotting backends. Due to API differences, you must use the `.ms_plot()` namespace instead of `.plot()` when working with Polars. **Pandas:** ```python From 6936efcf5858fc534b27ac8afa46b1b0bca2b0ff Mon Sep 17 00:00:00 2001 From: Sanjith1013 Date: Thu, 12 Mar 2026 22:07:37 +0530 Subject: [PATCH 10/16] Update test/test_polars_accessor.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test/test_polars_accessor.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 41dbb55b5..7d2a22c27 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -7,6 +7,14 @@ import pyopenms_viz +@pytest.fixture(autouse=True) +def load_backend(): + """ + Override the parametrized autouse `load_backend` fixture from `conftest.py` + so that tests in this module run only once. These tests explicitly pass + backend="ms_plotly", so no additional setup is required here. + """ + yield def test_polars_ms_plot_accessor(): """ Test that the ms_plot namespace is properly registered for Polars DataFrames From 0b79e211ad334c005fa497be9d4b6af7f527b225 Mon Sep 17 00:00:00 2001 From: Sanjith1013 Date: Thu, 12 Mar 2026 22:07:52 +0530 Subject: [PATCH 11/16] Update pyopenms_viz/__init__.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pyopenms_viz/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyopenms_viz/__init__.py b/pyopenms_viz/__init__.py index 17bcfc4fe..eb33fb4ba 100644 --- a/pyopenms_viz/__init__.py +++ b/pyopenms_viz/__init__.py @@ -1,7 +1,7 @@ """ init """ -from . import _polars_accessor +from . import _polars_accessor # Import for side effects: registers pandas accessors. noqa: F401 from pandas.plotting._core import PlotAccessor from pandas.core.frame import DataFrame from typing import Any From 67a628b6eaec912b74ed97595c6c0544a6d83b31 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Thu, 12 Mar 2026 22:12:08 +0530 Subject: [PATCH 12/16] test: implement bot suggestions to fix windows CI hang and json serialization --- test/test_polars_accessor.py | 38 ++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 7d2a22c27..541e94c4f 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -1,5 +1,6 @@ import pytest import pandas as pd +import json # Skip these tests if the user doesn't have Polars or PyArrow installed pl = pytest.importorskip("polars") @@ -12,9 +13,10 @@ def load_backend(): """ Override the parametrized autouse `load_backend` fixture from `conftest.py` so that tests in this module run only once. These tests explicitly pass - backend="ms_plotly", so no additional setup is required here. + backend="ms_plotly", so no additional setup is required. """ yield + def test_polars_ms_plot_accessor(): """ Test that the ms_plot namespace is properly registered for Polars DataFrames @@ -24,14 +26,15 @@ def test_polars_ms_plot_accessor(): "m/z": [100.1, 100.2, 105.0, 105.1], "intensity": [10.0, 50.0, 100.0, 90.0] }) - + fig = df_pl.ms_plot( - kind="spectrum", - x="m/z", - y="intensity", - backend="ms_plotly" + kind="spectrum", + x="m/z", + y="intensity", + backend="ms_plotly", + show_plot=False ) - + assert fig is not None assert type(fig).__name__ == "Figure" @@ -44,15 +47,16 @@ def test_polars_vs_pandas_snapshot(): "m/z": [100.1, 100.2, 105.0, 105.1], "intensity": [10.0, 50.0, 100.0, 90.0] } - - # Create identical DataFrames + df_pd = pd.DataFrame(data) df_pl = pl.DataFrame(data) - - # Generate figures - fig_pd = df_pd.plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly") - fig_pl = df_pl.ms_plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly") - - # Compare the underlying JSON structure of the figures. - # This guarantees the Polars delegation yields 100% identical Plotly output. - assert fig_pd.to_json() == fig_pl.to_json() + + fig_pd = df_pd.plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly", show_plot=False) + fig_pl = df_pl.ms_plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly", show_plot=False) + + # Parse the JSON strings into Python dictionaries to avoid strict string-ordering issues + dict_pd = json.loads(fig_pd.to_json()) + dict_pl = json.loads(fig_pl.to_json()) + + # Compare the core data arrays to guarantee the delegation plotted the exact same values + assert dict_pd["data"] == dict_pl["data"] \ No newline at end of file From 9b78e6ba4963f5dd7b326076a08b75e82c10e259 Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Fri, 13 Mar 2026 14:12:49 +0530 Subject: [PATCH 13/16] test: migrate polars plotting tests to use syrupy snapshot framework --- .../test_polars_ms_plot_snapshot.json | 1090 +++++++++++++++++ test/test_polars_accessor.py | 32 +- 2 files changed, 1109 insertions(+), 13 deletions(-) create mode 100644 test/__snapshots__/test_polars_accessor/test_polars_ms_plot_snapshot.json diff --git a/test/__snapshots__/test_polars_accessor/test_polars_ms_plot_snapshot.json b/test/__snapshots__/test_polars_accessor/test_polars_ms_plot_snapshot.json new file mode 100644 index 000000000..5408197e5 --- /dev/null +++ b/test/__snapshots__/test_polars_accessor/test_polars_ms_plot_snapshot.json @@ -0,0 +1,1090 @@ +{ + "data": [ + { + "line": { + "color": "#4575B4" + }, + "mode": "lines", + "x": [ + 100.1, + 100.1, + 100.1, + 100.2, + 100.2, + 100.2, + 105.0, + 105.0, + 105.0, + 105.1, + 105.1, + 105.1 + ], + "y": [ + 0.0, + 10.0, + 0.0, + 0.0, + 50.0, + 0.0, + 0.0, + 100.0, + 0.0, + 0.0, + 90.0, + 0.0 + ], + "type": "scatter", + "customdata": [ + [ + 100.1, + 10.0 + ], + [ + 100.1, + 10.0 + ], + [ + 100.1, + 10.0 + ], + [ + 100.2, + 50.0 + ], + [ + 100.2, + 50.0 + ], + [ + 100.2, + 50.0 + ], + [ + 105.0, + 100.0 + ], + [ + 105.0, + 100.0 + ], + [ + 105.0, + 100.0 + ], + [ + 105.1, + 90.0 + ], + [ + 105.1, + 90.0 + ], + [ + 105.1, + 90.0 + ] + ], + "hovertemplate": "m\u002fz: %{customdata[0]}\u003cbr\u003eintensity: %{customdata[1]}" + } + ], + "layout": { + "template": { + "data": { + "barpolar": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "bar": [ + { + "error_x": { + "color": "rgb(36,36,36)" + }, + "error_y": { + "color": "rgb(36,36,36)" + }, + "marker": { + "line": { + "color": "white", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "rgb(36,36,36)", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "rgb(36,36,36)" + }, + "baxis": { + "endlinecolor": "rgb(36,36,36)", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "rgb(36,36,36)" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "type": "choropleth" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "type": "contourcarpet" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "contour" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "heatmapgl" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "heatmap" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "histogram2dcontour" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "histogram2d" + } + ], + "histogram": [ + { + "marker": { + "line": { + "color": "white", + "width": 0.6 + } + }, + "type": "histogram" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatterpolargl" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatterpolar" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + }, + "colorscale": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "rgb(237,237,237)" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "rgb(217,217,217)" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 1, + "tickcolor": "rgb(36,36,36)", + "ticks": "outside" + } + }, + "colorscale": { + "diverging": [ + [ + 0.0, + "rgb(103,0,31)" + ], + [ + 0.1, + "rgb(178,24,43)" + ], + [ + 0.2, + "rgb(214,96,77)" + ], + [ + 0.3, + "rgb(244,165,130)" + ], + [ + 0.4, + "rgb(253,219,199)" + ], + [ + 0.5, + "rgb(247,247,247)" + ], + [ + 0.6, + "rgb(209,229,240)" + ], + [ + 0.7, + "rgb(146,197,222)" + ], + [ + 0.8, + "rgb(67,147,195)" + ], + [ + 0.9, + "rgb(33,102,172)" + ], + [ + 1.0, + "rgb(5,48,97)" + ] + ], + "sequential": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ], + "sequentialminus": [ + [ + 0.0, + "#440154" + ], + [ + 0.1111111111111111, + "#482878" + ], + [ + 0.2222222222222222, + "#3e4989" + ], + [ + 0.3333333333333333, + "#31688e" + ], + [ + 0.4444444444444444, + "#26828e" + ], + [ + 0.5555555555555556, + "#1f9e89" + ], + [ + 0.6666666666666666, + "#35b779" + ], + [ + 0.7777777777777778, + "#6ece58" + ], + [ + 0.8888888888888888, + "#b5de2b" + ], + [ + 1.0, + "#fde725" + ] + ] + }, + "colorway": [ + "#1F77B4", + "#FF7F0E", + "#2CA02C", + "#D62728", + "#9467BD", + "#8C564B", + "#E377C2", + "#7F7F7F", + "#BCBD22", + "#17BECF" + ], + "font": { + "color": "rgb(36,36,36)" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "white", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "white", + "polar": { + "angularaxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + }, + "bgcolor": "white", + "radialaxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "white", + "gridcolor": "rgb(232,232,232)", + "gridwidth": 2, + "linecolor": "rgb(36,36,36)", + "showbackground": true, + "showgrid": false, + "showline": true, + "ticks": "outside", + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + }, + "yaxis": { + "backgroundcolor": "white", + "gridcolor": "rgb(232,232,232)", + "gridwidth": 2, + "linecolor": "rgb(36,36,36)", + "showbackground": true, + "showgrid": false, + "showline": true, + "ticks": "outside", + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + }, + "zaxis": { + "backgroundcolor": "white", + "gridcolor": "rgb(232,232,232)", + "gridwidth": 2, + "linecolor": "rgb(36,36,36)", + "showbackground": true, + "showgrid": false, + "showline": true, + "ticks": "outside", + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + } + }, + "shapedefaults": { + "fillcolor": "black", + "line": { + "width": 0 + }, + "opacity": 0.3 + }, + "ternary": { + "aaxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + }, + "baxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + }, + "bgcolor": "white", + "caxis": { + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside", + "title": { + "standoff": 15 + }, + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + }, + "yaxis": { + "automargin": true, + "gridcolor": "rgb(232,232,232)", + "linecolor": "rgb(36,36,36)", + "showgrid": false, + "showline": true, + "ticks": "outside", + "title": { + "standoff": 15 + }, + "zeroline": false, + "zerolinecolor": "rgb(36,36,36)" + } + } + }, + "title": { + "text": "Mass Spectrum", + "font": { + "family": "Helvetica", + "size": 18 + } + }, + "xaxis": { + "title": { + "text": "mass-to-charge", + "font": { + "family": "Helvetica", + "size": 16 + } + }, + "tickfont": { + "size": 14, + "family": "Helvetica" + }, + "gridcolor": "#CCCCCC", + "showgrid": true, + "showline": true, + "linewidth": 1, + "linecolor": "black", + "ticks": "outside", + "tickwidth": 1, + "tickcolor": "black", + "range": [ + 80.08, + 126.11999999999999 + ] + }, + "yaxis": { + "title": { + "text": "Intensity", + "font": { + "family": "Helvetica", + "size": 16 + } + }, + "tickfont": { + "size": 14, + "family": "Helvetica" + }, + "gridcolor": "#CCCCCC", + "showgrid": true, + "showline": true, + "linewidth": 1, + "linecolor": "black", + "tickwidth": 1, + "tickcolor": "black", + "range": [ + 0, + 115.0 + ] + }, + "width": 500, + "height": 500, + "dragmode": "select", + "legend": { + "font": { + "size": 10, + "family": "Helvetica" + }, + "title": { + "text": "Trace" + } + }, + "showlegend": true, + "font": { + "family": "Helvetica" + }, + "plot_bgcolor": "#FFFFFF", + "annotations": [ + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "105.0", + "x": 105.0, + "xanchor": "left", + "y": 100.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "105.1", + "x": 105.1, + "xanchor": "left", + "y": 90.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "100.2", + "x": 100.2, + "xanchor": "left", + "y": 50.0 + }, + { + "font": { + "color": "black", + "family": "Open Sans Mono, monospace", + "size": 12 + }, + "showarrow": false, + "text": "100.1", + "x": 100.1, + "xanchor": "left", + "y": 10.0 + } + ], + "shapes": [ + { + "line": { + "color": "#EEEEEE", + "width": 2 + }, + "opacity": 1, + "type": "line", + "x0": 0, + "x1": 1, + "xref": "x domain", + "y0": 0, + "y1": 0, + "yref": "y" + } + ] + } +} \ No newline at end of file diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 541e94c4f..13125b46f 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -1,7 +1,8 @@ import pytest import pandas as pd import json - +if not str(pd.options.plotting.backend).startswith("ms_"): + pd.options.plotting.backend = "ms_plotly" # Skip these tests if the user doesn't have Polars or PyArrow installed pl = pytest.importorskip("polars") pytest.importorskip("pyarrow") @@ -38,25 +39,30 @@ def test_polars_ms_plot_accessor(): assert fig is not None assert type(fig).__name__ == "Figure" -def test_polars_vs_pandas_snapshot(): +def test_polars_ms_plot_snapshot(snapshot): """ - Test that Polars ms_plot() and Pandas plot() generate the exact same - underlying Plotly figure data (snapshot test). + Test that Polars ms_plot() generates a stable figure + using the syrupy snapshot testing framework. """ data = { "m/z": [100.1, 100.2, 105.0, 105.1], "intensity": [10.0, 50.0, 100.0, 90.0] } - df_pd = pd.DataFrame(data) df_pl = pl.DataFrame(data) - fig_pd = df_pd.plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly", show_plot=False) - fig_pl = df_pl.ms_plot(kind="spectrum", x="m/z", y="intensity", backend="ms_plotly", show_plot=False) - - # Parse the JSON strings into Python dictionaries to avoid strict string-ordering issues - dict_pd = json.loads(fig_pd.to_json()) - dict_pl = json.loads(fig_pl.to_json()) + # Generate the figure using the Polars accessor + out = df_pl.ms_plot( + kind="spectrum", + x="m/z", + y="intensity", + show_plot=False + ) - # Compare the core data arrays to guarantee the delegation plotted the exact same values - assert dict_pd["data"] == dict_pl["data"] \ No newline at end of file + # Apply tight layout to matplotlib to ensure it is not cut off (matches repo standards) + if pd.options.plotting.backend == "ms_matplotlib": + fig = out.get_figure() + fig.tight_layout() + + # Let syrupy handle the serialization and comparison + assert snapshot == out \ No newline at end of file From 703131d64b72cc67b791bac0c04505d9ee9101bc Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Fri, 13 Mar 2026 14:43:44 +0530 Subject: [PATCH 14/16] test: fix conftest setup error by scoping backend state to fixture --- test/test_polars_accessor.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/test/test_polars_accessor.py b/test/test_polars_accessor.py index 13125b46f..d8d290fc0 100644 --- a/test/test_polars_accessor.py +++ b/test/test_polars_accessor.py @@ -1,22 +1,23 @@ import pytest import pandas as pd -import json -if not str(pd.options.plotting.backend).startswith("ms_"): - pd.options.plotting.backend = "ms_plotly" + # Skip these tests if the user doesn't have Polars or PyArrow installed pl = pytest.importorskip("polars") pytest.importorskip("pyarrow") - -import pyopenms_viz - @pytest.fixture(autouse=True) def load_backend(): """ - Override the parametrized autouse `load_backend` fixture from `conftest.py` - so that tests in this module run only once. These tests explicitly pass - backend="ms_plotly", so no additional setup is required. + Safely override the global backend so the syrupy snapshot fixture + in conftest.py knows which extension to load, then reset it + to avoid leaking state to other tests. """ + original_backend = pd.options.plotting.backend + pd.options.plotting.backend = "ms_plotly" + yield + + # Teardown: restore the original state + pd.options.plotting.backend = original_backend def test_polars_ms_plot_accessor(): """ @@ -41,7 +42,7 @@ def test_polars_ms_plot_accessor(): def test_polars_ms_plot_snapshot(snapshot): """ - Test that Polars ms_plot() generates a stable figure + Test that Polars ms_plot() generates a stable Plotly figure using the syrupy snapshot testing framework. """ data = { @@ -56,13 +57,9 @@ def test_polars_ms_plot_snapshot(snapshot): kind="spectrum", x="m/z", y="intensity", + backend="ms_plotly", # Explicitly set backend to avoid global state leaks show_plot=False ) - - # Apply tight layout to matplotlib to ensure it is not cut off (matches repo standards) - if pd.options.plotting.backend == "ms_matplotlib": - fig = out.get_figure() - fig.tight_layout() # Let syrupy handle the serialization and comparison assert snapshot == out \ No newline at end of file From 01dd8768e04d6a545fa12dcf79fe9c84a7f2dbea Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Fri, 27 Mar 2026 01:21:47 +0530 Subject: [PATCH 15/16] docs: use consistent variable names in Polars guide --- docs/Getting Started.ipynb | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/Getting Started.ipynb b/docs/Getting Started.ipynb index 1fb03be3b..78692c703 100644 --- a/docs/Getting Started.ipynb +++ b/docs/Getting Started.ipynb @@ -3227,6 +3227,40 @@ " x_kind='chromatogram', # specify on x marginal axis to plot as a chromatogram rather than a spectrum \n", " grid=False) " ] + }, + { + "cell_type": "markdown", + "id": "4b971b57", + "metadata": {}, + "source": [ + "Using Polars DataFrames\n", + "\n", + "pyopenms_viz is deeply optimized for Pandas. While you can use Polars for high-speed data manipulation, downstream plotting libraries often force silent C-API memory materialization when handling PyArrow extension arrays.\n", + "\n", + "To maintain a strictly native, memory-safe footprint for large datasets, the recommended approach is to process your data in Polars, and then explicitly convert it to Pandas before calling the .ms_plot accessor." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12fa9e5a", + "metadata": {}, + "outputs": [], + "source": [ + "import polars as pl\n", + "\n", + "# Convert the existing 'spectrum_df' to Polars\n", + "df_polars = pl.from_pandas(spectrum_df)\n", + "\n", + "# ... perform your high-speed Polars operations here ...\n", + "\n", + "# Explicitly convert back to Pandas for memory-safe rendering\n", + "df_safe = df_polars.to_pandas()\n", + "\n", + "# Plot using the native pyopenms_viz plotting method\n", + "# (Using the 'mz' and 'intensity' columns from this notebook)\n", + "df_safe.plot(kind='spectrum', x='mz', y='intensity', backend='ms_bokeh')" + ] } ], "metadata": { From 03190d06cf9c623cb23a4dfbdac3f50b6e76dcce Mon Sep 17 00:00:00 2001 From: "Sanjith Badri K." Date: Tue, 7 Apr 2026 11:29:33 +0530 Subject: [PATCH 16/16] docs: fix broken Binder links Switch Sphinx Gallery Binder branch target from gh_pages to main. Fixes #94 --- .gitignore | 4 ++++ docs/conf.py | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7ad008943..07431f992 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,7 @@ docs/generated docs/gallery_scripts docs/_build .DS_Store +venv/ +.venv/ +_build/ +tmpclaude-c0db-cwd diff --git a/docs/conf.py b/docs/conf.py index c6ca73fd4..9f591e5b5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -312,7 +312,8 @@ def bokeh_scraper(block, block_vars, gallery_conf, **kwargs): "binder": { "org": "OpenMS", "repo": "pyopenms_viz", - "branch": "gh_pages", + # Use the source branch for Binder launches; gh_pages is a deploy artifact branch. + "branch": "main", "binderhub_url": "https://notebooks.gesis.org/binder", "dependencies": "./requirements.txt", },