# serialize


<div class="boopcell boop-raw">

</div>

<div class="boopcell boop-note">

<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

</div>

<div class="boopcell boop-note">

## The `#| export` pragma

boopiter keeps nbdev’s `#| export` pragma out of a cell’s editable
source entirely, tracking it as a flag instead.
[`_has_export`](https://drscotthawley.github.io/boopiter/serialize.html#_has_export)/[`_strip_export`](https://drscotthawley.github.io/boopiter/serialize.html#_strip_export)
detect and remove that leading line when reading a cell from disk.

</div>

<div class="boopcell boop-note">

## Saving

[`save_notebook`](https://drscotthawley.github.io/boopiter/serialize.html#save_notebook)
writes `nb` out as a real Jupyter `.ipynb`, merging each Prompt +
following Assistant into one markdown cell (solveit separator + the
assistant’s `<details>` block + the reply).
[`_blocks_to_nb_outputs`](https://drscotthawley.github.io/boopiter/serialize.html#_blocks_to_nb_outputs)
converts boopiter’s output blocks into valid nbformat outputs so saved
files still render in Jupyter. Cell ids and the separator’s reply-id are
reused across saves to keep git diffs clean.

</div>

<div class="prose" data-markdown="1">

------------------------------------------------------------------------

<a
href="https://github.com/drscotthawley/boopiter/blob/main/boopiter/serialize.py#L92"
target="_blank" style="float:right; font-size:smaller">source</a>

### save_notebook

``` python
def save_notebook(
    path:str | pathlib.Path | None=None
)->Path:
```

*Serialize `nb.cells` to a real Jupyter notebook file (`{nb.name}.ipynb`
in the cwd by default). A Prompt cell and the Assistant reply that
follows it are stored solveit-style as a SINGLE markdown cell – prompt,
a `##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_... -->` line, the assistant’s
`<details>` block, then the reply – so the prompt/reply structure lives
in the cell source (which nbdev-clean leaves alone) rather than in
strippable metadata. note/code/raw stay plain native cells; code cells
keep their `#| export` pragma and outputs. Cell ids (and the separator’s
reply-id hex) are preserved across saves so unchanged cells don’t churn
git diffs. A `solveit_ai:true` flag is also stamped on prompt/reply
cells for solveit’s benefit – boopiter itself relies only on the
separator.*

</div>

<div class="boopcell boop-note">

## Loading

[`load_notebook`](https://drscotthawley.github.io/boopiter/serialize.html#load_notebook)
is the inverse: a markdown cell carrying the `SOLVEIT_SEPARATOR` splits
back into a Prompt + Assistant pair (re-extracting the `<details>`
block); plain markdown is a note; code cells get their `#| export` flag
detected. It falls back to legacy `metadata.boopiter` for notebooks
saved by older boopiter, and to nbformat cell types for non-boopiter
notebooks.

</div>

<div class="prose" data-markdown="1">

------------------------------------------------------------------------

<a
href="https://github.com/drscotthawley/boopiter/blob/main/boopiter/serialize.py#L131"
target="_blank" style="float:right; font-size:smaller">source</a>

### load_notebook

``` python
def load_notebook(
    path:str | pathlib.Path
)->Notebook:
```

*Load a Jupyter notebook file into `nb`, replacing its current contents
– the inverse of save_notebook(). A markdown cell containing the
`SOLVEIT_SEPARATOR` splits back into a Prompt cell plus its Assistant
reply (with the `<details>` block re-extracted into `details`); other
markdown is a note; code cells get their leading `#| export` detected
and stripped. For notebooks saved by older boopiter it falls back to
reading legacy `metadata.boopiter` (ctype/visible/details); for plain
non-boopiter notebooks it falls back to nbformat cell types.*

</div>

<div class="boopcell boop-note">

## Round-trip test

Proves the point of this whole scheme: save a notebook, wipe every
cell’s metadata (what `nbdev-clean` does), reload, and confirm the
prompt/reply structure – plus code/note/raw, `#| export`, and the
assistant `details` – all survive because they live in the cell source,
not metadata. Runs under `nbdev-test`.

</div>

``` python
# Round-trip test: the prompt/reply structure (and code/note/raw, export, details) must survive
# nbdev-clean, which strips cell metadata. We prove it by saving, deleting ALL metadata (exactly
# what nbdev-clean does to the parts we no longer rely on), then reloading and checking.
import tempfile as _tf, json as _json, os as _os
nb.cells.clear(); nb._nid = 0; nb.selected = None
nb.add('note', '# hi there')
nb.add('prompt', 'What is 2+2?')
nb.add('assistant', 'It is 4.', details='<details><summary>Reply details</summary><ul><li>Model: test</li></ul></details>')
nb.add('code', 'x = 4', export=True)
nb.add('raw', 'literal text')
_p = _tf.mktemp(suffix='.ipynb')
save_notebook(_p)
_doc = _json.load(open(_p))                       # simulate nbdev-clean: wipe all metadata
_doc['metadata'] = {}
for _c in _doc['cells']: _c['metadata'] = {}
_json.dump(_doc, open(_p, 'w'))
load_notebook(_p)
assert [c.ctype for c in nb.cells] == ['note','prompt','assistant','code','raw'], [c.ctype for c in nb.cells]
_a = nb.cells[2]
assert _a.ctype == 'assistant' and _a.details and 'Model: test' in _a.details
assert nb.cells[3].export and nb.cells[3].source == 'x = 4'
assert nb.cells[1].source == 'What is 2+2?' and nb.cells[1].ctype == 'prompt'
_os.remove(_p)
print('round-trip survived a full metadata strip:', [c.ctype for c in nb.cells])
```

    round-trip survived a full metadata strip: ['note', 'prompt', 'assistant', 'code', 'raw']
