A (really) minimal static website generator with python and jinja2

From time to time I throw together a static HTML site for something and love how easy it is to just upload the files to s3 and have a fast site with pretty much no cost or work involved.  For single-page sites in particular, this is pretty near painless, but as you start to get to a few different HTML pages, work starts repeating.

For larger projects (like GitNOC, or PedalWrencher), I love using Flask, and through that have gotten pretty proficient and pleased with Jinja2 for server-side templating.

So I found myself in this middleground, where I didn’t really need a Flask server for a simple static site, but HTML was getting a bit annoying to muck around with directly.  The natural fit for that is one of the many static site generators out there.  I’ve used at one point or another:

  1. Jekyll
  2. Pelican
  3. Lektor

All of which are great in their own rights, but in my case, I really only wanted to get the modularity afforded by Jinja2, and really didn’t need a blog or CMS or anything like that.  So I just wrote a minimal generator.  Heres how it works:

To start with, my directory is just a single python file: compiler.py and a single directory: src/.  In src, I put in a subdirectory, static, with all of my images, CSS, and javascript (if I have any), and then I put my jinja templates in src.

Compiler looks like:

from jinja2 import Environment
import os
import shutil
from jinja2 import BaseLoader, TemplateNotFound
from os.path import join, exists, getmtime


class Loader(BaseLoader):
    def __init__(self, path):
        self.path = path

    def get_source(self, environment, template):
        path = join(self.path, template)
        if not exists(path):
            raise TemplateNotFound(template)
        mtime = getmtime(path)
        with open(path, 'r') as f:
            source = f.read()
        return source, path, lambda: mtime == getmtime(path)


class SourceBundle(object):
    def __init__(self, static_dir='static', src_dir='src', templates=None, dist_dir='dist'):
        if templates is None:
            self.templates = ['index.html']
        else:
            self.templates = templates

        self.static_dir = static_dir
        self.src_dir = src_dir
        self.dist_dir = dist_dir

    def _root(self):
        return os.path.dirname(os.path.abspath(__file__))

    def clean(self):
        if os.path.exists(self.dist_dir):
            shutil.rmtree(self.dist_dir)

    def build(self, **kwargs):
        if not os.path.exists(os.path.join(self._root(), self.dist_dir)):
            os.mkdir(os.path.join(self._root(), self.dist_dir))

        if self.static_dir is not None:
            if os.path.exists(os.path.join(self._root(), self.dist_dir, self.static_dir)):
                shutil.rmtree(os.path.join(self._root(), self.dist_dir, self.static_dir))
            shutil.copytree(
                os.path.join(self._root(), self.src_dir, self.static_dir),
                os.path.join(self._root(), self.dist_dir, self.static_dir)
            )

        env = Environment(loader=Loader(os.path.join(self._root(), self.src_dir)))
        for template in self.templates:
            with open(os.path.join(self._root(), self.dist_dir, template), 'w') as f2:
                slug = env.get_template(template)
                slug = slug.render(**kwargs)
                f2.write(slug)

        return True

if __name__ == '__main__':
    sb = SourceBundle(templates=[
        'index.html'
    ])
    sb.build()

It’s pretty simple, it just walks the src directory, copies/builds the various files, and dumps them in dist.  That directory can then be copied directly to S3 and you’re good to go. Easy.

The post A (really) minimal static website generator with python and jinja2 appeared first on Will’s Noise.