Skip to content

Commit 4bb1538

Browse files
authored
Merge pull request #21661 from tacaswell/fix_plot_directive_funccalls
Fix plot directive with func calls
2 parents 932d0e6 + 1411ba3 commit 4bb1538

File tree

5 files changed

+74
-90
lines changed

5 files changed

+74
-90
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
plot directive removals
2+
~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
The public methods:
5+
6+
- ``matplotlib.sphinxext.split_code_at_show``
7+
- ``matplotlib.sphinxext.unescape_doctest``
8+
- ``matplotlib.sphinxext.run_code``
9+
10+
have been removed.
11+
12+
The deprecated *encoding* option to the plot directive has been removed.

lib/matplotlib/sphinxext/plot_directive.py

Lines changed: 30 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,6 @@
6060
changed using the ``plot_html_show_source_link`` variable in
6161
:file:`conf.py` (which itself defaults to True).
6262
63-
``:encoding:`` : str
64-
If this source file is in a non-UTF8 or non-ASCII encoding, the
65-
encoding must be specified using the ``:encoding:`` option. The
66-
encoding will not be inferred using the ``-*- coding -*-`` metacomment.
67-
6863
``:context:`` : bool or str
6964
If provided, the code will be run in the context of all previous plot
7065
directives for which the ``:context:`` option was specified. This only
@@ -166,7 +161,7 @@
166161
import matplotlib
167162
from matplotlib.backend_bases import FigureManagerBase
168163
import matplotlib.pyplot as plt
169-
from matplotlib import _api, _pylab_helpers, cbook
164+
from matplotlib import _pylab_helpers, cbook
170165

171166
matplotlib.use("agg")
172167

@@ -200,11 +195,6 @@ def _option_format(arg):
200195
return directives.choice(arg, ('python', 'doctest'))
201196

202197

203-
def _deprecated_option_encoding(arg):
204-
_api.warn_deprecated("3.5", name="encoding", obj_type="option")
205-
return directives.encoding(arg)
206-
207-
208198
def mark_plot_labels(app, document):
209199
"""
210200
To make plots referenceable, we need to move the reference from the
@@ -254,7 +244,6 @@ class PlotDirective(Directive):
254244
'format': _option_format,
255245
'context': _option_context,
256246
'nofigs': directives.flag,
257-
'encoding': _deprecated_option_encoding,
258247
'caption': directives.unchanged,
259248
}
260249

@@ -316,47 +305,25 @@ def contains_doctest(text):
316305
return bool(m)
317306

318307

319-
@_api.deprecated("3.5", alternative="doctest.script_from_examples")
320-
def unescape_doctest(text):
321-
"""
322-
Extract code from a piece of text, which contains either Python code
323-
or doctests.
324-
"""
325-
if not contains_doctest(text):
326-
return text
327-
code = ""
328-
for line in text.split("\n"):
329-
m = re.match(r'^\s*(>>>|\.\.\.) (.*)$', line)
330-
if m:
331-
code += m.group(2) + "\n"
332-
elif line.strip():
333-
code += "# " + line.strip() + "\n"
334-
else:
335-
code += "\n"
336-
return code
337-
338-
339-
@_api.deprecated("3.5")
340-
def split_code_at_show(text):
308+
def _split_code_at_show(text, function_name):
341309
"""Split code at plt.show()."""
342-
return _split_code_at_show(text)[1]
343-
344310

345-
def _split_code_at_show(text):
346-
"""Split code at plt.show()."""
347-
parts = []
348311
is_doctest = contains_doctest(text)
349-
part = []
350-
for line in text.split("\n"):
351-
if (not is_doctest and line.strip() == 'plt.show()') or \
352-
(is_doctest and line.strip() == '>>> plt.show()'):
353-
part.append(line)
312+
if function_name is None:
313+
parts = []
314+
part = []
315+
for line in text.split("\n"):
316+
if ((not is_doctest and line.startswith('plt.show(')) or
317+
(is_doctest and line.strip() == '>>> plt.show()')):
318+
part.append(line)
319+
parts.append("\n".join(part))
320+
part = []
321+
else:
322+
part.append(line)
323+
if "\n".join(part).strip():
354324
parts.append("\n".join(part))
355-
part = []
356-
else:
357-
part.append(line)
358-
if "\n".join(part).strip():
359-
parts.append("\n".join(part))
325+
else:
326+
parts = [text]
360327
return is_doctest, parts
361328

362329

@@ -469,15 +436,6 @@ class PlotError(RuntimeError):
469436
pass
470437

471438

472-
@_api.deprecated("3.5")
473-
def run_code(code, code_path, ns=None, function_name=None):
474-
"""
475-
Import a Python module from a path, and run the function given by
476-
name, if function_name is not None.
477-
"""
478-
_run_code(unescape_doctest(code), code_path, ns, function_name)
479-
480-
481439
def _run_code(code, code_path, ns=None, function_name=None):
482440
"""
483441
Import a Python module from a path, and run the function given by
@@ -566,28 +524,30 @@ def render_figures(code, code_path, output_dir, output_base, context,
566524
Save the images under *output_dir* with file names derived from
567525
*output_base*
568526
"""
527+
if function_name is not None:
528+
output_base = f'{output_base}_{function_name}'
569529
formats = get_plot_formats(config)
570530

571531
# Try to determine if all images already exist
572532

573-
is_doctest, code_pieces = _split_code_at_show(code)
533+
is_doctest, code_pieces = _split_code_at_show(code, function_name)
574534

575535
# Look for single-figure output files first
576-
all_exists = True
577536
img = ImageFile(output_base, output_dir)
578537
for format, dpi in formats:
579538
if context or out_of_date(code_path, img.filename(format),
580539
includes=code_includes):
581540
all_exists = False
582541
break
583542
img.formats.append(format)
543+
else:
544+
all_exists = True
584545

585546
if all_exists:
586547
return [(code, [img])]
587548

588549
# Then look for multi-figure output files
589550
results = []
590-
all_exists = True
591551
for i, code_piece in enumerate(code_pieces):
592552
images = []
593553
for j in itertools.count():
@@ -611,6 +571,8 @@ def render_figures(code, code_path, output_dir, output_base, context,
611571
if not all_exists:
612572
break
613573
results.append((code_piece, images))
574+
else:
575+
all_exists = True
614576

615577
if all_exists:
616578
return results
@@ -793,13 +755,13 @@ def run(arguments, content, options, state_machine, state, lineno):
793755

794756
# make figures
795757
try:
796-
results = render_figures(code,
797-
source_file_name,
798-
build_dir,
799-
output_base,
800-
keep_context,
801-
function_name,
802-
config,
758+
results = render_figures(code=code,
759+
code_path=source_file_name,
760+
output_dir=build_dir,
761+
output_base=output_base,
762+
context=keep_context,
763+
function_name=function_name,
764+
config=config,
803765
context_reset=context_opt == 'reset',
804766
close_figs=context_opt == 'close-figs',
805767
code_includes=source_file_includes)

lib/matplotlib/tests/test_sphinxext.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@
1414
minversion=None if sys.version_info < (3, 10) else '4.1.3')
1515

1616

17+
def build_sphinx_html(source_dir, doctree_dir, html_dir, extra_args=None):
18+
# Build the pages with warnings turned into errors
19+
extra_args = [] if extra_args is None else extra_args
20+
cmd = [sys.executable, '-msphinx', '-W', '-b', 'html',
21+
'-d', str(doctree_dir), str(source_dir), str(html_dir), *extra_args]
22+
proc = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True,
23+
env={**os.environ, "MPLBACKEND": ""})
24+
out, err = proc.communicate()
25+
26+
assert proc.returncode == 0, \
27+
f"sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n"
28+
if err:
29+
pytest.fail(f"sphinx build emitted the following warnings:\n{err}")
30+
31+
assert html_dir.is_dir()
32+
33+
1734
def test_tinypages(tmp_path):
1835
shutil.copytree(Path(__file__).parent / 'tinypages', tmp_path,
1936
dirs_exist_ok=True)
@@ -60,7 +77,7 @@ def plot_directive_file(num):
6077
assert b'# Only a comment' in html_contents
6178
# check plot defined in external file.
6279
assert filecmp.cmp(range_4, img_dir / 'range4.png')
63-
assert filecmp.cmp(range_6, img_dir / 'range6.png')
80+
assert filecmp.cmp(range_6, img_dir / 'range6_range6.png')
6481
# check if figure caption made it into html file
6582
assert b'This is the caption for plot 15.' in html_contents
6683
# check if figure caption using :caption: made it into html file
@@ -74,6 +91,8 @@ def plot_directive_file(num):
7491
# Plot 21 is range(6) plot via an include directive. But because some of
7592
# the previous plots are repeated, the argument to plot_file() is only 17.
7693
assert filecmp.cmp(range_6, plot_file(17))
94+
# plot 22 is from the range6.py file again, but a different function
95+
assert filecmp.cmp(range_10, img_dir / 'range6_range10.png')
7796

7897
# Modify the included plot
7998
contents = (tmp_path / 'included_plot_21.rst').read_bytes()
@@ -159,20 +178,3 @@ def test_show_source_link_false(tmp_path, plot_html_show_source_link):
159178
build_sphinx_html(tmp_path, doctree_dir, html_dir, extra_args=[
160179
'-D', f'plot_html_show_source_link={plot_html_show_source_link}'])
161180
assert len(list(html_dir.glob("**/index-1.py"))) == 0
162-
163-
164-
def build_sphinx_html(tmp_path, doctree_dir, html_dir, extra_args=None):
165-
# Build the pages with warnings turned into errors
166-
extra_args = [] if extra_args is None else extra_args
167-
cmd = [sys.executable, '-msphinx', '-W', '-b', 'html',
168-
'-d', str(doctree_dir), str(tmp_path), str(html_dir), *extra_args]
169-
proc = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True,
170-
env={**os.environ, "MPLBACKEND": ""})
171-
out, err = proc.communicate()
172-
173-
assert proc.returncode == 0, \
174-
f"sphinx build failed with stdout:\n{out}\nstderr:\n{err}\n"
175-
if err:
176-
pytest.fail(f"sphinx build emitted the following warnings:\n{err}")
177-
178-
assert html_dir.is_dir()

lib/matplotlib/tests/tinypages/range6.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,10 @@ def range6():
1111
plt.figure()
1212
plt.plot(range(6))
1313
plt.show()
14+
15+
16+
def range10():
17+
"""The function that should be executed."""
18+
plt.figure()
19+
plt.plot(range(10))
20+
plt.show()

lib/matplotlib/tests/tinypages/some_plots.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,9 @@ Plot 14 uses ``include-source``:
120120

121121
# Only a comment
122122

123-
Plot 15 uses an external file with the plot commands and a caption (the
124-
encoding is ignored and just verifies the deprecation is not broken):
123+
Plot 15 uses an external file with the plot commands and a caption:
125124

126125
.. plot:: range4.py
127-
:encoding: utf-8
128126

129127
This is the caption for plot 15.
130128

@@ -168,8 +166,11 @@ scenario:
168166

169167
plt.figure()
170168
plt.plot(range(4))
171-
169+
172170
Plot 21 is generated via an include directive:
173171

174172
.. include:: included_plot_21.rst
175173

174+
Plot 22 uses a different specific function in a file with plot commands:
175+
176+
.. plot:: range6.py range10

0 commit comments

Comments
 (0)