Skip to content

Commit 9d30c42

Browse files
author
lindakladivova
committed
handling better when jupyter is not installed or is launched on Windows where jupyter is not part of the build process yet
1 parent 0b6f8ab commit 9d30c42

File tree

5 files changed

+169
-15
lines changed

5 files changed

+169
-15
lines changed
925 Bytes
Loading
Lines changed: 101 additions & 0 deletions
Loading

gui/wxpython/lmgr/toolbars.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"""
2525

2626
from core.gcmd import RunCommand
27+
from grass.workflows.server import is_jupyter_installed
2728
from gui_core.toolbars import BaseToolbar, AuiToolbar, BaseIcons
2829
from icons.icon import MetaIcon
2930

@@ -212,11 +213,25 @@ def _toolbarData(self):
212213
img="python", label=_("Open a simple Python code editor")
213214
),
214215
"jupyter": MetaIcon(img="jupyter", label=_("Start Jupyter Notebook")),
216+
"jupyter-inactive": MetaIcon(
217+
img="jupyter-inactive",
218+
label=_(
219+
"Start Jupyter Notebook - requires Jupyter Notebook, click for more info"
220+
),
221+
),
215222
"script-load": MetaIcon(
216223
img="script-load", label=_("Launch user-defined script")
217224
),
218225
}
219226

227+
# Decide if Jupyter is available
228+
if is_jupyter_installed():
229+
jupyter_icon = icons["jupyter"]
230+
jupyter_handler = self.parent.OnJupyterNotebook
231+
else:
232+
jupyter_icon = icons["jupyter-inactive"]
233+
jupyter_handler = self.parent.OnShowJupyterInfo
234+
220235
return self._getToolbarData(
221236
(
222237
(
@@ -252,9 +267,9 @@ def _toolbarData(self):
252267
self.parent.OnSimpleEditor,
253268
),
254269
(
255-
("jupyter", icons["jupyter"].label),
256-
icons["jupyter"],
257-
self.parent.OnJupyterNotebook,
270+
("jupyter", jupyter_icon.label),
271+
jupyter_icon,
272+
jupyter_handler,
258273
),
259274
(
260275
("script-load", icons["script-load"].label),

gui/wxpython/main_window/frame.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,26 @@ def OnJupyterNotebook(self, event=None, cmd=None):
919919
# add map display panel to notebook and make it current
920920
self.mainnotebook.AddPage(jupyter_panel, _("Jupyter Notebook"))
921921

922+
def OnShowJupyterInfo(self, event=None):
923+
"""Show information dialog when Jupyter Notebook is not installed."""
924+
if sys.platform.startswith("win"):
925+
message = _(
926+
"Jupyter Notebook is currently not included in the Windows GRASS build process.\n"
927+
"This feature will be available in a future release."
928+
)
929+
else:
930+
message = _(
931+
"To use notebooks in GRASS, you need to have the Jupyter Notebook "
932+
"package installed. After the installation, please restart GRASS to enable this feature."
933+
)
934+
935+
wx.MessageBox(
936+
message=message,
937+
caption=_("Jupyter Notebook not available"),
938+
style=wx.OK | wx.ICON_INFORMATION,
939+
parent=self,
940+
)
941+
922942
def OnPsMap(self, event=None, cmd=None):
923943
"""Launch Cartographic Composer. See OnIClass documentation"""
924944
from psmap.frame import PsMapFrame

python/grass/workflows/server.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
This module provides two classes for managing Jupyter Notebook servers
1515
programmatically within GRASS GIS tools or scripting environments:
1616
17+
Functions:
18+
- `is_jupyter_installed()`: Check if Jupyter Notebook is installed on the system.
1719
Classes:
1820
- `JupyterServerInstance`: Manages a single Jupyter Notebook server instance.
1921
- `JupyterServerRegistry`: Manages multiple `JupyterServerInstance` objects
@@ -51,6 +53,33 @@
5153
import shutil
5254

5355

56+
def is_jupyter_installed():
57+
"""Check if Jupyter Notebook is installed.
58+
59+
- On Linux/macOS: returns True if the presence command succeeds, False otherwise.
60+
- On Windows: currently always returns False because Jupyter is
61+
not bundled.
62+
TODO: Once Jupyter becomes part of the Windows build
63+
process, this method should simply return True without additional checks.
64+
65+
:return: True if Jupyter Notebook is installed and available, False otherwise.
66+
"""
67+
if sys.platform.startswith("win"):
68+
# For now, always disabled on Windows
69+
return False
70+
71+
try:
72+
result = subprocess.run(
73+
["jupyter", "notebook", "--version"],
74+
stdout=subprocess.DEVNULL,
75+
stderr=subprocess.DEVNULL,
76+
check=True,
77+
)
78+
return result.returncode == 0
79+
except (subprocess.CalledProcessError, FileNotFoundError):
80+
return False
81+
82+
5483
class JupyterServerInstance:
5584
"""Manage the lifecycle of a Jupyter server instance."""
5685

@@ -96,17 +125,6 @@ def _handle_exit_signal(self, signum, frame):
96125
finally:
97126
sys.exit(0)
98127

99-
@staticmethod
100-
def is_jupyter_notebook_installed():
101-
"""Check if Jupyter Notebook is installed.
102-
:return: True if Jupyter Notebook is installed, False otherwise (bool).
103-
"""
104-
try:
105-
subprocess.check_output(["jupyter", "notebook", "--version"])
106-
return True
107-
except (subprocess.CalledProcessError, FileNotFoundError):
108-
return False
109-
110128
@staticmethod
111129
def find_free_port():
112130
"""Find a free port on the local machine.
@@ -137,7 +155,7 @@ def is_server_running(self, retries=10, delay=0.2):
137155
def start_server(self):
138156
"""Run Jupyter server in the given directory on a free port."""
139157
# Check if Jupyter Notebook is installed
140-
if not JupyterServerInstance.is_jupyter_notebook_installed():
158+
if not is_jupyter_installed():
141159
raise RuntimeError(_("Jupyter Notebook is not installed"))
142160

143161
# Find free port and build server url

0 commit comments

Comments
 (0)