From 4bec52d96a7c96691a0b69633bcf039142ed7769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gero=20Schw=C3=A4ricke?= Date: Tue, 2 Nov 2021 14:13:50 +0100 Subject: [PATCH] add tests and fix None rtype --- sphinx/ext/autodoc/typehints.py | 10 ++-- tests/test_ext_autodoc_configs.py | 89 +++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 4 deletions(-) diff --git a/sphinx/ext/autodoc/typehints.py b/sphinx/ext/autodoc/typehints.py index fef8ee35e98..9aba50bbf04 100644 --- a/sphinx/ext/autodoc/typehints.py +++ b/sphinx/ext/autodoc/typehints.py @@ -63,8 +63,8 @@ def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element for field_list in field_lists: if app.config.autodoc_typehints_description_target == "all": modify_field_list(field_list, annotations[fullname]) - elif (app.config.autodoc_typehints_description_target - == "returnvalue_and_documented_params"): + elif (app.config.autodoc_typehints_description_target == + "returnvalue_and_documented_params"): augment_descriptions_with_types( field_list, annotations[fullname], force_rtype=True ) @@ -174,10 +174,12 @@ def augment_descriptions_with_types( # Add 'rtype' if 'return' is present and 'rtype' isn't. if 'return' in annotations: - if 'return' not in has_type and (force_rtype or 'return' in has_description): + rtype = annotations['return'] + if 'return' not in has_type and ('return' in has_description or + (force_rtype and rtype != "None")): field = nodes.field() field += nodes.field_name('', 'rtype') - field += nodes.field_body('', nodes.paragraph('', annotations['return'])) + field += nodes.field_body('', nodes.paragraph('', rtype)) node += field diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py index e04cd83b681..f444fe265bc 100644 --- a/tests/test_ext_autodoc_configs.py +++ b/tests/test_ext_autodoc_configs.py @@ -878,6 +878,70 @@ def test_autodoc_typehints_description_no_undoc(app): in context) +@pytest.mark.sphinx('text', testroot='ext-autodoc', + confoverrides={'autodoc_typehints': "description", + 'autodoc_typehints_description_target': 'returnvalue_and_documented_params'}) +def test_autodoc_typehints_description_no_undoc_doc_rtype(app): + # No :type: will be injected for `incr`, which does not have a description + # for its parameters or its return, just :rtype: will be injected due to + # autodoc_typehints_description_target. `tuple_args` does describe both, so + # :type: and :rtype: will be added. `nothing` has no parameters but a return + # type of None, which will be added. + (app.srcdir / 'index.rst').write_text( + '.. autofunction:: target.typehints.incr\n' + '\n' + '.. autofunction:: target.typehints.decr\n' + '\n' + ' :returns: decremented number\n' + '\n' + '.. autofunction:: target.typehints.tuple_args\n' + '\n' + ' :param x: arg\n' + ' :return: another tuple\n' + '\n' + '.. autofunction:: target.typehints.Math.nothing\n' + '\n' + '.. autofunction:: target.typehints.Math.horse\n' + '\n' + ' :return: nothing\n' + ) + app.build() + context = (app.outdir / 'index.txt').read_text() + assert ('target.typehints.incr(a, b=1)\n' + '\n' + ' Return type:\n' + ' int\n' + '\n' + 'target.typehints.decr(a, b=1)\n' + '\n' + ' Returns:\n' + ' decremented number\n' + '\n' + ' Return type:\n' + ' int\n' + '\n' + 'target.typehints.tuple_args(x)\n' + '\n' + ' Parameters:\n' + ' **x** (*Tuple**[**int**, **Union**[**int**, **str**]**]*) -- arg\n' + '\n' + ' Returns:\n' + ' another tuple\n' + '\n' + ' Return type:\n' + ' Tuple[int, int]\n' + '\n' + 'target.typehints.Math.nothing(self)\n' + '\n' + 'target.typehints.Math.horse(self, a, b)\n' + '\n' + ' Returns:\n' + ' nothing\n' + '\n' + ' Return type:\n' + ' None\n' == context) + + @pytest.mark.sphinx('text', testroot='ext-autodoc', confoverrides={'autodoc_typehints': "description"}) def test_autodoc_typehints_description_with_documented_init(app): @@ -930,6 +994,31 @@ def test_autodoc_typehints_description_with_documented_init_no_undoc(app): ' **x** (*int*) -- Some integer\n' == context) +@pytest.mark.sphinx('text', testroot='ext-autodoc', + confoverrides={'autodoc_typehints': "description", + 'autodoc_typehints_description_target': 'returnvalue_and_documented_params'}) +def test_autodoc_typehints_description_with_documented_init_no_undoc_doc_rtype(app): + # see test_autodoc_typehints_description_with_documented_init_no_undoc + # returnvalue_and_documented_params should not change class or method + # docstring. + (app.srcdir / 'index.rst').write_text( + '.. autoclass:: target.typehints._ClassWithDocumentedInit\n' + ' :special-members: __init__\n' + ) + app.build() + context = (app.outdir / 'index.txt').read_text() + assert ('class target.typehints._ClassWithDocumentedInit(x)\n' + '\n' + ' Class docstring.\n' + '\n' + ' __init__(x)\n' + '\n' + ' Init docstring.\n' + '\n' + ' Parameters:\n' + ' **x** (*int*) -- Some integer\n' == context) + + @pytest.mark.sphinx('text', testroot='ext-autodoc', confoverrides={'autodoc_typehints': "description"}) def test_autodoc_typehints_description_for_invalid_node(app):