[Python-checkins] r54185 - sandbox/trunk/pep0/TODO sandbox/trunk/pep0/pep0.py sandbox/trunk/pep0/test_pep0.py
brett.cannon
python-checkins at python.org
Tue Mar 6 23:43:19 CET 2007
Author: brett.cannon
Date: Tue Mar 6 23:43:15 2007
New Revision: 54185
Added:
sandbox/trunk/pep0/TODO (contents, props changed)
sandbox/trunk/pep0/pep0.py (contents, props changed)
sandbox/trunk/pep0/test_pep0.py (contents, props changed)
Log:
Initial checkin. No where near complete, but can at least parse metadata
headers of PEPs.
Added: sandbox/trunk/pep0/TODO
==============================================================================
--- (empty file)
+++ sandbox/trunk/pep0/TODO Tue Mar 6 23:43:15 2007
@@ -0,0 +1,25 @@
+In script:
+* Read PEPs as UTF-8, not ASCII.
+* Output static text for PEP 0.
+ + Store Owners list in a data structure.
+ - Support nicknames for PEP listing (e.g., "Guido van Rossum" ->
+ "GvR").
+ - Care about making sure that email addresses are up-to-date between
+ PEPs and PEP 0?
+ - Worth keeping emails in both PEP 0 and individual PEPs, or just make
+ PEP 0 master and leave out of PEPs so that single place can be
+ maintained and considered up-to-date?
+ + Store Key in data structure for easy mapping?
+ - Would allow for easy validation that metadata is correct in PEPs.
+* Output PEP 0 with numerical PEP index.
+* Output PEP 0 with special sections.
+
+For PEPs:
+* Define (and enforce) consistency in Author field.
+* Empty PEPs are not in any way identified within the PEPs themselves.
+ + Get ride of section and just consider rejected?
+ - No longer accept empty PEPs, right?
+* Meta-PEPs are not noted as such within the PEPs.
+ + Add a "Meta" option for "Type"?
+* Fix inconsistent usage of Status field.
+ + Some PEPs just say "Standard"; missing "Track".
Added: sandbox/trunk/pep0/pep0.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pep0/pep0.py Tue Mar 6 23:43:15 2007
@@ -0,0 +1,125 @@
+"""Auto-generate PEP 0 (PEP index). """
+from __future__ import with_statement
+import os
+
+def consume_headers(directory='.'):
+ """Pull out metadata for every PEP in the specified directory and return
+ them in a list sorted by PEP name."""
+ peps = []
+ for file_name in os.listdir(directory):
+ if file_name.startswith('pep-') and file_name.endswith('.txt'):
+ peps.append(consume_pep(os.path.join(directory, file_name)))
+ peps.sort(key=lambda pep: pep['PEP'])
+ return peps
+
+def consume_pep(path):
+ """Consume the specified file as a PEP to get its metadata."""
+ pep_info = {}
+ with open(path, 'rU') as pep_file:
+ try:
+ for line in pep_file:
+ if line == '\n':
+ break
+ elif line[1].isspace():
+ type_ = parse_metadata(pep_info, line, type_)
+ else:
+ type_ = parse_metadata(pep_info, line)
+ except Exception:
+ print "In", pep_file
+ raise
+ return pep_info
+
+def parse_metadata(pep_info, line, previous_type=None):
+ """Parse the given line for PEP metadata, adding on to existing metadata if
+ previous_type is specified, returning the last type of metadata handled."""
+ if previous_type:
+ type_ = previous_type
+ data = line
+ else:
+ type_, data = line.split(':', 1)
+ type_ = type_.strip()
+ data = data.strip()
+ handler = handlers.get(type_, handle_generic)
+ result = handler(data)
+ if previous_type:
+ previous_data = pep_info[type_]
+ if not isinstance(previous_data, list):
+ previous_data = [previous_data]
+ pep_info[type_] = previous_data
+ previous_data.extend(result)
+ else:
+ pep_info[type_] = result
+ return type_
+
+def handle_generic(data):
+ """Default handler for PEP metadata."""
+ return data
+
+def handle_pep_num(data):
+ """Return the integer for the PEP number."""
+ return int(data)
+
+def handle_author(data):
+ """Return a list of author names."""
+ if '<' in data:
+ author, email = data.split('<', 1)
+ elif '(' in data:
+ email, author = data.split('(', 1)
+ author = author[:author.index(')')]
+ else:
+ author = data
+ return [author_name.strip() for author_name in author.split(',') if
+ author_name]
+
+def handle_csv(data):
+ """Handle the Post-History."""
+ return [value.strip() for value in data.split(',') if value]
+
+handlers = {'Author': handle_author,
+ 'PEP': handle_pep_num,
+ 'Post-History': handle_csv,
+ }
+
+def sort_peps(peps):
+ """Sort PEPs into meta, informational, accepted, open, finished, empty,
+ and essentially dead."""
+ meta = []
+ info = []
+ accepted = []
+ open_ = []
+ finished = []
+ empty = []
+ dead = []
+ for pep in peps:
+ # XXX not all meta PEPs are process PEPs.
+ if pep['Type'] == 'Process':
+ meta.append(pep)
+ elif pep['Type'] == 'Informational':
+ info.append(pep)
+ elif pep['Status'] == 'Accepted':
+ accepted.append(pep)
+ elif pep['Status'] == 'Draft':
+ open_.append(pep)
+ elif pep['Status'] == 'Final':
+ finished.append(pep)
+ # XXX empty
+ elif pep['Status'] in ('Rejected', 'Withdrawn', 'Deferred',
+ 'Incomplete'):
+ dead.append(pep)
+ return meta, info, accepted, open_, finished, empty, dead
+
+if __name__ == '__main__':
+ from sys import argv
+
+ if not argv[1:]:
+ directory = '.'
+ else:
+ directory = argv[1]
+ peps = consume_headers(directory)
+
+ status = set()
+ type_ = set()
+ for pep in peps:
+ status.add(pep['Status'])
+ type_.add(pep['Type'])
+ meta, info, accepted, open_, done, empty, dead = sort_peps(peps)
Added: sandbox/trunk/pep0/test_pep0.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pep0/test_pep0.py Tue Mar 6 23:43:15 2007
@@ -0,0 +1,187 @@
+from __future__ import with_statement
+import unittest
+from test import test_support
+import pep0
+from contextlib import contextmanager
+import os
+
+class HandlerTests(unittest.TestCase):
+
+ def test_handle_generic(self):
+ # Identity function.
+ for data in ('spam', 'spam,', '', 'spam, monty'):
+ self.failUnlessEqual(pep0.handle_generic(data), data)
+
+ def test_pep_num(self):
+ # String -> int.
+ num = 42
+ string_rep = str(num)
+ self.failUnlessEqual(pep0.handle_pep_num(string_rep), num)
+
+ def test_handle_csv(self):
+ # Split on commas.
+ data = ['a', 'b', 'c']
+ string_rep = ','.join(data)
+ self.failUnlessEqual(pep0.handle_csv(string_rep), data)
+ string_rep = ', '.join(data)
+ self.failUnlessEqual(pep0.handle_csv(string_rep), data)
+ string_rep = ', '.join(data) + ','
+ got = pep0.handle_csv(string_rep)
+ self.failUnlessEqual(got, data,
+ '%r != %r (using %r)' %
+ (got, data, string_rep))
+
+ def test_handle_author(self):
+ # Handle the various ways authors can be specified:
+ # Name <email>,
+ # email (name),
+ # Name.
+ author = "Guido van Rossum"
+ str_rep = "%s <email>" % author
+ self.failUnlessEqual(pep0.handle_author(str_rep), [author])
+ str_rep += ','
+ self.failUnlessEqual(pep0.handle_author(str_rep), [author])
+ str_rep = "email (%s)" % author
+ self.failUnlessEqual(pep0.handle_author(str_rep), [author])
+ str_rep += ','
+ self.failUnlessEqual(pep0.handle_author(str_rep), [author])
+ str_rep = author
+ self.failUnlessEqual(pep0.handle_author(str_rep), [author])
+ str_rep += ','
+ self.failUnlessEqual(pep0.handle_author(str_rep), [author])
+ authors = ["Guido van Rossum", "Brett Cannon"]
+ str_rep = ', '.join(authors)
+ self.failUnlessEqual(pep0.handle_author(str_rep), authors)
+
+
+class ParseMetaDataTests(unittest.TestCase):
+
+ def test_parse_known_formats(self):
+ # Handle specific metadata format types.
+ assert "PEP" in pep0.handlers
+ line = "PEP: 42"
+ info = {}
+ handled = pep0.parse_metadata(info, line)
+ self.failUnlessEqual(handled, "PEP")
+ self.failUnless("PEP" in info)
+ self.failUnlessEqual(info['PEP'], 42)
+
+ def test_parse_unknown_formats(self):
+ # If a format is not known then it should just be returned with
+ # whitespace stripped.
+ info = {}
+ type_ = "Spam"
+ data = "Monty Python"
+ line = "%s: %s" % (type_, data)
+ handled = pep0.parse_metadata(info, line)
+ self.failUnlessEqual(handled, type_)
+ self.failUnless(type_ in info)
+ self.failUnlessEqual(info[type_], data)
+
+ def test_multiline_formats(self):
+ # Deal with formats that can span multiple lines (e.g., authors and
+ # Post-History).
+ type_ = 'Prev'
+ info = {type_ : [1]}
+ data = 'A'
+ handled = pep0.parse_metadata(info, data, type_)
+ self.failUnlessEqual(handled, type_)
+ self.failUnlessEqual(len(info[type_]), 2)
+ self.failUnlessEqual(info[type_][1], data)
+
+
+ at contextmanager
+def test_file(path):
+ try:
+ open_file = open(path, 'w')
+ yield open_file
+ finally:
+ open_file.close()
+ if os.path.exists(path):
+ os.unlink(path)
+
+
+class ConsumePepTests(unittest.TestCase):
+
+ def test_single_line(self):
+ # Test a PEP that only has a single line of metadata.
+ type_ = 'Spam'
+ data = 'Monty'
+ with test_file(test_support.TESTFN) as pep_file:
+ pep_file.write('%s: %s\n' % (type_, data))
+ pep_file.write('\n')
+ pep_file.write('The PEP.')
+ pep_file.close()
+ metadata = pep0.consume_pep(test_support.TESTFN)
+ self.failUnless(type_ in metadata)
+ self.failUnlessEqual(metadata[type_], data)
+
+ def test_multi_line_authors(self):
+ # Make sure that metadata that takes multiple lines works when it
+ # is expected (e.g., Author and Post-History).
+ authors = ['A', 'B']
+ output = 'Author: ' + authors[0] + '\n'
+ output += ' ' + authors[1] + '\n'
+ with test_file(test_support.TESTFN) as pep_file:
+ pep_file.write(output)
+ pep_file.write('\n')
+ pep_file.write('The PEP.')
+ pep_file.close()
+ metadata = pep0.consume_pep(test_support.TESTFN)
+ self.failUnless('Author' in metadata)
+ self.failUnlessEqual(metadata['Author'], authors)
+
+ def test_multi_line_post_history(self):
+ # Post-History entries on multiple lines is fine.
+ dates = ['04-Jul-1776', '23-Oct-2007', '01-Jan-2001']
+ output = 'Post-History: ' + ', '.join(dates[:2]) + '\n'
+ output += ' ' + dates[2] + '\n'
+ with test_file(test_support.TESTFN) as pep_file:
+ pep_file.write(output)
+ pep_file.write('\n')
+ pep_file.write('The PEP.')
+ pep_file.close()
+ metadata = pep0.consume_pep(test_support.TESTFN)
+ self.failUnless('Post-History' in metadata)
+ self.failUnlessEqual(metadata['Post-History'], dates)
+
+ def test_utf8(self):
+ # Need to handle text as UTF-8.
+ # XXX
+ pass
+
+
+class PepSortTests(unittest.TestCase):
+
+ def test_meta(self):
+ pass
+
+ def test_info(self):
+ pass
+
+ def test_accepted(self):
+ pass
+
+ def test_open(self):
+ pass
+
+ def test_finished(self):
+ pass
+
+ def test_empty(self):
+ pass
+
+ def test_dead(self):
+ pass
+
+
+def test_main():
+ test_support.run_unittest(
+ HandlerTests,
+ ParseMetaDataTests,
+ ConsumePepTests,
+ )
+
+
+if __name__ == '__main__':
+ test_main()
More information about the Python-checkins
mailing list