[Checkins] SVN: manuel/trunk/src/manuel/ switch to using regular Python classes for Manuel objects instead of

Benji York benji at zope.com
Sat Apr 25 09:27:02 EDT 2009


Log message for revision 99487:
  switch to using regular Python classes for Manuel objects instead of
  previous prototype-y style
  

Changed:
  U   manuel/trunk/src/manuel/README.txt
  U   manuel/trunk/src/manuel/__init__.py
  U   manuel/trunk/src/manuel/doctest.py
  U   manuel/trunk/src/manuel/footnote.py
  U   manuel/trunk/src/manuel/tests.py

-=-
Modified: manuel/trunk/src/manuel/README.txt
===================================================================
--- manuel/trunk/src/manuel/README.txt	2009-04-25 11:12:16 UTC (rev 99486)
+++ manuel/trunk/src/manuel/README.txt	2009-04-25 13:27:01 UTC (rev 99487)
@@ -20,9 +20,6 @@
 corresponding slot to which various implementations are attached.
 
     >>> import manuel
-    >>> m = manuel.Manuel()
-    >>> m
-    <manuel.Manuel object at 0x...>
 
 
 Parsing
@@ -131,8 +128,7 @@
 Now we can register a parser that will identify the regions we're interested in
 and create NumbersTest objects from the source text.
 
-    >>> @m.parser
-    ... def parse_numbers_test(document):
+    >>> def parse(document):
     ...     for region in document.find_regions(numbers_test_finder):
     ...         description = region.start_match.group('description')
     ...         numbers = map(
@@ -140,7 +136,7 @@
     ...         test = NumbersTest(description, numbers)
     ...         document.replace_region(region, test)
 
-    >>> document.parse_with(m)
+    >>> parse(document)
     >>> [region.source for region in document]
     ['This is our document, it has several lines.\n',
      'one: 1, 2, 3\n',
@@ -166,8 +162,7 @@
     ...         self.test = test
     ...         self.passed = passed
 
-    >>> @m.evaluater
-    ... def evaluate_numbers(document):
+    >>> def evaluate(document):
     ...     for region in document:
     ...         if not isinstance(region.parsed, NumbersTest):
     ...             continue
@@ -175,7 +170,7 @@
     ...         passed = sorted(test.numbers) == test.numbers
     ...         region.evaluated = NumbersResult(test, passed)
 
-    >>> document.evaluate_with(m)
+    >>> evaluate(document)
     >>> [region.evaluated for region in document]
     [None,
      <NumbersResult object at 0x...>,
@@ -191,8 +186,7 @@
 a message about whether or not our lists of numbers are sorted properly.  A
 formatting function returns None when it has no output, or a string otherwise.
 
-    >>> @m.formatter
-    ... def format(document):
+    >>> def format(document):
     ...     for region in document:
     ...         if not isinstance(region.evaluated, NumbersResult):
     ...             continue
@@ -204,20 +198,25 @@
 
 Since our test case passed we don't get anything out of the report function.
 
-    >>> document.format_with(m)
+    >>> format(document)
     >>> [region.formatted for region in document]
     [None, None, None, "the numbers aren't in sorted order: 3, 5, 1"]
 
 
-We'll want to use this Manuel object later, so lets stash it away
+Manuel Objects
+--------------
 
-    >>> sorted_numbers_manuel = m
+We'll want to use all these parse, evaluate, and format functions later, so we
+bundle them together into a Manuel object.
 
+    >>> sorted_numbers_manuel = manuel.Manuel(
+    ...     parsers=[parse], evaluaters=[evaluate], formatters=[format])
 
+
 Doctests
 ========
 
-We can use manuel to run doctests.  Let's create a simple doctest to
+We can use Manuel to run doctests.  Let's create a simple doctest to
 demonstrate with.
 
     >>> source = """This is my
@@ -374,15 +373,14 @@
 ==========
 
 Some functionality requires that code be called early or late in a phase.  The
-"timing" keyword parameter allows either "early" or "late" to be specified.
+"manuel_timing" attribute allows either "early" or "late" to be specified.
 
 Early functions are run first (in arbitrary order), then functions with no
 specified timing, then the late functions are called (again in arbitrary
 order).  This function also demonstrates the "copy" method of Region objects
 and the "insert_region_before" and "insert_region_after" methods of Documents.
 
-    >>> @m.parser(timing='late')
-    ... def cloner(document):
+    >>> def cloner_parser(document):
     ...     to_be_cloned = None
     ...     # find the region to clone
     ...     document_iter = iter(document)
@@ -407,6 +405,10 @@
     ...                 clone.provenance = 'cloned to go after'
     ...                 document.insert_region_after(region, clone)
 
+    >>> cloner_parser.manuel_timing = 'late'
+    >>> cloning_manuel = manuel.Manuel([cloner_parser])
+    >>> m.extend(cloning_manuel)
+
     >>> source = """\
     ... This is my clone:
     ...

Modified: manuel/trunk/src/manuel/__init__.py
===================================================================
--- manuel/trunk/src/manuel/__init__.py	2009-04-25 11:12:16 UTC (rev 99486)
+++ manuel/trunk/src/manuel/__init__.py	2009-04-25 13:27:01 UTC (rev 99487)
@@ -217,11 +217,14 @@
     def insert_region_after(self, marker_region, new_region):
         self.insert_region('after', marker_region, new_region)
 
-
     def do_with(self, things):
         """Private helper for other do_* functions.
         """
-        for timing, thing in sorted(things):
+        def key(f):
+            # "j" was chosen because it sorts between "early" and "late"
+            return getattr(f, 'manuel_timing', 'j')
+
+        for thing in sorted(things, key=key):
             thing(self)
 
     def parse_with(self, m):
@@ -253,35 +256,22 @@
 
 class Manuel(object):
 
-    def __init__(self):
-        self.parsers = []
-        self.evaluaters = []
-        self.formatters = []
+    def __init__(self, parsers=None, evaluaters=None, formatters=None):
+        if parsers is not None:
+            self.parsers = parsers
+        else:
+            self.parsers = []
 
-    def parser(self, func=None, timing=None):
-        return self.thinger(self.parsers, func, timing)
+        if evaluaters is not None:
+            self.evaluaters = evaluaters
+        else:
+            self.evaluaters = []
 
-    def evaluater(self, func=None, timing=None):
-        return self.thinger(self.evaluaters, func, timing)
+        if formatters is not None:
+            self.formatters = formatters
+        else:
+            self.formatters = []
 
-    def formatter(self, func=None, timing=None):
-        return self.thinger(self.formatters, func, timing)
-
-    def thinger(self, things, func, timing):
-        """Private helper for adding functions to a phase."""
-        if func is None:
-            # the decorator is being called prior to being used as a decorator,
-            # return a callable that can be called to provide the function
-            # to be decorated
-            return lambda func: self.thinger(things, func, timing=timing)
-
-        assert timing in ('early', 'late', None)
-        if timing == None:
-            # arbitrarily chosen string that sorts between "early" and "late"
-            timing = 'k'
-
-        things.append((timing, func))
-
     def extend(self, other):
         self.parsers.extend(other.parsers)
         self.evaluaters.extend(other.evaluaters)

Modified: manuel/trunk/src/manuel/doctest.py
===================================================================
--- manuel/trunk/src/manuel/doctest.py	2009-04-25 11:12:16 UTC (rev 99486)
+++ manuel/trunk/src/manuel/doctest.py	2009-04-25 13:27:01 UTC (rev 99487)
@@ -14,63 +14,66 @@
         return self
 
 
-def Manuel(optionflags=0, checker=None):
-    m = manuel.Manuel()
-    m.runner = doctest.DocTestRunner(optionflags=optionflags, checker=checker)
-    m.debug_runner = doctest.DebugRunner(optionflags=optionflags)
-    m.globs = SharedGlobs()
-    m.debug = False
-
-    @m.parser
-    def parse(document):
-        for region in list(document):
-            if region.parsed:
+def parse(document):
+    for region in list(document):
+        if region.parsed:
+            continue
+        region_start = region.lineno
+        region_end = region.lineno + region.source.count('\n')
+        for chunk in doctest.DocTestParser().parse(region.source):
+            if isinstance(chunk, basestring):
                 continue
-            region_start = region.lineno
-            region_end = region.lineno + region.source.count('\n')
-            for chunk in doctest.DocTestParser().parse(region.source):
-                if isinstance(chunk, basestring):
-                    continue
-                chunk_line_count = (chunk.source.count('\n')
-                    + chunk.want.count('\n'))
+            chunk_line_count = (chunk.source.count('\n')
+                + chunk.want.count('\n'))
 
-                split_line_1 = region_start + chunk.lineno
-                split_line_2 = split_line_1 + chunk_line_count
+            split_line_1 = region_start + chunk.lineno
+            split_line_2 = split_line_1 + chunk_line_count
 
-                # if there is some source we need to trim off the front...
-                if split_line_1 > region.lineno:
-                    _, region = document.split_region(region, split_line_1)
+            # if there is some source we need to trim off the front...
+            if split_line_1 > region.lineno:
+                _, region = document.split_region(region, split_line_1)
 
-                if split_line_2 <= region_end:
-                    found, region = document.split_region(region, split_line_2)
-                    document.replace_region(found, chunk)
+            if split_line_2 <= region_end:
+                found, region = document.split_region(region, split_line_2)
+                document.replace_region(found, chunk)
 
-                assert region in document
+            assert region in document
 
-    @m.evaluater
-    def evaluate(document):
-        for region in document:
-            if not isinstance(region.parsed, doctest.Example):
-                continue
-            result = DocTestResult()
-            test_name = os.path.split(document.location)[1]
-            if m.debug:
-                runner = m.debug_runner
-            else:
-                runner = m.runner
 
-            runner.DIVIDER = '' # disable unwanted result formatting
-            runner.run(
-                doctest.DocTest([region.parsed], m.globs, test_name,
-                    document.location, 0, None),
-                out=result.write, clear_globs=False)
-            region.evaluated = result
+def evaluate(m, document):
+    for region in document:
+        if not isinstance(region.parsed, doctest.Example):
+            continue
+        result = DocTestResult()
+        test_name = os.path.split(document.location)[1]
+        if m.debug:
+            runner = m.debug_runner
+        else:
+            runner = m.runner
 
-    @m.formatter
-    def format(document):
-        for region in document:
-            if not isinstance(region.evaluated, DocTestResult):
-                continue
-            region.formatted = region.evaluated.getvalue().lstrip()
+        runner.DIVIDER = '' # disable unwanted result formatting
+        runner.run(
+            doctest.DocTest([region.parsed], m.globs, test_name,
+                document.location, 0, None),
+            out=result.write, clear_globs=False)
+        region.evaluated = result
 
-    return m
+
+def format(document):
+    for region in document:
+        if not isinstance(region.evaluated, DocTestResult):
+            continue
+        region.formatted = region.evaluated.getvalue().lstrip()
+
+
+class Manuel(manuel.Manuel):
+
+    def __init__(self, optionflags=0, checker=None):
+        self.runner = doctest.DocTestRunner(optionflags=optionflags,
+            checker=checker)
+        self.debug_runner = doctest.DebugRunner(optionflags=optionflags)
+        self.globs = SharedGlobs()
+        self.debug = False
+        def evaluate_closure(document):
+            evaluate(self, document)
+        manuel.Manuel.__init__(self, [parse], [evaluate_closure], [format])

Modified: manuel/trunk/src/manuel/footnote.py
===================================================================
--- manuel/trunk/src/manuel/footnote.py	2009-04-25 11:12:16 UTC (rev 99486)
+++ manuel/trunk/src/manuel/footnote.py	2009-04-25 13:27:01 UTC (rev 99487)
@@ -18,60 +18,61 @@
         self.name = name
 
 
-def Manuel():
-    """Factory for Manuel objects that handle reST style footnotes.
-    """
-    m = manuel.Manuel()
+def find_footnote_references(document):
+    # find the markers that show where footnotes have been defined.
+    footnote_names = []
+    for region in document.find_regions(FOOTNOTE_DEFINITION_RE):
+        name = region.start_match.group(1)
+        document.replace_region(region, FootnoteDefinition(name))
+        footnote_names.append(name)
 
-    @m.parser(timing='early')
-    def find_footnote_references(document):
-        # find the markers that show where footnotes have been defined.
-        footnote_names = []
-        for region in document.find_regions(FOOTNOTE_DEFINITION_RE):
-            name = region.start_match.group(1)
-            document.replace_region(region, FootnoteDefinition(name))
-            footnote_names.append(name)
+    # find the markers that show where footnotes have been referenced.
+    for region in document.find_regions(FOOTNOTE_REFERENCE_LINE_RE):
+        assert region.source.count('\n') == 1
+        names = FOOTNOTE_REFERENCE_RE.findall(region.source)
+        for name in names:
+            if name not in footnote_names:
+                raise RuntimeError('Unknown footnote: %r' % name)
 
-        # find the markers that show where footnotes have been referenced.
-        for region in document.find_regions(FOOTNOTE_REFERENCE_LINE_RE):
-            assert region.source.count('\n') == 1
-            names = FOOTNOTE_REFERENCE_RE.findall(region.source)
-            for name in names:
-                if name not in footnote_names:
-                    raise RuntimeError('Unknown footnote: %r' % name)
+        assert names
+        document.replace_region(region, FootnoteReference(names))
 
-            assert names
-            document.replace_region(region, FootnoteReference(names))
+find_footnote_references.manuel_timing = 'early'
 
-    @m.parser(timing='late')
-    def do_footnotes(document):
-        """Copy footnoted items into their appropriate position.
-        """
-        # first find all the regions that are in footnotes
-        footnotes = {}
-        name = None
-        for region in list(document):
-            if isinstance(region.parsed, FootnoteDefinition):
-                name = region.parsed.name
-                footnotes[name] = []
-                document.remove_region(region)
-                continue
 
-            if END_OF_FOOTNOTE_RE.search(region.source):
-                name = None
+def do_footnotes(document):
+    """Copy footnoted items into their appropriate position.
+    """
+    # first find all the regions that are in footnotes
+    footnotes = {}
+    name = None
+    for region in list(document):
+        if isinstance(region.parsed, FootnoteDefinition):
+            name = region.parsed.name
+            footnotes[name] = []
+            document.remove_region(region)
+            continue
 
-            if name is not None:
-                footnotes[name].append(region)
-                document.remove_region(region)
+        if END_OF_FOOTNOTE_RE.search(region.source):
+            name = None
 
-        # now make copies of the footnotes in the right places
-        for region in list(document):
-            if not isinstance(region.parsed, FootnoteReference):
-                continue
-            names = region.parsed.names
-            for name in names:
-                for footnoted in footnotes[name]:
-                    document.insert_region_before(region, footnoted.copy())
+        if name is not None:
+            footnotes[name].append(region)
             document.remove_region(region)
 
-    return m
+    # now make copies of the footnotes in the right places
+    for region in list(document):
+        if not isinstance(region.parsed, FootnoteReference):
+            continue
+        names = region.parsed.names
+        for name in names:
+            for footnoted in footnotes[name]:
+                document.insert_region_before(region, footnoted.copy())
+        document.remove_region(region)
+
+do_footnotes.manuel_timing = 'late'
+
+
+class Manuel(manuel.Manuel):
+    def __init__(self):
+        manuel.Manuel.__init__(self, [find_footnote_references, do_footnotes])

Modified: manuel/trunk/src/manuel/tests.py
===================================================================
--- manuel/trunk/src/manuel/tests.py	2009-04-25 11:12:16 UTC (rev 99486)
+++ manuel/trunk/src/manuel/tests.py	2009-04-25 13:27:01 UTC (rev 99487)
@@ -6,6 +6,7 @@
 import unittest
 
 doctest = manuel.absolute_import('doctest')
+#from zope.testing import doctest
 
 def test_suite():
     optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS



More information about the Checkins mailing list