Skip to main content

🐾 PET — Program Enhanced Text

Write docs once. Automate the rest. Never copy-paste again.

Python License Version


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

Metadata

Release files for pet-doc 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pet-doc 2.0.0
File Size Uploaded Provenance
pet_doc-2.0.0.tar.gz 70.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pet-doc 2.0.0
File Interpreter ABI Platform
pet_doc-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 102.4 kB

Release files / pet_doc-2.0.0.tar.gz

Download URL pet_doc-2.0.0.tar.gz
Size 70.7 kB
Tags Source
SHA-256 checksum
How to use checksums
b2abf37b33f03e364b2af8cb98d3b7e8e1002fcc1b29f081e61650b2472c9718
BLAKE2b-256 checksum
How to use checksums
55fac4afd965b17685817b683e5f8c7ea8abc952d135bfc723745a86b64099aa
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release files / pet_doc-2.0.0-py3-none-any.whl

Download URL pet_doc-2.0.0-py3-none-any.whl
Size 31.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7fc31f0214481b48cc08cf73ed9e2e04bedc11fcc66f4d5b878a819491bdb7a6
BLAKE2b-256 checksum
How to use checksums
4b2d1ac6dc8f64cbc0d069e41918a85013c7bafcdb948e041598d5b36c2ac59f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Security for pet-doc 2.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page