Software Engineering

Python Package Documentation: Sphinx, MkDocs, and Read the Docs Guide

Create professional Python package documentation with Sphinx and MkDocs. Learn docstring formats, API generation, Read the Docs hosting, and documentation best practices.

By InventiveHQ Team

For most Python packages, use Sphinx when you need to auto-generate an API reference from docstrings and type hints, and use MkDocs (with the Material theme) when your docs are prose-first and you'd rather write Markdown than reStructuredText. Both build cleanly on Read the Docs, which hosts documentation for free, rebuilds on every git push, and keeps a versioned copy of your docs for each branch and tag. The fastest working setup is: write Google-style docstrings, wire them into Sphinx with the napoleon and autodoc extensions, add a .readthedocs.yaml, and let Read the Docs build on push.

That's the summary an AI Overview gives you. What it can't show you is the actual decision — Sphinx or MkDocs — mapped to your project's shape, or the exact sequence of files and commands that turns a docstring into a hosted, versioned page. Below: a side-by-side comparison table with a clear "pick this when" row, an animated diagram of the docstring-to-hosted-docs pipeline, copy-paste configs for both tools, and a pre-publish checklist. Start with the tool choice.

Sphinx vs MkDocs: Which Should You Use?

FactorSphinxMkDocs (Material)
Markup languagereStructuredText (Markdown via MyST)Markdown
API from docstringsautodoc + napoleon (mature, deep)mkdocstrings (good, newer)
Setup complexityHigher (conf.py, .rst toctrees)Lower (mkdocs.yml, sensible defaults)
Cross-references / intersphinxFirst-class, links to CPython & other projectsLimited
PDF / EPUB outputYes (LaTeX / rinoh)No (HTML only)
Live reload while writingsphinx-autobuild (add-on)mkdocs serve (built-in)
Look out of the boxsphinx-rtd-theme / FuroMaterial (polished, modern)
Pick this whenYou have a real API surface, type hints, and want reference docs that stay in sync with codeYour docs are guides/tutorials/prose and you want the fastest path to a beautiful site

Both render identically well on Read the Docs, so this choice is about the writing experience and how much of your documentation is generated from code versus written by hand.

From Docstring to Hosted Docs: The Pipeline

Python documentation build pipeline A docstring in source code is read by Sphinx or MkDocs, built into an HTML site, pushed to git, and rebuilt automatically by Read the Docs into versioned hosted documentation. 1. Docstring Google/NumPy style in code def f(x: int) 2. Builder Sphinx autodoc or mkdocstrings reads + renders 3. git push commit docs + .readthedocs.yaml branch or tag 4. Read the Docs auto-rebuild versioned + search Every push re-runs the whole pipeline — docs never drift from code

The key insight the diagram encodes: once .readthedocs.yaml is committed, steps 2–4 happen automatically on every push. Your only ongoing job is keeping docstrings accurate (step 1).

Documentation Types

Professional documentation includes multiple types:

┌─────────────────────────────────────────────────────────────┐
│                   Documentation Quadrant                     │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  LEARNING                         INFORMATION                │
│  ──────────────────────────────────────────────             │
│                                                              │
│  Tutorials                        Reference                  │
│  • Get started quickly            • API documentation        │
│  • Learning-oriented              • Function signatures      │
│  • Step-by-step                   • Complete & accurate      │
│                                                              │
│  ──────────────────────────────────────────────             │
│                                                              │
│  How-to Guides                    Explanation                │
│  • Solve specific problems        • Background & context     │
│  • Task-oriented                  • Why things work          │
│  • Practical steps                • Architecture decisions   │
│                                                              │
│  DOING                            UNDERSTANDING              │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Docstring Formats

Clean and readable, works well with type hints:

def process_data(input_path: str, output_path: str, *, validate: bool = True) -> dict:
    """Process data from input file and write to output.

    Reads the input file, applies transformations, and writes
    the result to the output path.

    Args:
        input_path: Path to the input file. Must exist.
        output_path: Path for the output file. Created if doesn't exist.
        validate: Whether to validate data before processing.
            Defaults to True.

    Returns:
        A dictionary containing processing statistics:
            - processed: Number of records processed
            - errors: Number of errors encountered
            - duration: Processing time in seconds

    Raises:
        FileNotFoundError: If input_path doesn't exist.
        ValidationError: If validate=True and data is invalid.

    Example:
        >>> stats = process_data("input.csv", "output.json")
        >>> print(stats["processed"])
        100
    """

NumPy Style

Common in scientific Python:

def calculate_statistics(data, axis=None):
    """
    Calculate basic statistics for the input data.

    Parameters
    ----------
    data : array_like
        Input data for statistical calculation.
    axis : int or None, optional
        Axis along which to compute statistics.
        If None, compute over flattened array.

    Returns
    -------
    dict
        Dictionary containing:
        - mean : float
            Arithmetic mean of the data.
        - std : float
            Standard deviation.
        - min : float
            Minimum value.
        - max : float
            Maximum value.

    See Also
    --------
    numpy.mean : Compute arithmetic mean.
    numpy.std : Compute standard deviation.

    Examples
    --------
    >>> data = [1, 2, 3, 4, 5]
    >>> stats = calculate_statistics(data)
    >>> stats['mean']
    3.0
    """

Sphinx (reStructuredText) Style

Native Sphinx format:

def fetch_user(user_id, include_details=False):
    """
    Fetch a user by ID.

    :param user_id: The unique user identifier.
    :type user_id: int
    :param include_details: Include additional user details.
    :type include_details: bool, optional
    :returns: User object or None if not found.
    :rtype: User or None
    :raises ValueError: If user_id is negative.

    .. note::
       This function makes a network request.

    .. warning::
       Rate limited to 100 requests per minute.
    """

Sphinx is the standard for Python documentation.

Setup

pip install sphinx sphinx-rtd-theme sphinx-autodoc-typehints

# Initialize in your project
cd my-package
sphinx-quickstart docs

# Answer prompts:
# > Separate source and build directories? [n]: n
# > Project name: My Package
# > Author name(s): Your Name
# > Project release: 1.0.0

Project Structure

my-package/
├── src/
│   └── my_package/
│       ├── __init__.py
│       └── core.py
├── docs/
│   ├── conf.py           # Sphinx configuration
│   ├── index.rst         # Main page
│   ├── installation.rst
│   ├── quickstart.rst
│   ├── api.rst           # API reference
│   └── _static/          # Custom CSS, images
├── pyproject.toml
└── README.md

Configuration (conf.py)

# docs/conf.py
import os
import sys

# Add source to path for autodoc
sys.path.insert(0, os.path.abspath("../src"))

# Project information
project = "My Package"
copyright = "2026, Your Name"
author = "Your Name"
release = "1.0.0"

# Extensions
extensions = [
    "sphinx.ext.autodoc",        # Auto-generate from docstrings
    "sphinx.ext.napoleon",       # Google/NumPy docstring support
    "sphinx.ext.viewcode",       # Add links to source code
    "sphinx.ext.intersphinx",    # Link to other projects
    "sphinx_autodoc_typehints",  # Type hints in docs
]

# Napoleon settings (for Google/NumPy docstrings)
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = True

# Autodoc settings
autodoc_default_options = {
    "members": True,
    "undoc-members": True,
    "show-inheritance": True,
}
autodoc_typehints = "description"

# Intersphinx mapping
intersphinx_mapping = {
    "python": ("https://docs.python.org/3", None),
    "requests": ("https://requests.readthedocs.io/en/latest/", None),
}

# Theme
html_theme = "sphinx_rtd_theme"
html_theme_options = {
    "navigation_depth": 4,
}

Main Page (index.rst)

My Package
==========

My Package is a Python library for doing awesome things.

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   installation
   quickstart
   api
   changelog

Features
--------

* Feature one
* Feature two
* Feature three

Quick Example
-------------

.. code-block:: python

   from my_package import process

   result = process("input.txt")
   print(result)

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
Advertisement

API Reference (api.rst)

API Reference
=============

Core Module
-----------

.. automodule:: my_package.core
   :members:
   :undoc-members:
   :show-inheritance:

Utilities
---------

.. automodule:: my_package.utils
   :members:

Specific Function
-----------------

.. autofunction:: my_package.process_data

Building Documentation

# Build HTML
cd docs
make html

# View locally
open _build/html/index.html

# Clean build
make clean html

MkDocs: Simple Markdown Docs

MkDocs uses Markdown and is simpler to configure.

Setup

pip install mkdocs mkdocs-material mkdocstrings[python]

# Initialize
mkdocs new .

Project Structure

my-package/
├── src/
│   └── my_package/
├── docs/
│   ├── index.md
│   ├── installation.md
│   ├── quickstart.md
│   ├── api/
│   │   └── core.md
│   └── assets/
├── mkdocs.yml
└── pyproject.toml

Configuration (mkdocs.yml)

# mkdocs.yml
site_name: My Package
site_url: https://mypackage.readthedocs.io/
repo_url: https://github.com/user/my-package
repo_name: user/my-package

theme:
  name: material
  features:
    - navigation.tabs
    - navigation.sections
    - content.code.copy
  palette:
    - scheme: default
      primary: indigo
      accent: indigo

plugins:
  - search
  - mkdocstrings:
      default_handler: python
      handlers:
        python:
          options:
            show_source: true
            show_root_heading: true

nav:
  - Home: index.md
  - Getting Started:
    - Installation: installation.md
    - Quick Start: quickstart.md
  - API Reference:
    - Core: api/core.md
  - Changelog: changelog.md

markdown_extensions:
  - pymdownx.highlight:
      anchor_linenums: true
  - pymdownx.superfences
  - admonition
  - toc:
      permalink: true

API Documentation (api/core.md)

# Core Module

Main functionality of the package.

## process_data

::: my_package.core.process_data

## DataProcessor

::: my_package.core.DataProcessor
    options:
      members: true
      show_source: false

Building

# Serve locally with hot reload
mkdocs serve

# Build static site
mkdocs build

# Deploy to GitHub Pages
mkdocs gh-deploy

Read the Docs

Host your documentation for free with automatic builds.

Configuration (.readthedocs.yaml)

# .readthedocs.yaml
version: 2

build:
  os: ubuntu-22.04
  tools:
    python: "3.11"

# Sphinx
sphinx:
  configuration: docs/conf.py

# Or MkDocs
# mkdocs:
#   configuration: mkdocs.yml

python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - docs

pyproject.toml Docs Dependencies

[project.optional-dependencies]
docs = [
    "sphinx>=7.0",
    "sphinx-rtd-theme>=1.0",
    "sphinx-autodoc-typehints>=1.0",
    # Or for MkDocs:
    # "mkdocs>=1.5",
    # "mkdocs-material>=9.0",
    # "mkdocstrings[python]>=0.24",
]

Setting Up Read the Docs

  1. Push your code to GitHub/GitLab/Bitbucket
  2. Sign up at readthedocs.org
  3. Import your project
  4. Configure build settings
  5. Docs build automatically on each push

Versioned Documentation

Read the Docs builds docs for:

  • Main branch (latest)
  • Tags (stable versions)
  • Feature branches (preview)
# .readthedocs.yaml
version: 2

build:
  os: ubuntu-22.04
  tools:
    python: "3.11"

# Build PDF/EPUB
formats:
  - pdf
  - epub

Documentation Best Practices

1. Write README First

# My Package

[![PyPI](https://img.shields.io/pypi/v/my-package)](https://pypi.org/project/my-package/)
[![Docs](https://readthedocs.org/projects/my-package/badge/)](https://my-package.readthedocs.io/)

Short description of what your package does.

## Installation

```bash
pip install my-package

Quick Start

from my_package import process

result = process("input.txt")

Documentation

Full documentation: https://my-package.readthedocs.io/

License

MIT


### 2. Include Working Examples

```python
def calculate_sum(numbers: list[int]) -> int:
    """Calculate the sum of numbers.

    Example:
        >>> calculate_sum([1, 2, 3])
        6
        >>> calculate_sum([])
        0
        >>> calculate_sum([-1, 1])
        0

    Note:
        Examples are tested with doctest.
    """
    return sum(numbers)

3. Document the Why, Not Just What

def connect(host: str, port: int, timeout: float = 30.0) -> Connection:
    """Connect to the server.

    The timeout defaults to 30 seconds, which works well for most
    network conditions. For slow connections or when connecting to
    remote servers, you may need to increase this value.

    If the server uses a non-standard port, ensure your firewall
    allows outbound connections on that port.

    Args:
        host: Server hostname or IP address.
        port: Server port number.
        timeout: Connection timeout in seconds. Increase for slow
            networks or distant servers.

    Returns:
        Active connection to the server.
    """

4. Use Admonitions

!!! note
    This feature requires Python 3.10+

!!! warning
    This operation cannot be undone.

!!! tip
    Use `--verbose` for detailed output.

!!! danger
    Never commit API keys to version control.
.. note::
   This feature requires Python 3.10+

.. warning::
   This operation cannot be undone.

5. Keep Docs in Sync

# pyproject.toml
[tool.pytest.ini_options]
addopts = "--doctest-modules"

# Run doctests to verify examples
# pytest src/ --doctest-modules

Project Documentation Structure

docs/
├── index.rst/md           # Introduction, quick links
├── installation.rst/md    # How to install
├── quickstart.rst/md      # 5-minute tutorial
├── tutorials/             # In-depth tutorials
│   ├── tutorial1.rst/md
│   └── tutorial2.rst/md
├── how-to/                # Task-oriented guides
│   ├── configure.rst/md
│   └── deploy.rst/md
├── api/                   # API reference
│   ├── core.rst/md
│   └── utils.rst/md
├── changelog.rst/md       # Version history
└── contributing.rst/md    # How to contribute

Adding Badges

# Badges for README

[![PyPI version](https://badge.fury.io/py/my-package.svg)](https://pypi.org/project/my-package/)
[![Python versions](https://img.shields.io/pypi/pyversions/my-package.svg)](https://pypi.org/project/my-package/)
[![Documentation](https://readthedocs.org/projects/my-package/badge/)](https://my-package.readthedocs.io/)
[![Tests](https://github.com/user/my-package/actions/workflows/test.yml/badge.svg)](https://github.com/user/my-package/actions)
[![Coverage](https://codecov.io/gh/user/my-package/branch/main/graph/badge.svg)](https://codecov.io/gh/user/my-package)
[![License](https://img.shields.io/pypi/l/my-package.svg)](https://github.com/user/my-package/blob/main/LICENSE)

Command Reference

Sphinx

sphinx-quickstart docs      # Initialize Sphinx
make html                   # Build HTML
make clean                  # Clean build
sphinx-build -b html . _build  # Build command

MkDocs

mkdocs new .               # Initialize MkDocs
mkdocs serve               # Local development server
mkdocs build               # Build static site
mkdocs gh-deploy           # Deploy to GitHub Pages

Pre-Publish Documentation Checklist

Run through this before you tag a release. If any item fails, the docs will ship broken or drift out of sync.

Documentation pre-publish checklist Seven checks to run before publishing: docstrings present, build has no warnings, doctests pass, links resolve, .readthedocs.yaml committed, version pinned, and README badges point to the live docs. Ship checklist Every public function/class has a docstring Build is warning-free (-W turns warnings to errors) doctests pass (pytest --doctest-modules) No broken links (make linkcheck) .readthedocs.yaml committed to repo root Release/version matches the package version README badges link to the live docs URL Common failure Read the Docs build succeeds but the API reference is empty — autodoc could not import your package. Fix: install the package itself in the docs build env.

If your API reference renders blank on Read the Docs, it is almost always an import failure: autodoc and mkdocstrings both import your code to read its docstrings, so the package must be installed in the build environment. That is what path: . under python.install in .readthedocs.yaml does — it pip installs your project so autodoc can import it.

Next Steps

For more Python development guides, explore our complete Python packaging series.

Frequently Asked Questions

Should I use Sphinx or MkDocs for my Python package?

Use Sphinx if you need autodoc from docstrings, have complex API documentation, or want reStructuredText. Use MkDocs if you prefer Markdown, want simpler setup, or are building more prose-heavy documentation. Both work well with Read the Docs.

What docstring format should I use?

Use Google style for readability and modern Python, NumPy style for scientific packages, or Sphinx style if you prefer reST. All three work with Sphinx's napoleon extension. Pick one and be consistent throughout your codebase.

How does autodoc generate documentation from code?

Sphinx's autodoc extension imports your Python modules and extracts docstrings, function signatures, and type hints. It generates documentation automatically, so your docs stay in sync with code as long as docstrings are updated.

What is Read the Docs and why should I use it?

Read the Docs is a free hosting platform for documentation. It automatically builds docs when you push to git, hosts multiple versions, and provides search. It's the standard for open-source Python projects.

How do I document a CLI tool?

Use sphinx-click for Click-based CLIs or generate help text programmatically. Include examples, explain each command and option, and show common workflows. CLI documentation should be task-oriented.

How do I include code examples in documentation?

Use code blocks in Markdown/reST with language hints (```python). For testable examples, use doctest format. Sphinx can run examples to verify they work. Always show imports and expected output.

How do I document type hints?

Use sphinx-autodoc-typehints to extract types from annotations and include them in docs. This keeps type information in code (not duplicated in docstrings) while still showing types in generated documentation.

What should my package documentation include?

Essential: README, installation, quickstart, API reference. Recommended: tutorials, how-to guides, changelog, contributing guide. Great docs have both reference (what functions do) and guides (how to accomplish tasks).

How do I version my documentation?

Read the Docs automatically builds docs for each git branch/tag. Users can switch between versions. Use the version switcher and maintain docs for supported releases.

How do I add documentation badges to my README?

Read the Docs provides a badge URL at https://readthedocs.org/projects/your-project/badge/. Add it to your README: Docs. It shows build status and links to docs.

pythondocumentationsphinxmkdocsreadthedocsdevops