Status: Active
Type: Informational
+Content-Type: text/x-rst
Created: 13-Jul-2000
"""
-intro = u"""
- This PEP contains the index of all Python Enhancement Proposals,
- known as PEPs. PEP numbers are assigned by the PEP editors, and
- once assigned are never changed[1]. The Mercurial history[2] of
- the PEP texts represent their historical record.
+intro = """\
+This PEP contains the index of all Python Enhancement Proposals,
+known as PEPs. PEP numbers are assigned by the PEP editors, and
+once assigned are never changed [1_]. The version control history [2_] of
+the PEP texts represent their historical record.
"""
-references = u"""
- [1] PEP 1: PEP Purpose and Guidelines
- [2] View PEP history online
- https://hg.python.org/peps/
+references = """\
+.. [1] PEP 1: PEP Purpose and Guidelines
+.. [2] View PEP history online: https://github.com/python/peps
"""
-footer = u"""
-Local Variables:
-mode: indented-text
-indent-tabs-mode: nil
-sentence-end-double-space: t
-fill-column: 70
-coding: utf-8
-End:"""
+footer = """\
+..
+ Local Variables:
+ mode: indented-text
+ indent-tabs-mode: nil
+ sentence-end-double-space: t
+ fill-column: 70
+ coding: utf-8
+ End:\
+"""
diff --git a/pep0/output.py b/pep0/output.py
index 3f02abc51..10024c221 100644
--- a/pep0/output.py
+++ b/pep0/output.py
@@ -26,15 +26,13 @@ RESERVED = [
indent = u' '
-def write_column_headers(output):
+def emit_column_headers(output):
"""Output the column headers for the PEP indices."""
- column_headers = {'status': u'', 'type': u'', 'number': u'num',
- 'title': u'title', 'authors': u'owner'}
+ column_headers = {'status': '.', 'type': '.', 'number': 'PEP',
+ 'title': 'PEP Title', 'authors': 'PEP Author(s)'}
+ print(constants.table_separator, file=output)
print(constants.column_format % column_headers, file=output)
- underline_headers = {}
- for key, value in column_headers.items():
- underline_headers[key] = constants.text_type(len(value) * '-')
- print(constants.column_format % underline_headers, file=output)
+ print(constants.table_separator, file=output)
def sort_peps(peps):
@@ -42,6 +40,7 @@ def sort_peps(peps):
and essentially dead."""
meta = []
info = []
+ provisional = []
accepted = []
open_ = []
finished = []
@@ -74,6 +73,8 @@ def sort_peps(peps):
info.append(pep)
else:
historical.append(pep)
+ elif pep.status == 'Provisional':
+ provisional.append(pep)
elif pep.status in ('Accepted', 'Active'):
accepted.append(pep)
elif pep.status == 'Final':
@@ -82,14 +83,15 @@ def sort_peps(peps):
raise PEPError("unsorted (%s/%s)" %
(pep.type_, pep.status),
pep.filename, pep.number)
- return meta, info, accepted, open_, finished, historical, deferred, dead
+ return (meta, info, provisional, accepted, open_,
+ finished, historical, deferred, dead)
def verify_email_addresses(peps):
authors_dict = {}
for pep in peps:
for author in pep.authors:
- # If this is the first time we have come across an author, add him.
+ # If this is the first time we have come across an author, add them.
if author not in authors_dict:
authors_dict[author] = [author.email]
else:
@@ -129,112 +131,160 @@ def sort_authors(authors_dict):
def normalized_last_first(name):
return len(unicodedata.normalize('NFC', name.last_first))
+def emit_title(text, anchor, output, *, symbol="="):
+ print(".. _{anchor}:\n".format(anchor=anchor), file=output)
+ print(text, file=output)
+ print(symbol*len(text), file=output)
+ print(file=output)
+
+def emit_subtitle(text, anchor, output):
+ emit_title(text, anchor, output, symbol="-")
+
+def emit_pep_category(output, category, anchor, peps):
+ emit_subtitle(category, anchor, output)
+ emit_column_headers(output)
+ for pep in peps:
+ print(pep, file=output)
+ print(constants.table_separator, file=output)
+ print(file=output)
def write_pep0(peps, output=sys.stdout):
+ # PEP metadata
today = datetime.date.today().strftime("%Y-%m-%d")
print(constants.header % today, file=output)
print(file=output)
- print(u"Introduction", file=output)
+ # Introduction
+ emit_title("Introduction", "intro", output)
print(constants.intro, file=output)
print(file=output)
- print(u"Index by Category", file=output)
+ # PEPs by category
+ (meta, info, provisional, accepted, open_,
+ finished, historical, deferred, dead) = sort_peps(peps)
+ emit_title("Index by Category", "by-category", output)
+ emit_pep_category(
+ category="Meta-PEPs (PEPs about PEPs or Processes)",
+ anchor="by-category-meta",
+ peps=meta,
+ output=output,
+ )
+ emit_pep_category(
+ category="Other Informational PEPs",
+ anchor="by-category-other-info",
+ peps=info,
+ output=output,
+ )
+ emit_pep_category(
+ category="Provisional PEPs (provisionally accepted; interface may still change)",
+ anchor="by-category-provisional",
+ peps=provisional,
+ output=output,
+ )
+ emit_pep_category(
+ category="Accepted PEPs (accepted; may not be implemented yet)",
+ anchor="by-category-accepted",
+ peps=accepted,
+ output=output,
+ )
+ emit_pep_category(
+ category="Open PEPs (under consideration)",
+ anchor="by-category-open",
+ peps=open_,
+ output=output,
+ )
+ emit_pep_category(
+ category="Finished PEPs (done, with a stable interface)",
+ anchor="by-category-finished",
+ peps=finished,
+ output=output,
+ )
+ emit_pep_category(
+ category="Historical Meta-PEPs and Informational PEPs",
+ anchor="by-category-historical",
+ peps=historical,
+ output=output,
+ )
+ emit_pep_category(
+ category="Deferred PEPs (postponed pending further research or updates)",
+ anchor="by-category-deferred",
+ peps=deferred,
+ output=output,
+ )
+ emit_pep_category(
+ category="Abandoned, Withdrawn, and Rejected PEPs",
+ anchor="by-category-abandoned",
+ peps=dead,
+ output=output,
+ )
print(file=output)
- write_column_headers(output)
- (meta, info, accepted, open_, finished,
- historical, deferred, dead) = sort_peps(peps)
- print(file=output)
- print(u" Meta-PEPs (PEPs about PEPs or Processes)", file=output)
- print(file=output)
- for pep in meta:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(u" Other Informational PEPs", file=output)
- print(file=output)
- for pep in info:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(u" Accepted PEPs (accepted; may not be implemented yet)", file=output)
- print(file=output)
- for pep in accepted:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(u" Open PEPs (under consideration)", file=output)
- print(file=output)
- for pep in open_:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(u" Finished PEPs (done, implemented in code repository)", file=output)
- print(file=output)
- for pep in finished:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(u" Historical Meta-PEPs and Informational PEPs", file=output)
- print(file=output)
- for pep in historical:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(u" Deferred PEPs", file=output)
- print(file=output)
- for pep in deferred:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(u" Abandoned, Withdrawn, and Rejected PEPs", file=output)
- print(file=output)
- for pep in dead:
- print(constants.text_type(pep), file=output)
- print(file=output)
- print(file=output)
- print(u"Numerical Index", file=output)
- print(file=output)
- write_column_headers(output)
+ # PEPs by number
+ emit_title("Numerical Index", "by-pep-number", output)
+ emit_column_headers(output)
prev_pep = 0
for pep in peps:
if pep.number - prev_pep > 1:
print(file=output)
print(constants.text_type(pep), file=output)
prev_pep = pep.number
+ print(constants.table_separator, file=output)
print(file=output)
- print(file=output)
- print(u'Reserved PEP Numbers', file=output)
- print(file=output)
- write_column_headers(output)
+ # Reserved PEP numbers
+ emit_title('Reserved PEP Numbers', "reserved", output)
+ emit_column_headers(output)
for number, claimants in sorted(RESERVED):
print(constants.column_format % {
- 'type': '',
- 'status': '',
+ 'type': '.',
+ 'status': '.',
'number': number,
'title': 'RESERVED',
'authors': claimants,
}, file=output)
+ print(constants.table_separator, file=output)
print(file=output)
- print(file=output)
- print(u"Key", file=output)
- print(file=output)
- for type_ in PEP.type_values:
+ # PEP types key
+ emit_title("PEP Types Key", "type-key", output)
+ for type_ in sorted(PEP.type_values):
print(u" %s - %s PEP" % (type_[0], type_), file=output)
+ print(file=output)
print(file=output)
- for status in PEP.status_values:
- print(u" %s - %s proposal" % (status[0], status), file=output)
+ # PEP status key
+ emit_title("PEP Status Key", "status-key", output)
+ for status in sorted(PEP.status_values):
+ # Draft PEPs have no status displayed, Active shares a key with Accepted
+ if status in ("Active", "Draft"):
+ continue
+ if status == "Accepted":
+ msg = " A - Accepted (Standards Track only) or Active proposal"
+ else:
+ msg = " {status[0]} - {status} proposal".format(status=status)
+ print(msg, file=output)
+ print(file=output)
print(file=output)
- print(file=output)
- print(u"Owners", file=output)
- print(file=output)
+ # PEP owners
+ emit_title("Authors/Owners", "authors", output)
authors_dict = verify_email_addresses(peps)
max_name = max(authors_dict.keys(), key=normalized_last_first)
max_name_len = len(max_name.last_first)
- print(u" %s %s" % ('name'.ljust(max_name_len), 'email address'), file=output)
- print(u" %s %s" % ((len('name')*'-').ljust(max_name_len),
- len('email address')*'-'), file=output)
+ author_table_separator = "="*max_name_len + " " + "="*len("email address")
+ print(author_table_separator, file=output)
+ _author_header_fmt = "{name:{max_name_len}} Email Address"
+ print(_author_header_fmt.format(name="Name", max_name_len=max_name_len), file=output)
+ print(author_table_separator, file=output)
sorted_authors = sort_authors(authors_dict)
+ _author_fmt = "{author.last_first:{max_name_len}} {author_email}"
for author in sorted_authors:
# Use the email from authors_dict instead of the one from 'author' as
# the author instance may have an empty email.
- print((u" %s %s" %
- (author.last_first.ljust(max_name_len), authors_dict[author])), file=output)
+ _entry = _author_fmt.format(
+ author=author,
+ author_email=authors_dict[author],
+ max_name_len=max_name_len,
+ )
+ print(_entry, file=output)
+ print(author_table_separator, file=output)
print(file=output)
print(file=output)
- print(u"References", file=output)
- print(file=output)
+ # References for introduction footnotes
+ emit_title("References", "references", output)
print(constants.references, file=output)
print(constants.footer, file=output)
diff --git a/pep0/pep.py b/pep0/pep.py
index 1b84dd35a..b3194afc2 100644
--- a/pep0/pep.py
+++ b/pep0/pep.py
@@ -99,11 +99,11 @@ class Author(object):
name_parts = self.last.split()
for index, part in enumerate(name_parts):
if part[0].isupper():
+ base = u' '.join(name_parts[index:]).lower()
break
else:
- raise ValueError("last name missing a capital letter: %r"
- % name_parts)
- base = u' '.join(name_parts[index:]).lower()
+ # If no capitals, use the whole string
+ base = self.last.lower()
return unicodedata.normalize('NFKD', base).encode('ASCII', 'ignore')
def _last_name(self, full_name):
@@ -169,7 +169,8 @@ class PEP(object):
type_values = (u"Standards Track", u"Informational", u"Process")
# Valid values for the Status header.
# Active PEPs can only be for Informational or Process PEPs.
- status_values = (u"Accepted", u"Rejected", u"Withdrawn", u"Deferred",
+ status_values = (u"Accepted", u"Provisional",
+ u"Rejected", u"Withdrawn", u"Deferred",
u"Final", u"Active", u"Draft", u"Superseded")
def __init__(self, pep_file):
@@ -229,6 +230,11 @@ class PEP(object):
raise PEPError("Only Process and Informational PEPs may "
"have an Active status", pep_file.name,
self.number)
+ # Special case for Provisional PEPs.
+ if (status == u"Provisional" and self.type_ != "Standards Track"):
+ raise PEPError("Only Standards Track PEPs may "
+ "have a Provisional status", pep_file.name,
+ self.number)
self.status = status
# 'Author'.
authors_and_emails = self._parse_author(metadata['Author'])
diff --git a/pep2html.py b/pep2html.py
index a7799713e..86fbef425 100755
--- a/pep2html.py
+++ b/pep2html.py
@@ -235,7 +235,7 @@ def fixfile(inpath, input_lines, outfile):
else:
mailtos.append(part)
v = COMMASPACE.join(mailtos)
- elif k.lower() in ('replaces', 'replaced-by', 'requires'):
+ elif k.lower() in ('replaces', 'superseded-by', 'requires'):
otherpeps = ''
for otherpep in re.split(',?\s+', v):
otherpep = int(otherpep)
@@ -296,7 +296,7 @@ def fixfile(inpath, input_lines, outfile):
print(re.sub(
parts[1],
'%s' % (url, parts[1]),
- line, 1), end=' ', file=outfile)
+ line, 1), end='', file=outfile)
continue
elif parts and '@' in parts[-1]:
# This is a pep email address line, so filter it.
@@ -305,7 +305,7 @@ def fixfile(inpath, input_lines, outfile):
print('', file=outfile)
need_pre = 0
print(re.sub(
- parts[-1], url, line, 1), end=' ', file=outfile)
+ parts[-1], url, line, 1), end='', file=outfile)
continue
line = fixpat.sub(lambda x, c=inpath: fixanchor(c, x), line)
if need_pre:
@@ -409,7 +409,7 @@ class PEPHeaders(Transform):
for node in para:
if isinstance(node, nodes.reference):
node.replace_self(peps.mask_email(node, pep))
- elif name in ('replaces', 'replaced-by', 'requires'):
+ elif name in ('replaces', 'superseded-by', 'requires'):
newbody = []
space = nodes.Text(' ')
for refpep in re.split(r',?\s+', body.astext()):
diff --git a/pep2pyramid.py b/pep2pyramid.py
index 71f827951..e41891da0 100755
--- a/pep2pyramid.py
+++ b/pep2pyramid.py
@@ -224,7 +224,7 @@ def fixfile(inpath, input_lines, outfile):
else:
mailtos.append(part)
v = COMMASPACE.join(mailtos)
- elif k.lower() in ('replaces', 'replaced-by', 'requires'):
+ elif k.lower() in ('replaces', 'superseded-by', 'requires'):
otherpeps = ''
for otherpep in re.split(',?\s+', v):
otherpep = int(otherpep)
diff --git a/pep2rss.py b/pep2rss.py
index e0e5c86b0..71e2c413c 100755
--- a/pep2rss.py
+++ b/pep2rss.py
@@ -1,22 +1,23 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# usage: pep-hook.py $REPOS $REV
# (standard post-commit args)
import os, glob, time, datetime, stat, re, sys
-import codecs
import PyRSS2Gen as rssgen
RSS_PATH = os.path.join(sys.argv[1], 'peps.rss')
def firstline_startingwith(full_path, text):
- for line in codecs.open(full_path, encoding="utf-8"):
+ for line in open(full_path, encoding="utf-8"):
if line.startswith(text):
return line[len(text):].strip()
return None
-# get list of peps with creation time (from "Created:" string in pep .txt)
+# get list of peps with creation time
+# (from "Created:" string in pep .rst or .txt)
peps = glob.glob('pep-*.txt')
+peps.extend(glob.glob('pep-*.rst'))
def pep_creation_dt(full_path):
created_str = firstline_startingwith(full_path, 'Created:')
# bleh, I was hoping to avoid re but some PEPs editorialize
@@ -69,5 +70,5 @@ rss = rssgen.RSS2(
lastBuildDate = datetime.datetime.now(),
items = items)
-with open(RSS_PATH, 'w') as fp:
- fp.write(rss.to_xml())
+with open(RSS_PATH, 'w', encoding="utf-8") as fp:
+ fp.write(rss.to_xml(encoding="utf-8"))