Last active
October 15, 2021 23:58
-
-
Save lelandbatey/bea331e365ee60d1fe55195f048b5498 to your computer and use it in GitHub Desktop.
Generate sphinx docs for an entire module, recursively
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
''' | |
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