From b7e25f837414a0c2d7c539c79dc9e806d64fb2be Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Sun, 21 Jul 2024 16:16:38 +1000 Subject: [PATCH 01/18] MNT Bump version (#1350) --- sphinx_gallery/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx_gallery/__init__.py b/sphinx_gallery/__init__.py index f774e775e..776bf26f7 100644 --- a/sphinx_gallery/__init__.py +++ b/sphinx_gallery/__init__.py @@ -8,7 +8,7 @@ # dev versions should have "dev" in them, stable should not. # doc/conf.py makes use of this to set the version drop-down. -__version__ = "0.17.0" +__version__ = "0.18.dev0" def glr_path_static(): From 2ec364c25e98adca76632bdc1394d387753fbbf9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 23:01:12 +0000 Subject: [PATCH 02/18] [pre-commit.ci] pre-commit autoupdate (#1351) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 772406b4a..4ad2cfd1e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.2 + rev: v0.5.4 hooks: - id: ruff-format exclude: plot_syntaxerror From 7ed9930a34c45f19c8f3d4fc3dc9350d2e2066ec Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 23 Jul 2024 16:36:17 +1000 Subject: [PATCH 03/18] Update pyvista in doc CI (#1352) --- .circleci/config.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e4e2a89f3..399418db4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,8 +41,7 @@ jobs: numpy matplotlib seaborn statsmodels pillow joblib \ "sphinx!=7.3.2,!=7.3.3,!=7.3.4,!=7.3.5,!=7.3.6" pytest \ traits memory_profiler "ipython!=8.7.0" plotly graphviz \ - "git+https://github.com/pyvista/pyvista" \ - "docutils>=0.18" imageio pydata-sphinx-theme \ + "pyvista>=0.44.0" "docutils>=0.18" imageio pydata-sphinx-theme \ "jupyterlite-sphinx>=0.8.0,<0.9.0" "jupyterlite-pyodide-kernel<0.1.0" \ libarchive-c "sphinxcontrib-video>=0.2.1rc0" intersphinx_registry pip uninstall -yq vtk # pyvista installs vtk above From 2fc5067f519ee17de066471cf1e1f2eeffb51add Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Thu, 25 Jul 2024 02:10:53 +1000 Subject: [PATCH 04/18] FIX generate zipfiles when index passed by user (#1353) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- sphinx_gallery/gen_gallery.py | 14 ++++++++------ sphinx_gallery/tests/test_full.py | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/sphinx_gallery/gen_gallery.py b/sphinx_gallery/gen_gallery.py index f8b3c7b1d..5237eb6a7 100644 --- a/sphinx_gallery/gen_gallery.py +++ b/sphinx_gallery/gen_gallery.py @@ -608,13 +608,15 @@ def _finish_index_rst( ) indexst += subsections_toctree - if sg_root_index: - # Download examples - if gallery_conf["download_all_examples"]: - download_fhindex = generate_zipfiles( - gallery_dir_abs_path, app.builder.srcdir, gallery_conf - ) + # Always generate download zipfiles, only add to index.rst if required + if gallery_conf["download_all_examples"]: + download_fhindex = generate_zipfiles( + gallery_dir_abs_path, app.builder.srcdir, gallery_conf + ) + if sg_root_index: indexst += download_fhindex + + if sg_root_index: # Signature if app.config.sphinx_gallery_conf["show_signature"]: indexst += SPHX_GLR_SIG diff --git a/sphinx_gallery/tests/test_full.py b/sphinx_gallery/tests/test_full.py index e4cf0df24..4a35f5de8 100644 --- a/sphinx_gallery/tests/test_full.py +++ b/sphinx_gallery/tests/test_full.py @@ -83,7 +83,14 @@ def _sphinx_app(tmpdir_factory, buildername): src_dir = Path(__file__).parent / "tinybuild" def ignore(src, names): - return ("_build", "gen_modules", "auto_examples") + return ( + "_build", + "gen_modules", + "auto_examples", + "auto_examples_README_header", + "auto_examples_rst_index", + "auto_examples_with_rst", + ) shutil.copytree(src_dir, temp_dir, ignore=ignore) # For testing iteration, you can get similar behavior just doing `make` @@ -308,6 +315,13 @@ def test_run_sphinx(sphinx_app): assert re.match(want, warning, re.DOTALL) is not None, warning +def test_user_index_download(sphinx_app): + """Test download zipfiles still generated when user supplies index.rst""" + src_dir = Path(sphinx_app.srcdir) / "auto_examples_rst_index" + assert (src_dir / "auto_examples_rst_index_jupyter.zip").is_file() + assert (src_dir / "auto_examples_rst_index_python.zip").is_file() + + def test_thumbnail_path(sphinx_app, tmpdir): """Test sphinx_gallery_thumbnail_path.""" import numpy as np From 5d337d142e37afa9b5e27c1f2e2b8bb98447d955 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 23:39:20 +0000 Subject: [PATCH 05/18] [pre-commit.ci] pre-commit autoupdate (#1357) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ad2cfd1e..5d0d273ea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.4 + rev: v0.5.5 hooks: - id: ruff-format exclude: plot_syntaxerror From b47628c885c9780f18a83ddd9fe7dad2d27a145c Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 30 Jul 2024 13:11:38 +1000 Subject: [PATCH 06/18] FIX Allow str path minigallery entries when backreferences off (#1355) --- doc/configuration.rst | 45 ++++++++++++++------ sphinx_gallery/directives.py | 26 +++++++---- sphinx_gallery/tests/test_gen_gallery.py | 15 +++++++ sphinx_gallery/tests/testconfs/src/plot_1.py | 4 ++ 4 files changed, 69 insertions(+), 21 deletions(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index e36d6e2ec..02e16bcdb 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -50,7 +50,7 @@ file, inside a ``sphinx_gallery_conf`` dictionary. - ``reference_url``, ``prefer_full_module`` (:ref:`link_to_documentation`) - ``backreferences_dir``, ``doc_module``, ``exclude_implicit_doc``, - and ``inspect_global_variables`` (:ref:`references_to_examples`) + and ``inspect_global_variables`` (:ref:`minigalleries_to_examples`) - ``minigallery_sort_order`` (:ref:`minigallery_order`) **Images and thumbnails** @@ -656,36 +656,50 @@ Add mini-galleries Sphinx-Gallery provides the :class:`sphinx_gallery.directives.MiniGallery` directive so that you can easily add a reduced version of the Gallery to -your Sphinx documentation ``.rst`` files. The mini-gallery directive therefore +your Sphinx documentation ``.rst`` files. The minigallery directive therefore supports passing a list (space separated) of any of the following: -* full qualified name of object (see :ref:`references_to_examples`) +* fully qualified name of object (see :ref:`references_to_examples`) - this + adds all examples where the object was used in the code or referenced in + the example text * pathlike strings to example Python files, including glob-style (see :ref:`file_based_minigalleries`) +To use object names, you must enable backreference generation, see +:ref:`references_to_examples` for details. +If backreference generation is not enabled, object entries to the +:class:`~sphinx_gallery.directives.MiniGallery` directive will be ignored +and all entries will be treated as pathlike strings. + .. _references_to_examples: Add mini-galleries for API documentation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -When documenting a given function/method/attribute/object/class, Sphinx-Gallery -enables you to link to any examples that either: +Sphinx-Gallery can generate minigalleries for objects from specified modules, +consisting of all examples that either: 1. Use the function/method/attribute/object or instantiate the class in the - code (generates *implicit backreferences*). + code (called *implicit backreferences*) or 2. Refer to that function/method/attribute/object/class using sphinx markup ``:func:`` / ``:meth:`` / ``:attr:`` / ``:obj:`` / ``:class:`` in a text block. You can omit this role markup if you have set the `default_role `_ - in your ``conf.py`` to any of these roles (generates *explicit + in your ``conf.py`` to any of these roles (called *explicit backreferences*). -The former is useful for auto-documenting functions/methods/attributes/objects -that are used and classes that are explicitly instantiated. The generated links -are called implicit backreferences. The latter is useful for classes that are -typically implicitly returned rather than explicitly instantiated (e.g., +This allows you to pass a fully qualified name of an object (e.g., function, method, +attribute, class) to the minigallery directive to add a minigallery of all examples +relevant to that object. This can be useful in API documentation. + +**Implicit backreferences** are useful for auto-documenting objects +that are used and classes that are explicitly instantiated, in the code. Any examples +where an object is used in the code are added *implicitly* as backreferences. +**Explicit backreferences** are for objects that are *explicitly* referred to +in an example's text. They are useful for classes that are +typically implicitly returned in the code rather than explicitly instantiated (e.g., :class:`matplotlib.axes.Axes` which is most often instantiated only indirectly -within function calls). Such links are called explicit backreferences. +within function calls).. For example, we can embed a small gallery of all examples that use or refer to :obj:`numpy.exp`, which looks like this: @@ -713,9 +727,14 @@ your Sphinx-Gallery configuration ``conf.py`` file with:: The path you specify in ``backreferences_dir`` (here we choose ``gen_modules/backreferences``) will be populated with -ReStructuredText files. Each .rst file will contain a reduced version of the +ReStructuredText files, with names ending with '.examples'. +Each .rst file will contain a reduced version of the gallery specific to every function/class that is used across all the examples and belonging to the modules listed in ``doc_module``. +Note that backreference files will be generated for all objects. Objects that +are not used in any example will have an empty file to prevent inclusion +errors during autodoc parsing. + ``backreferences_dir`` should be a string or ``pathlib.Path`` object that is **relative** to the ``conf.py`` file, or ``None``. It is ``None`` by default. diff --git a/sphinx_gallery/directives.py b/sphinx_gallery/directives.py index 97c98bc25..4b00ec917 100644 --- a/sphinx_gallery/directives.py +++ b/sphinx_gallery/directives.py @@ -8,6 +8,7 @@ from docutils.parsers.rst import Directive, directives from docutils.parsers.rst.directives import images from sphinx.errors import ExtensionError +from sphinx.util.logging import getLogger from .backreferences import ( THUMBNAIL_PARENT_DIV, @@ -17,6 +18,8 @@ from .gen_rst import extract_intro_and_title from .py_source_parser import split_code_and_text_blocks +logger = getLogger("sphinx-gallery") + class MiniGallery(Directive): """Custom directive to insert a mini-gallery. @@ -67,12 +70,16 @@ def run(self): # Retrieve the backreferences directory config = self.state.document.settings.env.config backreferences_dir = config.sphinx_gallery_conf["backreferences_dir"] + if backreferences_dir is None: + logger.warning( + "'backreferences_dir' config is None, minigallery " + "directive will only add example file paths." + ) # Retrieve source directory src_dir = config.sphinx_gallery_conf["src_dir"] # Parse the argument into the individual objects - obj_list = [] if self.arguments: @@ -100,7 +107,7 @@ def has_backrefs(obj): file_paths = [] for obj in obj_list: - if path := has_backrefs(obj): + if backreferences_dir and (path := has_backrefs(obj)): file_paths.append((obj, path)) elif paths := Path(src_dir).glob(obj): file_paths.extend([(obj, p) for p in paths]) @@ -131,18 +138,21 @@ def has_backrefs(obj): :end-before: thumbnail-parent-div-close""" ) else: + examples_dirs = config.sphinx_gallery_conf["examples_dirs"] + if not isinstance(examples_dirs, list): + examples_dirs = [examples_dirs] + gallery_dirs = config.sphinx_gallery_conf["gallery_dirs"] + if not isinstance(gallery_dirs, list): + gallery_dirs = [gallery_dirs] dirs = [ (e, g) - for e, g in zip( - config.sphinx_gallery_conf["examples_dirs"], - config.sphinx_gallery_conf["gallery_dirs"], - ) + for e, g in zip(examples_dirs, gallery_dirs) if (obj.find(e) != -1) ] if len(dirs) != 1: raise ExtensionError( - f"Error in gallery lookup: input={obj}, matches={dirs}, " - f"examples={config.sphinx_gallery_conf['examples_dirs']}" + f"Error in minigallery file lookup: input={obj}, " + f"matches={dirs}, examples_dirs={examples_dirs}" ) example_dir, target_dir = [Path(src_dir, d) for d in dirs[0]] diff --git a/sphinx_gallery/tests/test_gen_gallery.py b/sphinx_gallery/tests/test_gen_gallery.py index 08765f27a..c048e9d7c 100644 --- a/sphinx_gallery/tests/test_gen_gallery.py +++ b/sphinx_gallery/tests/test_gen_gallery.py @@ -539,6 +539,7 @@ def test_examples_not_expected_to_pass(sphinx_app_wrapper): sphinx_gallery_conf = { 'show_memory': _sg_call_memory_noop, + 'examples_dirs': 'src', 'gallery_dirs': 'ex', }""" ) @@ -585,6 +586,20 @@ def test_backreferences_dir_config(sphinx_app_wrapper): fill_gallery_conf_defaults(app, app.config, check_keys=False) +@pytest.mark.conf_file( + content=""" +sphinx_gallery_conf = { + 'examples_dirs': 'src', + 'gallery_dirs': 'ex', +}""" +) +def test_minigallery_no_backreferences_dir(sphinx_app_wrapper): + """Check warning when no backreferences_dir set but minigallery directive used.""" + sphinx_app = sphinx_app_wrapper.build_sphinx_app() + build_warn = sphinx_app._warning.getvalue() + assert "'backreferences_dir' config is None, minigallery" in build_warn + + @pytest.mark.conf_file( content=""" import pathlib diff --git a/sphinx_gallery/tests/testconfs/src/plot_1.py b/sphinx_gallery/tests/testconfs/src/plot_1.py index e8c020735..f87aecd6e 100644 --- a/sphinx_gallery/tests/testconfs/src/plot_1.py +++ b/sphinx_gallery/tests/testconfs/src/plot_1.py @@ -9,3 +9,7 @@ print("foo") print("bar") print("again") + +# %% +# +# .. minigallery:: src/plot_2.py From 215e039e758546acd9a18e25361ca46f99303140 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Tue, 30 Jul 2024 13:22:42 +1000 Subject: [PATCH 07/18] DOC Minor update to minigallery directive doc (#1358) --- doc/configuration.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index 02e16bcdb..0cc1e3f1d 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -669,7 +669,8 @@ To use object names, you must enable backreference generation, see :ref:`references_to_examples` for details. If backreference generation is not enabled, object entries to the :class:`~sphinx_gallery.directives.MiniGallery` directive will be ignored -and all entries will be treated as pathlike strings. +and all entries will be treated as pathlike strings or glob-style pathlike strings. +See :ref:`file_based_minigalleries` for details. .. _references_to_examples: From 07aca19c1a9044487f75ebbf3ca5994c77459b92 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Thu, 1 Aug 2024 12:16:49 +1000 Subject: [PATCH 08/18] MNT Add warning when 'examples_dirs' and 'gallery_dirs' unequal lengths (#1361) --- sphinx_gallery/gen_gallery.py | 6 ++++++ sphinx_gallery/tests/test_gen_gallery.py | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/sphinx_gallery/gen_gallery.py b/sphinx_gallery/gen_gallery.py index 5237eb6a7..6b3937ff8 100644 --- a/sphinx_gallery/gen_gallery.py +++ b/sphinx_gallery/gen_gallery.py @@ -567,6 +567,12 @@ def _prepare_sphx_glr_dirs(gallery_conf, srcdir): if not isinstance(gallery_dirs, list): gallery_dirs = [gallery_dirs] + if len(examples_dirs) != len(gallery_dirs): + logger.warning( + "'examples_dirs' and 'gallery_dirs' are of different lengths. " + "Surplus entries will be ignored." + ) + if bool(gallery_conf["backreferences_dir"]): backreferences_dir = os.path.join(srcdir, gallery_conf["backreferences_dir"]) os.makedirs(backreferences_dir, exist_ok=True) diff --git a/sphinx_gallery/tests/test_gen_gallery.py b/sphinx_gallery/tests/test_gen_gallery.py index c048e9d7c..f77a91990 100644 --- a/sphinx_gallery/tests/test_gen_gallery.py +++ b/sphinx_gallery/tests/test_gen_gallery.py @@ -100,6 +100,20 @@ def test_no_warning_simple_config(sphinx_app_wrapper): assert build_warn == "" +@pytest.mark.conf_file( + content=""" +sphinx_gallery_conf = { + 'examples_dirs': ['src', 'excess'], + 'gallery_dirs': 'ex', +}""" +) +def test_unequal_examples_gallery_dirs(sphinx_app_wrapper): + """Check warning when 'examples_dirs' and 'gallery_dirs' unequal lengths.""" + sphinx_app = sphinx_app_wrapper.create_sphinx_app() + build_warn = sphinx_app._warning.getvalue() + assert "'examples_dirs' and 'gallery_dirs' are of different lengths" in build_warn + + # This changed from passing the ValueError directly to # raising "sphinx.errors.ConfigError" with "threw an exception" @pytest.mark.parametrize( From dd7eb0d53b2c5c5d60200ddfecef5ffecd24ee0d Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 1 Aug 2024 09:43:38 -0400 Subject: [PATCH 09/18] Fix linking to class attributes with prefer_full_module (#1363) --- sphinx_gallery/docs_resolv.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sphinx_gallery/docs_resolv.py b/sphinx_gallery/docs_resolv.py index 6f0c5c0fa..ae1ce6e70 100644 --- a/sphinx_gallery/docs_resolv.py +++ b/sphinx_gallery/docs_resolv.py @@ -199,14 +199,12 @@ def _get_index_match(self, first, second): def _get_link_type(self, cobj, use_full_module=False): """Get a valid link and type_, False if not found.""" - module_type = "module_short" - if use_full_module: - module_type = "module" + module_type = "module" if use_full_module else "module_short" first, second = cobj[module_type], cobj["name"] match = self._get_index_match(first, second) if match is None and "." in second: # possible class attribute first, second = second.split(".", 1) - first = ".".join([cobj["module_short"], first]) + first = ".".join([cobj[module_type], first]) match = self._get_index_match(first, second) if match is None: link = type_ = None From 89954e8f0ec38f8cb13392a85fb2849b07a75d70 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Fri, 2 Aug 2024 00:10:40 +1000 Subject: [PATCH 10/18] DOC add note on filtering joblib warnings (#1362) Co-authored-by: Eric Larson --- doc/configuration.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index 0cc1e3f1d..c32f6e99b 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -44,6 +44,7 @@ file, inside a ``sphinx_gallery_conf`` dictionary. - ``abort_on_example_error`` (:ref:`abort_on_first`) - ``expected_failing_examples`` (:ref:`dont_fail_exit`) - ``only_warn_on_example_error`` (:ref:`warning_on_error`) +- ``reset_modules`` and ``reset_modules_order`` (:ref:`reset_modules`) - ``parallel`` (:ref:`parallel`) **Cross-referencing** @@ -86,7 +87,6 @@ file, inside a ``sphinx_gallery_conf`` dictionary. **Miscellaneous** -- ``reset_modules`` and ``reset_modules_order`` (:ref:`reset_modules`) - ``recommender`` (:ref:`recommend_examples`) - ``log_level`` (:ref:`log_level`) - ``show_api_usage`` and ``api_usage_ignore`` (:ref:`show_api_usage`) @@ -2155,6 +2155,17 @@ If an ``int``, then that number of jobs will be passed to :class:`joblib.Paralle If ``True``, then the same number of jobs will be used as the ``-j`` flag for Sphinx. +Warnings emitted by :mod:`joblib` during gallery generation (e.g., the ``UserWarning`` +about a `worker restarting `_), +will be captured by Sphinx-Gallery in a similar way to warnings during example +execution. This can be filtered out with ``warnings.filterwarnings`` +(see :ref:`removing_warnings`). This can be particularly important +to do if you have tweaked warning handling in your doc build +to treat warnings as errors, e.g., with a line like +``warnings.filterwarnings("error")``. Note that this +differs from warnings affected by the ``- W`` / ``--fail-on-warning`` ``sphinx-build`` +flag, which converts warnings during documentation building into errors. + .. warning:: Some packages might not play nicely with parallel processing, so this feature is considered **experimental**! From daeb602d4115238c5e29b68093e50bdde2960ea6 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Fri, 2 Aug 2024 00:47:03 +1000 Subject: [PATCH 11/18] Improve minigallery directive path input resolution (#1360) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- pyproject.toml | 1 + sphinx_gallery/directives.py | 77 ++++++++++----- sphinx_gallery/tests/conftest.py | 58 ++++++++++-- sphinx_gallery/tests/test_gen_gallery.py | 113 ++++++++++++++++------- 4 files changed, 181 insertions(+), 68 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a2110d719..b35488030 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -167,4 +167,5 @@ filterwarnings = [ junit_family = "xunit2" markers = [ "conf_file: Configuration file.", + "add_rst: Add rst file to src.", ] diff --git a/sphinx_gallery/directives.py b/sphinx_gallery/directives.py index 4b00ec917..ff5a91aa8 100644 --- a/sphinx_gallery/directives.py +++ b/sphinx_gallery/directives.py @@ -53,6 +53,52 @@ class MiniGallery(Directive): "heading-level": directives.single_char_or_unicode, } + def _get_target_dir(self, config, src_dir, path, obj): + """Get thumbnail target directory, errors when ambiguous/not in example dir.""" + examples_dirs = config.sphinx_gallery_conf["examples_dirs"] + if not isinstance(examples_dirs, list): + examples_dirs = [examples_dirs] + gallery_dirs = config.sphinx_gallery_conf["gallery_dirs"] + if not isinstance(gallery_dirs, list): + gallery_dirs = [gallery_dirs] + + gal_matches = [] + for e, g in zip(examples_dirs, gallery_dirs): + e = Path(src_dir, e).resolve(strict=True) + g = Path(src_dir, g).resolve(strict=True) + try: + gal_matches.append((path.relative_to(e), g)) + except ValueError: + continue + + # `path` inside one `examples_dirs` + if (n_match := len(gal_matches)) == 1: + ex_parents, target_dir = ( + gal_matches[0][0].parents, + gal_matches[0][1], + ) + # `path` inside several `examples_dirs`, take the example dir with + # longest match (shortest parents after relative) + elif n_match > 1: + ex_parents, target_dir = min( + [(match[0].parents, match[1]) for match in gal_matches], + key=lambda x: len(x[0]), + ) + # `path` is not inside a `examples_dirs` + else: + raise ExtensionError( + f"minigallery directive error: path input '{obj}' found file:" + f" '{path}' but this does not live inside any examples_dirs: " + f"{examples_dirs}" + ) + + # Add subgallery path, if present + subdir = "" + if (ex_p := ex_parents[0]) != Path("."): + subdir = ex_p + target_dir = target_dir / subdir + return target_dir + def run(self): """Generate mini-gallery from backreference and example files.""" from .gen_rst import _get_callables @@ -73,7 +119,7 @@ def run(self): if backreferences_dir is None: logger.warning( "'backreferences_dir' config is None, minigallery " - "directive will only add example file paths." + "directive will resolve all inputs as file paths or globs." ) # Retrieve source directory @@ -108,9 +154,9 @@ def has_backrefs(obj): file_paths = [] for obj in obj_list: if backreferences_dir and (path := has_backrefs(obj)): - file_paths.append((obj, path)) + file_paths.append((obj, path.resolve())) elif paths := Path(src_dir).glob(obj): - file_paths.extend([(obj, p) for p in paths]) + file_paths.extend([(obj, p.resolve()) for p in paths]) if len(file_paths) == 0: return [] @@ -126,7 +172,7 @@ def has_backrefs(obj): ) for obj, path in sorted( set(file_paths), - key=((lambda x: sortkey(os.path.abspath(x[-1]))) if sortkey else None), + key=((lambda x: sortkey(str(x[-1]))) if sortkey else None), ): if path.suffix == ".examples": # Insert the backreferences file(s) using the `include` directive. @@ -138,27 +184,8 @@ def has_backrefs(obj): :end-before: thumbnail-parent-div-close""" ) else: - examples_dirs = config.sphinx_gallery_conf["examples_dirs"] - if not isinstance(examples_dirs, list): - examples_dirs = [examples_dirs] - gallery_dirs = config.sphinx_gallery_conf["gallery_dirs"] - if not isinstance(gallery_dirs, list): - gallery_dirs = [gallery_dirs] - dirs = [ - (e, g) - for e, g in zip(examples_dirs, gallery_dirs) - if (obj.find(e) != -1) - ] - if len(dirs) != 1: - raise ExtensionError( - f"Error in minigallery file lookup: input={obj}, " - f"matches={dirs}, examples_dirs={examples_dirs}" - ) - - example_dir, target_dir = [Path(src_dir, d) for d in dirs[0]] - - # finds thumbnails in subdirs - target_dir = target_dir / path.relative_to(example_dir).parent + target_dir = self._get_target_dir(config, src_dir, path, obj) + # Get thumbnail _, script_blocks = split_code_and_text_blocks( str(path), return_node=False ) diff --git a/sphinx_gallery/tests/conftest.py b/sphinx_gallery/tests/conftest.py index 8bd44abce..3e65cbe40 100644 --- a/sphinx_gallery/tests/conftest.py +++ b/sphinx_gallery/tests/conftest.py @@ -1,9 +1,9 @@ """Pytest fixtures.""" -import os import shutil from contextlib import contextmanager from io import StringIO +from pathlib import Path from unittest.mock import Mock import pytest @@ -16,6 +16,23 @@ from sphinx_gallery.scrapers import _import_matplotlib from sphinx_gallery.utils import _get_image +NESTED_PY = """\"\"\" +Header +====== + +Text. +\"\"\" + +a = 1 +""" + +GALLERY_HEADER = """ +Gallery header +============== + +Some text. +""" + def pytest_report_header(config, startdir=None): """Add information to the pytest run header.""" @@ -133,6 +150,16 @@ def conf_file(request): return result +@pytest.fixture +def add_rst(request): + try: + env = request.node.get_closest_marker("add_rst") + except AttributeError: # old pytest + env = request.node.get_marker("add_rst") + file = env.kwargs["file"] if env else "" + return file + + class SphinxAppWrapper: """Wrapper for sphinx.application.Application. @@ -181,13 +208,24 @@ def build_sphinx_app(self, *args, **kwargs): @pytest.fixture -def sphinx_app_wrapper(tmpdir, conf_file, req_mpl, req_pil): - _fixturedir = os.path.join(os.path.dirname(__file__), "testconfs") - srcdir = os.path.join(str(tmpdir), "config_test") +def sphinx_app_wrapper(tmpdir, conf_file, add_rst, req_mpl, req_pil): + _fixturedir = Path(__file__).parent / "testconfs" + srcdir = Path(tmpdir) / "config_test" shutil.copytree(_fixturedir, srcdir) - shutil.copytree( - os.path.join(_fixturedir, "src"), os.path.join(str(tmpdir), "examples") - ) + # Copy files to 'examples/' as well because default `examples_dirs` is + # '../examples' - for tests where we don't update config + shutil.copytree((_fixturedir / "src"), (Path(tmpdir) / "examples")) + if add_rst: + with open((srcdir / "minigallery_test.rst"), "w") as rstfile: + rstfile.write(add_rst) + # Add nested gallery + if "sub_folder/sub_sub_folder" in add_rst: + dir_path = srcdir / "src" / "sub_folder" / "sub_sub_folder" + dir_path.mkdir(parents=True) + with open((dir_path / "plot_nested.py"), "w") as pyfile: + pyfile.write(NESTED_PY) + with open((dir_path / "GALLERY_HEADER.rst"), "w") as rstfile: + rstfile.write(GALLERY_HEADER) base_config = f""" import os @@ -199,14 +237,14 @@ def sphinx_app_wrapper(tmpdir, conf_file, req_mpl, req_pil): # General information about the project. project = 'Sphinx-Gallery '\n\n """ - with open(os.path.join(srcdir, "conf.py"), "w") as conffile: + with open((srcdir / "conf.py"), "w") as conffile: conffile.write(base_config + conf_file["content"]) return SphinxAppWrapper( srcdir, srcdir, - os.path.join(srcdir, "_build"), - os.path.join(srcdir, "_build", "toctree"), + (srcdir / "_build"), + (srcdir / "_build" / "toctree"), "html", warning=StringIO(), status=StringIO(), diff --git a/sphinx_gallery/tests/test_gen_gallery.py b/sphinx_gallery/tests/test_gen_gallery.py index f77a91990..a28f2d3be 100644 --- a/sphinx_gallery/tests/test_gen_gallery.py +++ b/sphinx_gallery/tests/test_gen_gallery.py @@ -273,7 +273,7 @@ def _check_order(sphinx_app, key): Used to test that these keys (in index.rst) appear in a specific order. """ - index_fname = os.path.join(sphinx_app.outdir, "..", "ex", "index.rst") + index_fname = Path(sphinx_app.outdir, "..", "ex", "index.rst") order = list() regex = f".*:{key}=(.):.*" with open(index_fname, "r", encoding="utf-8") as fid: @@ -401,7 +401,7 @@ def test_collect_gallery_files_ignore_pattern(tmpdir, gallery_conf): expected_files = { ap.strpath for ap in abs_paths - if re.search(r"one", os.path.basename(ap.strpath)) is None + if re.search(r"one", Path(ap.strpath).name) is None } assert collected_files == expected_files @@ -426,19 +426,17 @@ def test_binder_copy_files(sphinx_app_wrapper): sphinx_app = sphinx_app_wrapper.create_sphinx_app() gallery_conf = sphinx_app.config.sphinx_gallery_conf # Create requirements file - with open(os.path.join(sphinx_app.srcdir, "requirements.txt"), "w"): + with open(Path(sphinx_app.srcdir, "requirements.txt"), "w"): pass copy_binder_files(sphinx_app, None) for i_file in ["plot_1", "plot_2", "plot_3"]: - assert os.path.exists( - os.path.join( - sphinx_app.outdir, - "ntbk_folder", - gallery_conf["gallery_dirs"][0], - i_file + ".ipynb", - ) - ) + assert Path( + sphinx_app.outdir, + "ntbk_folder", + gallery_conf["gallery_dirs"][0], + i_file + ".ipynb", + ).exists() @pytest.mark.conf_file( @@ -449,7 +447,7 @@ def test_binder_copy_files(sphinx_app_wrapper): }""" ) def test_failing_examples_raise_exception(sphinx_app_wrapper): - example_dir = os.path.join(sphinx_app_wrapper.srcdir, "src") + example_dir = Path(sphinx_app_wrapper.srcdir, "src") bad_line = "print(f'{a[}')" # never closed bracket inside print -> SyntaxError bad_code = f"""\ ''' @@ -465,7 +463,7 @@ def test_failing_examples_raise_exception(sphinx_app_wrapper): {bad_line} """ bad_line_no = bad_code.split("\n").index(bad_line) + 1 - with open(os.path.join(example_dir, "plot_3.py"), "w", encoding="utf-8") as fid: + with open(Path(example_dir, "plot_3.py"), "w", encoding="utf-8") as fid: fid.write(bad_code) with pytest.raises(ExtensionError) as excinfo: sphinx_app_wrapper.build_sphinx_app() @@ -628,6 +626,59 @@ def test_backreferences_dir_pathlib_config(sphinx_app_wrapper): fill_gallery_conf_defaults(app, app.config, check_keys=False) +@pytest.mark.conf_file( + content=""" +sphinx_gallery_conf = { + 'examples_dirs': 'src', + 'gallery_dirs': 'ex', +}""" +) +@pytest.mark.add_rst( + file=""" +Header +====== + +.. minigallery:: index.rst +""" +) +def test_minigallery_not_in_examples_dirs(sphinx_app_wrapper): + """Check error when minigallery directive's path input not in `examples_dirs`.""" + msg = "minigallery directive error: path input 'index.rst'" + with pytest.raises(ExtensionError, match=msg): + sphinx_app_wrapper.build_sphinx_app() + + +@pytest.mark.conf_file( + content=""" +sphinx_gallery_conf = { + 'examples_dirs': ['src', 'src/sub_folder/sub_sub_folder'], + 'gallery_dirs': ['ex', 'ex/sub_folder/sub_sub_folder'], +}""" +) +@pytest.mark.add_rst( + file=""" +Header +====== + +.. minigallery:: src/sub_folder/sub_sub_folder/plot_nested.py +""" +) +def test_minigallery_multi_match(sphinx_app_wrapper): + """Check minigallery directive's path input resolution in nested `examples_dirs`. + + When a examples gallery is nested inside another examples gallery, path inputs + from the nested gallery should resolve to the nested gallery. + """ + sphinx_app = sphinx_app_wrapper.build_sphinx_app() + minigallery_html = Path(sphinx_app.outdir) / "minigallery_test.html" + with open(minigallery_html, "r") as fid: + mg_html = fid.read() + # Check thumbnail correct + assert "_images/sphx_glr_plot_nested_thumb.png" in mg_html + # Check href correct + assert "sphx-glr-ex-sub-folder-sub-sub-folder-plot-nested-py" in mg_html + + def test_write_computation_times_noop(sphinx_app_wrapper): app = sphinx_app_wrapper.create_sphinx_app() write_computation_times(app.config.sphinx_gallery_conf, None, []) @@ -687,14 +738,12 @@ def test_create_jupyterlite_contents(sphinx_app_wrapper): create_jupyterlite_contents(sphinx_app, exception=None) for i_file in ["plot_1", "plot_2", "plot_3"]: - assert os.path.exists( - os.path.join( - sphinx_app.srcdir, - "jupyterlite_contents", - gallery_conf["gallery_dirs"][0], - i_file + ".ipynb", - ) - ) + assert Path( + sphinx_app.srcdir, + "jupyterlite_contents", + gallery_conf["gallery_dirs"][0], + i_file + ".ipynb", + ).exists() @pytest.mark.conf_file( @@ -717,14 +766,12 @@ def test_create_jupyterlite_contents_non_default_contents(sphinx_app_wrapper): create_jupyterlite_contents(sphinx_app, exception=None) for i_file in ["plot_1", "plot_2", "plot_3"]: - assert os.path.exists( - os.path.join( - sphinx_app.srcdir, - "this_is_the_contents_dir", - gallery_conf["gallery_dirs"][0], - i_file + ".ipynb", - ) - ) + assert Path( + sphinx_app.srcdir, + "this_is_the_contents_dir", + gallery_conf["gallery_dirs"][0], + i_file + ".ipynb", + ).exists() @pytest.mark.conf_file( @@ -743,7 +790,7 @@ def test_create_jupyterlite_contents_without_jupyterlite_sphinx_loaded( sphinx_app = sphinx_app_wrapper.create_sphinx_app() create_jupyterlite_contents(sphinx_app, exception=None) - assert not os.path.exists(os.path.join(sphinx_app.srcdir, "jupyterlite_contents")) + assert not Path(sphinx_app.srcdir, "jupyterlite_contents").exists() @pytest.mark.conf_file( @@ -768,7 +815,7 @@ def test_create_jupyterlite_contents_with_jupyterlite_disabled_via_config( sphinx_app = sphinx_app_wrapper.create_sphinx_app() create_jupyterlite_contents(sphinx_app, exception=None) - assert not os.path.exists(os.path.join(sphinx_app.outdir, "jupyterlite_contents")) + assert not Path(sphinx_app.outdir, "jupyterlite_contents").exists() @pytest.mark.conf_file( @@ -802,13 +849,13 @@ def test_create_jupyterlite_contents_with_modification(sphinx_app_wrapper): create_jupyterlite_contents(sphinx_app, exception=None) for i_file in ["plot_1", "plot_2", "plot_3"]: - notebook_filename = os.path.join( + notebook_filename = Path( sphinx_app.srcdir, "jupyterlite_contents", gallery_conf["gallery_dirs"][0], i_file + ".ipynb", ) - assert os.path.exists(notebook_filename) + assert notebook_filename.exists() with open(notebook_filename) as f: notebook_content = json.load(f) From e7eb5ec0677da77537b380b121cdee4c7c183e43 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Sat, 3 Aug 2024 10:43:54 +1000 Subject: [PATCH 12/18] MNT Change mark and fixture names for adding files (#1365) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- pyproject.toml | 2 +- sphinx_gallery/tests/conftest.py | 14 ++--- sphinx_gallery/tests/test_gen_gallery.py | 70 ++++++++++++------------ sphinx_gallery/tests/test_load_style.py | 2 +- 4 files changed, 43 insertions(+), 45 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b35488030..60e5a80f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -166,6 +166,6 @@ filterwarnings = [ ] junit_family = "xunit2" markers = [ - "conf_file: Configuration file.", + "add_conf: Configuration file.", "add_rst: Add rst file to src.", ] diff --git a/sphinx_gallery/tests/conftest.py b/sphinx_gallery/tests/conftest.py index 3e65cbe40..d9c92f0a6 100644 --- a/sphinx_gallery/tests/conftest.py +++ b/sphinx_gallery/tests/conftest.py @@ -137,9 +137,9 @@ def req_pil(): @pytest.fixture def conf_file(request): try: - env = request.node.get_closest_marker("conf_file") + env = request.node.get_closest_marker("add_conf") except AttributeError: # old pytest - env = request.node.get_marker("conf_file") + env = request.node.get_marker("add_conf") kwargs = env.kwargs if env else {} result = { "content": "", @@ -151,7 +151,7 @@ def conf_file(request): @pytest.fixture -def add_rst(request): +def rst_file(request): try: env = request.node.get_closest_marker("add_rst") except AttributeError: # old pytest @@ -208,18 +208,18 @@ def build_sphinx_app(self, *args, **kwargs): @pytest.fixture -def sphinx_app_wrapper(tmpdir, conf_file, add_rst, req_mpl, req_pil): +def sphinx_app_wrapper(tmpdir, conf_file, rst_file, req_mpl, req_pil): _fixturedir = Path(__file__).parent / "testconfs" srcdir = Path(tmpdir) / "config_test" shutil.copytree(_fixturedir, srcdir) # Copy files to 'examples/' as well because default `examples_dirs` is # '../examples' - for tests where we don't update config shutil.copytree((_fixturedir / "src"), (Path(tmpdir) / "examples")) - if add_rst: + if rst_file: with open((srcdir / "minigallery_test.rst"), "w") as rstfile: - rstfile.write(add_rst) + rstfile.write(rst_file) # Add nested gallery - if "sub_folder/sub_sub_folder" in add_rst: + if "sub_folder/sub_sub_folder" in rst_file: dir_path = srcdir / "src" / "sub_folder" / "sub_sub_folder" dir_path.mkdir(parents=True) with open((dir_path / "plot_nested.py"), "w") as pyfile: diff --git a/sphinx_gallery/tests/test_gen_gallery.py b/sphinx_gallery/tests/test_gen_gallery.py index a28f2d3be..ea2c41d82 100644 --- a/sphinx_gallery/tests/test_gen_gallery.py +++ b/sphinx_gallery/tests/test_gen_gallery.py @@ -80,7 +80,7 @@ def test_serializable(sphinx_app_wrapper): assert not bad, f"Non-serializable values found:\n{bad}" -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -100,7 +100,7 @@ def test_no_warning_simple_config(sphinx_app_wrapper): assert build_warn == "" -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': ['src', 'excess'], @@ -123,7 +123,7 @@ def test_unequal_examples_gallery_dirs(sphinx_app_wrapper): ConfigError, "Unknown string option for reset_modules", id="Resetter unknown", - marks=pytest.mark.conf_file( + marks=pytest.mark.add_conf( content="sphinx_gallery_conf={'reset_modules': ('f',)}" ), ), @@ -131,7 +131,7 @@ def test_unequal_examples_gallery_dirs(sphinx_app_wrapper): ConfigError, "reset_modules.* must be callable", id="Resetter not callable", - marks=pytest.mark.conf_file( + marks=pytest.mark.add_conf( content="sphinx_gallery_conf={'reset_modules': (1.,),}" ), ), @@ -149,7 +149,7 @@ def test_bad_reset(sphinx_app_wrapper, err_class, err_match): ConfigError, "'reset_modules_order' config allowed types", id="Resetter unknown", - marks=pytest.mark.conf_file( + marks=pytest.mark.add_conf( content=("sphinx_gallery_conf=" "{'reset_modules_order': 1,}") ), ), @@ -157,7 +157,7 @@ def test_bad_reset(sphinx_app_wrapper, err_class, err_match): ConfigError, "reset_modules_order must be in", id="reset_modules_order not valid", - marks=pytest.mark.conf_file( + marks=pytest.mark.add_conf( content=("sphinx_gallery_conf=" "{'reset_modules_order': 'invalid',}") ), ), @@ -175,15 +175,13 @@ def test_bad_reset_modules_order(sphinx_app_wrapper, err_class, err_match): ConfigError, "Unknown css", id="CSS str error", - marks=pytest.mark.conf_file( - content="sphinx_gallery_conf={'css': ('foo',)}" - ), + marks=pytest.mark.add_conf(content="sphinx_gallery_conf={'css': ('foo',)}"), ), pytest.param( ConfigError, "config allowed types:", id="CSS type error", - marks=pytest.mark.conf_file(content="sphinx_gallery_conf={'css': 1.}"), + marks=pytest.mark.add_conf(content="sphinx_gallery_conf={'css': 1.}"), ), ], ) @@ -203,7 +201,7 @@ def test_bad_api(): _fill_gallery_conf_defaults(sphinx_gallery_conf) -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'backreferences_dir': os.path.join('gen_modules', 'backreferences'), @@ -284,7 +282,7 @@ def _check_order(sphinx_app, key): assert order == [1, 2, 3] -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -297,7 +295,7 @@ def test_example_sorting_default(sphinx_app_wrapper): _check_order(sphinx_app, "lines") -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -311,7 +309,7 @@ def test_example_sorting_filesize(sphinx_app_wrapper): _check_order(sphinx_app, "filesize") -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -325,7 +323,7 @@ def test_example_sorting_filename(sphinx_app_wrapper): _check_order(sphinx_app, "filename") -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -407,7 +405,7 @@ def test_collect_gallery_files_ignore_pattern(tmpdir, gallery_conf): assert collected_files == expected_files -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'backreferences_dir' : os.path.join('modules', 'gen'), @@ -439,7 +437,7 @@ def test_binder_copy_files(sphinx_app_wrapper): ).exists() -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -474,7 +472,7 @@ def test_failing_examples_raise_exception(sphinx_app_wrapper): assert bad_line in tb -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -490,7 +488,7 @@ def test_expected_failing_examples_were_executed(sphinx_app_wrapper): sphinx_app_wrapper.build_sphinx_app() -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -510,7 +508,7 @@ def test_only_warn_on_example_error(sphinx_app_wrapper): assert "WARNING: Here is a summary of the problems" in build_warn -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -530,7 +528,7 @@ def test_only_warn_on_example_error_sphinx_warning(sphinx_app_wrapper): assert "plot_3.py unexpectedly failed to execute" in exc -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -545,7 +543,7 @@ def test_examples_not_expected_to_pass(sphinx_app_wrapper): assert "expected to fail, but not failing" in exc -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" from sphinx_gallery.gen_rst import _sg_call_memory_noop @@ -566,13 +564,13 @@ def test_show_memory_callable(sphinx_app_wrapper): [ pytest.param( id="first notebook cell", - marks=pytest.mark.conf_file( + marks=pytest.mark.add_conf( content="""sphinx_gallery_conf = {'first_notebook_cell': 2,}""" ), ), pytest.param( id="last notebook cell", - marks=pytest.mark.conf_file( + marks=pytest.mark.add_conf( content="""sphinx_gallery_conf = {'last_notebook_cell': 2,}""" ), ), @@ -585,7 +583,7 @@ def test_notebook_cell_config(sphinx_app_wrapper): fill_gallery_conf_defaults(app, app.config, check_keys=False) -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'backreferences_dir': False, @@ -598,7 +596,7 @@ def test_backreferences_dir_config(sphinx_app_wrapper): fill_gallery_conf_defaults(app, app.config, check_keys=False) -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -612,7 +610,7 @@ def test_minigallery_no_backreferences_dir(sphinx_app_wrapper): assert "'backreferences_dir' config is None, minigallery" in build_warn -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" import pathlib @@ -626,7 +624,7 @@ def test_backreferences_dir_pathlib_config(sphinx_app_wrapper): fill_gallery_conf_defaults(app, app.config, check_keys=False) -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': 'src', @@ -648,7 +646,7 @@ def test_minigallery_not_in_examples_dirs(sphinx_app_wrapper): sphinx_app_wrapper.build_sphinx_app() -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'examples_dirs': ['src', 'src/sub_folder/sub_sub_folder'], @@ -688,7 +686,7 @@ def test_write_api_usage_noop(sphinx_app_wrapper): write_api_entry_usage(sphinx_app_wrapper.create_sphinx_app(), list(), None) -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'pypandoc': ['list',], @@ -704,7 +702,7 @@ def test_pypandoc_config_list(sphinx_app_wrapper): fill_gallery_conf_defaults(app, app.config, check_keys=False) -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'pypandoc': {'bad key': 1}, @@ -719,7 +717,7 @@ def test_pypandoc_config_keys(sphinx_app_wrapper): fill_gallery_conf_defaults(app, app.config, check_keys=False) -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" extensions += ['jupyterlite_sphinx'] @@ -746,7 +744,7 @@ def test_create_jupyterlite_contents(sphinx_app_wrapper): ).exists() -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" extensions += ['jupyterlite_sphinx'] @@ -774,7 +772,7 @@ def test_create_jupyterlite_contents_non_default_contents(sphinx_app_wrapper): ).exists() -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" sphinx_gallery_conf = { 'backreferences_dir' : os.path.join('modules', 'gen'), @@ -793,7 +791,7 @@ def test_create_jupyterlite_contents_without_jupyterlite_sphinx_loaded( assert not Path(sphinx_app.srcdir, "jupyterlite_contents").exists() -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" extensions += ['jupyterlite_sphinx'] @@ -818,7 +816,7 @@ def test_create_jupyterlite_contents_with_jupyterlite_disabled_via_config( assert not Path(sphinx_app.outdir, "jupyterlite_contents").exists() -@pytest.mark.conf_file( +@pytest.mark.add_conf( content=""" extensions += ['jupyterlite_sphinx'] diff --git a/sphinx_gallery/tests/test_load_style.py b/sphinx_gallery/tests/test_load_style.py index bd25186d2..b4f816b57 100644 --- a/sphinx_gallery/tests/test_load_style.py +++ b/sphinx_gallery/tests/test_load_style.py @@ -5,7 +5,7 @@ import pytest -@pytest.mark.conf_file(extensions=["sphinx_gallery.load_style"]) +@pytest.mark.add_conf(extensions=["sphinx_gallery.load_style"]) def test_load_style(sphinx_app_wrapper): """Testing that style loads properly.""" sphinx_app = sphinx_app_wrapper.build_sphinx_app() From 2d8401cbc7f21843d2796a621bb079df62673202 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 3 Aug 2024 09:47:44 -0400 Subject: [PATCH 13/18] Obey prefer_full_module setting when finding backreferences (#1364) --- sphinx_gallery/_dummy/__init__.py | 14 +++ sphinx_gallery/_dummy/nested.py | 11 ++ sphinx_gallery/backreferences.py | 14 +-- sphinx_gallery/gen_rst.py | 10 +- sphinx_gallery/tests/conftest.py | 5 +- sphinx_gallery/tests/test_backreferences.py | 13 ++- sphinx_gallery/tests/test_full.py | 103 +++++++++++++++--- sphinx_gallery/tests/tinybuild/doc/conf.py | 1 + sphinx_gallery/tests/tinybuild/doc/index.rst | 2 + .../examples/plot_numpy_matplotlib.py | 7 +- 10 files changed, 148 insertions(+), 32 deletions(-) create mode 100644 sphinx_gallery/_dummy/__init__.py create mode 100644 sphinx_gallery/_dummy/nested.py diff --git a/sphinx_gallery/_dummy/__init__.py b/sphinx_gallery/_dummy/__init__.py new file mode 100644 index 000000000..422465a2c --- /dev/null +++ b/sphinx_gallery/_dummy/__init__.py @@ -0,0 +1,14 @@ +from .nested import NestedDummyClass # noqa: F401 + + +class DummyClass: + """Dummy class for testing method resolution.""" + + def run(self): + """Do nothing.""" + pass + + @property + def prop(self): + """Property.""" + return "Property" diff --git a/sphinx_gallery/_dummy/nested.py b/sphinx_gallery/_dummy/nested.py new file mode 100644 index 000000000..7f1d3f007 --- /dev/null +++ b/sphinx_gallery/_dummy/nested.py @@ -0,0 +1,11 @@ +class NestedDummyClass: + """Nested dummy class for testing method resolution.""" + + def run(self): + """Do nothing.""" + pass + + @property + def prop(self): + """Property.""" + return "Property" diff --git a/sphinx_gallery/backreferences.py b/sphinx_gallery/backreferences.py index e955ec616..f99202dbf 100644 --- a/sphinx_gallery/backreferences.py +++ b/sphinx_gallery/backreferences.py @@ -16,6 +16,7 @@ import sphinx.util from sphinx.errors import ExtensionError +from ._dummy import DummyClass # noqa: F401 from .scrapers import _find_image_ext from .utils import _W_KW, _replace_md5 @@ -37,19 +38,6 @@ """ -class DummyClass: - """Dummy class for testing method resolution.""" - - def run(self): - """Do nothing.""" - pass - - @property - def prop(self): - """Property.""" - return "Property" - - class NameFinder(ast.NodeVisitor): """Finds the longest form of variable names and their imports in code. diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index 02fc269b6..cb9372c72 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -1234,8 +1234,16 @@ def _get_backreferences(gallery_conf, script_vars, script_blocks, node, target_f if example_code_obj: _write_code_obj(target_file, example_code_obj) exclude_regex = gallery_conf["exclude_implicit_doc_regex"] + + def _normalize_name(cobj): + full_name = "{module}.{name}".format(**cobj) + for pattern in gallery_conf["prefer_full_module"]: + if re.search(pattern, full_name): + return full_name + return "{module_short}.{name}".format(**cobj) + backrefs = { - "{module_short}.{name}".format(**cobj) + _normalize_name(cobj) for cobjs in example_code_obj.values() for cobj in cobjs if cobj["module"].startswith(gallery_conf["doc_module"]) diff --git a/sphinx_gallery/tests/conftest.py b/sphinx_gallery/tests/conftest.py index d9c92f0a6..4087a4177 100644 --- a/sphinx_gallery/tests/conftest.py +++ b/sphinx_gallery/tests/conftest.py @@ -88,9 +88,12 @@ def unicode_sample(tmpdir): from sphinx_gallery.back_references import identify_names identify_names -from sphinx_gallery.back_references import DummyClass +from sphinx_gallery._dummy import DummyClass DummyClass().prop +from sphinx_gallery._dummy.nested import NestedDummyClass +NestedDummyClass().prop + import matplotlib.pyplot as plt _ = plt.figure() diff --git a/sphinx_gallery/tests/test_backreferences.py b/sphinx_gallery/tests/test_backreferences.py index 634c5d22a..5b041e94c 100644 --- a/sphinx_gallery/tests/test_backreferences.py +++ b/sphinx_gallery/tests/test_backreferences.py @@ -128,8 +128,17 @@ def test_identify_names(unicode_sample, gallery_conf): "DummyClass": [ { "name": "DummyClass", - "module": "sphinx_gallery.back_references", - "module_short": "sphinx_gallery.back_references", + "module": "sphinx_gallery._dummy", + "module_short": "sphinx_gallery._dummy", + "is_class": False, + "is_explicit": False, + } + ], + "NestedDummyClass": [ + { + "name": "NestedDummyClass", + "module": "sphinx_gallery._dummy.nested", + "module_short": "sphinx_gallery._dummy", "is_class": False, "is_explicit": False, } diff --git a/sphinx_gallery/tests/test_full.py b/sphinx_gallery/tests/test_full.py index 4a35f5de8..8aa519638 100644 --- a/sphinx_gallery/tests/test_full.py +++ b/sphinx_gallery/tests/test_full.py @@ -54,7 +54,7 @@ # (examples + examples_rst_index + examples_with_rst + examples_README_header + root-level) N_EXECUTE = 2 + 3 + 1 + 1 + 1 # gen_modules + sg_api_usage + doc/index.rst + minigallery.rst -N_OTHER = 9 + 1 + 1 + 1 + 1 +N_OTHER = 11 + 1 + 1 + 1 + 1 N_RST = N_EXAMPLES + N_PASS + N_INDEX + N_EXECUTE + N_OTHER N_RST = f"({N_RST}|{N_RST - 1}|{N_RST - 2})" # AppVeyor weirdness @@ -548,25 +548,49 @@ def test_embed_links_and_styles(sphinx_app): # issue 617 (regex '-'s) # instance dummy_class_inst = re.search( - r'sphinx_gallery.backreferences.html#sphinx[_,-]gallery[.,-]backreferences[.,-][D,d]ummy[C,c]lass" title="sphinx_gallery.backreferences.DummyClass" class="sphx-glr-backref-module-sphinx_gallery-backreferences sphx-glr-backref-type-py-class sphx-glr-backref-instance">dc', # noqa: E501 + r'sphinx_gallery._dummy.html#sphinx[_-]gallery[.-]_dummy[.-][Dd]ummy[Cc]lass" title="sphinx_gallery._dummy.DummyClass" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-class sphx-glr-backref-instance">dc', # noqa: E501 lines, ) assert dummy_class_inst is not None # class dummy_class_class = re.search( - r'sphinx_gallery.backreferences.html#sphinx[_,-]gallery[.,-]backreferences[.,-][D,d]ummy[C,c]lass" title="sphinx_gallery.backreferences.DummyClass" class="sphx-glr-backref-module-sphinx_gallery-backreferences sphx-glr-backref-type-py-class">sphinx_gallery.backreferences.DummyClass', # noqa: E501 + r'sphinx_gallery._dummy.html#sphinx[_-]gallery[.-]_dummy[.-][Dd]ummy[Cc]lass" title="sphinx_gallery._dummy.DummyClass" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-class">sphinx_gallery._dummy.DummyClass', # noqa: E501 lines, ) assert dummy_class_class is not None # method dummy_class_meth = re.search( - r'sphinx_gallery.backreferences.html#sphinx[_,-]gallery[.,-]backreferences[.,-][D,d]ummy[C,c]lass[.,-]run" title="sphinx_gallery.backreferences.DummyClass.run" class="sphx-glr-backref-module-sphinx_gallery-backreferences sphx-glr-backref-type-py-method">dc.run', # noqa: E501 + r'sphinx_gallery._dummy.html#sphinx[_-]gallery[.-]_dummy[.-][Dd]ummy[Cc]lass[.-]run" title="sphinx_gallery._dummy.DummyClass.run" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-method">dc.run', # noqa: E501 lines, ) assert dummy_class_meth is not None # property (Sphinx 2+ calls it a method rather than attribute, so regex) dummy_class_prop = re.compile( - r'sphinx_gallery.backreferences.html#sphinx[_,-]gallery[.,-]backreferences[.,-][D,d]ummy[C,c]lass[.,-]prop" title="sphinx_gallery.backreferences.DummyClass.prop" class="sphx-glr-backref-module-sphinx_gallery-backreferences sphx-glr-backref-type-py-(attribute|method|property)">dc.prop' + r'sphinx_gallery._dummy.html#sphinx[_-]gallery[.-]_dummy[.-][Dd]ummy[Cc]lass[.-]prop" title="sphinx_gallery._dummy.DummyClass.prop" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-(attribute|method|property)">dc.prop' + ) # noqa: E501 + assert dummy_class_prop.search(lines) is not None + # gh-1364: methods of nested classes in the module currently being documented + # instance + dummy_class_inst = re.search( + r'sphinx_gallery._dummy.nested.html#sphinx[_-]gallery[.-]_dummy[.-]nested[.-][Nn]ested[Dd]ummy[Cc]lass" title="sphinx_gallery._dummy.NestedDummyClass" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-class sphx-glr-backref-instance">ndc', # noqa: E501 + lines, + ) + assert dummy_class_inst is not None + # class + dummy_class_class = re.search( + r'sphinx_gallery._dummy.nested.html#sphinx[_-]gallery[.-]_dummy[.-]nested[.-][Nn]ested[Dd]ummy[Cc]lass" title="sphinx_gallery._dummy.NestedDummyClass" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-class">sphinx_gallery._dummy.nested.NestedDummyClass', # noqa: E501 + lines, + ) + assert dummy_class_class is not None + # method + dummy_class_meth = re.search( + r'sphinx_gallery._dummy.nested.html#sphinx[_-]gallery[.-]_dummy[.-]nested[.-][Nn]ested[Dd]ummy[Cc]lass[.-]run" title="sphinx_gallery._dummy.NestedDummyClass.run" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-method">ndc.run', # noqa: E501 + lines, + ) + assert dummy_class_meth is not None + # property (Sphinx 2+ calls it a method rather than attribute, so regex) + dummy_class_prop = re.compile( + r'sphinx_gallery._dummy.nested.html#sphinx[_-]gallery[.-]_dummy[.-]nested[.-][Nn]ested[Dd]ummy[Cc]lass[.-]prop" title="sphinx_gallery._dummy.NestedDummyClass.prop" class="sphx-glr-backref-module-sphinx_gallery-_dummy sphx-glr-backref-type-py-(attribute|method|property)">ndc.prop' ) # noqa: E501 assert dummy_class_prop.search(lines) is not None @@ -666,19 +690,70 @@ def test_backreferences_examples_html(sphinx_app): regex = re.compile(r'`_. :template: module.rst backreferences + _dummy + _dummy.nested docs_resolv downloads gen_gallery diff --git a/sphinx_gallery/tests/tinybuild/examples/plot_numpy_matplotlib.py b/sphinx_gallery/tests/tinybuild/examples/plot_numpy_matplotlib.py index d68134bc4..b5bf7c675 100644 --- a/sphinx_gallery/tests/tinybuild/examples/plot_numpy_matplotlib.py +++ b/sphinx_gallery/tests/tinybuild/examples/plot_numpy_matplotlib.py @@ -20,6 +20,7 @@ from matplotlib.figure import Figure import sphinx_gallery.backreferences +import sphinx_gallery._dummy.nested from sphinx_gallery.py_source_parser import Block t = np.arange(N) / float(N) @@ -47,6 +48,10 @@ sphinx_gallery.backreferences._make_ref_regex(), ) # 583: methods don't link properly -dc = sphinx_gallery.backreferences.DummyClass() +dc = sphinx_gallery._dummy.DummyClass() dc.run() print(dc.prop) +# 1364: nested methods don't link properly +ndc = sphinx_gallery._dummy.nested.NestedDummyClass() +ndc.run() +print(ndc.prop) From 36a9ee95d7383d6ae94df380b52f38e56c55bdf6 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sat, 3 Aug 2024 15:53:27 +0200 Subject: [PATCH 14/18] ENH: Improve _sanitize_rst (#1366) --- sphinx_gallery/gen_rst.py | 6 +++++- sphinx_gallery/tests/test_backreferences.py | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index cb9372c72..b1154e2c4 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -284,8 +284,12 @@ def _sanitize_rst(string): # ``whatever thing`` --> whatever thing p = r"(\s|^)`" string = re.sub(p + r"`([^`]+)`" + e, r"\1\2\3", string) + # `~mymodule.MyClass` --> MyClass + string = re.sub(p + r"~([^`]+)" + e, _regroup, string) + # `whatever thing` --> whatever thing - string = re.sub(p + r"([^`]+)" + e, r"\1\2\3", string) + # `.MyClass` --> MyClass + string = re.sub(p + r"\.?([^`]+)" + e, r"\1\2\3", string) # **string** --> string string = re.sub(r"\*\*([^\*]*)\*\*", r"\1", string) diff --git a/sphinx_gallery/tests/test_backreferences.py b/sphinx_gallery/tests/test_backreferences.py index 5b041e94c..ae2190bfc 100644 --- a/sphinx_gallery/tests/test_backreferences.py +++ b/sphinx_gallery/tests/test_backreferences.py @@ -51,6 +51,11 @@ "this and that; and these things and those things", False, ), + ( + "See `.MyClass` and `~.MyClass.close`", + "See MyClass and close", + False, + ), ], ) def test_thumbnail_div(content, tooltip, is_backref): From b1ff40af96d0fbe2b381815fed9317571ec8bc52 Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Sat, 3 Aug 2024 23:58:21 +1000 Subject: [PATCH 15/18] DOC Improve doc about joblib warnings (#1367) --- doc/configuration.rst | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/doc/configuration.rst b/doc/configuration.rst index c32f6e99b..8d3762d36 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -2155,16 +2155,19 @@ If an ``int``, then that number of jobs will be passed to :class:`joblib.Paralle If ``True``, then the same number of jobs will be used as the ``-j`` flag for Sphinx. -Warnings emitted by :mod:`joblib` during gallery generation (e.g., the ``UserWarning`` -about a `worker restarting `_), -will be captured by Sphinx-Gallery in a similar way to warnings during example -execution. This can be filtered out with ``warnings.filterwarnings`` -(see :ref:`removing_warnings`). This can be particularly important -to do if you have tweaked warning handling in your doc build +Warnings emitted by :mod:`joblib` during documentation building (e.g., the +``UserWarning`` about a +`worker restarting `_) are emitted +during gallery generation at the same time as warnings from example +code execution. These can be filtered out with +``warnings.filterwarnings`` (see :ref:`removing_warnings`). This is particularly +important to do if you have tweaked warning handling in your documentation build to treat warnings as errors, e.g., with a line like -``warnings.filterwarnings("error")``. Note that this -differs from warnings affected by the ``- W`` / ``--fail-on-warning`` ``sphinx-build`` -flag, which converts warnings during documentation building into errors. +``warnings.filterwarnings("error)`` which converts all warnings into errors. In +this case, if joblib emits a warning during build of an example, this example will fail +unexpectedly unless they are filtered out. Note that this differs from the warnings +affected by the ``- W`` / ``--fail-on-warning`` ``sphinx-build`` flag, which converts +Sphinx warnings during documentation building into errors. .. warning:: Some packages might not play nicely with parallel processing, so this feature From 707899331b1f4b6050fc85f498bcf3abd3c2267f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 22:01:05 +0000 Subject: [PATCH 16/18] [pre-commit.ci] pre-commit autoupdate (#1368) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d0d273ea..f97cc15dc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.5 + rev: v0.5.6 hooks: - id: ruff-format exclude: plot_syntaxerror @@ -35,7 +35,7 @@ repos: - id: sphinx-lint - repo: https://github.com/tox-dev/pyproject-fmt - rev: 2.1.4 + rev: 2.2.1 hooks: - id: pyproject-fmt additional_dependencies: [tox] From 3fce549803d95df0b6c413c6b7d417edff4572fb Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 6 Aug 2024 19:53:28 -0400 Subject: [PATCH 17/18] FIX: Fix stability of stored compiled regex (#1369) --- sphinx_gallery/gen_gallery.py | 6 ++++-- sphinx_gallery/gen_rst.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sphinx_gallery/gen_gallery.py b/sphinx_gallery/gen_gallery.py index 6b3937ff8..c3b29f4a9 100644 --- a/sphinx_gallery/gen_gallery.py +++ b/sphinx_gallery/gen_gallery.py @@ -159,9 +159,11 @@ def _update_gallery_conf_exclude_implicit_doc(gallery_conf): This is separate function for better testability. """ - # prepare regex for exclusions from implicit documentation + # prepare regex for exclusions from implicit documentation, ensuring that what + # gets complied has a stable __repr__ (i.e., by sorting the exclude_implicit_doc + # set before joining) exclude_regex = ( - re.compile("|".join(gallery_conf["exclude_implicit_doc"])) + re.compile("|".join(sorted(gallery_conf["exclude_implicit_doc"]))) if gallery_conf["exclude_implicit_doc"] else False ) diff --git a/sphinx_gallery/gen_rst.py b/sphinx_gallery/gen_rst.py index b1154e2c4..74f691d71 100644 --- a/sphinx_gallery/gen_rst.py +++ b/sphinx_gallery/gen_rst.py @@ -1347,7 +1347,7 @@ def generate_file_rst(fname, target_dir, src_dir, gallery_conf): script_blocks, script_vars, gallery_conf, file_conf ) - logger.debug("%s ran in : %.2g seconds\n", src_file, time_elapsed) + logger.debug("%s ran in : %.2g seconds", src_file, time_elapsed) # Create dummy images _make_dummy_images(executable, file_conf, script_vars) From 1a1e728df84b7b140989e9a4789c084a086dfbea Mon Sep 17 00:00:00 2001 From: Lucy Liu Date: Wed, 7 Aug 2024 10:43:38 +1000 Subject: [PATCH 18/18] Release 0.17.1 (#1370) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- CHANGES.rst | 29 +++++++++++++++++++++++++++++ sphinx_gallery/__init__.py | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index bf8e23ec1..632114566 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,35 @@ Changelog ========= +v0.17.1 +------- + +**Fixed bugs:** + +- FIX: Fix stability of stored compiled regex `#1369 `__ (`larsoner `__) +- ENH: Improve \_sanitize_rst `#1366 `__ (`timhoffm `__) +- Obey prefer_full_module setting when finding backreferences `#1364 `__ (`QuLogic `__) +- Fix linking to class attributes with prefer_full_module `#1363 `__ (`QuLogic `__) +- Improve minigallery directive path input resolution `#1360 `__ (`lucyleeow `__) +- FIX Allow str path minigallery entries when backreferences off `#1355 `__ (`lucyleeow `__) +- FIX generate zipfiles when index passed by user `#1353 `__ (`lucyleeow `__) + +**Documentation** + +- DOC Improve doc about joblib warnings `#1367 `__ (`lucyleeow `__) +- DOC add note on filtering joblib warnings `#1362 `__ (`lucyleeow `__) +- DOC Minor update to minigallery directive doc `#1358 `__ (`lucyleeow `__) + +**Project maintenance** + +- [pre-commit.ci] pre-commit autoupdate `#1368 `__ (`pre-commit-ci[bot] `__) +- MNT Change mark and fixture names for adding files `#1365 `__ (`lucyleeow `__) +- MNT Add warning when ‘examples_dirs’ and ‘gallery_dirs’ unequal lengths `#1361 `__ (`lucyleeow `__) +- [pre-commit.ci] pre-commit autoupdate `#1357 `__ (`pre-commit-ci[bot] `__) +- Update pyvista in doc CI `#1352 `__ (`lucyleeow `__) +- [pre-commit.ci] pre-commit autoupdate `#1351 `__ (`pre-commit-ci[bot] `__) +- MNT Bump version `#1350 `__ (`lucyleeow `__) + v0.17.0 ------- diff --git a/sphinx_gallery/__init__.py b/sphinx_gallery/__init__.py index 776bf26f7..06e8a7650 100644 --- a/sphinx_gallery/__init__.py +++ b/sphinx_gallery/__init__.py @@ -8,7 +8,7 @@ # dev versions should have "dev" in them, stable should not. # doc/conf.py makes use of this to set the version drop-down. -__version__ = "0.18.dev0" +__version__ = "0.17.1" def glr_path_static():