Skip to content

Instantly share code, notes, and snippets.

@lelandbatey
Last active October 15, 2021 23:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lelandbatey/bea331e365ee60d1fe55195f048b5498 to your computer and use it in GitHub Desktop.
Save lelandbatey/bea331e365ee60d1fe55195f048b5498 to your computer and use it in GitHub Desktop.
Generate sphinx docs for an entire module, recursively
'''
CLI tool for generating sphinx auto-doc compatible files for all the python
modules in a directory. I'm using this to generate raw Sphinx docs for a big
disorganized, undocumented Python repo.
'''
# From here: https://stackoverflow.com/a/54323162
import sys
from setuptools import find_packages
from pkgutil import iter_modules
def find_modules(path):
modules = set()
for pkg in find_packages(path):
modules.add(pkg)
pkgpath = path + '/' + pkg.replace('.', '/')
if sys.version_info.major == 2 or (
sys.version_info.major == 3 and sys.version_info.minor < 6
):
for _, name, ispkg in iter_modules([pkgpath]):
if not ispkg:
modules.add(pkg + '.' + name)
else:
for info in iter_modules([pkgpath]):
if not info.ispkg:
modules.add(pkg + '.' + info.name)
return modules
def is_pkg_initpkg(modname, mods):
for m in mods:
if modname in m:
return True
return False
class Pkginfo:
def __init__(self, modname: str, is_initpkg: bool):
self.modname = modname
self.filename = self.modname.replace('.', '_')
self.is_initpkg = is_initpkg
@property
def doc_filename(self):
return self.filename + '.rst'
@property
def doc_file_contents(self):
return f'''
{self.modname}
{len(self.modname)*'='}
.. automodule:: {self.modname}
:members:
'''
@property
def index_toc_entry(self):
return ' ' + self.filename
def replace_index_rst(mods):
lines = list()
with open('index.rst', 'r') as index:
for line in index:
line = line.rstrip('\n')
lines.append(line)
if '===' in line:
break
original_lines = '\n'.join(lines)
toc_mods = '\n'.join([x.index_toc_entry for x in mods])
contents = f'''
{original_lines}
.. toctree::
:maxdepth: 2
:caption: Contents:
{toc_mods}
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`'''
with open('index.rst', 'w+') as index:
index.write(contents)
def main():
search_dir = './'
if len(sys.argv) > 1:
search_dir = sys.argv[1]
mods = find_modules(search_dir)
pkginfos = list()
for mod in mods:
is_initpkg = is_pkg_initpkg(mod, mods)
pinfo = Pkginfo(mod, is_initpkg)
pkginfos.append(pinfo)
# print('''
# .. toctree::
# :maxdepth: 2
# :caption: Contents:
# ''')
pkginfos = sorted(pkginfos, key=lambda x: x.modname)
for pinfo in pkginfos:
with open(pinfo.doc_filename, 'w+') as pkg_doc:
pkg_doc.write(pinfo.doc_file_contents)
# print(pinfo.index_toc_entry)
replace_index_rst(pkginfos)
if __name__ == '__main__':
# print('\n'.join(find_modules("./")))
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment