Allow me to add a GLORIOUS (but not perfect) solution to your list.
If you put an imported attribute without the full prefix within .. autosummary:: or after .. autodata::, Sphinx will insert the docstring of the attribute's type.
For example, if you write something like:
.. automodule:: rogue_scroll
.. autosummary::
SYLLABLES
.. autodata:: SYLLABLES
You would get something like:
It is not easy to automatically (not hardcode) get the full path of an imported attribute. Even inside a template, you do not have easy access to that information. But I am here to the rescue!
It is a three-step solution:
sphinx-autogen and template magic.sphinx-autogen is a command-line tool shipped with Sphinx. It is also used automatically under the hood by the directive .. autosummary:: with the option :toctree:.
I am using Python 3.13 and Sphinx 8.2.3.
├─ src/
│ └─ rogue_scroll/
│ ├─ __init__.py
│ └─ _scroll.py
└─ docs/
├─ Makefile
├─ *build/
└─ source/
├─ conf.py
├─ index.rst
├─ modules.rst
├─ autogen_attributes.rst
├─ *_attributes/
├─ *_autosummary/
├─ _static/
└─ _templates/
└─ autosummary/
├─ attributes.rst
└─ module.rst
*Generated during build
The attribute lists will be placed in the directory docs/source/_attributes/.
The final documentation will be generated in docs/build/.
Open the file docs/build/html/index.html to visualize it.
SOURCE_DIR := source
BUILD_DIR := build
TEMPLATES_DIR := $(SOURCE_DIR)/_templates
AUTOSUMMARY_DIR := $(SOURCE_DIR)/_autosummary
ATTRIBUTES_DIR := $(SOURCE_DIR)/_attributes
ATTRIBUTES_LIST := $(ATTRIBUTES_DIR)/list.rst
AUTOGEN_ATTR_FILE := $(SOURCE_DIR)/autogen_attributes.rst
.PHONY: html attributes
html: $(ATTRIBUTES_LIST)
sphinx-build -M html $(SOURCE_DIR) $(BUILD_DIR) -v -a -E
$(ATTRIBUTES_LIST): attributes
$(file > $(ATTRIBUTES_LIST),$(subst $(eval ) ,,\
$(foreach FILE,$(wildcard $(ATTRIBUTES_DIR)/*),\
$(firstword $(file < $(FILE))))))
attributes: $(AUTOGEN_ATTR_FILE)
rm -rf $(ATTRIBUTES_LIST)
export PYTHONPATH=../src && sphinx-autogen -i -t $(TEMPLATES_DIR) $<
When you execute make html to build the documentation, the Makefile will execute the following commands:
rm -rf source/_attributes/list.rst
export PYTHONPATH=../src && sphinx-autogen -i -t source/_templates source/autogen_attributes.rst
sphinx-build -M html source build -v -a -E
import os
import sys
sys.path.insert(0, os.path.abspath('../../src'))
project = "Project name"
author = "Author name"
version = "1.0.0"
release = version
copyright = f"2025, {author}"
extensions = ['sphinx.ext.autodoc','sphinx.ext.autosummary']
templates_path = ['_templates']
exclude_patterns = ['build', '_attributes', 'autogen_attributes.rst']
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
autosummary_imported_members = True
Note that autogen_attributes.rst and _attributes/ are listed in exclude_patterns, because they are only used to generate the attributes list and must not be included in the final documentation.
Also, I am using the "Read The Docs" theme because it is pretty :). You can install it executing pip install sphinx-rtd-theme.
Main page title
===============
.. toctree::
:maxdepth: 3
modules
Main file of the documentation.
Modules
=======
.. autosummary::
:toctree: _autosummary
:template: module
:recursive:
rogue_scroll
This file generates documentation for each module using the template docs/source/_templates/module.rst.
The reST files generated during build will be placed in docs/source/_autosummary.
.. autosummary::
:toctree: _attributes
:template: attributes
:recursive:
rogue_scroll
rogue_scroll._scroll
This is the main file parsed by sphinx-autogen to generate an attribute list for each module using the template docs/source/_templates/attributes.rst.
Usually, you would include only the top-level package (rogue_scroll), but Sphinx does not include private modules (any file starting with _) by default, so I had to include rogue_scroll._scroll manually.
{# PART 1 --------------------------------------------------------------------#}
{% for a in attributes -%}
{{ '%s.%s,' % (fullname, a) -}}
{% endfor -%}
{# PART 2 --------------------------------------------------------------------#}
{{ ',\n\n.. automodule:: %s\n\n' % fullname -}}
{% if modules -%}
{{ ' .. autosummary::\n'
' :toctree:\n'
' :template: attributes\n'
' :recursive:\n\n' -}}
{% for m in modules -%}
{{ ' %s\n' % m -}}
{% endfor -%}
{% endif -%}
This template is used to generate an attribute list for each module.
{{ fullname | underline }}
{# PART 1 --------------------------------------------------------------------#}
{% set attributes_file -%}
{% include '../_attributes/list.rst' -%}
{% endset -%}
{% set global_attributes = attributes_file.split(',') -%}
{% for a in global_attributes -%}
{% if a == '' -%}
{% set _ = global_attributes.pop(loop.index0) -%}
{% endif -%}
{% endfor -%}
{# PART 2 --------------------------------------------------------------------#}
{% set imported_attributes = [] -%}
{% for m in members -%}
{% if m not in modules ~ functions ~ classes ~ exceptions ~ attributes -%}
{% set outer_loop = loop -%}
{% for a in global_attributes -%}
{% if m == a.split('.')[-1] -%}
{% set _ = imported_attributes.append((a, m)) -%}
{% endif -%}
{% endfor -%}
{% endif -%}
{% endfor -%}
{# PART 3 --------------------------------------------------------------------#}
{{ '.. automodule:: %s' % fullname -}}
{% if attributes or imported_attributes -%}
{{ '\n\n .. autosummary::\n\n' -}}
{% for a in attributes -%}
{{ ' ~%s\n' % a -}}
{% endfor -%}
{% if imported_attributes -%}
{% for a in imported_attributes -%}
{{ ' ~%s\n' % (a[0].replace('%s.' % fullname, '', 1)) -}}
{% endfor -%}
{% endif -%}
{% endif -%}
{# PART 4 --------------------------------------------------------------------#}
{% if attributes or imported_attributes -%}
{% for a in attributes if a in members -%}
{{ '\n.. autodata:: %s\n' % a -}}
{% endfor -%}
{% if imported_attributes -%}
{% for a in imported_attributes -%}
{{ '\n.. py:data:: %s\n\n' % a[1] -}}
{{ ' .. autodata:: %s\n' % a[0] -}}
{{ ' :noindex:\n' -}}
{% endfor -%}
{% endif -%}
{% endif -%}
{# PART 5 --------------------------------------------------------------------#}
{{ '\n\nname: %s\n\n' % name -}}
{{ 'objname: %s\n\n' % objname -}}
{{ 'fullname: %s\n\n' % fullname -}}
{{ 'objtype: %s\n\n' % objtype -}}
{{ 'module: %s\n\n' % module -}}
{{ 'class: %s\n\n' % class -}}
{{ 'members: %s\n\n' % members -}}
{{ 'inherited_members: %s\n\n' % inherited_members -}}
{{ 'functions: %s\n\n' % functions -}}
{{ 'classes: %s\n\n' % classes -}}
{{ 'exceptions: %s\n\n' % exceptions -}}
{{ 'methods: %s\n\n' % methods -}}
{{ 'attributes: %s\n\n' % attributes -}}
{{ 'modules: %s\n\n' % modules -}}
{{ 'global_attributes:\n' -}}
{% for a in global_attributes -%}
{{ ' %s,\n' % a -}}
{% endfor -%}
{{ '\n' -}}
{{ 'imported_attributes:\n' -}}
{% for a in imported_attributes -%}
{{ ' (%s, %s),\n' % a -}}
{% endfor -%}
{{ '\n' -}}
This template is used to generate the documentation for each module.
global_attributes.global_attributes were imported and create the imported_attributes list.rogue_scroll.SYLLABLES is available. However, I cannot use directive .. autodata::, as mentioned in the problem section. So I decided to use .. py:data:: for the top-level attribute and, inside it, .. autodata:: for the full path attribute to get the correct docstring."""rogue_scroll docstring"""
from ._scroll import SYLLABLES
from ._scroll import SCROLL_PROBS
"""_scroll docstring"""
SYLLABLES = 123
"""SYLLABLES docstring"""
SCROLL_PROBS = 123
"""SCROLL_PROBS docstring"""
I created this answer specifically for your case (Merry Christmas!), but I posted a more complete and generic example on my GitHub. It is licensed under the "do whatever the f*** you want" license (MIT).
This solution is not absolute. It is just a result of many days of research and template tinkering. 100% AI-free. If you can improve this solution somehow, please let me know!