[Zope-CVS] SVN: zope.tutorial/trunk/ My first stab at the necessary
abstraction of running a doctest as a
Stephan Richter
srichter at cosmos.phy.tufts.edu
Sun Nov 13 08:41:58 EST 2005
Log message for revision 40081:
My first stab at the necessary abstraction of running a doctest as a
tutorial. I am so unhappy with that, it's not even funny.
Changed:
U zope.tutorial/trunk/README.txt
U zope.tutorial/trunk/browser/configure.zcml
A zope.tutorial/trunk/browser/index.pt
D zope.tutorial/trunk/browser/tutorial.pt
A zope.tutorial/trunk/cli.py
U zope.tutorial/trunk/configure.zcml
A zope.tutorial/trunk/manager.py
A zope.tutorial/trunk/session.txt
U zope.tutorial/trunk/tests.py
U zope.tutorial/trunk/tutorial.py
-=-
Modified: zope.tutorial/trunk/README.txt
===================================================================
--- zope.tutorial/trunk/README.txt 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/README.txt 2005-11-13 13:41:57 UTC (rev 40081)
@@ -13,7 +13,7 @@
tutorials. This package provides the necessary framework to run testbrowser
tests inside a real browser in a tutorial style:
- >>> from zope.tutorial import tutorial
+ >>> from zope.tutorial import tutorial, manager
Tutorial
--------
@@ -28,12 +28,12 @@
All tutorials are managed by the tutorial manager, which also serves as an
entrance point in the Web UI.
- >>> manager = tutorial.TutorialManager()
+ >>> tm = manager.TutorialManager()
The tutorial manager implements the `IReadContainer` interface to query for
tutorials. Initially there are no tutorials:
- >>> manager.keys()
+ >>> tm.keys()
[]
Once we add some tutorials by registering soem utilities,
@@ -48,7 +48,7 @@
we have some results:
- >>> manager.items()
+ >>> tm.items()
[(u'tut1', <Tutorial title='Tutorial 1', file='tut1.txt'>),
(u'tut2', <Tutorial title='Tutorial 2', file='tut2.txt'>)]
@@ -60,13 +60,13 @@
an entrance point to the tutorial application. Once the namespace is created.
>>> parent = object()
- >>> namespace = tutorial.tutorialsNamespace(parent)
+ >>> namespace = manager.tutorialsNamespace(parent)
you can traverse the parent to the tutorial manager. If an empty name is
passed into the namespace, the manager is returned:
>>> namespace.traverse('')
- <zope.tutorial.tutorial.TutorialManager object at ...>
+ <zope.tutorial.manager.TutorialManager object at ...>
If a name is provided, then the actual tutorial is looked up:
Modified: zope.tutorial/trunk/browser/configure.zcml
===================================================================
--- zope.tutorial/trunk/browser/configure.zcml 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/browser/configure.zcml 2005-11-13 13:41:57 UTC (rev 40081)
@@ -3,8 +3,37 @@
xmlns:zope="http://namespaces.zope.org/zope"
i18n_domain="zope">
+ <resource
+ name="tutorial.css"
+ file="tutorial.css"
+ />
+
+ <page
+ for="*"
+ name="tutorial_macros"
+ permission="zope.View"
+ class=".tutorial.TutorialMacros"
+ allowed_interface="zope.interface.common.mapping.IItemMapping"
+ />
+
+ <page
+ for="*"
+ name="runner_macros"
+ permission="zope.View"
+ template="runner_macros.pt"
+ />
+
+ <page
+ name="index.html"
+ for="..interfaces.ITutorialManager"
+ class=".tutorial.TutorialsRunner"
+ template="index.pt"
+ permission="zope.View"
+ />
+
+ <!-- Make Selenium available -->
<resourceDirectory
- name="selenium-driver"
+ name="selenium"
directory="../selenium/javascript" />
</configure>
Copied: zope.tutorial/trunk/browser/index.pt (from rev 40074, zope.tutorial/trunk/browser/tutorial.pt)
===================================================================
--- zope.tutorial/trunk/browser/tutorial.pt 2005-11-12 18:29:16 UTC (rev 40074)
+++ zope.tutorial/trunk/browser/index.pt 2005-11-13 13:41:57 UTC (rev 40081)
@@ -0,0 +1,62 @@
+<html>
+ <head>
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" />
+ <title>Online Tutorial Viewer</title>
+ <link rel="stylesheet" type="text/css"
+ href="/@@/tutorial.css" />
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/jsunit/app/jsUnitCore.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/xmlextras.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/selenium-browserbot.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/selenium-api.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/selenium-commandhandlers.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/selenium-executionloop.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/selenium-seleneserunner.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/selenium-logging.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/htmlutils.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/xpath.js"></script>
+ <script
+ language="JavaScript" type="text/javascript"
+ src="/@@/selenium/user-extensions.js"></script>
+ </head>
+ <body>
+
+ <table width="100%" style="height: 100%;">
+ <tr>
+ <td width="30%" height="30%">
+ <metal:block use-macro="context/@@tutorial_macros/runner" />
+ </td>
+ <td width="70%" height="30%">
+ <b>Last Four Commands</b><br/>
+ <div id="commandList"></div>
+ </td>
+ </tr>
+ <tr>
+ <td colspan="2" height="70%">
+ <iframe name="myiframe" id="myiframe" src=""
+ height="100%" width="100%"></iframe>
+ </td>
+ </tr>
+ </table>
+
+ </body>
+</html>
Deleted: zope.tutorial/trunk/browser/tutorial.pt
===================================================================
--- zope.tutorial/trunk/browser/tutorial.pt 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/browser/tutorial.pt 2005-11-13 13:41:57 UTC (rev 40081)
@@ -1,64 +0,0 @@
-<html>
- <head>
- <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
- <title>Online Tutorial Viewer</title>
- <link rel="stylesheet" type="text/css" href="selenium.css" />
- <script
- language="JavaScript" type="text/javascript"
- src="jsunit/app/jsUnitCore.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="xmlextras.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="selenium-browserbot.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="selenium-api.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="selenium-commandhandlers.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="selenium-executionloop.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="selenium-seleneserunner.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="selenium-logging.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="htmlutils.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="xpath.js"></script>
- <script
- language="JavaScript" type="text/javascript"
- src="user-extensions.js"></script>
- </head>
-
- <body onLoad="runTest()">
-
- <table border="1" style="height: 100%;">
- <tr>
- <td width="50%" height="30%">
- <form action="">
- <label id="context" name="context"></label>
- </form>
- </td>
- <td width="50%" height="30%">
- <b>Last Four Commands</b><br/>
- <div id="commandList"></div>
- </td>
- </tr>
- <tr>
- <td colspan="2" height="70%">
- <iframe name="myiframe" id="myiframe" src=""
- height="100%" width="100%"></iframe>
- </td>
- </tr>
- </table>
-
- </body>
-</html>
Added: zope.tutorial/trunk/cli.py
===================================================================
--- zope.tutorial/trunk/cli.py 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/cli.py 2005-11-13 13:41:57 UTC (rev 40081)
@@ -0,0 +1,79 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Simple Text Controller implementation.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import types
+import zope.interface
+
+from zope.tutorial import interfaces
+
+class SimpleCLIController(object):
+ """A dummy Command-line based controller.
+
+ Instead of running the tests, this controller simply displays the text and
+ examples. This makes this controller well-suited for testing.
+ """
+ #zope.interface.implements(interfaces.ITutorialController)
+
+ PYTHON_PROMPT = '>>> '
+ PYTHON_CONTINUE = '... '
+
+ def __init__(self, session):
+ self.session = session
+ self.running = False
+
+ def start(self):
+ """See interfaces.ITutorialController"""
+ self.running = True
+ print 'Starting Tutorial: ' + self.session.tutorial.title
+
+ def end(self):
+ """See interfaces.ITutorialController"""
+ print '---------- The End ----------'
+ self.running = False
+
+ def display(self, text):
+ """See interfaces.ITutorialController"""
+ print
+ print text.strip()
+ print
+
+ def run(self, example):
+ """See interfaces.ITutorialController"""
+ # Prepare the source and print it
+ source = example.source.strip()
+ source = ' '*example.indent + self.PYTHON_PROMPT + source
+ source = source.replace(
+ '\n', '\n' + ' '*example.indent + self.PYTHON_CONTINUE)
+ print source
+
+ # Prepare the expected output and print it
+ if example.want:
+ want = example.want.strip()
+ want = ' '*example.indent + want
+ want = want.replace('\n', '\n' + ' '*example.indent)
+ print want
+
+ def doNextStep(self):
+ """See interfaces.ITutorialController"""
+ step = self.session.getNextStep()
+ if isinstance(step, types.StringTypes):
+ self.display(step)
+ elif step is None:
+ self.end()
+ else:
+ self.run(step)
Property changes on: zope.tutorial/trunk/cli.py
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: zope.tutorial/trunk/configure.zcml
===================================================================
--- zope.tutorial/trunk/configure.zcml 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/configure.zcml 2005-11-13 13:41:57 UTC (rev 40081)
@@ -18,7 +18,7 @@
<!-- Setup of initial tutorials -->
- <include file="tutorials.zcml" />
+ <!--include file="tutorials.zcml" /-->
<!-- Browser Configuration -->
Added: zope.tutorial/trunk/manager.py
===================================================================
--- zope.tutorial/trunk/manager.py 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/manager.py 2005-11-13 13:41:57 UTC (rev 40081)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tutorial Manager Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import zope.interface
+from zope.app.apidoc import utilities
+from zope.app import location
+from zope.app import zapi
+
+from zope.tutorial import interfaces
+
+
+class TutorialManager(utilities.ReadContainerBase):
+ """TutorialManager"""
+ zope.interface.implements(interfaces.ITutorialManager,
+ location.interfaces.ILocation)
+
+ def __init__(self, parent=None):
+ self.__parent__ = parent
+ self.__name__ = '++tutorials++'
+
+ def get(self, key, default=None):
+ """See zope.app.container.interfaces.IReadContainer"""
+ utility = zapi.queryUtility(interfaces.ITutorial, key, default)
+ if utility != default:
+ location.locate(utility, self, key)
+ return utility
+
+ def items(self):
+ """See zope.app.container.interfaces.IReadContainer"""
+ items = list(zapi.getUtilitiesFor(interfaces.ITutorial))
+ items.sort()
+ utils = []
+ for key, value in items:
+ location.locate(value, self, key)
+ utils.append((key, value))
+ return utils
+
+
+class tutorialsNamespace(object):
+ """Used to traverse the `++tutorials++` namespace"""
+
+ def __init__(self, ob=None, request=None):
+ self.tutorialManager = TutorialManager(ob)
+
+ def traverse(self, name, ignore=None):
+ if name == '':
+ return self.tutorialManager
+ else:
+ return self.tutorialManager[name]
Property changes on: zope.tutorial/trunk/manager.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zope.tutorial/trunk/session.txt
===================================================================
--- zope.tutorial/trunk/session.txt 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/session.txt 2005-11-13 13:41:57 UTC (rev 40081)
@@ -0,0 +1,77 @@
+====================
+The Tutorial Session
+====================
+
+A tutorial session is created whenever a user watches or takes a tutorial. A
+session is an adapter to a tutorial, so we have to create that first:
+
+ >>> import os, tempfile
+ >>> temp_dir = tempfile.mkdtemp()
+ >>> sample_txt = os.path.join(temp_dir, 'sample.txt')
+
+ >>> open(sample_txt, 'w').write('''
+ ... Sample Documentation
+ ... ====================
+ ...
+ ... Here is a simple print statement:
+ ...
+ ... >>> print 'sample'
+ ... sample
+ ...
+ ... And now a variable assignment with a return value:
+ ...
+ ... >>> id = 5
+ ... >>> id
+ ... 5
+ ...
+ ... That's it!
+ ... ''')
+
+ >>> from zope.tutorial import tutorial
+ >>> sample = tutorial.Tutorial('Sample Documentation', sample_txt)
+
+Now that we have the tutorial, we can create a session:
+
+ >>> session = tutorial.TutorialSession(sample)
+ >>> session.initialize()
+
+Nothing is setup until ``initialize()`` is called. Once the session is
+prepared, we can choose a controller that knows about the input and output
+interfaces. The simplest controller is the `SimpleCLIController`, which simply
+displays the text and examples:
+
+ >>> from zope.tutorial import cli
+ >>> controller = cli.SimpleCLIController(session)
+
+Since we are in a unit test file already, the Python prompt needs to be
+changed, so that the test does not get confused:
+
+ >>> controller.PYTHON_PROMPT = 'Py: '
+
+We can now write a simple function that runs the tutorial for us:
+
+ >>> def run():
+ ... controller.start()
+ ... while controller.running:
+ ... controller.doNextStep()
+
+ >>> run()
+ Starting Tutorial: Sample Documentation
+ <BLANKLINE>
+ Sample Documentation
+ ====================
+ <BLANKLINE>
+ Here is a simple print statement:
+ <BLANKLINE>
+ Py: print 'sample'
+ sample
+ <BLANKLINE>
+ And now a variable assignment with a return value:
+ <BLANKLINE>
+ Py: id = 5
+ Py: id
+ 5
+ <BLANKLINE>
+ That's it!
+ <BLANKLINE>
+ ---------- The End ----------
Property changes on: zope.tutorial/trunk/session.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: zope.tutorial/trunk/tests.py
===================================================================
--- zope.tutorial/trunk/tests.py 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/tests.py 2005-11-13 13:41:57 UTC (rev 40081)
@@ -30,6 +30,11 @@
tearDown=placelesssetup.tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
+ DocFileSuite('session.txt',
+ setUp=placelesssetup.setUp,
+ tearDown=placelesssetup.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
DocFileSuite('directives.txt',
setUp=placelesssetup.setUp,
tearDown=placelesssetup.tearDown,
Modified: zope.tutorial/trunk/tutorial.py
===================================================================
--- zope.tutorial/trunk/tutorial.py 2005-11-13 02:58:15 UTC (rev 40080)
+++ zope.tutorial/trunk/tutorial.py 2005-11-13 13:41:57 UTC (rev 40081)
@@ -11,47 +11,21 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Tutorial Manager Implementation
+"""Tutorial Implementation
$Id$
"""
__docformat__ = "reStructuredText"
+import doctest
import os
+import persistent
+import types
+import zope.component
import zope.interface
-from zope.app.apidoc import utilities
-from zope.app import location
-from zope.app import zapi
from zope.tutorial import interfaces
-class TutorialManager(utilities.ReadContainerBase):
- """TutorialManager"""
- zope.interface.implements(interfaces.ITutorialManager,
- location.interfaces.ILocation)
-
- def __init__(self, parent=None):
- self.__parent__ = parent
- self.__name__ = '++tutorials++'
-
- def get(self, key, default=None):
- """See zope.app.container.interfaces.IReadContainer"""
- utility = zapi.queryUtility(interfaces.ITutorial, key, default)
- if utility != default:
- location.locate(utility, self, key)
- return utility
-
- def items(self):
- """See zope.app.container.interfaces.IReadContainer"""
- items = list(zapi.getUtilitiesFor(interfaces.ITutorial))
- items.sort()
- utils = []
- for key, value in items:
- location.locate(value, self, key)
- utils.append((key, value))
- return utils
-
-
class Tutorial(object):
"""Tutorial"""
zope.interface.implements(interfaces.ITutorial)
@@ -65,14 +39,35 @@
self.__class__.__name__, self.title, os.path.split(self.path)[-1])
-class tutorialsNamespace(object):
- """Used to traverse the `++tutorials++` namespace"""
+class TutorialSession(persistent.Persistent):
+ """Tutorial Session"""
- def __init__(self, ob=None, request=None):
- self.tutorialManager = TutorialManager(ob)
+ zope.component.adapts(interfaces.ITutorial)
+ #zope.interface.implements(interfaces.ITutorialSession)
- def traverse(self, name, ignore=None):
- if name == '':
- return self.tutorialManager
- else:
- return self.tutorialManager[name]
+ def __init__(self, tutorial):
+ self.tutorial = tutorial
+
+
+ def initialize(self):
+ """See interfaces.ITutorialSession"""
+ text = open(self.tutorial.path, 'r').read()
+ parser = doctest.DocTestParser()
+ self.parts = parser.parse(text)
+ # Clean up the parts by removing empty strings
+ self.parts = [part for part in self.parts
+ if (not isinstance(part, types.StringTypes) or
+ part.strip())]
+ # Create a parts stack
+ self.parts.reverse()
+
+ # Set some runtime variables
+ self.globs = {}
+
+
+ def getNextStep(self):
+ """See interfaces.ITutorialSession"""
+ try:
+ return self.parts.pop()
+ except IndexError:
+ return None
More information about the Zope-CVS
mailing list