Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a69de87

Browse files
committed
NF+BF: add close-figs option to plot_directive
Add `close-figs` choice to `context` option of plot_directive. This keeps the current context, but closes any previous figures. This is necessary because the plot directive first runs the specified code, then collects figures with: fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() If you are using the "context" option, prior figures maybe generated from a previous plot directive command, are still open, and therefore collected with the new figure(s). The old figures will usually be earlier in the list, and so, for something like this ReST block, you will always get the first generated figure (``range(10)``) repeated in the second plot directive panel: ####### A title ####### .. plot:: :context: import matplotlib.pyplot as plt plt.plot(range(10)) Then some text. .. plot:: :context: plt.figure() plt.plot(range(5), 'r') This PR add a context option of form `:context: close-figs` that closes any previous figure windows before running the directive code. It also adds a small test sphinx project to test the directive commands. See email discussion thread starting here: https://www.mail-archive.com/[email protected]/msg10816.html
1 parent edd4f86 commit a69de87

File tree

12 files changed

+518
-14
lines changed

12 files changed

+518
-14
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ matrix:
2323
env: BUILD_DOCS=true
2424

2525
install:
26-
- pip install -q --use-mirrors nose python-dateutil $NUMPY pep8 pyparsing pillow
26+
- pip install -q --use-mirrors nose python-dateutil $NUMPY pep8 pyparsing pillow sphinx
2727
- sudo apt-get update && sudo apt-get -qq install inkscape libav-tools gdb
2828
# We use --no-install-recommends to avoid pulling in additional large latex docs that we don't need
2929
- if [[ $BUILD_DOCS == true ]]; then sudo apt-get install -qq --no-install-recommends dvipng texlive-latex-base texlive-latex-extra texlive-fonts-recommended graphviz; fi

lib/matplotlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,7 @@ def tk_window_focus():
14421442
'matplotlib.tests.test_triangulation',
14431443
'mpl_toolkits.tests.test_mplot3d',
14441444
'matplotlib.tests.test_widgets',
1445+
'matplotlib.sphinxext.tests.test_tinypages',
14451446
]
14461447

14471448

lib/matplotlib/sphinxext/plot_directive.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,11 @@
6262
If provided, the code will be run in the context of all
6363
previous plot directives for which the `:context:` option was
6464
specified. This only applies to inline code plot directives,
65-
not those run from files. If the ``:context: reset`` is specified,
66-
the context is reset for this and future plots.
65+
not those run from files. If the ``:context: reset`` option is
66+
specified, the context is reset for this and future plots, and
67+
previous figures are closed prior to running the code.
68+
``:context:close-figs`` keeps the context but closes previous figures
69+
before running the code.
6770
6871
nofigs : bool
6972
If specified, the code block will be run, but no figures will
@@ -190,11 +193,9 @@ def _option_boolean(arg):
190193

191194

192195
def _option_context(arg):
193-
if arg in [None, 'reset']:
196+
if arg in [None, 'reset', 'close-figs']:
194197
return arg
195-
else:
196-
raise ValueError("argument should be None or 'reset'")
197-
return directives.choice(arg, ('None', 'reset'))
198+
raise ValueError("argument should be None or 'reset' or 'close-figs'")
198199

199200

200201
def _option_format(arg):
@@ -524,7 +525,8 @@ def clear_state(plot_rcparams, close=True):
524525

525526

526527
def render_figures(code, code_path, output_dir, output_base, context,
527-
function_name, config, context_reset=False):
528+
function_name, config, context_reset=False,
529+
close_figs=False):
528530
"""
529531
Run a pyplot script and save the low and high res PNGs and a PDF
530532
in *output_dir*.
@@ -600,11 +602,16 @@ def render_figures(code, code_path, output_dir, output_base, context,
600602

601603
if context_reset:
602604
clear_state(config.plot_rcparams)
605+
plot_context.clear()
606+
607+
close_figs = not context or close_figs
603608

604609
for i, code_piece in enumerate(code_pieces):
605610

606611
if not context or config.plot_apply_rcparams:
607-
clear_state(config.plot_rcparams, close=not context)
612+
clear_state(config.plot_rcparams, close_figs)
613+
elif close_figs:
614+
plt.close('all')
608615

609616
run_code(code_piece, code_path, ns, function_name)
610617

@@ -644,8 +651,8 @@ def run(arguments, content, options, state_machine, state, lineno):
644651
nofigs = 'nofigs' in options
645652

646653
options.setdefault('include-source', config.plot_include_source)
647-
context = 'context' in options
648-
context_reset = True if (context and options['context'] == 'reset') else False
654+
keep_context = 'context' in options
655+
context_opt = None if not keep_context else options['context']
649656

650657
rst_file = document.attributes['source']
651658
rst_dir = os.path.dirname(rst_file)
@@ -729,9 +736,15 @@ def run(arguments, content, options, state_machine, state, lineno):
729736

730737
# make figures
731738
try:
732-
results = render_figures(code, source_file_name, build_dir, output_base,
733-
context, function_name, config,
734-
context_reset=context_reset)
739+
results = render_figures(code,
740+
source_file_name,
741+
build_dir,
742+
output_base,
743+
keep_context,
744+
function_name,
745+
config,
746+
context_reset=context_opt == 'reset',
747+
close_figs=context_opt == 'close-figs')
735748
errors = []
736749
except PlotError as err:
737750
reporter = state.memo.reporter
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Make tests a package
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
""" Tests for tinypages build using sphinx extensions """
2+
3+
import shutil
4+
import tempfile
5+
6+
from os.path import (join as pjoin, dirname, isdir)
7+
8+
from subprocess import call, Popen, PIPE
9+
10+
from nose import SkipTest
11+
from nose.tools import assert_true
12+
13+
HERE = dirname(__file__)
14+
TINY_PAGES = pjoin(HERE, 'tinypages')
15+
16+
17+
def setup():
18+
# Check we have the sphinx-build command
19+
try:
20+
ret = call(['sphinx-build', '--help'], stdout=PIPE, stderr=PIPE)
21+
except OSError:
22+
raise SkipTest('Need sphinx-build on path for these tests')
23+
if ret != 0:
24+
raise RuntimeError('sphinx-build does not return 0')
25+
26+
27+
def file_same(file1, file2):
28+
with open(file1, 'rb') as fobj:
29+
contents1 = fobj.read()
30+
with open(file2, 'rb') as fobj:
31+
contents2 = fobj.read()
32+
return contents1 == contents2
33+
34+
35+
class TestTinyPages(object):
36+
# Test build and output of tinypages project
37+
38+
@classmethod
39+
def setup_class(cls):
40+
cls.page_build = tempfile.mkdtemp()
41+
try:
42+
cls.html_dir = pjoin(cls.page_build, 'html')
43+
cls.doctree_dir = pjoin(cls.page_build, 'doctrees')
44+
# Build the pages with warnings turned into errors
45+
cmd = ['sphinx-build', '-W', '-b', 'html',
46+
'-d', cls.doctree_dir,
47+
TINY_PAGES,
48+
cls.html_dir]
49+
proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
50+
out, err = proc.communicate()
51+
except Exception as e:
52+
shutil.rmtree(cls.page_build)
53+
raise e
54+
if proc.returncode != 0:
55+
shutil.rmtree(cls.page_build)
56+
raise RuntimeError('sphinx-build failed with stdout:\n'
57+
'{0}\nstderr:\n{1}\n'.format(
58+
out, err))
59+
60+
@classmethod
61+
def teardown_class(cls):
62+
shutil.rmtree(cls.page_build)
63+
64+
def test_some_plots(self):
65+
assert_true(isdir(self.html_dir))
66+
67+
def plot_file(num):
68+
return pjoin(self.html_dir, 'some_plots-{0}.png'.format(num))
69+
70+
range_10, range_6, range_4 = [plot_file(i) for i in range(1, 4)]
71+
# Plot 5 is range(6) plot
72+
assert_true(file_same(range_6, plot_file(5)))
73+
# Plot 7 is range(4) plot
74+
assert_true(file_same(range_4, plot_file(7)))
75+
# Plot 11 is range(10) plot
76+
assert_true(file_same(range_10, plot_file(11)))
77+
# Plot 12 uses the old range(10) figure and the new range(6) figure
78+
assert_true(file_same(range_10, plot_file('12_00')))
79+
assert_true(file_same(range_6, plot_file('12_01')))
80+
# Plot 13 shows close-figs in action
81+
assert_true(file_same(range_4, plot_file(13)))
82+
# Plot 14 has included source
83+
with open(pjoin(self.html_dir, 'some_plots.html'), 'rt') as fobj:
84+
html_contents = fobj.read()
85+
assert_true('# Only a comment' in html_contents)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
_build/
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Test project for matplotlib sphinx extensions
2+
3+
A tiny sphinx project from ``sphinx-quickstart`` with all default answers.

lib/matplotlib/sphinxext/tests/tinypages/_static/.gitignore

Whitespace-only changes.

0 commit comments

Comments
 (0)