diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index d8f3048ad556..28aeeb394478 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -71,6 +71,11 @@ If specified, the code block will be run, but no figures will be inserted. This is usually useful with the ``:context:`` option. + caption : str + If specified, the option's argument will be used as a caption for the + figure. This overwrites the caption given in the content, when the plot + is generated from a file. + Additionally, this directive supports all of the options of the `image` directive, except for *target* (since plot will add its own target). These include *alt*, *height*, *width*, *scale*, *align* and *class*. @@ -240,12 +245,16 @@ class PlotDirective(Directive): 'context': _option_context, 'nofigs': directives.flag, 'encoding': directives.encoding, + 'caption': directives.unchanged, } def run(self): """Run the plot directive.""" - return run(self.arguments, self.content, self.options, - self.state_machine, self.state, self.lineno) + try: + return run(self.arguments, self.content, self.options, + self.state_machine, self.state, self.lineno) + except Exception as e: + raise self.error(str(e)) def setup(app): @@ -636,6 +645,16 @@ def run(arguments, content, options, state_machine, state, lineno): # If there is content, it will be passed as a caption. caption = '\n'.join(content) + # Enforce unambiguous use of captions. + if "caption" in options: + if caption: + raise ValueError( + 'Caption specified in both content and options.' + ' Please remove ambiguity.' + ) + # Use caption option + caption = options["caption"] + # If the optional function name is provided, use it if len(arguments) == 2: function_name = arguments[1] @@ -652,7 +671,7 @@ def run(arguments, content, options, state_machine, state, lineno): base, ext = os.path.splitext(os.path.basename(source_file_name)) output_base = '%s-%d.py' % (base, counter) function_name = None - caption = '' + caption = options.get('caption', '') base, source_ext = os.path.splitext(output_base) if source_ext in ('.py', '.rst', '.txt'): diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index fd9e9344e5ac..3ce8d2951d07 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -21,6 +21,7 @@ def test_tinypages(tmpdir): str(Path(__file__).parent / 'tinypages'), str(html_dir)] proc = Popen(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True) out, err = proc.communicate() + assert proc.returncode == 0, \ "sphinx build failed with stdout:\n{}\nstderr:\n{}\n".format(out, err) if err: @@ -52,3 +53,7 @@ def plot_file(num): assert filecmp.cmp(range_6, html_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 + assert b'Plot 17 uses the caption option.' in html_contents + # check if figure caption made it into html file + assert b'This is the caption for plot 18.' in html_contents diff --git a/lib/matplotlib/tests/tinypages/some_plots.rst b/lib/matplotlib/tests/tinypages/some_plots.rst index 615908b0107f..5a71abc9e761 100644 --- a/lib/matplotlib/tests/tinypages/some_plots.rst +++ b/lib/matplotlib/tests/tinypages/some_plots.rst @@ -127,3 +127,17 @@ Plot 16 uses a specific function in a file with plot commands: .. plot:: range6.py range6 +Plot 17 gets a caption specified by the :caption: option: + +.. plot:: + :caption: Plot 17 uses the caption option. + + plt.figure() + plt.plot(range(6)) + + +Plot 18 uses an external file with the plot commands and a caption +using the :caption: option: + +.. plot:: range4.py + :caption: This is the caption for plot 18.