Code blocks (part 1)
Overview
Franklin supports executing Julia code blocks and using or showing their output. Here is a basic example:
At a high level, this both shows the code on the webpage (as illustrated above)
and also uses the running Julia session to execute the code so that the output
can be shown somewhere else via the \show
command:
The orange box helps indicate what corresponds to the output of an executed code cell.
This is just the styling we use on the present site though, you can of course control this
yourself by styling the class .code-output
or one of the more specific sub-classes
(see the section on code output).
The syntax
```julia:name
...
```
identifies a fenced code block as "executable" and assigns a name 'name
' which
allows to refer specifically to the output of that code block.
The command \show{name}
then displays stdout
, stderr
and the result of
the code block if it's not nothing
.
Syntax for executable code blocks
Beyond the syntax introduced briefly above, there are several other ways of indicating that a fenced code block should be executed:
```
julia:name
— the language and the name of the block are explicit,```
:name
— the language is implicit, the name of the block is explicit,```
:
— the language and the name are implicit.
For all these you can swap the colon (:
) for an exclamation mark (!
) with the same
effect (see examples below).
When the language is implicit, it is taken from the page variable lang
(with default
"julia"
).
When the name is implicit, a name is automatically generated and the output is placed
directly below the code.
Here's an example with implicit language and explicit name:
And here's an example with implicit language and implicit name (the output is shown automatically below the code):
Unless you intend to show the code output somewhere else than below the code block, or use a custom method to show the code output, this last syntax (where everything is implicit) is likely the one you will want to use most often.
Hiding lines of code
In some cases an executable code cell might need some lines of code to work which you don't
want to show.
You can indicate that a line should be hidden by adding #hide
to it (letter case and whitespace
don't matter).
If you want to hide an entire code cell (e.g. you're just interested in the output)
you can put #hideall
in the code.
Mock lines
In some cases you might want to show a line of code without it being run. The example below should make this clearer:
data = fetch("./tests/data.txt") # <-- depends on your setup
data = fetch("/user/$name/api") # <-- what the user should do
plot(data)
In this example, you'd want to run but hide the first line and not run but show the second
line.
You already know #hide
, the other one is #mock
(also allowed: #fake
, #noexec
, #norun
).
Providing name hints
For auto-named code blocks (which you likely will want to use most of the time), you can provide a name hint which will help identify what is being evaluated in the REPL output when you build your site.
So for instance if you just write
when the page gets built, something like
evaluating cell auto_cell_5...
will be shown in the REPL.
But it can sometimes be useful to know precisely which block is being run and you can do
this by adding a "# name: name hint
" line in your code:
then, when the page gets built, something like
evaluating cell auto_cell_5 (printing example)...
will be shown in the REPL.
Marking a cell as independent
If a code block is fully independent of any other code blocks on the page, and of the Utils
module, you can mark it as independent (with # indep
), this will allow Franklin to skip re-evaluating this block when other code blocks around it change.
To clarify this, imagine you have a page with two blocks A and B.
By default if A is modified, B will be re-evaluated because Franklin considers that B might
depend on some things that A defines.
However, if you know that B is fully independent code, you can mark it as such and then when A changes, B will not get re-evaluated.
When marking a block as independent, the user guarantees to Franklin that:
- the code does not depend on any other code block on the page,
- no other code block on the page depends on that code block.
The marker for independence should be placed at the top of your code:
REPL mode
Instead of !
or :
above, you can also use one of >
, ;
, ]
and ?
to
mimick the corresponding REPL mode.
In the case of the ?
one, only a single line of input is allowed (other lines,
if provided, will be ignored).
REPL common mode
With the >
, you indicate that the code cell should be split in the same way
as it would in the REPL:
REPL shell mode
With the ;
, you indicate that the code cell should be executed as in the
REPL shell mode:
REPL pkg mode
With the ]
, you indicate that the code cell should be executed as in the
REPL pkg mode, note that this will affect the environment the subsequent cells
are run in (only on that page, it won't affect the other pages which will
keep being run in the website environment unless otherwise specified):
REPL help mode
With the ?
, you indicate that the code cell should be executed as in the
REPL help mode. Note that only the first line of the cell will be considered
as the output will be displayed in a separate div
with class repl-help
(which you should style).
The basic styling used here is:
Understanding how things work
Each page can be seen as one "notebook".
All executed code blocks on the page are executed in one sandbox module
attached to that page and share the same scope.
That sandbox module also loads the content of utils.jl
as a Utils
module
and so all objects defined there can be accessed in any code block via
Utils.$name
(see also the page on Utils).
To illustrate the scoping, consider these two consecutive cells:
When a code block is executed, the code string as well as the output strings are
cached.
This allows for code blocks to not be systematically re-executed if they don't need to be.
The cached representation can however be considered stale in which case the code block
will be re-evaluated as soon as the page changes.
This can be mitigated with the use of # indep
as mentioned above.
When adding or modifying a code block, every code block below that one are considered stale,
and so will be re-executed.
Since Utils
is loaded into the sandbox module attached to a page, if utils.jl
changes,
the entirety of each page's "notebook" will be marked as stale and will be re-run to guarantee
that it uses the latest definitions from Utils
(even if it doesn't use Utils
at all).
In that sense changing utils.jl
amounts to clearing the entire cache and re-building
the website (see also the page discussing Utils).
What to do when things go wrong
While hopefully this shouldn't happen too often, two things can go wrong:
- some code fails in an un-expected way (e.g.: it calls objects which it should have access to but doesn't seem to),
- the output of an auto-cell is incorrect (either wasn't refreshed or some other random output that you didn't expect).
In both cases, you can first try to force re-execute the cell and, if that fails, you can try clearing the cache:
- to force re-execute a cell, just double the execution marker (e.g.
!!
or::
), - to clear the page cache, interrupt the server, add the page variable
ignore_cache = true
and re-start the server, - to clear the entire site cache, interrupt the server and restart it passing the
argument
clear = true
toserve(...)
.
In the first case, that cell along with all subsequent cells will be re-evaluated. In the second case, only the cache associated with the current page will be ignored and only that page will be (completely) re-evaluated. In the last case, the whole site is re-built from scratch.
In any case, if you can reproduce what led to the problem, and you think it could be addressed on the Franklin side, kindly open an issue on GitHub.
Output of executable code blocks
When evaluating a code block, Franklin captures stdout
, stderr
and, if the code
doesn't fail, the result of the execution.
When using the command \show
(or automatically if you use implicit naming), the output
is placed in the following HTML:
<div class="code-output">
<!-- If stdout is not empty -->
<pre><code clas="code-stdout language-plaintext">
...captured stdout...
</code></pre>
<!-- if stderr is not empty -->
<pre><code class="code-stderr language-plaintext">
...captured stderr...
</code></pre>
<!-- if result is not nothing -->
...appropriate representation of the result...
</div>
If the code block didn't fail, the appropriate representation of the result is obtained by considering the following cases in order:
- the result is
nothing
, nothing gets shown (no string), - there is a custom
html_show
function fortypeof(result)
that is defined in yourUtils
, the string returned by the call to that function is then added, - the object has a
Base.show
method forMIME"image/svg+xml"
orMIME"image/png"
, the image is automatically saved to an appropriate location and shown (with priority to SVG output), - the object has a
Base.show
method forMIME"text/html"
, it gets called and the HTML gets shown, - the object has a
Base.show
method forMIME"text/plain"
, it gets called and shown in a<pre><code class="code-result language-plaintext">...</code></pre>
block, - otherwise, the fallback
Base.show
is called and shown as in the previous point in a<pre><code...
block.
Note that you can always suppress the display of a code block result by
adding a final ';
' to the code.
These different cases are illustrated further below.
You can also overwrite the \show
command by defining a custom
lx_show
function in your utils (see how to define latex commands) if you
want to handle the output in your own way (or define your own alternative command that does it).
Nothing to show
Nothing will be shown beyond stdout
if
- the last command in the code block is a
@show
or returnsnothing
- the last command in the code block is followed by '
;
'
A few examples follow:
Default show
For a result that does not have a custom show, is not showable as an image, or HTML,
Base.show
will be applied with a result similar to what you would get in the Julia REPL:
One exception to this is if the result has a dedicated show method for MIME"text/plain"
in which
case that is what will be called and shown.
Showable as SVG or PNG
If the result is showable as SVG or PNG then a relevant file (.svg
or .png
) is
generated and the image is inserted with
<img class="code-figure" src="...generated_path_to_img...">
For instance:
If you inspect the HTML, you will see that the image displayed corresponds to a generated path that looks like
/assets/syntax/code/figs-html/__autofig_1682931969501726440.svg
The generated path is built as
/assets/[relative-path]/figs-html/[gen]
where relative-path
is the relative path to the page with the code and
gen
is built out of the hash of the code that generated the image:
hash("""
using Colors
colorant"cornflowerblue"
""" |> strip
) |> string
"1682931969501726440"
Here's another example with Gaston (and you could use any other plotting library such as Plots, PlotlyJS, etc., though you'll have to be careful about setting dependencies properly. Check out the page dedicated to plots with Franklin for more informations.
using Gaston
set(term="svg")
x = range(0, pi, length=350)
z = 0.2 * randn(length(x))
y = @. sin(exp(x)) * sinc(x) + z
plot(x, y)
HTML show
Some packages define objects which indicate how to show objects with a MIME"text/html"
,
in that case the corresponding show
method is called and the HTML shown.
This is for instance the case with DataFrame objects:
using DataFrames
df = DataFrame(
(
names = ["Vlad", "Martha", "Hasan", "Carl"],
age = [50, 34, 23, 42],
gender = ["M", "F", "M", "M"]
)
)
Row | names | age | gender |
---|---|---|---|
String | Int64 | String | |
1 | Vlad | 50 | M |
2 | Martha | 34 | F |
3 | Hasan | 23 | M |
4 | Carl | 42 | M |
Custom show
If you have defined a custom html_show(r)
in your Utils
that accepts an object of the type
of the result of a code block and returns a string, then that will be used to represent the
result on the page.
For instance in the utils.jl
for the present website, we've defined
struct Foo
x::Int
end
html_show(f::Foo) = "<strong>Foo: $(f.x)</strong>"
We can use the type Foo
by indicating it is defined in Utils
and the custom show method will be used:
Showing the output as Markdown/HTML
In some cases it can be convenient to use a code block to generate some markdown (resp. HTML).
One way is to define a utils function but you can also use the \mdshow
(resp. \htmlshow
) command
which interprets the output of stdout
and the string representation of the result as Markdown.
Here's a simple illustration:
Here's another one with \htmlshow
:
What if the code errors?
If there's an error in the code, no result will be shown and stderr
will
capture a trimmed stacktrace of the problem which will be displayed:
In what path does the code run?
In the same path as where serve
was called.
And since you can call serve()
from within the site folder or from elsewhere specifying
serve("path/to/folder")
, this path can vary.
As a consequence, if you want some code to do something with a path (e.g. read or write a file),
you should use Utils.path(:folder)
as the base path pointing to your website folder.
You can also use Utils.path(:site)
as the base path pointing to the website build folder.
For instance let's say you want to save a DataFrame to a CSV that you can link to as an asset on your site:
using DataFrames, CSV
build_dir = Utils.path(:site)
target_dir = mkpath(joinpath(build_dir, "assets", "data"))
df = DataFrame(A=1:4, B=["M", "F", "F", "M"])
CSV.write(joinpath(target_dir, "data1.csv"), df);
this outputs nothing but it does save data1.csv
in the build folder at location
/assets/data/
so that you could then link to it explicitly:
Another example is that you might want to write a file in the build dir:
build_dir = Utils.path(:site)
open(joinpath(build_dir, "405.html"), "w") do f
write(f, """
Dummy page 405, <a href="/">return home</a>
"""
)
end;
The above code block writes a dummy HTLM page 405.html
in the build folder.
You can actually see it here.