Skip to content

Commit a2cbea8

Browse files
committed
way better make-html.py and unlicensing update-ends.py
1 parent 43f4105 commit a2cbea8

File tree

2 files changed

+211
-16
lines changed

2 files changed

+211
-16
lines changed

make-html.py

Lines changed: 184 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
11
#!/usr/bin/env python3
2+
3+
# This is free and unencumbered software released into the public
4+
# domain.
5+
6+
# Anyone is free to copy, modify, publish, use, compile, sell, or
7+
# distribute this software, either in source code form or as a
8+
# compiled binary, for any purpose, commercial or non-commercial, and
9+
# by any means.
10+
11+
# In jurisdictions that recognize copyright laws, the author or
12+
# authors of this software dedicate any and all copyright interest in
13+
# the software to the public domain. We make this dedication for the
14+
# benefit of the public at large and to the detriment of our heirs
15+
# and successors. We intend this dedication to be an overt act of
16+
# relinquishment in perpetuity of all present and future rights to
17+
# this software under copyright law.
18+
19+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
23+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24+
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
27+
# For more information, please refer to <http://unlicense.org>
28+
229
"""Create HTML files of the tutorial."""
330

4-
import glob
5-
import shutil
631
import os
32+
import re
33+
import shutil
34+
import string
735
import sys
836
import webbrowser
937

@@ -16,38 +44,178 @@
1644
print(">>> pip.main(['install', '--user', 'mistune'])")
1745
sys.exit(1)
1846

47+
try:
48+
import pygments.formatters
49+
import pygments.lexers
50+
except ImportError:
51+
# we can work without pygments, but we won't get colors
52+
pygments = None
53+
54+
55+
LINK_REGEX = r'\[.*\]\((.*\.md)\)'
56+
57+
58+
def askyesno(question, default=True):
59+
"""Ask a yes/no question and return True or False.
60+
61+
The default answer is yes if default is True and no if default is
62+
False.
63+
"""
64+
if default:
65+
# yes by default
66+
question += ' [Y/n] '
67+
else:
68+
# no by default
69+
question += ' [y/N] '
70+
while True:
71+
result = input(question).upper().strip()
72+
if result == 'Y':
73+
return True
74+
if result == 'N':
75+
return False
76+
if not result:
77+
return default
78+
79+
80+
def fix_filename(filename):
81+
if filename == 'README.md':
82+
return 'index.html'
83+
if filename.endswith('.md'):
84+
return filename[:-3] + '.html'
85+
return filename
86+
87+
88+
class TutorialRenderer(mistune.Renderer):
89+
90+
def __init__(self):
91+
super().__init__()
92+
self._headercounts = {}
93+
94+
def _get_header_link(self, title):
95+
"""Return a github-style link target for a title.
96+
97+
>>> r = TutorialRenderer()
98+
>>> r._get_header_link('Hello there!')
99+
'hello-there'
100+
>>> r._get_header_link('Hello there!')
101+
'hello-there-1'
102+
>>> r._get_header_link('Hello there!')
103+
'hello-there-2'
104+
>>>
105+
"""
106+
result = ''
107+
for character in title:
108+
if character in string.whitespace:
109+
result += '-'
110+
elif character in string.punctuation:
111+
pass
112+
else:
113+
result += character.lower()
114+
115+
if result not in self._headercounts:
116+
# this title appears in this file for the first time
117+
self._headercounts[result] = 1
118+
return result
119+
# there has been already a link with the same text on this page,
120+
# we need to do thetitle, thetitle-1, thetitle-2, etc.
121+
real_result = '%s-%d' % (result, self._headercounts[result])
122+
self._headercounts[result] += 1
123+
return real_result
124+
125+
def header(self, text, level, raw):
126+
"""Create a header that is also a link and a # link target."""
127+
# "# raw"
128+
target = self._get_header_link(raw)
129+
content = super().header(text, level, raw)
130+
return '<a name="{0}" href="#{0}">{1}</a>'.format(target, content)
131+
132+
def link(self, link, title, text):
133+
"""Return a link that points to the correct file."""
134+
# "[text](link)"
135+
if link.startswith('#'):
136+
# it's like "#title", no need to do anything
137+
pass
138+
elif '#' in link:
139+
# it's like "some-file#title", we need to fix some-file
140+
before, after = link.split('#', 1)
141+
link = fix_filename(before) + '#' + after
142+
else:
143+
# it's like "some-file"
144+
link = fix_filename(link)
145+
return super().link(link, title, text)
146+
147+
def block_code(self, code, lang=None):
148+
"""Highlight Python code blocks with Pygments if it's installed."""
149+
if lang == 'py' and pygments is not None:
150+
# we can highlight it
151+
if code.startswith('>>> '):
152+
lexer = pygments.lexers.PythonConsoleLexer()
153+
else:
154+
lexer = pygments.lexers.PythonLexer()
155+
formatter = pygments.formatters.HtmlFormatter(
156+
style='tango',
157+
noclasses=True)
158+
return pygments.highlight(code, lexer, formatter)
159+
# we can't highlight it
160+
return super().block_code(code, lang)
161+
162+
def image(self, src, title, text):
163+
"""Return an image inside a link."""
164+
result = super().image(src, title, text)
165+
return self.link(src, title, result)
166+
167+
def table(self, header, body):
168+
"""Return a table with a border."""
169+
result = super().table(header, body)
170+
return result.replace('<table>', '<table border="1">', 1)
171+
19172

20173
def main():
174+
if pygments is None:
175+
print("Pygments isn't installed. You can install it like this:")
176+
print()
177+
print(">>> import pip")
178+
print(">>> pip.main(['install', '--user', 'Pygments'])")
179+
print()
180+
print("You can also continue without Pygments, but the code examples")
181+
print("will not be in color.")
182+
if not askyesno("Continue without pygments?"):
183+
print("Interrupt.")
184+
return
185+
21186
if os.path.exists('html'):
22-
if input("html exists. Do you want "
23-
"to remove it? [Y/n] ").upper() == 'N':
187+
if not askyesno("html exists. Do you want to remove it?"):
24188
print("Interrupt.")
25189
return
26190
if os.path.isdir('html'):
27191
shutil.rmtree('html')
28192
else:
29193
os.remove('html')
30194
os.mkdir('html')
195+
196+
print("Getting a list of files to generate...")
197+
with open('README.md', 'r') as f:
198+
filelist = re.findall(LINK_REGEX, f.read()) + ['README.md']
199+
31200
print("Generating HTML files...")
32-
for markdownfile in glob.glob('*.md'):
33-
htmlfile = os.path.join('html', markdownfile[:-3] + '.html')
201+
for markdownfile in filelist:
202+
htmlfile = os.path.join('html', fix_filename(markdownfile))
34203
print(' ', markdownfile, '->', htmlfile)
35204
with open(markdownfile, 'r') as f1:
36205
with open(htmlfile, 'w') as f2:
37206
md = f1.read()
38-
md = md.replace('.md', '.html')
39-
html = mistune.markdown(md)
207+
html = mistune.markdown(md, renderer=TutorialRenderer())
40208
print(html, file=f2)
41-
os.rename(os.path.join('html', 'README.html'),
42-
os.path.join('html', 'index.html'))
209+
210+
print("Copying other files...")
43211
shutil.copytree('images', os.path.join('html', 'images'))
212+
shutil.copy('LICENSE', os.path.join('html', 'LICENSE'))
213+
214+
print("\n*********************\n")
215+
print("Ready! The files are in the html directory.")
216+
print("Go to html and double-click index.html to read the tutorial.")
44217
print()
45-
print("*********************")
46-
print()
47-
print("Ready! The files are in the html directory,")
48-
print("double-click html/index.html to read the tutorial.")
49-
print()
50-
if input("Do you want to view the tutorial now? [Y/n] ").upper() != 'N':
218+
if askyesno("Do you want to view the tutorial now?"):
51219
print("Opening the tutorial...")
52220
webbrowser.open(os.path.join('html', 'index.html'))
53221

update-ends.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
11
#!/usr/bin/env python3
2+
3+
# This is free and unencumbered software released into the public
4+
# domain.
5+
6+
# Anyone is free to copy, modify, publish, use, compile, sell, or
7+
# distribute this software, either in source code form or as a
8+
# compiled binary, for any purpose, commercial or non-commercial, and
9+
# by any means.
10+
11+
# In jurisdictions that recognize copyright laws, the author or
12+
# authors of this software dedicate any and all copyright interest in
13+
# the software to the public domain. We make this dedication for the
14+
# benefit of the public at large and to the detriment of our heirs
15+
# and successors. We intend this dedication to be an overt act of
16+
# relinquishment in perpetuity of all present and future rights to
17+
# this software under copyright law.
18+
19+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
23+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
24+
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26+
27+
# For more information, please refer to <http://unlicense.org>
28+
229
"""Update ends of markdown files."""
330

431
import re

0 commit comments

Comments
 (0)