To launch a tutorial, click on the 🚀 button below! Join us on Slack!

Jupyter notebooks

Jupyter notebooks#

jupyblog can turn your Jupyter notebooks into markdown (.md) files with embedded outputs. It supports plots and HTML outputs.

Installation#

%pip install jupyblog --quiet
Note: you may need to restart the kernel to use updated packages.

Example#

Let’s run an example, we’ll download a configuration file (jupyblog.yaml) and a sample post:

from pathlib import Path
import urllib.request

# create folder to store posts
path = Path("posts")
path.mkdir(exist_ok=True)

# folder to store a specific post
path_to_post = path / "my-jupyter-post"
path_to_post.mkdir(exist_ok=True)

# config file
urllib.request.urlretrieve(
    "https://raw.githubusercontent.com/ploomber/jupyblog/master/examples/quick-start-jupyter/jupyblog.yaml",
    path / "jupyblog.yaml",
)

# download post
_ = urllib.request.urlretrieve(
    "https://raw.githubusercontent.com/ploomber/jupyblog/master/examples/quick-start-jupyter/my-post/post.ipynb",
    path_to_post / "post.ipynb",
)

We stored everything in a posts/ directory, this is the structure that jupyblog expectds: a directory with a jupyblog.yaml configuration file and one directory per post:

%ls posts/
jupyblog.yaml  my-jupyter-post/

The configuration file sets a few settings:

  • path_to_posts: Where to store the rendered posts (path is relative to the posts/ directory

  • path_to_static: Where to store any images referenced in the post (path is relative to the posts/ directory

  • prefix_img: A prefix that will be applied to all image paths (e.g., ![img](path.png) becomes ![img](/images/blog/path.png))

These settings will depend on our blog structure configuration, these values are examples.

print(Path("posts/jupyblog.yaml").read_text())
path_to_posts: content/posts
path_to_static: static/images
prefix_img: /images/blog

Posts are organized in folders. Inside each folder we have a post file (post.ipynb in this case) and any associated images. This means that you can reference your images with relative paths (e.g., ![img](path/to/image.png)) so you can preview them with any Markdown editor.

%%sh
ls posts/my-jupyter-post/
post.ipynb

The only requirement for the notebook is to have a raw cell at the top with the following format:

---
title: My post
jupyblog:
    execute_code: false
description: Some post description
---

Title is the title of the post, the jupyblog section can be copied as-is, and description is the blog post description (a one-sentence summary)

Now, we use jupyblog to create our post:

%%sh
cd posts/my-jupyter-post
jupyblog render
Input: /home/docs/checkouts/readthedocs.org/user_builds/jupyblog/checkouts/latest/doc/user-guide/posts/my-jupyter-post
Processing post "my-jupyter-post"
Post will be saved to /home/docs/checkouts/readthedocs.org/user_builds/jupyblog/checkouts/latest/doc/user-guide/posts/content/posts
Rendering markdown...
Making img links absolute and adding canonical name as prefix...
Output: /home/docs/checkouts/readthedocs.org/user_builds/jupyblog/checkouts/latest/doc/user-guide/posts/content/posts/my-jupyter-post.md

In our configuration file (jupyblog.yaml), we said we wanted to store our rendered posts in the content/posts/ directory, let’s look at it:

%ls posts/content/posts/
my-jupyter-post.md

We see that it contains a file our rendered post, let’s look at its content. You’ll see that it’s the same content as your notebook, except it contains new code fences with the outputs of each cell:

print(Path("posts/content/posts/my-jupyter-post.md").read_text())
---
description: Some post description
jupyblog:
  execute_code: false
  version_jupysql: 0.0.15dev
title: My post
---

## My section

This sentence is some description:

```python
x = 21
y = 2

result = x * y
print(f"Result is: {result}")
```

<!-- #region -->


**Console output (1/1):**

```txt
Result is: 42
```

<!-- #endregion -->

Let's show a second snippet:


```python
x = 1
y = 41

result = x + y
print(f"Result is: {result}")
```

<!-- #region -->


**Console output (1/1):**

```txt
Result is: 42
```

<!-- #endregion -->
# remove example directory
import shutil

shutil.rmtree("posts")