Files
bugis/md2html/md2html.py
Walter Oggioni 514b50ff88 initial commit
2018-09-13 08:59:22 +01:00

157 lines
5.3 KiB
Python

#!/usr/bin/env python3
import argparse
import sys
import markdown
from os.path import basename, dirname
import json
TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.0/css/bootstrap-combined.min.css" rel="stylesheet">
<style>
body {{
font-family: sans-serif;
}}
code, pre {{
font-family: monospace;
}}
h1 code,
h2 code,
h3 code,
h4 code,
h5 code,
h6 code {{
font-size: inherit;
}}
</style>
</head>
<body>
<script type=\"text/javascript\">
let eventSource = new EventSource("/stream");
eventSource.addEventListener('reload', function(e) {{
window.location.reload(true);
}});
</script>
<div class="container">
{content}
</div>
</body>
</html>
"""
class ServerSentEvent(object):
def __init__(self, id=None, event=None, data=None, retry=1000):
self.id = id
self.event = event
self.data = json.dumps(data)
self.retry = retry
def encode(self):
if not self.data:
return ""
lines = [f"{key}: {value}" for key, value in vars(self).items() if value]
return "%s\n\n" % "\n".join(lines)
def parse_args(args=None):
parser = argparse.ArgumentParser(description='Make a complete, styled HTML document from a Markdown file.')
parser.add_argument('mdfile', help='File to convert. Defaults to stdin.')
parser.add_argument('-o', '--out', help='Output file name. Defaults to stdout.')
parser.add_argument('-r', '--raw', action='store_true',
help='Just output a raw html fragment, as returned from the markdown module')
parser.add_argument('-e', '--extensions', nargs='+', default=['extra', 'smarty', 'tables'],
help='Activate specified markdown extensions (defaults to "extra smarty tables")')
try:
import inotify
import flask
import gevent
import signal
parser.add_argument('-w', '--watch', action='store_true',
help='Watch specified source file and rerun the compilation for every time it changes')
parser.add_argument('-p', '--port', default=5000, type=int,
help='Specify http server port (defaults to 5000)')
parser.add_argument('-i', '--interface', default='',
help='Specify http server listen interface (defaults to localhost)')
except ImportError:
pass
return parser.parse_args(args)
def compile_html(mdfile=None, extensions=None, raw=None, **kwargs):
html = None
with mdfile and open(mdfile, 'r') or sys.stdin as instream:
html = markdown.markdown(instream.read(), extensions=extensions, output_format='html5')
if raw:
doc = html
else:
doc = TEMPLATE.format(**dict(content=html))
return doc
def write_html(out=None, **kwargs):
doc = compile_html(**kwargs)
with (out and open(out, 'w')) or sys.stdout as outstream:
outstream.write(doc)
def main(args=None):
import signal
args = parse_args(args)
exit = False
def sigint_handler(signum, frame):
nonlocal exit
exit = True
handlers = (sigint_handler, signal.getsignal(signal.SIGINT))
signal.signal(signal.SIGINT, lambda signum, frame: [handler(signum, frame) for handler in handlers])
if hasattr(args, 'watch') and args.watch:
import threading
from flask import Flask, Response
from gevent.pywsgi import WSGIServer
condition_variable = threading.Condition()
def watch_file():
import inotify.adapters
nonlocal condition_variable, exit
watcher = inotify.adapters.Inotify()
watcher.add_watch(dirname(args.mdfile))
target_file = basename(args.mdfile)
while True:
if exit:
break
for event in watcher.event_gen(yield_nones=True, timeout_s=1):
if not event:
continue
(_, event_type, path, filename) = event
if filename == target_file and len(set(event_type).intersection(
{'IN_CREATE', 'IN_MODIFY', 'IN_CLOSE_WRITE'})):
condition_variable.acquire()
condition_variable.notify_all()
condition_variable.release()
file_watcher = threading.Thread(target=watch_file)
file_watcher.start()
app = Flask(__name__)
@app.route('/')
def get():
return Response(compile_html(**vars(args)), mimetype='text/html')
@app.route("/stream")
def stream():
nonlocal condition_variable
def gen():
while True:
condition_variable.acquire()
condition_variable.wait()
sse = ServerSentEvent(event='reload')
return sse.encode()
return Response(gen(), mimetype="text/event-stream")
server = WSGIServer((args.interface, args.port), app, environ={'wsgi.multithread': True})
server.serve_forever()
else:
write_html(**vars(args))
if __name__ == '__main__':
main()