pubify-pubs 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pubify_pubs-1.0.0/LICENSE +21 -0
- pubify_pubs-1.0.0/MANIFEST.in +15 -0
- pubify_pubs-1.0.0/PKG-INFO +328 -0
- pubify_pubs-1.0.0/README.md +309 -0
- pubify_pubs-1.0.0/pyproject.toml +40 -0
- pubify_pubs-1.0.0/setup.cfg +4 -0
- pubify_pubs-1.0.0/src/pubify_pubs/__init__.py +25 -0
- pubify_pubs-1.0.0/src/pubify_pubs/assets/__init__.py +1 -0
- pubify_pubs-1.0.0/src/pubify_pubs/assets/init/__init__.py +1 -0
- pubify_pubs-1.0.0/src/pubify_pubs/assets/init/figures.py +52 -0
- pubify_pubs-1.0.0/src/pubify_pubs/assets/init/main.tex +25 -0
- pubify_pubs-1.0.0/src/pubify_pubs/cli.py +1806 -0
- pubify_pubs-1.0.0/src/pubify_pubs/commands/__init__.py +33 -0
- pubify_pubs-1.0.0/src/pubify_pubs/commands/common.py +228 -0
- pubify_pubs-1.0.0/src/pubify_pubs/commands/core.py +873 -0
- pubify_pubs-1.0.0/src/pubify_pubs/commands/registry.py +56 -0
- pubify_pubs-1.0.0/src/pubify_pubs/config.py +396 -0
- pubify_pubs-1.0.0/src/pubify_pubs/data.py +86 -0
- pubify_pubs-1.0.0/src/pubify_pubs/decorators.py +127 -0
- pubify_pubs-1.0.0/src/pubify_pubs/discovery.py +415 -0
- pubify_pubs-1.0.0/src/pubify_pubs/export.py +250 -0
- pubify_pubs-1.0.0/src/pubify_pubs/helpers.py +21 -0
- pubify_pubs-1.0.0/src/pubify_pubs/latex_bootstrap.py +130 -0
- pubify_pubs-1.0.0/src/pubify_pubs/runtime.py +672 -0
- pubify_pubs-1.0.0/src/pubify_pubs/shell_incremental.py +392 -0
- pubify_pubs-1.0.0/src/pubify_pubs/stats.py +134 -0
- pubify_pubs-1.0.0/src/pubify_pubs/stubs.py +172 -0
- pubify_pubs-1.0.0/src/pubify_pubs/tables.py +537 -0
- pubify_pubs-1.0.0/src/pubify_pubs/texlog.py +93 -0
- pubify_pubs-1.0.0/src/pubify_pubs.egg-info/SOURCES.txt +32 -0
- pubify_pubs-1.0.0/tests/test_cli_core.py +3637 -0
- pubify_pubs-1.0.0/tests/test_export.py +596 -0
- pubify_pubs-1.0.0/tests/test_release.py +71 -0
- pubify_pubs-1.0.0/tests/test_stats.py +125 -0
- pubify_pubs-1.0.0/tests/test_tables.py +174 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nelson V. Nunes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
exclude .DS_Store .coverage coverage.xml
|
|
2
|
+
recursive-exclude * __pycache__ *.py[cod] .DS_Store
|
|
3
|
+
recursive-exclude * .ipynb_checkpoints/*
|
|
4
|
+
recursive-exclude * build/*
|
|
5
|
+
prune src/*.egg-info
|
|
6
|
+
prune build
|
|
7
|
+
prune dist
|
|
8
|
+
prune .pytest_cache
|
|
9
|
+
prune .mypy_cache
|
|
10
|
+
prune .ruff_cache
|
|
11
|
+
prune htmlcov
|
|
12
|
+
prune .venv
|
|
13
|
+
prune .conda
|
|
14
|
+
prune .idea
|
|
15
|
+
prune .vscode
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pubify-pubs
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Workspace-oriented publication workflow engine with the pubs CLI
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: matplotlib>=3.8
|
|
10
|
+
Requires-Dist: numpy>=1.26
|
|
11
|
+
Requires-Dist: pubify-mpl>=1.0.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
14
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
15
|
+
Requires-Dist: mkdocs>=1.6; extra == "dev"
|
|
16
|
+
Requires-Dist: mkdocstrings[python]>=0.30; extra == "dev"
|
|
17
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# pubify-pubs
|
|
21
|
+
|
|
22
|
+
`pubify-pubs` is a local-first publication workflow package built around `pubify-mpl`.
|
|
23
|
+
|
|
24
|
+
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.
|
|
25
|
+
|
|
26
|
+
This package does not own your publications. A host workspace does.
|
|
27
|
+
|
|
28
|
+
See [CHANGELOG.md](https://github.com/nvnunes/pubify-pubs/blob/main/CHANGELOG.md) for release history and user-visible changes, and [CONTRIBUTING.md](https://github.com/nvnunes/pubify-pubs/blob/main/CONTRIBUTING.md) for contributor and release workflow guidance.
|
|
29
|
+
|
|
30
|
+
## Requirements
|
|
31
|
+
|
|
32
|
+
- Python 3.10+
|
|
33
|
+
- `pubify-mpl`
|
|
34
|
+
- a working LaTeX installation for `pubs <publication-id> build`
|
|
35
|
+
|
|
36
|
+
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.
|
|
37
|
+
|
|
38
|
+
## How It Works
|
|
39
|
+
|
|
40
|
+
`pubify-pubs` treats a configured host workspace as the source of truth.
|
|
41
|
+
|
|
42
|
+
- `pubify.conf` defines where publications live and where pinned publication data is stored
|
|
43
|
+
- each publication lives under `papers/<publication-id>/`
|
|
44
|
+
- `figures.py` declares loaders, figures, stats, and tables
|
|
45
|
+
- generated figures are exported into `tex/autofigures/`
|
|
46
|
+
- generated stats are written into `tex/autostats.tex`
|
|
47
|
+
- generated tables are written into `tex/autotables.tex`
|
|
48
|
+
- LaTeX builds run against the publication-local `tex/` tree
|
|
49
|
+
|
|
50
|
+
The local publication tree is canonical.
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
Create a workspace rooted by `pubify.conf`:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
publications_root: papers
|
|
58
|
+
data_root: output/papers
|
|
59
|
+
preview:
|
|
60
|
+
publication: preview
|
|
61
|
+
figure: preview
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Initialize a new publication:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pubs init my-paper
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
That creates a publication skeleton like:
|
|
71
|
+
|
|
72
|
+
```text
|
|
73
|
+
papers/my-paper/
|
|
74
|
+
figures.py
|
|
75
|
+
pub.yaml
|
|
76
|
+
tex/
|
|
77
|
+
main.tex
|
|
78
|
+
autofigures/
|
|
79
|
+
build/
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Then iterate with:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pubs my-paper update
|
|
86
|
+
pubs my-paper build
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Workspace Model
|
|
90
|
+
|
|
91
|
+
A host workspace is rooted by `pubify.conf`. The package discovers that file by walking upward from the current working directory.
|
|
92
|
+
|
|
93
|
+
`publications_root` contains publication directories. `data_root` contains pinned publication-local data, typically under:
|
|
94
|
+
|
|
95
|
+
```text
|
|
96
|
+
output/papers/<publication-id>/...
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This separation is intentional:
|
|
100
|
+
|
|
101
|
+
- publications stay under the host workspace's configured publication root
|
|
102
|
+
- pinned data stays under the configured data root
|
|
103
|
+
- package code lives independently from both
|
|
104
|
+
|
|
105
|
+
`pubify.conf` can also configure preview backends independently for publication PDFs and exported figure PDFs:
|
|
106
|
+
|
|
107
|
+
```yaml
|
|
108
|
+
preview:
|
|
109
|
+
publication: vscode
|
|
110
|
+
figure: preview
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Supported backend values are:
|
|
114
|
+
|
|
115
|
+
- `preview`
|
|
116
|
+
- opens PDFs in macOS Preview via `open -a Preview`
|
|
117
|
+
- `vscode`
|
|
118
|
+
- opens PDFs in a separate VS Code window via `code -n`
|
|
119
|
+
|
|
120
|
+
If the `preview` section is omitted, both commands default to the `preview` backend.
|
|
121
|
+
|
|
122
|
+
## Publication Layout
|
|
123
|
+
|
|
124
|
+
A typical publication contains:
|
|
125
|
+
|
|
126
|
+
```text
|
|
127
|
+
papers/<publication-id>/
|
|
128
|
+
figures.py
|
|
129
|
+
pub.yaml
|
|
130
|
+
tex/
|
|
131
|
+
main.tex
|
|
132
|
+
autofigures/
|
|
133
|
+
build/
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`pub.yaml` owns publication-local settings such as:
|
|
137
|
+
|
|
138
|
+
- `main_tex`
|
|
139
|
+
- `mirror_root`
|
|
140
|
+
- `external_data_roots`
|
|
141
|
+
- `sync_excludes`
|
|
142
|
+
- `pubify-mpl-template`
|
|
143
|
+
- `pubify-mpl-defaults`
|
|
144
|
+
|
|
145
|
+
`figures.py` is the publication entrypoint. It defines:
|
|
146
|
+
|
|
147
|
+
- loaders decorated with `@data(...)` or `@external_data(...)`
|
|
148
|
+
- plotters decorated with `@figure`
|
|
149
|
+
- stats decorated with `@stat`
|
|
150
|
+
- tables decorated with `@table`
|
|
151
|
+
|
|
152
|
+
## Typical Workflow
|
|
153
|
+
|
|
154
|
+
1. Keep publication-local TeX sources under `papers/<publication-id>/tex/`.
|
|
155
|
+
2. Define loaders, figure functions, stats, and tables in `figures.py`.
|
|
156
|
+
3. Run `pubs <publication-id> update` to refresh package-owned TeX support files, validate the publication definition, and regenerate figures, stats, and tables.
|
|
157
|
+
4. Run `pubs <publication-id> build` to validate and compile the publication.
|
|
158
|
+
5. Use `pubs <publication-id> preview` or `pubs <publication-id> figure <figure-id> preview` while iterating.
|
|
159
|
+
|
|
160
|
+
To scaffold starter entrypoints directly into `figures.py`:
|
|
161
|
+
|
|
162
|
+
- `pubs <publication-id> data add <data-id>`
|
|
163
|
+
- `pubs <publication-id> figure add <figure-id>`
|
|
164
|
+
- `pubs <publication-id> stat add <stat-id>`
|
|
165
|
+
- `pubs <publication-id> table add <table-id>`
|
|
166
|
+
|
|
167
|
+
## Figures, Tables, And Loaders
|
|
168
|
+
|
|
169
|
+
Prefer `@data(...)` for pinned publication-local inputs under the configured `data_root`. Use `@external_data(...)` only for explicit external roots declared in `pub.yaml`.
|
|
170
|
+
|
|
171
|
+
Host publications import from the extracted package namespace directly:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from pubify_pubs.data import load_publication_data_npz, publication_data_path, save_publication_data_npz
|
|
175
|
+
from pubify_pubs import TableResult
|
|
176
|
+
from pubify_pubs.decorators import data, external_data, figure, stat, table
|
|
177
|
+
from pubify_pubs.export import FigureExport, panel
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
`@data(...)` and `@external_data(...)` both require relative paths. They reject absolute paths and path traversal.
|
|
181
|
+
|
|
182
|
+
`@figure` marks a callable as a logical publication figure. Exported figure functions typically return `FigureExport` values built from one or more panels.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
return FigureExport(fig, layout="one")
|
|
186
|
+
return FigureExport([fig1, fig2], layout="two")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
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:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
@figure
|
|
195
|
+
def custom_map(ctx):
|
|
196
|
+
with ctx.rc:
|
|
197
|
+
fig = build_custom_map()
|
|
198
|
+
return fig
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
For figure-specific cleanup that pubify still cannot discover generically, pass `prepare_export(...)` through `FigureExport(..., kwargs={...})`.
|
|
202
|
+
|
|
203
|
+
`@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.
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
@table
|
|
207
|
+
def tabulate_summary(ctx):
|
|
208
|
+
return TableResult(
|
|
209
|
+
[
|
|
210
|
+
["Metric", "Value"],
|
|
211
|
+
["Count", 3],
|
|
212
|
+
["Mean", 2.00],
|
|
213
|
+
],
|
|
214
|
+
formats=["{}", "{:.2f}"],
|
|
215
|
+
)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Column rendering is intentionally small:
|
|
219
|
+
|
|
220
|
+
- `formats[col]`
|
|
221
|
+
- `None`, `""`, or `"{}"` means `str(value)` then LaTeX-escape
|
|
222
|
+
- ordinary format strings like `"{:.2f}"` format then escape
|
|
223
|
+
- `"tex"` means the value itself is already TeX and is inserted raw
|
|
224
|
+
- `tex_wrappers[col]`
|
|
225
|
+
- wrap the formatted value into raw TeX using one `@` placeholder
|
|
226
|
+
- `multicolumns`
|
|
227
|
+
- enables compact horizontal merging without changing logical width
|
|
228
|
+
|
|
229
|
+
## Pinned Publication Data
|
|
230
|
+
|
|
231
|
+
`pubify-pubs` includes helpers for publication-owned binary data:
|
|
232
|
+
|
|
233
|
+
- `publication_data_path(...)`
|
|
234
|
+
- `save_publication_data_npz(...)`
|
|
235
|
+
- `load_publication_data_npz(...)`
|
|
236
|
+
|
|
237
|
+
`publication_data_path(...)` resolves paths under:
|
|
238
|
+
|
|
239
|
+
```text
|
|
240
|
+
<data_root>/<publication-id>/...
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
It rejects absolute paths and `..`, and it creates parent directories automatically.
|
|
244
|
+
|
|
245
|
+
Format-specific helpers should generally come in save/load pairs when `pubify-pubs` owns the format handling.
|
|
246
|
+
|
|
247
|
+
## Generated Figures, Stats, Tables, And TeX Assets
|
|
248
|
+
|
|
249
|
+
`tex/autofigures/` is the framework-owned generated figure directory.
|
|
250
|
+
|
|
251
|
+
- generated figures from `figures.py` are exported there
|
|
252
|
+
- full `figure update` treats it as an authoritative snapshot and clears stale generated files first
|
|
253
|
+
- targeted `figure <figure-id> update` stays incremental
|
|
254
|
+
- TeX should reference generated figures explicitly by path such as `autofigures/<name>.pdf`
|
|
255
|
+
|
|
256
|
+
`tex/autostats.tex` is the framework-owned generated stats file.
|
|
257
|
+
|
|
258
|
+
- `stat update` rewrites it as one authoritative snapshot
|
|
259
|
+
- TeX should include it explicitly, for example with `\input{autostats.tex}`
|
|
260
|
+
- stats return either one value or a `dict[str, object]`
|
|
261
|
+
- generated stat macros are named `\Stat<StatId>` and `\Stat<StatId><Key>`
|
|
262
|
+
|
|
263
|
+
`tex/autotables.tex` is the framework-owned generated tables file.
|
|
264
|
+
|
|
265
|
+
- `table update` rewrites it as one authoritative snapshot
|
|
266
|
+
- `table <table-id> update` still rewrites the full snapshot after computing the selected table
|
|
267
|
+
- TeX should include it explicitly, for example with `\input{autotables.tex}`
|
|
268
|
+
- single-body tables emit `\Table<Id>`
|
|
269
|
+
- multi-body tables emit `\Table<Id>{1}`, `\Table<Id>{2}`, ...
|
|
270
|
+
- `update` and `build` validate logical table width against direct manuscript uses inside supported environments such as `tabular`, `tabularx`, and `longtable`
|
|
271
|
+
|
|
272
|
+
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/`.
|
|
273
|
+
|
|
274
|
+
## CLI
|
|
275
|
+
|
|
276
|
+
The installed command is `pubs`.
|
|
277
|
+
|
|
278
|
+
Top-level commands:
|
|
279
|
+
|
|
280
|
+
- `pubs list`
|
|
281
|
+
- `pubs init <publication-id>`
|
|
282
|
+
|
|
283
|
+
Publication commands:
|
|
284
|
+
|
|
285
|
+
- `pubs <publication-id> shell`
|
|
286
|
+
- `pubs <publication-id> data [list|add <data-id>]`
|
|
287
|
+
- `pubs <publication-id> figure [list|add <figure-id>|update|<figure-id> update|<figure-id> preview [<subfig-idx>]|<figure-id> latex [subcaption]]`
|
|
288
|
+
- `pubs <publication-id> stat [list|add <stat-id>|update|<stat-id> update|<stat-id> latex]`
|
|
289
|
+
- `pubs <publication-id> table [list|add <table-id>|update|<table-id> update|<table-id> latex]`
|
|
290
|
+
- `pubs <publication-id> update`
|
|
291
|
+
- `pubs <publication-id> build [--clear]`
|
|
292
|
+
- `pubs <publication-id> preview`
|
|
293
|
+
|
|
294
|
+
Optional advanced workflows:
|
|
295
|
+
|
|
296
|
+
`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.
|
|
297
|
+
|
|
298
|
+
`tables` is an alias for `table` in both the CLI and the publication shell.
|
|
299
|
+
|
|
300
|
+
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`.
|
|
301
|
+
|
|
302
|
+
## Development
|
|
303
|
+
|
|
304
|
+
Install the package in editable mode:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
pip install -e .
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Run the package tests:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
pytest
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Build the docs site:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
mkdocs build --strict
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Development Approach
|
|
323
|
+
|
|
324
|
+
Keep publication-specific science code in host publications, not in this package.
|
|
325
|
+
|
|
326
|
+
## License
|
|
327
|
+
|
|
328
|
+
MIT
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# pubify-pubs
|
|
2
|
+
|
|
3
|
+
`pubify-pubs` is a local-first publication workflow package built around `pubify-mpl`.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
This package does not own your publications. A host workspace does.
|
|
8
|
+
|
|
9
|
+
See [CHANGELOG.md](https://github.com/nvnunes/pubify-pubs/blob/main/CHANGELOG.md) for release history and user-visible changes, and [CONTRIBUTING.md](https://github.com/nvnunes/pubify-pubs/blob/main/CONTRIBUTING.md) for contributor and release workflow guidance.
|
|
10
|
+
|
|
11
|
+
## Requirements
|
|
12
|
+
|
|
13
|
+
- Python 3.10+
|
|
14
|
+
- `pubify-mpl`
|
|
15
|
+
- a working LaTeX installation for `pubs <publication-id> build`
|
|
16
|
+
|
|
17
|
+
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.
|
|
18
|
+
|
|
19
|
+
## How It Works
|
|
20
|
+
|
|
21
|
+
`pubify-pubs` treats a configured host workspace as the source of truth.
|
|
22
|
+
|
|
23
|
+
- `pubify.conf` defines where publications live and where pinned publication data is stored
|
|
24
|
+
- each publication lives under `papers/<publication-id>/`
|
|
25
|
+
- `figures.py` declares loaders, figures, stats, and tables
|
|
26
|
+
- generated figures are exported into `tex/autofigures/`
|
|
27
|
+
- generated stats are written into `tex/autostats.tex`
|
|
28
|
+
- generated tables are written into `tex/autotables.tex`
|
|
29
|
+
- LaTeX builds run against the publication-local `tex/` tree
|
|
30
|
+
|
|
31
|
+
The local publication tree is canonical.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
Create a workspace rooted by `pubify.conf`:
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
publications_root: papers
|
|
39
|
+
data_root: output/papers
|
|
40
|
+
preview:
|
|
41
|
+
publication: preview
|
|
42
|
+
figure: preview
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Initialize a new publication:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pubs init my-paper
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
That creates a publication skeleton like:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
papers/my-paper/
|
|
55
|
+
figures.py
|
|
56
|
+
pub.yaml
|
|
57
|
+
tex/
|
|
58
|
+
main.tex
|
|
59
|
+
autofigures/
|
|
60
|
+
build/
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Then iterate with:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
pubs my-paper update
|
|
67
|
+
pubs my-paper build
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Workspace Model
|
|
71
|
+
|
|
72
|
+
A host workspace is rooted by `pubify.conf`. The package discovers that file by walking upward from the current working directory.
|
|
73
|
+
|
|
74
|
+
`publications_root` contains publication directories. `data_root` contains pinned publication-local data, typically under:
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
output/papers/<publication-id>/...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This separation is intentional:
|
|
81
|
+
|
|
82
|
+
- publications stay under the host workspace's configured publication root
|
|
83
|
+
- pinned data stays under the configured data root
|
|
84
|
+
- package code lives independently from both
|
|
85
|
+
|
|
86
|
+
`pubify.conf` can also configure preview backends independently for publication PDFs and exported figure PDFs:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
preview:
|
|
90
|
+
publication: vscode
|
|
91
|
+
figure: preview
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Supported backend values are:
|
|
95
|
+
|
|
96
|
+
- `preview`
|
|
97
|
+
- opens PDFs in macOS Preview via `open -a Preview`
|
|
98
|
+
- `vscode`
|
|
99
|
+
- opens PDFs in a separate VS Code window via `code -n`
|
|
100
|
+
|
|
101
|
+
If the `preview` section is omitted, both commands default to the `preview` backend.
|
|
102
|
+
|
|
103
|
+
## Publication Layout
|
|
104
|
+
|
|
105
|
+
A typical publication contains:
|
|
106
|
+
|
|
107
|
+
```text
|
|
108
|
+
papers/<publication-id>/
|
|
109
|
+
figures.py
|
|
110
|
+
pub.yaml
|
|
111
|
+
tex/
|
|
112
|
+
main.tex
|
|
113
|
+
autofigures/
|
|
114
|
+
build/
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`pub.yaml` owns publication-local settings such as:
|
|
118
|
+
|
|
119
|
+
- `main_tex`
|
|
120
|
+
- `mirror_root`
|
|
121
|
+
- `external_data_roots`
|
|
122
|
+
- `sync_excludes`
|
|
123
|
+
- `pubify-mpl-template`
|
|
124
|
+
- `pubify-mpl-defaults`
|
|
125
|
+
|
|
126
|
+
`figures.py` is the publication entrypoint. It defines:
|
|
127
|
+
|
|
128
|
+
- loaders decorated with `@data(...)` or `@external_data(...)`
|
|
129
|
+
- plotters decorated with `@figure`
|
|
130
|
+
- stats decorated with `@stat`
|
|
131
|
+
- tables decorated with `@table`
|
|
132
|
+
|
|
133
|
+
## Typical Workflow
|
|
134
|
+
|
|
135
|
+
1. Keep publication-local TeX sources under `papers/<publication-id>/tex/`.
|
|
136
|
+
2. Define loaders, figure functions, stats, and tables in `figures.py`.
|
|
137
|
+
3. Run `pubs <publication-id> update` to refresh package-owned TeX support files, validate the publication definition, and regenerate figures, stats, and tables.
|
|
138
|
+
4. Run `pubs <publication-id> build` to validate and compile the publication.
|
|
139
|
+
5. Use `pubs <publication-id> preview` or `pubs <publication-id> figure <figure-id> preview` while iterating.
|
|
140
|
+
|
|
141
|
+
To scaffold starter entrypoints directly into `figures.py`:
|
|
142
|
+
|
|
143
|
+
- `pubs <publication-id> data add <data-id>`
|
|
144
|
+
- `pubs <publication-id> figure add <figure-id>`
|
|
145
|
+
- `pubs <publication-id> stat add <stat-id>`
|
|
146
|
+
- `pubs <publication-id> table add <table-id>`
|
|
147
|
+
|
|
148
|
+
## Figures, Tables, And Loaders
|
|
149
|
+
|
|
150
|
+
Prefer `@data(...)` for pinned publication-local inputs under the configured `data_root`. Use `@external_data(...)` only for explicit external roots declared in `pub.yaml`.
|
|
151
|
+
|
|
152
|
+
Host publications import from the extracted package namespace directly:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from pubify_pubs.data import load_publication_data_npz, publication_data_path, save_publication_data_npz
|
|
156
|
+
from pubify_pubs import TableResult
|
|
157
|
+
from pubify_pubs.decorators import data, external_data, figure, stat, table
|
|
158
|
+
from pubify_pubs.export import FigureExport, panel
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`@data(...)` and `@external_data(...)` both require relative paths. They reject absolute paths and path traversal.
|
|
162
|
+
|
|
163
|
+
`@figure` marks a callable as a logical publication figure. Exported figure functions typically return `FigureExport` values built from one or more panels.
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
return FigureExport(fig, layout="one")
|
|
167
|
+
return FigureExport([fig1, fig2], layout="two")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
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.
|
|
171
|
+
|
|
172
|
+
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:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
@figure
|
|
176
|
+
def custom_map(ctx):
|
|
177
|
+
with ctx.rc:
|
|
178
|
+
fig = build_custom_map()
|
|
179
|
+
return fig
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
For figure-specific cleanup that pubify still cannot discover generically, pass `prepare_export(...)` through `FigureExport(..., kwargs={...})`.
|
|
183
|
+
|
|
184
|
+
`@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.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
@table
|
|
188
|
+
def tabulate_summary(ctx):
|
|
189
|
+
return TableResult(
|
|
190
|
+
[
|
|
191
|
+
["Metric", "Value"],
|
|
192
|
+
["Count", 3],
|
|
193
|
+
["Mean", 2.00],
|
|
194
|
+
],
|
|
195
|
+
formats=["{}", "{:.2f}"],
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Column rendering is intentionally small:
|
|
200
|
+
|
|
201
|
+
- `formats[col]`
|
|
202
|
+
- `None`, `""`, or `"{}"` means `str(value)` then LaTeX-escape
|
|
203
|
+
- ordinary format strings like `"{:.2f}"` format then escape
|
|
204
|
+
- `"tex"` means the value itself is already TeX and is inserted raw
|
|
205
|
+
- `tex_wrappers[col]`
|
|
206
|
+
- wrap the formatted value into raw TeX using one `@` placeholder
|
|
207
|
+
- `multicolumns`
|
|
208
|
+
- enables compact horizontal merging without changing logical width
|
|
209
|
+
|
|
210
|
+
## Pinned Publication Data
|
|
211
|
+
|
|
212
|
+
`pubify-pubs` includes helpers for publication-owned binary data:
|
|
213
|
+
|
|
214
|
+
- `publication_data_path(...)`
|
|
215
|
+
- `save_publication_data_npz(...)`
|
|
216
|
+
- `load_publication_data_npz(...)`
|
|
217
|
+
|
|
218
|
+
`publication_data_path(...)` resolves paths under:
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
<data_root>/<publication-id>/...
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
It rejects absolute paths and `..`, and it creates parent directories automatically.
|
|
225
|
+
|
|
226
|
+
Format-specific helpers should generally come in save/load pairs when `pubify-pubs` owns the format handling.
|
|
227
|
+
|
|
228
|
+
## Generated Figures, Stats, Tables, And TeX Assets
|
|
229
|
+
|
|
230
|
+
`tex/autofigures/` is the framework-owned generated figure directory.
|
|
231
|
+
|
|
232
|
+
- generated figures from `figures.py` are exported there
|
|
233
|
+
- full `figure update` treats it as an authoritative snapshot and clears stale generated files first
|
|
234
|
+
- targeted `figure <figure-id> update` stays incremental
|
|
235
|
+
- TeX should reference generated figures explicitly by path such as `autofigures/<name>.pdf`
|
|
236
|
+
|
|
237
|
+
`tex/autostats.tex` is the framework-owned generated stats file.
|
|
238
|
+
|
|
239
|
+
- `stat update` rewrites it as one authoritative snapshot
|
|
240
|
+
- TeX should include it explicitly, for example with `\input{autostats.tex}`
|
|
241
|
+
- stats return either one value or a `dict[str, object]`
|
|
242
|
+
- generated stat macros are named `\Stat<StatId>` and `\Stat<StatId><Key>`
|
|
243
|
+
|
|
244
|
+
`tex/autotables.tex` is the framework-owned generated tables file.
|
|
245
|
+
|
|
246
|
+
- `table update` rewrites it as one authoritative snapshot
|
|
247
|
+
- `table <table-id> update` still rewrites the full snapshot after computing the selected table
|
|
248
|
+
- TeX should include it explicitly, for example with `\input{autotables.tex}`
|
|
249
|
+
- single-body tables emit `\Table<Id>`
|
|
250
|
+
- multi-body tables emit `\Table<Id>{1}`, `\Table<Id>{2}`, ...
|
|
251
|
+
- `update` and `build` validate logical table width against direct manuscript uses inside supported environments such as `tabular`, `tabularx`, and `longtable`
|
|
252
|
+
|
|
253
|
+
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/`.
|
|
254
|
+
|
|
255
|
+
## CLI
|
|
256
|
+
|
|
257
|
+
The installed command is `pubs`.
|
|
258
|
+
|
|
259
|
+
Top-level commands:
|
|
260
|
+
|
|
261
|
+
- `pubs list`
|
|
262
|
+
- `pubs init <publication-id>`
|
|
263
|
+
|
|
264
|
+
Publication commands:
|
|
265
|
+
|
|
266
|
+
- `pubs <publication-id> shell`
|
|
267
|
+
- `pubs <publication-id> data [list|add <data-id>]`
|
|
268
|
+
- `pubs <publication-id> figure [list|add <figure-id>|update|<figure-id> update|<figure-id> preview [<subfig-idx>]|<figure-id> latex [subcaption]]`
|
|
269
|
+
- `pubs <publication-id> stat [list|add <stat-id>|update|<stat-id> update|<stat-id> latex]`
|
|
270
|
+
- `pubs <publication-id> table [list|add <table-id>|update|<table-id> update|<table-id> latex]`
|
|
271
|
+
- `pubs <publication-id> update`
|
|
272
|
+
- `pubs <publication-id> build [--clear]`
|
|
273
|
+
- `pubs <publication-id> preview`
|
|
274
|
+
|
|
275
|
+
Optional advanced workflows:
|
|
276
|
+
|
|
277
|
+
`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.
|
|
278
|
+
|
|
279
|
+
`tables` is an alias for `table` in both the CLI and the publication shell.
|
|
280
|
+
|
|
281
|
+
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`.
|
|
282
|
+
|
|
283
|
+
## Development
|
|
284
|
+
|
|
285
|
+
Install the package in editable mode:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
pip install -e .
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Run the package tests:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
pytest
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Build the docs site:
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
mkdocs build --strict
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Development Approach
|
|
304
|
+
|
|
305
|
+
Keep publication-specific science code in host publications, not in this package.
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
MIT
|