prompt: 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()
  
  ```
  
golden_diff: 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
  
verification_info: {"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"}]}