1919
2020"""Config for sphinx."""
2121
22+ import ast
23+ import contextlib
2224import datetime
25+ import importlib
2326from importlib .metadata import version as get_version
2427from inspect import getsource
2528import ntpath
2932from subprocess import run
3033import sys
3134from tempfile import gettempdir
35+ import textwrap
3236from urllib .parse import quote
3337import warnings
3438
@@ -219,6 +223,11 @@ def _dotv(version):
219223autoclass_content = "both"
220224modindex_common_prefix = ["iris" ]
221225
226+ # if geovista is not installed we need to mock the imports so the autodoc build works:
227+ if importlib .util .find_spec ("geovista" ) is None :
228+ autodoc_mock_imports = ["geovista" , "pyvista" ]
229+
230+
222231# -- apidoc extension ---------------------------------------------------------
223232# See https://github.com/sphinx-contrib/apidoc
224233source_code_root = (Path (__file__ ).parents [2 ]).absolute ()
@@ -283,7 +292,15 @@ def _dotv(version):
283292
284293# -- Doctest ("make doctest")--------------------------------------------------
285294
286- doctest_global_setup = "import iris"
295+ doctest_global_setup = """
296+ import iris
297+
298+ # To handle conditional doctest skipping if geovista is not installed:
299+ try:
300+ import geovista as gv
301+ except ImportError:
302+ gv = None
303+ """
287304
288305# -- Options for HTML output --------------------------------------------------
289306
@@ -412,12 +429,14 @@ def reset_modules(gallery_conf, fname):
412429)
413430sys .path .insert (0 , str (reset_modules_dir ))
414431
432+ GALLERY_CODE : str = "../gallery_code"
433+ GALLERY_DIRS : str = "generated/gallery"
415434
416435sphinx_gallery_conf = {
417436 # path to your example scripts
418- "examples_dirs" : [ "../gallery_code" ] ,
437+ "examples_dirs" : GALLERY_CODE ,
419438 # path to where to save gallery generated output
420- "gallery_dirs" : [ "generated/gallery" ] ,
439+ "gallery_dirs" : GALLERY_DIRS ,
421440 # filename pattern for the files in the gallery
422441 "filename_pattern" : "/plot_" ,
423442 # filename pattern to ignore in the gallery
@@ -441,3 +460,133 @@ def reset_modules(gallery_conf, fname):
441460 "section" : "Section %s" ,
442461 "table" : "Table %s" ,
443462}
463+
464+ # ============================================================================
465+ # | Copyright GeoVista |
466+ # | Code from this point unto the termination banner is copyright GeoVista. |
467+ # | Minimal code changes made to make it generic. |
468+ # | |
469+ # | License details can be found at: |
470+ # | https://github.com/bjlittle/geovista/blob/main/LICENSE |
471+ # ============================================================================
472+
473+ # Source: https://github.com/bjlittle/geovista/blob/main/docs/src/conf.py
474+
475+
476+ def _bool_eval (* , arg : str | bool ) -> bool :
477+ """Sanitise to a boolean only configuration."""
478+ if isinstance (arg , str ):
479+ with contextlib .suppress (TypeError ):
480+ arg = ast .literal_eval (arg .capitalize ())
481+
482+ return bool (arg )
483+
484+
485+ def generate_carousel (
486+ app : Sphinx ,
487+ fname : Path ,
488+ ncards : int | None = None ,
489+ margin : int | None = None ,
490+ width : int | None = None ,
491+ ) -> None :
492+ """Generate and write the gallery carousel RST file."""
493+ if ncards is None :
494+ ncards = 3
495+
496+ if margin is None :
497+ margin = 4
498+
499+ if width is None :
500+ width = "25%"
501+
502+ base = Path (app .srcdir , * GALLERY_DIRS .split ("/" ))
503+ cards_by_link = {}
504+
505+ card = r""".. card::
506+ :img-background: {image}
507+ :link: {link}
508+ :link-type: ref
509+ :width: {width}
510+ :margin: {margin}
511+ :class-card: align-self-center
512+ """
513+
514+ # TODO @bjlittle: use Path.walk when python >=3.12
515+ for root , _ , files in os .walk (str (base )):
516+ root = Path (root ) # noqa: PLW2901
517+ if root .name == "images" :
518+ root_relative = root .relative_to (app .srcdir )
519+ link_relative = root .parent .relative_to (app .srcdir )
520+
521+ for file in files :
522+ path = Path (file )
523+ if path .suffix == ".png" :
524+ # generate the card "img-background" filename
525+ image = root_relative / path
526+
527+ # generate the card "link" reference
528+ # remove numeric gallery image index e.g., "001"
529+ parts = path .stem .split ("_" )[:- 1 ]
530+ link = parts [:2 ] + list (link_relative .parts ) + parts [2 :]
531+ link = f"{ '_' .join (link )} .py"
532+
533+ # needed in case a gallery filename has mixed case
534+ link = link .lower ()
535+
536+ kwargs = {
537+ "image" : image ,
538+ "link" : link ,
539+ "width" : width ,
540+ "margin" : margin ,
541+ }
542+
543+ cards_by_link [link ] = card .format (** kwargs )
544+
545+ # sort the cards by their link
546+ cards = [cards_by_link [link ] for link in sorted (cards_by_link .keys ())]
547+ cards = textwrap .indent ("\n " .join (cards ), prefix = " " * 4 )
548+
549+ # now, create the card carousel
550+ carousel = f""".. card-carousel:: { ncards }
551+
552+ { cards }
553+
554+ .. rst-class:: center
555+
556+ :fa:`images` Gallery Carousel
557+
558+ """
559+
560+ # finally, write the rst for the gallery carousel
561+ Path (app .srcdir , fname ).write_text (carousel )
562+
563+
564+ def gallery_carousel (
565+ app : Sphinx ,
566+ env : BuildEnvironment , # noqa: ARG001
567+ docnames : list [str ], # noqa: ARG001
568+ ) -> None :
569+ """Create the gallery carousel."""
570+ # create empty or truncate existing file
571+ fname = Path (app .srcdir , "gallery_carousel.txt" )
572+
573+ with fname .open ("w" ):
574+ pass
575+
576+ if _bool_eval (arg = app .builder .config .plot_gallery ):
577+ # only generate the carousel if we have a gallery
578+ generate_carousel (app , fname )
579+
580+
581+ # ============================================================================
582+ # | END GeoVista copyright |
583+ # ============================================================================
584+
585+
586+ def setup (app : Sphinx ) -> None :
587+ """Configure sphinx application."""
588+ # we require the output of this extension
589+ app .setup_extension ("sphinx_gallery.gen_gallery" )
590+
591+ # register callback to generate gallery carousel
592+ app .connect ("env-before-read-docs" , gallery_carousel )
0 commit comments