@nb_extract(source_path, template)
Create a function out of a Pluto
notebook,
based on a template
.
The source notebook path is given as source_path
.
The signature of the returned function is exactly the one of the template
.
The arguments take precedence over the values defined in the notebook.
The notebook topology and the template are then analyzed
to find the cells needed to evaluate the template body
(the cells that define b
and c
in the example below).
Note: the template body ends up at the end of the function.
The result of the macro is this fleshed-out function, just as if it had been typed by hand. Preliminary benchmarks show no runtime overhead.
Remark: @nb_extract
can be used not only from a running Pluto notebook,
but from anywhere else (a script, the REPL, ...).
Say in the source notebook there are three cells: a = 1
, b = 2a
, c = 2b
,
here is how to make a function that return the value c
from any given a
:
julia> using PlutoExtractors
julia> source_path = pkgdir(PlutoExtractors,
"test", "notebooks", "source_basic.jl"
) # to be replaced with the path of your source notebook
julia> @nb_extract(
source_path,
function fun(a)
return c
end
)
julia> fun(2)
8
More examples can be found in the test/notebooks/extract_from_*.jl
Pluto notebooks.
The destination notebook packages are always used.
So the best experience should be expected with running notebooks within the “shared environment” pattern,
e.g. with Pkg.activate(Base.current_project())
.
This ensures that both source and destination share exactly the same packages versions.
In principle @nb_extract
may be used with the standard Pluto package system, but then
- The necessary
using
statements must be copied manually to the destination notebook (otherwise they would be missing from the notebook project) - Something that used to work in the source might fail in the destination just because package versions differ.
This package is in alpha stage. It works well here, but no doubt bugs will come up when used by others, please file issues !
The source notebook is analyzed to find all the using
and import
statements.
A module is created with all these statements.
This module has enough information to evaluate and hold
a fully updated topology of the source notebook (including macroexpansions).
This is the "topology module".
For flexibility another module is created (the "function module"), to hold the function and all the necessary code. Thanks to the full topology created above, the needed cells are found, and split in two groups:
- If the cell depends on any template argument, then its code goes inside the function. This code will run upon every function call.
- Otherwise its code is needed but kind of static, so it goes to the "function module" toplevel.
(with some
const
prepended for performance, where relevant). This code runs only once.
In the caller scope a wrapper with exactly the template signature is evaluated, that calls the function defined in the "function module".
Most of the work is done by Pluto.jl for the notebook loading, as well as ExpressionExplorer.jl, PlutoDependencyExplorer.jl and MacroTools.jl for the analysis.