diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index f483d17ce1f3..671484790d66 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -369,16 +369,16 @@ def _split_code_at_show(text): .. only:: html - {% if source_link or (html_show_formats and not multi_image) %} + {% if src_name or (html_show_formats and not multi_image) %} ( - {%- if source_link -%} - `Source code <{{ source_link }}>`__ + {%- if src_name -%} + :download:`Source code <{{ build_dir }}/{{ src_name }}>` {%- endif -%} {%- if html_show_formats and not multi_image -%} {%- for img in images -%} {%- for fmt in img.formats -%} - {%- if source_link or not loop.first -%}, {% endif -%} - `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ + {%- if src_name or not loop.first -%}, {% endif -%} + :download:`{{ fmt }} <{{ build_dir }}/{{ img.basename }}.{{ fmt }}>` {%- endfor -%} {%- endfor -%} {%- endif -%} @@ -395,7 +395,7 @@ def _split_code_at_show(text): ( {%- for fmt in img.formats -%} {%- if not loop.first -%}, {% endif -%} - `{{ fmt }} <{{ dest_dir }}/{{ img.basename }}.{{ fmt }}>`__ + :download:`{{ fmt }} <{{ build_dir }}/{{ img.basename }}.{{ fmt }}>` {%- endfor -%} ) {%- endif -%} @@ -756,21 +756,13 @@ def run(arguments, content, options, state_machine, state, lineno): build_dir = os.path.normpath(build_dir) os.makedirs(build_dir, exist_ok=True) - # output_dir: final location in the builder's directory - dest_dir = os.path.abspath(os.path.join(setup.app.builder.outdir, - source_rel_dir)) - os.makedirs(dest_dir, exist_ok=True) - # how to link to files from the RST file - dest_dir_link = os.path.join(relpath(setup.confdir, rst_dir), - source_rel_dir).replace(os.path.sep, '/') try: build_dir_link = relpath(build_dir, rst_dir).replace(os.path.sep, '/') except ValueError: # on Windows, relpath raises ValueError when path and start are on # different mounts/drives build_dir_link = build_dir - source_link = dest_dir_link + '/' + output_base + source_ext # get list of included rst files so that the output is updated when any # plots in the included files change. These attributes are modified by the @@ -791,6 +783,14 @@ def run(arguments, content, options, state_machine, state, lineno): except ValueError: pass + # save script (if necessary) + if options['show-source-link']: + Path(build_dir, output_base + source_ext).write_text( + doctest.script_from_examples(code) + if source_file_name == rst_file and is_doctest + else code, + encoding='utf-8') + # make figures try: results = render_figures(code, @@ -837,18 +837,17 @@ def run(arguments, content, options, state_machine, state, lineno): ':%s: %s' % (key, val) for key, val in options.items() if key in ('alt', 'height', 'width', 'scale', 'align', 'class')] - # Not-None src_link signals the need for a source link in the generated - # html + # Not-None src_name signals the need for a source download in the + # generated html if j == 0 and options['show-source-link']: - src_link = source_link + src_name = output_base + source_ext else: - src_link = None + src_name = None result = jinja2.Template(config.plot_template or TEMPLATE).render( default_fmt=default_fmt, - dest_dir=dest_dir_link, build_dir=build_dir_link, - source_link=src_link, + src_name=src_name, multi_image=len(images) > 1, options=opts, images=images, @@ -862,22 +861,4 @@ def run(arguments, content, options, state_machine, state, lineno): if total_lines: state_machine.insert_input(total_lines, source=source_file_name) - # copy image files to builder's output directory, if necessary - Path(dest_dir).mkdir(parents=True, exist_ok=True) - - for code_piece, images in results: - for img in images: - for fn in img.filenames(): - destimg = os.path.join(dest_dir, os.path.basename(fn)) - if fn != destimg: - shutil.copyfile(fn, destimg) - - # copy script (if necessary) - if options['show-source-link']: - Path(dest_dir, output_base + source_ext).write_text( - doctest.script_from_examples(code) - if source_file_name == rst_file and is_doctest - else code, - encoding='utf-8') - return errors diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index de3146070918..7b8ab1eeff50 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -18,6 +18,7 @@ def test_tinypages(tmp_path): shutil.copytree(Path(__file__).parent / 'tinypages', tmp_path, dirs_exist_ok=True) html_dir = tmp_path / '_build' / 'html' + img_dir = html_dir / '_images' doctree_dir = tmp_path / 'doctrees' # Build the pages with warnings turned into errors cmd = [sys.executable, '-msphinx', '-W', '-b', 'html', @@ -35,7 +36,7 @@ def test_tinypages(tmp_path): build_sphinx_html(tmp_path, doctree_dir, html_dir) def plot_file(num): - return html_dir / f'some_plots-{num}.png' + return img_dir / f'some_plots-{num}.png' def plot_directive_file(num): # This is always next to the doctree dir. @@ -58,8 +59,8 @@ def plot_directive_file(num): assert b'# Only a comment' in html_contents # check plot defined in external file. - assert filecmp.cmp(range_4, html_dir / 'range4.png') - assert filecmp.cmp(range_6, html_dir / 'range6.png') + assert filecmp.cmp(range_4, img_dir / 'range4.png') + assert filecmp.cmp(range_6, img_dir / 'range6.png') # check if figure caption made it into html file assert b'This is the caption for plot 15.' in html_contents # check if figure caption using :caption: made it into html file @@ -111,13 +112,13 @@ def test_plot_html_show_source_link(tmp_path): # Make sure source scripts are created by default html_dir1 = tmp_path / '_build' / 'html1' build_sphinx_html(tmp_path, doctree_dir, html_dir1) - assert "index-1.py" in [p.name for p in html_dir1.iterdir()] + assert len(list(html_dir1.glob("**/index-1.py"))) == 1 # Make sure source scripts are NOT created when # plot_html_show_source_link` is False html_dir2 = tmp_path / '_build' / 'html2' build_sphinx_html(tmp_path, doctree_dir, html_dir2, extra_args=['-D', 'plot_html_show_source_link=0']) - assert "index-1.py" not in [p.name for p in html_dir2.iterdir()] + assert len(list(html_dir2.glob("**/index-1.py"))) == 0 @pytest.mark.parametrize('plot_html_show_source_link', [0, 1]) @@ -137,7 +138,7 @@ def test_show_source_link_true(tmp_path, plot_html_show_source_link): html_dir = tmp_path / '_build' / 'html' build_sphinx_html(tmp_path, doctree_dir, html_dir, extra_args=[ '-D', f'plot_html_show_source_link={plot_html_show_source_link}']) - assert "index-1.py" in [p.name for p in html_dir.iterdir()] + assert len(list(html_dir.glob("**/index-1.py"))) == 1 @pytest.mark.parametrize('plot_html_show_source_link', [0, 1]) @@ -157,7 +158,7 @@ def test_show_source_link_false(tmp_path, plot_html_show_source_link): html_dir = tmp_path / '_build' / 'html' build_sphinx_html(tmp_path, doctree_dir, html_dir, extra_args=[ '-D', f'plot_html_show_source_link={plot_html_show_source_link}']) - assert "index-1.py" not in [p.name for p in html_dir.iterdir()] + assert len(list(html_dir.glob("**/index-1.py"))) == 0 def build_sphinx_html(tmp_path, doctree_dir, html_dir, extra_args=None):