forked from truveris/pygreen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpygreen.py
197 lines (174 loc) · 7.52 KB
/
pygreen.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#!/usr/bin/env python
from __future__ import division, absolute_import, print_function, unicode_literals
import flask
import os.path
from mako.lookup import TemplateLookup
from mako import exceptions
import os
import os.path
import wsgiref.handlers
import sys
import logging
import re
import argparse
import sys
import markdown
import waitress
_logger = logging.getLogger(__name__)
sys.path.append(".")
MD_EXTENSIONS = [
'extra', # https://python-markdown.github.io/extensions/
'sane_lists',
'smarty',
'pymdownx.smartsymbols', # https://facelessuser.github.io/pymdown-extensions/extensions/smartsymbols/
]
def mdown(text):
"""Custom calling of markdown to turn on the features I want."""
return markdown.markdown(text, output_format='html5',
extensions=MD_EXTENSIONS, encoding='utf-8')
class PyGreen:
def __init__(self):
if os.path.isfile("env.py"):
import env
else:
env = None
self.env = env
# the Bottle application
self.app = flask.Flask(__name__, static_folder=None, template_folder=None)
# a set of strings that identifies the extension of the files
# that should be processed using Mako
self.template_exts = set(["html"])
# the folder where the files to serve are located. Do not set
# directly, use set_folder instead
self.folder = "."
self.app.root_path = "."
# the TemplateLookup of Mako
self.templates = TemplateLookup(directories=[self.folder],
imports=['from pygreen import mdown'],
input_encoding='utf8',
collection_size=100,
strict_undefined=True,
)
# A list of regular expression. Files whose the name match
# one of those regular expressions will not be outputed when generating
# a static version of the web site
self.file_exclusion = [r".*\.mako", r".*\.py",
r".*\.md", r".*\.scss", r".*\.map",
r"_.*", r"(^|.*\/)\..*"]
def is_public(path):
for ex in self.file_exclusion:
if re.match(ex,path):
return False
return True
def base_lister():
files = []
for dirpath, dirnames, filenames in os.walk(self.folder):
for f in filenames:
absp = os.path.join(dirpath, f)
path = os.path.relpath(absp, self.folder)
if is_public(path):
files.append(path)
return files
# A list of functions. Each function must return a list of paths
# of files to export during the generation of the static web site.
# The default one simply returns all the files contained in the folder.
# It is necessary to define new listers when new routes are defined
# in the Bottle application, or the static site generation routine
# will not be able to detect the files to export.
self.file_listers = [base_lister]
def file_renderer(path):
if is_public(path):
# check to see if the url is a directory, then
# look for index.html if so
if os.path.isdir(os.path.join(self.folder, path)):
tmp = os.path.join(path, "index.html")
if os.path.isfile(os.path.join(self.folder, tmp)):
path = tmp
else:
return b"No index.html in path: %s" % path
if path.split(".")[-1] in self.template_exts and self.templates.has_template(path):
t = self.templates.get_template(path)
try:
data = t.render_unicode(pygreen=self, env=self.env)
except:
return exceptions.html_error_template().render()
return data.encode(t.module._source_encoding)
if os.path.exists(os.path.join(self.folder, path)):
return flask.send_file(path)
flask.abort(404)
# The default function used to render files. Could be modified to change the way files are
# generated, like using another template language or transforming css...
self.file_renderer = file_renderer
self.app.add_url_rule('/', "root", lambda: self.file_renderer('index.html'), methods=['GET', 'POST', 'PUT', 'DELETE'])
self.app.add_url_rule('/<path:path>', "all_files", lambda path: self.file_renderer(path), methods=['GET', 'POST', 'PUT', 'DELETE'])
def set_folder(self, folder):
"""
Sets the folder where the files to serve are located.
"""
self.folder = folder
self.templates.directories[0] = folder
self.app.root_path = folder
def run(self, host='0.0.0.0', port=8080):
"""
Launch a development web server.
"""
waitress.serve(self.app, host=host, port=port)
def get(self, path):
"""
Get the content of a file, indentified by its path relative to the folder configured
in PyGreen. If the file extension is one of the extensions that should be processed
through Mako, it will be processed.
"""
data = self.app.test_client().get("/%s" % path).data
return data
def gen_static(self, output_folder):
"""
Generates a complete static version of the web site. It will stored in
output_folder.
"""
files = []
for l in self.file_listers:
files += l()
for f in files:
_logger.info("generating %s" % f)
content = self.get(f)
loc = os.path.join(output_folder, f)
d = os.path.dirname(loc)
if not os.path.exists(d):
os.makedirs(d)
with open(loc, "wb") as file_:
file_.write(content)
def __call__(self, environ, start_response):
return self.app(environ, start_response)
def cli(self, cmd_args=None):
"""
The command line interface of PyGreen.
"""
logging.basicConfig(level=logging.INFO, format='%(message)s')
parser = argparse.ArgumentParser(description='PyGreen, micro web framework/static web site generator')
subparsers = parser.add_subparsers(dest='action')
parser_serve = subparsers.add_parser('serve', help='serve the web site')
parser_serve.add_argument('-p', '--port', type=int, default=8080, help='port to serve on')
parser_serve.add_argument('-f', '--folder', default=".", help='folder containg files to serve')
parser_serve.add_argument('-d', '--disable-templates', action='store_true', default=False, help='just serve static files, do not use Mako')
def serve():
if args.disable_templates:
self.template_exts = set([])
self.run(port=args.port)
parser_serve.set_defaults(func=serve)
parser_gen = subparsers.add_parser('gen', help='generate a static version of the site')
parser_gen.add_argument('output', help='folder to store the files')
parser_gen.add_argument('-f', '--folder', default=".", help='folder containing files to generate')
def gen():
self.gen_static(args.output)
parser_gen.set_defaults(func=gen)
args = parser.parse_args(cmd_args)
self.set_folder(args.folder)
print(parser.description)
print("")
args.func()
def main():
pygreen = PyGreen()
pygreen.cli()
if __name__ == "__main__":
main()