initial commit

This commit is contained in:
Walter Oggioni
2018-09-13 08:59:22 +01:00
commit 514b50ff88
3 changed files with 218 additions and 0 deletions

22
LICENSE.md Normal file
View File

@@ -0,0 +1,22 @@
Copyright 2018 Walter Oggioni
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

156
md2html/md2html.py Normal file
View File

@@ -0,0 +1,156 @@
#!/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()

40
setup.py Normal file
View File

@@ -0,0 +1,40 @@
from os.path import join, dirname
from setuptools import setup, find_packages
def read(fname):
return open(join(dirname(__file__), fname)).read()
config = {
'name': "md2html",
'version': "0.1",
'author': "Walter Oggioni",
'author_email': "oggioni.walter@gmail.com",
'description': ("Various development utility scripts"),
'long_description': '',
'license': "MIT",
'keywords': "build",
'url': "https://github.com/oggio88/md2html",
'packages': ['md2html'],
'include_package_data': True,
'classifiers': [
'Development Status :: 3 - Alpha',
'Topic :: Utilities',
'License :: OSI Approved :: MIT License',
'Intended Audience :: System Administrators',
'Intended Audience :: Developers',
'Environment :: Console',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
],
'install_requires': [
'markdown'
],
"entry_points": {
'console_scripts': [
'md2html=md2html.md2html:main',
],
}
}
setup(**config)