From bcebe71c8e0ab9ca5eb5bbb8098dd5520b0026d6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 26 Dec 2020 23:24:30 +0900 Subject: [PATCH 1/2] refactor: autodoc: Add DataDocumenter.get_module_comment() Similar to AttributeDocumenter.get_attribute_comment(), add a helper method DataDocumenter.get_module_comment() --- sphinx/ext/autodoc/__init__.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 021468fe997..e9d7a6f008c 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1914,8 +1914,32 @@ def get_real_modname(self) -> str: return self.get_attr(self.parent or self.object, '__module__', None) \ or self.modname + def get_module_comment(self, attrname: str) -> Optional[List[str]]: + try: + analyzer = ModuleAnalyzer.for_module(self.modname) + analyzer.analyze() + key = ('', attrname) + if key in analyzer.attr_docs: + return list(analyzer.attr_docs[key]) + except PycodeError: + pass + + return None + + def get_doc(self, encoding: str = None, ignore: int = None) -> List[List[str]]: + # Check the variable has a docstring-comment + comment = self.get_module_comment(self.objpath[-1]) + if comment: + return [comment] + else: + return super().get_doc(encoding, ignore) + def add_content(self, more_content: Optional[StringList], no_docstring: bool = False ) -> None: + # Disable analyzing variable comment on Documenter.add_content() to control it on + # DataDocumenter.add_content() + self.analyzer = None + if not more_content: more_content = StringList() From 9e9e486e658be27337cc4d78f99646dcc2b13c1e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 25 Dec 2020 14:28:28 +0900 Subject: [PATCH 2/2] Close #8022: autodoc: Allow to hide the value of the variables via metadata autodata and autoattribute directives does not show right-hand value of the variable if its docstring contains ``:meta hide-value:`` in info-field-list. --- CHANGES | 4 ++ doc/usage/extensions/autodoc.rst | 10 ++++ sphinx/ext/autodoc/__init__.py | 22 +++++++++ .../test-ext-autodoc/target/hide_value.py | 19 ++++++++ tests/test_ext_autodoc.py | 46 +++++++++++++++++++ tests/test_ext_autodoc_autoattribute.py | 26 +++++++++++ tests/test_ext_autodoc_autodata.py | 26 +++++++++++ 7 files changed, 153 insertions(+) create mode 100644 tests/roots/test-ext-autodoc/target/hide_value.py diff --git a/CHANGES b/CHANGES index 9360f347300..d8e6e6faabd 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,10 @@ Deprecated Features added -------------- +* #8022: autodoc: autodata and autoattribute directives does not show right-hand + value of the variable if docstring contains ``:meta hide-value:`` in + info-field-list + Bugs fixed ---------- diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst index 382d3160ccd..77340f49437 100644 --- a/doc/usage/extensions/autodoc.rst +++ b/doc/usage/extensions/autodoc.rst @@ -182,6 +182,16 @@ inserting them into the page source under a suitable :rst:dir:`py:module`, .. versionadded:: 3.1 + * autodoc considers a variable member does not have any default value if its + docstring contains ``:meta hide-value:`` in its :ref:`info-field-lists`. + Example: + + .. code-block:: rst + + var1 = None #: :meta hide-value: + + .. versionadded:: 3.5 + * Python "special" members (that is, those named like ``__special__``) will be included if the ``special-members`` flag option is given:: diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index e9d7a6f008c..8d9fe8e4a77 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -1883,6 +1883,17 @@ def import_object(self, raiseerror: bool = False) -> bool: return ret + def should_suppress_value_header(self) -> bool: + if super().should_suppress_value_header(): + return True + else: + doc = self.get_doc() + metadata = extract_metadata('\n'.join(sum(doc, []))) + if 'hide-value' in metadata: + return True + + return False + def add_directive_header(self, sig: str) -> None: super().add_directive_header(sig) sourcename = self.get_sourcename() @@ -2376,6 +2387,17 @@ def get_real_modname(self) -> str: return self.get_attr(self.parent or self.object, '__module__', None) \ or self.modname + def should_suppress_value_header(self) -> bool: + if super().should_suppress_value_header(): + return True + else: + doc = self.get_doc() + metadata = extract_metadata('\n'.join(sum(doc, []))) + if 'hide-value' in metadata: + return True + + return False + def add_directive_header(self, sig: str) -> None: super().add_directive_header(sig) sourcename = self.get_sourcename() diff --git a/tests/roots/test-ext-autodoc/target/hide_value.py b/tests/roots/test-ext-autodoc/target/hide_value.py new file mode 100644 index 00000000000..1d53aabe925 --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/hide_value.py @@ -0,0 +1,19 @@ +#: docstring +#: +#: :meta hide-value: +SENTINEL1 = object() + +#: :meta hide-value: +SENTINEL2 = object() + + +class Foo: + """docstring""" + + #: docstring + #: + #: :meta hide-value: + SENTINEL1 = object() + + #: :meta hide-value: + SENTINEL2 = object() diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 1fa0c1d7dfa..39897eb7da1 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -2235,3 +2235,49 @@ def test_name_mangling(app): ' name of Foo', '', ] + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_hide_value(app): + options = {'members': True} + actual = do_autodoc(app, 'module', 'target.hide_value', options) + assert list(actual) == [ + '', + '.. py:module:: target.hide_value', + '', + '', + '.. py:class:: Foo()', + ' :module: target.hide_value', + '', + ' docstring', + '', + '', + ' .. py:attribute:: Foo.SENTINEL1', + ' :module: target.hide_value', + '', + ' docstring', + '', + ' :meta hide-value:', + '', + '', + ' .. py:attribute:: Foo.SENTINEL2', + ' :module: target.hide_value', + '', + ' :meta hide-value:', + '', + '', + '.. py:data:: SENTINEL1', + ' :module: target.hide_value', + '', + ' docstring', + '', + ' :meta hide-value:', + '', + '', + '.. py:data:: SENTINEL2', + ' :module: target.hide_value', + '', + ' :meta hide-value:', + '', + ] diff --git a/tests/test_ext_autodoc_autoattribute.py b/tests/test_ext_autodoc_autoattribute.py index 7f79ca332bb..659c8c0145f 100644 --- a/tests/test_ext_autodoc_autoattribute.py +++ b/tests/test_ext_autodoc_autoattribute.py @@ -189,3 +189,29 @@ def test_autoattribute_TypeVar(app): " alias of TypeVar('T1')", '', ] + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autoattribute_hide_value(app): + actual = do_autodoc(app, 'attribute', 'target.hide_value.Foo.SENTINEL1') + assert list(actual) == [ + '', + '.. py:attribute:: Foo.SENTINEL1', + ' :module: target.hide_value', + '', + ' docstring', + '', + ' :meta hide-value:', + '', + ] + + actual = do_autodoc(app, 'attribute', 'target.hide_value.Foo.SENTINEL2') + assert list(actual) == [ + '', + '.. py:attribute:: Foo.SENTINEL2', + ' :module: target.hide_value', + '', + ' :meta hide-value:', + '', + ] diff --git a/tests/test_ext_autodoc_autodata.py b/tests/test_ext_autodoc_autodata.py index d3f63f4326c..c80ae5a81d1 100644 --- a/tests/test_ext_autodoc_autodata.py +++ b/tests/test_ext_autodoc_autodata.py @@ -129,3 +129,29 @@ def test_autodata_TypeVar(app): " alias of TypeVar('T1')", '', ] + + +@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.') +@pytest.mark.sphinx('html', testroot='ext-autodoc') +def test_autodata_hide_value(app): + actual = do_autodoc(app, 'data', 'target.hide_value.SENTINEL1') + assert list(actual) == [ + '', + '.. py:data:: SENTINEL1', + ' :module: target.hide_value', + '', + ' docstring', + '', + ' :meta hide-value:', + '', + ] + + actual = do_autodoc(app, 'data', 'target.hide_value.SENTINEL2') + assert list(actual) == [ + '', + '.. py:data:: SENTINEL2', + ' :module: target.hide_value', + '', + ' :meta hide-value:', + '', + ]