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.
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
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.
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 \\
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
stablefill.tomlIf 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
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.
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 | 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 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.
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) \\
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 \\
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.