Sphinx turns text files and information from code into navigable documentation. A small Python project needs a straightforward workflow: install Sphinx, run sphinx-quickstart, write an introduction, enable autodoc, build HTML, and publish it. There is no need to begin with dozens of extensions, a custom theme, or elaborate documentation infrastructure.
This guide creates a maintainable base for a library using the src layout. It combines human-written explanations with an API reference extracted from docstrings and Python type hints.
Project layout and installation
Start with this structure:
calculator/
├── docs/
├── src/
│ └── calculator/
│ ├── __init__.py
│ └── operations.py
├── tests/
└── pyproject.toml
Use a virtual environment and install Sphinx. Installing the package itself in editable mode lets autodoc import it without editing sys.path in conf.py:
python -m pip install -e .
python -m pip install sphinx
Record Sphinx in the project's documentation dependencies so contributors and CI use the same supported range. Depending on your dependency manager, that may be an extra in pyproject.toml or a short docs/requirements.txt file.
Create the foundation with sphinx-quickstart
Run this from the repository root:
sphinx-quickstart docs
Choose separate source and build directories, then provide the project name, author, release, and en language. The command creates files such as:
docs/
├── Makefile
├── make.bat
├── build/
└── source/
├── conf.py
└── index.rst
Prompts can change between releases, so check the official Sphinx tutorial. conf.py is executable Python configuration, index.rst is the root page, and build/ contains generated artifacts that normally should not be committed.
Keep the initial configuration short:
## docs/source/conf.py
project = "Calculator"
author = "Calculator Team"
release = "1.0.0"
language = "en"
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
]
html_theme = "alabaster"
autodoc reads Python objects. napoleon understands Google- and NumPy-style docstrings. The bundled Alabaster theme is enough to begin; adopt an external theme later only when there is a clear need.
Write docstrings that explain contracts
Docstrings should cover intent, parameters, return values, relevant exceptions, and limitations. They should not narrate each implementation line. PEP 257 defines basic conventions such as a summary line and the organization of multiline docstrings.
With Napoleon enabled, a function can use Google style:
def divide(dividend: float, divisor: float) -> float:
"""Divide two numbers.
Args:
dividend: Value to divide.
divisor: Value to divide by. It cannot be zero.
Returns:
The quotient of both values.
Raises:
ValueError: If ``divisor`` is zero.
"""
if divisor == 0:
raise ValueError("divisor cannot be zero")
return dividend / divisor
Annotations communicate types, while the docstring communicates semantics. Avoid repeating types when the generated page already displays type hints; document meaning, units, conditions, and side effects instead. Public docstrings should change with behavior, just like the project's pytest tests.
Generate an API reference with autodoc
Create docs/source/api.rst:
API Reference
=============
Operations
----------
.. automodule:: calculator.operations
:members:
:undoc-members:
:show-inheritance:
Add the page to index.rst:
Calculator
==========
A small library for predictable mathematical operations.
.. toctree::
:maxdepth: 2
:caption: Contents
usage
api
Also create usage.rst with installation, one minimal example, and expected behavior. Generated API pages answer “what objects exist?” Narrative documentation answers “how do I complete a task?” Neither one replaces the other.
During the build, autodoc imports calculator.operations. Any top-level module code therefore runs as well. Avoid network calls, mandatory secret reads, and expensive initialization on import. If an optional dependency is unavailable, autodoc_mock_imports = ["dependency"] may unblock the build, but mocking your own package would conceal genuine installation or import errors.
Check the official autodoc reference before adding directive options; it explains imports, member selection, and display order.
Build HTML and fail on warnings
From the project root, run:
sphinx-build -M html docs/source docs/build
Open docs/build/html/index.html. During development, make -C docs html or docs\make.bat html provides an equivalent shortcut created by quickstart.
In CI, turn warnings into failures so broken references and malformed docstrings are caught:
sphinx-build -W --keep-going -b html docs/source docs/build/html
-W treats warnings as errors, while --keep-going reports more issues before stopping. Enable this while documentation is small. Waiting until hundreds of pages exist makes cleanup expensive. Do not suppress warning categories globally without understanding their cause.
Frequent errors include failing to install the package, leaving a page outside the toctree, misindenting an RST directive, and importing modules with side effects. Another common mistake is generating every API object but never writing a usage page. Documentation should help someone accomplish a real task, not merely list signatures.
Publish with Read the Docs
Read the Docs clones the repository, installs dependencies, and runs Sphinx. Add a minimal .readthedocs.yaml at the root:
version: 2
sphinx:
configuration: docs/source/conf.py
fail_on_warning: true
python:
install:
- method: pip
path: .
- requirements: docs/requirements.txt
Declare the supported Sphinx version range in docs/requirements.txt. Then import the repository into the service and inspect the first build log. The official Read the Docs tutorial describes the current setup and supported configuration.
Alternatively, add the strict build command to the pipeline described in the Python GitHub Actions CI guide and deploy docs/build/html to your static host. Avoid running two publication mechanisms without a requirement: choose Read the Docs or the existing CI as the primary owner.
Links, inventories, and versions
Internal references should use Sphinx roles instead of relative URLs where practical. An explicit label such as .. _installation: lets another page refer to the section with :ref:\installation`even if the source file moves. For documented Python objects, roles such as:class:and:func:` create links and allow the build to detect incorrect names.
Intersphinx connects the documentation to inventories published by other projects, including Python's standard library. Enable sphinx.ext.intersphinx, configure only trusted sources, and verify that references resolve during the build. This is more maintainable than copying external explanations or managing deep links by hand.
If several library versions remain public, state which documentation describes the stable release and how readers can reach older versions. Read the Docs can retain builds for tags; a custom host needs a CI-defined URL structure. Do not publish documentation from the main branch as though it necessarily described the latest stable package. Documenting unreleased behavior without a visible warning confuses readers who installed the available release.
Maintain documentation without bureaucracy
Update documentation and code in the same change. When a public function changes, review its docstring, usage page, and related test. A pull request review can check three things: the example still runs, the generated signature matches the API, and the build emits no warnings. This short routine prevents a large cleanup later.
Do not attempt to document every private helper. Prioritize installation, the first useful result, surprising decisions, and public interfaces. Examples should be small enough to understand in isolation and complete enough to run. If a snippet requires files or environment variables, state that before the code. Delete obsolete pages instead of preserving them to increase volume: finding outdated instructions is worse than finding no page at all.
Checklist and conclusion
- Install the package and Sphinx in an isolated environment.
- Run
sphinx-quickstart docsand keepconf.pyfocused. - Enable
autodocandnapoleonbecause the project uses them. - Write public docstrings around contracts, returns, and exceptions.
- Pair a usage guide with the generated API reference.
- Build HTML locally and resolve every warning.
- Run
sphinx-build -W --keep-goingin CI. - Publish through Read the Docs or one CI workflow.
A healthy starting point is small: one landing page, one usage page, and one API reference. When real users need better search, versioned docs, or another output format, Sphinx can grow with the project. Until then, accurate content and a reliable build matter more than a large collection of extensions.