typst-pyexec 0.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.
- typst_pyexec-0.0.0/.github/workflows/ci.yml +56 -0
- typst_pyexec-0.0.0/.github/workflows/release.yml +50 -0
- typst_pyexec-0.0.0/.gitignore +9 -0
- typst_pyexec-0.0.0/LICENSE +21 -0
- typst_pyexec-0.0.0/PKG-INFO +235 -0
- typst_pyexec-0.0.0/README.md +200 -0
- typst_pyexec-0.0.0/examples/demo.typ +102 -0
- typst_pyexec-0.0.0/examples/persistent_kernel.typ +42 -0
- typst_pyexec-0.0.0/examples/reactive.typ +37 -0
- typst_pyexec-0.0.0/examples/subplot_options.typ +31 -0
- typst_pyexec-0.0.0/pyproject.toml +74 -0
- typst_pyexec-0.0.0/tests/__init__.py +1 -0
- typst_pyexec-0.0.0/tests/test_builder.py +93 -0
- typst_pyexec-0.0.0/tests/test_cache.py +123 -0
- typst_pyexec-0.0.0/tests/test_dag.py +124 -0
- typst_pyexec-0.0.0/tests/test_execution.py +183 -0
- typst_pyexec-0.0.0/tests/test_kernel.py +25 -0
- typst_pyexec-0.0.0/tests/test_parser.py +96 -0
- typst_pyexec-0.0.0/tests/test_renderer.py +155 -0
- typst_pyexec-0.0.0/typst_pyexec/__init__.py +6 -0
- typst_pyexec-0.0.0/typst_pyexec/builder.py +218 -0
- typst_pyexec-0.0.0/typst_pyexec/cli.py +114 -0
- typst_pyexec-0.0.0/typst_pyexec/core/__init__.py +1 -0
- typst_pyexec-0.0.0/typst_pyexec/core/cache.py +110 -0
- typst_pyexec-0.0.0/typst_pyexec/core/dag.py +267 -0
- typst_pyexec-0.0.0/typst_pyexec/core/executor.py +572 -0
- typst_pyexec-0.0.0/typst_pyexec/core/kernel.py +241 -0
- typst_pyexec-0.0.0/typst_pyexec/core/parser.py +110 -0
- typst_pyexec-0.0.0/typst_pyexec/core/renderer.py +490 -0
- typst_pyexec-0.0.0/typst_pyexec/core/scheduler.py +80 -0
- typst_pyexec-0.0.0/typst_pyexec/runtime/__init__.py +1 -0
- typst_pyexec-0.0.0/typst_pyexec/runtime/capture.py +49 -0
- typst_pyexec-0.0.0/typst_pyexec/runtime/displayhook.py +39 -0
- typst_pyexec-0.0.0/typst_pyexec/utils/__init__.py +1 -0
- typst_pyexec-0.0.0/typst_pyexec/utils/hashing.py +39 -0
- typst_pyexec-0.0.0/typst_pyexec/utils/logging.py +39 -0
- typst_pyexec-0.0.0/uv.lock +2291 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- "**"
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test-and-quality:
|
|
11
|
+
name: ${{ matrix.os }} / py${{ matrix.python-version }}
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-latest, windows-latest]
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Setup Python
|
|
24
|
+
uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Setup uv
|
|
29
|
+
uses: astral-sh/setup-uv@v5
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync --extra dev
|
|
33
|
+
|
|
34
|
+
- name: Ruff lint
|
|
35
|
+
run: uv run ruff check .
|
|
36
|
+
|
|
37
|
+
- name: Black auto-format
|
|
38
|
+
run: uv run black typst_pyexec tests
|
|
39
|
+
|
|
40
|
+
- name: Black format check
|
|
41
|
+
run: uv run black --check typst_pyexec tests
|
|
42
|
+
|
|
43
|
+
- name: Mypy
|
|
44
|
+
run: uv run mypy typst_pyexec
|
|
45
|
+
|
|
46
|
+
- name: Pytest
|
|
47
|
+
env:
|
|
48
|
+
typst_pyexec_SKIP_KERNEL_TESTS: "1"
|
|
49
|
+
run: uv run pytest --cov=typst_pyexec --cov-report=term-missing --cov-report=xml
|
|
50
|
+
|
|
51
|
+
- name: Upload coverage XML
|
|
52
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
|
|
53
|
+
uses: actions/upload-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: coverage-xml
|
|
56
|
+
path: coverage.xml
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*.*.*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Setup Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.11"
|
|
21
|
+
|
|
22
|
+
- name: Setup uv
|
|
23
|
+
uses: astral-sh/setup-uv@v5
|
|
24
|
+
|
|
25
|
+
- name: update version
|
|
26
|
+
run: uv version "${GITHUB_REF_NAME#v}"
|
|
27
|
+
|
|
28
|
+
- name: Build package
|
|
29
|
+
run: uv build
|
|
30
|
+
|
|
31
|
+
- name: Upload distribution artifacts
|
|
32
|
+
uses: actions/upload-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: dist
|
|
35
|
+
path: dist/*
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
needs: build
|
|
40
|
+
permissions:
|
|
41
|
+
id-token: write
|
|
42
|
+
steps:
|
|
43
|
+
- name: Download distribution artifacts
|
|
44
|
+
uses: actions/download-artifact@v4
|
|
45
|
+
with:
|
|
46
|
+
name: dist
|
|
47
|
+
path: dist
|
|
48
|
+
|
|
49
|
+
- name: Publish to PyPI
|
|
50
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michel Jean Joseph Donnet
|
|
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,235 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: typst_pyexec
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Reactive Python execution engine for Typst documents
|
|
5
|
+
Author: typst_pyexec contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: jupyter,notebook,python,reactive,typst
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering
|
|
17
|
+
Classifier: Topic :: Text Processing :: Markup
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: black>=24.0
|
|
20
|
+
Requires-Dist: ipykernel>=6.0
|
|
21
|
+
Requires-Dist: joblib>=1.3
|
|
22
|
+
Requires-Dist: jupyter-client>=8.0
|
|
23
|
+
Requires-Dist: matplotlib>=3.7
|
|
24
|
+
Requires-Dist: nbformat>=5.9
|
|
25
|
+
Requires-Dist: pandas>=2.0
|
|
26
|
+
Requires-Dist: watchdog>=3.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-timeout>=2.1; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# typst_pyexec
|
|
37
|
+
|
|
38
|
+
typst_pyexec is a reactive Python execution engine for Typst documents.
|
|
39
|
+
|
|
40
|
+
It executes Python fences inside `.typ` files, captures outputs (stdout, figures, tables), and injects rendered Typst markup into an intermediate file (`*.typst_pyexec.typ`) that can be compiled directly.
|
|
41
|
+
|
|
42
|
+
## Core Capabilities
|
|
43
|
+
|
|
44
|
+
- Reactive dependency graph based on Python AST analysis
|
|
45
|
+
- Incremental execution with source-hash cache
|
|
46
|
+
- Persistent Jupyter kernel between builds
|
|
47
|
+
- Automatic cold-kernel hydration for dependency prerequisites
|
|
48
|
+
- Matplotlib figure export to SVG with PNG fallback
|
|
49
|
+
- Subfigure reconstruction via `figure(grid(...))`
|
|
50
|
+
- DataFrame HTML rendering to Typst `#table(...)`
|
|
51
|
+
- Watch mode with automatic rebuilds
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install typst_pyexec
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
For development:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uv sync --extra dev
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## CLI Usage
|
|
66
|
+
|
|
67
|
+
Build once:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
typst_pyexec build document.typ
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Watch and rebuild on save:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
typst_pyexec watch document.typ
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Clean local state directory:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
typst_pyexec clean
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Common options:
|
|
86
|
+
|
|
87
|
+
- `--output-dir <dir>`: write intermediate/state outputs to a custom folder
|
|
88
|
+
- `--no-cache`: disable cache lookups and force execution
|
|
89
|
+
- `--jobs <n>`: reserved for future multi-kernel scheduling (`-1` default)
|
|
90
|
+
- `--compiler <cmd>`: Typst compiler binary name/path (default `typst`)
|
|
91
|
+
|
|
92
|
+
## Block Options (`%|`)
|
|
93
|
+
|
|
94
|
+
Place options at the top of a Python fence:
|
|
95
|
+
|
|
96
|
+
````typst
|
|
97
|
+
```python
|
|
98
|
+
%| echo: false
|
|
99
|
+
%| raw: true
|
|
100
|
+
%| figure: true
|
|
101
|
+
print("hello")
|
|
102
|
+
```
|
|
103
|
+
````
|
|
104
|
+
|
|
105
|
+
Supported options:
|
|
106
|
+
|
|
107
|
+
- `execute` (default `true`): skip execution when `false`
|
|
108
|
+
- `refresh` (default `false`): force this cell to run every build
|
|
109
|
+
- `echo` (default `true`): show or hide source code
|
|
110
|
+
- `raw` (default `true`): show or hide textual runtime output (`stdout`, traceback, `text/plain` display bundles)
|
|
111
|
+
- `figure` (default `true`): show or hide rendered figures
|
|
112
|
+
- `caption`: explicit figure caption override
|
|
113
|
+
- `label`: Typst label for cross-references
|
|
114
|
+
- `keep-subplots` (default `false`): preserve multi-axis plot as one image
|
|
115
|
+
- `img-*`: passthrough kwargs for Typst `image(...)`
|
|
116
|
+
- `fig-*`: passthrough kwargs for Typst `figure(...)`
|
|
117
|
+
- `grid-*`: passthrough kwargs for Typst `grid(...)`
|
|
118
|
+
|
|
119
|
+
Behavior notes:
|
|
120
|
+
|
|
121
|
+
- `cell_id` is internal and generated automatically.
|
|
122
|
+
- Option-only edits do not invalidate cache because cache keys are based on Python source.
|
|
123
|
+
- To re-run on option updates, set `refresh: true`.
|
|
124
|
+
|
|
125
|
+
## Figure and Caption Behavior
|
|
126
|
+
|
|
127
|
+
- For single-axis plots, title text is promoted into Typst caption when `caption` is not set.
|
|
128
|
+
- For subplot grids, each axis title becomes child caption; suptitle becomes outer caption.
|
|
129
|
+
- Title text is removed from exported images after promotion to captions.
|
|
130
|
+
- If SVG export fails and PNG is emitted, renderer auto-resolves PNG paths in final Typst output.
|
|
131
|
+
|
|
132
|
+
## How It Works
|
|
133
|
+
|
|
134
|
+
1. Parse Python fences from Typst source
|
|
135
|
+
2. Build def/use DAG from AST
|
|
136
|
+
3. Compute changed cells from source-hash cache
|
|
137
|
+
4. Execute required cells in topological groups
|
|
138
|
+
5. Capture stdout, display bundles, and figure artifacts
|
|
139
|
+
6. Render Typst fragments per cell
|
|
140
|
+
7. Inject into `document.typst_pyexec.typ`
|
|
141
|
+
8. Compile with Typst compiler
|
|
142
|
+
|
|
143
|
+
## Local State
|
|
144
|
+
|
|
145
|
+
typst_pyexec writes runtime state into `.typst_pyexec/`:
|
|
146
|
+
|
|
147
|
+
- `kernel_connection.json`: reconnect data for persistent kernel
|
|
148
|
+
- `cache/`: JSON entries keyed by SHA-256 of cell source
|
|
149
|
+
- `figures/`: SVG/PNG artifacts
|
|
150
|
+
- `notebook.ipynb`: synchronized notebook representation
|
|
151
|
+
|
|
152
|
+
## Development Quality Gates
|
|
153
|
+
|
|
154
|
+
Run checks locally:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
uv run ruff check .
|
|
158
|
+
uv run black --check typst_pyexec tests
|
|
159
|
+
uv run mypy typst_pyexec
|
|
160
|
+
uv run pytest
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## CI/CD (GitHub Actions)
|
|
164
|
+
|
|
165
|
+
- `CI`: matrix on Ubuntu and Windows, Python 3.10-3.12, with lint + format + type-check + tests + coverage artifact
|
|
166
|
+
- `Release`: tag-driven (`v*.*.*`) build and publish workflow for PyPI using trusted publishing
|
|
167
|
+
|
|
168
|
+
Workflows are in:
|
|
169
|
+
|
|
170
|
+
- `.github/workflows/ci.yml`
|
|
171
|
+
- `.github/workflows/release.yml`
|
|
172
|
+
|
|
173
|
+
## Publish to PyPI
|
|
174
|
+
|
|
175
|
+
typst_pyexec is already configured for trusted publishing through GitHub Actions.
|
|
176
|
+
|
|
177
|
+
Release steps:
|
|
178
|
+
|
|
179
|
+
1. Bump version in `pyproject.toml` and `typst_pyexec/__init__.py`.
|
|
180
|
+
2. Commit and push to main.
|
|
181
|
+
3. Create and push a version tag:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
git tag v0.1.1
|
|
185
|
+
git push origin v0.1.1
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
4. GitHub Actions runs `.github/workflows/release.yml` and publishes to PyPI.
|
|
189
|
+
|
|
190
|
+
Pre-tag checklist:
|
|
191
|
+
|
|
192
|
+
1. `python -m pytest -q` is green.
|
|
193
|
+
2. `python -m build` succeeds.
|
|
194
|
+
3. `python -m twine check dist/*` passes.
|
|
195
|
+
4. Version matches in `pyproject.toml` and `typst_pyexec/__init__.py`.
|
|
196
|
+
5. Git tag matches that version (`vX.Y.Z`).
|
|
197
|
+
|
|
198
|
+
Local preflight checks before tagging:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
python -m build
|
|
202
|
+
python -m twine check dist/*
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
If you prefer token-based manual publishing:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
python -m twine upload dist/*
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Use in Other Repositories
|
|
212
|
+
|
|
213
|
+
After publishing, install as a normal dependency.
|
|
214
|
+
|
|
215
|
+
With pip:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
pip install typst_pyexec
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
With uv (project dependency):
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
uv add typst_pyexec
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
With uvx (run CLI without adding dependency):
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
uvx typst_pyexec build document.typ
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# typst_pyexec
|
|
2
|
+
|
|
3
|
+
typst_pyexec is a reactive Python execution engine for Typst documents.
|
|
4
|
+
|
|
5
|
+
It executes Python fences inside `.typ` files, captures outputs (stdout, figures, tables), and injects rendered Typst markup into an intermediate file (`*.typst_pyexec.typ`) that can be compiled directly.
|
|
6
|
+
|
|
7
|
+
## Core Capabilities
|
|
8
|
+
|
|
9
|
+
- Reactive dependency graph based on Python AST analysis
|
|
10
|
+
- Incremental execution with source-hash cache
|
|
11
|
+
- Persistent Jupyter kernel between builds
|
|
12
|
+
- Automatic cold-kernel hydration for dependency prerequisites
|
|
13
|
+
- Matplotlib figure export to SVG with PNG fallback
|
|
14
|
+
- Subfigure reconstruction via `figure(grid(...))`
|
|
15
|
+
- DataFrame HTML rendering to Typst `#table(...)`
|
|
16
|
+
- Watch mode with automatic rebuilds
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install typst_pyexec
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
For development:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv sync --extra dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## CLI Usage
|
|
31
|
+
|
|
32
|
+
Build once:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
typst_pyexec build document.typ
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Watch and rebuild on save:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
typst_pyexec watch document.typ
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Clean local state directory:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
typst_pyexec clean
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Common options:
|
|
51
|
+
|
|
52
|
+
- `--output-dir <dir>`: write intermediate/state outputs to a custom folder
|
|
53
|
+
- `--no-cache`: disable cache lookups and force execution
|
|
54
|
+
- `--jobs <n>`: reserved for future multi-kernel scheduling (`-1` default)
|
|
55
|
+
- `--compiler <cmd>`: Typst compiler binary name/path (default `typst`)
|
|
56
|
+
|
|
57
|
+
## Block Options (`%|`)
|
|
58
|
+
|
|
59
|
+
Place options at the top of a Python fence:
|
|
60
|
+
|
|
61
|
+
````typst
|
|
62
|
+
```python
|
|
63
|
+
%| echo: false
|
|
64
|
+
%| raw: true
|
|
65
|
+
%| figure: true
|
|
66
|
+
print("hello")
|
|
67
|
+
```
|
|
68
|
+
````
|
|
69
|
+
|
|
70
|
+
Supported options:
|
|
71
|
+
|
|
72
|
+
- `execute` (default `true`): skip execution when `false`
|
|
73
|
+
- `refresh` (default `false`): force this cell to run every build
|
|
74
|
+
- `echo` (default `true`): show or hide source code
|
|
75
|
+
- `raw` (default `true`): show or hide textual runtime output (`stdout`, traceback, `text/plain` display bundles)
|
|
76
|
+
- `figure` (default `true`): show or hide rendered figures
|
|
77
|
+
- `caption`: explicit figure caption override
|
|
78
|
+
- `label`: Typst label for cross-references
|
|
79
|
+
- `keep-subplots` (default `false`): preserve multi-axis plot as one image
|
|
80
|
+
- `img-*`: passthrough kwargs for Typst `image(...)`
|
|
81
|
+
- `fig-*`: passthrough kwargs for Typst `figure(...)`
|
|
82
|
+
- `grid-*`: passthrough kwargs for Typst `grid(...)`
|
|
83
|
+
|
|
84
|
+
Behavior notes:
|
|
85
|
+
|
|
86
|
+
- `cell_id` is internal and generated automatically.
|
|
87
|
+
- Option-only edits do not invalidate cache because cache keys are based on Python source.
|
|
88
|
+
- To re-run on option updates, set `refresh: true`.
|
|
89
|
+
|
|
90
|
+
## Figure and Caption Behavior
|
|
91
|
+
|
|
92
|
+
- For single-axis plots, title text is promoted into Typst caption when `caption` is not set.
|
|
93
|
+
- For subplot grids, each axis title becomes child caption; suptitle becomes outer caption.
|
|
94
|
+
- Title text is removed from exported images after promotion to captions.
|
|
95
|
+
- If SVG export fails and PNG is emitted, renderer auto-resolves PNG paths in final Typst output.
|
|
96
|
+
|
|
97
|
+
## How It Works
|
|
98
|
+
|
|
99
|
+
1. Parse Python fences from Typst source
|
|
100
|
+
2. Build def/use DAG from AST
|
|
101
|
+
3. Compute changed cells from source-hash cache
|
|
102
|
+
4. Execute required cells in topological groups
|
|
103
|
+
5. Capture stdout, display bundles, and figure artifacts
|
|
104
|
+
6. Render Typst fragments per cell
|
|
105
|
+
7. Inject into `document.typst_pyexec.typ`
|
|
106
|
+
8. Compile with Typst compiler
|
|
107
|
+
|
|
108
|
+
## Local State
|
|
109
|
+
|
|
110
|
+
typst_pyexec writes runtime state into `.typst_pyexec/`:
|
|
111
|
+
|
|
112
|
+
- `kernel_connection.json`: reconnect data for persistent kernel
|
|
113
|
+
- `cache/`: JSON entries keyed by SHA-256 of cell source
|
|
114
|
+
- `figures/`: SVG/PNG artifacts
|
|
115
|
+
- `notebook.ipynb`: synchronized notebook representation
|
|
116
|
+
|
|
117
|
+
## Development Quality Gates
|
|
118
|
+
|
|
119
|
+
Run checks locally:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
uv run ruff check .
|
|
123
|
+
uv run black --check typst_pyexec tests
|
|
124
|
+
uv run mypy typst_pyexec
|
|
125
|
+
uv run pytest
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## CI/CD (GitHub Actions)
|
|
129
|
+
|
|
130
|
+
- `CI`: matrix on Ubuntu and Windows, Python 3.10-3.12, with lint + format + type-check + tests + coverage artifact
|
|
131
|
+
- `Release`: tag-driven (`v*.*.*`) build and publish workflow for PyPI using trusted publishing
|
|
132
|
+
|
|
133
|
+
Workflows are in:
|
|
134
|
+
|
|
135
|
+
- `.github/workflows/ci.yml`
|
|
136
|
+
- `.github/workflows/release.yml`
|
|
137
|
+
|
|
138
|
+
## Publish to PyPI
|
|
139
|
+
|
|
140
|
+
typst_pyexec is already configured for trusted publishing through GitHub Actions.
|
|
141
|
+
|
|
142
|
+
Release steps:
|
|
143
|
+
|
|
144
|
+
1. Bump version in `pyproject.toml` and `typst_pyexec/__init__.py`.
|
|
145
|
+
2. Commit and push to main.
|
|
146
|
+
3. Create and push a version tag:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
git tag v0.1.1
|
|
150
|
+
git push origin v0.1.1
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
4. GitHub Actions runs `.github/workflows/release.yml` and publishes to PyPI.
|
|
154
|
+
|
|
155
|
+
Pre-tag checklist:
|
|
156
|
+
|
|
157
|
+
1. `python -m pytest -q` is green.
|
|
158
|
+
2. `python -m build` succeeds.
|
|
159
|
+
3. `python -m twine check dist/*` passes.
|
|
160
|
+
4. Version matches in `pyproject.toml` and `typst_pyexec/__init__.py`.
|
|
161
|
+
5. Git tag matches that version (`vX.Y.Z`).
|
|
162
|
+
|
|
163
|
+
Local preflight checks before tagging:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
python -m build
|
|
167
|
+
python -m twine check dist/*
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
If you prefer token-based manual publishing:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
python -m twine upload dist/*
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Use in Other Repositories
|
|
177
|
+
|
|
178
|
+
After publishing, install as a normal dependency.
|
|
179
|
+
|
|
180
|
+
With pip:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
pip install typst_pyexec
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
With uv (project dependency):
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
uv add typst_pyexec
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
With uvx (run CLI without adding dependency):
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
uvx typst_pyexec build document.typ
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
= typst_pyexec Example — Python Output, Plots and Tables
|
|
2
|
+
|
|
3
|
+
#set document(title: "typst_pyexec Demo", author: "typst_pyexec")
|
|
4
|
+
#set page(numbering: "1")
|
|
5
|
+
|
|
6
|
+
== Scalar and array output
|
|
7
|
+
|
|
8
|
+
```python
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
import matplotlib
|
|
12
|
+
matplotlib.use("Agg")
|
|
13
|
+
import matplotlib.pyplot as plt
|
|
14
|
+
print("Libraries loaded.")
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
arr = np.arange(10)
|
|
19
|
+
print(arr)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
== Reactive dependency demo
|
|
23
|
+
|
|
24
|
+
If you change `cell_scalar` below, only `cell_derived` will re-execute.
|
|
25
|
+
`cell_array` (no dependency on `x`) stays cached.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
x = 7
|
|
29
|
+
print(f"x = {x}")
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
y = x ** 2
|
|
34
|
+
print(f"y = x**2 = {y}")
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
== Simple plot
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
%| label: fig-trig
|
|
41
|
+
fig, ax = plt.subplots()
|
|
42
|
+
t = np.linspace(0, 2 * np.pi, 200)
|
|
43
|
+
ax.plot(t, np.sin(t), label="sin(t)")
|
|
44
|
+
ax.plot(t, np.cos(t), label="cos(t)")
|
|
45
|
+
ax.legend()
|
|
46
|
+
ax.set_title("Trigonometric functions")
|
|
47
|
+
plt.show()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
== Multi-panel figure (subfigures)
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
%| img-width: 100%
|
|
54
|
+
%| label: fig-multipanel
|
|
55
|
+
fig, axes = plt.subplots(1, 2, figsize=(8, 3))
|
|
56
|
+
axes[0].plot([1, 2, 3], [1, 4, 8])
|
|
57
|
+
axes[0].set_title("Quadratic")
|
|
58
|
+
axes[1].bar(["A", "B", "C"], [3, 1, 4])
|
|
59
|
+
axes[1].set_title("Bar chart")
|
|
60
|
+
plt.tight_layout()
|
|
61
|
+
plt.show()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
== Pandas DataFrame table
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
df = pd.DataFrame({
|
|
68
|
+
"Name": ["Alice", "Bo", "Carol"],
|
|
69
|
+
"Score": [95, 87, 92],
|
|
70
|
+
"Grade": ["A", "B+", "A-"],
|
|
71
|
+
})
|
|
72
|
+
df
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
== Adding a new cell — no recomputation of previous cells
|
|
76
|
+
|
|
77
|
+
The cell below is independent of all prior cells. If you add it
|
|
78
|
+
to an existing document, typst_pyexec will execute only *this* cell because
|
|
79
|
+
all previous cells are served from the SHA-256 cache.
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
msg = "I was added later and ran independently!"
|
|
83
|
+
print(msg)
|
|
84
|
+
print("Coucou")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
test = np.arange(5)
|
|
89
|
+
print("Coucou")
|
|
90
|
+
print(test)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
%| label: test
|
|
95
|
+
plt.figure()
|
|
96
|
+
plt.title("Gnyajaja")
|
|
97
|
+
plt.plot(np.arange(6), np.arange(6))
|
|
98
|
+
plt.show()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Here is some coucou in @test and in @fig-multipanel-a
|
|
102
|
+
The code is amazing because the rebuild is very fast
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
= Persistent Kernel Demo
|
|
2
|
+
|
|
3
|
+
Imports defined in an earlier cell are available in all later cells
|
|
4
|
+
because the Jupyter kernel's namespace is preserved across builds.
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
import numpy as np
|
|
8
|
+
import matplotlib
|
|
9
|
+
matplotlib.use("Agg")
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
print("numpy version:", np.__version__)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
# np is already in the kernel namespace from the `imports` cell.
|
|
16
|
+
result = np.sqrt(np.array([1, 4, 9, 16, 25]))
|
|
17
|
+
print("Square roots:", result)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
%| caption: Matplotlib reuses imported namespace
|
|
22
|
+
x = np.linspace(0, 10, 300)
|
|
23
|
+
plt.plot(x, np.exp(-0.3 * x) * np.sin(x))
|
|
24
|
+
plt.title("Damped oscillation")
|
|
25
|
+
plt.xlabel("t")
|
|
26
|
+
plt.ylabel("f(t)")
|
|
27
|
+
plt.show()
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
== Adding a new cell later
|
|
31
|
+
|
|
32
|
+
If you add the cell below *after* an initial build, typst_pyexec will:
|
|
33
|
+
|
|
34
|
+
1. Detect that the new cell is not in the cache.
|
|
35
|
+
2. Execute only the new cell (imports cell is cached — kernel already has `np`).
|
|
36
|
+
3. Re-use the live kernel namespace so `np` is available immediately.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
# This cell was added in a second build but imports still work.
|
|
40
|
+
arr = np.random.default_rng(0).integers(0, 100, size=5)
|
|
41
|
+
print("Random integers:", arr)
|
|
42
|
+
```
|