Skip to content

a result a notebook can read - #17

Merged
tamnd merged 1 commit into
mainfrom
notebooks
Aug 18, 2026
Merged

tamnd merged 1 commit into
mainfrom
notebooks

Conversation

@tamnd

@tamnd tamnd commented Aug 18, 2026

Copy link
Copy Markdown
Owner

A result printed in a Jupyter cell was a line saying how many rows it had. The rows are already in memory, so this draws them: _repr_html_ on Result, and on Node, Rel and Path so a graph value looks the same on its own as it does inside a cell.

The markup is a table and a stylesheet and no script, so it survives nbconvert, an exported HTML file and a notebook diff, and there is nothing to install for it to work. Colours come from the notebook itself through currentColor and opacity, because a light theme and a dark one are both in the room and a guess about which would be wrong half the time. Classes rather than a style attribute on every cell, since a hundred rows of ten columns is a thousand cells.

A hundred rows is the cut and the note underneath says how many rows there really were, because a million rows of markup is a notebook file that will not open again. Two hundred characters is the cut on a single cell, so a column holding a document does not become a page holding one. Values and column names are escaped, since a string column holding a tag is a string column and pasting one in unescaped would run a caller's data as code in their notebook. Drawing a result does not move its cursor: a person who looked at a result has not read it.

zudb.magic goes with it. %gql <path> opens a connection, --read-only opens it that way, --close shuts it, and the bare form says which one is open. %%gql runs the cell on it, with --conn to say which of several connections, --params to name a dict in the namespace, and --out to put the result in a variable instead of showing it. A notebook that already called zudb.connect needs no %gql: a namespace with one connection in it is found, and a namespace with two says which names it saw. The magic is a way in and not a layer, so the statement goes straight to execute and the engine's exception arrives as the engine's exception.

IPython is a dev dependency, not a wheel dependency. The magic tests skip without it, the way the pyarrow and pandas ones do, and they run against a real InteractiveShell through %load_ext rather than a mock, so what they check is what a notebook does.

36 new tests, 16 on the markup and 20 on the magics. Local gate is green: ruff, cargo fmt, clippy, a release build and the full suite at 528 passed.

Jupyter asks an object for _repr_html_ before it falls back to repr, so
a result printed in a cell is a line saying how many rows it has. The
rows are already in memory and a table of them is a strictly better
answer, so this draws one, and draws nodes, edges and walks the same way
whether they turn up on their own or inside a cell.

The markup is a table and a stylesheet and no script, which means it
survives nbconvert, an exported HTML file and a notebook diff, and there
is nothing to install for it to work. Colours come from the notebook
through currentColor and opacity, because a light theme and a dark one
are both in the room and a guess about which would be wrong half the
time. A hundred rows is the cut, since a million rows of markup is a
notebook file nobody can open again, and the note underneath says how
many rows there really were. Two hundred characters is the cut on a
cell, so a column holding a document does not become a page holding one.
Values are escaped, because a string column holding a tag is a string
column and pasting one into the page unescaped would run a caller's
data as code in their notebook.

zudb.magic goes with it: %gql opens a connection and %%gql runs a
statement on it, with --conn to say which of several, --params to name a
dict, and --out to put the result in a variable instead of showing it. A
notebook that already called zudb.connect needs no %gql at all, since a
cell with one connection in the namespace finds it, and a cell with two
says which names it saw. The magic is a way in and not a layer: the
statement goes straight to execute and the engine's exception arrives as
the engine's exception.

IPython is a dev dependency and not a wheel dependency, and the tests
skip without it the way the pyarrow and pandas ones do. They run against
a real InteractiveShell with %load_ext rather than a mock, so what is
checked is what a notebook does.
@tamnd
tamnd merged commit cc7521a into main Aug 18, 2026
28 of 30 checks passed
@tamnd
tamnd deleted the notebooks branch August 18, 2026 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant