diff --git a/py/__init__.py b/py/__init__.py index 0a92588ca..b2c5a76a8 100644 --- a/py/__init__.py +++ b/py/__init__.py @@ -27,8 +27,8 @@ version = "1.0.0a1" initpkg(__name__, description = "pylib and py.test: agile development and test support library", - revision = int('$LastChangedRevision: 63144 $'.split(':')[1][:-1]), - lastchangedate = '$LastChangedDate: 2009-03-20 16:36:45 +0100 (Fri, 20 Mar 2009) $', + revision = int('$LastChangedRevision: 63147 $'.split(':')[1][:-1]), + lastchangedate = '$LastChangedDate: 2009-03-20 17:28:14 +0100 (Fri, 20 Mar 2009) $', version = version, url = "http://pylib.org", download_url = "http://codespeak.net/py/0.9.2/download.html", @@ -151,7 +151,6 @@ initpkg(__name__, 'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'), 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), - 'execnet.GatewaySpec' : ('./execnet/gwmanage.py', 'GatewaySpec'), 'execnet.XSpec' : ('./execnet/xspec.py', 'XSpec'), 'execnet.makegateway' : ('./execnet/xspec.py', 'makegateway'), 'execnet.MultiGateway' : ('./execnet/multi.py', 'MultiGateway'), diff --git a/py/conftest.py b/py/conftest.py index 96564533f..048c27e3f 100644 --- a/py/conftest.py +++ b/py/conftest.py @@ -37,9 +37,11 @@ def getspecssh(config=None): if not py.path.local.sysfind("ssh"): py.test.skip("command not found: ssh") return spec + py.test.skip("need '--gx ssh=...'") def getsocketspec(config=None): xspecs = getgspecs(config) for spec in xspecs: if spec.socket: return spec + py.test.skip("need '--gx socket=...'") diff --git a/py/execnet/gwmanage.py b/py/execnet/gwmanage.py index 6049a8735..f9a52e4b5 100644 --- a/py/execnet/gwmanage.py +++ b/py/execnet/gwmanage.py @@ -1,105 +1,19 @@ """ instantiating, managing and rsyncing to hosts -Host specification strings and implied gateways: - - socket:hostname:port:path SocketGateway - popen[-executable][:path] PopenGateway - [ssh:]spec:path SshGateway - * [SshGateway] - -on hostspec.makeconnection() a Host object -will be created which has an instantiated gateway. -the remote side will be chdir()ed to the specified path. -if no path was specified, do no chdir() at all. - - """ + import py import sys, os -from py.__.test.dsession.masterslave import MasterNode -from py.__.test import event from py.__.execnet.channel import RemoteError NO_ENDMARKER_WANTED = object() -class GatewaySpec(object): - python = None - def __init__(self, spec, defaultjoinpath="pyexecnetcache"): - self._spec = spec - if spec == "popen" or spec.startswith("popen:"): - parts = spec.split(":", 2) - self.type = self.address = parts.pop(0) - if parts: - python = parts.pop(0) - # XXX XXX XXX do better GWSPEC that can deal - # with "C:" - if py.std.sys.platform == "win32" and len(python) == 1: - python = "%s:%s" %(python, parts.pop(0)) - self.python = python - if parts: - self.joinpath = parts.pop(0) - else: - self.joinpath = "" - if not self.python: - self.python = py.std.sys.executable - - elif spec.startswith("socket:"): - parts = spec[7:].split(":", 2) - self.address = parts.pop(0) - if parts: - port = int(parts.pop(0)) - self.address = self.address, port - self.joinpath = parts and parts.pop(0) or "" - self.type = "socket" - else: - if spec.startswith("ssh:"): - spec = spec[4:] - parts = spec.split(":", 2) - self.address = parts.pop(0) - self.python = parts and parts.pop(0) or "python" - self.joinpath = parts and parts.pop(0) or "" - self.type = "ssh" - if not self.joinpath and not self.inplacelocal(): - self.joinpath = defaultjoinpath - - def inplacelocal(self): - return bool(self.type == "popen" and not self.joinpath) - - def __str__(self): - return "" % self._spec - __repr__ = __str__ - - def makegateway(self, waitclose=True): - if self.type == "popen": - gw = py.execnet.PopenGateway(python=self.python) - elif self.type == "socket": - gw = py.execnet.SocketGateway(*self.address) - elif self.type == "ssh": - gw = py.execnet.SshGateway(self.address, remotepython=self.python) - if self.joinpath: - channel = gw.remote_exec(""" - import os - path = %r - try: - os.chdir(path) - except OSError: - os.mkdir(path) - os.chdir(path) - """ % self.joinpath) - if waitclose: - channel.waitclose() - else: - if waitclose: - gw.remote_exec("").waitclose() - gw.spec = self - return gw - class GatewayManager: RemoteError = RemoteError def __init__(self, specs): - self.specs = [GatewaySpec(spec) for spec in specs] + self.specs = [py.execnet.XSpec(spec) for spec in specs] self.gateways = [] def trace(self, msg): @@ -111,7 +25,7 @@ class GatewayManager: def makegateways(self): assert not self.gateways for spec in self.specs: - gw = spec.makegateway() + gw = py.execnet.makegateway(spec) self.gateways.append(gw) gw.id = "[%s]" % len(self.gateways) self.notify("gwmanage_newgateway", gw) @@ -121,7 +35,7 @@ class GatewayManager: self.makegateways() l = [] for gw in self.gateways: - if gw.spec.inplacelocal(): + if gw.spec._samefilesystem(): if inplacelocal: l.append(gw) else: @@ -150,15 +64,14 @@ class GatewayManager: seen = {} for gateway in self.gateways: spec = gateway.spec - if not spec.inplacelocal(): - key = spec.type, spec.address, spec.joinpath - if key in seen: + if not spec._samefilesystem(): + if spec in seen: continue def finished(): if notify: notify("rsyncrootready", spec, source) rsync.add_target_host(gateway, finished=finished) - seen[key] = gateway + seen[spec] = gateway if seen: self.notify("gwmanage_rsyncstart", source=source, gateways=seen.values()) rsync.send() @@ -203,5 +116,5 @@ class HostRSync(py.execnet.RSync): def _report_send_file(self, gateway, modified_rel_path): if self._verbose: path = os.path.basename(self._sourcedir) + "/" + modified_rel_path - remotepath = gateway.spec.joinpath + remotepath = gateway.spec.chdir print '%s:%s <= %s' % (gateway.remoteaddress, remotepath, path) diff --git a/py/execnet/testing/test_gwmanage.py b/py/execnet/testing/test_gwmanage.py index d115732f2..62a669ffa 100644 --- a/py/execnet/testing/test_gwmanage.py +++ b/py/execnet/testing/test_gwmanage.py @@ -37,7 +37,7 @@ class TestGatewayManagerPopen: assert not len(hm.gateways) def test_hostmanager_rsync_popen_with_path(self, source, dest): - hm = GatewayManager(["popen::%s" %dest] * 1) + hm = GatewayManager(["popen//chdir=%s" %dest] * 1) hm.makegateways() source.ensure("dir1", "dir2", "hello") l = [] @@ -51,7 +51,7 @@ class TestGatewayManagerPopen: assert dest.join("dir1", "dir2", 'hello').check() def test_hostmanage_rsync_same_popen_twice(self, source, dest, eventrecorder): - hm = GatewayManager(["popen::%s" %dest] * 2) + hm = GatewayManager(["popen//chdir=%s" %dest] * 2) hm.makegateways() source.ensure("dir1", "dir2", "hello") hm.rsync(source) @@ -65,7 +65,7 @@ class TestGatewayManagerPopen: def test_multi_chdir_popen_with_path(self, testdir): import os - hm = GatewayManager(["popen::hello"] * 2) + hm = GatewayManager(["popen//chdir=hello"] * 2) testdir.tmpdir.chdir() hellopath = testdir.tmpdir.mkdir("hello") hm.makegateways() @@ -122,8 +122,7 @@ class TestHRSync: assert 'somedir' in basenames def test_hrsync_one_host(self, source, dest): - spec = py.execnet.GatewaySpec("popen::%s" % dest) - gw = spec.makegateway() + gw = py.execnet.makegateway("popen//chdir=%s" % dest) finished = [] rsync = HostRSync(source) rsync.add_target_host(gw, finished=lambda: finished.append(1)) diff --git a/py/execnet/testing/test_gwspec.py b/py/execnet/testing/test_gwspec.py deleted file mode 100644 index 96e02a646..000000000 --- a/py/execnet/testing/test_gwspec.py +++ /dev/null @@ -1,101 +0,0 @@ -""" - tests for py.execnet.GatewaySpec -""" - -import py - -class TestGatewaySpec: - """ - socket:hostname:port:path SocketGateway - popen[-executable][:path] PopenGateway - [ssh:]spec:path SshGateway - * [SshGateway] - """ - def test_popen(self): - for python in ('', 'python2.4'): - for joinpath in ('', 'abc', 'ab:cd', '/x/y'): - s = ":".join(["popen", python, joinpath]) - print s - spec = py.execnet.GatewaySpec(s) - assert spec.address == "popen" - assert spec.python == (python or py.std.sys.executable) - assert spec.joinpath == joinpath - assert spec.type == "popen" - spec2 = py.execnet.GatewaySpec("popen" + joinpath) - self._equality(spec, spec2) - - def test_ssh(self): - for prefix in ('ssh', ''): # ssh is default - for hostpart in ('x.y', 'xyz@x.y'): - for python in ('python', 'python2.5'): - for joinpath in ('', 'abc', 'ab:cd', '/tmp'): - specstring = ":".join([prefix, hostpart, python, joinpath]) - if specstring[0] == ":": - specstring = specstring[1:] - print specstring - spec = py.execnet.GatewaySpec(specstring) - assert spec.address == hostpart - assert spec.python == python - if joinpath: - assert spec.joinpath == joinpath - else: - assert spec.joinpath == "pyexecnetcache" - assert spec.type == "ssh" - spec2 = py.execnet.GatewaySpec(specstring) - self._equality(spec, spec2) - - def test_socket(self): - for hostpart in ('x.y', 'x', 'popen'): - for port in ":80", ":1000": - for joinpath in ('', ':abc', ':abc:de'): - spec = py.execnet.GatewaySpec("socket:" + hostpart + port + joinpath) - assert spec.address == (hostpart, int(port[1:])) - if joinpath[1:]: - assert spec.joinpath == joinpath[1:] - else: - assert spec.joinpath == "pyexecnetcache" - assert spec.type == "socket" - spec2 = py.execnet.GatewaySpec("socket:" + hostpart + port + joinpath) - self._equality(spec, spec2) - - def _equality(self, spec1, spec2): - assert spec1 != spec2 - assert hash(spec1) != hash(spec2) - assert not (spec1 == spec2) - - -class TestGatewaySpecAPI: - def test_popen_nopath_makegateway(self, testdir): - spec = py.execnet.GatewaySpec("popen") - gw = spec.makegateway() - p = gw.remote_exec("import os; channel.send(os.getcwd())").receive() - curdir = py.std.os.getcwd() - assert curdir == p - gw.exit() - - def test_popen_makegateway(self, testdir): - spec = py.execnet.GatewaySpec("popen::" + str(testdir.tmpdir)) - gw = spec.makegateway() - p = gw.remote_exec("import os; channel.send(os.getcwd())").receive() - assert spec.joinpath == p - gw.exit() - - def test_popen_makegateway_python(self, testdir): - spec = py.execnet.GatewaySpec("popen:%s" % py.std.sys.executable) - gw = spec.makegateway() - res = gw.remote_exec("import sys ; channel.send(sys.executable)").receive() - assert py.std.sys.executable == py.std.sys.executable - gw.exit() - - def test_ssh(self, specssh): - sshhost = specssh.ssh - spec = py.execnet.GatewaySpec("ssh:" + sshhost) - gw = spec.makegateway() - p = gw.remote_exec("import os ; channel.send(os.getcwd())").receive() - gw.exit() - - @py.test.mark.xfail("implement socketserver test scenario") - def test_socketgateway(self): - gw = py.execnet.PopenGateway() - spec = py.execnet.GatewaySpec("ssh:" + sshhost) - diff --git a/py/execnet/testing/test_xspec.py b/py/execnet/testing/test_xspec.py index 56b31a1f9..983bd1322 100644 --- a/py/execnet/testing/test_xspec.py +++ b/py/execnet/testing/test_xspec.py @@ -4,23 +4,23 @@ XSpec = py.execnet.XSpec class TestXSpec: def test_attributes(self): - spec = XSpec("socket=192.168.102.2:8888//python=c:/this/python2.5//path=d:\hello") + spec = XSpec("socket=192.168.102.2:8888//python=c:/this/python2.5//chdir=d:\hello") assert spec.socket == "192.168.102.2:8888" assert spec.python == "c:/this/python2.5" - assert spec.path == "d:\hello" - assert spec.xyz is None + assert spec.chdir == "d:\hello" + assert not hasattr(spec, 'xyz') py.test.raises(AttributeError, "spec._hello") spec = XSpec("socket=192.168.102.2:8888//python=python2.5") assert spec.socket == "192.168.102.2:8888" assert spec.python == "python2.5" - assert spec.path is None + assert spec.chdir is None - spec = XSpec("ssh=user@host//path=/hello/this//python=/usr/bin/python2.5") + spec = XSpec("ssh=user@host//chdir=/hello/this//python=/usr/bin/python2.5") assert spec.ssh == "user@host" assert spec.python == "/usr/bin/python2.5" - assert spec.path == "/hello/this" + assert spec.chdir == "/hello/this" spec = XSpec("popen") assert spec.popen == True @@ -28,7 +28,17 @@ class TestXSpec: def test__samefilesystem(self): assert XSpec("popen")._samefilesystem() assert XSpec("popen//python=123")._samefilesystem() - assert not XSpec("popen//path=hello")._samefilesystem() + assert not XSpec("popen//chdir=hello")._samefilesystem() + + def test__spec_spec(self): + for x in ("popen", "popen//python=this"): + assert XSpec(x)._spec == x + + def test_hash_equality(self): + assert XSpec("popen") == XSpec("popen") + assert hash(XSpec("popen")) == hash(XSpec("popen")) + assert XSpec("popen//python=123") != XSpec("popen") + assert hash(XSpec("socket=hello:8080")) != hash(XSpec("popen")) class TestMakegateway: def test_popen(self): diff --git a/py/execnet/xspec.py b/py/execnet/xspec.py index 676da1e32..0b1f82db7 100644 --- a/py/execnet/xspec.py +++ b/py/execnet/xspec.py @@ -9,22 +9,36 @@ class XSpec: * keys are not allowed to start with underscore * if no "=value" is given, assume a boolean True value """ - def __init__(self, *strings): - for string in strings: - for keyvalue in string.split("//"): - i = keyvalue.find("=") - if i == -1: - setattr(self, keyvalue, True) - else: - setattr(self, keyvalue[:i], keyvalue[i+1:]) + # XXX for now we are very restrictive about actually allowed key-values + popen = ssh = socket = python = chdir = None - def __getattr__(self, name): - if name[0] == "_": - raise AttributeError(name) - return None + def __init__(self, string): + self._spec = string + for keyvalue in string.split("//"): + i = keyvalue.find("=") + if i == -1: + key, value = keyvalue, True + else: + key, value = keyvalue[:i], keyvalue[i+1:] + # XXX be restrictive for now + if key not in XSpec.__dict__: + raise AttributeError("%r not a valid attribute" % key) + setattr(self, key, value) + + def __hash__(self): + return hash(self._spec) + def __eq__(self, other): + return self._spec == getattr(other, '_spec', None) + def __ne__(self, other): + return self._spec != getattr(other, '_spec', None) + + #def __getattr__(self, name): + # if name[0] == "_": + # raise AttributeError(name) + # return None def _samefilesystem(self): - return bool(self.popen and not self.path) + return bool(self.popen and not self.chdir) def makegateway(spec): if not isinstance(spec, XSpec):