From c5b35efce353a3e1670185704bcd1453b9da3533 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 5 Sep 2021 18:28:27 +0900 Subject: [PATCH] Close #9560: autodoc: Allow to refer NewType with modname in py310+ Before 3.10, an instance of NewType has incorrect module name. But it was fixed on 3.10. This starts to use the module info if the interpreter is 3.10+. --- CHANGES | 2 ++ sphinx/util/typing.py | 14 +++++++++++--- tests/test_util_typing.py | 11 +++++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 5c75a1d62d9..79a7a87bb4c 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,8 @@ Features added * #9445: autodoc: Support class properties * #9479: autodoc: Emit a warning if target is a mocked object +* #9560: autodoc: Allow to refer NewType instances with module name in Python + 3.10 or above * #9447: html theme: Expose the version of Sphinx in the form of tuple as a template variable ``sphinx_version_tuple`` * #9445: py domain: ``:py:property:`` directive supports ``:classmethod:`` diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index cf4318cdabb..004fb4a71c1 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -116,7 +116,12 @@ def restify(cls: Optional[Type]) -> str: elif cls in INVALID_BUILTIN_CLASSES: return ':class:`%s`' % INVALID_BUILTIN_CLASSES[cls] elif inspect.isNewType(cls): - return ':class:`%s`' % cls.__name__ + 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__) + else: + return ':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) @@ -307,8 +312,11 @@ def stringify(annotation: Any) -> str: else: return '.'.join([annotation.__module__, annotation.__name__]) elif inspect.isNewType(annotation): - # Could not get the module where it defined - return annotation.__name__ + if sys.version_info > (3, 10): + # newtypes have correct module info since Python 3.10+ + return '%s.%s' % (annotation.__module__, annotation.__name__) + else: + return annotation.__name__ elif not annotation: return repr(annotation) elif annotation is NoneType: diff --git a/tests/test_util_typing.py b/tests/test_util_typing.py index 9cb1d61ef5f..bbee68f82cc 100644 --- a/tests/test_util_typing.py +++ b/tests/test_util_typing.py @@ -113,7 +113,11 @@ def test_restify_type_hints_typevars(): 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(MyInt) == ":class:`MyInt`" + + if sys.version_info >= (3, 10): + assert restify(MyInt) == ":class:`tests.test_util_typing.MyInt`" + else: + assert restify(MyInt) == ":class:`MyInt`" def test_restify_type_hints_custom_class(): @@ -250,7 +254,10 @@ def test_stringify_type_hints_typevars(): assert stringify(T_contra) == "tests.test_util_typing.T_contra" assert stringify(List[T]) == "List[tests.test_util_typing.T]" - assert stringify(MyInt) == "MyInt" + if sys.version_info >= (3, 10): + assert stringify(MyInt) == "tests.test_util_typing.MyInt" + else: + assert stringify(MyInt) == "MyInt" def test_stringify_type_hints_custom_class():