[pypy-svn] r74676 - in pypy/trunk: . lib-python py py/_cmdline py/_code py/_io py/_plugin py/_test pypy pypy/interpreter pypy/interpreter/test pypy/jit/metainterp/test pypy/tool/pytest pypy/tool/pytest/test

hpk at codespeak.net hpk at codespeak.net
Sat May 22 13:29:15 CEST 2010


Author: hpk
Date: Sat May 22 13:29:12 2010
New Revision: 74676

Removed:
   pypy/trunk/py/__init__.py.orig
   pypy/trunk/py/_plugin/pytest_pytester.py.orig
   pypy/trunk/py/_plugin/pytest_terminal.py.orig
Modified:
   pypy/trunk/   (props changed)
   pypy/trunk/lib-python/conftest.py
   pypy/trunk/py/__init__.py
   pypy/trunk/py/_builtin.py
   pypy/trunk/py/_cmdline/pytest.py
   pypy/trunk/py/_code/code.py
   pypy/trunk/py/_code/source.py
   pypy/trunk/py/_io/capture.py
   pypy/trunk/py/_plugin/pytest__pytest.py
   pypy/trunk/py/_plugin/pytest_capture.py
   pypy/trunk/py/_plugin/pytest_genscript.py
   pypy/trunk/py/_plugin/pytest_junitxml.py
   pypy/trunk/py/_plugin/pytest_mark.py
   pypy/trunk/py/_plugin/pytest_pytester.py
   pypy/trunk/py/_plugin/pytest_resultlog.py
   pypy/trunk/py/_plugin/pytest_runner.py
   pypy/trunk/py/_plugin/pytest_skipping.py
   pypy/trunk/py/_plugin/pytest_terminal.py
   pypy/trunk/py/_test/cmdline.py
   pypy/trunk/py/_test/pycollect.py
   pypy/trunk/pypy/conftest.py
   pypy/trunk/pypy/interpreter/gateway.py
   pypy/trunk/pypy/interpreter/test/test_code.py
   pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py
   pypy/trunk/pypy/tool/pytest/appsupport.py
   pypy/trunk/pypy/tool/pytest/test/test_appsupport.py
Log:
merge py131 branch (which merges py1.3.1a1 - release pending) relevant news:

* fixes keyboardinterrupt issues - hopefully strange internal TBs 
  when doing ctrl-c are a thing of the past now
* be more robust against bogus source code and fix/improve 
  reporting of failing applevel tests 
* fix some xfail/skipping issues and introduce a new
  imperative way to xfail a test by 

    py.test.xfail()  # or py.test.xfail("reasonstring")

  I strongly recommend to use this instead of py.test.skip("xxx") because
  it more clearly communicates that not just some platform/dependency 
  problem but an implementation issue is present.  Note that it is also
  possible to do:

    @py.test.mark.xfail
    def test_function()

  and with Python2.6 it can also be applied to a test class like this:

    @py.test.mark.xfail(reason="xyz needs improvement/refactoring")
    class TestClass:
        def test_...

  And btw, you can issue "py.test --runxfail" to still run those tests and see 
  tracebacks. And finally, 

    @py.test.mark.xfail(run=False)
    def test_function()

  will not try to run the function at all. 



Modified: pypy/trunk/lib-python/conftest.py
==============================================================================
--- pypy/trunk/lib-python/conftest.py	(original)
+++ pypy/trunk/lib-python/conftest.py	Sat May 22 13:29:12 2010
@@ -14,7 +14,6 @@
 
 # the following adds command line options as a side effect! 
 from pypy.conftest import gettestobjspace, option as pypy_option 
-from test import pystone
 
 from pypy.tool.pytest import appsupport 
 from pypy.tool.pytest.confpath import pypydir, libpythondir, \
@@ -40,6 +39,7 @@
 option = py.test.config.option 
 
 def gettimeout(): 
+    from test import pystone
     timeout = option.timeout.lower()
     if timeout.endswith('mp'): 
         megapystone = float(timeout[:-2])

Modified: pypy/trunk/py/__init__.py
==============================================================================
--- pypy/trunk/py/__init__.py	(original)
+++ pypy/trunk/py/__init__.py	Sat May 22 13:29:12 2010
@@ -8,7 +8,7 @@
 
 (c) Holger Krekel and others, 2004-2010
 """
-__version__ = version = "1.3.0"
+__version__ = version = "1.3.1a1"
 
 import py.apipkg
 
@@ -25,7 +25,6 @@
         'pytest':     '._cmdline.pytest:main',
         'pylookup':   '._cmdline.pylookup:main',
         'pycountloc': '._cmdline.pycountlog:main',
-        'pytest':     '._test.cmdline:main',
         'pylookup':   '._cmdline.pylookup:main',
         'pycountloc': '._cmdline.pycountloc:main',
         'pycleanup':  '._cmdline.pycleanup:main',

Modified: pypy/trunk/py/_builtin.py
==============================================================================
--- pypy/trunk/py/_builtin.py	(original)
+++ pypy/trunk/py/_builtin.py	Sat May 22 13:29:12 2010
@@ -151,7 +151,10 @@
         return getattr(function, "__dict__", None)
 
     def _getcode(function):
-        return getattr(function, "func_code", None)
+        try:
+            return getattr(function, "__code__")
+        except AttributeError:
+            return getattr(function, "func_code", None)
 
     def print_(*args, **kwargs):
         """ minimal backport of py3k print statement. """ 
@@ -175,6 +178,7 @@
 
     def exec_(obj, globals=None, locals=None):
         """ minimal backport of py3k exec statement. """ 
+        __tracebackhide__ = True
         if globals is None: 
             frame = sys._getframe(1)
             globals = frame.f_globals 
@@ -187,14 +191,17 @@
 if sys.version_info >= (3,0):
     exec ("""
 def _reraise(cls, val, tb):
+    __tracebackhide__ = True
     assert hasattr(val, '__traceback__')
     raise val
 """)
 else:
     exec ("""
 def _reraise(cls, val, tb):
+    __tracebackhide__ = True
     raise cls, val, tb
 def exec2(obj, globals, locals):
+    __tracebackhide__ = True
     exec obj in globals, locals 
 """)
 

Modified: pypy/trunk/py/_cmdline/pytest.py
==============================================================================
--- pypy/trunk/py/_cmdline/pytest.py	(original)
+++ pypy/trunk/py/_cmdline/pytest.py	Sat May 22 13:29:12 2010
@@ -1,5 +1,5 @@
 #!/usr/bin/env python 
 import py
 
-def main(args):
-    py.test.cmdline.main(args) 
+def main(args=None):
+    raise SystemExit(py.test.cmdline.main(args))

Modified: pypy/trunk/py/_code/code.py
==============================================================================
--- pypy/trunk/py/_code/code.py	(original)
+++ pypy/trunk/py/_code/code.py	Sat May 22 13:29:12 2010
@@ -23,64 +23,14 @@
     def __ne__(self, other):
         return not self == other
 
-    def new(self, rec=False, **kwargs): 
-        """ return new code object with modified attributes. 
-            if rec-cursive is true then dive into code 
-            objects contained in co_consts. 
-        """ 
-        if sys.platform.startswith("java"):
-            # XXX jython does not support the below co_filename hack
-            return self.raw 
-        names = [x for x in dir(self.raw) if x[:3] == 'co_']
-        for name in kwargs: 
-            if name not in names: 
-                raise TypeError("unknown code attribute: %r" %(name, ))
-        if rec and hasattr(self.raw, 'co_consts'):  # jython 
-            newconstlist = []
-            co = self.raw
-            cotype = type(co)
-            for c in co.co_consts:
-                if isinstance(c, cotype):
-                    c = self.__class__(c).new(rec=True, **kwargs) 
-                newconstlist.append(c)
-            return self.new(rec=False, co_consts=tuple(newconstlist), **kwargs) 
-        for name in names:
-            if name not in kwargs:
-                kwargs[name] = getattr(self.raw, name)
-        arglist = [
-                 kwargs['co_argcount'],
-                 kwargs['co_nlocals'],
-                 kwargs.get('co_stacksize', 0), # jython
-                 kwargs.get('co_flags', 0), # jython
-                 kwargs.get('co_code', ''), # jython
-                 kwargs.get('co_consts', ()), # jython
-                 kwargs.get('co_names', []), # 
-                 kwargs['co_varnames'],
-                 kwargs['co_filename'],
-                 kwargs['co_name'],
-                 kwargs['co_firstlineno'],
-                 kwargs.get('co_lnotab', ''), #jython
-                 kwargs.get('co_freevars', None), #jython
-                 kwargs.get('co_cellvars', None), # jython
-        ]
-        if sys.version_info >= (3,0):
-            arglist.insert(1, kwargs['co_kwonlyargcount'])
-            return self.raw.__class__(*arglist)
-        else:
-            return py.std.new.code(*arglist)
-
     def path(self):
         """ return a path object pointing to source code"""
-        fn = self.raw.co_filename 
-        try:
-            return fn.__path__
-        except AttributeError:
-            p = py.path.local(self.raw.co_filename)
-            if not p.check():
-                # XXX maybe try harder like the weird logic 
-                # in the standard lib [linecache.updatecache] does? 
-                p = self.raw.co_filename
-            return p
+        p = py.path.local(self.raw.co_filename)
+        if not p.check():
+            # XXX maybe try harder like the weird logic 
+            # in the standard lib [linecache.updatecache] does? 
+            p = self.raw.co_filename
+        return p
                 
     path = property(path, None, None, "path of this code object")
 

Modified: pypy/trunk/py/_code/source.py
==============================================================================
--- pypy/trunk/py/_code/source.py	(original)
+++ pypy/trunk/py/_code/source.py	Sat May 22 13:29:12 2010
@@ -2,6 +2,7 @@
 import sys
 import inspect, tokenize
 import py
+from types import ModuleType
 cpy_compile = compile 
 
 try:
@@ -25,6 +26,8 @@
                 partlines = []
             if isinstance(part, Source):
                 partlines = part.lines
+            elif isinstance(part, (tuple, list)):
+                partlines = [x.rstrip("\n") for x in part]
             elif isinstance(part, py.builtin._basestring):
                 partlines = part.split('\n')
                 if rstrip:
@@ -171,7 +174,9 @@
         try:
             #compile(source+'\n', "x", "exec")
             syntax_checker(source+'\n')
-        except SyntaxError:
+        except KeyboardInterrupt:
+            raise
+        except Exception:
             return False
         else:
             return True
@@ -212,10 +217,17 @@
         else:
             if flag & _AST_FLAG:
                 return co
-            co_filename = MyStr(filename)
-            co_filename.__source__ = self
-            return py.code.Code(co).new(rec=1, co_filename=co_filename) 
-            #return newcode_withfilename(co, co_filename)
+            lines = [(x + "\n") for x in self.lines]
+            if sys.version_info[0] >= 3:
+                # XXX py3's inspect.getsourcefile() checks for a module
+                # and a pep302 __loader__ ... we don't have a module
+                # at code compile-time so we need to fake it here
+                m = ModuleType("_pycodecompile_pseudo_module")
+                py.std.inspect.modulesbyfile[filename] = None
+                py.std.sys.modules[None] = m
+                m.__loader__ = 1
+            py.std.linecache.cache[filename] = (1, None, lines, filename)
+            return co
 
 #
 # public API shortcut functions
@@ -224,11 +236,9 @@
 def compile_(source, filename=None, mode='exec', flags=
             generators.compiler_flag, dont_inherit=0):
     """ compile the given source to a raw code object,
-        which points back to the source code through
-        "co_filename.__source__".  All code objects
-        contained in the code object will recursively
-        also have this special subclass-of-string
-        filename.
+        and maintain an internal cache which allows later
+        retrieval of the source code for the code object 
+        and any recursively created code objects. 
     """
     if _ast is not None and isinstance(source, _ast.AST):
         # XXX should Source support having AST?
@@ -262,44 +272,26 @@
 #
 # helper functions
 #
-class MyStr(str):
-    """ custom string which allows to add attributes. """
 
 def findsource(obj):
-    obj = py.code.getrawcode(obj)
     try:
-        fullsource = obj.co_filename.__source__
-    except AttributeError:
-        try:
-            sourcelines, lineno = py.std.inspect.findsource(obj)
-        except (KeyboardInterrupt, SystemExit):
-            raise
-        except:
-            return None, None
-        source = Source()
-        source.lines = [line.rstrip() for line in sourcelines]
-        return source, lineno
-    else:
-        lineno = obj.co_firstlineno - 1        
-        return fullsource, lineno
-
+        sourcelines, lineno = py.std.inspect.findsource(obj)
+    except (KeyboardInterrupt, SystemExit):
+        raise
+    except:
+        return None, None
+    source = Source()
+    source.lines = [line.rstrip() for line in sourcelines]
+    return source, lineno
 
 def getsource(obj, **kwargs):
     obj = py.code.getrawcode(obj)
     try:
-        fullsource = obj.co_filename.__source__
-    except AttributeError:
-        try:
-            strsrc = inspect.getsource(obj)
-        except IndentationError:
-            strsrc = "\"Buggy python version consider upgrading, cannot get source\""
-        assert isinstance(strsrc, str)
-        return Source(strsrc, **kwargs)
-    else:
-        lineno = obj.co_firstlineno - 1
-        end = fullsource.getblockend(lineno)
-        return Source(fullsource[lineno:end+1], deident=True)
-
+        strsrc = inspect.getsource(obj)
+    except IndentationError:
+        strsrc = "\"Buggy python version consider upgrading, cannot get source\""
+    assert isinstance(strsrc, str)
+    return Source(strsrc, **kwargs)
 
 def deindent(lines, offset=None):
     if offset is None:

Modified: pypy/trunk/py/_io/capture.py
==============================================================================
--- pypy/trunk/py/_io/capture.py	(original)
+++ pypy/trunk/py/_io/capture.py	Sat May 22 13:29:12 2010
@@ -26,46 +26,56 @@
                 raise TypeError("not a byte value: %r" %(data,))
             StringIO.write(self, data)
 
+patchsysdict = {0: 'stdin', 1: 'stdout', 2: 'stderr'}
+
 class FDCapture: 
     """ Capture IO to/from a given os-level filedescriptor. """
     
-    def __init__(self, targetfd, tmpfile=None): 
+    def __init__(self, targetfd, tmpfile=None, now=True, patchsys=False):
         """ save targetfd descriptor, and open a new 
             temporary file there.  If no tmpfile is 
             specified a tempfile.Tempfile() will be opened
             in text mode. 
         """
         self.targetfd = targetfd
-        if tmpfile is None: 
+        if tmpfile is None and targetfd != 0:
             f = tempfile.TemporaryFile('wb+')
             tmpfile = dupfile(f, encoding="UTF-8") 
             f.close()
         self.tmpfile = tmpfile 
-        self._savefd = os.dup(targetfd)
-        os.dup2(self.tmpfile.fileno(), targetfd) 
-        self._patched = []
-
-    def setasfile(self, name, module=sys): 
-        """ patch <module>.<name> to self.tmpfile
-        """
-        key = (module, name)
-        self._patched.append((key, getattr(module, name)))
-        setattr(module, name, self.tmpfile) 
+        self._savefd = os.dup(self.targetfd)
+        if patchsys:
+            self._oldsys = getattr(sys, patchsysdict[targetfd])
+        if now:
+            self.start()
 
-    def unsetfiles(self): 
-        """ unpatch all patched items
-        """
-        while self._patched: 
-            (module, name), value = self._patched.pop()
-            setattr(module, name, value) 
+    def start(self):
+        try:
+            os.fstat(self._savefd)
+        except OSError:
+            raise ValueError("saved filedescriptor not valid, "
+                "did you call start() twice?")
+        if self.targetfd == 0 and not self.tmpfile:
+            fd = os.open(devnullpath, os.O_RDONLY)
+            os.dup2(fd, 0)
+            os.close(fd)
+            if hasattr(self, '_oldsys'):
+                setattr(sys, patchsysdict[self.targetfd], DontReadFromInput())
+        else:
+            fd = self.tmpfile.fileno()
+            os.dup2(self.tmpfile.fileno(), self.targetfd) 
+            if hasattr(self, '_oldsys'):
+                setattr(sys, patchsysdict[self.targetfd], self.tmpfile)
 
     def done(self): 
         """ unpatch and clean up, returns the self.tmpfile (file object)
         """
         os.dup2(self._savefd, self.targetfd) 
-        self.unsetfiles() 
         os.close(self._savefd) 
-        self.tmpfile.seek(0)
+        if self.targetfd != 0:
+            self.tmpfile.seek(0)
+        if hasattr(self, '_oldsys'):
+            setattr(sys, patchsysdict[self.targetfd], self._oldsys)
         return self.tmpfile 
 
     def writeorg(self, data):
@@ -100,7 +110,7 @@
         if encoding is not None:
             mode = mode.replace("b", "")
             buffering = True
-        return os.fdopen(newfd, mode, buffering, encoding, closefd=False)
+        return os.fdopen(newfd, mode, buffering, encoding, closefd=True)
     else:
         f = os.fdopen(newfd, mode, buffering) 
         if encoding is not None:
@@ -146,90 +156,94 @@
 
     def reset(self):
         """ reset sys.stdout/stderr and return captured output as strings. """
-        if hasattr(self, '_suspended'):
-            outfile = self._kwargs['out']
-            errfile = self._kwargs['err']
-            del self._kwargs
-        else:
-            outfile, errfile = self.done() 
+        outfile, errfile = self.done() 
         out, err = "", ""
-        if outfile:
+        if outfile and not outfile.closed:
             out = outfile.read()
             outfile.close()
-        if errfile and errfile != outfile:
+        if errfile and errfile != outfile and not errfile.closed:
             err = errfile.read()
             errfile.close()
         return out, err
 
     def suspend(self):
         """ return current snapshot captures, memorize tempfiles. """
-        assert not hasattr(self, '_suspended')
-        self._suspended = True
         outerr = self.readouterr()
         outfile, errfile = self.done()
-        self._kwargs['out'] = outfile
-        self._kwargs['err'] = errfile
         return outerr
 
-    def resume(self):
-        """ resume capturing with original temp files. """
-        assert self._suspended
-        self._initialize(**self._kwargs)
-        del self._suspended
-
 
 class StdCaptureFD(Capture): 
     """ This class allows to capture writes to FD1 and FD2 
         and may connect a NULL file to FD0 (and prevent
-        reads from sys.stdin)
+        reads from sys.stdin).  If any of the 0,1,2 file descriptors 
+        is invalid it will not be captured. 
     """
-    def __init__(self, out=True, err=True, 
-                 mixed=False, in_=True, patchsys=True): 
-        self._kwargs = locals().copy()
-        del self._kwargs['self']
-        self._initialize(**self._kwargs)
-
-    def _initialize(self, out=True, err=True, 
-                    mixed=False, in_=True, patchsys=True): 
+    def __init__(self, out=True, err=True, mixed=False, 
+        in_=True, patchsys=True, now=True):
+        self._options = locals()
+        self._save()
+        if now:
+            self.startall()
+
+    def _save(self):
+        in_ = self._options['in_']
+        out = self._options['out']
+        err = self._options['err']
+        mixed = self._options['mixed']
+        patchsys = self._options['patchsys']
         if in_:
-            self._oldin = (sys.stdin, os.dup(0))
-            sys.stdin  = DontReadFromInput()
-            fd = os.open(devnullpath, os.O_RDONLY)
-            os.dup2(fd, 0)
-            os.close(fd)
-        if out: 
+            try:
+                self.in_ = FDCapture(0, tmpfile=None, now=False,
+                    patchsys=patchsys)
+            except OSError:
+                pass 
+        if out:
             tmpfile = None
             if hasattr(out, 'write'):
                 tmpfile = out
-            self.out = py.io.FDCapture(1, tmpfile=tmpfile)
-            if patchsys: 
-                self.out.setasfile('stdout')
-        if err: 
-            if mixed and out:
+            try:
+                self.out = FDCapture(1, tmpfile=tmpfile, 
+                           now=False, patchsys=patchsys)
+                self._options['out'] = self.out.tmpfile
+            except OSError:
+                pass 
+        if err:
+            if out and mixed:
                 tmpfile = self.out.tmpfile 
             elif hasattr(err, 'write'):
                 tmpfile = err
             else:
                 tmpfile = None
-            self.err = py.io.FDCapture(2, tmpfile=tmpfile) 
-            if patchsys: 
-                self.err.setasfile('stderr')
+            try:
+                self.err = FDCapture(2, tmpfile=tmpfile, 
+                           now=False, patchsys=patchsys) 
+                self._options['err'] = self.err.tmpfile
+            except OSError:
+                pass 
+
+    def startall(self):
+        if hasattr(self, 'in_'):
+            self.in_.start()
+        if hasattr(self, 'out'):
+            self.out.start()
+        if hasattr(self, 'err'):
+            self.err.start()
+
+    def resume(self):
+        """ resume capturing with original temp files. """
+        self.startall()
 
     def done(self):
         """ return (outfile, errfile) and stop capturing. """
-        if hasattr(self, 'out'): 
+        outfile = errfile = None
+        if hasattr(self, 'out') and not self.out.tmpfile.closed:
             outfile = self.out.done() 
-        else:
-            outfile = None
-        if hasattr(self, 'err'): 
+        if hasattr(self, 'err') and not self.err.tmpfile.closed:
             errfile = self.err.done() 
-        else:
-            errfile = None 
-        if hasattr(self, '_oldin'):
-            oldsys, oldfd = self._oldin 
-            os.dup2(oldfd, 0)
-            os.close(oldfd)
-            sys.stdin = oldsys 
+        if hasattr(self, 'in_'):
+            tmpfile = self.in_.done()
+        self._save()
         return outfile, errfile 
 
     def readouterr(self):
@@ -252,69 +266,61 @@
         modifies sys.stdout|stderr|stdin attributes and does not 
         touch underlying File Descriptors (use StdCaptureFD for that). 
     """
-    def __init__(self, out=True, err=True, in_=True, mixed=False):
-        self._kwargs = locals().copy()
-        del self._kwargs['self']
-        self._initialize(**self._kwargs)
-
-    def _initialize(self, out, err, in_, mixed):
-        self._out = out
-        self._err = err 
-        self._in = in_
-        if out: 
-            self._oldout = sys.stdout
-            if not hasattr(out, 'write'):
-                out = TextIO()
-            sys.stdout = self.out = out
-        if err: 
-            self._olderr = sys.stderr
-            if out and mixed: 
-                err = self.out 
+    def __init__(self, out=True, err=True, in_=True, mixed=False, now=True):
+        self._oldout = sys.stdout
+        self._olderr = sys.stderr
+        self._oldin  = sys.stdin
+        if out and not hasattr(out, 'file'):
+            out = TextIO()
+        self.out = out
+        if err:
+            if mixed:
+                err = out
             elif not hasattr(err, 'write'):
                 err = TextIO()
-            sys.stderr = self.err = err
-        if in_:
-            self._oldin  = sys.stdin
-            sys.stdin  = self.newin  = DontReadFromInput()
+        self.err = err
+        self.in_ = in_
+        if now:
+            self.startall()
+
+    def startall(self):
+        if self.out: 
+            sys.stdout = self.out
+        if self.err: 
+            sys.stderr = self.err
+        if self.in_:
+            sys.stdin  = self.in_  = DontReadFromInput()
 
     def done(self): 
         """ return (outfile, errfile) and stop capturing. """
-        o,e = sys.stdout, sys.stderr
-        if self._out: 
-            try:
-                sys.stdout = self._oldout 
-            except AttributeError:
-                raise IOError("stdout capturing already reset")
-            del self._oldout
+        outfile = errfile = None
+        if self.out and not self.out.closed:
+            sys.stdout = self._oldout 
             outfile = self.out
             outfile.seek(0)
-        else:
-            outfile = None
-        if self._err: 
-            try:
-                sys.stderr = self._olderr 
-            except AttributeError:
-                raise IOError("stderr capturing already reset")
-            del self._olderr 
+        if self.err and not self.err.closed: 
+            sys.stderr = self._olderr 
             errfile = self.err 
             errfile.seek(0)
-        else:
-            errfile = None
-        if self._in:
+        if self.in_:
             sys.stdin = self._oldin 
         return outfile, errfile
 
+    def resume(self):
+        """ resume capturing with original temp files. """
+        self.startall()
+
     def readouterr(self):
         """ return snapshot value of stdout/stderr capturings. """
         out = err = ""
-        if self._out:
-            out = sys.stdout.getvalue()
-            sys.stdout.truncate(0)
-            sys.stdout.seek(0)
-        if self._err:
-            err = sys.stderr.getvalue()
-            sys.stderr.truncate(0)
-            sys.stderr.seek(0)
+        if self.out:
+            out = self.out.getvalue()
+            self.out.truncate(0)
+            self.out.seek(0)
+        if self.err:
+            err = self.err.getvalue()
+            self.err.truncate(0)
+            self.err.seek(0)
         return out, err 
 
 class DontReadFromInput:
@@ -344,5 +350,3 @@
         devnullpath = 'NUL'
     else:
         devnullpath = '/dev/null'
-
-

Modified: pypy/trunk/py/_plugin/pytest__pytest.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest__pytest.py	(original)
+++ pypy/trunk/py/_plugin/pytest__pytest.py	Sat May 22 13:29:12 2010
@@ -46,7 +46,8 @@
             recorder = RecordCalls()
             self._recorders[hookspec] = recorder
             self._registry.register(recorder)
-        self.hook = HookRelay(hookspecs, registry=self._registry)
+        self.hook = HookRelay(hookspecs, registry=self._registry, 
+            prefix="pytest_")
 
     def finish_recording(self):
         for recorder in self._recorders.values():

Modified: pypy/trunk/py/_plugin/pytest_capture.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_capture.py	(original)
+++ pypy/trunk/py/_plugin/pytest_capture.py	Sat May 22 13:29:12 2010
@@ -106,6 +106,14 @@
 def pytest_configure(config):
     config.pluginmanager.register(CaptureManager(), 'capturemanager')
 
+class NoCapture:
+    def startall(self):
+        pass
+    def resume(self):
+        pass
+    def suspend(self):
+        return "", ""
+
 class CaptureManager:
     def __init__(self):
         self._method2capture = {}
@@ -118,15 +126,17 @@
     def _makestringio(self):
         return py.io.TextIO() 
 
-    def _startcapture(self, method):
+    def _getcapture(self, method):
         if method == "fd": 
-            return py.io.StdCaptureFD(
+            return py.io.StdCaptureFD(now=False,
                 out=self._maketempfile(), err=self._maketempfile()
             )
         elif method == "sys":
-            return py.io.StdCapture(
+            return py.io.StdCapture(now=False,
                 out=self._makestringio(), err=self._makestringio()
             )
+        elif method == "no":
+            return NoCapture()
         else:
             raise ValueError("unknown capturing method: %r" % method)
 
@@ -152,27 +162,25 @@
         if hasattr(self, '_capturing'):
             raise ValueError("cannot resume, already capturing with %r" % 
                 (self._capturing,))
-        if method != "no":
-            cap = self._method2capture.get(method)
-            if cap is None:
-                cap = self._startcapture(method)
-                self._method2capture[method] = cap 
-            else:
-                cap.resume()
+        cap = self._method2capture.get(method)
         self._capturing = method 
+        if cap is None:
+            self._method2capture[method] = cap = self._getcapture(method)
+            cap.startall()
+        else:
+            cap.resume()
 
     def suspendcapture(self, item=None):
         self.deactivate_funcargs()
         if hasattr(self, '_capturing'):
             method = self._capturing
-            if method != "no":
-                cap = self._method2capture[method]
+            cap = self._method2capture.get(method)
+            if cap is not None:
                 outerr = cap.suspend()
-            else:
-                outerr = "", ""
             del self._capturing
             if item:
-                outerr = (item.outerr[0] + outerr[0], item.outerr[1] + outerr[1])
+                outerr = (item.outerr[0] + outerr[0], 
+                          item.outerr[1] + outerr[1])
             return outerr 
         return "", ""
 
@@ -180,19 +188,17 @@
         if not hasattr(pyfuncitem, 'funcargs'):
             return
         assert not hasattr(self, '_capturing_funcargs')
-        l = []
-        for name, obj in pyfuncitem.funcargs.items():
-            if name == 'capfd' and not hasattr(os, 'dup'):
-                py.test.skip("capfd funcarg needs os.dup")
+        self._capturing_funcargs = capturing_funcargs = []
+        for name, capfuncarg in pyfuncitem.funcargs.items():
             if name in ('capsys', 'capfd'):
-                obj._start()
-                l.append(obj)
-        if l:
-            self._capturing_funcargs = l
+                capturing_funcargs.append(capfuncarg)
+                capfuncarg._start()
 
     def deactivate_funcargs(self):
-        if hasattr(self, '_capturing_funcargs'):
-            for capfuncarg in self._capturing_funcargs:
+        capturing_funcargs = getattr(self, '_capturing_funcargs', None)
+        if capturing_funcargs is not None:
+            while capturing_funcargs:
+                capfuncarg = capturing_funcargs.pop()
                 capfuncarg._finalize()
             del self._capturing_funcargs
 
@@ -256,16 +262,19 @@
     platform does not have ``os.dup`` (e.g. Jython) tests using
     this funcarg will automatically skip. 
     """ 
+    if not hasattr(os, 'dup'):
+        py.test.skip("capfd funcarg needs os.dup")
     return CaptureFuncarg(request, py.io.StdCaptureFD)
 
 
 class CaptureFuncarg:
     def __init__(self, request, captureclass):
         self._cclass = captureclass
+        self.capture = self._cclass(now=False)
         #request.addfinalizer(self._finalize)
 
     def _start(self):
-        self.capture = self._cclass()
+        self.capture.startall()
 
     def _finalize(self):
         if hasattr(self, 'capture'):
@@ -276,6 +285,4 @@
         return self.capture.readouterr()
 
     def close(self):
-        self.capture.reset()
-        del self.capture
-
+        self._finalize()

Modified: pypy/trunk/py/_plugin/pytest_genscript.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_genscript.py	(original)
+++ pypy/trunk/py/_plugin/pytest_genscript.py	Sat May 22 13:29:12 2010
@@ -4,14 +4,7 @@
 """
 
 import os
-import zlib
-import base64
 import sys
-try:
-    import pickle
-except Importerror:
-    import cPickle as pickle
-
 def pytest_addoption(parser):
     group = parser.getgroup("debugconfig")
     group.addoption("--genscript", action="store", default=None, 
@@ -30,6 +23,13 @@
         raise SystemExit(0)
 
 def main(pybasedir, outfile, infile):
+    import base64
+    import zlib
+    try:
+        import pickle
+    except Importerror:
+        import cPickle as pickle
+
     outfile = str(outfile)
     infile = str(infile)
     assert os.path.isabs(outfile)

Modified: pypy/trunk/py/_plugin/pytest_junitxml.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_junitxml.py	(original)
+++ pypy/trunk/py/_plugin/pytest_junitxml.py	Sat May 22 13:29:12 2010
@@ -56,10 +56,15 @@
     def append_failure(self, report):
         self._opentestcase(report)
         #msg = str(report.longrepr.reprtraceback.extraline)
-        self.appendlog('<failure message="test failure">%s</failure>', 
-            report.longrepr)
+        if "xfail" in report.keywords:
+            self.appendlog(
+                '<skipped message="xfail-marked test passes unexpectedly"/>')
+            self.skipped += 1
+        else:
+            self.appendlog('<failure message="test failure">%s</failure>', 
+                report.longrepr)
+            self.failed += 1
         self._closetestcase()
-        self.failed += 1
 
     def _opentestcase_collectfailure(self, report):
         node = report.collector
@@ -95,7 +100,12 @@
 
     def append_skipped(self, report):
         self._opentestcase(report)
-        self.appendlog("<skipped/>")
+        if "xfail" in report.keywords:
+            self.appendlog(
+                '<skipped message="expected test failure">%s</skipped>', 
+                report.keywords['xfail'])
+        else:
+            self.appendlog("<skipped/>")
         self._closetestcase()
         self.skipped += 1
 

Modified: pypy/trunk/py/_plugin/pytest_mark.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_mark.py	(original)
+++ pypy/trunk/py/_plugin/pytest_mark.py	Sat May 22 13:29:12 2010
@@ -34,38 +34,43 @@
 
 .. _`scoped-marking`:
 
-Marking classes or modules 
+Marking whole classes or modules 
 ----------------------------------------------------
 
-To mark all methods of a class set a ``pytestmark`` attribute like this::
+If you are programming with Python2.6 you may use ``py.test.mark`` decorators
+with classes to apply markers to all its test methods::
+
+    @py.test.mark.webtest
+    class TestClass:
+        def test_startup(self):
+            ...
+
+This is equivalent to directly applying the decorator to the
+``test_startup`` function. 
+
+To remain compatible with Python2.5 you can instead set a 
+``pytestmark`` attribute on a TestClass like this::
 
     import py
 
     class TestClass:
         pytestmark = py.test.mark.webtest
 
-You can re-use the same markers that you would use for decorating
-a function - in fact this marker decorator will be applied
-to all test methods of the class. 
+or if you need to use multiple markers::
+
+    import py
+
+    class TestClass:
+        pytestmark = [py.test.mark.webtest, pytest.mark.slowtest]
 
 You can also set a module level marker::
 
     import py
     pytestmark = py.test.mark.webtest
 
-in which case then the marker decorator will be applied to all functions and 
+in which case then it will be applied to all functions and 
 methods defined in the module.  
 
-The order in which marker functions are called is this::
-
-    per-function (upon import of module already) 
-    per-class
-    per-module 
-
-Later called markers may overwrite previous key-value settings. 
-Positional arguments are all appended to the same 'args' list 
-of the Marker object. 
-
 Using "-k MARKNAME" to select tests
 ----------------------------------------------------
 
@@ -105,15 +110,23 @@
         """ if passed a single callable argument: decorate it with mark info. 
             otherwise add *args/**kwargs in-place to mark information. """
         if args:
-            if len(args) == 1 and hasattr(args[0], '__call__'):
-                func = args[0]
-                holder = getattr(func, self.markname, None)
-                if holder is None:
-                    holder = MarkInfo(self.markname, self.args, self.kwargs)
-                    setattr(func, self.markname, holder)
+            func = args[0]
+            if len(args) == 1 and hasattr(func, '__call__') or \
+               hasattr(func, '__bases__'):
+                if hasattr(func, '__bases__'):
+                    l = func.__dict__.setdefault("pytestmark", [])
+                    if not isinstance(l, list):
+                       func.pytestmark = [l, self]
+                    else: 
+                       l.append(self)
                 else:
-                    holder.kwargs.update(self.kwargs)
-                    holder.args.extend(self.args)
+                    holder = getattr(func, self.markname, None)
+                    if holder is None:
+                        holder = MarkInfo(self.markname, self.args, self.kwargs)
+                        setattr(func, self.markname, holder)
+                    else:
+                        holder.kwargs.update(self.kwargs)
+                        holder.args.extend(self.args)
                 return func
             else:
                 self.args.extend(args)
@@ -147,6 +160,10 @@
         func = getattr(func, 'im_func', func)  # py2
         for parent in [x for x in (mod, cls) if x]:
             marker = getattr(parent.obj, 'pytestmark', None)
-            if isinstance(marker, MarkDecorator):
-                marker(func)
+            if marker is not None:
+                if not isinstance(marker, list):
+                    marker = [marker]
+                for mark in marker:
+                    if isinstance(mark, MarkDecorator):
+                        mark(func)
     return item

Modified: pypy/trunk/py/_plugin/pytest_pytester.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_pytester.py	(original)
+++ pypy/trunk/py/_plugin/pytest_pytester.py	Sat May 22 13:29:12 2010
@@ -110,7 +110,7 @@
     def _makefile(self, ext, args, kwargs):
         items = list(kwargs.items())
         if args:
-            source = "\n".join(map(str, args))
+            source = "\n".join(map(str, args)) + "\n"
             basename = self.request.function.__name__
             items.insert(0, (basename, source))
         ret = None
@@ -294,8 +294,10 @@
         ret = popen.wait()
         f1.close()
         f2.close()
-        out = p1.read("rb").decode("utf-8").splitlines()
-        err = p2.read("rb").decode("utf-8").splitlines()
+        out = p1.read("rb")
+        out = getdecoded(out).splitlines()
+        err = p2.read("rb")
+        err = getdecoded(err).splitlines()
         def dump_lines(lines, fp):
             try:
                 for line in lines:
@@ -360,6 +362,13 @@
         child.timeout = expect_timeout
         return child
 
+def getdecoded(out):
+        try:
+            return out.decode("utf-8")
+        except UnicodeDecodeError:
+            return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (
+                    py.io.saferepr(out),)
+
 class PseudoPlugin:
     def __init__(self, vars):
         self.__dict__.update(vars) 

Modified: pypy/trunk/py/_plugin/pytest_resultlog.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_resultlog.py	(original)
+++ pypy/trunk/py/_plugin/pytest_resultlog.py	Sat May 22 13:29:12 2010
@@ -73,7 +73,7 @@
             code = report.shortrepr
         if code == 'x':
             longrepr = str(report.longrepr)
-        elif code == 'P':
+        elif code == 'X':
             longrepr = ''
         elif report.passed:
             longrepr = ""

Modified: pypy/trunk/py/_plugin/pytest_runner.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_runner.py	(original)
+++ pypy/trunk/py/_plugin/pytest_runner.py	Sat May 22 13:29:12 2010
@@ -10,6 +10,7 @@
         'skip'         : skip,
         'importorskip' : importorskip,
         'fail'         : fail, 
+        'xfail'        : xfail, 
         'exit'         : exit, 
     }
 
@@ -295,6 +296,10 @@
     """ raised from an explicit call to py.test.fail() """
     __module__ = 'builtins'
 
+class XFailed(OutcomeException): 
+    """ raised from an explicit call to py.test.xfail() """
+    __module__ = 'builtins'
+
 class ExceptionFailure(Failed): 
     """ raised by py.test.raises on an exception-assertion mismatch. """
     def __init__(self, expr, expected, msg=None, excinfo=None): 
@@ -335,6 +340,14 @@
 
 fail.Exception = Failed
 
+def xfail(reason=""):
+    """ xfail an executing test or setup functions, taking an optional 
+    reason string.
+    """
+    __tracebackhide__ = True
+    raise XFailed(reason)
+xfail.Exception = XFailed
+
 def raises(ExpectedException, *args, **kwargs):
     """ if args[0] is callable: raise AssertionError if calling it with 
         the remaining arguments does not raise the expected exception.  

Modified: pypy/trunk/py/_plugin/pytest_skipping.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_skipping.py	(original)
+++ pypy/trunk/py/_plugin/pytest_skipping.py	Sat May 22 13:29:12 2010
@@ -60,6 +60,19 @@
             #
 
 The ``pytestmark`` decorator will be applied to each test function.
+If your code targets python2.6 or above you can equivalently use 
+the skipif decorator on classes::
+
+    @py.test.mark.skipif("sys.platform == 'win32'")
+    class TestPosixCalls:
+    
+        def test_function(self):
+            # will not be setup or run under 'win32' platform
+            #
+
+It is fine in general to apply multiple "skipif" decorators
+on a single function - this means that if any of the conditions
+apply the function will be skipped. 
 
 .. _`whole class- or module level`: mark.html#scoped-marking
 
@@ -82,10 +95,17 @@
 depending on platform::
 
     @py.test.mark.xfail("sys.version_info >= (3,0)")
-
     def test_function():
         ...
 
+To not run a test and still regard it as "xfailed"::
+
+    @py.test.mark.xfail(..., run=False)
+
+To specify an explicit reason to be shown with xfailure detail::
+
+    @py.test.mark.xfail(..., reason="my reason")
+
 
 skipping on a missing import dependency
 --------------------------------------------------
@@ -115,11 +135,14 @@
             py.test.skip("unsuppored configuration")
 
 """
-# XXX py.test.skip, .importorskip and the Skipped class 
-# should also be defined in this plugin, requires thought/changes
 
 import py
 
+def pytest_addoption(parser):
+    group = parser.getgroup("general")
+    group.addoption('--runxfail', 
+           action="store_true", dest="runxfail", default=False,
+           help="run tests even if they are marked xfail")
 
 class MarkEvaluator:
     def __init__(self, item, name):
@@ -134,17 +157,20 @@
     def istrue(self):
         if self.holder:
             d = {'os': py.std.os, 'sys': py.std.sys, 'config': self.item.config}
-            self.result = True
-            for expr in self.holder.args:
-                self.expr = expr
-                if isinstance(expr, str):
-                    result = cached_eval(self.item.config, expr, d)
-                else:
-                    result = expr
-                if not result:
-                    self.result = False
+            if self.holder.args:
+                self.result = False
+                for expr in self.holder.args:
                     self.expr = expr
-                    break
+                    if isinstance(expr, str):
+                        result = cached_eval(self.item.config, expr, d)
+                    else:
+                        result = expr
+                    if result:
+                        self.result = True
+                        self.expr = expr
+                        break
+            else:
+                self.result = True
         return getattr(self, 'result', False)
 
     def get(self, attr, default=None):
@@ -161,27 +187,53 @@
         
 
 def pytest_runtest_setup(item):
-    expr, result = evalexpression(item, 'skipif')
-    if result:
-        py.test.skip(expr)
+    if not isinstance(item, py.test.collect.Function):
+        return
+    evalskip = MarkEvaluator(item, 'skipif')
+    if evalskip.istrue():
+        py.test.skip(evalskip.getexplanation())
+    item._evalxfail = MarkEvaluator(item, 'xfail')
+    if not item.config.getvalue("runxfail"):
+        if item._evalxfail.istrue():
+            if not item._evalxfail.get('run', True):
+                py.test.skip("xfail")
 
 def pytest_runtest_makereport(__multicall__, item, call):
-    if call.when != "call":
+    if not isinstance(item, py.test.collect.Function):
         return
-    expr, result = evalexpression(item, 'xfail')
-    rep = __multicall__.execute()
-    if result:
-        if call.excinfo:
-            rep.skipped = True
-            rep.failed = rep.passed = False
+    if not (call.excinfo and 
+        call.excinfo.errisinstance(py.test.xfail.Exception)):
+        evalxfail = getattr(item, '_evalxfail', None)
+        if not evalxfail:
+            return
+    if call.excinfo and call.excinfo.errisinstance(py.test.xfail.Exception):
+        rep = __multicall__.execute()
+        rep.keywords['xfail'] = "reason: " + call.excinfo.value.msg
+        rep.skipped = True
+        rep.failed = False
+        return rep
+    if call.when == "setup":
+        rep = __multicall__.execute()
+        if rep.skipped and evalxfail.istrue():
+            expl = evalxfail.getexplanation()
+            if not evalxfail.get("run", True):
+                expl = "[NOTRUN] " + expl
+            rep.keywords['xfail'] = expl
+        return rep
+    elif call.when == "call":
+        rep = __multicall__.execute()
+        if not item.config.getvalue("runxfail") and evalxfail.istrue():
+            if call.excinfo:
+                rep.skipped = True
+                rep.failed = rep.passed = False
+            else:
+                rep.skipped = rep.passed = False
+                rep.failed = True
+            rep.keywords['xfail'] = evalxfail.getexplanation()
         else:
-            rep.skipped = rep.passed = False
-            rep.failed = True
-        rep.keywords['xfail'] = expr 
-    else:
-        if 'xfail' in rep.keywords:
-            del rep.keywords['xfail']
-    return rep
+            if 'xfail' in rep.keywords:
+                del rep.keywords['xfail']
+        return rep
 
 # called by terminalreporter progress reporting
 def pytest_report_teststatus(report):
@@ -189,7 +241,7 @@
         if report.skipped:
             return "xfailed", "x", "xfail"
         elif report.failed:
-            return "xpassed", "P", "xpass"
+            return "xpassed", "X", "XPASS"
 
 # called by the terminalreporter instance/plugin
 def pytest_terminal_summary(terminalreporter):
@@ -229,13 +281,8 @@
     xfailed = terminalreporter.stats.get("xfailed")
     if xfailed:
         for rep in xfailed:
-            entry = rep.longrepr.reprcrash
-            modpath = rep.item.getmodpath(includemodule=True)
-            pos = "%s %s:%d: " %(modpath, entry.path, entry.lineno)
-            reason = rep.longrepr.reprcrash.message
-            i = reason.find("\n")
-            if i != -1:
-                reason = reason[:i]
+            pos = terminalreporter.gettestid(rep.item)
+            reason = rep.keywords['xfail']
             lines.append("XFAIL %s %s" %(pos, reason))
 
 def show_xpassed(terminalreporter, lines):
@@ -246,24 +293,6 @@
             reason = rep.keywords['xfail']
             lines.append("XPASS %s %s" %(pos, reason))
 
-
-def evalexpression(item, keyword):
-    if isinstance(item, py.test.collect.Function):
-        markholder = getattr(item.obj, keyword, None)
-        result = False
-        if markholder:
-            d = {'os': py.std.os, 'sys': py.std.sys, 'config': item.config}
-            expr, result = None, True
-            for expr in markholder.args:
-                if isinstance(expr, str):
-                    result = cached_eval(item.config, expr, d)
-                else:
-                    result = expr
-                if not result:
-                    break
-            return expr, result
-    return None, False
-
 def cached_eval(config, expr, d):
     if not hasattr(config, '_evalcache'):
         config._evalcache = {}

Modified: pypy/trunk/py/_plugin/pytest_terminal.py
==============================================================================
--- pypy/trunk/py/_plugin/pytest_terminal.py	(original)
+++ pypy/trunk/py/_plugin/pytest_terminal.py	Sat May 22 13:29:12 2010
@@ -141,6 +141,20 @@
         else: 
             return "???", dict(red=True)
 
+    def gettestid(self, item, relative=True):
+        fspath = item.fspath
+        chain = [x for x in item.listchain() if x.fspath == fspath]
+        chain = chain[1:]
+        names = [x.name for x in chain if x.name != "()"]
+        path = item.fspath
+        if relative:
+            relpath = path.relto(self.curdir)
+            if relpath:
+                path = relpath
+        names.insert(0, str(path))
+        return "::".join(names)
+
+
     def pytest_internalerror(self, excrepr):
         for line in str(excrepr).split("\n"):
             self.write_line("INTERNALERROR> " + line)

Modified: pypy/trunk/py/_test/cmdline.py
==============================================================================
--- pypy/trunk/py/_test/cmdline.py	(original)
+++ pypy/trunk/py/_test/cmdline.py	Sat May 22 13:29:12 2010
@@ -16,8 +16,9 @@
         colitems = config.getinitialnodes()
         exitstatus = session.main(colitems)
         config.pluginmanager.do_unconfigure(config)
-        raise SystemExit(exitstatus)
     except config.Error:
         e = sys.exc_info()[1]
         sys.stderr.write("ERROR: %s\n" %(e.args[0],))
-        raise SystemExit(3)
+        exitstatus = 3
+    py.test.config = py.test.config.__class__()
+    return exitstatus

Modified: pypy/trunk/py/_test/pycollect.py
==============================================================================
--- pypy/trunk/py/_test/pycollect.py	(original)
+++ pypy/trunk/py/_test/pycollect.py	Sat May 22 13:29:12 2010
@@ -393,5 +393,5 @@
 def hasinit(obj):
     init = getattr(obj, '__init__', None)
     if init:
-        if not isinstance(init, type(object.__init__)):
+        if init != object.__init__:
             return True

Modified: pypy/trunk/pypy/conftest.py
==============================================================================
--- pypy/trunk/pypy/conftest.py	(original)
+++ pypy/trunk/pypy/conftest.py	Sat May 22 13:29:12 2010
@@ -383,10 +383,15 @@
         if option.runappdirect:
             return target()
         space = gettestobjspace() 
-        func = app2interp_temp(target)
+        filename = self._getdynfilename(target)
+        func = app2interp_temp(target, filename=filename)
         print "executing", func
         self.execute_appex(space, func, space)
 
+    def _getdynfilename(self, func):
+        code = getattr(func, 'im_func', func).func_code
+        return "[%s:%s]" % (code.co_filename, code.co_firstlineno)
+
 class AppTestMethod(AppTestFunction): 
 
     def setup(self): 
@@ -410,7 +415,8 @@
         if option.runappdirect:
             return target()
         space = target.im_self.space 
-        func = app2interp_temp(target.im_func) 
+        filename = self._getdynfilename(target)
+        func = app2interp_temp(target.im_func, filename=filename) 
         w_instance = self.parent.w_instance 
         self.execute_appex(space, func, space, w_instance) 
 

Modified: pypy/trunk/pypy/interpreter/gateway.py
==============================================================================
--- pypy/trunk/pypy/interpreter/gateway.py	(original)
+++ pypy/trunk/pypy/interpreter/gateway.py	Sat May 22 13:29:12 2010
@@ -821,9 +821,6 @@
 # and now for something completely different ... 
 #
 
-class MyStr(str):
-    pass
-
 class ApplevelClass:
     """NOT_RPYTHON
     A container for app-level source code that should be executed
@@ -837,8 +834,7 @@
         # HAAACK (but a good one)
         if filename is None:
             f = sys._getframe(1)
-            filename = MyStr('<%s:%d>' % (f.f_code.co_filename, f.f_lineno))
-            filename.__source__ = py.code.Source(source)
+            filename = '<%s:%d>' % (f.f_code.co_filename, f.f_lineno)
         self.filename = filename
         self.source = str(py.code.Source(source).deindent())
         self.modname = modname
@@ -848,6 +844,9 @@
             self.can_use_geninterp = False
         else:
             self.can_use_geninterp = True
+        # make source code available for tracebacks
+        lines = [x + "\n" for x in source.split("\n")]
+        py.std.linecache.cache[filename] = (1, None, lines, filename)
 
     def __repr__(self):
         return "<ApplevelClass filename=%r can_use_geninterp=%r>" % (self.filename, self.can_use_geninterp)
@@ -1081,14 +1080,14 @@
 
 # ____________________________________________________________
 
-def appdef(source, applevel=ApplevelClass):
+def appdef(source, applevel=ApplevelClass, filename=None):
     """ NOT_RPYTHON: build an app-level helper function, like for example:
     myfunc = appdef('''myfunc(x, y):
                            return x+y
                     ''')
     """ 
     if not isinstance(source, str): 
-        source = str(py.code.Source(source).strip())
+        source = py.std.inspect.getsource(source).lstrip()
         while source.startswith('@py.test.mark.'):
             # these decorators are known to return the same function
             # object, we may ignore them
@@ -1100,7 +1099,11 @@
     assert p >= 0
     funcname = source[:p].strip()
     source = source[p:]
-    return applevel("def %s%s\n" % (funcname, source)).interphook(funcname)
+    assert source.strip()
+    funcsource = "def %s%s\n"  % (funcname, source)
+    #for debugging of wrong source code: py.std.parser.suite(funcsource)
+    a = applevel(funcsource, filename=filename)
+    return a.interphook(funcname)
 
 applevel = ApplevelClass   # backward compatibility
 app2interp = appdef   # backward compatibility
@@ -1118,6 +1121,6 @@
         return PyPyCacheDir.build_applevelinterp_dict(self, space)
 
 # app2interp_temp is used for testing mainly
-def app2interp_temp(func, applevel_temp=applevel_temp):
+def app2interp_temp(func, applevel_temp=applevel_temp, filename=None):
     """ NOT_RPYTHON """
-    return appdef(func, applevel_temp)
+    return appdef(func, applevel_temp, filename=filename)

Modified: pypy/trunk/pypy/interpreter/test/test_code.py
==============================================================================
--- pypy/trunk/pypy/interpreter/test/test_code.py	(original)
+++ pypy/trunk/pypy/interpreter/test/test_code.py	Sat May 22 13:29:12 2010
@@ -6,11 +6,7 @@
     def setup_class(cls):
         space = gettestobjspace()
         cls.space = space
-        if py.test.config.option.runappdirect:
-            filename = __file__
-        else:
-            filename = gateway.__file__
-
+        filename = __file__
         if filename[-3:] != '.py':
             filename = filename[:-1]
 

Modified: pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_warmspot.py	Sat May 22 13:29:12 2010
@@ -131,7 +131,7 @@
         assert warmrunnerdescr.state.optimize_loop is optimize.optimize_loop
         assert warmrunnerdescr.state.optimize_bridge is optimize.optimize_bridge
 
-    def test_static_debug_level(self):
+    def test_static_debug_level(self, capfd):
         py.test.skip("debug_level is being deprecated")
         from pypy.rlib.jit import DEBUG_PROFILE, DEBUG_OFF, DEBUG_STEPS
         from pypy.jit.metainterp.jitprof import EmptyProfiler, Profiler
@@ -144,36 +144,31 @@
                 n -= 1
             return n
 
-        outerr = py.io.StdCaptureFD()
+        capfd.readouterr()
         self.meta_interp(f, [10], debug_level=DEBUG_OFF,
                                   ProfilerClass=Profiler)
-        out, errf = outerr.done()
-        err = errf.read()
+        out, err = capfd.readouterr()
         assert not 'ENTER' in err
         assert not 'LEAVE' in err
         assert not "Running asm" in err
-        outerr = py.io.StdCaptureFD()
         self.meta_interp(f, [10], debug_level=DEBUG_PROFILE,
                                   ProfilerClass=Profiler)
-        out, errf = outerr.done()
-        err = errf.read()
+        out, err = capfd.readouterr()
         assert not 'ENTER' in err
         assert not 'LEAVE' in err
         assert not 'compiled new' in err
         assert "Running asm" in err
-        outerr = py.io.StdCaptureFD()
+
         self.meta_interp(f, [10], debug_level=DEBUG_STEPS,
                                   ProfilerClass=Profiler)
-        out, errf = outerr.done()
-        err = errf.read()
+        out, err = capfd.readouterr()
         assert 'ENTER' in err
         assert 'LEAVE' in err
         assert "Running asm" in err
-        outerr = py.io.StdCaptureFD()
+
         self.meta_interp(f, [10], debug_level=DEBUG_STEPS,
                                   ProfilerClass=EmptyProfiler)
-        out, errf = outerr.done()
-        err = errf.read()
+        out, err = capfd.readouterr()
         assert 'ENTER' in err
         assert 'LEAVE' in err
         assert not "Running asm" in err

Modified: pypy/trunk/pypy/tool/pytest/appsupport.py
==============================================================================
--- pypy/trunk/pypy/tool/pytest/appsupport.py	(original)
+++ pypy/trunk/pypy/tool/pytest/appsupport.py	Sat May 22 13:29:12 2010
@@ -11,7 +11,8 @@
         self.raw = pycode
         self.w_file = space.getattr(pycode, space.wrap('co_filename'))
         self.name = space.getattr(pycode, space.wrap('co_name'))
-        self.firstlineno = space.unwrap(space.getattr(pycode, space.wrap('co_firstlineno')))
+        self.firstlineno = space.unwrap(
+            space.getattr(pycode, space.wrap('co_firstlineno'))) - 1
         #try:
         #    self.path = space.unwrap(space.getattr(self.w_file, space.wrap('__path__')))
         #except OperationError:
@@ -20,13 +21,14 @@
         self.space = space
     
     def fullsource(self):
+        filename = self.space.str_w(self.w_file)
+        source = py.code.Source(py.std.linecache.getlines(filename))
+        if source.lines:
+            return source
         try:
-            return self.space.str_w(self.w_file).__source__
-        except AttributeError:
-            try:
-                return py.code.Source(self.path.read(mode="rU"))
-            except py.error.Error:
-                return None
+            return py.code.Source(self.path.read(mode="rU"))
+        except py.error.Error:
+            return None
     fullsource = property(fullsource, None, None, "Full source of AppCode")
 
     def getargs(self):
@@ -211,12 +213,19 @@
         source = py.code.Source(expr)
         frame = space.getexecutioncontext().gettopframe()
         w_locals = frame.getdictscope()
+        pycode = frame.pycode
+        filename = "<%s:%s>" %(pycode.co_filename, frame.f_lineno)
+        lines = [x + "\n" for x in expr.split("\n")]
+        py.std.linecache.cache[filename] = (1, None, lines, filename)
         w_locals = space.call_method(w_locals, 'copy')
         for key, w_value in kwds_w.items():
             space.setitem(w_locals, space.wrap(key), w_value)
+        #filename = __file__
+        #if filename.endswith("pyc"):
+        #    filename = filename[:-1]
         try:
             space.exec_(str(source), frame.w_globals, w_locals,
-                        filename=__file__)
+                        filename=filename)
         except OperationError, e:
             if e.match(space, w_ExpectedException):
                 return _exc_info(space, e)

Modified: pypy/trunk/pypy/tool/pytest/test/test_appsupport.py
==============================================================================
--- pypy/trunk/pypy/tool/pytest/test/test_appsupport.py	(original)
+++ pypy/trunk/pypy/tool/pytest/test/test_appsupport.py	Sat May 22 13:29:12 2010
@@ -70,6 +70,49 @@
         assert result.ret == 0
         result.stdout.fnmatch_lines(["*2 passed*"])
 
+def test_applevel_raises_simple_display(testdir):
+    setpypyconftest(testdir)
+    p = testdir.makepyfile("""
+        def app_test_raises():
+            raises(ValueError, x)
+        class AppTestRaises:
+            def test_func(self):
+                raises (ValueError, x)
+        #
+    """)
+    result = testdir.runpytest(p, "-s")
+    assert result.ret == 1
+    result.stdout.fnmatch_lines([
+        "*E*application-level*NameError*x*not defined",
+        "*test_func(self)*",
+        ">*raises*ValueError*",
+        "*E*application-level*NameError*x*not defined",
+        "*test_applevel_raises_simple_display*",
+    ])
+    result = testdir.runpytest(p) # this time we may run the pyc file
+    assert result.ret == 1
+    result.stdout.fnmatch_lines([
+        "*E*application-level*NameError*x*not defined",
+    ])
+
+def test_applevel_raises_display(testdir):
+    setpypyconftest(testdir)
+    p = testdir.makepyfile("""
+        def app_test_raises():
+            raises(ValueError, "x")
+            pass
+    """)
+    result = testdir.runpytest(p, "-s")
+    assert result.ret == 1
+    result.stdout.fnmatch_lines([
+        "*E*application-level*NameError*x*not defined",
+    ])
+    result = testdir.runpytest(p) # this time we may run the pyc file
+    assert result.ret == 1
+    result.stdout.fnmatch_lines([
+        "*E*application-level*NameError*x*not defined",
+    ])
+
 def app_test_raises():
     info = raises(TypeError, id)
     assert info.type is TypeError



More information about the Pypy-commit mailing list