[Checkins] SVN: manuel/trunk/ major docs rework, added Sphinx, still needs more user-oriented docs
Benji York
benji at zope.com
Mon Jun 15 20:06:35 EDT 2009
Log message for revision 101027:
major docs rework, added Sphinx, still needs more user-oriented docs
Changed:
_U manuel/trunk/
U manuel/trunk/README.txt
U manuel/trunk/buildout.cfg
U manuel/trunk/setup.py
A manuel/trunk/sphinx/
A manuel/trunk/sphinx/conf.py
A manuel/trunk/src/index.txt
A manuel/trunk/src/intro.txt
U manuel/trunk/src/manuel/README.txt
U manuel/trunk/src/manuel/bugs.txt
U manuel/trunk/src/manuel/code-block.txt
U manuel/trunk/src/manuel/codeblock.py
U manuel/trunk/src/manuel/table-example.txt
U manuel/trunk/src/manuel/tests.py
-=-
Property changes on: manuel/trunk
___________________________________________________________________
Modified: svn:ignore
- develop-eggs
Python-trunk
dist
bin
parts
.installed.cfg
+ develop-eggs
Python-trunk
docs
dist
bin
parts
.installed.cfg
Modified: manuel/trunk/README.txt
===================================================================
--- manuel/trunk/README.txt 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/README.txt 2009-06-16 00:06:35 UTC (rev 101027)
@@ -1 +1 @@
-See src/manuel/README.txt
+See src/intro.txt
Modified: manuel/trunk/buildout.cfg
===================================================================
--- manuel/trunk/buildout.cfg 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/buildout.cfg 2009-06-16 00:06:35 UTC (rev 101027)
@@ -1,6 +1,6 @@
[buildout]
develop = .
-parts = test interpreter
+parts = test interpreter sphinx-docs-html build-docs
allow-picked-versions = false
use-dependency-links = false
versions = versions
@@ -16,9 +16,37 @@
eggs = manuel
interpreter = py
+# generate a script that will build the user docs (HTML)
+[sphinx-docs-html]
+recipe = zc.recipe.egg:script
+eggs =
+ docutils
+ Sphinx
+ PILwoTk
+scripts = sphinx-build=docs
+base-sphinx-args = ('-W -N -c ${buildout:directory}/sphinx ${buildout:directory}/src ${buildout:directory}/docs'.split())
+arguments = sys.argv + ${sphinx-docs-html:base-sphinx-args}
+initialization =
+ # XXX for some reason "import Image" doesn't work for us; hack it
+ import PIL.Image
+ sys.modules['Image'] = PIL.Image
+
+# build the (HTML) user docs each time the buildout is run
+[build-docs]
+recipe = iw.recipe.cmd
+on_install = true
+on_update = true
+cmds = ${buildout:directory}/bin/docs
+
[versions]
+iw.recipe.cmd = 0.3
+docutils = 0.5
setuptools = 0.6c9
+PILwoTk = 1.1.6.3
zc.buildout = 1.2.1
+Sphinx = 0.6.1
+Jinja2 = 2.1.1
+Pygments = 1.0
zc.recipe.egg = 1.2.2
zc.recipe.testrunner = 1.2.0
zope.interface = 3.5.1
Modified: manuel/trunk/setup.py
===================================================================
--- manuel/trunk/setup.py 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/setup.py 2009-06-16 00:06:35 UTC (rev 101027)
@@ -1,11 +1,8 @@
+from setuptools import setup, find_packages
import os
-from setuptools import setup, find_packages
-
long_description = (
- open(os.path.join('src', 'manuel', 'README.txt')).read()
- #+ '\n\n'
- #+ open(os.path.join('src', 'manuel', 'table-example.txt')).read()
+ open(os.path.join('README.txt')).read()
+ '\n\n'
+ open('CHANGES.txt').read()
)
Added: manuel/trunk/sphinx/conf.py
===================================================================
--- manuel/trunk/sphinx/conf.py (rev 0)
+++ manuel/trunk/sphinx/conf.py 2009-06-16 00:06:35 UTC (rev 101027)
@@ -0,0 +1,14 @@
+source_suffix = '.txt'
+master_doc = 'index'
+project = 'Manuel'
+copyright = 'Benji York'
+version = '1'
+release = '1'
+today_fmt = '%Y-%m-%d'
+pygments_style = 'sphinx'
+
+html_last_updated_fmt = '%Y-%m-%d'
+html_title = 'Manuel Documentation'
+
+todo_include_todos = False
+exclude_dirnames = ['manuel.egg-info']
Property changes on: manuel/trunk/sphinx/conf.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: svn:eol-style
+ native
Added: manuel/trunk/src/index.txt
===================================================================
--- manuel/trunk/src/index.txt (rev 0)
+++ manuel/trunk/src/index.txt 2009-06-16 00:06:35 UTC (rev 101027)
@@ -0,0 +1,15 @@
+====================
+Manuel documentation
+====================
+
+.. toctree::
+ :maxdepth: 1
+ :numbered:
+
+ intro.txt
+ manuel/README.txt
+ manuel/table-example.txt
+ manuel/code-block.txt
+ manuel/footnote.txt
+ manuel/isolation.txt
+ manuel/bugs.txt
Property changes on: manuel/trunk/src/index.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: manuel/trunk/src/intro.txt
===================================================================
--- manuel/trunk/src/intro.txt (rev 0)
+++ manuel/trunk/src/intro.txt 2009-06-16 00:06:35 UTC (rev 101027)
@@ -0,0 +1,38 @@
+Introduction
+============
+
+Manuel lets you mix and match traditional doctests with new test syntax that
+you create or is included in Manuel.
+
+For example, if you've ever wanted to include a large chunk of Python in a
+doctest but were irritated by all the ">>>" and "..." prompts required, you'd
+like the "manuel.codeblock" module. It lets you include code using Sphinx-style
+".. code-block:: python" directives, like so:
+
+.. code-block:: python
+
+ import foo
+
+ def my_func(bar):
+ return foo.baz(bar)
+
+Incidentally, the implementation of manuel.codeblock is only 24 lines of code.
+
+For an example of creating your own test syntax, take a look at
+table-example.txt or for full details, README.txt.
+
+
+Included Functionality
+======================
+
+Manuel includes several extentions to doctest out of the box.
+
+manuel.codeblock
+ executes code in ".. code-block:: python" blocks
+
+manuel.footnote
+ executes code in reST-style footnodes each time they're referenced (good
+ for getting incedental code out of the main flow of a document)
+
+manuel.isolation
+ makes it easier to have test isolation in doctests
Property changes on: manuel/trunk/src/intro.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: manuel/trunk/src/manuel/README.txt
===================================================================
--- manuel/trunk/src/manuel/README.txt 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/src/manuel/README.txt 2009-06-16 00:06:35 UTC (rev 101027)
@@ -1,22 +1,12 @@
-Contents
-========
+Theory of Operation
+===================
-.. contents::
+Manuel parses documents (tests), evaluates their contents, then formats the
+result of the evaluation. The functionality is accessed via the "manuel"
+package.
-
-Overview
-========
-
-In short, Manuel parses documents (tests), evaluates their contents, then
-formats the result of the evaluation.
-
-The core functionality is accessed through an instance of a Manuel object. It
-is used to build up the handling of a document type. Each phase has a
-corresponding slot to which various implementations are attached.
-
>>> import manuel
-
Parsing
-------
@@ -32,7 +22,7 @@
>>> document = manuel.Document(source)
For example purposes we will create a type of test that consists of a sequence
-of numbers so lets create a NumbersTest object to represent the parsed list.
+of numbers. Lets create a NumbersTest object to represent the parsed list.
>>> class NumbersTest(object):
... def __init__(self, description, numbers):
@@ -99,6 +89,8 @@
...
ValueError: Regions must start at the begining of a line.
+.. more "whole-line" tests.
+
>>> document.find_regions('three')
Traceback (most recent call last):
...
@@ -149,23 +141,26 @@
After a document has been parsed the resulting tests are evaluated. Unlike
parsing and formatting, evaluation is done one region at a time, in the order
-that the regions appear in the document. Manuel provides another method to
-evaluate tests. Lets define a function to evaluate NumberTests. The function
-determines whether or not the numbers are in sorted order and records the
-result along with the description of the list of numbers.
+that the regions appear in the document. Lets define a function to evaluate
+NumberTests. The function determines whether or not the numbers are in sorted
+order and records the result along with the description of the list of numbers.
- >>> class NumbersResult(object):
- ... def __init__(self, test, passed):
- ... self.test = test
- ... self.passed = passed
+.. code-block:: python
- >>> def evaluate(region, document, globs):
- ... if not isinstance(region.parsed, NumbersTest):
- ... return
- ... test = region.parsed
- ... passed = sorted(test.numbers) == test.numbers
- ... region.evaluated = NumbersResult(test, passed)
+ class NumbersResult(object):
+ def __init__(self, test, passed):
+ self.test = test
+ self.passed = passed
+ def evaluate(region, document, globs):
+ if not isinstance(region.parsed, NumbersTest):
+ return
+ test = region.parsed
+ passed = sorted(test.numbers) == test.numbers
+ region.evaluated = NumbersResult(test, passed)
+
+.. a test of the above
+
>>> for region in document:
... evaluate(region, document, {})
>>> [region.evaluated for region in document]
@@ -183,18 +178,21 @@
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.
- >>> def format(document):
- ... for region in document:
- ... if not isinstance(region.evaluated, NumbersResult):
- ... continue
- ... result = region.evaluated
- ... if not result.passed:
- ... region.formatted = (
- ... "the numbers aren't in sorted order: "
- ... + ', '.join(map(str, result.test.numbers)))
+.. code-block:: python
-Since our test case passed we don't get anything out of the report function.
+ def format(document):
+ for region in document:
+ if not isinstance(region.evaluated, NumbersResult):
+ continue
+ result = region.evaluated
+ if not result.passed:
+ region.formatted = (
+ "the numbers aren't in sorted order: "
+ + ', '.join(map(str, result.test.numbers)))
+Since one of the test cases failed we get an appropriate message out of the
+formatter.
+
>>> format(document)
>>> [region.formatted for region in document]
[None, None, None, "the numbers aren't in sorted order: 3, 5, 1"]
@@ -211,7 +209,7 @@
Doctests
-========
+--------
We can use Manuel to run doctests. Let's create a simple doctest to
demonstrate with.
@@ -261,7 +259,6 @@
... >>> 1 + 1
... 42
... """)
-
>>> document.process_with(m, globs={})
>>> print document.formatted()
File "<memory>", line 4, in <memory>
@@ -301,14 +298,14 @@
...
... >>> string.digits
... '0123456789'
- ...
+ ...
... """)
>>> document.process_with(m, globs={})
>>> print document.formatted()
Combining Test Types
-====================
+--------------------
Now that we have both doctests and the silly "sorted numbers" tests, lets
create a single document that has both.
@@ -369,7 +366,7 @@
Priorities
-==========
+----------
Some functionality requires that code be called early or late in a phase. The
"timing" decorator allows either EARLY or LATE to be specified.
@@ -411,9 +408,9 @@
... This is my clone:
...
... clone: 1, 2, 3
- ...
+ ...
... I want some copies of my clone.
- ...
+ ...
... For example, I'd like a clone before *here*.
...
... I'd also like a clone after *here*.
@@ -437,21 +434,23 @@
First we'll create an evaluater that includes pertinant variable binding
information on failures.
- >>> import doctest
+.. code-block:: python
- >>> def informative_evaluater(region, document, globs):
- ... if not isinstance(region.parsed, doctest.Example):
- ... return
- ... if region.evaluated.getvalue():
- ... info = ''
- ... for name in sorted(globs):
- ... if name in region.parsed.source:
- ... info += '\n ' + name + ' = ' + repr(globs[name])
- ...
- ... if info:
- ... region.evaluated.write('Additional Information:')
- ... region.evaluated.write(info)
+ import doctest
+ def informative_evaluater(region, document, globs):
+ if not isinstance(region.parsed, doctest.Example):
+ return
+ if region.evaluated.getvalue():
+ info = ''
+ for name in sorted(globs):
+ if name in region.parsed.source:
+ info += '\n ' + name + ' = ' + repr(globs[name])
+
+ if info:
+ region.evaluated.write('Additional Information:')
+ region.evaluated.write(info)
+
To do that we'll start with an instance of manuel.doctest.Manuel and add in our
additional functionality.
@@ -473,6 +472,9 @@
... 5
... """)
+When we run the document through our Manuel instance, we see the additional
+information.
+
>>> document.process_with(m, globs={})
>>> print document.formatted()
File "<memory>", line 10, in <memory>
Modified: manuel/trunk/src/manuel/bugs.txt
===================================================================
--- manuel/trunk/src/manuel/bugs.txt 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/src/manuel/bugs.txt 2009-06-16 00:06:35 UTC (rev 101027)
@@ -1,8 +1,5 @@
-Miscellaneous Tests
-===================
-
Fixed Bugs
-----------
+==========
If a line of text matches both a "start" and "end" regular expression, no
exception should be raised.
Modified: manuel/trunk/src/manuel/code-block.txt
===================================================================
--- manuel/trunk/src/manuel/code-block.txt 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/src/manuel/code-block.txt 2009-06-16 00:06:35 UTC (rev 101027)
@@ -1,4 +1,4 @@
-Code blocks
+Code Blocks
===========
`Sphinx <http://sphinx.pocoo.org/>`_ and other docutils `extensions
Modified: manuel/trunk/src/manuel/codeblock.py
===================================================================
--- manuel/trunk/src/manuel/codeblock.py 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/src/manuel/codeblock.py 2009-06-16 00:06:35 UTC (rev 101027)
@@ -3,8 +3,11 @@
import textwrap
CODEBLOCK_START = re.compile(
- r'^\.\.\s*code-block::\s*python\s*$', re.MULTILINE)
+ r'(?<=\n\n)\.\.\s*code-block::?\s*python\s*\n', re.DOTALL)
+# XXX document code-blocks that don't get executed and code-blocks that are
+# really comments but get executed anyway (perhaps with better syntax for both)
+
# XXX should probably take end-of-file into account
CODEBLOCK_END = re.compile(r'^\S.*$', re.MULTILINE)
Modified: manuel/trunk/src/manuel/table-example.txt
===================================================================
--- manuel/trunk/src/manuel/table-example.txt 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/src/manuel/table-example.txt 2009-06-16 00:06:35 UTC (rev 101027)
@@ -140,13 +140,12 @@
False True True
True True True
===== ===== ======
-
>>> region.parsed
<Table object at ...>
Evaluating
-==========
+----------
Now that we can find and extract the tables from the source, we need to be able
to check them for correctness.
@@ -231,7 +230,7 @@
Formatting Errors
-=================
+-----------------
Now that we can parse the tables and evaluate them, we need to be able to
display the results in a readable fashion.
@@ -268,17 +267,22 @@
All Together Now
-================
+----------------
All the pieces (parsing, evaluating, and formatting) are available now, so we
just have to put them together into a single "Manuel" object.
- >>> m = manuel.Manuel(parsers=[parse_tables], evaluaters=[evaluate_table],
- ... formatters=[format_table_errors])
+.. code-block:: python
-Now we can create a fresh document and tell it to do all the above steps with
-our Manuel instance.
+ class Manuel(manuel.Manuel):
+ def __init__(self):
+ manuel.Manuel.__init__(self, [parse_tables], [evaluate_table],
+ [format_table_errors])
+Now we can create a fresh document and tell it to do all the above steps
+(parse, evaluate, format) an instance of our table example Manuel.
+
+ >>> m = Manuel()
>>> document = manuel.Document(source_with_errors, location='fake.txt')
>>> document.process_with(m, globs={})
>>> print document.formatted(),
@@ -292,15 +296,32 @@
>>> document.process_with(m, globs={})
>>> print document.formatted()
-If we wanted to use our new table tests in a file named "table-example.txt" and
-include them in a unittest TestSuite, it would look something like this:
+Unittest Integration
+--------------------
+
+If we wanted to use our new table tests in conjunction with unittest, we could
+put the code above in tableexample.py. Then to use the new syntax in a test
+file named "my-table-tests.txt" we can create a file named tests.py and build a
+TestSuite like so:
+
+.. this next chunk isn't actually executed by tests, so watch out
.. code-block:: python
import unittest
import manuel.testing
+ import tableexample
- suite = unittest.TestSuite()
+ def test_suite():
+ m = tableexample.Manuel()
+ return manuel.testing.TestSuite(m, 'my-table-tests.txt')
+
+.. this next bit is actually a reST comment, but it is run during tests anyway
+ (note the single colon instead of double colon)
+
+.. code-block: python
+
+ import unittest
suite = manuel.testing.TestSuite(m, 'table-example.txt')
If the tests are run (e.g., by a test runner), everything works.
Modified: manuel/trunk/src/manuel/tests.py
===================================================================
--- manuel/trunk/src/manuel/tests.py 2009-06-15 19:52:19 UTC (rev 101026)
+++ manuel/trunk/src/manuel/tests.py 2009-06-16 00:06:35 UTC (rev 101027)
@@ -17,19 +17,13 @@
suite = unittest.TestSuite()
tests = ['README.txt', 'footnote.txt', 'bugs.txt', 'code-block.txt',
- 'isolation.txt']
+ 'isolation.txt', 'table-example.txt']
- # Run the tests once with doctest.
- suite.addTest(
- doctest.DocFileSuite(optionflags=optionflags, checker=checker, *tests))
-
- # Run them again with Manuel's doctest support.
+ # run the tests with Manuel's doctest support
m = manuel.doctest.Manuel(optionflags=optionflags, checker=checker)
+ # add in the codeblock extension
+ m.extend(manuel.codeblock.Manuel())
+ # build the test suite
suite.addTest(manuel.testing.TestSuite(m, *tests))
- # Run the table example with doctest plus the codeblock extension.
- m = manuel.doctest.Manuel(optionflags=optionflags, checker=checker)
- m.extend(manuel.codeblock.Manuel())
- suite.addTest(manuel.testing.TestSuite(
- m, 'table-example.txt'))
return suite
More information about the Checkins
mailing list