Skip to main content

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

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.yaml defines where publications live and where pinned publication data is stored
  • each publication lives under papers/<publication-id>/
  • figures.py declares 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
  • vscode
    • opens PDFs in a separate VS Code window via code -n

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_tex
  • mirror_root
  • external_data_roots
  • sync_excludes
  • pubify-mpl-template
  • pubify-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

  1. Keep publication-local TeX sources under papers/<publication-id>/tex/.
  2. Define loaders, figure functions, stats, and tables in figures.py.
  3. Run pubs <publication-id> update to refresh package-owned TeX support files, validate the publication definition, and regenerate figures, stats, and tables.
  4. Run pubs <publication-id> build to validate and compile the publication.
  5. Use pubs <publication-id> preview or pubs <publication-id> figure <figure-id> preview while 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 FigureExport value 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 "{}" means str(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
  • 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.py are exported there
  • full figure update treats it as an authoritative snapshot and clears stale generated files first
  • targeted figure <figure-id> update stays 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 update rewrites 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 update rewrites it as one authoritative snapshot
  • table <table-id> update still 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}, ...
  • update and build validate logical table width against direct manuscript uses inside supported environments such as tabular, tabularx, and longtable
  • build validates 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 list
  • pubs init
  • pubs init <publication-id>

Publication commands:

  • pubs <publication-id> shell
  • pubs <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> update
  • pubs <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

pubify_pubs-1.0.3.tar.gz (77.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pubify_pubs-1.0.3-py3-none-any.whl (63.3 kB view details)

Uploaded Python 3

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

Hashes for pubify_pubs-1.0.3.tar.gz
Algorithm Hash digest
SHA256 458f22882a0eacac35fd4e0266a418389f9e8ead03faaab54aa5debb4ba5db7f
MD5 4d7fad4de308ecced05587f21d26d2c5
BLAKE2b-256 f65c8f72e5f59b9e698b934c6df62c541ea347ad2c2f062de1d431a8cb908bf9

See more details on using hashes here.

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

Hashes for pubify_pubs-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 a82e42ed09e2f65f94539ff4df242f4125b68f6c53f69c0dd8af3e3570d28470
MD5 0010366ec054b2392fc1920dcbca0b8a
BLAKE2b-256 145292063e60c61fe8f9c7ef953905c9e5347504dbd74f18b23169c74f1e3426

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page