python-peps/pep2html.py

263 lines
7.3 KiB
Python
Raw Normal View History

#!/usr/bin/env python
"""
convert PEP's to (X)HTML - courtesy of /F
Usage: %(PROGRAM)s [options] [peps]
Notes:
The optional argument peps can be either pep numbers or .txt files.
Options:
-u/--user
SF username
-i/--install
After generating the HTML, install it and the plain text source file
(.txt) SourceForge. In that case the user's name is used in the scp
and ssh commands, unless sf_username is given (in which case, it is
used instead). Without -i, sf_username is ignored.
-q/--quiet
Turn off verbose messages.
-h/--help
Print this help message and exit.
"""
import sys
import os
import re
import cgi
import glob
import getopt
import errno
PROGRAM = sys.argv[0]
RFCURL = 'http://www.faqs.org/rfcs/rfc%d.html'
PEPURL = 'pep-%04d.html'
HOST = "shell.sourceforge.net" # host for update
HDIR = "/home/groups/p/py/python/htdocs/peps" # target host directory
LOCALVARS = "Local Variables:"
# The generated HTML doesn't validate -- you cannot use <hr> and <h3> inside
# <pre> tags. But if I change that, the result doesn't look very nice...
DTD = ('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"\n'
' "http://www.w3.org/TR/REC-html40/loose.dtd">')
fixpat = re.compile("((http|ftp):[-_a-zA-Z0-9/.+~:?#$=&]+)|(pep-\d+(.txt)?)|"
"(RFC[- ]?(?P<rfcnum>\d+))|"
"(PEP\s+(?P<pepnum>\d+))|"
".")
def usage(code, msg=''):
sys.stderr.write(__doc__ % globals() + '\n')
if msg:
msg = str(msg)
if msg[-1] <> '\n':
msg = msg + '\n'
sys.stderr.write(msg)
sys.exit(code)
def fixanchor(current, match):
text = match.group(0)
link = None
if text[:5] == "http:" or text[:4] == "ftp:":
link = text
elif text[:4] == "pep-" and text != current:
link = os.path.splitext(text)[0] + ".html"
elif text[:3] == 'PEP':
pepnum = int(match.group('pepnum'))
link = PEPURL % pepnum
elif text[:3] == 'RFC':
rfcnum = int(match.group('rfcnum'))
link = RFCURL % rfcnum
if link:
return "<a href='%s'>%s</a>" % (link, cgi.escape(text))
return cgi.escape(match.group(0)) # really slow, but it works...
def fixfile(infile, outfile):
# convert plain text pep to minimal XHTML markup
try:
fi = open(infile)
except IOError, e:
if e.errno <> errno.ENOENT: raise
print >> sys.stderr, 'Error: Skipping missing PEP file:', e.filename
return
fo = open(outfile, "w")
fo.write(DTD + "\n<html>\n<head>\n")
# head
header = []
pep = ""
title = ""
while 1:
line = fi.readline()
if not line.strip():
break
if line[0].strip():
if ":" not in line:
break
key, value = line.split(":", 1)
value = value.strip()
header.append((key, value))
else:
# continuation line
key, value = header[-1]
value = value + line
header[-1] = key, value
if key.lower() == "title":
title = value
elif key.lower() == "pep":
pep = value
if pep:
title = "PEP " + pep + " -- " + title
if title:
fo.write(" <title>%s</title>\n"
' <link rel="STYLESHEET" href="style.css">\n'
% cgi.escape(title))
fo.write("</head>\n")
# body
fo.write('<body bgcolor="white">\n'
'<div class="navigation">\n')
fo.write('[<b><a href="../">home</a></b>]\n')
if os.path.basename(infile) != "pep-0000.txt":
fo.write('[<b><a href=".">index</a></b>]\n')
fo.write('[<b><a href="pep-%04d.txt">PEP source</a></b>]\n' % int(pep))
fo.write('</div>\n'
'<div class="header">\n<table border="0">\n')
for k, v in header:
if k.lower() in ('author', 'discussions-to'):
mailtos = []
for addr in v.split():
if '@' in addr:
mailtos.append(
'<a href="mailto:%s?subject=PEP%%20%s">%s</a>' %
(addr, pep, addr))
elif addr.startswith('http:'):
mailtos.append(
'<a href="%s">%s</a>' % (addr, addr))
else:
mailtos.append(addr)
v = ' '.join(mailtos)
elif k.lower() in ('replaces', 'replaced-by'):
peps = ''
for pep in v.split():
pep = int(pep)
peps += '<a href="pep-%04d.html">%i</a> ' % (pep, pep)
v = peps
else:
v = cgi.escape(v)
fo.write(" <tr><th align='right'>%s:</th><td>%s</td></tr>\n"
% (cgi.escape(k), v))
title = 0
fo.write("</table>\n</div>\n<hr />\n"
"<pre>")
while 1:
line = fi.readline()
if not line:
break
if line[0] != "\f":
if line[0].strip():
if line.strip() == LOCALVARS:
break
fo.write("</pre>\n<h3>%s</h3>\n<pre>" % line.strip())
title = 0
else:
line = fixpat.sub(lambda x, c=infile: fixanchor(c, x), line)
fo.write(line)
fo.write("</pre>\n"
"</body>\n"
"</html>\n")
fo.close()
os.chmod(outfile, 0664)
def find_pep(pep_str):
"""Find the .txt file indicated by a cmd line argument"""
if os.path.exists(pep_str):
return pep_str
num = int(pep_str)
return "pep-%04d.txt" % num
def make_html(file, verbose=0):
newfile = os.path.splitext(file)[0] + ".html"
if verbose:
print file, "->", newfile
fixfile(file, newfile)
return newfile
SPACE = ' '
def push_pep(htmlfiles, txtfiles, username, verbose):
if verbose:
quiet = ""
else:
quiet = "-q"
if username:
username = username + "@"
target = username + HOST + ":" + HDIR
files = htmlfiles[:]
files.extend(txtfiles)
files.append("style.css")
filelist = SPACE.join(files)
rc = os.system("scp %s %s %s" % (quiet, filelist, target))
if rc:
sys.exit(rc)
rc = os.system("ssh %s%s chmod 664 %s/*" % (username, HOST, HDIR))
if rc:
sys.exit(rc)
def main():
# defaults
update = 0
username = ''
verbose = 1
try:
opts, args = getopt.getopt(sys.argv[1:], 'ihqu:',
['install', 'help', 'quiet', 'user='])
except getopt.error, msg:
usage(1, msg)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-i', '--install'):
update = 1
elif opt in ('-u', '--user'):
username = arg
elif opt in ('-q', '--quiet'):
verbose = 0
if args:
peptxt = []
html = []
for pep in args:
file = find_pep(pep)
peptxt.append(file)
newfile = make_html(file, verbose=verbose)
html.append(newfile)
else:
# do them all
peptxt = []
for file in glob.glob("pep-*.txt"):
peptxt.append(file)
make_html(file, verbose=verbose)
html = ["pep-*.html"]
if update:
push_pep(html, peptxt, username, verbose)
if __name__ == "__main__":
main()