Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
177 lines (151 sloc) 6.09 KB
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""rt (redtape) is a simple, no-nonsense documentation generator which will
convert one or more markdown documents into good looking HTML files with
niceties like syntax highlighting, simple access to icons and styles from
twitter bootstrap, and more.
It is not meant for complex documentation generation, but as an easy
way to flexibly create one-off documents or collections of documents while
retaining control over document style."""
import pygments
import jinja2
try:
from lxml.html import document_fromstring
from lxml.cssselect import CSSSelector as cs
has_lxml = True
except:
has_lxml = False
import os
import redtape
from glob import glob
from redtape import gfm
from optparse import OptionParser
parser = OptionParser(version=".".join(map(str, redtape.VERSION)),
usage="%prog [opts] <file(s)>")
parser.add_option("-t", "--template", help="use alternate document template")
parser.add_option("-e", "--embed", action="store_true", help="embed all css/js in each output file")
parser.add_option("-o", "--destination", help="write HTML/assets to a separate destination directory")
parser.add_option("-r", "--recursive", action="store_true", help="recurse into subdirectories")
parser.add_option("", "--use-js", action="store_true", help="link in jquery & bootstrap js files")
parser.add_option("", "--create-assets", action="store_true", help="create/update directory ./assets with rt assets")
parser.add_option("", "--prettify", action="store_true", help="use google prettify for code blocks instead of pygments")
pkg_dir = os.path.dirname(__file__)
asset_path = os.path.join(os.path.dirname(__file__), "assets")
def extract_title(fragment):
if not has_lxml: return ""
doc = document_fromstring(fragment)
try:
return cs('h1')(doc)[0].text_content()
except:
import traceback
traceback.print_exc()
return ""
def markdown_files(directory):
paths = []
for ext in (".md", ".mdown", ".markdown"):
paths += glob(os.path.join(directory, "*%s" % ext))
return paths
def arg_to_paths(arg, recursive=False):
paths = []
if not os.path.exists(arg):
raise Exception("File not found: %s" % arg)
if os.path.isfile(arg):
paths.append(arg)
if os.path.isdir(arg):
if recursive:
for path, dirs, filenames in os.walk(arg):
paths += markdown_files(path)
else:
paths += markdown_files(arg)
return paths
def create_assets():
import shutil
current = os.path.abspath(os.curdir)
destination = os.path.join(current, "assets")
if os.path.exists(destination) and not os.path.isdir(destination):
raise Exception("The path ./assets exists and is a file.")
shutil.copytree(asset_path, destination)
def find_customizations(path):
header_path = os.path.join(path, "header.html")
footer_path = os.path.join(path, "footer.html")
header, footer = "", ""
if os.path.isfile(footer_path):
with open(footer_path) as f:
footer = f.read()
if os.path.isfile(header_path):
with open(header_path) as f:
header = f.read()
return header, footer
def find_custom_template(args):
"""Finds a custom template in the directories provided as arguments. The
first template found is returned and used for the remainder of the docs."""
for arg in args:
if os.path.isdir(arg):
dirlist = os.listdir(arg)
if "custom.html" in dirlist:
return os.path.join(arg, "custom.html")
elif "custom.jinja" in dirlist:
return os.path.join(arg, "custom.jinja")
def get_jinja_template(opts, args):
"""Get a jinja template suitable for rendering our documents. Care is
taken to chose figure out which template to render, and to allow custom
templates to inherit from the default one."""
template_paths = [asset_path]
template = "basic.jinja"
if opts.template:
extra, template = os.path.split(os.path.abspath(opts.template))
template_paths.append(extra)
else:
template = find_custom_template(args) or template
env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_paths))
return env.get_template(template)
def main():
opts, args = parser.parse_args()
if opts.create_assets:
create_assets()
return 0
if not args:
parser.print_usage()
return -1
context = {}
use_prettify = context['prettify'] = opts.prettify
css = [
"assets/css/bootstrap.min.css",
"assets/css/%s" % ("prettify.css" if use_prettify else "pygments.css"),
]
js = [] if not opts.use_js else [
"assets/js/jquery.min.js",
"assets/js/bootstrap.min.js",
]
if use_prettify: # if prettifying, link prettify no matter what
js.append("assets/js/prettify.js")
context['js'] = js
context['css'] = css
if opts.embed:
embed = {'css':[], 'js':[]}
for csf in css:
with open(os.path.join(pkg_dir, csf)) as f:
embed['css'].append(f.read().decode("utf-8"))
for jsf in js:
with open(os.path.join(pkg_dir, jsf)) as f:
embed['js'].append(f.read().decode("utf-8"))
context['embed'] = embed
else:
context['embed'] = None
for arg in args:
header, footer = "", ""
paths = arg_to_paths(arg, opts.recursive)
if os.path.isdir(arg):
header, footer = find_customizations(arg)
for path in paths:
output = path.rsplit('.', 1)[0] + '.html'
with open(path) as f:
document = gfm.gfmd(f.read().decode("utf-8"), fenced="pygments" if not opts.prettify else "bootstrap")
context['title'] = extract_title(document)
context['document'] = document
context['header'] = header
context['footer'] = footer
template = get_jinja_template(opts, args)
with open(output, "w") as f:
f.write(template.render(context).encode("utf-8"))
print "Created %s from %s" % (output, path)