[py-svn] pytest commit e4e9f0f7c4dd: document and refine py.test.fail helper and strike superflous ExceptionFailure class

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Sat Nov 6 09:57:01 CET 2010


# HG changeset patch -- Bitbucket.org
# Project pytest
# URL http://bitbucket.org/hpk42/pytest/overview
# User holger krekel <holger at merlinux.eu>
# Date 1288996651 -3600
# Node ID e4e9f0f7c4dd2e8b39991b38db44f676802f1b99
# Parent  567a0d7b8fa2be3820a64b6a4d5ecb326b383f7a
document and refine py.test.fail helper and strike superflous ExceptionFailure class
refine builtin organisation and start a new doc

--- /dev/null
+++ b/doc/example/layout1/setup.cfg
@@ -0,0 +1,4 @@
+[pytest]
+testfilepatterns =
+    ${topdir}/tests/unit/test_${basename}
+    ${topdir}/tests/functional/*.py

--- a/testing/plugin/test_python.py
+++ b/testing/plugin/test_python.py
@@ -1,4 +1,4 @@
-import py, sys
+import pytest, py, sys
 from pytest.plugin import python as funcargs
 
 class TestModule:
@@ -1118,3 +1118,60 @@ def test_show_funcarg(testdir):
             "*temporary directory*",
         ]
     )
+
+class TestRaises:
+    def test_raises(self):
+        source = "int('qwe')"
+        excinfo = py.test.raises(ValueError, source)
+        code = excinfo.traceback[-1].frame.code
+        s = str(code.fullsource)
+        assert s == source
+
+    def test_raises_exec(self):
+        py.test.raises(ValueError, "a,x = []")
+
+    def test_raises_syntax_error(self):
+        py.test.raises(SyntaxError, "qwe qwe qwe")
+
+    def test_raises_function(self):
+        py.test.raises(ValueError, int, 'hello')
+
+    def test_raises_callable_no_exception(self):
+        class A:
+            def __call__(self):
+                pass
+        try:
+            py.test.raises(ValueError, A())
+        except py.test.raises.Exception:
+            pass
+
+    @py.test.mark.skipif('sys.version < "2.5"')
+    def test_raises_as_contextmanager(self, testdir):
+        testdir.makepyfile("""
+            from __future__ import with_statement
+            import py
+
+            def test_simple():
+                with py.test.raises(ZeroDivisionError) as excinfo:
+                    assert isinstance(excinfo, py.code.ExceptionInfo)
+                    1/0
+                print (excinfo)
+                assert excinfo.type == ZeroDivisionError
+
+            def test_noraise():
+                with py.test.raises(py.test.raises.Exception):
+                    with py.test.raises(ValueError):
+                           int()
+
+            def test_raise_wrong_exception_passes_by():
+                with py.test.raises(ZeroDivisionError):
+                    with py.test.raises(ValueError):
+                           1/0
+        """)
+        result = testdir.runpytest()
+        result.stdout.fnmatch_lines([
+            '*3 passed*',
+        ])
+
+
+

--- a/testing/plugin/test_skipping.py
+++ b/testing/plugin/test_skipping.py
@@ -417,7 +417,7 @@ def test_skipped_reasons_functional(test
     result.stdout.fnmatch_lines([
         "*test_two.py S",
         "*test_one.py ss",
-        "*SKIP*3*conftest.py:3: 'test'",
+        "*SKIP*3*conftest.py:3: test",
     ])
     assert result.ret == 0
 

--- a/pytest/plugin/python.py
+++ b/pytest/plugin/python.py
@@ -7,7 +7,6 @@ import sys
 import pytest
 from py._code.code import TerminalRepr
 
-import pytest
 cutdir = py.path.local(pytest.__file__).dirpath()
 
 
@@ -22,11 +21,16 @@ def pytest_cmdline_main(config):
         showfuncargs(config)
         return 0
 
-def pytest_namespace():
-    return {'collect': {
+def pytest_namespace(__multicall__):
+    __multicall__.execute()
+    raises.Exception = pytest.fail.Exception
+    return {
+        'raises' : raises,
+        'collect': {
         'Module': Module, 'Class': Class, 'Instance': Instance,
         'Function': Function, 'Generator': Generator,
-        '_fillfuncargs': fillfuncargs}}
+        '_fillfuncargs': fillfuncargs}
+    }
 
 def pytest_funcarg__pytestconfig(request):
     """ the pytest config object with access to command line opts."""
@@ -300,17 +304,17 @@ class FunctionMixin(PyobjMixin):
         if teardown_func_or_meth is not None:
             teardown_func_or_meth(self.obj)
 
-    def _prunetraceback(self, traceback):
+    def _prunetraceback(self, excinfo):
         if hasattr(self, '_obj') and not self.config.option.fulltrace:
             code = py.code.Code(self.obj)
             path, firstlineno = code.path, code.firstlineno
+            traceback = excinfo.traceback
             ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
             if ntraceback == traceback:
                 ntraceback = ntraceback.cut(path=path)
                 if ntraceback == traceback:
                     ntraceback = ntraceback.cut(excludepath=cutdir)
-            traceback = ntraceback.filter()
-        return traceback
+            excinfo.traceback = ntraceback.filter()
 
     def _repr_failure_py(self, excinfo, style="long"):
         if excinfo.errisinstance(FuncargRequest.LookupError):
@@ -746,3 +750,71 @@ def getlocation(function, curdir):
     if fn.relto(curdir):
         fn = fn.relto(curdir)
     return "%s:%d" %(fn, lineno+1)
+
+# builtin pytest.raises helper
+
+def raises(ExpectedException, *args, **kwargs):
+    """ assert that a code block/function call raises an exception.
+
+        If using Python 2.5 or above, you may use this function as a
+        context manager::
+
+        >>> with raises(ZeroDivisionError):
+        ...    1/0
+
+        Or you can one of two forms:
+
+        if args[0] is callable: raise AssertionError if calling it with
+        the remaining arguments does not raise the expected exception.
+        if args[0] is a string: raise AssertionError if executing the
+        the string in the calling scope does not raise expected exception.
+        examples:
+        >>> x = 5
+        >>> raises(TypeError, lambda x: x + 'hello', x=x)
+        >>> raises(TypeError, "x + 'hello'")
+    """
+    __tracebackhide__ = True
+
+    if not args:
+        return RaisesContext(ExpectedException)
+    elif isinstance(args[0], str):
+        code, = args
+        assert isinstance(code, str)
+        frame = sys._getframe(1)
+        loc = frame.f_locals.copy()
+        loc.update(kwargs)
+        #print "raises frame scope: %r" % frame.f_locals
+        try:
+            code = py.code.Source(code).compile()
+            py.builtin.exec_(code, frame.f_globals, loc)
+            # XXX didn'T mean f_globals == f_locals something special?
+            #     this is destroyed here ...
+        except ExpectedException:
+            return py.code.ExceptionInfo()
+    else:
+        func = args[0]
+        try:
+            func(*args[1:], **kwargs)
+        except ExpectedException:
+            return py.code.ExceptionInfo()
+        k = ", ".join(["%s=%r" % x for x in kwargs.items()])
+        if k:
+            k = ', ' + k
+        expr = '%s(%r%s)' %(getattr(func, '__name__', func), args, k)
+    pytest.fail("DID NOT RAISE")
+
+class RaisesContext(object):
+    def __init__(self, ExpectedException):
+        self.ExpectedException = ExpectedException
+        self.excinfo = None
+
+    def __enter__(self):
+        self.excinfo = object.__new__(py.code.ExceptionInfo)
+        return self.excinfo
+
+    def __exit__(self, *tp):
+        __tracebackhide__ = True
+        if tp[0] is None:
+            pytest.fail("DID NOT RAISE")
+        self.excinfo.__init__(tp)
+        return issubclass(self.excinfo.type, self.ExpectedException)

--- a/pytest/__init__.py
+++ b/pytest/__init__.py
@@ -1,5 +1,5 @@
 """
-py.test / pytest API for unit and functional testing with Python.
+unit and functional testing with Python.
 
 see http://pytest.org for documentation and details
 

--- /dev/null
+++ b/doc/example/builtin.txt
@@ -0,0 +1,36 @@
+
+writing well integrated assertion helpers
+========================================================
+
+If you have a test helper function called from a test you can
+use the ``pytest.fail``_ builtin to cleanly fail a test with a message.
+The test support function will never itself show up in the traceback.
+Example::
+
+    # content of test_checkconfig.py
+    import pytest
+    def checkconfig(x):
+        __tracebackhide__ = True
+        if not hasattr(x, "config"):
+            pytest.fail("not configured: %s" %(x,))
+
+    def test_something():
+        checkconfig(42)
+
+The ``__tracebackhide__`` setting influences py.test showing
+of tracebacks: the ``checkconfig`` function will not be shown
+unless the ``--fulltrace`` command line option is specified.
+Let's run our little function::
+
+    $ py.test -q
+    F
+    ================================= FAILURES =================================
+    ______________________________ test_something ______________________________
+
+        def test_something():
+    >       checkconfig(42)
+    E       Failed: not configured: 42
+
+    test_checkconfig.py:8: Failed
+    1 failed in 0.02 seconds
+

--- /dev/null
+++ b/doc/builtin.txt
@@ -0,0 +1,70 @@
+
+pytest builtin helpers
+================================================
+
+
+builtin function arguments
+-----------------------------------------------------
+
+You can ask for available builtin or project-custom
+:ref:`function arguments` by typing::
+
+    $ py.test --funcargs
+    pytestconfig
+        the pytest config object with access to command line opts.
+    capsys
+        captures writes to sys.stdout/sys.stderr and makes
+        them available successively via a ``capsys.readouterr()`` method
+        which returns a ``(out, err)`` tuple of captured snapshot strings.
+
+    capfd
+        captures writes to file descriptors 1 and 2 and makes
+        snapshotted ``(out, err)`` string tuples available
+        via the ``capsys.readouterr()`` method.  If the underlying
+        platform does not have ``os.dup`` (e.g. Jython) tests using
+        this funcarg will automatically skip.
+
+    tmpdir
+        return a temporary directory path object
+        unique to each test function invocation,
+        created as a sub directory of the base temporary
+        directory.  The returned object is a `py.path.local`_
+        path object.
+
+    monkeypatch
+        The returned ``monkeypatch`` funcarg provides these
+        helper methods to modify objects, dictionaries or os.environ::
+
+        monkeypatch.setattr(obj, name, value, raising=True)
+        monkeypatch.delattr(obj, name, raising=True)
+        monkeypatch.setitem(mapping, name, value)
+        monkeypatch.delitem(obj, name, raising=True)
+        monkeypatch.setenv(name, value, prepend=False)
+        monkeypatch.delenv(name, value, raising=True)
+        monkeypatch.syspath_prepend(path)
+
+        All modifications will be undone when the requesting
+        test function finished its execution.  The ``raising``
+        parameter determines if a KeyError or AttributeError
+        will be raised if the set/deletion operation has no target.
+
+    recwarn
+        Return a WarningsRecorder instance that provides these methods:
+
+        * ``pop(category=None)``: return last warning matching the category.
+        * ``clear()``: clear list of warnings
+
+
+builtin py.test.* helpers
+-----------------------------------------------------
+
+You can always use an interactive Python prompt and type::
+
+    import pytest
+    help(pytest)
+
+to get an overview on available globally available helpers.
+
+.. automodule:: pytest
+    :members:
+

--- a/doc/apiref.txt
+++ b/doc/apiref.txt
@@ -6,9 +6,10 @@ py.test reference documentation
 
 .. toctree::
    :maxdepth: 2
- 
+
+   builtin.txt
    customize.txt
-   assert.txt 
+   assert.txt
    funcargs.txt
    xunit_setup.txt
    capture.txt
@@ -16,7 +17,7 @@ py.test reference documentation
    tmpdir.txt
    skipping.txt
    mark.txt
-   recwarn.txt 
+   recwarn.txt
    unittest.txt
    doctest.txt
-   
+

--- a/pytest/plugin/session.py
+++ b/pytest/plugin/session.py
@@ -7,6 +7,7 @@
 import py
 import pytest
 import os, sys
+tracebackcutdir = py.path.local(pytest.__file__).dirpath()
 
 # exitcodes for the command line
 EXIT_OK = 0
@@ -403,14 +404,14 @@ class Node(object):
             current = current.parent
         return current
 
-    def _prunetraceback(self, traceback):
-        return traceback
+    def _prunetraceback(self, excinfo):
+        pass
 
     def _repr_failure_py(self, excinfo, style=None):
         if self.config.option.fulltrace:
             style="long"
         else:
-            excinfo.traceback = self._prunetraceback(excinfo.traceback)
+            self._prunetraceback(excinfo)
         # XXX should excinfo.getrepr record all data and toterminal()
         # process it?
         if style is None:
@@ -448,14 +449,14 @@ class Collector(Node):
         """ internal helper method to cache results of calling collect(). """
         return self._memoizedcall('_collected', self.collect)
 
-    def _prunetraceback(self, traceback):
+    def _prunetraceback(self, excinfo):
         if hasattr(self, 'fspath'):
             path = self.fspath
+            traceback = excinfo.traceback
             ntraceback = traceback.cut(path=self.fspath)
             if ntraceback == traceback:
-                ntraceback = ntraceback.cut(excludepath=py._pydir)
-            traceback = ntraceback.filter()
-        return traceback
+                ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
+            excinfo.traceback = ntraceback.filter()
 
 class FSCollector(Collector):
     def __init__(self, fspath, parent=None, config=None, collection=None):

--- a/testing/plugin/test_terminal.py
+++ b/testing/plugin/test_terminal.py
@@ -195,7 +195,7 @@ class TestCollectonly:
         assert len(cols) == 0
         linecomp.assert_contains_lines("""
             <Module 'test_collectonly_skipped_module.py'>
-              !!! Skipped: 'nomod' !!!
+              !!! Skipped: nomod !!!
         """)
 
     def test_collectonly_failed_module(self, testdir, linecomp):

--- a/pytest/plugin/runner.py
+++ b/pytest/plugin/runner.py
@@ -7,11 +7,9 @@ from py._code.code import TerminalRepr
 
 def pytest_namespace():
     return {
-        'raises'       : raises,
+        'fail'         : fail,
         'skip'         : skip,
         'importorskip' : importorskip,
-        'fail'         : fail,
-        'xfail'        : xfail,
         'exit'         : exit,
     }
 
@@ -337,13 +335,12 @@ class OutcomeException(Exception):
     """ OutcomeException and its subclass instances indicate and
         contain info about test and collection outcomes.
     """
-    def __init__(self, msg=None, excinfo=None):
+    def __init__(self, msg=None):
         self.msg = msg
-        self.excinfo = excinfo
 
     def __repr__(self):
         if self.msg:
-            return repr(self.msg)
+            return str(self.msg)
         return "<%s instance>" %(self.__class__.__name__,)
     __str__ = __repr__
 
@@ -356,19 +353,8 @@ class Failed(OutcomeException):
     """ 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):
-        Failed.__init__(self, msg=msg, excinfo=excinfo)
-        self.expr = expr
-        self.expected = expected
-
 class Exit(KeyboardInterrupt):
-    """ raised by py.test.exit for immediate program exits without tracebacks and reporter/summary. """
+    """ raised for immediate program exits (no tracebacks/summaries)"""
     def __init__(self, msg="unknown reason"):
         self.msg = msg
         KeyboardInterrupt.__init__(self, msg)
@@ -384,103 +370,20 @@ exit.Exception = Exit
 
 def skip(msg=""):
     """ skip an executing test with the given message.  Note: it's usually
-    better use the py.test.mark.skipif marker to declare a test to be
+    better to use the py.test.mark.skipif marker to declare a test to be
     skipped under certain conditions like mismatching platforms or
     dependencies.  See the pytest_skipping plugin for details.
     """
     __tracebackhide__ = True
     raise Skipped(msg=msg)
-
 skip.Exception = Skipped
 
 def fail(msg=""):
     """ explicitely fail an currently-executing test with the given Message. """
     __tracebackhide__ = True
     raise Failed(msg=msg)
-
 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):
-    """ assert that a code block/function call raises an exception.
-
-        If using Python 2.5 or above, you may use this function as a
-        context manager::
-
-        >>> with raises(ZeroDivisionError):
-        ...    1/0
-
-        Or you can one of two forms:
-
-        if args[0] is callable: raise AssertionError if calling it with
-        the remaining arguments does not raise the expected exception.
-        if args[0] is a string: raise AssertionError if executing the
-        the string in the calling scope does not raise expected exception.
-        examples:
-        >>> x = 5
-        >>> raises(TypeError, lambda x: x + 'hello', x=x)
-        >>> raises(TypeError, "x + 'hello'")
-    """
-    __tracebackhide__ = True
-
-    if not args:
-        return RaisesContext(ExpectedException)
-    elif isinstance(args[0], str):
-        code, = args
-        assert isinstance(code, str)
-        frame = sys._getframe(1)
-        loc = frame.f_locals.copy()
-        loc.update(kwargs)
-        #print "raises frame scope: %r" % frame.f_locals
-        try:
-            code = py.code.Source(code).compile()
-            py.builtin.exec_(code, frame.f_globals, loc)
-            # XXX didn'T mean f_globals == f_locals something special?
-            #     this is destroyed here ...
-        except ExpectedException:
-            return py.code.ExceptionInfo()
-    else:
-        func = args[0]
-        try:
-            func(*args[1:], **kwargs)
-        except ExpectedException:
-            return py.code.ExceptionInfo()
-        k = ", ".join(["%s=%r" % x for x in kwargs.items()])
-        if k:
-            k = ', ' + k
-        expr = '%s(%r%s)' %(getattr(func, '__name__', func), args, k)
-    raise ExceptionFailure(msg="DID NOT RAISE",
-                           expr=args, expected=ExpectedException)
-
-
-class RaisesContext(object):
-
-    def __init__(self, ExpectedException):
-        self.ExpectedException = ExpectedException
-        self.excinfo = None
-
-    def __enter__(self):
-        self.excinfo = object.__new__(py.code.ExceptionInfo)
-        return self.excinfo
-
-    def __exit__(self, *tp):
-        __tracebackhide__ = True
-        if tp[0] is None:
-            raise ExceptionFailure(msg="DID NOT RAISE",
-                                   expr=(),
-                                   expected=self.ExpectedException)
-        self.excinfo.__init__(tp)
-        return issubclass(self.excinfo.type, self.ExpectedException)
-
-
-raises.Exception = ExceptionFailure
 
 def importorskip(modname, minversion=None):
     """ return imported module if it has a higher __version__ than the
@@ -503,5 +406,3 @@ def importorskip(modname, minversion=Non
         py.test.skip("module %r has __version__ %r, required is: %r" %(
                      modname, verattr, minversion))
     return mod
-
-

--- a/doc/example/index.txt
+++ b/doc/example/index.txt
@@ -7,6 +7,7 @@ Usages and Examples
 .. toctree::
    :maxdepth: 2
 
+   builtin.txt
    pythoncollection.txt
    controlskip.txt
    mysetup.txt

--- a/testing/plugin/test_runner.py
+++ b/testing/plugin/test_runner.py
@@ -319,61 +319,6 @@ def test_runtest_in_module_ordering(test
         "*2 passed*"
     ])
 
-class TestRaises:
-    def test_raises(self):
-        source = "int('qwe')"
-        excinfo = py.test.raises(ValueError, source)
-        code = excinfo.traceback[-1].frame.code
-        s = str(code.fullsource)
-        assert s == source
-
-    def test_raises_exec(self):
-        py.test.raises(ValueError, "a,x = []")
-
-    def test_raises_syntax_error(self):
-        py.test.raises(SyntaxError, "qwe qwe qwe")
-
-    def test_raises_function(self):
-        py.test.raises(ValueError, int, 'hello')
-
-    def test_raises_callable_no_exception(self):
-        class A:
-            def __call__(self):
-                pass
-        try:
-            py.test.raises(ValueError, A())
-        except py.test.raises.Exception:
-            pass
-
-    @py.test.mark.skipif('sys.version < "2.5"')
-    def test_raises_as_contextmanager(self, testdir):
-        testdir.makepyfile("""
-            from __future__ import with_statement
-            import py
-
-            def test_simple():
-                with py.test.raises(ZeroDivisionError) as excinfo:
-                    assert isinstance(excinfo, py.code.ExceptionInfo)
-                    1/0
-                print (excinfo)
-                assert excinfo.type == ZeroDivisionError
-
-            def test_noraise():
-                with py.test.raises(py.test.raises.Exception):
-                    with py.test.raises(ValueError):
-                           int()
-
-            def test_raise_wrong_exception_passes_by():
-                with py.test.raises(ZeroDivisionError):
-                    with py.test.raises(ValueError):
-                           1/0
-        """)
-        result = testdir.runpytest()
-        result.stdout.fnmatch_lines([
-            '*3 passed*',
-        ])
-
-
 
 def test_pytest_exit():
     try:

--- a/pytest/plugin/skipping.py
+++ b/pytest/plugin/skipping.py
@@ -1,154 +1,8 @@
 """
-advanced skipping for python test functions, classes or modules.
-
-With this plugin you can mark test functions for conditional skipping
-or as "xfail", expected-to-fail.  Skipping a test will avoid running it
-while xfail-marked tests will run and result in an inverted outcome:
-a pass becomes a failure and a fail becomes a semi-passing one.
-
-The need for skipping a test is usually connected to a condition.
-If a test fails under all conditions then it's probably better
-to mark your test as 'xfail'.
-
-By passing ``-rxs`` to the terminal reporter you will see extra
-summary information on skips and xfail-run tests at the end of a test run.
-
-.. _skipif:
-
-Skipping a single function
--------------------------------------------
-
-Here is an example for marking a test function to be skipped
-when run on a Python3 interpreter::
-
-    @py.test.mark.skipif("sys.version_info >= (3,0)")
-    def test_function():
-        ...
-
-During test function setup the skipif condition is
-evaluated by calling ``eval(expr, namespace)``.  The namespace
-contains the  ``sys`` and ``os`` modules and the test
-``config`` object.  The latter allows you to skip based
-on a test configuration value e.g. like this::
-
-    @py.test.mark.skipif("not config.getvalue('db')")
-    def test_function(...):
-        ...
-
-Create a shortcut for your conditional skip decorator
-at module level like this::
-
-    win32only = py.test.mark.skipif("sys.platform != 'win32'")
-
-    @win32only
-    def test_function():
-        ...
-
-
-skip groups of test functions
---------------------------------------
-
-As with all metadata function marking you can do it at
-`whole class- or module level`_.  Here is an example
-for skipping all methods of a test class based on platform::
-
-    class TestPosixCalls:
-        pytestmark = py.test.mark.skipif("sys.platform == 'win32'")
-
-        def test_function(self):
-            # will not be setup or run under 'win32' platform
-            #
-
-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
-
-.. _xfail:
-
-mark a test function as **expected to fail**
--------------------------------------------------------
-
-You can use the ``xfail`` marker to indicate that you
-expect the test to fail::
-
-    @py.test.mark.xfail
-    def test_function():
-        ...
-
-This test will be run but no traceback will be reported
-when it fails. Instead terminal reporting will list it in the
-"expected to fail" or "unexpectedly passing" sections.
-
-Same as with skipif_ you can also selectively expect a failure
-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")
-
-imperative xfail from within a test or setup function
-------------------------------------------------------
-
-If you cannot declare xfail-conditions at import time
-you can also imperatively produce an XFail-outcome from
-within test or setup code.  Example::
-
-    def test_function():
-        if not valid_config():
-            py.test.xfail("unsuppored configuration")
-
-
-skipping on a missing import dependency
---------------------------------------------------
-
-You can use the following import helper at module level
-or within a test or test setup function::
-
-    docutils = py.test.importorskip("docutils")
-
-If ``docutils`` cannot be imported here, this will lead to a
-skip outcome of the test.  You can also skip dependeing if
-if a library does not come with a high enough version::
-
-    docutils = py.test.importorskip("docutils", minversion="0.3")
-
-The version will be read from the specified module's ``__version__`` attribute.
-
-imperative skip from within a test or setup function
-------------------------------------------------------
-
-If for some reason you cannot declare skip-conditions
-you can also imperatively produce a Skip-outcome from
-within test or setup code.  Example::
-
-    def test_function():
-        if not valid_config():
-            py.test.skip("unsuppored configuration")
-
+plugin providing skip and xfail functionality.
 """
 
-import py
+import py, pytest
 
 def pytest_addoption(parser):
     group = parser.getgroup("general")
@@ -156,6 +10,18 @@ def pytest_addoption(parser):
            action="store_true", dest="runxfail", default=False,
            help="run tests even if they are marked xfail")
 
+def pytest_namespace():
+    return dict(xfail=xfail)
+
+class XFailed(pytest.fail.Exception):
+    """ raised from an explicit call to py.test.xfail() """
+
+def xfail(reason=""):
+    """ xfail an executing test or setup functions with the given reason."""
+    __tracebackhide__ = True
+    raise XFailed(reason)
+xfail.Exception = XFailed
+
 class MarkEvaluator:
     def __init__(self, item, name):
         self.item = item

--- a/testing/plugin/test_resultlog.py
+++ b/testing/plugin/test_resultlog.py
@@ -89,7 +89,7 @@ class TestWithFunctionIntegration:
         assert lines[0].startswith("S ")
         assert lines[0].endswith("test_collection_skip.py")
         assert lines[1].startswith(" ")
-        assert lines[1].endswith("test_collection_skip.py:1: Skipped: 'hello'")
+        assert lines[1].endswith("test_collection_skip.py:1: Skipped: hello")
 
         lines = self.getresultlog(testdir, fail)
         assert lines

--- a/pytest/_core.py
+++ b/pytest/_core.py
@@ -7,7 +7,7 @@ assert py.__version__.split(".")[:2] >= 
     "%s is too old, remove or upgrade 'py'" % (py.__version__))
 
 default_plugins = (
- "config session terminal python runner pdb capture unittest mark skipping "
+ "config session terminal runner python pdb capture unittest mark skipping "
  "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
  "junitxml doctest").split()



More information about the pytest-commit mailing list