Repository: sphinx-doc/sphinx

Issue:
autosummary emits broken links if `primary_domain` is not `py`
### Describe the bug

autosummary expects the `primary_domain` to be `py`. Otherwise, it gives warnings like these:

```
WARNING: Unknown interpreted text role "obj".
```

for each `.. autosummary::` directive.

In the HTML documentation there are broken links with the text

```
:obj:`get_random_ingredients <lumache.get_random_ingredients>`
:obj:`InvalidKindError <lumache.InvalidKindError>`
:obj:`lumache <lumache>`
```

for every `.. autosummary::` directive.

### How to Reproduce

(TBC)

### Expected behavior

I would expect `autosummary` to work regardless of the `primary_domain` configuration.

### Your project

(TBC)

### Screenshots

![Screenshot 2021-09-13 at 13-03-32 lumache - Lumache 0 1 documentation](https://user-images.githubusercontent.com/316517/133072806-1cf5d392-c5d6-484d-9ce1-a3ccd4f1176e.png)


### OS

Linux

### Python version

3.9.7

### Sphinx version

4.2.0 and earlier

### Sphinx extensions

_No response_

### Extra tools

_No response_

### Additional context

This was originally reported in #2001, but it was not sufficiently well described and was perhaps prematurely closed.

Discussion:

Patch:
diff --git a/CHANGES b/CHANGES
index 9e8bc1151..ee96137fd 100644
--- a/CHANGES
+++ b/CHANGES
@@ -16,6 +16,11 @@ Features added
 Bugs fixed
 ----------
 
+* #9630: autodoc: Failed to build cross references if :confval:`primary_domain`
+  is not 'py'
+* #9630: autosummary: Failed to build summary table if :confval:`primary_domain`
+  is not 'py'
+
 Testing
 --------
 
diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py
index 581211996..8bd3d50cd 100644
--- a/sphinx/ext/autosummary/__init__.py
+++ b/sphinx/ext/autosummary/__init__.py
@@ -444,9 +444,9 @@ def append_row(*column_texts: str) -> None:
         for name, sig, summary, real_name in items:
             qualifier = 'obj'
             if 'nosignatures' not in self.options:
-                col1 = ':%s:`%s <%s>`\\ %s' % (qualifier, name, real_name, rst.escape(sig))
+                col1 = ':py:%s:`%s <%s>`\\ %s' % (qualifier, name, real_name, rst.escape(sig))
             else:
-                col1 = ':%s:`%s <%s>`' % (qualifier, name, real_name)
+                col1 = ':py:%s:`%s <%s>`' % (qualifier, name, real_name)
             col2 = summary
             append_row(col1, col2)
 
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index 8912de8cd..7380f4783 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -110,18 +110,18 @@ def restify(cls: Optional[Type]) -> str:
 
     try:
         if cls is None or cls is NoneType:
-            return ':obj:`None`'
+            return ':py:obj:`None`'
         elif cls is Ellipsis:
             return '...'
         elif cls in INVALID_BUILTIN_CLASSES:
-            return ':class:`%s`' % INVALID_BUILTIN_CLASSES[cls]
+            return ':py:class:`%s`' % INVALID_BUILTIN_CLASSES[cls]
         elif inspect.isNewType(cls):
             if sys.version_info > (3, 10):
                 # newtypes have correct module info since Python 3.10+
                 print(cls, type(cls), dir(cls))
-                return ':class:`%s.%s`' % (cls.__module__, cls.__name__)
+                return ':py:class:`%s.%s`' % (cls.__module__, cls.__name__)
             else:
-                return ':class:`%s`' % cls.__name__
+                return ':py:class:`%s`' % cls.__name__
         elif UnionType and isinstance(cls, UnionType):
             if len(cls.__args__) > 1 and None in cls.__args__:
                 args = ' | '.join(restify(a) for a in cls.__args__ if a)
@@ -130,12 +130,12 @@ def restify(cls: Optional[Type]) -> str:
                 return ' | '.join(restify(a) for a in cls.__args__)
         elif cls.__module__ in ('__builtin__', 'builtins'):
             if hasattr(cls, '__args__'):
-                return ':class:`%s`\\ [%s]' % (
+                return ':py:class:`%s`\\ [%s]' % (
                     cls.__name__,
                     ', '.join(restify(arg) for arg in cls.__args__),
                 )
             else:
-                return ':class:`%s`' % cls.__name__
+                return ':py:class:`%s`' % cls.__name__
         else:
             if sys.version_info >= (3, 7):  # py37+
                 return _restify_py37(cls)
@@ -155,20 +155,20 @@ def _restify_py37(cls: Optional[Type]) -> str:
         if len(cls.__args__) > 1 and cls.__args__[-1] is NoneType:
             if len(cls.__args__) > 2:
                 args = ', '.join(restify(a) for a in cls.__args__[:-1])
-                return ':obj:`~typing.Optional`\\ [:obj:`~typing.Union`\\ [%s]]' % args
+                return ':py:obj:`~typing.Optional`\\ [:obj:`~typing.Union`\\ [%s]]' % args
             else:
-                return ':obj:`~typing.Optional`\\ [%s]' % restify(cls.__args__[0])
+                return ':py:obj:`~typing.Optional`\\ [%s]' % restify(cls.__args__[0])
         else:
             args = ', '.join(restify(a) for a in cls.__args__)
-            return ':obj:`~typing.Union`\\ [%s]' % args
+            return ':py:obj:`~typing.Union`\\ [%s]' % args
     elif inspect.isgenericalias(cls):
         if isinstance(cls.__origin__, typing._SpecialForm):
             text = restify(cls.__origin__)  # type: ignore
         elif getattr(cls, '_name', None):
             if cls.__module__ == 'typing':
-                text = ':class:`~%s.%s`' % (cls.__module__, cls._name)
+                text = ':py:class:`~%s.%s`' % (cls.__module__, cls._name)
             else:
-                text = ':class:`%s.%s`' % (cls.__module__, cls._name)
+                text = ':py:class:`%s.%s`' % (cls.__module__, cls._name)
         else:
             text = restify(cls.__origin__)
 
@@ -188,20 +188,20 @@ def _restify_py37(cls: Optional[Type]) -> str:
 
         return text
     elif isinstance(cls, typing._SpecialForm):
-        return ':obj:`~%s.%s`' % (cls.__module__, cls._name)
+        return ':py:obj:`~%s.%s`' % (cls.__module__, cls._name)
     elif hasattr(cls, '__qualname__'):
         if cls.__module__ == 'typing':
-            return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__)
+            return ':py:class:`~%s.%s`' % (cls.__module__, cls.__qualname__)
         else:
-            return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__)
+            return ':py:class:`%s.%s`' % (cls.__module__, cls.__qualname__)
     elif isinstance(cls, ForwardRef):
-        return ':class:`%s`' % cls.__forward_arg__
+        return ':py:class:`%s`' % cls.__forward_arg__
     else:
         # not a class (ex. TypeVar)
         if cls.__module__ == 'typing':
-            return ':obj:`~%s.%s`' % (cls.__module__, cls.__name__)
+            return ':py:obj:`~%s.%s`' % (cls.__module__, cls.__name__)
         else:
-            return ':obj:`%s.%s`' % (cls.__module__, cls.__name__)
+            return ':py:obj:`%s.%s`' % (cls.__module__, cls.__name__)
 
 
 def _restify_py36(cls: Optional[Type]) -> str:
@@ -225,9 +225,9 @@ def _restify_py36(cls: Optional[Type]) -> str:
     if (isinstance(cls, typing.TupleMeta) and  # type: ignore
             not hasattr(cls, '__tuple_params__')):
         if module == 'typing':
-            reftext = ':class:`~typing.%s`' % qualname
+            reftext = ':py:class:`~typing.%s`' % qualname
         else:
-            reftext = ':class:`%s`' % qualname
+            reftext = ':py:class:`%s`' % qualname
 
         params = cls.__args__
         if params:
@@ -237,9 +237,9 @@ def _restify_py36(cls: Optional[Type]) -> str:
             return reftext
     elif isinstance(cls, typing.GenericMeta):
         if module == 'typing':
-            reftext = ':class:`~typing.%s`' % qualname
+            reftext = ':py:class:`~typing.%s`' % qualname
         else:
-            reftext = ':class:`%s`' % qualname
+            reftext = ':py:class:`%s`' % qualname
 
         if cls.__args__ is None or len(cls.__args__) <= 2:
             params = cls.__args__
@@ -262,38 +262,38 @@ def _restify_py36(cls: Optional[Type]) -> str:
             if len(params) > 1 and params[-1] is NoneType:
                 if len(params) > 2:
                     param_str = ", ".join(restify(p) for p in params[:-1])
-                    return (':obj:`~typing.Optional`\\ '
-                            '[:obj:`~typing.Union`\\ [%s]]' % param_str)
+                    return (':py:obj:`~typing.Optional`\\ '
+                            '[:py:obj:`~typing.Union`\\ [%s]]' % param_str)
                 else:
-                    return ':obj:`~typing.Optional`\\ [%s]' % restify(params[0])
+                    return ':py:obj:`~typing.Optional`\\ [%s]' % restify(params[0])
             else:
                 param_str = ', '.join(restify(p) for p in params)
-                return ':obj:`~typing.Union`\\ [%s]' % param_str
+                return ':py:obj:`~typing.Union`\\ [%s]' % param_str
         else:
-            return ':obj:`Union`'
+            return ':py:obj:`Union`'
     elif hasattr(cls, '__qualname__'):
         if cls.__module__ == 'typing':
-            return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__)
+            return ':py:class:`~%s.%s`' % (cls.__module__, cls.__qualname__)
         else:
-            return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__)
+            return ':py:class:`%s.%s`' % (cls.__module__, cls.__qualname__)
     elif hasattr(cls, '_name'):
         # SpecialForm
         if cls.__module__ == 'typing':
-            return ':obj:`~%s.%s`' % (cls.__module__, cls._name)
+            return ':py:obj:`~%s.%s`' % (cls.__module__, cls._name)
         else:
-            return ':obj:`%s.%s`' % (cls.__module__, cls._name)
+            return ':py:obj:`%s.%s`' % (cls.__module__, cls._name)
     elif hasattr(cls, '__name__'):
         # not a class (ex. TypeVar)
         if cls.__module__ == 'typing':
-            return ':obj:`~%s.%s`' % (cls.__module__, cls.__name__)
+            return ':py:obj:`~%s.%s`' % (cls.__module__, cls.__name__)
         else:
-            return ':obj:`%s.%s`' % (cls.__module__, cls.__name__)
+            return ':py:obj:`%s.%s`' % (cls.__module__, cls.__name__)
     else:
         # others (ex. Any)
         if cls.__module__ == 'typing':
-            return ':obj:`~%s.%s`' % (cls.__module__, qualname)
+            return ':py:obj:`~%s.%s`' % (cls.__module__, qualname)
         else:
-            return ':obj:`%s.%s`' % (cls.__module__, qualname)
+            return ':py:obj:`%s.%s`' % (cls.__module__, qualname)
 
 
 def stringify(annotation: Any) -> str:


Test patch:
diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py
index 299c1c681..155842007 100644
--- a/tests/test_ext_autodoc.py
+++ b/tests/test_ext_autodoc.py
@@ -984,7 +984,7 @@ def test_autodoc_inner_class(app):
         '   .. py:attribute:: Outer.factory',
         '      :module: target',
         '',
-        '      alias of :class:`dict`'
+        '      alias of :py:class:`dict`'
     ]
 
     actual = do_autodoc(app, 'class', 'target.Outer.Inner', options)
@@ -1009,7 +1009,7 @@ def test_autodoc_inner_class(app):
         '',
         '.. py:class:: InnerChild()',
         '   :module: target', '',
-        '   Bases: :class:`target.Outer.Inner`',
+        '   Bases: :py:class:`target.Outer.Inner`',
         '',
         '   InnerChild docstring',
         '',
@@ -1750,7 +1750,7 @@ def test_autodoc_typed_instance_variables(app):
         '.. py:attribute:: Alias',
         '   :module: target.typed_vars',
         '',
-        '   alias of :class:`target.typed_vars.Derived`',
+        '   alias of :py:class:`target.typed_vars.Derived`',
         '',
         '.. py:class:: Class()',
         '   :module: target.typed_vars',
@@ -1915,12 +1915,12 @@ def test_autodoc_GenericAlias(app):
             '   .. py:attribute:: Class.T',
             '      :module: target.genericalias',
             '',
-            '      alias of :class:`~typing.List`\\ [:class:`int`]',
+            '      alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
             '',
             '.. py:attribute:: T',
             '   :module: target.genericalias',
             '',
-            '   alias of :class:`~typing.List`\\ [:class:`int`]',
+            '   alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
         ]
     else:
         assert list(actual) == [
@@ -1937,7 +1937,7 @@ def test_autodoc_GenericAlias(app):
             '',
             '      A list of int',
             '',
-            '      alias of :class:`~typing.List`\\ [:class:`int`]',
+            '      alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
             '',
             '',
             '.. py:data:: T',
@@ -1945,7 +1945,7 @@ def test_autodoc_GenericAlias(app):
             '',
             '   A list of int',
             '',
-            '   alias of :class:`~typing.List`\\ [:class:`int`]',
+            '   alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
             '',
         ]
 
@@ -1977,7 +1977,7 @@ def test_autodoc_TypeVar(app):
         '',
         '      T6',
         '',
-        '      alias of :class:`int`',
+        '      alias of :py:class:`int`',
         '',
         '',
         '.. py:data:: T1',
@@ -2017,7 +2017,7 @@ def test_autodoc_TypeVar(app):
         '',
         '   T6',
         '',
-        '   alias of :class:`int`',
+        '   alias of :py:class:`int`',
         '',
         '',
         '.. py:data:: T7',
@@ -2025,7 +2025,7 @@ def test_autodoc_TypeVar(app):
         '',
         '   T7',
         '',
-        "   alias of TypeVar('T7', bound=\\ :class:`int`)",
+        "   alias of TypeVar('T7', bound=\\ :py:class:`int`)",
         '',
     ]
 
diff --git a/tests/test_ext_autodoc_autoattribute.py b/tests/test_ext_autodoc_autoattribute.py
index 20317b8da..a9392163a 100644
--- a/tests/test_ext_autodoc_autoattribute.py
+++ b/tests/test_ext_autodoc_autoattribute.py
@@ -167,7 +167,7 @@ def test_autoattribute_GenericAlias(app):
             '',
             '   A list of int',
             '',
-            '   alias of :class:`~typing.List`\\ [:class:`int`]',
+            '   alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
             '',
         ]
 
@@ -182,7 +182,7 @@ def test_autoattribute_NewType(app):
         '',
         '   T6',
         '',
-        '   alias of :class:`int`',
+        '   alias of :py:class:`int`',
         '',
     ]
 
diff --git a/tests/test_ext_autodoc_autoclass.py b/tests/test_ext_autodoc_autoclass.py
index 24617bf0a..0470bd6fd 100644
--- a/tests/test_ext_autodoc_autoclass.py
+++ b/tests/test_ext_autodoc_autoclass.py
@@ -265,8 +265,8 @@ def test_show_inheritance_for_subclass_of_generic_type(app):
         '.. py:class:: Quux(iterable=(), /)',
         '   :module: target.classes',
         '',
-        '   Bases: :class:`~typing.List`\\ '
-        '[:obj:`~typing.Union`\\ [:class:`int`, :class:`float`]]',
+        '   Bases: :py:class:`~typing.List`\\ '
+        '[:py:obj:`~typing.Union`\\ [:py:class:`int`, :py:class:`float`]]',
         '',
         '   A subclass of List[Union[int, float]]',
         '',
@@ -296,7 +296,7 @@ def autodoc_process_bases(app, name, obj, options, bases):
             '.. py:class:: Quux(*args, **kwds)',
             '   :module: target.classes',
             '',
-            '   Bases: :class:`int`, :class:`str`',
+            '   Bases: :py:class:`int`, :py:class:`str`',
             '',
             '   A subclass of List[Union[int, float]]',
             '',
@@ -307,7 +307,7 @@ def autodoc_process_bases(app, name, obj, options, bases):
             '.. py:class:: Quux(iterable=(), /)',
             '   :module: target.classes',
             '',
-            '   Bases: :class:`int`, :class:`str`',
+            '   Bases: :py:class:`int`, :py:class:`str`',
             '',
             '   A subclass of List[Union[int, float]]',
             '',
@@ -375,7 +375,7 @@ def autodoc_process_docstring(*args):
         '.. py:attribute:: Alias',
         '   :module: target.classes',
         '',
-        '   alias of :class:`target.classes.Foo`',
+        '   alias of :py:class:`target.classes.Foo`',
     ]
 
 
diff --git a/tests/test_ext_autodoc_autodata.py b/tests/test_ext_autodoc_autodata.py
index d01e45fc1..f983726ad 100644
--- a/tests/test_ext_autodoc_autodata.py
+++ b/tests/test_ext_autodoc_autodata.py
@@ -96,7 +96,7 @@ def test_autodata_GenericAlias(app):
             '',
             '   A list of int',
             '',
-            '   alias of :class:`~typing.List`\\ [:class:`int`]',
+            '   alias of :py:class:`~typing.List`\\ [:py:class:`int`]',
             '',
         ]
 
@@ -111,7 +111,7 @@ def test_autodata_NewType(app):
         '',
         '   T6',
         '',
-        '   alias of :class:`int`',
+        '   alias of :py:class:`int`',
         '',
     ]
 
diff --git a/tests/test_util_typing.py b/tests/test_util_typing.py
index bbee68f82..ad57a347e 100644
--- a/tests/test_util_typing.py
+++ b/tests/test_util_typing.py
@@ -41,66 +41,69 @@ class BrokenType:
 
 
 def test_restify():
-    assert restify(int) == ":class:`int`"
-    assert restify(str) == ":class:`str`"
-    assert restify(None) == ":obj:`None`"
-    assert restify(Integral) == ":class:`numbers.Integral`"
-    assert restify(Struct) == ":class:`struct.Struct`"
-    assert restify(TracebackType) == ":class:`types.TracebackType`"
-    assert restify(Any) == ":obj:`~typing.Any`"
+    assert restify(int) == ":py:class:`int`"
+    assert restify(str) == ":py:class:`str`"
+    assert restify(None) == ":py:obj:`None`"
+    assert restify(Integral) == ":py:class:`numbers.Integral`"
+    assert restify(Struct) == ":py:class:`struct.Struct`"
+    assert restify(TracebackType) == ":py:class:`types.TracebackType`"
+    assert restify(Any) == ":py:obj:`~typing.Any`"
 
 
 def test_restify_type_hints_containers():
-    assert restify(List) == ":class:`~typing.List`"
-    assert restify(Dict) == ":class:`~typing.Dict`"
-    assert restify(List[int]) == ":class:`~typing.List`\\ [:class:`int`]"
-    assert restify(List[str]) == ":class:`~typing.List`\\ [:class:`str`]"
-    assert restify(Dict[str, float]) == (":class:`~typing.Dict`\\ "
-                                         "[:class:`str`, :class:`float`]")
-    assert restify(Tuple[str, str, str]) == (":class:`~typing.Tuple`\\ "
-                                             "[:class:`str`, :class:`str`, :class:`str`]")
-    assert restify(Tuple[str, ...]) == ":class:`~typing.Tuple`\\ [:class:`str`, ...]"
-    assert restify(Tuple[()]) == ":class:`~typing.Tuple`\\ [()]"
-    assert restify(List[Dict[str, Tuple]]) == (":class:`~typing.List`\\ "
-                                               "[:class:`~typing.Dict`\\ "
-                                               "[:class:`str`, :class:`~typing.Tuple`]]")
-    assert restify(MyList[Tuple[int, int]]) == (":class:`tests.test_util_typing.MyList`\\ "
-                                                "[:class:`~typing.Tuple`\\ "
-                                                "[:class:`int`, :class:`int`]]")
-    assert restify(Generator[None, None, None]) == (":class:`~typing.Generator`\\ "
-                                                    "[:obj:`None`, :obj:`None`, :obj:`None`]")
+    assert restify(List) == ":py:class:`~typing.List`"
+    assert restify(Dict) == ":py:class:`~typing.Dict`"
+    assert restify(List[int]) == ":py:class:`~typing.List`\\ [:py:class:`int`]"
+    assert restify(List[str]) == ":py:class:`~typing.List`\\ [:py:class:`str`]"
+    assert restify(Dict[str, float]) == (":py:class:`~typing.Dict`\\ "
+                                         "[:py:class:`str`, :py:class:`float`]")
+    assert restify(Tuple[str, str, str]) == (":py:class:`~typing.Tuple`\\ "
+                                             "[:py:class:`str`, :py:class:`str`, "
+                                             ":py:class:`str`]")
+    assert restify(Tuple[str, ...]) == ":py:class:`~typing.Tuple`\\ [:py:class:`str`, ...]"
+    assert restify(Tuple[()]) == ":py:class:`~typing.Tuple`\\ [()]"
+    assert restify(List[Dict[str, Tuple]]) == (":py:class:`~typing.List`\\ "
+                                               "[:py:class:`~typing.Dict`\\ "
+                                               "[:py:class:`str`, :py:class:`~typing.Tuple`]]")
+    assert restify(MyList[Tuple[int, int]]) == (":py:class:`tests.test_util_typing.MyList`\\ "
+                                                "[:py:class:`~typing.Tuple`\\ "
+                                                "[:py:class:`int`, :py:class:`int`]]")
+    assert restify(Generator[None, None, None]) == (":py:class:`~typing.Generator`\\ "
+                                                    "[:py:obj:`None`, :py:obj:`None`, "
+                                                    ":py:obj:`None`]")
 
 
 def test_restify_type_hints_Callable():
-    assert restify(Callable) == ":class:`~typing.Callable`"
+    assert restify(Callable) == ":py:class:`~typing.Callable`"
 
     if sys.version_info >= (3, 7):
-        assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ "
-                                                 "[[:class:`str`], :class:`int`]")
-        assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ "
-                                               "[[...], :class:`int`]")
+        assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
+                                                 "[[:py:class:`str`], :py:class:`int`]")
+        assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ "
+                                               "[[...], :py:class:`int`]")
     else:
-        assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ "
-                                                 "[:class:`str`, :class:`int`]")
-        assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ "
-                                               "[..., :class:`int`]")
+        assert restify(Callable[[str], int]) == (":py:class:`~typing.Callable`\\ "
+                                                 "[:py:class:`str`, :py:class:`int`]")
+        assert restify(Callable[..., int]) == (":py:class:`~typing.Callable`\\ "
+                                               "[..., :py:class:`int`]")
 
 
 def test_restify_type_hints_Union():
-    assert restify(Optional[int]) == ":obj:`~typing.Optional`\\ [:class:`int`]"
-    assert restify(Union[str, None]) == ":obj:`~typing.Optional`\\ [:class:`str`]"
-    assert restify(Union[int, str]) == ":obj:`~typing.Union`\\ [:class:`int`, :class:`str`]"
+    assert restify(Optional[int]) == ":py:obj:`~typing.Optional`\\ [:py:class:`int`]"
+    assert restify(Union[str, None]) == ":py:obj:`~typing.Optional`\\ [:py:class:`str`]"
+    assert restify(Union[int, str]) == (":py:obj:`~typing.Union`\\ "
+                                        "[:py:class:`int`, :py:class:`str`]")
 
     if sys.version_info >= (3, 7):
-        assert restify(Union[int, Integral]) == (":obj:`~typing.Union`\\ "
-                                                 "[:class:`int`, :class:`numbers.Integral`]")
+        assert restify(Union[int, Integral]) == (":py:obj:`~typing.Union`\\ "
+                                                 "[:py:class:`int`, :py:class:`numbers.Integral`]")
         assert (restify(Union[MyClass1, MyClass2]) ==
-                (":obj:`~typing.Union`\\ "
-                 "[:class:`tests.test_util_typing.MyClass1`, "
-                 ":class:`tests.test_util_typing.<MyClass2>`]"))
+                (":py:obj:`~typing.Union`\\ "
+                 "[:py:class:`tests.test_util_typing.MyClass1`, "
+                 ":py:class:`tests.test_util_typing.<MyClass2>`]"))
     else:
-        assert restify(Union[int, Integral]) == ":class:`numbers.Integral`"
-        assert restify(Union[MyClass1, MyClass2]) == ":class:`tests.test_util_typing.MyClass1`"
+        assert restify(Union[int, Integral]) == ":py:class:`numbers.Integral`"
+        assert restify(Union[MyClass1, MyClass2]) == ":py:class:`tests.test_util_typing.MyClass1`"
 
 
 @pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
@@ -109,58 +112,61 @@ def test_restify_type_hints_typevars():
     T_co = TypeVar('T_co', covariant=True)
     T_contra = TypeVar('T_contra', contravariant=True)
 
-    assert restify(T) == ":obj:`tests.test_util_typing.T`"
-    assert restify(T_co) == ":obj:`tests.test_util_typing.T_co`"
-    assert restify(T_contra) == ":obj:`tests.test_util_typing.T_contra`"
-    assert restify(List[T]) == ":class:`~typing.List`\\ [:obj:`tests.test_util_typing.T`]"
+    assert restify(T) == ":py:obj:`tests.test_util_typing.T`"
+    assert restify(T_co) == ":py:obj:`tests.test_util_typing.T_co`"
+    assert restify(T_contra) == ":py:obj:`tests.test_util_typing.T_contra`"
+    assert restify(List[T]) == ":py:class:`~typing.List`\\ [:py:obj:`tests.test_util_typing.T`]"
 
     if sys.version_info >= (3, 10):
-        assert restify(MyInt) == ":class:`tests.test_util_typing.MyInt`"
+        assert restify(MyInt) == ":py:class:`tests.test_util_typing.MyInt`"
     else:
-        assert restify(MyInt) == ":class:`MyInt`"
+        assert restify(MyInt) == ":py:class:`MyInt`"
 
 
 def test_restify_type_hints_custom_class():
-    assert restify(MyClass1) == ":class:`tests.test_util_typing.MyClass1`"
-    assert restify(MyClass2) == ":class:`tests.test_util_typing.<MyClass2>`"
+    assert restify(MyClass1) == ":py:class:`tests.test_util_typing.MyClass1`"
+    assert restify(MyClass2) == ":py:class:`tests.test_util_typing.<MyClass2>`"
 
 
 def test_restify_type_hints_alias():
     MyStr = str
     MyTuple = Tuple[str, str]
-    assert restify(MyStr) == ":class:`str`"
-    assert restify(MyTuple) == ":class:`~typing.Tuple`\\ [:class:`str`, :class:`str`]"
+    assert restify(MyStr) == ":py:class:`str`"
+    assert restify(MyTuple) == ":py:class:`~typing.Tuple`\\ [:py:class:`str`, :py:class:`str`]"
 
 
 @pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
 def test_restify_type_ForwardRef():
     from typing import ForwardRef  # type: ignore
-    assert restify(ForwardRef("myint")) == ":class:`myint`"
+    assert restify(ForwardRef("myint")) == ":py:class:`myint`"
 
 
 @pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
 def test_restify_type_Literal():
     from typing import Literal  # type: ignore
-    assert restify(Literal[1, "2", "\r"]) == ":obj:`~typing.Literal`\\ [1, '2', '\\r']"
+    assert restify(Literal[1, "2", "\r"]) == ":py:obj:`~typing.Literal`\\ [1, '2', '\\r']"
 
 
 @pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
 def test_restify_pep_585():
-    assert restify(list[str]) == ":class:`list`\\ [:class:`str`]"  # type: ignore
-    assert restify(dict[str, str]) == ":class:`dict`\\ [:class:`str`, :class:`str`]"  # type: ignore
-    assert restify(dict[str, tuple[int, ...]]) == \
-        ":class:`dict`\\ [:class:`str`, :class:`tuple`\\ [:class:`int`, ...]]"  # type: ignore
+    assert restify(list[str]) == ":py:class:`list`\\ [:py:class:`str`]"  # type: ignore
+    assert restify(dict[str, str]) == (":py:class:`dict`\\ "  # type: ignore
+                                       "[:py:class:`str`, :py:class:`str`]")
+    assert restify(dict[str, tuple[int, ...]]) == (":py:class:`dict`\\ "  # type: ignore
+                                                   "[:py:class:`str`, :py:class:`tuple`\\ "
+                                                   "[:py:class:`int`, ...]]")
 
 
 @pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
 def test_restify_type_union_operator():
-    assert restify(int | None) == ":class:`int` | :obj:`None`"  # type: ignore
-    assert restify(int | str) == ":class:`int` | :class:`str`"  # type: ignore
-    assert restify(int | str | None) == ":class:`int` | :class:`str` | :obj:`None`"  # type: ignore
+    assert restify(int | None) == ":py:class:`int` | :py:obj:`None`"  # type: ignore
+    assert restify(int | str) == ":py:class:`int` | :py:class:`str`"  # type: ignore
+    assert restify(int | str | None) == (":py:class:`int` | :py:class:`str` | "  # type: ignore
+                                         ":py:obj:`None`")
 
 
 def test_restify_broken_type_hints():
-    assert restify(BrokenType) == ':class:`tests.test_util_typing.BrokenType`'
+    assert restify(BrokenType) == ':py:class:`tests.test_util_typing.BrokenType`'
 
 
 def test_stringify():