Skip to content

Handsdown

Root of handsdown source code.

Handsdown

Python package handsdown provides types, functions, and a command-line interface for accessing public documentation of Python modules, and for presenting it in a Markdown format ready to be publiched to GitHub Pages or Read the Docs.

handsdown extracts documentation of:

  • Modules
  • docstring
  • submodules
  • function definitions
  • class definitions
  • global variables documented in comment-style
  • global objects mentioned in docstring
  • Functions
  • docstring
  • arguments
  • decorators
  • type annotations
  • global objects mentioned in docstring
  • parent module objects mentioned in docstring
  • Classes
  • docstring
  • __init__ method
  • public methods
  • magic methods with defined docstring
  • attributes documented in comment-style
  • decorators
  • base classes
  • global objects mentioned in docstring
  • parent module objects mentioned in docstring
  • Class methods
  • docstring
  • arguments
  • decorators
  • type annotations
  • global objects mentioned in docstring
  • parent class objects mentioned in docstring
  • parent module objects mentioned in docstring

Prepare your code

# main_example.py
"""
This is a module docstring. It will appear in documentation.

## Notes

You can use Markdown here to make it nicer. Also, in any docstring you
can put a global object import string in backticks, like `other_module.OtherClass`,
and it will be transformed to a link.
"""
from typing import Text, TYPE_CHECKING

from my_project.other_module import BaseClass  # pylint: disable=import-error

if TYPE_CHECKING:
    from my_project.type_defs import StuffCallable  # pylint: disable=import-error

# This is a comment-style documented global variable, so it is added to
# `main_example` module attributes with this comment as a documentation for it
# FIXME: FIXME and TODO comments are igonred
MODULE_NAME = "My Module"

# Private args never appear in docs
_PRIVATE_ATTR = "Private attr"


def hello(name: Text) -> Text:
    """
    This is module function and it is added to documentation even if it does
    not have a docstring. Function signature will be also generated respecting
    regular and comment-style type annotations. Let's use PEP 257 format here.

    Examples::

        # Google-style code block here, but we could use Markdown code block as well
        >>> hello('John')
        'Hello, John!'

        >>> hello('')
        'Hello!'

    Arguments:
        name -- Name of the person to greet.

    Returns:
        A greeting. No need to put types here if you use type annotations.
    """
    if not name:
        return "Hello!"

    return f"Hello, {name}!"


class MyClass(BaseClass):
    """
    MyClass documentation here.

    .. note:: This time we use RST docstrings format.
    """

    # This is a comment-style documented class attribute, so it is added to
    # `main_example.MyClass` attributes with this comment as a documentation for it.
    STUFF_COUNT = 3

    @classmethod
    def do_something(cls, stuff):
        # type: (StuffCallable) -> bool
        """
        This is a public method that uses comment-style type annotations. If decorators
        or types from annotations are from your project, links to them will be added
        to `See also` section. Since this function depends on `STUFF_COUNT`, we can add
        it to a docstring in backticks and it will be transformed to a link.

        .. code-block:: python

            # usage example
            def my_stuff(amount):
                return amount > 5

            MyClass.do_something(my_stuff)  # False

        .. versionadded:: 1.3
        .. deprecated:: 1.8
        .. versionchanged:: 1.4
            All these directives are added to `Notes` section and formatted in Sphinx-style.

        :param stuff: Function do execute.
        :returns: `stuff` result.
        """
        return stuff(cls.STUFF_COUNT)

    def __bool__(self):
        # type: () -> bool
        """
        Magic methods are added to docs only if they have docstrings.

        :returns: True if `STUFF_COUNT` is not zero
        """
        return self.STUFF_COUNT

Usage

From command line

Just go to your favorite project that has lots of docstrings but missing auto-generated docs and let handsdown do the thing.

cd ~/my/project

# build documentation *.md* files in docs/* directory
handsdown

# or provide custom output directory: output_dir/*
handsdown -o output_dir

# generate docs only for my_module, but exclude migrations
handsdown my_module --exclude 'build/*' 'tests/*' 'test/*' '*/__pycache__/*' '.*/*' 'my_module/migrations'

# generate documentation for deployment
handsdown --external `git config --get remote.origin.url` -n ProjectName

Navigate to docs/README.md to check your new documentation!

As a module

from handsdown.generator import Generator
from handsdown.utils.path_finder import PathFinder

# this is our project root directory
repo_path = Path.cwd()

# this little tool works like `pathlib.Path.glob` with some extra magic
# but in this case `repo_path.glob("**/*.py")` would do as well
path_finder = PathFinder(repo_path, "**/*.py")

# no docs for tests and build
path_finder.exclude("tests/*", "build/*")

# initialize generator
handsdown = Generator(
    input_path=repo_path,
    output_path=repo_path / 'output',
    source_paths=path_finder.glob("**/*.py")
)

# generate all docs at once
handsdown.generate_docs()

# or generate just for one doc
handsdown.generate_doc(repo_path / 'my_module' / 'source.py')

# and generate index.md file
handsdown.generate_index()

# navigate to `output` dir and check results

Installation

Install using pip

pip install handsdown

More examples

Handsdown API Index / Handsdown

Auto-generated documentation for handsdown module.

Modules