Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions camelot/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def prepare_plot(table, ax=None):
class PlotMethods:
"""Classmethod for plotting methods."""

def __call__(self, table, kind="text", filename=None, ax=None):
def __call__(self, table, kind="text", filename=None, ax=None, show=False):
"""Plot elements found on PDF page based on kind specified.

Useful for debugging and playing with different
Expand All @@ -193,6 +193,11 @@ def __call__(self, table, kind="text", filename=None, ax=None):
filename: str, optional (default: None)
Absolute path for saving the generated plot.
ax : matplotlib.axes.Axes (optional)
show : bool, optional (default: False)
If True, call ``plt.show(block=True)`` after creating the figure
so the window stays open until closed by the user. Useful when
running camelot in a plain Python script where
``figure.show()`` returns immediately.

Returns
-------
Expand All @@ -215,7 +220,10 @@ def __call__(self, table, kind="text", filename=None, ax=None):
fig.savefig(filename)
return None

return plot_method(table, ax)
fig = plot_method(table, ax)
if show:
plt.show(block=True)
return fig

def text(self, table, ax=None):
"""Generate a plot for all text elements present on the PDF page.
Expand Down
79 changes: 79 additions & 0 deletions tests/test_plot_show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""Unit tests for the ``show`` parameter of ``PlotMethods.__call__`` (issue #296).

``camelot.plot(table).show()`` calls matplotlib's ``Figure.show()`` which
returns immediately in non-interactive (script) backends, so the window
closes before the user can see it.

The fix adds ``show=True`` to ``camelot.plot()`` which internally calls
``plt.show(block=True)`` — the correct blocking entry point for scripts.
These tests verify that the parameter is wired correctly without needing
a real PDF or a display.
"""

from unittest.mock import MagicMock, patch, call
import pytest


def _make_table(flavor="lattice"):
"""Return a minimal mock that satisfies PlotMethods.__call__."""
table = MagicMock()
table.flavor = flavor
return table


@pytest.fixture()
def plot_methods():
from camelot.plotting import PlotMethods

return PlotMethods()


class TestPlotMethodsShowParameter:
"""Regression tests for the show=True blocking fix (issue #296)."""

def test_show_false_does_not_call_plt_show(self, plot_methods):
"""Default (show=False) must not call plt.show() — existing behaviour unchanged."""
table = _make_table()
mock_fig = MagicMock()

with patch.object(plot_methods, "text", return_value=mock_fig) as mock_plot, \
patch("camelot.plotting.plt.show") as mock_plt_show:
plot_methods(table, kind="text", show=False)

mock_plt_show.assert_not_called()

def test_show_true_calls_plt_show_with_block(self, plot_methods):
"""show=True must call plt.show(block=True) so scripts stay open."""
table = _make_table()
mock_fig = MagicMock()

with patch.object(plot_methods, "text", return_value=mock_fig), \
patch("camelot.plotting.plt.show") as mock_plt_show:
result = plot_methods(table, kind="text", show=True)

mock_plt_show.assert_called_once_with(block=True)
assert result is mock_fig

def test_show_true_returns_figure(self, plot_methods):
"""show=True must still return the figure so callers can further inspect it."""
table = _make_table()
sentinel_fig = MagicMock(name="sentinel_fig")

with patch.object(plot_methods, "text", return_value=sentinel_fig), \
patch("camelot.plotting.plt.show"):
result = plot_methods(table, kind="text", show=True)

assert result is sentinel_fig

def test_show_ignored_when_filename_given(self, plot_methods):
"""When filename is provided the figure is saved, not displayed."""
table = _make_table()
mock_fig = MagicMock()

with patch.object(plot_methods, "text", return_value=mock_fig), \
patch("camelot.plotting.plt.show") as mock_plt_show:
result = plot_methods(table, kind="text", filename="/tmp/out.png", show=True)

mock_plt_show.assert_not_called()
mock_fig.savefig.assert_called_once_with("/tmp/out.png")
assert result is None
Loading