From 90069b47c44a20e66cce715dfe122e5e96960012 Mon Sep 17 00:00:00 2001 From: "Ulrich J. Herter" Date: Mon, 7 Sep 2020 10:14:42 +0200 Subject: [PATCH 1/6] plot directive: caption-option This new option allows the user to add a caption to plots with the plotting code given in the directive's contents. * The option :caption: enforces a caption. * The option take precedence over the contents. * Tested and documented in the modul's doc string. * Note in next_whats_new --- lib/matplotlib/sphinxext/plot_directive.py | 13 ++++++++++--- lib/matplotlib/tests/test_sphinxext.py | 5 +++++ lib/matplotlib/tests/tinypages/some_plots.rst | 16 ++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index d8f3048ad556..e8799ea0d747 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,6 +245,7 @@ class PlotDirective(Directive): 'context': _option_context, 'nofigs': directives.flag, 'encoding': directives.encoding, + 'caption': directives.unchanged, } def run(self): @@ -633,8 +639,9 @@ def run(arguments, content, options, state_machine, state, lineno): source_file_name = os.path.join(setup.confdir, config.plot_basedir, directives.uri(arguments[0])) - # If there is content, it will be passed as a caption. - caption = '\n'.join(content) + # If there is content, it will be passed as a caption, if the caption + # option is not present. + caption = options.get("caption", '\n'.join(content)) # If the optional function name is provided, use it if len(arguments) == 2: @@ -652,7 +659,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..af5dcd41da7a 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 usin :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 actual 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..9c7e9fd50854 100644 --- a/lib/matplotlib/tests/tinypages/some_plots.rst +++ b/lib/matplotlib/tests/tinypages/some_plots.rst @@ -127,3 +127,19 @@ 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 +the using :caption: option: + +.. plot:: range4.py + :caption: This is the actual caption for plot 18. + + This is the caption for plot 18. From 9c1d1d05bd4fc7a790c7200692b83b62b079f9cf Mon Sep 17 00:00:00 2001 From: "Ulrich J. Herter" Date: Tue, 8 Sep 2020 16:17:59 +0200 Subject: [PATCH 2/6] Enforce unambiguous use of :caption: option. --- lib/matplotlib/sphinxext/plot_directive.py | 16 +++++++++++++--- lib/matplotlib/tests/test_sphinxext.py | 2 +- lib/matplotlib/tests/tinypages/some_plots.rst | 4 +--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index e8799ea0d747..140e07afd2c4 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -639,9 +639,19 @@ def run(arguments, content, options, state_machine, state, lineno): source_file_name = os.path.join(setup.confdir, config.plot_basedir, directives.uri(arguments[0])) - # If there is content, it will be passed as a caption, if the caption - # option is not present. - caption = options.get("caption", '\n'.join(content)) + # 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 twice: In content and options.' + + ' Please remove ambiguity.' + ) + else: + # Use caption option + caption = options["caption"] # If the optional function name is provided, use it if len(arguments) == 2: diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index af5dcd41da7a..dba3febe29a0 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -56,4 +56,4 @@ def plot_file(num): # check if figure caption usin :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 actual caption for plot 18.' in html_contents + 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 9c7e9fd50854..93e1d708381f 100644 --- a/lib/matplotlib/tests/tinypages/some_plots.rst +++ b/lib/matplotlib/tests/tinypages/some_plots.rst @@ -140,6 +140,4 @@ Plot 18 uses an external file with the plot commands and a caption the using :caption: option: .. plot:: range4.py - :caption: This is the actual caption for plot 18. - - This is the caption for plot 18. + :caption: This is the caption for plot 18. From bda148ecc624ff381b0dec51c69a9280bf8801e4 Mon Sep 17 00:00:00 2001 From: ulijh Date: Wed, 9 Sep 2020 07:38:30 +0200 Subject: [PATCH 3/6] Update lib/matplotlib/sphinxext/plot_directive.py Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/sphinxext/plot_directive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index 140e07afd2c4..c0f83fff7f90 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -646,8 +646,8 @@ def run(arguments, content, options, state_machine, state, lineno): if "caption" in options: if caption: raise ValueError( - 'Caption specified twice: In content and options.' - + ' Please remove ambiguity.' + 'Caption specified in both content and options.' + ' Please remove ambiguity.' ) else: # Use caption option From 9107c1293c1e6b2f94fb92fc8f35fc16b08c1cbe Mon Sep 17 00:00:00 2001 From: ulijh Date: Wed, 9 Sep 2020 07:39:52 +0200 Subject: [PATCH 4/6] Update lib/matplotlib/tests/tinypages/some_plots.rst Fix typo in sphinx test document. Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/tests/tinypages/some_plots.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/tinypages/some_plots.rst b/lib/matplotlib/tests/tinypages/some_plots.rst index 93e1d708381f..5a71abc9e761 100644 --- a/lib/matplotlib/tests/tinypages/some_plots.rst +++ b/lib/matplotlib/tests/tinypages/some_plots.rst @@ -137,7 +137,7 @@ Plot 17 gets a caption specified by the :caption: option: Plot 18 uses an external file with the plot commands and a caption -the using :caption: option: +using the :caption: option: .. plot:: range4.py :caption: This is the caption for plot 18. From 1d6e939c937971d2e408fe4539653066c9a470a0 Mon Sep 17 00:00:00 2001 From: ulijh Date: Wed, 9 Sep 2020 07:47:49 +0200 Subject: [PATCH 5/6] Update lib/matplotlib/tests/test_sphinxext.py Fix typo in comment. Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/tests/test_sphinxext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_sphinxext.py b/lib/matplotlib/tests/test_sphinxext.py index dba3febe29a0..3ce8d2951d07 100644 --- a/lib/matplotlib/tests/test_sphinxext.py +++ b/lib/matplotlib/tests/test_sphinxext.py @@ -53,7 +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 usin :caption: made it into html file + # 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 From c6ab4ab821cd36ea7c21503c3e5dd7b7ad1cf2f2 Mon Sep 17 00:00:00 2001 From: "Ulrich J. Herter" Date: Wed, 9 Sep 2020 08:16:36 +0200 Subject: [PATCH 6/6] Using self.error in drective to provide context. --- lib/matplotlib/sphinxext/plot_directive.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/matplotlib/sphinxext/plot_directive.py b/lib/matplotlib/sphinxext/plot_directive.py index c0f83fff7f90..28aeeb394478 100644 --- a/lib/matplotlib/sphinxext/plot_directive.py +++ b/lib/matplotlib/sphinxext/plot_directive.py @@ -250,8 +250,11 @@ class PlotDirective(Directive): 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): @@ -649,9 +652,8 @@ def run(arguments, content, options, state_machine, state, lineno): 'Caption specified in both content and options.' ' Please remove ambiguity.' ) - else: - # Use caption option - caption = options["caption"] + # Use caption option + caption = options["caption"] # If the optional function name is provided, use it if len(arguments) == 2: