Repository: sphinx-gallery/sphinx-gallery

Issue:
`minigallery` raises cryptic error when `backreferences_dir` is `None`
I encountered this while trying to add a mini-gallery based on explicit file paths, following the documentation at https://sphinx-gallery.github.io/stable/configuration.html#create-mini-galleries-using-file-paths.  That section doesn't mention that a definition of `backreferences_dir` is required, and earlier sections describe the configuration as being "the directory where function/class granular galleries are stored".  Since I wasn't trying to generate function/class granular galleries, I left the configuration empty.

The result is the following error:
```
Exception occurred:
  File "/usr/local/lib/python3.12/pathlib.py", line 373, in __init__
    raise TypeError(
TypeError: argument should be a str or an os.PathLike object where __fspath__ returns a str, not 'NoneType'
```

I thought it was complaining about the paths I'd supplied to the `minigallery` directive, and it took me a long time to work out that this arises due to `backreferences_dir` being set to `None`.

I think one or more of the following would improve UX here:

- Generating a meaningful error message if `backreferences_dir` is `None`.
- Emphasizing in the documentation that `backreferences_dir` must be set even for mini-galleries based on explicit file paths.

Discussion:
lucyleeow: @larsoner will release after this FYI
lucyleeow: > "../../examples" as input

As input to the minigallery directive? But would glob find anything if it was the wrong relative path?
lucyleeow: >  same file inside the examples_dir folder, but the paths don't evaluate to the same path , 

sorry I do not follow what you mean. Can you give an example?

>  I think something like input needs to be relative path but making the thumbnail needed this path

Are you are talking about:

https://github.com/sphinx-gallery/sphinx-gallery/blob/2fc5067f519ee17de066471cf1e1f2eeffb51add/sphinx_gallery/directives.py#L151

Could you give an example? E.g., if I wanted to test this error, what would the test look like?

lucyleeow: > this will trigger the error: ../../tinybuild/examples/*/*.py

I get this error:

```
sphinx.errors.ExtensionError: Error in minigallery file lookup: input=../../tinybuild/examples/*/*.py, matches=[], examples_dirs=['../examples/', '../examples_with_rst/', '../examples_rst_index', '../examples_README_header']
```

But again we are in the case where there are NO matches - i.e., glob found files, but the files aren't in `examples_dirs`.

What is an example of a case where there are >1 matches?
lucyleeow: Okay I think I'm going to open another issue for the match error, because it probably shouldn't hold up this PR.

This is ready for review/merge. I just had a question about warnings: https://github.com/sphinx-gallery/sphinx-gallery/pull/1355#discussion_r1692324187
lucyleeow: Let's continue the conversation in #1356

@story645 any insights on https://github.com/sphinx-gallery/sphinx-gallery/pull/1355#discussion_r1692324187 ? I see you use a `self.warning` in the directive?
lucyleeow: > an improvement/follow up if I have time would be to make a list of (example/thumbnail/intro/title) so examples don't need to get reparsed here and that would probably clean up some of this.

re-reading, could you expand? Where would the list live?
lucyleeow: Ah okay, maybe with the thumbnail images as image metadata. But we'd have to consider whether it's worth all the extra work generating the files, performance wise (especially as some projects may not use the minigallery directive). Anyway, things to consider.

@larsoner I'll merge this next week, in case you want to take a look.

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4ad2cfd..5d0d273 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
diff --git a/doc/configuration.rst b/doc/configuration.rst
index e36d6e2..02e16bc 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
    <https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-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 97c98bc..4b00ec9 100644
--- a/sphinx_gallery/directives.py
+++ b/sphinx_gallery/directives.py
@@ -8,6 +8,7 @@ from docutils import nodes, statemachine
 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 .backreferences import (
 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 @@ class MiniGallery(Directive):
         # 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 @@ class MiniGallery(Directive):
 
         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 @@ class MiniGallery(Directive):
     :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]]


Test patch:
diff --git a/sphinx_gallery/tests/test_gen_gallery.py b/sphinx_gallery/tests/test_gen_gallery.py
index 08765f2..c048e9d 100644
--- a/sphinx_gallery/tests/test_gen_gallery.py
+++ b/sphinx_gallery/tests/test_gen_gallery.py
@@ -539,6 +539,7 @@ from sphinx_gallery.gen_rst import _sg_call_memory_noop
 
 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 e8c0207..f87aecd 100644
--- a/sphinx_gallery/tests/testconfs/src/plot_1.py
+++ b/sphinx_gallery/tests/testconfs/src/plot_1.py
@@ -9,3 +9,7 @@ B test
 print("foo")
 print("bar")
 print("again")
+
+# %%
+#
+# .. minigallery:: src/plot_2.py