Repository: holoviz/holoviews
Pull request #6465 (2024-12-05 08:40:08)

ci: Add `COVERAGE_CORE=sysmon` for tests
Better performance, needs feature added in Python 3.12

See https://coverage.readthedocs.io/en/7.6.8/cmd.html#execution-coverage-run

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/holoviz/holoviews/pull/6465?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 88.74%. Comparing base [(`0305b47`)](https://app.codecov.io/gh/holoviz/holoviews/commit/0305b4757abb444b90c38d9d1f961c69c9467329?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz) to head [(`7b16ab2`)](https://app.codecov.io/gh/holoviz/holoviews/commit/7b16ab25dd62ce4e621d6af71892bba74f8712e9?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #6465      +/-   ##
==========================================
+ Coverage   88.50%   88.74%   +0.23%     
==========================================
  Files         323      323              
  Lines       68654    68632      -22     
==========================================
+ Hits        60764    60906     +142     
+ Misses       7890     7726     -164     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/holoviz/holoviews/pull/6465?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=holoviz).


Patch:
diff --git a/examples/gallery/demos/bokeh/multichannel_timeseries_viewer.ipynb b/examples/gallery/demos/bokeh/multichannel_timeseries_viewer.ipynb
index 1aecca40e..bd23decd8 100644
--- a/examples/gallery/demos/bokeh/multichannel_timeseries_viewer.ipynb
+++ b/examples/gallery/demos/bokeh/multichannel_timeseries_viewer.ipynb
@@ -222,7 +222,7 @@
     "    intervalsx=(None, 3),\n",
     ")\n",
     "\n",
-    "dashboard = (normalized_overlay + minimap).cols(1).opts(shared_axes=False)\n",
+    "dashboard = (normalized_overlay + minimap).cols(1)\n",
     "dashboard"
    ]
   }
diff --git a/holoviews/plotting/bokeh/links.py b/holoviews/plotting/bokeh/links.py
index 6fd18a773..ec3172cb2 100644
--- a/holoviews/plotting/bokeh/links.py
+++ b/holoviews/plotting/bokeh/links.py
@@ -162,6 +162,14 @@ class RangeToolLinkCallback(LinkCallback):
             else:
                 target_range_name = range_name
             axes[range_name] = ax = target_plot.handles[target_range_name]
+            if ax is source_plot.handles.get(target_range_name):
+                # Cloning the axis as it does not make sense to have a link
+                # for the same axis
+                new_ax = ax.clone()
+                source_plot.handles[target_range_name] = new_ax
+                setattr(source_plot.handles["plot"], range_name, new_ax)
+                # So it is not re-linked by pn.pane.HoloViews(..., linked_axes=True)
+                new_ax.tags = []
             interval = getattr(link, f'intervals{axis}', None)
             if interval is not None and BOKEH_GE_3_4_0:
                 min, max = interval
diff --git a/pixi.toml b/pixi.toml
index 829855d17..fdcec03da 100644
--- a/pixi.toml
+++ b/pixi.toml
@@ -53,10 +53,16 @@ bokeh_sampledata = "*"
 python = "3.12.*"
 bokeh_sampledata = "*"
 
+[feature.py312.activation.env]
+COVERAGE_CORE = "sysmon"
+
 [feature.py313.dependencies]
 python = "3.13.*"
 bokeh_sampledata = "*"
 
+[feature.py313.activation.env]
+COVERAGE_CORE = "sysmon"
+
 [feature.example.dependencies]
 cftime = "*"
 dask-core = "*"


Test patch:
diff --git a/holoviews/tests/plotting/bokeh/test_links.py b/holoviews/tests/plotting/bokeh/test_links.py
index ed11ac9a4..5b654877e 100644
--- a/holoviews/tests/plotting/bokeh/test_links.py
+++ b/holoviews/tests/plotting/bokeh/test_links.py
@@ -2,6 +2,7 @@ import numpy as np
 import pytest
 from bokeh.models import ColumnDataSource, RangeTool
 
+import holoviews as hv
 from holoviews.core.spaces import DynamicMap
 from holoviews.element import Curve, Image, Path, Points, Polygons, Scatter, Table
 from holoviews.plotting.links import DataLink, Link, RangeToolLink
@@ -215,3 +216,24 @@ class TestLinkCallbacks(TestBokehPlot):
             bokeh_renderer.get_plot(a+b)
         except Exception:
             self.fail()
+
+def test_range_tool_link_clones_axis():
+    x = np.linspace(0, 10, 100)
+    y = np.sin(x)
+    c0 = Curve((x, y)).opts(title="Source")
+    c1 = Curve((x, y)).opts(title="Target")
+
+    bk_grid = hv.render(c0 + c1)
+    bk_plot0 = bk_grid.children[0][0]
+    bk_plot1 = bk_grid.children[1][0]
+    assert bk_plot0.x_range is bk_plot1.x_range
+    assert bk_plot0.y_range is bk_plot1.y_range
+
+    # Will clone the source axis
+    RangeToolLink(c0, c1, axes=["x", "y"])
+
+    bk_grid = hv.render(c0 + c1)
+    bk_plot0 = bk_grid.children[0][0]
+    bk_plot1 = bk_grid.children[1][0]
+    assert bk_plot0.x_range is not bk_plot1.x_range
+    assert bk_plot0.y_range is not bk_plot1.y_range