50
50
``:output-base-name:`` : str
51
51
The base name (without the extension) of the outputted image files. The
52
52
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 .
55
55
56
56
``:format:`` : {'python', 'doctest'}
57
57
The format of the input. If unset, the format is auto-detected.
171
171
and *TEMPLATE_SRCSET*.
172
172
"""
173
173
174
+ from collections import defaultdict
174
175
import contextlib
175
176
import doctest
176
177
from io import StringIO
188
189
from docutils .parsers .rst .directives .images import Image
189
190
import jinja2 # Sphinx dependency.
190
191
192
+ from sphinx .environment .collectors import EnvironmentCollector
191
193
from sphinx .errors import ExtensionError
192
194
193
195
import matplotlib
@@ -319,9 +321,35 @@ def setup(app):
319
321
app .connect ('build-finished' , _copy_css_file )
320
322
metadata = {'parallel_read_safe' : True , 'parallel_write_safe' : True ,
321
323
'version' : matplotlib .__version__ }
324
+ app .connect ('builder-inited' , init_filename_registry )
325
+ app .add_env_collector (FilenameCollector )
322
326
return metadata
323
327
324
328
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
+
325
353
# -----------------------------------------------------------------------------
326
354
# Doctest handling
327
355
# -----------------------------------------------------------------------------
@@ -606,6 +634,16 @@ def _parse_srcset(entries):
606
634
raise ExtensionError (f'srcset argument { entry !r} is invalid.' )
607
635
return srcset
608
636
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 )
609
647
610
648
def render_figures (code , code_path , output_dir , output_base , context ,
611
649
function_name , config , context_reset = False ,
@@ -730,6 +768,7 @@ def render_figures(code, code_path, output_dir, output_base, context,
730
768
def run (arguments , content , options , state_machine , state , lineno ):
731
769
document = state_machine .document
732
770
config = document .settings .env .config
771
+ env = document .settings .env
733
772
nofigs = 'nofigs' in options
734
773
735
774
if config .plot_srcset and setup .app .builder .name == 'singlehtml' :
@@ -785,6 +824,7 @@ def run(arguments, content, options, state_machine, state, lineno):
785
824
code = Path (source_file_name ).read_text (encoding = 'utf-8' )
786
825
if options ['output-base-name' ]:
787
826
output_base = options ['output-base-name' ]
827
+ check_output_base_name (env , output_base )
788
828
else :
789
829
output_base = os .path .basename (source_file_name )
790
830
else :
@@ -793,6 +833,7 @@ def run(arguments, content, options, state_machine, state, lineno):
793
833
base , ext = os .path .splitext (os .path .basename (source_file_name ))
794
834
if options ['output-base-name' ]:
795
835
output_base = options ['output-base-name' ]
836
+ check_output_base_name (env , output_base )
796
837
else :
797
838
counter = document .attributes .get ('_plot_counter' , 0 ) + 1
798
839
document .attributes ['_plot_counter' ] = counter
0 commit comments