Skip to content

Commit 97cba7e

Browse files
authored
PYGMT_USE_EXTERNAL_DISPLAY should NOT disable image display in Jupyter notebook environment (#3418)
* Refactor and add the _get_default_display_method function * PYGMT_USE_EXTERNAL_DISPLAY should only affect external display * Add some tests * Simplify the code block for checking IPython kernels
1 parent 2157333 commit 97cba7e

File tree

2 files changed

+92
-19
lines changed

2 files changed

+92
-19
lines changed

pygmt/figure.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
from pathlib import Path, PurePath
88
from tempfile import TemporaryDirectory
9+
from typing import Literal
910

1011
try:
1112
import IPython
@@ -26,27 +27,49 @@
2627
use_alias,
2728
)
2829

30+
31+
def _get_default_display_method() -> Literal["external", "notebook", "none"]:
32+
"""
33+
Get the default method to display preview images.
34+
35+
The function checks the current environment and determines the most suitable method
36+
to display preview images when calling :meth:`pygmt.Figure.show`. Valid display
37+
methods are:
38+
39+
- ``"external"``: External PDF preview using the default PDF viewer
40+
- ``"notebook"``: Inline PNG preview in the current notebook
41+
- ``"none"``: Disable image preview
42+
43+
The default display method is ``"notebook"`` in the Jupyter notebook environment,
44+
and ``"external"`` in other cases.
45+
46+
Setting environment variable **PYGMT_USE_EXTERNAL_DISPLAY** to ``"false"`` can
47+
disable image preview in external viewers. It's useful when running the tests and
48+
building the documentation to avoid popping up windows.
49+
50+
Returns
51+
-------
52+
method
53+
The default display method.
54+
"""
55+
# Check if an IPython kernel is running.
56+
if _HAS_IPYTHON and (ipy := IPython.get_ipython()) and "IPKernelApp" in ipy.config:
57+
return "notebook"
58+
# Check if the environment variable PYGMT_USE_EXTERNAL_DISPLAY is set to "false".
59+
if os.environ.get("PYGMT_USE_EXTERNAL_DISPLAY", "true").lower() == "false":
60+
return "none"
61+
# Fallback to using the external viewer.
62+
return "external"
63+
64+
2965
# A registry of all figures that have had "show" called in this session.
3066
# This is needed for the sphinx-gallery scraper in pygmt/sphinx_gallery.py
3167
SHOWED_FIGURES = []
32-
33-
# Configurations for figure display
68+
# Configurations for figure display.
3469
SHOW_CONFIG = {
35-
"method": "external", # Open in an external viewer by default
70+
"method": _get_default_display_method(), # The image preview display method.
3671
}
3772

38-
# Show figures in Jupyter notebooks if available
39-
if _HAS_IPYTHON:
40-
get_ipython = IPython.get_ipython()
41-
if get_ipython and "IPKernelApp" in get_ipython.config: # Jupyter Notebook enabled
42-
SHOW_CONFIG["method"] = "notebook"
43-
44-
# Set environment variable PYGMT_USE_EXTERNAL_DISPLAY to 'false' to disable
45-
# external display. Use it when running the tests and building the docs to
46-
# avoid popping up windows.
47-
if os.environ.get("PYGMT_USE_EXTERNAL_DISPLAY", "true").lower() == "false":
48-
SHOW_CONFIG["method"] = "none"
49-
5073

5174
class Figure:
5275
"""

pygmt/tests/test_figure.py

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@
44
Doesn't include the plotting commands which have their own test files.
55
"""
66

7-
import importlib
87
from pathlib import Path
98

109
import numpy as np
1110
import numpy.testing as npt
1211
import pytest
1312
from pygmt import Figure, set_display
1413
from pygmt.exceptions import GMTError, GMTInvalidInput
14+
from pygmt.figure import _get_default_display_method
1515
from pygmt.helpers import GMTTempFile
1616

17-
HAS_IPYTHON = bool(importlib.util.find_spec("IPython"))
17+
try:
18+
import IPython
19+
20+
_HAS_IPYTHON = True
21+
except ImportError:
22+
_HAS_IPYTHON = False
1823

1924

2025
def test_figure_region():
@@ -307,7 +312,7 @@ def test_figure_savefig_worldfile():
307312
fig.savefig(fname=imgfile.name, worldfile=True)
308313

309314

310-
@pytest.mark.skipif(not HAS_IPYTHON, reason="run when IPython is installed")
315+
@pytest.mark.skipif(not _HAS_IPYTHON, reason="run when IPython is installed")
311316
def test_figure_show():
312317
"""
313318
Test that show creates the correct file name and deletes the temp dir.
@@ -347,7 +352,7 @@ def test_figure_show_invalid_method():
347352
fig.show(method="test")
348353

349354

350-
@pytest.mark.skipif(HAS_IPYTHON, reason="run without IPython installed")
355+
@pytest.mark.skipif(_HAS_IPYTHON, reason="run without IPython installed")
351356
def test_figure_show_notebook_error_without_ipython():
352357
"""
353358
Test to check if an error is raised when display method is 'notebook', but IPython
@@ -390,3 +395,48 @@ def test_figure_unsupported_xshift_yshift():
390395
fig.plot(x=1, y=1, style="c3c", yshift="3c")
391396
with pytest.raises(GMTInvalidInput):
392397
fig.plot(x=1, y=1, style="c3c", Y="3c")
398+
399+
400+
class TestGetDefaultDisplayMethod:
401+
"""
402+
Test the _get_default_display_method function.
403+
"""
404+
405+
def test_default_display_method(self, monkeypatch):
406+
"""
407+
Default display method is "external" if PYGMT_USE_EXTERNAL_DISPLAY is undefined.
408+
"""
409+
monkeypatch.delenv("PYGMT_USE_EXTERNAL_DISPLAY", raising=False)
410+
assert _get_default_display_method() == "external"
411+
412+
def test_disable_external_display(self, monkeypatch):
413+
"""
414+
Setting PYGMT_USE_EXTERNAL_DISPLAY to "false" should disable external display.
415+
"""
416+
monkeypatch.setenv("PYGMT_USE_EXTERNAL_DISPLAY", "false")
417+
assert _get_default_display_method() == "none"
418+
419+
@pytest.mark.skipif(not _HAS_IPYTHON, reason="Run when IPython is installed")
420+
def test_notebook_display(self, monkeypatch):
421+
"""
422+
Default display method is "notebook" when an IPython kernel is running.
423+
"""
424+
425+
class MockIPython:
426+
"""
427+
A simple mock class to simulate an IPython instance.
428+
"""
429+
430+
def __init__(self):
431+
self.config = {"IPKernelApp": True}
432+
433+
# Mock IPython.get_ipython() to return a MockIPython instance.
434+
mock_ipython = MockIPython()
435+
monkeypatch.setattr(IPython, "get_ipython", lambda: mock_ipython)
436+
437+
# Default display method should be "notebook" when an IPython kernel is running.
438+
assert _get_default_display_method() == "notebook"
439+
440+
# PYGMT_USE_EXTERNAL_DISPLAY should not affect notebook display.
441+
monkeypatch.setenv("PYGMT_USE_EXTERNAL_DISPLAY", "false")
442+
assert _get_default_display_method() == "notebook"

0 commit comments

Comments
 (0)