doit
integration - a mkdocs
example¤
in the hunt for a computable blog, i've wanted to be able to reuse the code i write about in posts.
one integration we are beginning to experiment with in this post is access to doit
task specifications
from posts. the tonyfast.dodo
module integrates posts with tasks.
doit
is a great tool for orchestrating commands that operate on files.
to demonstrate the integration we'll compose a task to build a mkdocs
site,
and deal with some pain points in the configuration.
the mkdocs
task¤
mkdocs
integrations are identified when a project has the mkdocs.yml
file.
this file contains all the information for your site including the documents to build.
for blog style content, adding a new post means updating the configuration file.
my adhd brain has a tendency to forget the configuration bit.
in this file, we wrote tools to infer the files that should be included in the configuration,
we sort them, and place them back in the configuration file.
what follow is the incovation for mkdocs
for the tonyfast
site.
in this document we:
- define functions to find posts
- define a
doit
task to executemkdocs
> there is a mkdocs-material
blog feature avaiable to insiders. when this is generally available a lot of this content will be moot.
the mkdocs
doit
task¤
primarily doit
tasks are functions that begin with the prefix task_
;
task_mkdocs
is the one task we define in this document.
find posts and build the mkdocs site
def task_mkdocs():
yield dict(name="toc", actions=[
(set_nav, (pathlib.Path("tonyfast"), "*.ipynb", "mkdocs.yml"))
], targets=["mkdocs.yml"], uptodate=[False])
yield dict(
name="build",
actions=["mkdocs build"],
file_dep=["mkdocs.yml"],
targets=["site/index.html"]
)
finding and sorting the posts¤
find all the potential notebook posts
def get_posts_from_dir(dir, glob="*.ipynb"):
for file in dir.iterdir():
if file.is_dir():
if "checkpoints" not in file.name: yield from get_posts_from_dir(file, glob)
elif file.suffix in {".ipynb"}:
if (m := title.match(str(file.stem))): yield file, m
def get_posts(dir, glob="*.ipynb"):
return sorted((
(x.relative_to(WHERE), y) for x, y in get_posts_from_dir(dir, glob)
), reverse=True, key=lambda x: x[1].group(*"ymd"))
format the ordered entries
def get_toc(dir, glob="*.ipynb"):
for x, y in list(get_posts(WHERE, glob)):
yield " "* 4 + F"- {x}"
we don't use yaml
cause our mkdocs
uses yaml
tags.
instead we put the nav at the end of the document
then replace the default with the updated version.
replace the nav¤
def get_nav(dir, glob="*.ipynb"):
return "".join(re.split(
"(notes\S*:\S*)",
(WHERE.parent / "mkdocs.yml").read_text(),
1
)[:2] )+ "\n" + "\n".join(get_toc(dir, glob))
import tonyfast, pathlib, yaml, re
WHERE = pathlib.Path(tonyfast.__file__).parent
title = re.compile("(?P<y>[0-9]{4})-(?P<m>[0-9]{1,2})-(?P<d>[0-9]{1,2})-(?P<t>.+)")
def set_nav(dir, glob="*.ipynb", target=WHERE.parent / "mkdocs.yml"):
pathlib.Path(target).write_text(get_nav(pathlib.Path(dir), glob))
invocation¤
-
the task is exposed in the
tonyfast
modulepython -m tonyfast tasks mkdocs
-
this command is invoked with
hatch
in a virtual environment using:hatch run docs:build
-
use with
importnb -t
if "__file__" not in locals():
!python -m importnb -t 2022-12-18-mkdocs-task.ipynb info mkdocs