* don't add distributed command line options when 'execnet' is not

installed, report a nice message.

* fix tests and code to work with non-existing execnet

* point execnet doc to the new package

--HG--
branch : trunk
This commit is contained in:
holger krekel 2009-10-02 22:29:22 +02:00
parent ab9f6a75ad
commit 1f29529a24
16 changed files with 91 additions and 43 deletions

View File

@ -7,8 +7,6 @@ documentation on the most interesting ones:
`py.test`_ write and deploy unit- and functional tests to multiple machines.
`py.execnet`_ elastic distributed programming.
`py.code`_: generate code and use advanced introspection/traceback support.
`py.path`_: use path objects to transparently access local and svn filesystems.
@ -33,7 +31,6 @@ For the latest Release, see `PyPI project page`_
.. _`download and installation`: download.html
.. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev
.. _`py.execnet`: execnet.html
.. _`py.log`: log.html
.. _`py.io`: io.html
.. _`py.path`: path.html

View File

@ -246,7 +246,7 @@ on the `svn` command line, not on the bindings.
It makes sense now to directly use the bindings.
Moreover, it would be good, also considering
`py.execnet`_ distribution of programs, to
`execnet`_ distribution of programs, to
be able to manipulate Windows Paths on Linux
and vice versa. So we'd like to consider
refactoring the path implementations
@ -269,5 +269,5 @@ the quite full interface without requiring
to know about all details of the full path
implementation.
.. _`py.execnet`: execnet.html
.. _`execnet`: execnet.html

View File

@ -11,10 +11,8 @@ synchronizes your program source code to the remote place. All test results
are reported back and displayed to your local test session. You may
specify different Python versions and interpreters.
Synchronisation and running of tests only requires
a bare Python installation on the remote side. No
special software is installed - this is realized
by use of the **zero installation** `py.execnet`_ mechanisms.
**Requirements**: you need to install the `execnet`_ package
to perform distributed test runs.
Speed up test runs by sending tests to multiple CPUs
----------------------------------------------------------
@ -90,13 +88,13 @@ The basic command to run tests on multiple platforms is::
If you specify a windows host, an OSX host and a Linux
environment this command will send each tests to all
platforms - and report back failures from all platforms
at once. The provided specifications strings
use the `xspec syntax`_.
at once. The specifications strings use the `xspec syntax`_.
.. _`xspec syntax`: ../execnet.html#xspec
.. _`xspec syntax`: http://codespeak.net/execnet/trunk/basics.html#xspec
.. _`socketserver.py`: http://codespeak.net/svn/py/dist/py/execnet/script/socketserver.py
.. _`py.execnet`: ../execnet.html
.. _`execnet`: http://codespeak.net/execnet
Specifying test exec environments in a conftest.py
-------------------------------------------------------------

View File

@ -15,7 +15,6 @@ The py lib strives to offer enough functionality to represent
itself and especially its API in html or xml.
.. _xist: http://www.livinglogic.de/Python/xist/index.html
.. _`exchange data`: execnet.html#exchange-data
a pythonic object model , please
================================

View File

@ -13,7 +13,6 @@
"""
import py
from execnet.gateway_base import Channel
import sys, os, struct
#debug = open("log-mypickle-%d" % os.getpid(), 'w')
@ -139,6 +138,7 @@ class PickleChannel(object):
self.RemoteError = channel.RemoteError
def send(self, obj):
from execnet.gateway_base import Channel
if not isinstance(obj, Channel):
pickled_obj = self._ipickle.dumps(obj)
self._channel.send(pickled_obj)

View File

@ -24,12 +24,16 @@ class Parser:
self._groups = [self._anonymous]
self._processopt = processopt
self._usage = usage
self.epilog = ""
def processoption(self, option):
if self._processopt:
if option.dest:
self._processopt(option)
def addnote(self, note):
self._notes.append(note)
def addgroup(self, name, description=""):
for group in self._groups:
if group.name == name:
@ -51,6 +55,7 @@ class Parser:
def parse(self, args):
optparser = optparse.OptionParser(usage=self._usage)
# make sure anaonymous group is at the end
optparser.epilog = self.epilog
groups = self._groups[1:] + [self._groups[0]]
for group in groups:
if group.options:

View File

@ -3,6 +3,11 @@
import sys
import py
try:
import execnet
except ImportError:
execnet = None
def pytest_pyfunc_call(__multicall__, pyfuncitem):
if not __multicall__.execute():
testfunction = pyfuncitem.obj
@ -63,14 +68,22 @@ def pytest_addoption(parser):
help="traceback verboseness (long/short/no).")
group._addoption('-p', action="append", dest="plugins", default = [],
help=("load the specified plugin after command line parsing. "))
group._addoption('-f', '--looponfail',
action="store_true", dest="looponfail", default=False,
help="run tests, re-run failing test set until all pass.")
if execnet:
group._addoption('-f', '--looponfail',
action="store_true", dest="looponfail", default=False,
help="run tests, re-run failing test set until all pass.")
group = parser.addgroup("debugconfig", "test process debugging and configuration")
group.addoption('--basetemp', dest="basetemp", default=None, metavar="dir",
help="base temporary directory for this test run.")
if execnet:
add_dist_options(parser)
else:
parser.epilog = (
"execnet missing: --looponfailing and distributed testing not available.")
def add_dist_options(parser):
group = parser.addgroup("dist", "distributed testing") # see http://pytest.org/help/dist")
group._addoption('--dist', metavar="distmode",
action="store", choices=['load', 'each', 'no'],
@ -97,18 +110,19 @@ def pytest_configure(config):
setsession(config)
def fixoptions(config):
if config.option.numprocesses:
config.option.dist = "load"
config.option.tx = ['popen'] * int(config.option.numprocesses)
if config.option.distload:
config.option.dist = "load"
if execnet:
if config.option.numprocesses:
config.option.dist = "load"
config.option.tx = ['popen'] * int(config.option.numprocesses)
if config.option.distload:
config.option.dist = "load"
def setsession(config):
val = config.getvalue
if val("collectonly"):
from py.__.test.session import Session
config.setsessionclass(Session)
else:
elif execnet:
if val("looponfail"):
from py.__.test.looponfail.remote import LooponfailingSession
config.setsessionclass(LooponfailingSession)

View File

@ -4,6 +4,10 @@ interactive debugging with the Python Debugger.
import py
import pdb, sys, linecache
from py.__.test.outcome import Skipped
try:
import execnet
except ImportError:
execnet = None
def pytest_addoption(parser):
group = parser.getgroup("general")
@ -14,10 +18,11 @@ def pytest_addoption(parser):
def pytest_configure(config):
if config.option.usepdb:
if config.getvalue("looponfail"):
raise config.Error("--pdb incompatible with --looponfail.")
if config.option.dist != "no":
raise config.Error("--pdb incompatible with distributing tests.")
if execnet:
if config.getvalue("looponfail"):
raise config.Error("--pdb incompatible with --looponfail.")
if config.option.dist != "no":
raise config.Error("--pdb incompatible with distributing tests.")
config.pluginmanager.register(PdbInvoke())
class PdbInvoke:

View File

@ -165,7 +165,7 @@ class TerminalReporter:
self.stats.setdefault('deselected', []).append(items)
def pytest_itemstart(self, item, node=None):
if self.config.option.dist != "no":
if getattr(self.config.option, 'dist', 'no') != "no":
# for dist-testing situations itemstart means we
# queued the item for sending, not interesting (unless debugging)
if self.config.option.debug:

4
testing/pytest/dist/conftest.py vendored Normal file
View File

@ -0,0 +1,4 @@
try:
import execnet
except ImportError:
collect_ignore = ['.']

View File

@ -1,4 +1,5 @@
import py
py.test.importorskip("execnet")
from py.__.test.looponfail.remote import LooponfailingSession, LoopState, RemoteControl
class TestRemoteControl:

View File

@ -10,6 +10,7 @@ def test_implied_different_sessions(tmpdir):
return Exception
return getattr(config._sessionclass, '__name__', None)
assert x() == None
py.test.importorskip("execnet")
assert x('-d') == 'DSession'
assert x('--dist=each') == 'DSession'
assert x('-n3') == 'DSession'
@ -31,6 +32,8 @@ def test_plugin_already_exists(testdir):
class TestDistOptions:
def setup_method(self, method):
py.test.importorskip("execnet")
def test_getxspecs(self, testdir):
config = testdir.parseconfigure("--tx=popen", "--tx", "ssh=xyz")
xspecs = config.getxspecs()
@ -64,13 +67,13 @@ class TestDistOptions:
assert py.path.local('z') in roots
assert testdir.tmpdir.join('x') in roots
def test_dist_options(testdir):
config = testdir.parseconfigure("-n 2")
assert config.option.dist == "load"
assert config.option.tx == ['popen'] * 2
config = testdir.parseconfigure("-d")
assert config.option.dist == "load"
def test_dist_options(self, testdir):
config = testdir.parseconfigure("-n 2")
assert config.option.dist == "load"
assert config.option.tx == ['popen'] * 2
config = testdir.parseconfigure("-d")
assert config.option.dist == "load"
def test_pytest_report_iteminfo():
class FakeItem(object):

View File

@ -44,7 +44,8 @@ class TestPDB:
if child.isalive():
child.wait()
def test_incompatibility_messages(self, testdir):
def test_dist_incompatibility_messages(self, testdir):
py.test.importorskip("execnet")
Error = py.test.config.Error
py.test.raises(Error, "testdir.parseconfigure('--pdb', '--looponfail')")
result = testdir.runpytest("--pdb", "-n", "3")

View File

@ -3,6 +3,10 @@ terminal reporting of the full testing process.
"""
import py
import sys
try:
import execnet
except ImportError:
execnet = None
# ===============================================================================
# plugin tests
@ -42,7 +46,7 @@ def pytest_generate_tests(metafunc):
funcargs={'option': Option(verbose=True)}
)
nodist = getattr(metafunc.function, 'nodist', False)
if not nodist:
if execnet and not nodist:
metafunc.addcall(
id="verbose-dist",
funcargs={'option': Option(dist='each', verbose=True)}
@ -602,9 +606,10 @@ class TestTerminalFunctional:
"*test_verbose_reporting.py:10: test_gen*FAIL*",
])
assert result.ret == 1
result = testdir.runpytest(p1, '-v', '-n 1')
result.stdout.fnmatch_lines([
"*FAIL*test_verbose_reporting.py:2: test_fail*",
])
assert result.ret == 1
if execnet:
result = testdir.runpytest(p1, '-v', '-n 1')
result.stdout.fnmatch_lines([
"*FAIL*test_verbose_reporting.py:2: test_fail*",
])
assert result.ret == 1

View File

@ -218,6 +218,7 @@ class TestOptionEffects:
config = py.test.config._reparse([tmpdir])
config.initsession()
assert not config.option.boxed
py.test.importorskip("execnet")
config = py.test.config._reparse(['-d', tmpdir])
config.initsession()
assert not config.option.boxed

View File

@ -8,6 +8,12 @@ class TestParser:
out, err = capsys.readouterr()
assert out.find("xyz") != -1
def test_epilog(self):
parser = parseopt.Parser()
assert not parser.epilog
parser.epilog += "hello"
assert parser.epilog == "hello"
def test_group_add_and_get(self):
parser = parseopt.Parser()
group = parser.addgroup("hello", description="desc")
@ -70,6 +76,15 @@ class TestParser:
args = parser.parse_setoption([], option)
assert option.hello == "x"
def test_parser_epilog(self, testdir):
testdir.makeconftest("""
def pytest_addoption(parser):
parser.epilog = "hello world"
""")
result = testdir.runpytest('--help')
#assert result.ret != 0
assert result.stdout.fnmatch_lines(["*hello world*"])
def test_parse_setoption(self):
parser = parseopt.Parser()
parser.addoption("--hello", dest="hello", action="store")