[py-svn] r12006 - in py/dist/py/test: . terminal

hpk at codespeak.net hpk at codespeak.net
Fri May 6 12:36:57 CEST 2005


Author: hpk
Date: Fri May  6 12:36:56 2005
New Revision: 12006

Modified:
   py/dist/py/test/collect.py
   py/dist/py/test/item.py
   py/dist/py/test/terminal/remote.py
Log:
make building the collector's tree more persistent 
by introducing "buildname2items()" which is responsible
for building a dictionary mapping names to collectors/items. 
the default run()/join() methods now build this dictionary
and cache it. 

The net result is that py lib's own and PyPy's test runs
run some 10% faster and are somewhat saner!



Modified: py/dist/py/test/collect.py
==============================================================================
--- py/dist/py/test/collect.py	(original)
+++ py/dist/py/test/collect.py	Fri May  6 12:36:56 2005
@@ -41,19 +41,17 @@
        raise py.error.ENOENT(fspath) 
     pkgpath = fspath.pypkgpath() 
     if pkgpath is None: 
-        if fspath.check(file=1): 
-            clsname = 'Module' 
-        else: 
-            clsname = 'Directory' 
+        clsname = fspath.check(file=1) and 'Module' or 'Directory' 
         fscol = py.test.Config.getvalue(clsname, fspath) 
         current = fscol(fspath) 
     else: 
         Directory = py.test.Config.getvalue('Directory', pkgpath)
         current = Directory(pkgpath) 
         #print "pkgpath", pkgpath
-        for name in filter(None, fspath.relto(pkgpath).split(fspath.sep)): 
-            #print "joining", name
+        names = fspath.relto(pkgpath).split(fspath.sep)
+        for name in names: 
             current = current.join(name) 
+            assert current, "joining %r resulted in None!" % (names,)
     top = current.listchain()[0]
     #top._config = config 
     return current 
@@ -104,6 +102,12 @@
         except AttributeError: 
             return False 
 
+    def __cmp__(self, other): 
+        s1 = self.getsortvalue()
+        s2 = other.getsortvalue()
+        #print "cmp", s1, s2
+        return cmp(s1, s2) 
+
     def obj(): 
         def fget(self):
             try: 
@@ -119,13 +123,6 @@
     def _getobj(self): 
         return getattr(self.parent.obj, self.name) 
 
-    def sortvalue(self): 
-        """ sorting function helper to bring test methods in
-            the same order as in their file.
-        """
-        for x in self.run():
-            return self.join(x).sortvalue() 
-
     def multijoin(self, namelist): 
         """ return a list of colitems for the given namelist. """ 
         return [self.join(name) for name in namelist]
@@ -184,6 +181,26 @@
                 for y in self.join(x).tryiter(stopitems): 
                     yield y
 
+    def _prepare(self): 
+        if not hasattr(self, 'name2items'): 
+            self.name2items = self.buildname2items()
+
+    def buildname2items(self): 
+        raise NotImplementedError, "abstract" 
+
+    def getsortvalue(self): 
+        return self.name 
+
+    def run(self): 
+        self._prepare()
+        itemlist = self.name2items.values()
+        itemlist.sort()
+        return [x.name for x in itemlist]
+
+    def join(self, name): 
+        self._prepare()
+        return self.name2items.get(name, None) 
+
     captured_out = captured_err = None
     def startcapture(self): 
         return None # by default collectors don't capture output 
@@ -194,8 +211,7 @@
 
 class FSCollector(Collector): 
     def __init__(self, fspath, parent=None): 
-        if isinstance(fspath, str): 
-            fspath = py.path.local(fspath)
+        fspath = py.path.local(fspath) 
         super(FSCollector, self).__init__(fspath.basename, parent) 
         self.fspath = fspath 
 
@@ -209,20 +225,27 @@
         return path.check(dotfile=0) and \
                path.basename not in ('CVS', '_darcs', '{arch}')
 
-    def run(self): 
-        l = self.fspath.listdir() 
-        l.sort() 
-        return [x.basename for x in l 
-                if (x.check(file=1) and self.filefilter(x) or 
-                    x.check(dir=1) and self.recfilter(x))] 
+    def buildname2items(self): 
+        d = {} 
+        for p in self.fspath.listdir(): 
+            x = self.makeitem(p.basename, self.filefilter, self.recfilter)
+            if x is not None: 
+                d[p.basename] = x
+        return d 
+
+    def makeitem(self, basename, filefilter=None, recfilter=None): 
+        p = self.fspath.join(basename)
+        if p.check(file=1) and (not filefilter or filefilter(p)): 
+            return self.Module(p, parent=self) 
+        elif p.check(dir=1) and (not recfilter or recfilter(p)): 
+            Directory = py.test.Config.getvalue('Directory', p) 
+            return Directory(p, parent=self) 
 
     def join(self, name): 
-        x = self.fspath.join(name) 
-        if x.check(file=1): 
-            return self.Module(x, parent=self) 
-        elif x.check(dir=1): 
-            Directory = py.test.Config.getvalue('Directory', x) 
-            return Directory(x, parent=self)  
+        x = super(Directory, self).join(name)
+        if x is None:    
+            x = self.makeitem(name)
+        return x 
 
 class PyCollectorMixin(object): 
     def funcnamefilter(self, name): 
@@ -230,28 +253,20 @@
     def classnamefilter(self, name): 
         return name.startswith('Test')
 
-    def run(self): 
-        l = []
+    def buildname2items(self): 
+        d = {} 
         for name in dir(self.obj): 
-            if self.funcnamefilter(name) or self.classnamefilter(name): 
-                x = self.join(name) 
-                if x is not None: 
-                    l.append((x.sortvalue(), name))
-        l.sort() 
-        return [x[1] for x in l]
-
-    def join(self, name): 
-        obj = getattr(self.obj, name) 
-        if isclass(obj): 
-            return self.Class(name, parent=self)
-        elif callable(obj): 
-            if obj.func_code.co_flags & 32: # generator function 
-                return self.Generator(name, parent=self) 
-            else: 
-                return self.Function(name, parent=self) 
+            obj = getattr(self.obj, name) 
+            if self.classnamefilter(name) and isclass(obj): 
+                d[name] = self.Class(name, parent=self) 
+            elif self.funcnamefilter(name) and callable(obj): 
+                if obj.func_code.co_flags & 32: # generator function 
+                    d[name] = self.Generator(name, parent=self) 
+                else: 
+                    d[name] = self.Function(name, parent=self) 
+        return d
 
 class Module(PyCollectorMixin, FSCollector): 
-
     def startcapture(self): 
         if not self.option.nocapture and not self.option.usepdb: 
             self._capture = SimpleOutErrCapture() 
@@ -289,6 +304,7 @@
         if hasattr(self.obj, 'teardown_module'): 
             self.obj.teardown_module(self.obj) 
 
+
 class Class(PyCollectorMixin, Collector): 
     def run(self): 
         if getattr(self.obj, 'disabled', 0):
@@ -311,6 +327,10 @@
             teardown_class = getattr(teardown_class, 'im_func', teardown_class) 
             teardown_class(self.obj) 
 
+    def getsortvalue(self): 
+        for x in self.tryiter((py.test.collect.Generator, py.test.Item)): 
+            return x.getsortvalue()
+
 class Instance(PyCollectorMixin, Collector): 
     def _getobj(self): 
         return self.parent.obj()  
@@ -320,37 +340,16 @@
     Function = property(Function)
 
 class Generator(Collector): 
-    def run(self): 
-        #def iterator(): 
-        #    for i,x in py.builtin.enumerate(self.obj()): 
-        #        yield self.join("[%d]" % i)
-        #return py.builtin.collect(iterator()) 
-        self._objlist = l = []
-        namelist = []
+    def buildname2items(self): 
+        d = {} 
         for i, x in py.builtin.enumerate(self.obj()): 
-            call,args = self.getcallargs(x)
+            call, args = self.getcallargs(x)
             if not callable(call): 
                 raise TypeError("yielded test %r not callable" %(call,))
-            l.append(x) 
-            namelist.append("[%d]" % i) 
-        return namelist 
-
-    def join(self, name): 
-        if name[:1] != '[' or name[-1:] != ']': 
-            raise NameError("%r is not an index" %(name,))
-        num = int(name[1:-1]) 
-        try: 
-            objlist = self._objlist
-        except AttributeError: 
-            self._objlist = objlist = list(self.obj())
-        for i, x in py.builtin.enumerate(objlist): 
-            if i == num: 
-                if isinstance(x, (Collector, )): 
-                    return x 
-                call, args = self.getcallargs(x) 
-                assert callable(call) 
-                return self.Function(name, self, args, obj=call)
-
+            name = "[%d]" % i
+            d[name] = self.Function(name, self, args, obj=call)
+        return d 
+                
     def getcallargs(self, obj):
         if isinstance(obj, (tuple, list)):
             call, args = obj[0], obj[1:]
@@ -362,5 +361,5 @@
         code = py.code.Code(self.obj) 
         return code.path, code.firstlineno 
 
-    def sortvalue(self):  
+    def getsortvalue(self):  
         return self.getpathlineno() 

Modified: py/dist/py/test/item.py
==============================================================================
--- py/dist/py/test/item.py	(original)
+++ py/dist/py/test/item.py	Fri May  6 12:36:56 2005
@@ -58,7 +58,7 @@
         code = py.code.Code(self.obj) 
         return code.path, code.firstlineno 
 
-    def sortvalue(self):  
+    def getsortvalue(self):  
         return self.getpathlineno() 
 
     def run(self):

Modified: py/dist/py/test/terminal/remote.py
==============================================================================
--- py/dist/py/test/terminal/remote.py	(original)
+++ py/dist/py/test/terminal/remote.py	Fri May  6 12:36:56 2005
@@ -97,17 +97,20 @@
     channel.send((args, failures))
     return waitfinish(channel)
 
+def generalize(p1, p2): 
+    general = p1 
+    for x, y in zip(p1.parts(), p2.parts()): 
+        if x != y: 
+            break 
+        general = x 
+    return general 
+
 def getrootdir(args): 
-    colitems = py.test.TerminalSession._map2colitems(args) 
-    tops = [x.listchain()[0].fspath for x in colitems]
-    def generalize(p1, p2): 
-        general = p1 
-        for x, y in zip(p1.parts(), p2.parts()): 
-            if x != y: 
-                break 
-            general = x 
-        return general 
-    p =reduce(generalize, tops) 
+    tops = []
+    for arg in args: 
+        p = py.path.local(arg)
+        tops.append(p.pypkgpath() or p)
+    p = reduce(generalize, tops) 
     if p.check(file=1): 
         p = p.dirpath()
     return p 



More information about the pytest-commit mailing list