Repository: jupyter/jupyter-sphinx

Issue:
Warning in sphinx 4.0.0
Sphinx released a new version that seems to cause this kind of warnings:

```bash
WARNING: unknown directive or role name: jupyter-download:notebook
WARNING: unknown directive or role name: jupyter-download:script
```

when using something like:

```
:jupyter-download:notebook:`Tutorial 1`
:jupyter-download:script:`Tutorial 1`
```

The docs build seems to be ok with the correct output, might just make some pipelines fail due to the warnings

Discussion:

Patch:
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 786f008..179e813 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -372,16 +372,16 @@ Downloading the code as a script
 --------------------------------
 
 Jupyter Sphinx includes 2 roles that can be used to download the code embedded in a document:
-``:jupyter-download:script:`` (for a raw script file) and ``:jupyter-download:notebook:`` or ``:jupyter-download:nb:`` (for
+``:jupyter-download-script:`` (for a raw script file) and ``:jupyter-download-notebook:`` or ``:jupyter-download-nb:`` (for
 a Jupyter notebook).
 
 These roles are equivalent to the standard sphinx `download role <https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html#role-download>`__, **except** the extension of the file should not be given.
 For example, to download all the code from this document as a script we
 would use::
 
-    :jupyter-download:script:`click to download <index>`
+    :jupyter-download-script:`click to download <index>`
 
-Which produces a link like this: :jupyter-download:script:`click to download <index>`. The target that the role is
+Which produces a link like this: :jupyter-download-nb:`click to download <index>`. The target that the role is
 applied to (``index`` in this case) is the name of the document for which you wish to download
 the code. If a document contains ``jupyter-kernel`` directives with ``:id:`` specified, then
 the name provided to ``:id:`` can be used to get the code for the cells belonging to the
@@ -477,6 +477,7 @@ Release 0.4.0
 - Improve script handling by using ``nbconvert`` directly.
 - Remove deprecated enabling of the extension as ``jupyter_sphinx.execute``.
 - Implement different output priorities in HTML and LaTeX builders. In practice this allows to provide a better fallback in PDF output.
+- Introduce new ``jupyter-download`` syntax compatible with Sphinx≥4, ``jupyter-download-nb``, ``jupyter-download-notebook``, and ``jupyter-download-script``
 
 Release 0.3.0
 ~~~~~~~~~~~~~
diff --git a/jupyter_sphinx/__init__.py b/jupyter_sphinx/__init__.py
index 0c4e609..81abbc0 100644
--- a/jupyter_sphinx/__init__.py
+++ b/jupyter_sphinx/__init__.py
@@ -271,9 +271,13 @@ def setup(app):
     app.add_directive("jupyter-input", CellInput)
     app.add_directive("jupyter-output", CellOutput)
     app.add_directive("thebe-button", ThebeButton)
-    app.add_role("jupyter-download:notebook", JupyterDownloadRole())
-    app.add_role("jupyter-download:nb", JupyterDownloadRole())
-    app.add_role("jupyter-download:script", JupyterDownloadRole())
+    for sep in [":", "-"]:
+        # Since Sphinx 4.0.0 using ":" inside of a role/directive does not work.
+        # Therefore, we add "-" as separator to get e.g., jupyter-download-notebook
+        # We leave the ":" syntax for backward compatibility reasons.
+        app.add_role(f"jupyter-download{sep}notebook", JupyterDownloadRole())
+        app.add_role(f"jupyter-download{sep}nb", JupyterDownloadRole())
+        app.add_role(f"jupyter-download{sep}script", JupyterDownloadRole())
     app.add_transform(CombineCellInputOutput)
     app.add_transform(ExecuteJupyterCells)
 
diff --git a/jupyter_sphinx/ast.py b/jupyter_sphinx/ast.py
index 67a1bc5..35402f3 100644
--- a/jupyter_sphinx/ast.py
+++ b/jupyter_sphinx/ast.py
@@ -1,6 +1,7 @@
 """Manipulating the Sphinx AST with Jupyter objects"""
 
 import json
+import warnings
 from pathlib import Path
 
 import docutils
@@ -587,7 +588,14 @@ def apply_styling(node, thebe_config):
 
 class JupyterDownloadRole(ReferenceRole):
     def run(self):
-        _, filetype = self.name.split(":")
+        sep = ":" if ":" in self.name else "-"
+        name, filetype = self.name.rsplit(sep, maxsplit=1)
+        if sep == ":":
+            warnings.warn(
+                f"The {self.name} syntax is deprecated and "
+                f"will be removed in 0.5.0, please use {name}-{filetype}",
+                category=DeprecationWarning,
+            )
 
         assert filetype in ("notebook", "nb", "script")
         ext = ".ipynb" if filetype in ("notebook", "nb") else ".py"


Test patch:
diff --git a/tests/test_execute.py b/tests/test_execute.py
index d81bbdc..1df4102 100644
--- a/tests/test_execute.py
+++ b/tests/test_execute.py
@@ -610,7 +610,7 @@ def test_download_role(text, reftarget, caption, tmp_path):
         "reporter.get_source_and_line": lambda l: ("source", l),
     }
     mock_inliner.configure_mock(**config)
-    ret, msg = role("jupyter-download:notebook", text, text, 0, mock_inliner)
+    ret, msg = role("jupyter-download-notebook", text, text, 0, mock_inliner)
 
     if os.name == "nt":
         # Get equivalent abs path for Windows