Workspace-oriented publication workflow engine with the pubs CLI
Project description
pubify-pubs
pubify-pubs is a local-first publication workflow package built around pubify-mpl.
It is meant for host workspaces that keep publications, publication-local TeX sources, and pinned inputs under version control, while the package owns the workflow around publication discovery, figure export, LaTeX builds, and publication bootstrapping.
This package does not own your publications. A host workspace does.
See CHANGELOG.md for release history and user-visible changes, and CONTRIBUTING.md for contributor and release workflow guidance.
Requirements
- Python 3.10+
pubify-mpl- a working LaTeX installation for
pubs <publication-id> build
The build command runs latexmk against the publication-local TeX tree. If you export figures that use LaTeX text rendering through pubify-mpl, your TeX installation also needs to be available during Python-side figure export.
How It Works
pubify-pubs treats a configured host workspace as the source of truth.
pubify.yamldefines where publications live and where pinned publication data is stored- each publication lives under
papers/<publication-id>/ figures.pydeclares loaders, figures, stats, and tables- generated figures are exported into
tex/autofigures/ - generated stats are written into
tex/autostats.tex - generated tables are written into
tex/autotables.tex - LaTeX builds run against the publication-local
tex/tree
The local publication tree is canonical.
Quick Start
Initialize a workspace:
pubs init
That writes pubify.yaml like:
publications_root: papers
data_root: ""
preview:
publication: preview
figure: preview
Then initialize a new publication:
pubs init my-paper
That creates a publication skeleton like:
papers/my-paper/
figures.py
pub.yaml
tex/
main.tex
autofigures/
build/
Then iterate with:
pubs my-paper update
pubs my-paper build
Workspace Model
A host workspace is rooted by pubify.yaml. The package discovers that file by walking upward from the current working directory.
publications_root contains publication directories. When data_root is blank, pinned publication-local data defaults to:
papers/<publication-id>/data/...
If you want a shared workspace-level data area instead, set data_root explicitly, for example:
data_root: output/papers
Then pinned publication-local data resolves under:
output/papers/<publication-id>/...
This flexibility is intentional:
- publications stay under the host workspace's configured publication root
- pinned data can stay publication-local by default or under a configured shared data root
- package code lives independently from both
pubify.yaml can also configure preview backends independently for publication PDFs and exported figure PDFs:
preview:
publication: vscode
figure: preview
Supported backend values are:
preview- opens PDFs in macOS Preview via
open -a Preview
- opens PDFs in macOS Preview via
vscode- opens PDFs in a separate VS Code window via
code -n
- opens PDFs in a separate VS Code window via
If the preview section is omitted, both commands default to the preview backend.
Publication Layout
A typical publication contains:
papers/<publication-id>/
figures.py
pub.yaml
tex/
main.tex
autofigures/
build/
pub.yaml owns publication-local settings such as:
main_texmirror_rootexternal_data_rootssync_excludespubify-mpl-templatepubify-mpl-defaults
figures.py is the publication entrypoint. It defines:
- loaders decorated with
@data(...)or@external_data(...) - plotters decorated with
@figure - stats decorated with
@stat - tables decorated with
@table
Typical Workflow
- Keep publication-local TeX sources under
papers/<publication-id>/tex/. - Define loaders, figure functions, stats, and tables in
figures.py. - Run
pubs <publication-id> updateto refresh package-owned TeX support files, validate the publication definition, and regenerate figures, stats, and tables. - Run
pubs <publication-id> buildto validate and compile the publication. - Use
pubs <publication-id> previeworpubs <publication-id> figure <figure-id> previewwhile iterating.
To scaffold starter entrypoints directly into figures.py:
pubs <publication-id> data add <data-id>pubs <publication-id> figure add <figure-id>pubs <publication-id> stat add <stat-id>pubs <publication-id> table add <table-id>
Figures, Tables, And Loaders
Prefer @data(...) for pinned publication-local inputs under the configured data_root. Use @external_data(...) only for explicit external roots declared in pub.yaml.
Host publications import from the extracted package namespace directly:
from pubify_pubs.data import load_publication_data_npz, publication_data_path, save_publication_data_npz
from pubify_pubs import TableResult
from pubify_pubs.decorators import data, external_data, figure, stat, table
from pubify_pubs.export import FigureExport, panel
@data(...) and @external_data(...) both require relative paths. They reject absolute paths and path traversal.
@figure marks a callable as a logical publication figure. Exported figure functions typically return FigureExport values built from one or more panels.
return FigureExport(fig, layout="one")
return FigureExport([fig1, fig2], layout="two")
Use panel(...) only when one panel needs extra pubify export metadata beyond the figure or axes itself, such as subcaption_lines or per-panel export overrides.
When a plotting library creates text artists during figure construction, build the figure under ctx.rc so those artists inherit publication font defaults at creation time:
@figure
def custom_map(ctx):
with ctx.rc:
fig = build_custom_map()
return fig
For figure-specific cleanup that pubify still cannot discover generically, pass prepare_export(...) through FigureExport(..., kwargs={...}).
@table marks a callable as a logical publication table. Table functions return TableResult(...), which owns logical table data and simple rendering while LaTeX keeps ownership of headers, captions, labels, rules, and layout.
@table
def tabulate_summary(ctx):
return TableResult(
[
["Metric", "Value"],
["Count", 3],
["Mean", 2.00],
],
formats=["{}", "{:.2f}"],
)
Column rendering is intentionally small:
formats[col]None,"", or"{}"meansstr(value)then LaTeX-escape- ordinary format strings like
"{:.2f}"format then escape "tex"means the value itself is already TeX and is inserted raw
tex_wrappers[col]- wrap the formatted value into raw TeX using one
@placeholder
- wrap the formatted value into raw TeX using one
multicolumns- enables compact horizontal merging without changing logical width
Pinned Publication Data
pubify-pubs includes helpers for publication-owned binary data:
publication_data_path(...)save_publication_data_npz(...)load_publication_data_npz(...)
publication_data_path(...) resolves paths under:
<data_root>/<publication-id>/...
It rejects absolute paths and .., and it creates parent directories automatically.
Format-specific helpers should generally come in save/load pairs when pubify-pubs owns the format handling.
Generated Figures, Stats, Tables, And TeX Assets
tex/autofigures/ is the framework-owned generated figure directory.
- generated figures from
figures.pyare exported there - full
figure updatetreats it as an authoritative snapshot and clears stale generated files first - targeted
figure <figure-id> updatestays incremental - TeX should reference generated figures explicitly by path such as
autofigures/<name>.pdf
tex/autostats.tex is the framework-owned generated stats file.
stat updaterewrites it as one authoritative snapshot- TeX should include it explicitly, for example with
\input{autostats.tex} - stats return either one value or a
dict[str, object] - generated stat macros are named
\Stat<StatId>and\Stat<StatId><Key>
tex/autotables.tex is the framework-owned generated tables file.
table updaterewrites it as one authoritative snapshottable <table-id> updatestill rewrites the full snapshot after computing the selected table- TeX should include it explicitly, for example with
\input{autotables.tex} - single-body tables emit
\Table<Id> - multi-body tables emit
\Table<Id>{1},\Table<Id>{2}, ... updateandbuildvalidate logical table width against direct manuscript uses inside supported environments such astabular,tabularx, andlongtable
Manual and static paper assets are ordinary publication-local TeX files. They are not part of the generated export surface and do not belong in tex/autofigures/.
CLI
The installed command is pubs.
Top-level commands:
pubs listpubs initpubs init <publication-id>
Publication commands:
pubs <publication-id> shellpubs <publication-id> data [list|add <data-id>]pubs <publication-id> figure [list|add <figure-id>|update|<figure-id> update|<figure-id> preview [<subfig-idx>]|<figure-id> latex [subcaption]]pubs <publication-id> stat [list|add <stat-id>|update|<stat-id> update|<stat-id> latex]pubs <publication-id> table [list|add <table-id>|update|<table-id> update|<table-id> latex]pubs <publication-id> updatepubs <publication-id> build [--clear]pubs <publication-id> preview
Optional advanced workflows:
update refreshes package-owned TeX support files, validates the publication definition, and regenerates figures, stats, and tables. build refreshes package-owned TeX support files, validates the publication definition, and then compiles the current TeX tree; it does not regenerate figures, stats, or tables, so run update first when generated outputs need refreshing.
tables is an alias for table in both the CLI and the publication shell.
The latex commands are read-only convenience helpers. They never edit manuscript files, and they print one blank line above and below the emitted snippet to make terminal selection easier. tex is accepted as an alias for latex.
Development
Install the package in editable mode:
pip install -e .
Run the package tests:
pytest
Build the docs site:
mkdocs build --strict
Development Approach
Keep publication-specific science code in host publications, not in this package.
License
MIT
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 pubify_pubs-1.0.1.tar.gz.
File metadata
- Download URL: pubify_pubs-1.0.1.tar.gz
- Upload date:
- Size: 76.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
edea638310b265bdad88ea123f66b1c2b9a28ae36a4f64531e8fd36ec4b5a40c
|
|
| MD5 |
7c8709634856b56a59858abd9cd982d6
|
|
| BLAKE2b-256 |
663ee01b67514187139a047d56c2f60d2667e88f38b7f80e5204a809e4f4c96d
|
File details
Details for the file pubify_pubs-1.0.1-py3-none-any.whl.
File metadata
- Download URL: pubify_pubs-1.0.1-py3-none-any.whl
- Upload date:
- Size: 62.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e15b4ba9a0270bfd7d24b025bd362ad35e1af713c028fa5e888c3140211fbb5b
|
|
| MD5 |
6bb38ee9704aefece8d0a0fbbfd771cb
|
|
| BLAKE2b-256 |
5668716103d0a02eda7e8a783465fed94b0f85307f4ab2a0640f9043d3092167
|