[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