StableFill

Getting Started

StableFill updates LaTeX, LyX, and Markdown documents from plain text result files. Put tables in labeled <Tab:...> blocks, put one-off values in <Val:...> or <Value:...> blocks, and run one command to create the filled document.

Installation

Recommended for command-line use:

uv tool install "git+https://github.com/avvorstenbosch/StableFill"

Alternative with pip:

python -m pip install "git+https://github.com/avvorstenbosch/StableFill"

Run StableFill with either command name:

stablefill -i results.txt -o paper_filled.tex paper_template.tex
tablefill  -i results.txt -o paper_filled.tex paper_template.tex

The Python distribution is named stablefill. The old tablefill command and from tablefill import tablefill import remain as compatibility aliases.

For development:

git clone https://github.com/avvorstenbosch/StableFill
cd StableFill
uv run --extra test python -m pytest -q
uv run --extra docs mkdocs build --strict

Initialize A Project

For a new paper, create the small default layout:

stablefill init

This creates:

stablefill.toml
tables/

The config points to paper_template.tex, writes paper_filled.tex, and reads all .txt files from tables/. StableFill does not create a manuscript template because most users already have their own TeX, LyX, or Markdown file.

The Basic Workflow

Create an input file called results.txt:

<Val:sample_size>
48210

<Value:mean_income>
57890.25

<Tab:regression>
0.125*** -0.456 0.789**
(0.031) (0.100) (0.222)
Yes No Yes
48210 48210 48210

Create a LaTeX template called paper_template.tex:

\documentclass{article}
\begin{document}
The final sample contains  observations.
Mean income is .

\begin{table}
\caption{Regression output}
\label{tab:regression}
\begin{tabular}{lccc}
Treatment & ### & ### & ### \\
          & ### & ### & ### \\
Controls  & ### & ### & ### \\
N         & #0,# & #0,# & #0,# \\
\end{tabular}
\end{table}
\end{document}

Fill the template:

stablefill -i results.txt -o paper_filled.tex paper_template.tex

The relevant filled content is:

The final sample contains 48,210 observations.
Mean income is 57,890.2.

Treatment & 0.125*** & -0.456 & 0.789** \\
          & (0.031) & (0.100) & (0.222) \\
Controls  & Yes & No & Yes \\
N         & 48,210 & 48,210 & 48,210 \\

Using An Input Directory

StableFill can read a directory of result files instead of one combined input file:

stablefill --input-dir tables -o paper_filled.tex paper_template.tex

All .txt files in the directory are read in sorted filename order. Other extensions are ignored. This lets you keep separate outputs from different analysis scripts, for example:

tables/
  01-summary.txt
  02-main-regressions.txt
  03-appendix.txt

If no --input or --input-dir is provided, StableFill automatically looks for a tables directory next to the template. With this layout:

paper_template.tex
tables/
  01-summary.txt
  02-main-regressions.txt

you can run:

stablefill -o paper_filled.tex paper_template.tex

Using stablefill.toml

If you run the same fill command repeatedly, put the settings in a stablefill.toml file:

template = "paper_template.tex"
output = "paper_filled.tex"
input_dir = "tables"
no_header = true

[formatting]
pvals = [0.1, 0.05, 0.01]
stars = ["*", "**", "***"]

Then run:

stablefill

Before writing a filled file, inspect what StableFill sees:

stablefill inspect

The inspect command prints the input files, table blocks, named values, template tables, placeholder counts, missing inputs, ignored already-filled tables, and unused input blocks. It does not create an output file.

Command-line arguments override config values. For example, this keeps the template and input directory from stablefill.toml but writes an annotated review copy:

stablefill --annotate -o paper_annotated.tex

Inline Values For Paragraphs

Paragraph values do not need start/end markers. Add a named value to your input file:

<Val:population>
5708

<Value:p_value>
0.024

Then reference it anywhere in your TeX, LyX, or Markdown template:

The study population contains  people.
The result is significant.

val: is an optional prefix inside the placeholder. These are equivalent:



Use val: when you want prose placeholders to stand out clearly from normal text.

Preview Values While Drafting

When you want to see the values without replacing the placeholders yet, create an annotated copy:

stablefill --annotate -i results.txt -o paper_annotated.tex paper_template.tex

StableFill keeps each placeholder and writes a compact [[SF: ...]] preview beside it:

The final sample contains [[SF: 48,210]] observations.
N         & #0,#[[SF: 48,210]] & #0,#[[SF: 48,210]] & #0,#[[SF: 48,210]] \\

Run the annotation command again to refresh existing previews after the input file changes. A normal fill removes adjacent annotations while replacing the placeholders:

stablefill -i results.txt -o paper_filled.tex paper_annotated.tex

To clean annotations from a template without filling anything, use:

stablefill --remove-annotations -o paper_clean.tex paper_annotated.tex

Annotated LaTeX files are intended for review and may not compile. Filled files do not keep [[SF: ...]] annotations.

Placeholder Formats

Placeholder Meaning
### Insert the next table value as written.
#0,# Round to zero decimals and add thousands separators.
#2# Round to two decimals.
#*# Convert a p-value to significance stars.
#{:.2f}# Use Python format syntax.
`` Insert a named inline value.
`` Insert a formatted named inline value.

Tables

Tables are matched by label. This template:

\begin{table}
\caption{Regression output}
\label{tab:regression}
...
\end{table}

uses this input block:

<Tab:regression>
...

Within a table, StableFill fills placeholders in document order: top to bottom, then left to right within each line. It does not require every table row to have the same number of placeholders.

In LaTeX, a fillable table is a \begin{table} or \begin{subtable} region with a \label{tab:name} inside it. StableFill only fills sequential table placeholders inside matched table regions. A table that already contains final values and no placeholders is left unchanged and does not produce a missing-input warning.

Economics Regression Tables

StableFill supports both common standard-error layouts.

Standard errors below estimates:

<Tab:se_below>
0.125*** -0.456 0.789**
(0.031) (0.100) (0.222)
Treatment & ### & ### & ### \\
          & ### & ### & ### \\

Standard errors beside estimates:

<Tab:inline_se>
0.125*** (0.031) -0.456 (0.100) 0.789** (0.222)
Treatment & ### & ### & ### \\

This fills as:

Treatment & 0.125*** (0.031) & -0.456 (0.100) & 0.789** (0.222) \\

Ragged Tables

Uneven placeholder layouts are valid. For example:

<Tab:ragged>
101 202
A1 A2 A3 A4
B1 B2 B3 B4
\label{tab:ragged}
\multicolumn{3}{l}{Header A: #0,#} & \multicolumn{2}{r}{Header B: #0,#} \\
Row A & ### & ### & ### & ### \\
Row B & ### & ### & ### & ### \\

fills as:

\multicolumn{3}{l}{Header A: 101} & \multicolumn{2}{r}{Header B: 202} \\
Row A & A1 & A2 & A3 & A4 \\
Row B & B1 & B2 & B3 & B4 \\

Python API

You can also call StableFill from Python:

from stablefill import stablefill

status, message = stablefill(
    input="results.txt",
    template="paper_template.tex",
    output="paper_filled.tex",
)

The historical from tablefill import tablefill import remains available as a compatibility alias.

More Examples