#!/usr/bin/env python # -*- coding: utf-8 - # # This file is part of gunicorn released under the MIT license. # See the NOTICE for more information. from __future__ import with_statement import codecs import datetime import inspect import os import subprocess as sp import sys from docutils.core import publish_parts from jinja2 import Environment from jinja2.loaders import FileSystemLoader from jinja2.utils import open_if_exists from sitemap_gen import CreateSitemapFromFile import conf class Site(object): def __init__(self): self.url = conf.SITE_URL.rstrip('/') fs_loader = FileSystemLoader(conf.TEMPLATES_PATH, encoding="utf-8") self.env = Environment(loader=fs_loader) self.env.charset = 'utf-8' self.env.filters['rel_url'] = self.rel_url def rel_url(self, value): return value.split(self.url)[1] def render(self): for curr_path, dirs, files in os.walk(conf.INPUT_PATH): tgt_path = curr_path.replace(conf.INPUT_PATH, conf.OUTPUT_PATH) if not os.path.isdir(tgt_path): os.makedirs(tgt_path) self.process(files, curr_path, tgt_path) self.sass_compile() def process(self, files, curr_path, tgt_path): for f in files: page = Page(self, f, curr_path, tgt_path) if not page.needed(): continue print "Page: %s" % page.source page.write() def sass_compile(self): print "" print "Updating css..." try: sp.check_call(["compass", "compile", "--boring"]) except sp.CalledProcessError: print "Failed to update CSS" def get_template(self, name): return self.env.get_template(name) class Page(object): def __init__(self, site, filename, curr_path, tgt_path): self.site = site self.filename = filename self.source = os.path.join(curr_path, filename) self.headers = {} self.body = "" with open(self.source, 'Ur') as handle: raw = handle.read() try: headers, body = raw.split("\n\n", 1) except ValueError: headers, body = "", raw try: for line in headers.splitlines(): name, value = line.split(':', 1) self.headers[name.strip()] = value.strip() except ValueError: self.headers = {} body = "\n\n".join([headers, body]) self.headers['pubDate'] = ctime = os.stat(self.source).st_ctime self.headers['published'] = datetime.datetime.fromtimestamp(ctime) basename, oldext = os.path.splitext(filename) oldext = oldext.lower()[1:] converter = getattr(self, "convert_%s" % oldext, lambda x: (None, x)) if "insert_settings" in self.headers: body = body % {"settings": self.format_settings()} self.toc, self.body = converter(body) newext = self.headers.get('ext', '.html') self.target = os.path.join(tgt_path, "%s%s" % (basename, newext)) def url(self): path = self.target.split(conf.OUTPUT_PATH)[1].lstrip('/') return "/".join([self.site.url, path]) def needed(self): for f in "force --force -f".split(): if f in sys.argv[1:]: return True if not os.path.exists(self.target): return True smtime = os.stat(self.source).st_mtime tmtime = os.stat(self.target).st_mtime return smtime > tmtime def write(self): contents = self.render() with codecs.open(self.target, 'w', 'utf-8') as tgt: tgt.write(contents) def render(self): tmpl_name = self.headers.get('template') if not tmpl_name: return self.body kwargs = { "conf": conf, "toc": self.toc, "body": self.body, "url": self.url() } kwargs.update(self.headers) return self.site.get_template(tmpl_name).render(kwargs) def convert_rst(self, body): overrides = {"initial_header_level": 2} parts = publish_parts( source=body, writer_name="html", settings_overrides=overrides ) lines = parts['html_body'].splitlines() toppos, botpos = None, None for idx, line in enumerate(lines): if line.find("_TOC_TOP_") >= 0: toppos = idx elif line.find("_TOC_BOT_") >= 0: botpos = idx if toppos is None or botpos is None: return None, parts['html_body'] toc = lines[toppos+1:botpos] body = lines[:toppos] + lines[botpos+1:] return '\n'.join(toc), '\n'.join(body) def format_settings(self): currdir = os.path.dirname(__file__) sys.path.insert(0, os.path.join(currdir, "..")) import gunicorn.config as guncfg ret = [] for i, s in enumerate(guncfg.KNOWN_SETTINGS): if i == 0 or s.section != guncfg.KNOWN_SETTINGS[i-1].section: ret.append("%s\n%s\n\n" % (s.section, "+" * len(s.section))) ret.append(self.fmt_setting(s)) return ''.join(ret) def fmt_setting(self, s): if callable(s.default): val = inspect.getsource(s.default) val = "\n".join(" %s" % l for l in val.splitlines()) val = " ::\n\n" + val else: val = "``%s``" % s.default if s.cli and s.meta: args = ["%s %s" % (arg, s.meta) for arg in s.cli] cli = ', '.join(args) elif s.cli: cli = ", ".join(s.cli) out = [] out.append("%s" % s.name) out.append("~" * len(s.name)) out.append("") if s.cli: out.append("* ``%s``" % cli) out.append("* %s" % val) out.append("") out.append(s.desc) out.append("") out.append("") return "\n".join(out) def main(): Site().render() if __name__ == "__main__": main()