diff --git a/CHANGELOG b/CHANGELOG index 69ddc9bca..6c2fe3615 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,10 @@ -$Id: CHANGELOG 58297 2008-09-21 12:50:56Z hpk $ +$Id: CHANGELOG 58308 2008-09-21 15:15:28Z hpk $ Changes between 0.9.2 and 1.0 (UNRELEASED) ============================================= -* py.test.skip(ifraises=execstring) allows to - conditionally skip, e.g. ifraises="import docutils" - will skip if there is no docutils installed. +* new method: py.test.importorskip(mod,minversion) + will either import or call py.test.skip() * revised internal py.test architecture diff --git a/py/__init__.py b/py/__init__.py index 7bbf46eab..1c7bbdddd 100644 --- a/py/__init__.py +++ b/py/__init__.py @@ -26,8 +26,8 @@ version = "1.0.0a1" initpkg(__name__, description = "pylib and py.test: agile development and test support library", - revision = int('$LastChangedRevision: 58190 $'.split(':')[1][:-1]), - lastchangedate = '$LastChangedDate: 2008-09-17 10:50:04 +0200 (Wed, 17 Sep 2008) $', + revision = int('$LastChangedRevision: 58308 $'.split(':')[1][:-1]), + lastchangedate = '$LastChangedDate: 2008-09-21 17:15:28 +0200 (Sun, 21 Sep 2008) $', version = version, url = "http://pylib.org", download_url = "http://codespeak.net/py/0.9.2/download.html", @@ -67,6 +67,7 @@ initpkg(__name__, 'test.raises' : ('./test/outcome.py', 'raises'), 'test.deprecated_call' : ('./test/outcome.py', 'deprecated_call'), 'test.skip' : ('./test/outcome.py', 'skip'), + 'test.importorskip' : ('./test/outcome.py', 'importorskip'), 'test.fail' : ('./test/outcome.py', 'fail'), 'test.exit' : ('./test/outcome.py', 'exit'), 'test.pdb' : ('./test/custompdb.py', 'set_trace'), diff --git a/py/doc/conftest.py b/py/doc/conftest.py index ce281d254..7bb922f4d 100644 --- a/py/doc/conftest.py +++ b/py/doc/conftest.py @@ -61,10 +61,7 @@ def deindent(s, sep='\n'): _initialized = False def checkdocutils(): global _initialized - try: - import docutils - except ImportError: - py.test.skip("docutils not importable") + py.test.importorskip("docutils") if not _initialized: from py.__.rest import directive directive.register_linkrole('api', resolve_linkrole) diff --git a/py/doc/test.txt b/py/doc/test.txt index 545ab0e43..3940d317e 100644 --- a/py/doc/test.txt +++ b/py/doc/test.txt @@ -90,20 +90,17 @@ Skipping tests ---------------------------------------- If you want to skip tests you can use ``py.test.skip`` within -test or setup functions. Examples:: +test or setup functions. Example:: py.test.skip("message") - py.test.skip(ifraises="import docutils") - py.test.skip(ifraises=""" - import somepkg - assert somepkg.__version__.startswith("2") - """) -The first skip will cause unconditional skipping -The second skip will only cause skipping if -``docutils`` is not importable. -The third form will cause skipping if ``somepkg`` -is not importable or is not at least version "2". +You can also use a helper to skip on a failing import:: + + docutils = py.test.importorskip("docutils") + +or to skip if the library does not have the right version:: + + docutils = py.test.importorskip("docutils", minversion="0.3") automatic collection of tests on all levels ------------------------------------------- diff --git a/py/path/local/local.py b/py/path/local/local.py index eff910a5a..512d485d2 100644 --- a/py/path/local/local.py +++ b/py/path/local/local.py @@ -181,7 +181,7 @@ class LocalPath(common.FSPathBase, PlatformMixin): break for arg in strargs: arg = arg.strip(sep) - if py.std.sys.platform == 'win32': + if iswin32: # allow unix style paths even on windows. arg = arg.strip('/') arg = arg.replace('/', sep) @@ -512,7 +512,7 @@ class LocalPath(common.FSPathBase, PlatformMixin): if p.check(file=1): return p else: - if py.std.sys.platform == 'win32': + if iswin32: paths = py.std.os.environ['Path'].split(';') try: systemroot = os.environ['SYSTEMROOT'] diff --git a/py/path/svn/testing/svntestbase.py b/py/path/svn/testing/svntestbase.py index 26974f097..410b32c0a 100644 --- a/py/path/svn/testing/svntestbase.py +++ b/py/path/svn/testing/svntestbase.py @@ -7,6 +7,13 @@ from py.__.path.svn import cache, svncommon mypath = py.magic.autopath() repodump = mypath.dirpath('repotest.dump') +def getsvnbin(): + svnbin = py.path.local.sysfind('svn') + if svnbin is None: + py.test.skip("svn binary not found") + return svnbin + + # make a wc directory out of a given root url # cache previously obtained wcs! # diff --git a/py/path/svn/testing/test_auth.py b/py/path/svn/testing/test_auth.py index bf2a66cc6..33f8cc993 100644 --- a/py/path/svn/testing/test_auth.py +++ b/py/path/svn/testing/test_auth.py @@ -95,6 +95,7 @@ class svnwc_no_svn(py.path.svnwc): class TestSvnWCAuth(object): def setup_method(self, meth): self.auth = SvnAuth('user', 'pass', cache_auth=False) + svntestbase.getsvnbin() def test_checkout(self): wc = svnwc_no_svn('foo', auth=self.auth) @@ -250,6 +251,7 @@ class TestSvnURLAuth(object): class SvnAuthFunctionalTestBase(object): def setup_class(cls): + svntestbase.getsvnbin() if not option.runslowtests: py.test.skip('skipping slow functional tests - use --runslowtests ' 'to override') diff --git a/py/path/svn/testing/test_test_repo.py b/py/path/svn/testing/test_test_repo.py index bc43bb4a3..0425a3632 100644 --- a/py/path/svn/testing/test_test_repo.py +++ b/py/path/svn/testing/test_test_repo.py @@ -1,12 +1,10 @@ import py -from py.__.path.svn.testing.svntestbase import make_test_repo +from py.__.path.svn.testing.svntestbase import make_test_repo, getsvnbin -if py.path.local.sysfind('svn') is None: - py.test.skip("cannot test py.path.svn, 'svn' binary not found") - class TestMakeRepo(object): def setup_class(cls): + getsvnbin() cls.repo = make_test_repo() cls.wc = py.path.svnwc(py.test.ensuretemp("test-wc").join("wc")) diff --git a/py/path/svn/testing/test_urlcommand.py b/py/path/svn/testing/test_urlcommand.py index 1e82a2d7c..e3138859c 100644 --- a/py/path/svn/testing/test_urlcommand.py +++ b/py/path/svn/testing/test_urlcommand.py @@ -1,13 +1,12 @@ import py from py.__.path.svn.urlcommand import InfoSvnCommand from py.__.path.svn.testing.svntestbase import CommonCommandAndBindingTests, \ - getrepowc + getrepowc, getsvnbin import datetime import time def setup_module(mod): - if py.path.local.sysfind('svn') is None: - py.test.skip("cannot test py.path.svn, 'svn' binary not found") + getsvnbin() class TestSvnURLCommandPath(CommonCommandAndBindingTests): def setup_class(cls): diff --git a/py/path/svn/testing/test_wccommand.py b/py/path/svn/testing/test_wccommand.py index 1b7887412..68c73db1e 100644 --- a/py/path/svn/testing/test_wccommand.py +++ b/py/path/svn/testing/test_wccommand.py @@ -1,6 +1,6 @@ import py import sys -from py.__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc +from py.__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc, getsvnbin from py.__.path.svn.wccommand import InfoSvnWCCommand, XMLWCStatus from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon @@ -22,8 +22,7 @@ else: return os.path.normpath(os.path.normcase(p)) def setup_module(mod): - if py.path.local.sysfind('svn') is None: - py.test.skip("cannot test py.path.svn, 'svn' binary not found") + getsvnbin() class TestWCSvnCommandPath(CommonSvnTests): def setup_class(cls): diff --git a/py/rest/testing/setup.py b/py/rest/testing/setup.py index f2c2a6d9c..dc1cbaa98 100644 --- a/py/rest/testing/setup.py +++ b/py/rest/testing/setup.py @@ -8,3 +8,4 @@ def getdata(): tmpdir = py.test.ensuretemp(rel.replace(pydir.sep, '_')) mydatadir.copy(tmpdir) return tmpdir + diff --git a/py/rest/testing/test_directive.py b/py/rest/testing/test_directive.py index 85f41f9b2..570abc8ca 100644 --- a/py/rest/testing/test_directive.py +++ b/py/rest/testing/test_directive.py @@ -1,13 +1,10 @@ import py -try: - import docutils -except ImportError: - py.test.skip("docutils not present") +from py.__.rest.testing.setup import getdata +docutils = py.test.importorskip("docutils") from py.__.rest import directive from py.__.misc import rest from py.__.rest.latex import process_rest_file -from py.__.rest.testing.setup import getdata def setup_module(mod): mod.datadir = getdata() diff --git a/py/rest/testing/test_htmlrest.py b/py/rest/testing/test_htmlrest.py index 52bba9f78..c3e3ada46 100644 --- a/py/rest/testing/test_htmlrest.py +++ b/py/rest/testing/test_htmlrest.py @@ -4,16 +4,13 @@ import py from py.__.misc import rest from py.__.rest.testing.setup import getdata + def setup_module(mod): + py.test.importorskip("docutils") if not py.path.local.sysfind("gs") or \ not py.path.local.sysfind("dot") or \ not py.path.local.sysfind("latex"): py.test.skip("ghostscript, graphviz and latex needed") - try: - import docutils - except ImportError: - py.test.skip("docutils not present") - mod.datadir = getdata() def test_process_simple(): diff --git a/py/rest/testing/test_rst2pdf.py b/py/rest/testing/test_rst2pdf.py index a13f4b1e9..89a58bd8d 100644 --- a/py/rest/testing/test_rst2pdf.py +++ b/py/rest/testing/test_rst2pdf.py @@ -4,11 +4,7 @@ import py from py.__.rest.latex import process_configfile, process_rest_file from py.__.rest.testing.setup import getdata -try: - import docutils -except ImportError: - py.test.skip("docutils not present") - +docutils = py.test.importorskip("docutils") def setup_module(mod): if not py.path.local.sysfind("gs") or \ diff --git a/py/test/outcome.py b/py/test/outcome.py index 2f9b1d262..468f91316 100644 --- a/py/test/outcome.py +++ b/py/test/outcome.py @@ -49,31 +49,30 @@ def exit(msg): __tracebackhide__ = True raise Exit(msg) -def skip(msg="", ifraises=None, ns=None): - """ (conditionally) skip this test/module/conftest. - - msg: use this message when skipping. - ifraises: - if "exec ifraises in {'py': py}" raises an exception - skip this test. - ns: use this namespace when executing ifraises - """ +def skip(msg=""): + """ skip with the given message. """ __tracebackhide__ = True - if ifraises is not None: - ifraises = py.code.Source(ifraises).compile() - if ns is None: - ns = {} - try: - exec ifraises in ns - except (KeyboardInterrupt, SystemExit): - raise - except Exception, e: - if not msg: - msg = repr(e) - else: - return raise Skipped(msg=msg) +def importorskip(modname, minversion=None): + """ return imported module or skip() """ + compile(modname, '', 'eval') # to catch syntaxerrors + try: + mod = __import__(modname) + except ImportError: + py.test.skip("could not import %r" %(modname,)) + if minversion is None: + return mod + verattr = getattr(mod, '__version__', None) + if isinstance(minversion, str): + minver = minversion.split(".") + else: + minver = list(minversion) + if verattr is None or verattr.split(".") < minver: + py.test.skip("module %r has __version__ %r, required is: %r" %( + modname, verattr, minversion)) + return mod + def fail(msg="unknown failure"): """ fail with the given Message. """ __tracebackhide__ = True diff --git a/py/test/testing/acceptance_test.py b/py/test/testing/acceptance_test.py index c6d9aa5c6..e3fdec714 100644 --- a/py/test/testing/acceptance_test.py +++ b/py/test/testing/acceptance_test.py @@ -387,7 +387,7 @@ class TestPyTest(AcceptBase): class TestInteractive(AcceptBase): def getspawn(self): - py.test.skip(ifraises="import pexpect", ns=globals()) + pexpect = py.test.importorskip("pexpect") def spawn(cmd): return pexpect.spawn(cmd, logfile=self.tmpdir.join("spawn.out").open("w")) return spawn diff --git a/py/test/testing/test_outcome.py b/py/test/testing/test_outcome.py index f5f15c50a..ea6ad6854 100644 --- a/py/test/testing/test_outcome.py +++ b/py/test/testing/test_outcome.py @@ -59,33 +59,25 @@ def test_deprecated_explicit_call(): py.test.deprecated_call(dep_explicit, 0) py.test.deprecated_call(dep_explicit, 0) - - def test_skip_simple(): excinfo = py.test.raises(Skipped, 'py.test.skip("xxx")') assert excinfo.traceback[-1].frame.code.name == "skip" assert excinfo.traceback[-1].ishidden() -def test_skip_ifraises(): - excinfo = py.test.raises(Skipped, ''' - py.test.skip(ifraises=""" - import lky - """) - ''') - assert excinfo.traceback[-1].frame.code.name == "skip" - assert excinfo.traceback[-1].ishidden() - assert excinfo.value.msg.startswith("ImportError") - -def test_skip_ifraises_ns(): - d = {} - py.test.skip(ns=d, ifraises="import py") - assert d['py'] == py - -def test_skip_ifraises_syntaxerror(): +def test_importorskip(): try: - excinfo = py.test.raises(SyntaxError, ''' - py.test.skip(ifraises="x y z")''') + sys = py.test.importorskip("sys") + assert sys == py.std.sys + #path = py.test.importorskip("os.path") + #assert path == py.std.os.path + py.test.raises(Skipped, "py.test.importorskip('alskdj')") + py.test.raises(SyntaxError, "py.test.importorskip('x y z')") + py.test.raises(SyntaxError, "py.test.importorskip('x=y')") + path = py.test.importorskip("py", minversion=".".join(py.__version__)) + py.test.raises(Skipped, """ + py.test.importorskip("py", minversion="5.0") + """) except Skipped: - py.test.fail("should not skip") - assert not excinfo.traceback[-1].ishidden() + print py.code.ExceptionInfo() + py.test.fail("spurious skip")