Just enough UI to let a build server generate documentation
import logging
import os
import pkgutil
from shutil import copy2
from typing import List, Optional, Tuple, Union
import pydoc_fork.formatter_html as html
from pydoc_fork.custom_types import TypeLike
from pydoc_fork.all_found import MENTIONED_MODULES
from pydoc_fork.format_page import render
from pydoc_fork.path_utils import _adjust_cli_sys_path, locate_file
from pydoc_fork.utils import describe, resolve
LOGGER = logging.getLogger(__name__)
MR output_folder Write HTML documentation to a file in the current directory.
def writedoc(
thing: Union[TypeLike, str],
output_folder: str,
document_internals: bool,
forceload: int = 0,
) -> Optional[str]:
try:
the_object, name = resolve(thing, forceload)
MR should go in constructor, but what? no constructor
html.OUTPUT_FOLDER = output_folder
html.DOCUMENT_INTERNALS = document_internals
page_out = render(describe(the_object), the_object, name)
MR output_folder + os.sep
full_path = calculate_file_name(name, output_folder)
with open(full_path, "w", encoding="utf-8") as file:
file.write(page_out)
print("wrote", name + ".html")
return full_path
except (ImportError, ErrorDuringImport) as value: print(value) return “”
If this was written, what would its name be
def calculate_file_name(name: str, output_folder: str) -> str:
if name is None:
print("What")
full_path = output_folder + os.sep + name + ".html"
full_path = (
full_path.replace("<", "")
.replace(">", "")
.replace(":", "")
.replace(",", "_")
.replace(" ", "_")
.replace("(", "")
.replace(")", "")
)
return full_path
Write out HTML documentation for all modules in a directory tree.
def write_docs_per_module(
modules: List[str],
output_folder: str,
document_internals: bool,
skip_if_written: bool = False,
) -> List[str]:
This is going to handle filesystem paths, e.g. ./module/submodule.py There will be ANOTHER method to handle MODULE paths, e.g. module.submodule” Attempting to mix these two types is a bad idea.
written: List[str] = []
for module in modules:
file
if module.lower().endswith(".py"):
full_path = writedoc(module[:-3], output_folder, document_internals)
if full_path:
written.append(full_path)
else:
full_path = writedoc(module, output_folder, document_internals)
if full_path:
written.append(full_path)
”.” needs to mean pwd… does it?
full_paths = writedocs(
".", output_folder, document_internals, for_only=module
)
written.extend(full_paths)
One pass, not ready to walk entire tree.
third_party_written = write_docs_live_module(
output_folder, document_internals, 0, skip_if_written
)
written.extend(third_party_written)
return written
def write_docs_live_module(
modules: List[Tuple[TypeLike, str]], Write out HTML documentation for all modules in a directory tree.
output_folder: str,
document_internals: bool,
total_third_party: int = 0,
skip_if_written: bool = False,
) -> List[str]:
This is going to handle filesystem paths, e.g. ./module/submodule.py There will be ANOTHER method to handle MODULE paths, e.g. module.submodule” Attempting to mix these two types is a bad idea.
written: List[str] = []
while MENTIONED_MODULES and total_third_party <= 100:
print(len(MENTIONED_MODULES))
module = MENTIONED_MODULES.pop()
thing, name = module # destructure it
should only be live modules or dot notation modules, not paths.
full_path = calculate_file_name(name, output_folder)
if os.path.exists(full_path) and skip_if_written:
MENTIONED_MODULES.discard(module)
else:
full_path = writedoc(thing, output_folder, document_internals)
total_third_party += 1
if full_path:
written.append(full_path)
MENTIONED_MODULES.discard(module)
TODO: make this a param
return written
Write out HTML documentation for all modules in a directory tree.
def writedocs(
source_directory: str,
output_folder: str,
document_internals: bool,
for_only: str = "",
) -> List[str]:
if done is None: done = {}
pkgpath = ""
walk packages is why pydoc drags along with it tests folders
LOGGER.debug(f"Walking packages for {source_directory}")
full_paths: List[str] = []
for _, modname, _ in pkgutil.walk_packages([source_directory], pkgpath):
if not str(modname).startswith(for_only):
continue
LOGGER.debug(f"docing {modname})")
full_path = writedoc(modname, output_folder, document_internals)
if full_path:
full_paths.append(full_path)
return full_paths
Command-line interface (looks at sys.argv to decide what to do).
def cli(
files: List[str],
output_folder: str,
document_internals: bool,
overwrite_existing: bool = False,
) -> List[str]:
LOGGER.debug(f"{files}, {output_folder}, {document_internals}")
if not os.path.exists(output_folder):
os.makedirs(output_folder)
copy2(locate_file("templates/style.css", __file__), output_folder)
_adjust_cli_sys_path()
opts, args = getopt.getopt(sys.argv[1:], ‘bk:n:p:w’)
return write_docs_per_module(
files, output_folder, document_internals, skip_if_written=not overwrite_existing
)
for file in files: if ispath(file) and not os.path.exists(file): print(“file %r does not exist” % files) break try: # single file module if ispath(file) and os.path.isfile(file): # new type assigned to same name arg_type = importfile(file) writedoc(arg_type, output_folder, document_internals) # directory elif ispath(file) and os.path.isdir(file): print(f”We think this is a path & a directory 1”)
elif (
isinstance(files, str) and os.path.exists(files) and os.path.isdir(files)
):
print(f"We think this is a path & a directory 2")
write_docs_per_module(files, output_folder, document_internals)
else:
print(f"We think this is a built in or something on the PYTHONPATH")
# built ins?
write_docs_per_module(files, output_folder, document_internals)
# raise TypeError("Not a file, not a directory")
except ErrorDuringImport as value:
print(value)
if __name__ == "__main__":
cli([".\\"], output_folder="docs_api", document_internals=True)
cli(["pydoc_fork"], output_folder="docs_api", document_internals=True)
cli(["sys"], output_folder="docs_api", document_internals=False)
cli(
["cats"], output_folder="docs_api", document_internals=False
) # writes cats.html, even tho this isn't a module!
cli(["inspect"], output_folder="docs_api", document_internals=False)