Program Enhanced Text — documentation automation tool
Project description
🐾 PET — Program Enhanced Text
Write docs once. Automate the rest. Never copy-paste again.
PET turns your documentation into a living document.
Instead of manually copying version numbers, pasting code samples, and updating
chapter headings by hand, you write a .md.pet template and let PET generate
the final Markdown. When your source changes, regenerate — done.
pet README.md.pet README.md
Not just Markdown. PET works with any text-based format — AsciiDoc, reStructuredText, LaTeX, HTML, plain text, or anything else. The template extension and output format are entirely up to you. Markdown is the most common use case, which is why the examples here use it.
1 The Idea in 30 Seconds
A .md.pet file is regular Markdown with two kinds of Python blocks:
| Syntax | Purpose |
|---|---|
{% statements %} |
Execute Python — output via print, doc |, or out() |
`{{` expr `}}` |
Evaluate an expression and write its value directly |
Template (docs.md.pet):
{%
use('number')
use('snippet')
n = number(fmt="{:3d} ")
src = snippet("src")
%}
The core loop:
{{ n(src('main_loop')) }}
Output (docs.md):
The core loop:
1 for item in queue:
2 process(item)
3 log(item)
{% ... %} blocks run arbitrary Python statements.
{{ expr }} is shorthand for single expressions — no doc | needed.
Everything else passes through untouched.
2 Installation
pip install pet-doc
Initialise a project (copies the macro library into .pet/):
cd my-project
pet init # first-time setup
pet init # safe to re-run — regenerates if macros unchanged, skips if you've edited them
pet init -f # force regeneration regardless of local edits
pet init for_claude # also installs the pet skill into Claude Code (.claude/)
Process a template once, or watch it for changes:
pet my-doc.md.pet my-doc.md # one-shot
pet watch my-doc.md.pet my-doc.md # live reload on every save
3 Built-in Macro Library
Text & Structure
| Macro | What it does |
|---|---|
chapter(header_prefix="#", sep=" ") |
Hierarchical section counter — # 1, ## 1.1, ### 1.1.1 |
number(start=1, step=1, fmt="{} ") |
Prefix each line of a text block with a sequential number |
include(filename) |
Read any file and return its content as a string |
snippet(source, exclude=None) |
Scan for named snippet / end snippet markers; source is a directory, glob pattern, or list of either; exclude accepts a filename, glob, or list |
dedent(text) |
Strip common leading indentation from included code |
Data Readers
All data macros expose get("dot.separated.path") for deep value access.
Lists support integer indices: get("dependencies.0.name").
| Macro | Format |
|---|---|
toml(file) |
TOML — great for pyproject.toml, config files |
yaml(file) |
YAML — nested keys, lists, indexed access |
json(file) |
JSON — identical API to yaml |
xml(file) |
XML — xpath queries, attribute access via attr() |
properties(file) |
Java .properties — flat key=value, dotted keys preserved |
env(file) |
.env files — strips surrounding quotes, skips comments |
Pipelines
pipe is an identity transformer. Compose steps with |:
use('pipe')
use('dedent')
use('number')
n = number(fmt="{:3d} ")
clean = pipe | dedent | n # dedent first, then add line numbers
doc | clean(include("src/core.py"))
Apply a transformation line by line with .on_lines():
shout = pipe | str.upper
doc | shout.on_lines()(include("words.txt"))
4 Worked Example — This README
This very document is generated by PET 2.0.0.
The version badge above is not hardcoded — it is read live from pyproject.toml:
1 | use('data/toml')
2 | proj = toml("pyproject.toml")
3 | VERSION = proj.get('project.version')
Inline values use {{ expr }} — the version in the footer is simply:
{{ VERSION }}
The section headings are auto-numbered by chapter:
4 | ch = chapter()
5 | # becomes -> # 1 The Idea in 30 Seconds
6 | # becomes -> # 2 Installation
7 | # etc.
Change the order, add a section, remove one — the numbers update on the next
pet README.md.pet README.md.
5 Why PET?
Documentation rots. Code evolves, but the docs that reference it often don't. Copy-pasted version numbers fall behind. Pasted code samples go stale. Chapter numbers break the moment you reorder sections.
PET's answer is deliberately small:
- No new language — your macros are plain Python classes
- No build framework — one command, one output file
- No lock-in — the output is standard Markdown; stop using PET any time
- Self-documenting — the template is the documentation intent
If information already exists somewhere in your project, humans shouldn't maintain a second copy of it. PET makes the automated copy the only copy.
For the full theory — single-source-of-truth, consistency checks as document unit tests, and why correct docs matter more than ever in the age of LLM RAG — see RATIONALE.md.
To learn how to write your own macros, with every built-in macro explained pattern by pattern, see MACROGUIDE.md.
Generated from README.md.pet by PET 2.0.0 · pet README.md.pet README.md
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pet_doc-2.0.0.tar.gz.
File metadata
- Download URL: pet_doc-2.0.0.tar.gz
- Upload date:
- Size: 70.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2abf37b33f03e364b2af8cb98d3b7e8e1002fcc1b29f081e61650b2472c9718
|
|
| MD5 |
3849cb313e56ab56ad0be6ab85bcdc5a
|
|
| BLAKE2b-256 |
55fac4afd965b17685817b683e5f8c7ea8abc952d135bfc723745a86b64099aa
|
File details
Details for the file pet_doc-2.0.0-py3-none-any.whl.
File metadata
- Download URL: pet_doc-2.0.0-py3-none-any.whl
- Upload date:
- Size: 31.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fc31f0214481b48cc08cf73ed9e2e04bedc11fcc66f4d5b878a819491bdb7a6
|
|
| MD5 |
2e4bb298bca233316425d5cc34f2d63d
|
|
| BLAKE2b-256 |
4b2d1ac6dc8f64cbc0d069e41918a85013c7bafcdb948e041598d5b36c2ac59f
|