diff --git a/pep2html.py b/pep2html.py
index b65df17fe..8467e8891 100755
--- a/pep2html.py
+++ b/pep2html.py
@@ -1,6 +1,5 @@
#!/usr/bin/env python
-"""
-convert PEP's to (X)HTML - courtesy of /F
+"""Convert PEPs to (X)HTML - courtesy of /F
Usage: %(PROGRAM)s [options] [peps]
@@ -17,10 +16,10 @@ Options:
will browse PEP 0.
-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 -u sf_username is given (in which case, it is
- used instead). Without -i, -u is ignored.
+ After generating the HTML, install it and the source file (.txt)
+ SourceForge. In that case the user's name is used in the scp and ssh
+ commands, unless -u sf_username is given (in which case, that is used
+ instead). Without -i, -u is ignored.
-q/--quiet
Turn off verbose messages.
@@ -31,8 +30,6 @@ Options:
The optional argument `peps' is a list of either pep numbers or .txt files.
"""
-# Requires Python 2.2
-
import sys
import os
import re
@@ -42,12 +39,14 @@ import getopt
import errno
import random
import time
-from email.Utils import parseaddr
+REQUIRES = {'python': '2.2',
+ 'docutils': '0.2.1'}
PROGRAM = sys.argv[0]
RFCURL = 'http://www.faqs.org/rfcs/rfc%d.html'
PEPURL = 'pep-%04d.html'
-PEPCVSURL = 'http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/python/python/nondist/peps/pep-%04d.txt'
+PEPCVSURL = ('http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/python/python'
+ '/nondist/peps/pep-%04d.txt')
PEPDIRRUL = 'http://www.python.org/peps/'
@@ -128,25 +127,19 @@ def linkemail(address, pepno):
% (parts[0], parts[1], pepno, parts[0], parts[1]))
-def fixfile(infile, outfile):
- basename = os.path.basename(infile)
+def fixfile(inpath, input_lines, outfile):
+ from email.Utils import parseaddr
+ basename = os.path.basename(inpath)
+ infile = iter(input_lines)
# 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")
- print >> fo, DTD
- print >> fo, ''
- print >> fo, '
'
+ print >> outfile, DTD
+ print >> outfile, ''
+ print >> outfile, ''
# head
header = []
pep = ""
title = ""
- while 1:
- line = fi.readline()
+ for line in infile:
if not line.strip():
break
if line[0].strip():
@@ -167,27 +160,27 @@ def fixfile(infile, outfile):
if pep:
title = "PEP " + pep + " -- " + title
if title:
- print >> fo, ' '
need_pre = 1
- while 1:
- line = fi.readline()
- if not line:
- break
+ for line in infile:
if line[0] == '\f':
continue
if line.strip() == LOCALVARS:
@@ -238,8 +232,8 @@ def fixfile(infile, outfile):
if line.strip() == LOCALVARS:
break
if not need_pre:
- print >> fo, ''
- print >> fo, '
%s
' % line.strip()
+ print >> outfile, ''
+ print >> outfile, '
%s
' % line.strip()
need_pre = 1
elif not line.strip() and need_pre:
continue
@@ -251,9 +245,9 @@ def fixfile(infile, outfile):
# This is a PEP summary line, which we need to hyperlink
url = PEPURL % int(parts[1])
if need_pre:
- print >> fo, '
'
+ print >> outfile, ''
need_pre = 0
- print >> fo, re.sub(
+ print >> outfile, re.sub(
parts[1],
'%s' % (url, parts[1]),
line, 1),
@@ -262,24 +256,79 @@ def fixfile(infile, outfile):
# This is a pep email address line, so filter it.
url = fixemail(parts[-1], pep)
if need_pre:
- print >> fo, ''
+ print >> outfile, ''
need_pre = 0
- print >> fo, re.sub(
+ print >> outfile, re.sub(
parts[-1], url, line, 1),
continue
- line = fixpat.sub(lambda x, c=infile: fixanchor(c, x), line)
+ line = fixpat.sub(lambda x, c=inpath: fixanchor(c, x), line)
if need_pre:
- print >> fo, ''
+ print >> outfile, ''
need_pre = 0
- fo.write(line)
+ outfile.write(line)
if not need_pre:
- print >> fo, '
'
- print >> fo, '
'
- print >> fo, ''
- print >> fo, ''
- fo.close()
- os.chmod(outfile, 0664)
+ print >> outfile, ''
+ print >> outfile, ''
+ print >> outfile, ''
+ print >> outfile, ''
+
+docutils_options = None
+"""Option value object used by Docutils. Can be set by the client application
+when this module is imported."""
+
+def fix_rst_pep(inpath, input_lines, outfile):
+ from docutils import core, io
+ pub = core.Publisher()
+ pub.set_reader(reader_name='pep', parser_name='restructuredtext',
+ parser=None)
+ pub.set_writer(writer_name='pep_html')
+ if docutils_options:
+ options = docutils_options
+ pub.options = options
+ else:
+ options = pub.set_options()
+ options._source = inpath
+ options._destination = outfile.name
+ pub.source = io.StringInput(
+ options, source=''.join(input_lines), source_path=inpath)
+ pub.destination = io.FileOutput(
+ options, destination=outfile, destination_path=outfile.name,
+ autoclose=0)
+ pub.publish()
+
+
+def get_pep_type(input_lines):
+ """
+ Return the Content-Type of the input. "text/plain" is the default.
+ Return ``None`` if the input is not a PEP.
+ """
+ pep_type = None
+ for line in input_lines:
+ line = line.rstrip().lower()
+ if not line:
+ # End of the RFC 2822 header (first blank line).
+ break
+ elif line.startswith('content-type: '):
+ pep_type = line.split()[1] or 'text/plain'
+ break
+ elif line.startswith('pep: '):
+ # Default PEP type, used if no explicit content-type specified:
+ pep_type = 'text/plain'
+ return pep_type
+
+
+def get_input_lines(inpath):
+ try:
+ infile = open(inpath)
+ except IOError, e:
+ if e.errno <> errno.ENOENT: raise
+ print >> sys.stderr, 'Error: Skipping missing PEP file:', e.filename
+ sys.stderr.flush()
+ return None, None
+ lines = infile.read().splitlines(1) # handles x-platform line endings
+ infile.close()
+ return lines
def find_pep(pep_str):
@@ -289,12 +338,30 @@ def find_pep(pep_str):
num = int(pep_str)
return "pep-%04d.txt" % num
-def make_html(file, verbose=0):
- newfile = os.path.splitext(file)[0] + ".html"
+def make_html(inpath, verbose=0):
+ input_lines = get_input_lines(inpath)
+ pep_type = get_pep_type(input_lines)
+ if pep_type is None:
+ print >> sys.stderr, 'Error: Input file %s is not a PEP.' % inpath
+ sys.stdout.flush()
+ return None
+ elif not PEP_TYPE_DISPATCH.has_key(pep_type):
+ print >> sys.stderr, ('Error: Unknown PEP type for input file %s: %s'
+ % (inpath, pep_type))
+ sys.stdout.flush()
+ return None
+ elif PEP_TYPE_DISPATCH[pep_type] == None:
+ pep_type_error(inpath, pep_type)
+ return None
+ outpath = os.path.splitext(inpath)[0] + ".html"
if verbose:
- print file, "->", newfile
- fixfile(file, newfile)
- return newfile
+ print inpath, "(%s)" % pep_type, "->", outpath
+ sys.stdout.flush()
+ outfile = open(outpath, "w")
+ PEP_TYPE_DISPATCH[pep_type](inpath, input_lines, outfile)
+ outfile.close()
+ os.chmod(outfile.name, 0664)
+ return outpath
def push_pep(htmlfiles, txtfiles, username, verbose):
if verbose:
@@ -315,6 +382,42 @@ def push_pep(htmlfiles, txtfiles, username, verbose):
if rc:
sys.exit(rc)
+
+PEP_TYPE_DISPATCH = {'text/plain': fixfile,
+ 'text/x-rst': fix_rst_pep}
+PEP_TYPE_MESSAGES = {}
+
+def check_requirements():
+ # Check Python:
+ try:
+ from email.Utils import parseaddr
+ except ImportError:
+ PEP_TYPE_DISPATCH['text/plain'] = None
+ PEP_TYPE_MESSAGES['text/plain'] = (
+ 'Python %s or better required for "%%(pep_type)s" PEP '
+ 'processing; %s present (%%(inpath)s).'
+ % (REQUIRES['python'], sys.version.split()[0]))
+ # Check Docutils:
+ try:
+ import docutils
+ except ImportError:
+ PEP_TYPE_DISPATCH['text/x-rst'] = None
+ PEP_TYPE_MESSAGES['text/x-rst'] = (
+ 'Docutils not present for "%(pep_type)s" PEP file %(inpath)s. '
+ 'See README.txt for installation.')
+ else:
+ if docutils.__version__ < REQUIRES['docutils']:
+ PEP_TYPE_DISPATCH['text/x-rst'] = None
+ PEP_TYPE_MESSAGES['text/x-rst'] = (
+ 'Docutils must be reinstalled for "%%(pep_type)s" PEP '
+ 'processing (%%(inpath)s). Version %s or better required; '
+ '%s present. See README.txt for installation.'
+ % (REQUIRES['docutils'], docutils.__version__))
+
+def pep_type_error(inpath, pep_type):
+ print >> sys.stderr, 'Error: ' + PEP_TYPE_MESSAGES[pep_type] % locals()
+ sys.stdout.flush()
+
def browse_file(pep):
import webbrowser
@@ -334,16 +437,21 @@ def browse_remote(pep):
webbrowser.open(url)
-def main():
+def main(argv=None):
# defaults
update = 0
username = ''
verbose = 1
browse = 0
+ check_requirements()
+
+ if argv is None:
+ argv = sys.argv[1:]
+
try:
opts, args = getopt.getopt(
- sys.argv[1:], 'bihqu:',
+ argv, 'bihqu:',
['browse', 'install', 'help', 'quiet', 'user='])
except getopt.error, msg:
usage(1, msg)
@@ -367,18 +475,21 @@ def main():
file = find_pep(pep)
peptxt.append(file)
newfile = make_html(file, verbose=verbose)
- html.append(newfile)
+ if newfile:
+ html.append(newfile)
if browse and not update:
browse_file(pep)
else:
# do them all
peptxt = []
+ html = []
files = glob.glob("pep-*.txt")
files.sort()
for file in files:
peptxt.append(file)
- make_html(file, verbose=verbose)
- html = ["pep-*.html"]
+ newfile = make_html(file, verbose=verbose)
+ if newfile:
+ html.append(newfile)
if browse and not update:
browse_file("0")