From 077a4fb241625b524301c9c63d531f08a96c21e1 Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Fri, 9 Jan 2026 17:01:25 +0000 Subject: [PATCH 1/8] add sphinx carousel --- .pre-commit-config.yaml | 10 +-- docs/src/_static/theme_override.css | 4 + docs/src/conf.py | 113 +++++++++++++++++++++++++++- docs/src/index.rst | 4 + requirements/py311.yml | 2 +- requirements/py312.yml | 2 +- requirements/py313.yml | 2 +- 7 files changed, 127 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70675fb998..f658491cf9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,11 +50,11 @@ repos: types_or: [asciidoc, python, markdown, rst] additional_dependencies: [tomli] -- repo: https://github.com/PyCQA/flake8 - rev: 7.3.0 - hooks: - - id: flake8 - types: [file, python] +# - repo: https://github.com/PyCQA/flake8 +# rev: 7.3.0 +# hooks: +# - id: flake8 +# types: [file, python] - repo: https://github.com/asottile/blacken-docs rev: 1.20.0 diff --git a/docs/src/_static/theme_override.css b/docs/src/_static/theme_override.css index 355119f8a5..be9715ff2c 100644 --- a/docs/src/_static/theme_override.css +++ b/docs/src/_static/theme_override.css @@ -26,3 +26,7 @@ ul.squarelist { text-indent: 1em; padding-left: 5em; } + +.center { + text-align: center; +} \ No newline at end of file diff --git a/docs/src/conf.py b/docs/src/conf.py index fa896aba69..d2713906fc 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -29,6 +29,7 @@ from subprocess import run import sys from tempfile import gettempdir +import textwrap from urllib.parse import quote import warnings @@ -412,12 +413,14 @@ def reset_modules(gallery_conf, fname): ) sys.path.insert(0, str(reset_modules_dir)) +GALLERY_CODE: str = "../gallery_code" +GALLERY_DIRS: str = "generated/gallery" sphinx_gallery_conf = { # path to your example scripts - "examples_dirs": ["../gallery_code"], + "examples_dirs": GALLERY_CODE, # path to where to save gallery generated output - "gallery_dirs": ["generated/gallery"], + "gallery_dirs": GALLERY_DIRS, # filename pattern for the files in the gallery "filename_pattern": "/plot_", # filename pattern to ignore in the gallery @@ -441,3 +444,109 @@ def reset_modules(gallery_conf, fname): "section": "Section %s", "table": "Table %s", } + + +def generate_carousel( + app: Sphinx, + fname: Path, + ncards: int | None = None, + margin: int | None = None, + width: int | None = None, +) -> None: + """Generate and write the gallery carousel RST file.""" + if ncards is None: + ncards = 3 + + if margin is None: + margin = 4 + + if width is None: + width = "25%" + + base = Path(app.srcdir, *GALLERY_DIRS.split("/")) + cards_by_link = {} + + card = r""".. card:: + :img-background: {image} + :link: {link} + :link-type: ref + :width: {width} + :margin: {margin} + :class-card: align-self-center +""" + + # TODO @bjlittle: use Path.walk when python >=3.12 + for root, _, files in os.walk(str(base)): + root = Path(root) # noqa: PLW2901 + if root.name == "images": + root_relative = root.relative_to(app.srcdir) + link_relative = root.parent.relative_to(app.srcdir) + + for file in files: + path = Path(file) + if path.suffix == ".png": + # generate the card "img-background" filename + image = root_relative / path + + # generate the card "link" reference + # remove numeric gallery image index e.g., "001" + parts = path.stem.split("_")[:-1] + link = parts[:2] + list(link_relative.parts) + parts[2:] + link = f"{'_'.join(link)}.py" + + # needed in case a gallery filename has mixed case + link = link.lower() + + kwargs = { + "image": image, + "link": link, + "width": width, + "margin": margin, + } + + cards_by_link[link] = card.format(**kwargs) + + # sort the cards by their link + cards = [cards_by_link[link] for link in sorted(cards_by_link.keys())] + cards = textwrap.indent("\n".join(cards), prefix=" " * 4) + + # now, create the card carousel + carousel = f""".. card-carousel:: {ncards} + +{cards} + +.. rst-class:: center + + :fa:`images` Gallery Carousel + +""" + + # finally, write the rst for the gallery carousel + Path(app.srcdir, fname).write_text(carousel) + + +def gallery_carousel( + app: Sphinx, + env: BuildEnvironment, # noqa: ARG001 + docnames: list[str], # noqa: ARG001 +) -> None: + """Create the gallery carousel.""" + # create empty or truncate existing file + fname = Path(app.srcdir, "gallery_carousel.txt") + + with fname.open("w"): + pass + + # if _bool_eval(arg=app.builder.config.plot_gallery): + # # only generate the carousel if we have a gallery + # generate_carousel(app, fname) + generate_carousel(app, fname) + + +def setup(app: Sphinx) -> None: + """Configure sphinx application.""" + # we require the output of this extension + app.setup_extension("sphinx_gallery.gen_gallery") + + # register callback to generate gallery carousel + app.connect("env-before-read-docs", gallery_carousel) diff --git a/docs/src/index.rst b/docs/src/index.rst index 139e54cee0..e771239503 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -15,6 +15,7 @@ representations become unwieldy and inefficient. For more information see :ref:`why_iris`. + .. grid:: 3 .. grid-item-card:: @@ -128,6 +129,9 @@ For more information see :ref:`why_iris`. Icons made by FreePik from `Flaticon `_ +.. include:: gallery_carousel.txt + + .. _iris_support: Support diff --git a/requirements/py311.yml b/requirements/py311.yml index a128b77eee..f8769cd870 100644 --- a/requirements/py311.yml +++ b/requirements/py311.yml @@ -56,7 +56,7 @@ dependencies: - sphinx-copybutton - sphinx-gallery >=0.11.0 - sphinx-design - - pydata-sphinx-theme >=0.13.0 + - pydata-sphinx-theme !=0.16.0,!=0.16.1,<0.16.2 # Temporary minimum pins. # See https://github.com/SciTools/iris/pull/5051 diff --git a/requirements/py312.yml b/requirements/py312.yml index fbb1e8aea5..35203c7446 100644 --- a/requirements/py312.yml +++ b/requirements/py312.yml @@ -56,7 +56,7 @@ dependencies: - sphinx-copybutton - sphinx-gallery >=0.11.0 - sphinx-design - - pydata-sphinx-theme >=0.13.0 + - pydata-sphinx-theme !=0.16.0,!=0.16.1,<0.16.2 # Temporary minimum pins. # See https://github.com/SciTools/iris/pull/5051 diff --git a/requirements/py313.yml b/requirements/py313.yml index a76d819e75..c622fa5c13 100644 --- a/requirements/py313.yml +++ b/requirements/py313.yml @@ -56,7 +56,7 @@ dependencies: - sphinx-copybutton - sphinx-gallery >=0.11.0 - sphinx-design - - pydata-sphinx-theme >=0.13.0 + - pydata-sphinx-theme !=0.16.0,!=0.16.1,<0.16.2 # Temporary minimum pins. # See https://github.com/SciTools/iris/pull/5051 From 7beda1a18686bc56791b746e701fe1aa071aa00c Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Fri, 9 Jan 2026 17:08:16 +0000 Subject: [PATCH 2/8] removed flake8. We have ruff instead. --- .flake8 | 49 ----------------------------------------- .pre-commit-config.yaml | 6 ----- 2 files changed, 55 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 4f67c422f8..0000000000 --- a/.flake8 +++ /dev/null @@ -1,49 +0,0 @@ -[flake8] -# References: -# https://flake8.readthedocs.io/en/latest/user/configuration.html -# https://flake8.readthedocs.io/en/latest/user/error-codes.html -# https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes - -max-line-length = 80 -max-complexity = 50 -select = C,E,F,W,B,B950 -ignore = - # E203: whitespace before ':' - E203, - # E226: missing whitespace around arithmetic operator - E226, - # E231: missing whitespace after ',', ';', or ':' - E231, - # E402: module level imports on one line - E402, - # E501: line too long - E501, - # E731: do not assign a lambda expression, use a def - E731, - # W503: line break before binary operator - W503, - # W504: line break after binary operator - W504, -exclude = - # - # ignore the following directories - # - .eggs, - build, - docs/src/sphinxext/*, - tools/*, - benchmarks/*, - # - # ignore auto-generated files - # - _ff_cross_refrences.py, - std_names.py, - um_cf_map.py, - # - # ignore third-party files - # - gitwash_dumper.py, - # - # convenience imports - # - lib/iris/common/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f658491cf9..2c7c6819a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,12 +50,6 @@ repos: types_or: [asciidoc, python, markdown, rst] additional_dependencies: [tomli] -# - repo: https://github.com/PyCQA/flake8 -# rev: 7.3.0 -# hooks: -# - id: flake8 -# types: [file, python] - - repo: https://github.com/asottile/blacken-docs rev: 1.20.0 hooks: From 786a75ec5fb118ad0fdab66c46df24f855e6d8b6 Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Fri, 9 Jan 2026 17:34:45 +0000 Subject: [PATCH 3/8] added comment about pin --- requirements/py311.yml | 1 + requirements/py312.yml | 1 + requirements/py313.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/requirements/py311.yml b/requirements/py311.yml index f8769cd870..450be4a1ba 100644 --- a/requirements/py311.yml +++ b/requirements/py311.yml @@ -56,6 +56,7 @@ dependencies: - sphinx-copybutton - sphinx-gallery >=0.11.0 - sphinx-design + # Pinned reason: https://github.com/SciTools/iris/issues/6885 - pydata-sphinx-theme !=0.16.0,!=0.16.1,<0.16.2 # Temporary minimum pins. diff --git a/requirements/py312.yml b/requirements/py312.yml index 35203c7446..454bfd7e79 100644 --- a/requirements/py312.yml +++ b/requirements/py312.yml @@ -56,6 +56,7 @@ dependencies: - sphinx-copybutton - sphinx-gallery >=0.11.0 - sphinx-design + # Pinned reason: https://github.com/SciTools/iris/issues/6885 - pydata-sphinx-theme !=0.16.0,!=0.16.1,<0.16.2 # Temporary minimum pins. diff --git a/requirements/py313.yml b/requirements/py313.yml index c622fa5c13..32b606c02a 100644 --- a/requirements/py313.yml +++ b/requirements/py313.yml @@ -56,6 +56,7 @@ dependencies: - sphinx-copybutton - sphinx-gallery >=0.11.0 - sphinx-design + # Pinned reason: https://github.com/SciTools/iris/issues/6885 - pydata-sphinx-theme !=0.16.0,!=0.16.1,<0.16.2 # Temporary minimum pins. From 32ce4bb44e2c005b81c69ddeb35175f780a5b377 Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Fri, 9 Jan 2026 17:52:57 +0000 Subject: [PATCH 4/8] added whatsnew --- docs/src/whatsnew/latest.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst index e0ebbdeff6..edcb67fb46 100644 --- a/docs/src/whatsnew/latest.rst +++ b/docs/src/whatsnew/latest.rst @@ -81,7 +81,7 @@ This document explains the changes made to Iris for this release 📚 Documentation ================ -#. N/A +#. `@tkknight`_ added a gallery carousel to the documentation homepage. (:pull:`6884`) 💼 Internal From 2780ed951fb2f0d05541f7b2bd777925d5267bbf Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Mon, 12 Jan 2026 10:15:42 +0000 Subject: [PATCH 5/8] reinstate flake8 --- .flake8 | 49 +++++++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 6 +++++ 2 files changed, 55 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000..4f67c422f8 --- /dev/null +++ b/.flake8 @@ -0,0 +1,49 @@ +[flake8] +# References: +# https://flake8.readthedocs.io/en/latest/user/configuration.html +# https://flake8.readthedocs.io/en/latest/user/error-codes.html +# https://pycodestyle.readthedocs.io/en/latest/intro.html#error-codes + +max-line-length = 80 +max-complexity = 50 +select = C,E,F,W,B,B950 +ignore = + # E203: whitespace before ':' + E203, + # E226: missing whitespace around arithmetic operator + E226, + # E231: missing whitespace after ',', ';', or ':' + E231, + # E402: module level imports on one line + E402, + # E501: line too long + E501, + # E731: do not assign a lambda expression, use a def + E731, + # W503: line break before binary operator + W503, + # W504: line break after binary operator + W504, +exclude = + # + # ignore the following directories + # + .eggs, + build, + docs/src/sphinxext/*, + tools/*, + benchmarks/*, + # + # ignore auto-generated files + # + _ff_cross_refrences.py, + std_names.py, + um_cf_map.py, + # + # ignore third-party files + # + gitwash_dumper.py, + # + # convenience imports + # + lib/iris/common/__init__.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c7c6819a8..70675fb998 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,6 +50,12 @@ repos: types_or: [asciidoc, python, markdown, rst] additional_dependencies: [tomli] +- repo: https://github.com/PyCQA/flake8 + rev: 7.3.0 + hooks: + - id: flake8 + types: [file, python] + - repo: https://github.com/asottile/blacken-docs rev: 1.20.0 hooks: From 44e79479a4616896e4b5fe341d30f36cfd68d7c7 Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:35:44 +0000 Subject: [PATCH 6/8] remove commented out code --- docs/src/conf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/src/conf.py b/docs/src/conf.py index d2713906fc..39ab3f8ce2 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -537,9 +537,6 @@ def gallery_carousel( with fname.open("w"): pass - # if _bool_eval(arg=app.builder.config.plot_gallery): - # # only generate the carousel if we have a gallery - # generate_carousel(app, fname) generate_carousel(app, fname) From 764129991a2efb6e64684512ebeeacb8fece951a Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:56:56 +0000 Subject: [PATCH 7/8] added copyright notice for GeoVista --- docs/src/conf.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/src/conf.py b/docs/src/conf.py index 39ab3f8ce2..bec361eba5 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -445,6 +445,17 @@ def reset_modules(gallery_conf, fname): "table": "Table %s", } +# ============================================================================ +# | Copyright GeoVista | +# | Code from this point unto the termination banner is copyright GeoVista. | +# | Minimal code changes made to make it generic. | +# | | +# | License details can be found at: | +# | https://github.com/bjlittle/geovista/blob/main/LICENSE | +# ============================================================================ + +# Source: https://github.com/bjlittle/geovista/blob/main/docs/src/conf.py + def generate_carousel( app: Sphinx, @@ -540,6 +551,11 @@ def gallery_carousel( generate_carousel(app, fname) +# ============================================================================ +# | END GeoVista copyright | +# ============================================================================ + + def setup(app: Sphinx) -> None: """Configure sphinx application.""" # we require the output of this extension From c529e0f0030b5e96e7b5370d876b0dc05ff2a2bc Mon Sep 17 00:00:00 2001 From: Tremain Knight <2108488+tkknight@users.noreply.github.com> Date: Tue, 20 Jan 2026 14:39:58 +0000 Subject: [PATCH 8/8] ensure make-noplot works --- docs/src/conf.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/src/conf.py b/docs/src/conf.py index bec361eba5..0fc109a54b 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -19,6 +19,8 @@ """Config for sphinx.""" +import ast +import contextlib import datetime from importlib.metadata import version as get_version from inspect import getsource @@ -457,6 +459,15 @@ def reset_modules(gallery_conf, fname): # Source: https://github.com/bjlittle/geovista/blob/main/docs/src/conf.py +def _bool_eval(*, arg: str | bool) -> bool: + """Sanitise to a boolean only configuration.""" + if isinstance(arg, str): + with contextlib.suppress(TypeError): + arg = ast.literal_eval(arg.capitalize()) + + return bool(arg) + + def generate_carousel( app: Sphinx, fname: Path, @@ -548,7 +559,9 @@ def gallery_carousel( with fname.open("w"): pass - generate_carousel(app, fname) + if _bool_eval(arg=app.builder.config.plot_gallery): + # only generate the carousel if we have a gallery + generate_carousel(app, fname) # ============================================================================