Configuration
A project’s wiring goes in the [tool.research-helpers] section in the pyproject.toml file.
No keys are required—defaults are provided out-of-the-box—so a new project can be started with
minimal or no configuration.
The keys are written in kebab-case and map to the snake_case attribute of the same name, so
tables-dir in TOML is paper.tables_dir in Python. An unknown key raises a
ConfigWarning and is ignored, as does a key with the wrong
value type or with a value outside the allowed set.
Note
Wiring lives in pyproject.toml because that file is also the project-root marker.
Finding the configuration and finding the root are therefore one operation, and every
path in the configuration is resolved relative to the directory containing it.
Three layers of resolution
Each layer overrides the one before it:
Package defaults: everything works with no configuration at all.
[tool.research-helpers]: a project’s wiring, read once per root and cached.Explicit keyword arguments: always take precedence.
A value of None in an override is read as not specified at this layer, so a function may forward its own
optional arguments straight through to resolve() without
filtering them first:
def save_something(path, *, dpi=None):
settings = resolve(current_project().figures, dpi=dpi) # dpi=None leaves the project's value
Every entry point is fully callable with plain arguments and no project file.
Complete example
[tool.research-helpers.paper]
main = "tex/paper.tex"
tables-dir = "tex/tables"
figures-dir = "tex/figures"
bbl = "tex/out_dir/paper.bbl"
build-dir = "build"
text-width-in = 6.45 # \textwidth: letterpaper, 73pt margins
column-width-in = 3.04 # \columnwidth
[tool.research-helpers.figures]
profile = "print"
palette = "colorblind"
font = "TeX Gyre Pagella"
dpi = 300
[tool.research-helpers.log]
directory = "data/logs" # omit for console output only
console-level = "INFO"
file-level = "DEBUG"
colour = "auto"
[tool.research-helpers.sweep]
runs-dir = "runs"
contention-factor = 1.8 # no default is provided, has to be measured
[tool.research-helpers.arxiv]
engine = "xelatex"
texlive = 2025
bbl-format = "3.3"
Subsections
paper
Describes the document’s geometry and the locations of its different components. All paths are relative to the project root.
Key |
Type |
Default |
Description |
|---|---|---|---|
|
path |
|
The manuscript. |
|
path |
|
Location of generated |
|
path |
|
Location of generated |
|
path |
|
The biblatex file. |
|
path |
|
Scratch space for generated artefacts. |
|
float |
|
|
|
float |
|
|
Note
Both figures and tables are laid out against the document’s width, so a figure or a table is authored at the size it will be printed rather than scaled afterwards.
To find the real values, put \showthe\textwidth and \showthe\columnwidth in your document and
read them off the log. They come out in points, which you can write down as they are (see
Units below).
Units
Any length may be written in inches, millimetres, centimetres, or TeX points, by changing the key’s suffix. These four all set the same field to the same value:
text-width-in = 6.4757
text-width-mm = 164.48
text-width-cm = 16.448
text-width-pt = 468.0
Suffix |
Unit |
In one inch |
|---|---|---|
|
inch |
1 |
|
millimetre |
25.4 |
|
centimetre |
2.54 |
|
TeX point |
72.27 |
-pt is the default unit LaTeX uses, i.e., the one \showthe\textwidth prints.
Settings are stored in inches internally, because that is the unit matplotlib figure sizes use.
research-helpers doctor shows the converted value.
figures
Key |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
|
|
str |
|
Any seaborn palette name. |
|
str |
|
A matplotlib family name. |
|
int |
|
Used for saving figures. On-screen display is separate and fixed. |
|
float |
|
Height of a |
log
Key |
Type |
Default |
Description |
|---|---|---|---|
|
path |
(unset) |
Location where log files should be written. Omit it for console output only. |
|
str |
|
Any level name accepted by the standard library, including custom ones. |
|
str |
|
Same as above. Usually more verbose than the console. |
|
|
|
|
sweep
Key |
Type |
Default |
Description |
|---|---|---|---|
|
path |
|
Location where a sweep’s manifest, parts, and results are written. |
|
float |
(unset) |
A value indicating how much slower a task runs when the node is full. |
Note
contention-factor is the ratio between a task’s runtime alone on a node and its runtime with every
core busy, and it depends on the specifics of the setup (i.e. the code, the node, and the scheduler).
No default is provided since a wrong one would lead to incorrect walltime estimates that either waste
a queue slot or get the job killed before completion. Left unset, estimate_runtime()
reports its numbers as optimistic and lists instructions on how to measure the factor.
arxiv
Key |
Type |
Default |
Description |
|---|---|---|---|
|
str |
|
The engine you select on arXiv. Used to check for engine-specific hazards, e.g. microtype font expansion under XeTeX. |
|
int |
|
The TeX Live year selected on arXiv. Used in diagnostic messages. |
|
str |
|
The biblatex |
Checking settings resolution
The doctor utility can be used to check settings resolution:
research-helpers doctor
It prints every setting, the value in force, where it came from, and flags any configured path that does not exist:
root /Users/john/my-project
pyproject pyproject.toml
paper.main tex/paper.tex [pyproject]
paper.tables_dir tex/tables [pyproject]
paper.figures_dir tex/figures [pyproject]
paper.bbl tex/out_dir/paper.bbl [pyproject]
paper.build_dir build [pyproject]
paper.text_width_in 6.48 [pyproject]
paper.column_width_in 3.04 [pyproject]
figures.profile screen [pyproject]
figures.palette husl [pyproject]
figures.font DejaVu Sans [default]
figures.dpi 300 [pyproject]
figures.print_height_in 3.2 [default]
log.directory None [default]
log.console_level INFO [default]
log.file_level DEBUG [default]
log.colour auto [default]
sweep.runs_dir runs [default] MISSING
sweep.contention_factor None [default]
arxiv.engine xelatex [pyproject]
arxiv.texlive 2025 [pyproject]
arxiv.bbl_format 3.3 [pyproject]
Checking [default] versus [pyproject] is useful when trying to troubleshoot keys,
for example when a setting appears to have no effect—usually a typo in the key name, which also
produces a warning you can see with python -W always::UserWarning -c 'import ...'.
MISSING indicates a path that is configured but does not exist. In the example above, runs is
simply a directory that sweeps have not created yet. The same value on paper.main would mean that
the manuscript path is wrong.
Overriding the root
find_project_root() searches upward for pyproject.toml or
.git. Set RESEARCH_HELPERS_ROOT to skip the search—necessary in a scheduler job whose
working directory is outside the tree:
export RESEARCH_HELPERS_ROOT=/scratch/$USER/my-project
If no root is found, current_project() returns a project
carrying pure defaults rather than raising an exception so, for example, a Jupyter notebook
in a scratch directory still works.
Reading settings
Settings can be easily read by any code in the project:
from research_helpers.project import current_project
paper = current_project().paper
width = paper.text_width_in
current_project() caches per root, so calling it in a loop is
free.