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 generic workflow around publication discovery, figure export, LaTeX builds, and publication bootstrapping.
This package does not own your publications. A host workspace does.
Project Docs
- Documentation home
- Architecture
- Development setup
- Testing and validation
- API reference
- Contributing
- Changelog
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 exported figures use LaTeX text rendering through pubify-mpl, LaTeX must also 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
pubs init also creates a minimal shared AGENTS.md under the configured
publications_root, which is papers/AGENTS.md by default.
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
The publication-local tex/ tree is canonical. tex/autofigures/ is the framework-owned generated figure directory, and tex/build/ is the local build output directory.
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.
Both data decorators require relative paths. They reject absolute paths and path traversal.
Host publications import from the extracted package namespace directly:
from pubify_pubs import TableResult
from pubify_pubs.data import (
load_publication_data_npz,
publication_data_path,
save_publication_data_npz,
)
from pubify_pubs.decorators import data, external_data, figure, stat, table
from pubify_pubs.export import FigureExport, panel
@figure marks a callable as a logical publication figure. Exported figure functions may return:
- a Matplotlib
Figure - a Matplotlib
Axes - a sequence of figures or axes
- a
FigureExportvalue for explicit multi-panel control
FigureExport accepts a single Matplotlib Figure or Axes, a list or tuple of them, one panel(...), or a list or tuple of panel(...) values.
Exported figure functions commonly return FigureExport values built from one or more panels:
return FigureExport(fig, layout="onewide")
return FigureExport([fig1, fig2], layout="twowide")
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, use ctx.rc so those artists are born under the publication construction-time font defaults:
@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(...)
These helpers resolve data under:
<data_root>/<publication-id>/...
publication_data_path(...) resolves paths under that root. 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, andlongtablebuildvalidates and compiles the current TeX tree, but does not regenerate figures, stats, or tables
Manual and static publication assets remain ordinary publication-local TeX files. They do not belong in tex/autofigures/.
CLI Overview
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 validates and compiles the current publication-local 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.
Python API Overview
The public Python API is intentionally small. Host publications import from the pubify_pubs.* namespace directly:
from pubify_pubs import TableResult
from pubify_pubs.data import (
load_publication_data_npz,
publication_data_path,
save_publication_data_npz,
)
from pubify_pubs.decorators import data, external_data, figure, stat, table
from pubify_pubs.export import FigureExport, panel
from pubify_pubs.discovery import find_workspace_root
Use the API reference for the docstring-driven reference pages.
Development
Use the repo-local ./.conda environment by default for Python commands, test runs, and docs builds. The durable contributor workflow lives in the development setup and testing docs.
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.3.tar.gz.
File metadata
- Download URL: pubify_pubs-1.0.3.tar.gz
- Upload date:
- Size: 77.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
458f22882a0eacac35fd4e0266a418389f9e8ead03faaab54aa5debb4ba5db7f
|
|
| MD5 |
4d7fad4de308ecced05587f21d26d2c5
|
|
| BLAKE2b-256 |
f65c8f72e5f59b9e698b934c6df62c541ea347ad2c2f062de1d431a8cb908bf9
|
File details
Details for the file pubify_pubs-1.0.3-py3-none-any.whl.
File metadata
- Download URL: pubify_pubs-1.0.3-py3-none-any.whl
- Upload date:
- Size: 63.3 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 |
a82e42ed09e2f65f94539ff4df242f4125b68f6c53f69c0dd8af3e3570d28470
|
|
| MD5 |
0010366ec054b2392fc1920dcbca0b8a
|
|
| BLAKE2b-256 |
145292063e60c61fe8f9c7ef953905c9e5347504dbd74f18b23169c74f1e3426
|