Repository: sphinx-doc/sphinx

Issue:
automodule :undoc-members: does not include undocumented module constants
The only way of getting undocumented constants defined in the module into the documentation seems to be to explicitly define:

```
   .. py:data:: SOMECONST
```

It would be less surprising if :undoc-members: option to automodule sucked in undocumented constants as well.

---
- Bitbucket: https://bitbucket.org/birkenfeld/sphinx/issue/1063
- Originally reported by: Daniel Svensson
- Originally created at: 2012-12-18T22:32:14.449


Discussion:

Patch:
diff --git a/CHANGES b/CHANGES
index 2363ea759..c228978ec 100644
--- a/CHANGES
+++ b/CHANGES
@@ -90,6 +90,8 @@ Features added
   autodoc considers values as a docstring of the attribute
 * #6361: autodoc: Add :confval:`autodoc_typehints` to suppress typehints from
   signature
+* #1063: autodoc: ``automodule`` directive now handles undocumented module level
+  variables
 * #6212 autosummary: Add :confval:`autosummary_imported_members` to display
   imported members on autosummary
 * #6271: ``make clean`` is catastrophically broken if building into '.'
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 7172553ac..f66269a6b 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -549,8 +549,10 @@ def filter_members(self, members, want_all):
 
         if self.analyzer:
             attr_docs = self.analyzer.find_attr_docs()
+            tagorder = self.analyzer.tagorder
         else:
             attr_docs = {}
+            tagorder = {}
 
         # process members and determine which to skip
         for (membername, member) in members:
@@ -580,12 +582,13 @@ def filter_members(self, members, want_all):
                         membername in self.options.special_members:
                     keep = has_doc or self.options.undoc_members
             elif (namespace, membername) in attr_docs:
+                has_doc = bool(attr_docs[namespace, membername])
                 if want_all and membername.startswith('_'):
                     # ignore members whose name starts with _ by default
-                    keep = self.options.private_members
+                    keep = has_doc and self.options.private_members
                 else:
                     # keep documented attributes
-                    keep = True
+                    keep = has_doc
                 isattr = True
             elif want_all and membername.startswith('_'):
                 # ignore members whose name starts with _ by default
@@ -594,6 +597,8 @@ def filter_members(self, members, want_all):
             else:
                 # ignore undocumented members if :undoc-members: is not given
                 keep = has_doc or self.options.undoc_members
+                # module top level item or not
+                isattr = membername in tagorder
 
             # give the user a chance to decide whether this member
             # should be skipped
@@ -1291,6 +1296,10 @@ def get_real_modname(self):
         return self.get_attr(self.parent or self.object, '__module__', None) \
             or self.modname
 
+    def get_doc(self, encoding=None, ignore=1):
+        # type: (str, int) -> List[List[str]]
+        return []
+
 
 class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter):  # type: ignore
     """


Test patch:
diff --git a/tests/roots/test-ext-autodoc/target/module.py b/tests/roots/test-ext-autodoc/target/module.py
new file mode 100644
index 000000000..d5b557d94
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/module.py
@@ -0,0 +1,4 @@
+#: docstring for CONSTANT1
+CONSTANT1 = ""
+
+CONSTANT2 = ""
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index f6f5a618c..518d23e8c 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -1689,6 +1689,30 @@ def test_partialmethod(app):
     assert list(actual) == expected
 
 
+@pytest.mark.usefixtures('setup_test')
+def test_module_variables():
+    options = {"members": None,
+               "undoc-members": True}
+    actual = do_autodoc(app, 'module', 'target.module', options)
+    assert list(actual) == [
+        '',
+        '.. py:module:: target.module',
+        '',
+        '',
+        '.. py:data:: CONSTANT1',
+        '   :module: target.module',
+        "   :annotation: = ''",
+        '',
+        '   docstring for CONSTANT1',
+        '   ',
+        '',
+        '.. py:data:: CONSTANT2',
+        '   :module: target.module',
+        "   :annotation: = ''",
+        '',
+    ]
+
+
 @pytest.mark.sphinx('html', testroot='ext-autodoc')
 def test_autodoc_typehints_signature(app):
     app.config.autodoc_typehints = "signature"