#!/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: x) if "insert_settings" in self.headers: body = body % {"settings": self.format_settings()} 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, "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 ) return parts['html_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_setting2(s)) return ''.join(ret) def fmt_setting2(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 fmt_setting(self, s): out = [] lines = s.desc.splitlines() width = max(map(lambda x: len(x), lines)) if not callable(s.default): val = '%s' % s.default else: val = '' 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) else: cli = "N/A" width = 80 namelen = 20 deflen = 20 clilen = width - (namelen + deflen + 4) args = ("-" * namelen, "-" * deflen, "-" * clilen) out.append("+%s+%s+%s+" % args) names = "| %s" % s.name names += " " * (namelen - (len(s.name) + 2)) names += " | %s" % val names += " " * (deflen - (len(val) + 2)) names += " | %s" % cli names += " " * (clilen - (len(cli) + 1)) names += "|" out.append(names) out.append(out[0].replace("-", "=")) for l in lines: l = l.rstrip("\n") if len(l) < width: l += " " * ((width - 2) - len(l)) out.append("|%s|" % l) out.append("+%s+" % ("-" * (width - 2))) out.extend(["", ""]) return "\n".join(out) def main(): Site().render() if __name__ == "__main__": main()