[py-svn] r7162 - in py: branch/test2 branch/test2/doc branch/test2/py branch/test2/py/path/extpy branch/test2/py/path/local branch/test2/py/test branch/test2/py/test2 branch/test2/py/test2/report branch/test2/py/test2/report/text dist/py/bin dist/py/path/local dist/py/test2

hpk at codespeak.net hpk at codespeak.net
Sun Oct 31 22:42:54 CET 2004


Author: hpk
Date: Sun Oct 31 22:42:53 2004
New Revision: 7162

Added:
   py/branch/test2/
      - copied from r7156, py/dist/
   py/branch/test2/py/path/extpy/extpy.py
      - copied unchanged from r7161, py/dist/py/path/extpy/extpy.py
   py/branch/test2/py/path/extpy/inc_test_extpy.py
      - copied unchanged from r7161, py/dist/py/path/extpy/inc_test_extpy.py
   py/branch/test2/py/path/extpy/test_extpy.py
      - copied unchanged from r7161, py/dist/py/path/extpy/test_extpy.py
   py/branch/test2/py/test/run.py
      - copied unchanged from r7157, py/dist/py/test/run.py
   py/branch/test2/py/test2/
      - copied from r7158, py/dist/py/test2/
Removed:
   py/dist/py/bin/py.test2
   py/dist/py/test2/
Modified:
   py/branch/test2/conftest.py
   py/branch/test2/doc/test.txt
   py/branch/test2/py/__init__.py
   py/branch/test2/py/path/local/local.py
   py/branch/test2/py/test2/cmdline.py
   py/branch/test2/py/test2/config.py
   py/branch/test2/py/test2/defaultconfig.py
   py/branch/test2/py/test2/item.py
   py/branch/test2/py/test2/report/memo.py
   py/branch/test2/py/test2/report/text/reporter.py
   py/dist/py/path/local/local.py
Log:
get the test2-stuff out of the trunk (dist) 
in favour of a true branch. After all opening 
up a branch within the trunk wasn't such a 
great idea.  Implementing the test-refactoring
will take some time and experiments ... 



Modified: py/branch/test2/conftest.py
==============================================================================
--- py/dist/conftest.py	(original)
+++ py/branch/test2/conftest.py	Sun Oct 31 22:42:53 2004
@@ -14,7 +14,8 @@
 verbose = 0 
 nocapture = False 
 collectonly = False 
-exitfirstproblem = False 
+exitfirstproblem = True 
 fulltrace = False 
 showlocals = False
 nomagic = False 
+session = False

Modified: py/branch/test2/doc/test.txt
==============================================================================
--- py/dist/doc/test.txt	(original)
+++ py/branch/test2/doc/test.txt	Sun Oct 31 22:42:53 2004
@@ -245,139 +245,128 @@
 your setup function callable. Did we mention that lazyness
 is a virtue? 
 
-The three components of ``py.test``
-===================================
+The underlying model of the ``py.test`` process
+===============================================
+
+    WARNING: THIS IS FUTURISTIC (documentation driven development) 
 
 In order to customize ``py.test`` it's good to understand 
-its basic architure::
+the basic testing process:: 
 
-     ___________________
-    |                   |
-    |    Collector      |
-    |___________________|
-           / \                
-            |                Item.execute(driver)
-            |               ^
-     receive test Items    /
-            |             /execute test Item 
-            |            /
-     ___________________/                         ________________
-    |                   |      send events       |                |
-    |     Driver        |----------------------->|    Reporter    |
+         iter(Item) 
+            ^      ......................... sends events 
+            .                                 .
+            . asks for items & results         .
+            .                                   .
+     ___________________                         __________________
+    |                   |      send events      >|                |
+    |    RootItem       |.......................>|    Reporter    |
     |___________________|                        |________________|
                                           
-                        .......................
-                        .    configuration    .
-                        .   cmdline options   .
-                        .......................
-
 
-The *Driver* basically receives test *Items* from a *Collector*, 
-executes them via the ``Item.execute()`` method, and takes the
-outcome (possibly an exception) and sends it over to the
-*reporter* instance.  
+The RootItem iterates over its start items in order to receive
+more items triggers To start the testing process you call the RootItem.run() 
+method.  
+
+The *Driver* receives Items or Test results and sends according 
+events to the reporter which interacts with the user, usually 
+by just showing progress output (we do want to allow real 
+user interaction later for implementing a "test console" or
+a gui-based frontend). 
 
 .. _`collection process`: 
 
-Collectors and the test collection process 
+generative test Items and final test Items 
 ------------------------------------------
 
-The collecting process is iterative, i.e. the driver 
-get test Items one by one and can thus start immediately 
-to execute the first collected test Item.  At the same time, 
-the collectors are completly shielded from any driving,
-execution or reporting details.  
-
-The ``driver`` invokes the iteration protocol, i.e. the
-``__iter__`` method of Collectors.  These methods yield more (sub)
-*Collectors* or test *Items*.  They should usually not raise
-exceptions but yield back a specific CollectError. This is to
-avoid that a collecting error breaks the whole collection
-chain.  It is at the drivers discretion to react to errors 
-from collectors. 
-
-The default DirectoryCollector collects test files that 
-match the glob patterns ``test_*.py`` or ``*_test.py`` and it 
-recurses into any directory that doesn't start with a leading 
-dot (e.g.  ``.svn`` direcotries is ignored). 
-
-Another ``PyCollector`` then recurses into the test files and 
-collects functions and methods that have a leading ``test_``. 
-By default, methods are only collected if their class starts 
-with ``Test``. 
-
-Drivers bind it all together
-----------------------------
-
-Drivers serve as the glue code between the various parts of
-the interacting ``py.test`` objects, more specifically 
-they: 
-
-- iterate over Collectors, which yield more Collectors or Items. 
-
-- call the ``Item.execute()`` method in order to execute 
-  a test
-
-- send "result" and other events to the reporter so that
-  the users gets informed about progress and problems
-  in possibly custom ways. 
+    WARNING: THIS IS FUTURISTIC (documentation driven development) 
+
+The testing process is iterative, i.e. the driver immediately
+starts to execute test Items. 
+
+For directories, modules and classes there are default
+**generative Items** which yield more **test Items** from your
+python source tree. 
+
+- generative items can be iter()ated yielding back more 
+  generative items or ``Result`` objects. 
+
+- if iter()ating a generative item results in an exception 
+  this is interpreted as a test failure related to the 
+  generative item that failed. 
+
+- generative items are restartable, i.e. multiple calls 
+  to ``iter(genitem)`` allow to iterate a generative 
+  item multiple times. 
+
+- A ``driver`` uses ``iter(genitem)`` to iterate over 
+  generative items the reporter will receive an 
+  IterationFailure Result. 
+
+- The default DirectoryItem yields ModuleItems for files
+  matching the glob patterns ``test_*.py`` or ``*_test.py`` and it 
+  recurses into any directory that doesn't start with a leading 
+  dot (e.g.  ``.svn`` direcotries are ignored by default). 
+
+- The default ModuleItem yields back GeneratorItems, ClassItems, 
+  FunctionItems. ClassItems can also yield back FunctionItems
+  or GeneratorItems. 
+
+- FunctionItems usually yield back Results obtaining from 
+  executing their own ``funcitem.execute()`` which will just
+  call the underlying callable by default. 
 
 .. _reporter:
 
 Reporters process test events
 -----------------------------
 
-A driver typically invokes certain methods of a Reporter
-for various points in the testing process.  A reporter
-is reponsible for representing the testing process 
-to the user or other programs. These are the events
+    WARNING: THIS IS FUTURISTIC (documentation driven development) 
+
+A driver invokes Reporter methods for various interesting points 
+in the testing process.  A reporter is reponsible for representing 
+the testing process to the user or other programs. These are the events
 (function calls) that the reporter receives::
 
-    start(), end() are invoked only from the outmost driver 
-                   to allow a reporter to write headers and 
-                   footers 
-
-    open(collector) is called when a collector is about 
-                    to be iterated.  This method should return 
-                    a "close" method (or None) which will be 
-                    called when the collector is finished iterating. 
-  
-    startitem(item) is called before executing a single test item 
-    enditem(result) is called when execution of a single test item 
-                    finished.  The result.item attribute points back 
-                    to the Test item the result object belongs to. 
+    begin(startitems) before the testing process begins 
+    end(startitems)   after the testing process finished
 
+    open(item)        before an item is about to be iterated 
+    result(result)    for obtained ``Result`` objects 
+    end(item)         after iteration of an item finished 
+
+    error(excinfo)    signals severe internal/iteration failures 
+  
 Customizing the py.test process 
 ===============================
 
-Customizing the collection process in a module
----------------------------------------------- 
+    WARNING: THIS IS FUTURISTIC (documentation driven development) 
+
+*test generators* can produce test items on the fly 
+---------------------------------------------------
+
+    WARNING: THIS IS FUTURISTIC (documentation driven development) 
 
 If you have a module where you want to take responsibility for
-collecting your own test Items and possibly even for executing
-a test then you can provide your own ``Collector`` at module
-level.  The default ModuleCollector looks for the name
-``Collector`` in the modules namespace and turns over
-reponsibility by invoking it with the "module-path". 
-
-The module path is a ``py.path.py()`` instance and carries
-information needed to traverse to to the module.  In general, 
-a pypath allows to address a python object on the filesystem.
-*Addressability of test Items* is a major concern because we
-want to memorize failing tests across py.test invocations.  A
-``pypath`` has two parts, a filesystem path and a dotted path
-leading to the python object.  Another benefits, apart from 
-from addressability, is that invoking setup/teardown operations 
-can simply be implemented by walking the path to the python 
-object. 
+collecting your own test Items you can provide a test generator 
+that yields back callables which will be seemlessly integrated 
+into the testing process. **Test generators** are automatically 
+picked up and are recognized by the usual ``test_`` prefix. 
+If iterating over your generator raises an exception this
+is reported as a test failure result. 
+
+If a **test generator** raises an ``EOFError`` the parent (generative) 
+Item will stop further iteration.  This allows your **test generator** 
+to control further inspection of a module or class. 
+
+Customizing the execution of Items 
+----------------------------------
 
-Customizing execution of Items 
------------------------------- 
+    WARNING: THIS IS FUTURISTIC (documentation driven development) 
 
 - Items allow total control of executing their contained test
-  method.  ``iteminstance.execute()`` will get called by the
-  driver in order to actually execute a test. Thus a custom
-  ``execute()`` method can pass arguments to test functions. 
+  method.  ``iteminstance.execute()`` will get called 
+  in order to actually execute a test.  
 
 - Item.execute() methods can provide custom paramters 
   (a db-connection, some initialized application entity, whatever) 

Modified: py/branch/test2/py/__init__.py
==============================================================================
--- py/dist/py/__init__.py	(original)
+++ py/branch/test2/py/__init__.py	Sun Oct 31 22:42:53 2004
@@ -28,6 +28,24 @@
     'test.config':       './test/config.config',
     'test.compat.TestCase': './test/compat.TestCase',
 
+    'test2.DefaultOptions':  './test2/option.DefaultOptions', 
+    'test2.Option':       './test2/tool/optparse.Option', 
+    'test2.TextReporter': './test2/report/text/reporter.TextReporter',
+    'test2.Actor': './test2/actor.Actor', 
+    'test2.CmdlineActor': './test2/cmdline.CmdlineActor',
+    #'test2.MemoActor': './test2/actor.MemoActor',
+    'test2.exit':       './test/drive.exit',
+    'test2.fail':       './test/drive.fail',
+    'test2.skip':       './test/drive.skip',
+    'test2.raises':       './test/raises.raises',
+    'test2.config':       './test/config.config',
+    'test2.compat.TestCase': './test/compat.TestCase',
+    'test2.item.Directory': './test/item.Directory',
+    'test2.item.Module': './test/item.Module',
+    'test2.item.Class': './test/item.Class',
+    'test2.item.Callable': './test/item.Callable',
+    'test2.item.Generator': './test/item.Generator',
+
     'process.cmdexec':    './process/cmdexec.cmdexec',
 
     'execnet.PopenGateway': './execnet/register.PopenGateway',

Modified: py/branch/test2/py/path/local/local.py
==============================================================================
--- py/dist/py/path/local/local.py	(original)
+++ py/branch/test2/py/path/local/local.py	Sun Oct 31 22:42:53 2004
@@ -266,9 +266,10 @@
 
     def write(self, content):
         """ write string content into path. """
+        s = str(content)
         f = self.open('wb')
         try:
-            f.write(content)
+            f.write(s)
         finally:
             f.close()
 

Modified: py/branch/test2/py/test2/cmdline.py
==============================================================================
--- py/dist/py/test2/cmdline.py	(original)
+++ py/branch/test2/py/test2/cmdline.py	Sun Oct 31 22:42:53 2004
@@ -1,35 +1,84 @@
 from __future__ import generators
 import py 
-import sys
-from py.__impl__.test import run 
+
+from py.__impl__.test2.tool import optparse 
+
+from py.__impl__.test2.item import Directory, Module 
 
 #
 # main entry point
 #
 
-def _old(argv): 
-    if argv is None:
-        argv = py.std.sys.argv
-        frame = py.std.sys._getframe(1)
-        name = frame.f_locals.get('__name__')
-        if name != '__main__':
-            return # called from an imported test file
-        import __main__
-        return [py.test.collect.Module(py.std.sys.argv[0])]
-
 def main(): 
-    args = py.std.sys.argv[1:]
-    py.test.config.readconfiguration(*run.getanchors(args)) 
+    actor = CmdlineActor(py.std.sys.argv) 
+    actor.start() 
+
+class CmdlineActor(py.test2.Actor): 
+    def __init__(self, argv):
+        anchors = getanchors(argv[1:])
+        self.options = py.test2.DefaultOptions(anchors) 
+        self.parse(argv)
+        self.reporter = self.options.Reporter(self) 
+        assert self.items, "startup should result in runnable Items" 
+        super(CmdlineActor, self).__init__() 
+
+    def parse(self, argv):
+        """ parse command line options and return items. """
+        # first a small fight with optparse to merge the 
+        # pytest.py file options correctly 
+        parser = optparse.OptionParser()
+        for config in self.options._configpaths: 
+            meth = config.join('options') 
+            if meth.check(): 
+                groupname, groupoptions = meth.resolve() 
+                optgroup = optparse.OptionGroup(parser, groupname) 
+                optgroup.add_options(groupoptions)
+                parser.add_option_group(optgroup)
+
+        # extract and remove defaults from options
+        for option in flattenoptions(parser):
+            if option.dest:
+                value = self.options._getfirst(option.dest, option.default) 
+                #print "setting %r to %r" %(option.dest, value)
+                setattr(self.options, option.dest, value) 
+                option.default = 'NODEFAULT'
+        
+        # parse cmdline args  
+        _, remaining = parser.parse_args(argv[1:], self.options) 
+        # override previously computed defaults 
+        #for name in cmdlineoption.__dict__:
+        #    if not name.startswith('_'):
+        #        print "resetting %r to %r" %(name, cmdlineoption.__dict__[name])
+        #        setattr(self.options, name, cmdlineoption.__dict__[name])
+        self.items = []
+        for x in remaining: 
+            p = py.path.local(x) 
+            if p.check(dir=1): 
+                self.items.append(Directory(p, self))
+            elif p.check(file=1): 
+                self.items.append(Module(p, self)) 
+            else: 
+                raise ValueError(x) 
+        if not self.items:
+            self.items.append(Directory(py.path.local(), self))
+
+def getanchors(args):
+    """ yield "anchors" from skimming the args for existing files/dirs. """
+    current = py.path.local()
+    l = []
+    for arg in args: 
+        anchor = current.join(arg, abs=1) 
+        if anchor.check():
+            l.append(anchor) 
+    if not l:
+        l = [current]
+    return l 
+
+# helpers 
 
-    filenames = py.test.config.parseargs(args)
-    if not filenames: 
-        filenames.append(str(py.path.local()))
-    try:
-        if py.test.config.option.session: 
-            run.session(args, filenames) 
-        else:
-            run.inprocess(args, filenames) 
-    except KeyboardInterrupt:
-        print
-        print "Keybordinterrupt"
-        raise SystemExit, 2
+def flattenoptions(parser):
+    for group in parser.option_groups:
+        for y in group.option_list: 
+            yield y
+    for x in parser.option_list:
+        yield x 

Modified: py/branch/test2/py/test2/config.py
==============================================================================
--- py/dist/py/test2/config.py	(original)
+++ py/branch/test2/py/test2/config.py	Sun Oct 31 22:42:53 2004
@@ -1,7 +1,7 @@
 from __future__ import generators
 
 import py
-from py.__impl__.test.tool import optparse 
+from py.__impl__.test2.tool import optparse 
 
 defaultconfig = py.magic.autopath().dirpath('defaultconfig.py')
 defaultconfig = py.path.extpy(defaultconfig) 
@@ -11,10 +11,42 @@
 # config file handling (utest.conf)
 #
 configbasename = 'conftest.py' 
+import py 
 
-class Config:
-    def __init__(self):
-        self.configpaths = []
+Option = py.test2.Option
+options = ('py.test standard options', [
+        Option('-v', '--verbose', 
+               action="count", dest="verbose", default=0,
+               help="increase verbosity"),
+        Option('-x', '--exitfirst', 
+               action="store_true", dest="exitfirstproblem", default=False, 
+               help="exit instantly on first error or failed test."),
+        Option('-S', '--nocapture', 
+               action="store_true", dest="nocapture", default=False,
+               help="disable catching of sys.stdout/stderr output."),
+        Option('-l', '--showlocals', 
+               action="store_true", dest="showlocals", default=False, 
+               help="show locals in tracebacks (disabled by default)"),
+        Option('', '--session', 
+               action="store_true", dest="session", default=False,
+               help="run a test session/rerun only failing tests. "),
+        Option('', '--fulltrace', 
+               action="store_true", dest="fulltrace", default=False, 
+               help="Don't try to cut any tracebacks (default is to cut)"),
+        Option('', '--nomagic', 
+               action="store_true", dest="nomagic", default=False, 
+               help="don't invoke magic to e.g. beautify failing/error statements."),
+        #Option('', '--pdb',
+        #       action="store_true", dest="usepdb", default=False,
+        #       help="Start pdb (the Python debugger) on errors."),
+        Option('', '--collectonly', 
+               action="store_true", dest="collectonly", default=False,
+               help="only collect tests, don't execute them. "),
+])
+
+class DefaultOptionsOptions:
+    def __init__(self, config=None):
+        self.configpaths = filter(None, [config, defaultconfig])
         self.option = optparse.Values()
 
     def _gettmpdir(self, sessiondir=[]):
@@ -26,19 +58,11 @@
             return d
     tmpdir = property(_gettmpdir, None, None, "Temporary Directory")
 
-    def readconfiguration(self, *anchors):
-        """ read configuration files left-to-right for the given anchor file. """
-        self.configpaths = []
-        for anchor in anchors: 
-            for p in anchor.parts(): 
-                x = p.join(configbasename)
-                if x.check(file=1): 
-                    extpy = py.path.extpy(x) 
-                    extpy.resolve() # trigger loading it 
-                    self.configpaths.append(extpy) 
-        self.configpaths.sort(lambda x,y: cmp(len(str(x)), len(str(y))))
-        self.configpaths.append(defaultconfig)
-       
+    def __getattr__(self, name):
+        if name[0] == '_': 
+            raise AttributeError(name) 
+        return self.getfirst(name) 
+
     def getfirst(self, param, default=dummy):
         """ return first found parameter. """
         for config in self.configpaths: 
@@ -51,8 +75,20 @@
         #if default is not dummy:
         #    return getattr(self, param, default)
         #return getattr(self, param)
+
+    def _getreporter(self):
+        try:
+            return self._reporter 
+        except AttributeError: 
+            self._reporter = r = self.Reporter() 
+            return r 
+    def _setreporter(self, rep):
+        self._reporter = rep 
+    reporter = property(_getreporter, _setreporter, None, "reporter instance") 
             
     def parseargs(self, args): 
+        anchors = getanchors(args) 
+        self.configpaths = readconfiguration(anchors) + self.configpaths 
         # first a small fight with optparse to merge the 
         # pytest.py file options correctly 
         parser = optparse.OptionParser()
@@ -79,10 +115,33 @@
         #    if not name.startswith('_'):
         #        print "resetting %r to %r" %(name, cmdlineoption.__dict__[name])
         #        setattr(self.option, name, cmdlineoption.__dict__[name])
-        return remaining 
-
+        self.testpaths = [py.path.local(x) for x in remaining]
+        #return remaining 
 
-config = Config()
+def readconfiguration(anchors):
+    """ read configuration files left-to-right for the given anchor file. """
+    configpaths = []
+    for anchor in anchors: 
+        for p in anchor.parts(): 
+            x = p.join(configbasename)
+            if x.check(file=1): 
+                extpy = py.path.extpy(x) 
+                extpy.resolve() # trigger loading it XXX why? 
+                configpaths.append(extpy) 
+    configpaths.sort(lambda x,y: cmp(len(str(x)), len(str(y))))
+    return configpaths 
+
+def getanchors(args):
+    """ yield "anchors" from skimming the args for existing files/dirs. """
+    current = py.path.local()
+    l = []
+    for arg in args: 
+        anchor = current.join(arg, abs=1) 
+        if anchor.check():
+            l.append(anchor) 
+    if not l:
+        l = [current]
+    return l 
 
 # helpers 
 

Modified: py/branch/test2/py/test2/defaultconfig.py
==============================================================================
--- py/dist/py/test2/defaultconfig.py	(original)
+++ py/branch/test2/py/test2/defaultconfig.py	Sun Oct 31 22:42:53 2004
@@ -1,9 +1,6 @@
 import py 
-Option = py.test.Option
-
-def getreporter():
-    return py.test.TextReporter()
 
+Option = py.test2.Option
 options = ('py.test standard options', [
         Option('-v', '--verbose', 
                action="count", dest="verbose", default=0,
@@ -33,3 +30,7 @@
                action="store_true", dest="collectonly", default=False,
                help="only collect tests, don't execute them. "),
 ])
+
+from py.__impl__.test2.item import DirectoryItem, ModuleItem
+from py.__impl__.test2.item import CmdlineItem, StartItem, ModuleItem, CallableItem
+

Modified: py/branch/test2/py/test2/item.py
==============================================================================
--- py/dist/py/test2/item.py	(original)
+++ py/branch/test2/py/test2/item.py	Sun Oct 31 22:42:53 2004
@@ -1,25 +1,10 @@
+from __future__ import generators
+import py 
 
 # ----------------------------------------------
 # Basic Test Item 
 # ----------------------------------------------
 class Item(object):
-    _setupcache = []
-    _lastinstance = None
-
-    def __init__(self, extpy, *args): 
-        self.extpy = extpy
-        self.name = extpy.basename 
-        self.args = args
-
-    def execute(self, driver):
-        driver.setup_path(self.extpy) 
-        target, teardown = driver.setup_method(self.extpy) 
-        try:
-            target(*self.args)
-        finally: 
-            if teardown: 
-                teardown(target) 
-            
     class Outcome: 
         def __init__(self, **kwargs):
             assert 'msg' not in kwargs or isinstance(kwargs['msg'], str), (
@@ -28,8 +13,164 @@
         def __repr__(self):
             return getattr(self, 'msg', object.__repr__(self)) 
     class Passed(Outcome): pass
-    class Failed(Outcome): pass
-    class ExceptionFailure(Failed): pass
+    class Failure(Outcome): pass
+    class ExceptionFailure(Failure): pass
     class Skipped(Outcome): pass 
 
+    parentitem = None 
+
+    def __init__(self, actor):
+        self.actor = actor 
+
+    def run(self):
+        print "self", self 
+        print "actor", self.actor
+        assert isinstance(self.actor, py.test2.Actor)
+        items = self.actor.call('collect', self) 
+        for item in items: 
+            self.actor.optcall('open', item) 
+            try:
+                try:
+                    item.run()
+                except (KeyboardInterrupt, SystemExit): 
+                    raise 
+                except:
+                    self.actor.error(py.std.sys.exc_info())
+            finally:
+                self.actor.optcall('close', item) 
+
+class Directory(Item): 
+    def __init__(self, fspath, actor): 
+        assert fspath.check(dir=1)
+        assert isinstance(actor, py.test2.Actor) 
+        self.fspath = fspath 
+        super(Directory, self).__init__(actor) 
+  
+    def strlocation(self):
+        return str(self.fspath) 
+
+    def fil(self, fspath):
+        return (fspath.check(file=1, fnmatch='test_*.py') or 
+                fspath.check(file=1, fnmatch='*_test.py'))
+    def rec(self, fspath): 
+        return fspath.check(dir=1, dotfile=0, link=0)
+
+    def collect(self):
+        l = []
+        append = l.append 
+        for fspath in self.fspath.listdir(sort=True): 
+            if self.rec(fspath):
+                append(Directory(fspath, self.actor))
+            elif self.fil(fspath):
+                extpy = py.path.extpy(fspath) 
+                append(Module(extpy, self.actor))
+        return l
+
+class PyItem(Item): 
+    def __init__(self, extpy, actor):
+        self.extpy = extpy 
+        self.yielders = [getattr(self, x) 
+                            for x in dir(self.__class__) 
+                                if x.startswith('collect_')]
+        super(PyItem, self).__init__(actor)
+
+    def strlocation(self):
+        return str(self.extpy) 
+
+    def sorted(self):
+        l = []
+        for extpy in self.extpy.listdir():
+            for meth in self.yielders:
+                for x in meth(extpy):
+                    x.fspath = self.extpy.root 
+                    sortkey = self.getsortkey(x) 
+                    l.append((sortkey, x)) 
+        l.sort() 
+        return [x[1] for x in l]
+
+    def getsortkey(self, obj): 
+        """ sorting function to bring test methods in 
+            the same order as int he file. 
+        """ 
+        if isinstance(obj, PyItem): 
+            obj = obj.extpy.resolve() 
+        elif isinstance(obj, PyItem):
+            return obj.getsortkey() 
+        if hasattr(obj, 'im_func'):
+            obj = obj.im_func 
+        if hasattr(obj, 'func_code'):
+            obj = obj.func_code 
+        # enough is enough 
+        return (getattr(obj, 'co_filename', None), 
+                getattr(obj, 'co_firstlineno', py.std.sys.maxint))
+                
+class Module(PyItem):
+    def collect(self): 
+        l = []
+        append = l.append
+        for x in self.extpy.listdir(): 
+            basename = x.basename 
+            if basename.startswith('test_'): 
+                if x.check(genfunc=1): 
+                    append(Generator(x, self.actor) )
+                elif x.check(func=1): 
+                    append(Callable(x, self.actor))
+            elif basename.startswith('Test') and self.extpy.samefile(x) \
+                                             and x.check(class_=1): 
+                obj = x.resolve()
+                print "found", obj
+                if not getattr(obj, 'disabled', 0):
+                    append(Class(x)) 
+
+        slist = [(x.getsortkey(x),x) for x in l]
+        slist.sort()
+        l = [x[1] for x in slist]
+        return l 
+
+class Callable(PyItem): 
+    def __init__(self, extpy, actor, args=()): 
+        super(Callable, self).__init__(extpy, actor) 
+        self.args = args 
+
+    def run(self):
+        target = self.actor.optcall('setup', self) 
+        try:
+            try:
+                self.execute(target) 
+            except (KeyboardInterrupt, SystemExit): 
+                raise 
+            except: 
+                self.actor.failed(self, py.std.sys.exc_info())
+            else:
+                self.actor.passed(self) 
+        finally: 
+            self.actor.optcall('teardown', self) 
+
+    def setup(self): 
+        """ setup objects along the path to the test-method 
+            (pointed to by extpy). Tear down any previously 
+            setup objects which are not directly needed. 
+        """ 
+        #self.conf.setup_path(self.extpy) 
+        return self.extpy.resolve()
+
+    def execute(self, target): 
+        target(*self.args)
+
+class Generator(Callable): 
+    def execute(self, obj): 
+        for x in obj(*self.args): 
+            if isinstance(x, (tuple, list)):
+                callable = x.pop(0)
+                args = tuple(x) 
+            else: 
+                callable = x 
+                args = () 
+            #if isgenerator(callable):
+            #    item = self.LiveGenerator(callable, self, args)
+            if callable(callable): 
+                item = Callable(callable, self, args)
+            else:
+                raise TypeError("not a callable or generator: %r" % callable)
+            item.run() 
 

Modified: py/branch/test2/py/test2/report/memo.py
==============================================================================
--- py/dist/py/test2/report/memo.py	(original)
+++ py/branch/test2/py/test2/report/memo.py	Sun Oct 31 22:42:53 2004
@@ -3,6 +3,34 @@
 from py.test import Item 
 
 class MemoReporter:
+    def __init__(self):
+        self._calls = []
+
+    def optcall(self, methodname, obj): 
+        c = getattr(obj, methodname, None) 
+        if c is not None:
+            c()
+            self._calls.append((methodname, obj))
+
+    def call(self, methodname, obj): 
+        c = getattr(obj, methodname, None) 
+        if c is not None:
+            c()
+        self._calls.append((methodname, obj))
+
+    def _dispatchmethod(self, methodname, item): 
+        cls = getattr(item, '__class__', None)
+        if cls is not None:
+            for typ in py.std.inspect.getmro(cls): 
+                reportmethodname = "%s_%s" % (methodname, typ.__name__) 
+                reportmeth = getattr(reporter, reportmethodname, None) 
+                if reportmeth is not None:
+                    reportmeth(item) 
+                    break 
+        call = getattr(item, methodname, None)
+        if call is not None: 
+            return call(item)
+
     typemap = {
         Item.Passed: '.', Item.Skipped: 's', Item.Failed: 'F',
     }

Modified: py/branch/test2/py/test2/report/text/reporter.py
==============================================================================
--- py/dist/py/test2/report/text/reporter.py	(original)
+++ py/branch/test2/py/test2/report/text/reporter.py	Sun Oct 31 22:42:53 2004
@@ -24,10 +24,9 @@
         Collector.Error: 'COLLECT ERROR', 
     }
 
-    def __init__(self, f=None): 
-        if f is None:
-            f = py.std.sys.stdout 
-        self.out = getout(f) 
+    def __init__(self, actor): 
+        self.actor = actor 
+        self.out = getout(py.std.sys.stdout) 
         self._started = {}
         self.summary = self.Summary() 
         self.summary.option = self.option = py.test.config.option 

Deleted: /py/dist/py/bin/py.test2
==============================================================================
--- /py/dist/py/bin/py.test2	Sun Oct 31 22:42:53 2004
+++ (empty file)
@@ -1,5 +0,0 @@
-#!/usr/bin/env python 
-
-from _findpy import py 
-from py.__impl__.test2.cmdline import main
-main() 

Modified: py/dist/py/path/local/local.py
==============================================================================
--- py/dist/py/path/local/local.py	(original)
+++ py/dist/py/path/local/local.py	Sun Oct 31 22:42:53 2004
@@ -266,9 +266,10 @@
 
     def write(self, content):
         """ write string content into path. """
+        s = str(content)
         f = self.open('wb')
         try:
-            f.write(content)
+            f.write(s)
         finally:
             f.close()
 



More information about the pytest-commit mailing list