* 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:
parent
ab9f6a75ad
commit
1f29529a24
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
-------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
================================
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
try:
|
||||
import execnet
|
||||
except ImportError:
|
||||
collect_ignore = ['.']
|
|
@ -1,4 +1,5 @@
|
|||
import py
|
||||
py.test.importorskip("execnet")
|
||||
from py.__.test.looponfail.remote import LooponfailingSession, LoopState, RemoteControl
|
||||
|
||||
class TestRemoteControl:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue