5050``:output-base-name:`` : str
5151 The base name (without the extension) of the outputted image files. The
5252 default is to use the same name as the input script, or the name of
53- the RST document if no script is provided. Note: two plots with the same
54- output-base-name may overwrite each other .
53+ the RST document if no script is provided. The output-base-name for each
54+ plot directive must be unique .
5555
5656``:format:`` : {'python', 'doctest'}
5757 The format of the input. If unset, the format is auto-detected.
171171and *TEMPLATE_SRCSET*.
172172"""
173173
174+ from collections import defaultdict
174175import contextlib
175176import doctest
176177from io import StringIO
188189from docutils .parsers .rst .directives .images import Image
189190import jinja2 # Sphinx dependency.
190191
192+ from sphinx .environment .collectors import EnvironmentCollector
191193from sphinx .errors import ExtensionError
192194
193195import matplotlib
@@ -319,9 +321,35 @@ def setup(app):
319321 app .connect ('build-finished' , _copy_css_file )
320322 metadata = {'parallel_read_safe' : True , 'parallel_write_safe' : True ,
321323 'version' : matplotlib .__version__ }
324+ app .connect ('builder-inited' , init_filename_registry )
325+ app .add_env_collector (FilenameCollector )
322326 return metadata
323327
324328
329+ # -----------------------------------------------------------------------------
330+ # Handle Duplicate Filenames
331+ # -----------------------------------------------------------------------------
332+
333+ def init_filename_registry (app ):
334+ env = app .builder .env
335+ if not hasattr (env , 'mpl_custom_base_names' ):
336+ env .mpl_custom_base_names = defaultdict (set )
337+
338+ class FilenameCollector (EnvironmentCollector ):
339+ def process_doc (self , app , doctree ):
340+ pass
341+
342+ def clear_doc (self , app , env , docname ):
343+ if docname in env .mpl_custom_base_names :
344+ del env .mpl_custom_base_names [docname ]
345+
346+ def merge_other (self , app , env , docnames , other ):
347+ for docname in docnames :
348+ if docname in other .mpl_custom_base_names :
349+ if docname not in env .mpl_custom_base_names :
350+ env .mpl_custom_base_names [docname ] = set ()
351+ env .mpl_custom_base_names [docname ].update (other .mpl_custom_base_names [docname ])
352+
325353# -----------------------------------------------------------------------------
326354# Doctest handling
327355# -----------------------------------------------------------------------------
@@ -606,6 +634,16 @@ def _parse_srcset(entries):
606634 raise ExtensionError (f'srcset argument { entry !r} is invalid.' )
607635 return srcset
608636
637+ def check_output_base_name (env , output_base ):
638+ docname = env .docname
639+
640+ for d in env .mpl_custom_base_names :
641+ if output_base in env .mpl_custom_base_names [d ]:
642+ if d == docname :
643+ raise PlotError (f"The output-base-name '{ output_base } ' is used multiple times." )
644+ raise PlotError (f"The output-base-name '{ output_base } ' is used multiple times (it is also used in { env .doc2path (d )} )." )
645+
646+ env .mpl_custom_base_names [docname ].add (output_base )
609647
610648def render_figures (code , code_path , output_dir , output_base , context ,
611649 function_name , config , context_reset = False ,
@@ -730,6 +768,7 @@ def render_figures(code, code_path, output_dir, output_base, context,
730768def run (arguments , content , options , state_machine , state , lineno ):
731769 document = state_machine .document
732770 config = document .settings .env .config
771+ env = document .settings .env
733772 nofigs = 'nofigs' in options
734773
735774 if config .plot_srcset and setup .app .builder .name == 'singlehtml' :
@@ -785,6 +824,7 @@ def run(arguments, content, options, state_machine, state, lineno):
785824 code = Path (source_file_name ).read_text (encoding = 'utf-8' )
786825 if options ['output-base-name' ]:
787826 output_base = options ['output-base-name' ]
827+ check_output_base_name (env , output_base )
788828 else :
789829 output_base = os .path .basename (source_file_name )
790830 else :
@@ -793,6 +833,7 @@ def run(arguments, content, options, state_machine, state, lineno):
793833 base , ext = os .path .splitext (os .path .basename (source_file_name ))
794834 if options ['output-base-name' ]:
795835 output_base = options ['output-base-name' ]
836+ check_output_base_name (env , output_base )
796837 else :
797838 counter = document .attributes .get ('_plot_counter' , 0 ) + 1
798839 document .attributes ['_plot_counter' ] = counter
0 commit comments