We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[DOC] Fix Sphinx issues related to adding new experimental module and typing
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe your proposed suggestion in detail.

1. Linkcode extension error https://github.com/nilearn/nilearn/pull/4017#issuecomment-1739213117 and related workaround: https://github.com/nilearn/nilearn/blob/9133bd98ddc085e14d545fd0439be621c8679f91/doc/sphinxext/github_link.py#L43-L44

2. See Sphinx warning https://github.com/nilearn/nilearn/pull/4017#issuecomment-1739630179 and related hotfix of remving sphinx role here: https://github.com/nilearn/nilearn/blob/9133bd98ddc085e14d545fd0439be621c8679f91/doc/modules/experimental.rst?plain=1#L10

3. Sphinx autodoc has an issue with resolving external libraries like numpy when using typehints. It seems to be a bug similar to what is reported here https://github.com/sphinx-doc/sphinx/issues/10785. For now this autodoc argument is added to hide the typehints from the integrated typing https://github.com/nilearn/nilearn/blob/9133bd98ddc085e14d545fd0439be621c8679f91/doc/conf.py#L57
And if we have both docstrings and the typehints we have double rendering in the docs of the description of a e.g. class and this is redundant and looks ugly so we should also discuss how we want to approach adding docstrings + typing and how to configure what will be displayed. I'll post an example once the dev docs are built

### List any pages that would be impacted.

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `doc/sphinxext/github_link.py`
Content:
```
1 import inspect
2 import os
3 import subprocess
4 import sys
5 from functools import partial
6 from operator import attrgetter
7 
8 REVISION_CMD = "git rev-parse --short HEAD"
9 
10 
11 def _get_git_revision():
12     try:
13         revision = subprocess.check_output(REVISION_CMD.split()).strip()
14     except (subprocess.CalledProcessError, OSError):
15         print("Failed to execute git to get revision")
16         return None
17     return revision.decode("utf-8")
18 
19 
20 def _linkcode_resolve(domain, info, package, url_fmt, revision):
21     """Determine a link to online source for a class/method/function.
22 
23     This is called by sphinx.ext.linkcode
24 
25     An example with a long-untouched module that everyone has
26     >>> _linkcode_resolve('py', {'module': 'tty',
27     ...                          'fullname': 'setraw'},
28     ...                   package='tty',
29     ...                   url_fmt='http://hg.python.org/cpython/file/'
30     ...                           '{revision}/Lib/{package}/{path}#L{lineno}',
31     ...                   revision='xxxx')
32     'http://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18'
33     """
34     if revision is None:
35         return
36     if domain not in ("py", "pyx"):
37         return
38     if not info.get("module") or not info.get("fullname"):
39         return
40 
41     class_name = info["fullname"].split(".")[0]
42     module = __import__(info["module"], fromlist=[class_name])
43     if info["module"] == "nilearn.experimental.surface":
44         return
45     obj = attrgetter(info["fullname"])(module)
46 
47     # Unwrap the object to get the correct source
48     # file in case that is wrapped by a decorator
49     obj = inspect.unwrap(obj)
50 
51     try:
52         fn = inspect.getsourcefile(obj)
53     except Exception:
54         fn = None
55     if not fn:
56         try:
57             fn = inspect.getsourcefile(sys.modules[obj.__module__])
58         except Exception:
59             fn = None
60     if not fn:
61         return
62 
63     # Don't include filenames from outside this package's tree
64     if os.path.dirname(__import__(package).__file__) not in fn:
65         return
66 
67     fn = os.path.relpath(
68         fn, start=os.path.dirname(__import__(package).__file__)
69     )
70     try:
71         lineno = inspect.getsourcelines(obj)[1]
72     except Exception:
73         lineno = ""
74     return url_fmt.format(
75         revision=revision, package=package, path=fn, lineno=lineno
76     )
77 
78 
79 def make_linkcode_resolve(package, url_fmt):
80     """Return a linkcode_resolve function for the given URL format.
81 
82     revision is a git commit reference (hash or name)
83 
84     package is the name of the root module of the package
85 
86     url_fmt is along the lines of ('https://github.com/USER/PROJECT/'
87                                    'blob/{revision}/{package}/'
88                                    '{path}#L{lineno}')
89     """
90     revision = _get_git_revision()
91     return partial(
92         _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt
93     )
94
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/doc/sphinxext/github_link.py b/doc/sphinxext/github_link.py
--- a/doc/sphinxext/github_link.py
+++ b/doc/sphinxext/github_link.py
@@ -40,9 +40,12 @@
 
     class_name = info["fullname"].split(".")[0]
     module = __import__(info["module"], fromlist=[class_name])
-    if info["module"] == "nilearn.experimental.surface":
+    # For typed parameters, this will try to get uninitialized attributes
+    # and fail
+    try:
+        obj = attrgetter(info["fullname"])(module)
+    except AttributeError:
         return
-    obj = attrgetter(info["fullname"])(module)
 
     # Unwrap the object to get the correct source
     # file in case that is wrapped by a decorator

{"golden_diff": "diff --git a/doc/sphinxext/github_link.py b/doc/sphinxext/github_link.py\n--- a/doc/sphinxext/github_link.py\n+++ b/doc/sphinxext/github_link.py\n@@ -40,9 +40,12 @@\n \n     class_name = info[\"fullname\"].split(\".\")[0]\n     module = __import__(info[\"module\"], fromlist=[class_name])\n-    if info[\"module\"] == \"nilearn.experimental.surface\":\n+    # For typed parameters, this will try to get uninitialized attributes\n+    # and fail\n+    try:\n+        obj = attrgetter(info[\"fullname\"])(module)\n+    except AttributeError:\n         return\n-    obj = attrgetter(info[\"fullname\"])(module)\n \n     # Unwrap the object to get the correct source\n     # file in case that is wrapped by a decorator\n", "issue": "[DOC] Fix Sphinx issues related to adding new experimental module and typing\n### Is there an existing issue for this?\r\n\r\n- [X] I have searched the existing issues\r\n\r\n### Describe your proposed suggestion in detail.\r\n\r\n1. Linkcode extension error https://github.com/nilearn/nilearn/pull/4017#issuecomment-1739213117 and related workaround: https://github.com/nilearn/nilearn/blob/9133bd98ddc085e14d545fd0439be621c8679f91/doc/sphinxext/github_link.py#L43-L44\r\n\r\n2. See Sphinx warning https://github.com/nilearn/nilearn/pull/4017#issuecomment-1739630179 and related hotfix of remving sphinx role here: https://github.com/nilearn/nilearn/blob/9133bd98ddc085e14d545fd0439be621c8679f91/doc/modules/experimental.rst?plain=1#L10\r\n\r\n3. Sphinx autodoc has an issue with resolving external libraries like numpy when using typehints. It seems to be a bug similar to what is reported here https://github.com/sphinx-doc/sphinx/issues/10785. For now this autodoc argument is added to hide the typehints from the integrated typing https://github.com/nilearn/nilearn/blob/9133bd98ddc085e14d545fd0439be621c8679f91/doc/conf.py#L57\r\nAnd if we have both docstrings and the typehints we have double rendering in the docs of the description of a e.g. class and this is redundant and looks ugly so we should also discuss how we want to approach adding docstrings + typing and how to configure what will be displayed. I'll post an example once the dev docs are built\r\n\r\n### List any pages that would be impacted.\r\n\r\n_No response_\n", "before_files": [{"content": "import inspect\nimport os\nimport subprocess\nimport sys\nfrom functools import partial\nfrom operator import attrgetter\n\nREVISION_CMD = \"git rev-parse --short HEAD\"\n\n\ndef _get_git_revision():\n    try:\n        revision = subprocess.check_output(REVISION_CMD.split()).strip()\n    except (subprocess.CalledProcessError, OSError):\n        print(\"Failed to execute git to get revision\")\n        return None\n    return revision.decode(\"utf-8\")\n\n\ndef _linkcode_resolve(domain, info, package, url_fmt, revision):\n    \"\"\"Determine a link to online source for a class/method/function.\n\n    This is called by sphinx.ext.linkcode\n\n    An example with a long-untouched module that everyone has\n    >>> _linkcode_resolve('py', {'module': 'tty',\n    ...                          'fullname': 'setraw'},\n    ...                   package='tty',\n    ...                   url_fmt='http://hg.python.org/cpython/file/'\n    ...                           '{revision}/Lib/{package}/{path}#L{lineno}',\n    ...                   revision='xxxx')\n    'http://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18'\n    \"\"\"\n    if revision is None:\n        return\n    if domain not in (\"py\", \"pyx\"):\n        return\n    if not info.get(\"module\") or not info.get(\"fullname\"):\n        return\n\n    class_name = info[\"fullname\"].split(\".\")[0]\n    module = __import__(info[\"module\"], fromlist=[class_name])\n    if info[\"module\"] == \"nilearn.experimental.surface\":\n        return\n    obj = attrgetter(info[\"fullname\"])(module)\n\n    # Unwrap the object to get the correct source\n    # file in case that is wrapped by a decorator\n    obj = inspect.unwrap(obj)\n\n    try:\n        fn = inspect.getsourcefile(obj)\n    except Exception:\n        fn = None\n    if not fn:\n        try:\n            fn = inspect.getsourcefile(sys.modules[obj.__module__])\n        except Exception:\n            fn = None\n    if not fn:\n        return\n\n    # Don't include filenames from outside this package's tree\n    if os.path.dirname(__import__(package).__file__) not in fn:\n        return\n\n    fn = os.path.relpath(\n        fn, start=os.path.dirname(__import__(package).__file__)\n    )\n    try:\n        lineno = inspect.getsourcelines(obj)[1]\n    except Exception:\n        lineno = \"\"\n    return url_fmt.format(\n        revision=revision, package=package, path=fn, lineno=lineno\n    )\n\n\ndef make_linkcode_resolve(package, url_fmt):\n    \"\"\"Return a linkcode_resolve function for the given URL format.\n\n    revision is a git commit reference (hash or name)\n\n    package is the name of the root module of the package\n\n    url_fmt is along the lines of ('https://github.com/USER/PROJECT/'\n                                   'blob/{revision}/{package}/'\n                                   '{path}#L{lineno}')\n    \"\"\"\n    revision = _get_git_revision()\n    return partial(\n        _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt\n    )\n", "path": "doc/sphinxext/github_link.py"}], "after_files": [{"content": "import inspect\nimport os\nimport subprocess\nimport sys\nfrom functools import partial\nfrom operator import attrgetter\n\nREVISION_CMD = \"git rev-parse --short HEAD\"\n\n\ndef _get_git_revision():\n    try:\n        revision = subprocess.check_output(REVISION_CMD.split()).strip()\n    except (subprocess.CalledProcessError, OSError):\n        print(\"Failed to execute git to get revision\")\n        return None\n    return revision.decode(\"utf-8\")\n\n\ndef _linkcode_resolve(domain, info, package, url_fmt, revision):\n    \"\"\"Determine a link to online source for a class/method/function.\n\n    This is called by sphinx.ext.linkcode\n\n    An example with a long-untouched module that everyone has\n    >>> _linkcode_resolve('py', {'module': 'tty',\n    ...                          'fullname': 'setraw'},\n    ...                   package='tty',\n    ...                   url_fmt='http://hg.python.org/cpython/file/'\n    ...                           '{revision}/Lib/{package}/{path}#L{lineno}',\n    ...                   revision='xxxx')\n    'http://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18'\n    \"\"\"\n    if revision is None:\n        return\n    if domain not in (\"py\", \"pyx\"):\n        return\n    if not info.get(\"module\") or not info.get(\"fullname\"):\n        return\n\n    class_name = info[\"fullname\"].split(\".\")[0]\n    module = __import__(info[\"module\"], fromlist=[class_name])\n    # For typed parameters, this will try to get uninitialized attributes\n    # and fail\n    try:\n        obj = attrgetter(info[\"fullname\"])(module)\n    except AttributeError:\n        return\n\n    # Unwrap the object to get the correct source\n    # file in case that is wrapped by a decorator\n    obj = inspect.unwrap(obj)\n\n    try:\n        fn = inspect.getsourcefile(obj)\n    except Exception:\n        fn = None\n    if not fn:\n        try:\n            fn = inspect.getsourcefile(sys.modules[obj.__module__])\n        except Exception:\n            fn = None\n    if not fn:\n        return\n\n    # Don't include filenames from outside this package's tree\n    if os.path.dirname(__import__(package).__file__) not in fn:\n        return\n\n    fn = os.path.relpath(\n        fn, start=os.path.dirname(__import__(package).__file__)\n    )\n    try:\n        lineno = inspect.getsourcelines(obj)[1]\n    except Exception:\n        lineno = \"\"\n    return url_fmt.format(\n        revision=revision, package=package, path=fn, lineno=lineno\n    )\n\n\ndef make_linkcode_resolve(package, url_fmt):\n    \"\"\"Return a linkcode_resolve function for the given URL format.\n\n    revision is a git commit reference (hash or name)\n\n    package is the name of the root module of the package\n\n    url_fmt is along the lines of ('https://github.com/USER/PROJECT/'\n                                   'blob/{revision}/{package}/'\n                                   '{path}#L{lineno}')\n    \"\"\"\n    revision = _get_git_revision()\n    return partial(\n        _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt\n    )\n", "path": "doc/sphinxext/github_link.py"}]}