Build Status

adoc project

This program generates HTML documentation for Python projects by parsing the source code using Python's AST module. As such the source code is not loaded, it never reaches the interpreter and side-effects are effortlessly avoided.

This projet targets Python 3.5 and above.

Features

  • AST-based Python parsing
  • Fully setup.py -based configuration
  • MD/RST docstrings support
  • Additional files inclusion as separate doc sections
  • Built-in HTTP live-server
  • Single HTML, PDF or Markdown artifact

Long-term Goals

  • Symbol resolution, allowing linking through symbols within the document
  • JPEG/PNG media inclusion (only one final artifact)
  • In-document Graphviz processing

Installation

This is fairly straighforward:

pip install adoc

If you wish to output PDF documentation you'll need to install a different flavour of adoc:

pip install adoc[pdf]

Usage

For iteractive generation:

adoc --http .

For an HTML export:

adoc --html docs/index.html .

Hacking on the project

Prepare a virtual environment:

python -m venv env
source env/bin/activate
pip install -r requirements.txt
pip install -r requirements-pdf.txt
pip install -r requirements-test.txt

Run the test suite:

pytest .

To start the web server:

python -m adoc --http .

FAQ

Why aren't my Python packages discovered properly?

adoc relies on either setup.py or find_packages() for package discovery. If you have any issue, you may want to check these places first.

The result of find_packages() is easy to reproduce:

>>> from setuptools import find_packages
>>> find_packages('.')
['adoc', 'adoc.formats']

If a package appears missing even though it's a valid package, you'll probably want to make sure it contains __init__.py since find_packages() relies on these files to determine what is a package and what is not (even though they are not required in recent Python versions).

Why is one of my packages not showing up at all?

adoc is probably detecting that your package is empty. Here's the definition of an empty package:

  • Your package does not hold any function
  • Your package does not hold any class
  • Your package does not hold any package

That definition is recursive so that a package holding empty packages is considered empty

My setup.py is weird, I don't want to rely on it.

adoc allows you to override most relevant directives:

  • --package-dir: where packages are located.
  • --packages: manually list packages.
  • --no-setup: ignore setup.py altogether.
  • --find-packages: force-discover packages.

I don't have a setup.py.

adoc will attempt to rely on setup.py as much as possible, but it will resort to find_packages() if it has to (and there are still command line overrides as well).

On a typical project, there wouldn't be any noticeable difference:

adoc --http .

However, you may need to adjust a couple of things if the documentaion comes out as expected:

adoc --package-dir=src --http .

See examples/django-project and the corresponding HTML documentation to get an idea.

My project is just a bunch of scripts.

adoc supports scripts declared in setup.py and these can be specified manually on the command-line as well:

adoc --scripts=main.py --http .

See examples/appengine-project and the corresponding HTML documentation to get an idea.

Examples

API Reference

Module adoc

Module adoc.cli

Command-line interface.

This module exports the program's entry point: main.

Functions

def cli_setup(

)

CLI arguments parser setup.

def cli_compat(

ap)

CLI backward compatibility.

def logging_setup(

verbose)

def main(

args=None)

Program entry point.

This is where command line arguments are configured and read. Then the configuration is fine-tuned for execution.

Classes

class SplitAppend

Argument parsing action for repeatable csv strings.

Ancestors

Methods

def __call__(

self, parser, namespace, values, option_string=None)

Module adoc.codegen

Python source code generation.

Functions

def lookup(

mapping, atom)

def make_signature(

node)

def make_python(

node)

Generate Python code from an AST node (a subtree).

Module adoc.errors

Classes

class FatalError

Ancestors

Methods

def __init__(

self, message, tb=None)

def log(

self, return_with=0)

Module adoc.formats

Module adoc.formats.md

Markdown formatter.

Functions

def format_md(

text)

Format Markdown text to HTML.

Module adoc.formats.rst

reStructuredText formatter.

This module aims to bring a decent level of compatibility with Sphinx without relying on anything except docutils.

This is obviously still a work in progress.

Docutils resources:

  • https://docutils.readthedocs.io/en/sphinx-docs/howto/rst-directives.html
  • https://docutils.readthedocs.io/en/sphinx-docs/howto/rst-roles.html

Sphinx resources:

  • http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
  • http://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html
  • http://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html
  • http://www.sphinx-doc.org/en/master/usage/restructuredtext/field-lists.html
  • http://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html

Functions

def dummy_role(

role, rawtext, text, lineno, inliner, options={}, content=[])

def symbol_lookup(

role, rawtext, text, lineno, inliner, options={}, content=[])

def emphasis(

role, rawtext, text, lineno, inliner, options={}, content=[])

def format_rst(

text)

Format reStructuredText text to HTML.

Classes

class DummyDirective

Generic directive that does nothing.

Ancestors

Methods

def run(

self)

class VersionAdmonition

Version-based admonitions.

Supports the following construction:

.. deprecated:: 0.1
    This method is ...

Ancestors

Methods

def run(

self)

Module adoc.httpd

Documentaion HTTP server.

This module provides an HTTP server exclusively for the purpose of serving HTML documentation over HTTP.

Classes

class RequestHandler

Documentation HTTP request handler.

It will serve HTML documentation for the project at project_path. A documentation server will only accept HEAD and GET requests and reply with 404s for any request that's not made on /.

Ancestors

Methods

def send_headers(

self)

def do_HEAD(

self)

Respond to HEAD requests.

def do_GET(

self)

Respond to GET requests.

Only / will be served, other paths will result in 404s.

def log_message(

self, format, *args)

class Server

Documentation HTTP server.

It will reponde to HTTP requests using RequestHandler.

Ancestors

Methods

def __init__(

self, host, port, parser, docstrings_format, strip_docstrings)

Module adoc.models

Project modelisation objects.

These data structures allow the modelisation of a project's documentation and source code.

They are handled mostly while parsing a project's source code and later transmited to the HTML writer.

Functions

def walk(

root, attr, max_depth=-1)

Classes

class Atom

Lowest-level unit representing a source code unit.

Methods

def __init__(

self, name, doc=None)

def __str__(

self)

@property

def type(

self)

@property
@memoized

def fully_qualified_name(

self)

Recursively generate an Atom's fully qualified name.

@classmethod

def from_ast(

cls, node)

Build Atom instances from an AST node.

class ParametersMixin

Mixin for classes that hold parameters.

Methods

def add_parameters(

self, parameters)

Add parameters representation (ie. str).

class DecoratorsMixin

Mixin for classes that hold decorators.

Methods

def add_decorators(

self, decorators)

Add decorators, (ie. str).

class FunctionsMixin

Mixin for classes that hold Function instances.

Methods

def is_empty(

self)

def add_function(

self, function)

Add a Function instance, setting self as its parent.

class Function

Representation of a function.

Ancestors

Methods

@classmethod

def from_ast(

cls, node)

Build a Function instance from an AST node.

class ClassesMixin

Mixin for classes that hold Class instances.

Methods

def is_empty(

self)

def add_class(

self, klass)

Add a Class instance, setting self as its parent.

class Class

Representation of a class.

Ancestors

Methods

def add_base(

self, base)

@classmethod

def from_ast(

cls, node)

Build a Class instance from an AST node.

class ModulesMixin

Mixin for classes that hold Module instances.

Methods

def is_empty(

self)

def add_module(

self, module)

Add a Module instance, setting self as its parent.

class Module

Representation of a module.

No distinction is made between modules and packages.

Ancestors

Methods

def is_empty(

self)

def merge(

self, module)

@classmethod

def from_ast(

cls, node, name)

Build a Module instance from an AST node.

class Project

Representation of a project.

Ancestors

Methods

def __init__(

self, name, doc, metadata=None)

def add_document(

self, document)

def has_meta(

self, *keys)

def get_meta(

self, key, default=None)

def iter_modules(

self, max_depth=-1)

def iter_functions(

self, max_depth=-1)

def iter_classes(

self, max_depth=-1)

class Document

Representation of a document.

Supported document formats are html, md and rst.

Methods

def __init__(

self, filename)

@property
@memoized

def html(

self)

Format a document to HTML.

The value of this property is cached for better performances.

@property
@memoized

def title(

self)

Extract a document's title.

For this to be possible, it has first to formatted to HTML.

The value of this property is cached for better performances.

Module adoc.parser

High-level parsing functions.

Classes

class ProjectParser

Methods

def __init__(

self, path, overrides, no_setup=False, exclude=None, find_packages=False, documents=None)

def parse(

self)

Parse a project, setting the current working directiory.

def parse_project(

self)

Parse a Python project in the current working directory.

def find_readme(

self)

Parse a README file in the current working directory.

def setup_exists(

self)

def load_setup(

self)

Parse a setup.py file in the current working directory.

def parse_module(

self, path, name)

Parse a Python module.

def parse_file(

self, path, name, strip_ext=True)

Parse a Python file.

Module adoc.utils

Functions

def memoized(

f)

def compose(

*functions)

Classes

class WorkingDirectory

Methods

def __init__(

self, directory)

def __enter__(

self)

def __exit__(

self, *args)

Module adoc.writers

Module adoc.writers.html

HTML writer.

Functions

def write_html(

filename, project, docstrings_format='md', strip_docstrings=False)

def make_html(

project, docstrings_format, strip_docstrings)

Module adoc.writers.md

Markdown writer.

Functions

def write_md(

filename, project, docstrings_format, strip_docstrings)

def make_md(

project, docstrings_format, strip_docstrings)

Module adoc.writers.pdf

Functions

def write_pdf(

filename, project, docstrings_format, strip_docstring)

def make_pdf(

project, docstrings_format, strip_docstring)

Classes

class WeasyPrintLogFilter

Ancestors

Methods

def filter(

self, record)