1313
1414import contextlib
1515import os
16+ import sys
1617from unittest import mock
1718
1819import pytest
2728 plotly_available ,
2829 resolve_backend ,
2930)
31+
32+ # Grab the *module* from sys.modules because the ``plotter`` attribute on the
33+ # ``datalab_kernel`` package is shadowed by a global variable (``None`` until
34+ # the kernel starts). ``mock.patch.object`` then patches the correct namespace.
35+ _plotter_mod = sys .modules ["datalab_kernel.plotter" ]
3036from datalab_kernel .tests .data import make_test_signal
3137from datalab_kernel .workspace import Workspace
3238
@@ -68,11 +74,11 @@ def test_auto_prefers_plotly(self):
6874 """Auto-detect prefers plotly when both are available."""
6975 with contextlib .ExitStack () as stack :
7076 stack .enter_context (
71- mock .patch ( "datalab_kernel.plotter. plotly_available" , return_value = True )
77+ mock .patch . object ( _plotter_mod , " plotly_available" , return_value = True )
7278 )
7379 stack .enter_context (
74- mock .patch (
75- "datalab_kernel.plotter. matplotlib_available" , return_value = True
80+ mock .patch . object (
81+ _plotter_mod , " matplotlib_available" , return_value = True
7682 )
7783 )
7884 assert resolve_backend (None ) == BACKEND_PLOTLY
@@ -81,13 +87,11 @@ def test_auto_falls_back_to_matplotlib(self):
8187 """Auto-detect falls back to matplotlib when plotly is missing."""
8288 with contextlib .ExitStack () as stack :
8389 stack .enter_context (
84- mock .patch (
85- "datalab_kernel.plotter.plotly_available" , return_value = False
86- )
90+ mock .patch .object (_plotter_mod , "plotly_available" , return_value = False )
8791 )
8892 stack .enter_context (
89- mock .patch (
90- "datalab_kernel.plotter. matplotlib_available" , return_value = True
93+ mock .patch . object (
94+ _plotter_mod , " matplotlib_available" , return_value = True
9195 )
9296 )
9397 assert resolve_backend (None ) == BACKEND_MATPLOTLIB
@@ -96,11 +100,11 @@ def test_auto_plotly_only(self):
96100 """Auto-detect returns plotly when only plotly is available."""
97101 with contextlib .ExitStack () as stack :
98102 stack .enter_context (
99- mock .patch ( "datalab_kernel.plotter. plotly_available" , return_value = True )
103+ mock .patch . object ( _plotter_mod , " plotly_available" , return_value = True )
100104 )
101105 stack .enter_context (
102- mock .patch (
103- "datalab_kernel.plotter. matplotlib_available" , return_value = False
106+ mock .patch . object (
107+ _plotter_mod , " matplotlib_available" , return_value = False
104108 )
105109 )
106110 assert resolve_backend (None ) == BACKEND_PLOTLY
@@ -109,13 +113,11 @@ def test_neither_raises(self):
109113 """ImportError when neither backend is available."""
110114 with contextlib .ExitStack () as stack :
111115 stack .enter_context (
112- mock .patch (
113- "datalab_kernel.plotter.plotly_available" , return_value = False
114- )
116+ mock .patch .object (_plotter_mod , "plotly_available" , return_value = False )
115117 )
116118 stack .enter_context (
117- mock .patch (
118- "datalab_kernel.plotter. matplotlib_available" , return_value = False
119+ mock .patch . object (
120+ _plotter_mod , " matplotlib_available" , return_value = False
119121 )
120122 )
121123 with pytest .raises (ImportError , match = "Neither plotly nor matplotlib" ):
@@ -125,13 +127,11 @@ def test_fallback_with_warning(self):
125127 """Falls back to the other backend with a warning."""
126128 with contextlib .ExitStack () as stack :
127129 stack .enter_context (
128- mock .patch (
129- "datalab_kernel.plotter.plotly_available" , return_value = False
130- )
130+ mock .patch .object (_plotter_mod , "plotly_available" , return_value = False )
131131 )
132132 stack .enter_context (
133- mock .patch (
134- "datalab_kernel.plotter. matplotlib_available" , return_value = True
133+ mock .patch . object (
134+ _plotter_mod , " matplotlib_available" , return_value = True
135135 )
136136 )
137137 with pytest .warns (UserWarning , match = "not installed.*Falling back" ):
@@ -142,12 +142,12 @@ def test_fallback_other_direction(self):
142142 """Falls back to plotly when matplotlib is requested but missing."""
143143 with contextlib .ExitStack () as stack :
144144 stack .enter_context (
145- mock .patch (
146- "datalab_kernel.plotter. matplotlib_available" , return_value = False
145+ mock .patch . object (
146+ _plotter_mod , " matplotlib_available" , return_value = False
147147 )
148148 )
149149 stack .enter_context (
150- mock .patch ( "datalab_kernel.plotter. plotly_available" , return_value = True )
150+ mock .patch . object ( _plotter_mod , " plotly_available" , return_value = True )
151151 )
152152 with pytest .warns (UserWarning , match = "not installed.*Falling back" ):
153153 result = resolve_backend ("matplotlib" )
@@ -157,13 +157,11 @@ def test_both_missing_with_explicit_raises(self):
157157 """ImportError when explicit backend and fallback are both missing."""
158158 with contextlib .ExitStack () as stack :
159159 stack .enter_context (
160- mock .patch (
161- "datalab_kernel.plotter.plotly_available" , return_value = False
162- )
160+ mock .patch .object (_plotter_mod , "plotly_available" , return_value = False )
163161 )
164162 stack .enter_context (
165- mock .patch (
166- "datalab_kernel.plotter. matplotlib_available" , return_value = False
163+ mock .patch . object (
164+ _plotter_mod , " matplotlib_available" , return_value = False
167165 )
168166 )
169167 with pytest .raises (ImportError , match = "Neither plotly nor matplotlib" ):
0 commit comments