Skip to main content

Static documentation generator for Python projects

Project description

slop-doc

Static documentation generator for Python projects. Parses Python source code via AST, renders Markdown pages with embedded data tags and presentation functions into a styled 3-column HTML site with navigation tree, cross-linking, and search.

Installation

pip install slop-doc

Requires Python >= 3.10.

Quick Start

# 1. Create a docs folder with starter root.md
slop-doc init --name docs

# 2. Edit docs/root.md, add .md pages

# 3. Build
slop-doc build -d docs

# 4. Open in browser
slop-doc open -d docs

CLI Commands

Command Description
slop-doc init [--name <folder>] Create a new docs folder with a starter root.md. Default name: docs
slop-doc build [-d <dir>] Build documentation. Looks for root.md in -d dir or current directory
slop-doc open [-d <dir>] [-p <port>] Serve built docs via local HTTP server and open in browser. SPA navigation works here (default port 8000)

How It Works

The folder structure is the documentation tree. Every .md file becomes a page; every subfolder with a root.md becomes a folder node in the navigation.

my-docs/                    ← docs root (contains root.md)
├── root.md                 ← project config + landing page
├── getting-started.md      ← top-level page
├── 1-installation.md       ← sorted by numeric prefix (prefix stripped from title)
├── 2-usage.md
└── api/                    ← subfolder = folder node in nav
    ├── root.md             ← folder config + folder landing page
    ├── overview.md         ← page inside the folder
    └── advanced/
        └── root.md

Build output: a self-contained HTML site with assets/style.css, assets/app.js, and one .html per page. Use slop-doc open to serve locally — pages load via SPA navigation (no full reload, smooth fade transitions). Also works with file:// protocol (falls back to standard page loads).


Front-matter

Each .md file can start with a JSON config block. The block is relaxed JSON — supports // comments, # comments, trailing commas, and unquoted keys.

{
    "title": "My Page",
    "default_source_folder": "../src/mypackage"
}

# My Page

Page content here...

Page-level keys

Key Type Description
title string Display name in the nav tree. Falls back to the first # heading, then filename
default_source_folder string Path to Python source folder for this page (and its children). Resolved relative to the parent directory of the docs root
children object Auto-generated child pages from source code. See Children
order int Explicit sort position in the nav tree (lower = shown first). Optional — unordered pages keep their filename-based position after ordered ones

Project-level keys (only in the root root.md)

Key Type Default Description
project_name string "Documentation" Displayed in the site header
version string "" Shown in page titles
output_dir string "build" Output folder (relative to docs root)
assets_dir string Custom assets folder (relative to docs root). Files here override defaults

Example root.md

{
    "title": "MyProject Docs",
    "project_name": "MyProject",
    "version": "2.1.0",
    "output_dir": "build",
    "default_source_folder": "../src/myproject"
}

# Welcome to MyProject

This is the landing page.

Source Folder

The default_source_folder key tells slop-doc where to find your Python source code. It is inherited by child pages — set it once on a folder's root.md and all pages inside that folder use it automatically.

A deeper root.md or individual page can override it with its own default_source_folder.

Path resolution: always relative to the parent of the docs root, not relative to the .md file. For example, if your docs are in project/docs/ and your source is in project/src/mypackage/, use:

{
    "default_source_folder": "../src/mypackage"
}

This works regardless of how deeply nested the .md file is.


Data Tags

Data tags are placeholders in Markdown that expand to lists of items from your Python source code. Write them as {{tag_name}} in the page body.

Available tags

Tag What it lists
{{classes}} All classes (from files directly in the source folder)
{{functions}} All module-level functions
{{constants}} All constants (ALL_CAPS names)
{{enums}} Enum classes
{{dataclasses}} Dataclass classes
{{interfaces}} Abstract base classes (ABC / have abstract methods)
{{protocols}} Protocol classes
{{exceptions}} Exception classes
{{plain_classes}} Regular classes that don't fit any category above

Each tag has a recursive variant with _rec suffix that includes files from all subfolders:

Flat (direct files only) Recursive (all subfolders)
{{classes}} {{classes_rec}}
{{functions}} {{functions_rec}}
{{enums}} {{enums_rec}}
... ...

Inline rendering

In the page body, class-type tags render as cross-linked lists:

## Available Classes

{{classes}}

Becomes something like: DataSource, FetchSpec, ColumnMap — each name is a clickable cross-link to its class page.

If no items are found, renders as: None found.


Children (Auto-Generated Pages)

The children key in front-matter generates child pages from source code. Each child gets a full dedicated page with class/function documentation.

{
    "title": "API Reference",
    "default_source_folder": "../src/mypackage",
    "children": {
        "classes": "{{classes}}"
    }
}

This creates a child page for every class found in the source folder. Each child page appears in the nav tree under this folder.

Syntax

The children value is an object mapping a type to a list of names:

{
    "children": {
        "classes": "{{classes}}",
        "functions": "{{functions}}"
    }
}

You can also mix tag expansion with explicit names:

{
    "children": {
        "classes": ["{{interfaces}}", "MySpecialClass"]
    }
}

Supported child types

Type Generates
classes Class pages with full docs (description, info, properties, methods)
enums Same as classes, filtered to enums
dataclasses Same, filtered to dataclasses
interfaces Same, filtered to interfaces/ABCs
protocols Same, filtered to protocols
exceptions Same, filtered to exceptions
plain_classes Same, filtered to plain classes
functions Function pages (name + stub)

Auto-generated class page content

Each auto-generated class page contains (empty sections are automatically hidden):

# ClassName

(class description)

## Info
(module, file:line, base classes)

## Properties
(table of @property methods — hidden if none)

## Public Methods
(summary table with links — hidden if none)

## Private Methods
(summary table — hidden if none)

## Method Details
(full signature, parameters, returns, raises for each method)

Presentation Functions

Presentation functions render structured data tables and detail blocks from your source code. Write them as %function_name(args)% in the page body.

Table functions

Function Output
%classes_table({{classes}})% Table of classes with descriptions. Class names are cross-links
%functions_table({{functions}})% Table of functions with signatures and descriptions
%constants_table({{constants}})% Table of constants with values and types

The argument can be a {{tag}} (expands to all matching items), a comma-separated list of names, or empty (uses all).

Class detail functions

Function Output
%class_description(ClassName)% Short + full description
%class_info(ClassName)% Table: module, file:line, base classes
%properties(ClassName)% Table of @property methods: name, type, description
%base_classes(ClassName)% Comma-separated list of base classes
%decorators(ClassName)% Comma-separated list of decorators
%source_link(ClassName)% Source file path and line number

Methods functions

Function Output
%methods_table(ClassName)% Public methods summary table
%methods_table(ClassName, private)% Private methods (_name) table
%methods_table(ClassName, static)% Static methods table
%methods_table(ClassName, classmethod)% Class methods table
%methods_table(ClassName, dunder)% Dunder methods (__name__) table
%methods_table(ClassName, all)% All methods (public + private, including dunder)
%methods_details(ClassName)% Full detail blocks for all methods: signature, params, returns, raises

Example page

{
    "title": "API Reference",
    "default_source_folder": "../src/mypackage"
}

# API Reference

## Classes

%classes_table({{classes}})%

## Functions

%functions_table({{functions}})%

## Constants

%constants_table({{constants}})%

Cross-Links

Link to any class or method page from anywhere in your documentation using [[double bracket]] syntax.

Syntax Links to
[[folder/ClassName]] Class page
[[folder/ClassName.method_name]] Method anchor on the class page
[[folder/ClassName|Display Text]] Class page with custom display text

The folder is the basename of the source folder. For example, if default_source_folder is ../src/mypackage, the folder slug is mypackage.

See the [[mypackage/DataSource]] class for details.

The [[mypackage/DataSource.fetch]] method handles data retrieval.

Check [[mypackage/DataSource|the data source]] documentation.

Hidden class pages

Every class in an indexed source folder automatically gets a dedicated page, even if not explicitly listed in children. These "hidden" pages:

  • Are rendered with full class documentation
  • Are indexed for cross-link resolution
  • Appear in search results
  • Do not appear in the navigation tree

This means [[folder/AnyClass]] always resolves, as long as the class exists in a parsed source folder.


Docstring Format

slop-doc parses Google-style docstrings:

class MyClass(BaseClass):
    """Short description of the class.

    Longer description that provides more detail
    about the class and its purpose.
    """

    def my_method(self, name: str, count: int = 5) -> list[str]:
        """Short description of the method.

        More detailed description here.

        Args:
            name: The name to process.
            count: How many times to repeat. Defaults to 5.

        Returns:
            A list of processed strings.

        Raises:
            ValueError: If name is empty.
            TypeError: If count is not an integer.
        """

What gets parsed

  • Classes: all public classes (private _ClassName are skipped)
  • Methods: all methods including private and dunder, with full signatures
  • Functions: top-level functions only (private and dunder are skipped)
  • Constants: ALL_CAPS names assigned at module level
  • Properties: methods decorated with @property
  • Type annotations: preserved from source, displayed in method signatures and tables

Class classification

Classes are automatically classified based on their base classes and decorators:

Category Detection rule
Enum Inherits from Enum, IntEnum, StrEnum, Flag, IntFlag
Dataclass Has @dataclass decorator
Interface/ABC Inherits from ABC/ABCMeta or has any @abstractmethod
Protocol Inherits from Protocol
Exception Inherits from Exception/BaseException or name ends with Error/Exception
Plain class None of the above

File Sorting

Files are sorted in the nav tree by:

  1. order front-matter field first: pages with "order": N appear before unordered pages, sorted ascending by N
  2. Numeric prefix second: 1-intro.md, 2-setup.md, 3-api.md — sorted by number
  3. Alphabetical third: files without numeric prefix or order sort alphabetically

The numeric prefix is stripped from the display title: 1-introduction.md shows as "Introduction".

Example using order:

{
    "title": "Getting Started",
    "order": 1
}

Assets and Styling

slop-doc ships with a default dark theme (style.css) and client-side app (app.js).

The app provides:

  • SPA navigation — internal links are fetched and swapped without full page reload (falls back to normal navigation on file:// or fetch failure)
  • Smooth transitions — content fade-in, nav tree expand/collapse animation, sidebar width transitions
  • Client-side search — search index is embedded inline in each page (no server required)
  • Scroll spy — right sidebar highlights the current section on scroll
  • Anchor highlight — clicking an anchor link smoothly scrolls and flashes the target element
  • Nav tree persistence — expand/collapse state is saved in localStorage across page loads

To customize styling:

  1. Set assets_dir in your root root.md:

    {
        "assets_dir": "assets"
    }
    
  2. Place your custom style.css in that folder. It will replace the default.

The app.js is always copied from defaults (the search index is embedded inline in each page).


Complete Example

Project structure

my-project/
├── src/
│   └── mylib/
│       ├── __init__.py
│       ├── client.py        # Client, Config classes
│       ├── models.py         # User, Product dataclasses
│       └── exceptions.py     # ApiError, NotFoundError
└── docs/
    ├── root.md
    ├── 1-getting-started.md
    └── api/
        ├── root.md
        └── overview.md

docs/root.md

{
    "title": "MyLib",
    "project_name": "MyLib",
    "version": "1.0.0",
    "output_dir": "build"
}

# MyLib Documentation

Welcome to the MyLib documentation.

docs/1-getting-started.md

# Getting Started

## Installation

​```bash
pip install mylib
​```

## Quick Start

​```python
from mylib import Client

client = Client(api_key="...")
result = client.fetch("data")
​```

docs/api/root.md

{
    "title": "API Reference",
    "default_source_folder": "../../src/mylib",
    "children": {
        "classes": "{{classes}}"
    }
}

# API Reference

## All Classes

%classes_table({{classes}})%

## Functions

%functions_table({{functions}})%

## Constants

%constants_table({{constants}})%

This generates:

  • A nav tree: Getting Started, API Reference > Client, Config, User, Product, ApiError, NotFoundError
  • Each class gets a full page with methods, properties, signatures
  • Cross-links like [[mylib/Client]] work from any page

Build and view

cd docs
slop-doc build
slop-doc open

Output Structure

docs/build/
├── index.html              ← root.md landing page
├── getting-started.html
├── api/
│   ├── index.html          ← api/root.md
│   ├── client.html         ← auto-generated from children
│   ├── config.html
│   ├── user.html
│   └── ...
└── assets/
    ├── style.css
    └── app.js

Summary of Syntax

Syntax Where Purpose
{ "key": "value" } Top of .md file Front-matter config
{{tag}} Page body or children value Expand to list of items from source
{{tag_rec}} Page body or children value Same but recursive (includes subfolders)
%function(args)% Page body Render tables/details from source data
[[folder/Class]] Page body Cross-link to class page
[[folder/Class.method]] Page body Cross-link to method anchor
[[folder/Class|text]] Page body Cross-link with custom display text

Architecture

Modules

Module Purpose
builder.py Build orchestrator — drives the full pipeline, CLI commands (init, build, open)
tree_builder.py Walks the docs folder, builds the navigation tree of Node objects
frontmatter.py Parses relaxed JSON front-matter from .md files
parser.py Python AST source parser — extracts classes, functions, constants, docstrings
tag_renderer.py Expands {{data tags}} and %presentation functions% into HTML
cross_links.py Builds cross-link index and resolves [[Target]] patterns
markdown_renderer.py Converts Markdown to HTML, adds heading anchors
layout.py Assembles 3-column HTML pages: nav tree, content, contents sidebar

Build Pipeline

When slop-doc build runs, the following steps execute in order:

1. Read project config
   └─ Parse root.md front-matter → project_name, version, output_dir, assets_dir

2. Build navigation tree
   └─ Recursively walk docs folder (tree_builder.py)
      ├─ Parse each .md front-matter + body
      ├─ Resolve default_source_folder (inherited down the tree)
      ├─ Expand children: generators ({{classes}}, {{functions}}, etc.)
      ├─ Parse Python source folders on demand (cached)
      └─ Sort nodes: order field → numeric prefix → alphabetical

3. Build cross-link index
   └─ Walk tree, index every class page URL + method anchors

4. Generate search index
   └─ Walk tree, collect all pages/classes/methods/functions/constants → JSON

5. Render each page
   │  For each node in the tree:
   │
   ├─ Auto-class pages → generate Markdown body from presentation functions
   ├─ Regular pages → use .md file content
   ├─ Empty folders with children → simple "# Title" placeholder
   │
   └─ Page rendering pipeline:
      a. Expand %presentation_functions(args)%  →  HTML tables/details
      b. Expand remaining {{data_tags}}         →  cross-links or inline text
      c. Strip empty sections                   →  remove headings with no content
      d. Markdown → HTML                        →  via python-markdown
      e. Resolve [[cross-links]]                →  relative <a href> tags
      f. Assemble full HTML page                →  3-column layout with nav, breadcrumb, search index

6. Copy assets
   └─ User assets (override) → default style.css (fallback) → app.js (always from defaults)

Source Parsing

parser.py uses Python's ast module to extract structured data from .py files:

  • Classes: name, base classes, decorators, docstring, properties, methods, classification (enum / dataclass / interface / protocol / exception / plain)
  • Functions: name, args (with types + defaults), return type, decorators, docstring
  • Constants: ALL_CAPS module-level assignments with values and types
  • Docstrings: Google-style parsing — Args:, Returns:, Raises:, Examples: sections

Two scopes per folder:

  • Flat — only direct .py files (used by {{classes}}, {{functions}}, etc.)
  • Recursive — includes all subfolders (used by {{classes_rec}}, {{functions_rec}}, etc.)

Tree Structure

Each node in the tree is a Node dataclass:

Node
├── title              display title
├── content            markdown body (from .md file)
├── source             path to Python source folder
├── output_path        relative output path (e.g. api/client.html)
├── children           list of child Nodes
├── order              explicit sort order (from front-matter)
├── is_auto            true for auto-generated pages
├── auto_class         class name (for auto class pages)
├── auto_function      function name (for function nav nodes)
└── meta               PageMeta from front-matter

Folder structure maps directly to the nav tree:

  • root.md in a folder → folder node config + landing page
  • Other .md files → child pages
  • Subfolders with root.md → nested folder nodes
  • children front-matter → auto-generated class/function pages

HTML Output

Each page is a self-contained HTML file with embedded search index:

<header>  project name | breadcrumb | search input  </header>

<nav class="sidebar-left">     ← navigation tree (persistent expand/collapse state)
<main class="content">          ← rendered page content
<aside class="sidebar-right">   ← table of contents (h2/h3 headings, scroll spy)

<script src="assets/app.js">     SPA navigation, search, scroll spy, transitions
<script>                          embedded search index + prefix (inline, no CORS issues)

Client-Side App (app.js)

The app runs entirely client-side with no build step or dependencies:

  • SPA navigation: intercepts internal link clicks, fetches pages via fetch(), swaps .content / sidebar / breadcrumb / nav without full reload. Falls back to normal navigation on file:// or fetch failure
  • Search: filters embedded __SEARCH_INDEX__ by title substring, renders dropdown
  • Scroll spy: highlights current section in the right sidebar on scroll
  • Nav tree: expand/collapse with localStorage persistence, animated via max-height CSS transitions
  • Anchor navigation: smooth scroll + highlight flash animation on target element
  • Sidebar sync: ResizeObserver keeps content margin aligned with dynamic sidebar width
  • History: pushState / popstate for browser back/forward support within SPA

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

slop_doc-1.0.tar.gz (66.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

slop_doc-1.0-py3-none-any.whl (65.1 kB view details)

Uploaded Python 3

File details

Details for the file slop_doc-1.0.tar.gz.

File metadata

  • Download URL: slop_doc-1.0.tar.gz
  • Upload date:
  • Size: 66.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for slop_doc-1.0.tar.gz
Algorithm Hash digest
SHA256 198d184e4a3e4519bade2eff4a0e3cdab47f34d9b380fe757ae8a2bc941cf4d1
MD5 2d2ab460c55a77559b0606d6b91715a2
BLAKE2b-256 4e30cac645bdf6d01eff2e1edd60d7dbe99a09a4405a8b264b0eb091b14801fa

See more details on using hashes here.

File details

Details for the file slop_doc-1.0-py3-none-any.whl.

File metadata

  • Download URL: slop_doc-1.0-py3-none-any.whl
  • Upload date:
  • Size: 65.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for slop_doc-1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6bf8ce3193bb7a5c7f7bd7f461384d52bb3c54fd9f7b173c161786710a789e4a
MD5 f838f89ae7df229dcefdfe083e3f4760
BLAKE2b-256 fa1724592a7d009e482f4d23ce2e45e9d8ff62f1a32b7d0e562c3ccfe3be11d9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page