From hpk at codespeak.net Fri Aug 1 13:46:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 1 Aug 2008 13:46:32 +0200 (CEST) Subject: [py-svn] r56892 - in py/branch/event/py: code test2/rep test2/rep/testing test2/testing Message-ID: <20080801114632.7749F169E27@codespeak.net> Author: hpk Date: Fri Aug 1 13:46:29 2008 New Revision: 56892 Modified: py/branch/event/py/code/tbpresent.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py Log: * test and impl for skip related functionality * enhancements to test setup/help machinery * small refactorings Modified: py/branch/event/py/code/tbpresent.py ============================================================================== --- py/branch/event/py/code/tbpresent.py (original) +++ py/branch/event/py/code/tbpresent.py Fri Aug 1 13:46:29 2008 @@ -10,10 +10,13 @@ flow_marker = ">" fail_marker = "E" - def __init__(self, showlocals=False, style="long"): + def __init__(self, out=None, showlocals=False, style="long"): self.showlocals = showlocals self.style = style - self.out = py.io.TerminalWriter() + if out is None: + self.out = py.io.TerminalWriter() + else: + self.out = out def repr_source(self, source, marker_location=-1): """ This one represents piece of source with possible Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Fri Aug 1 13:46:29 2008 @@ -28,8 +28,9 @@ def rep_CollectionFinish(self, ev): outcome = ev.runinfo.outcome - if outcome == "failed": - l = self._outcome2rep.setdefault("failed_collection", []) + if outcome in ("failed", "skipped"): + key = outcome + "_collection" + l = self._outcome2rep.setdefault(key, []) l.append(ev) def getreports(self, outcome=None, category=None): @@ -38,7 +39,10 @@ the category. """ if outcome is not None: - if outcome not in repevent.validoutcomes: + check = outcome + if outcome.endswith("_collection"): + check = outcome[:outcome.rfind("_")] + if check not in repevent.validoutcomes: raise ValueError("not a valid outcome %r" %(outcome)) l = self._outcome2rep.setdefault(outcome, []) return l[:] @@ -48,6 +52,10 @@ l += self.getreports("failed_crashed") l += self.getreports("failed_setup") l += self.getreports("failed_collection") - return l - return self.getreports(category) + elif category == "skipped": + l = self.getreports("skipped") + l += self.getreports("skipped_collection") + else: + l = self.getreports(category) + return l Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Fri Aug 1 13:46:29 2008 @@ -38,7 +38,8 @@ self.out.line("") for ev in self.getreports(category="failed"): #self.out.sep("_", "entrypoint: %s" %(ev.repr_path[1],)) - try: self.out.line(ev.repr_run) + try: + self.out.line(ev.repr_run) except AttributeError: self.out.line(ev.runinfo.repr_run()) #self.out.sep("=") Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Fri Aug 1 13:46:29 2008 @@ -97,3 +97,21 @@ assert l == [ev] l = rep.getreports("failed_collection") assert l == [ev] + + def test_skip_testitems_and_collections(self): + rep = BaseReporter() + runinfo = RunInfo(None, "skipped", None, None) + ev1 = repevent.CollectionFinish(None, runinfo) + rep.processevent(ev1) + ev2 = repevent.ItemTestReport(None, "skipped", None, None) + rep.processevent(ev2) + l = rep.getreports(category="skipped") + assert l == [ev2, ev1] + l = rep.getreports("skipped_collection") + assert l == [ev1] + + def test_skip_reasons(self): + + + + Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Fri Aug 1 13:46:29 2008 @@ -4,7 +4,26 @@ def setup_module(mod): mod.modtmpdir = py.test2.ensuretemp(mod.__name__) +class Result: + def __init__(self, ret, outlines, errlines): + self.ret = ret + self.outlines = outlines + self.errlines = errlines + class TestPyTest: + def runpytest(self, *args): + pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") + cmdargs = [py.std.sys.executable, pytestcmd] + list(args) + cmdargs = map(str, cmdargs) + p1 = py.path.local("stdout") + p2 = py.path.local("stderr") + print "running", cmdargs, "curdir=", py.path.local() + popen = py.std.subprocess.Popen(cmdargs, + stdout=p1.open("w"), stderr=p2.open("w")) + ret = popen.wait() + out, err = p1.readlines(cr=0), p2.readlines(cr=0) + return Result(ret, out, err) + def setup_method(self, method): name = self.__class__.__name__ + "_" + method.__name__ self.old = modtmpdir.mkdir(name).chdir() @@ -47,20 +66,38 @@ ]) assert result.ret == 1 - def runpytest(self, *args): - pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") - cmdargs = [py.std.sys.executable, pytestcmd] + list(args) - cmdargs = map(str, cmdargs) - p1 = py.path.local("stdout") - p2 = py.path.local("stderr") - popen = py.std.subprocess.Popen(cmdargs, - stdout=p1.open("w"), stderr=p2.open("w")) - ret = popen.wait() - out, err = p1.readlines(cr=0), p2.readlines(cr=0) - return Result(ret, out, err) + def makepyfile(self, **kwargs): + assert len(kwargs) == 1 + name, value = kwargs.popitem() + p = py.path.local(name).new(ext=".py") + p.write(py.code.Source(value)) + return p + + def test_skipped_reasons(self): + p1 = self.makepyfile(test_one=""" + from conftest import doskip + def setup_function(func): + doskip() + def test_func(): + pass + class TestClass: + def test_method(self): + doskip() + """) + p2 = self.makepyfile(test_two=""" + from conftest import doskip + doskip() + """) + p3 = self.makepyfile(conftest=""" + import py + def doskip(): + py.test.skip('test') + """) + result = self.runpytest() + extra = assert_lines_contain_lines(result.outlines, [ + "*test_one.py ss", + "*test_two.py - Skipped -", + "___* reasons for skipped tests *___", + "%s:3: 3 Skipped, reason: test" %(p3,) + ]) -class Result: - def __init__(self, ret, outlines, errlines): - self.ret = ret - self.outlines = outlines - self.errlines = errlines Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Fri Aug 1 13:46:29 2008 @@ -13,6 +13,7 @@ """ import py from py.__.test2 import repevent +from fnmatch import fnmatch def eventappender(config): l = [] @@ -141,11 +142,13 @@ for line in lines2: while lines1: nextline = lines1.pop(0) - print "comparing", repr(line) - print "and :", repr(nextline) if line == nextline: - print "match:", repr(line) + print "exact match:", repr(line) break + elif fnmatch(nextline, line): + print "fnmatch:", repr(line), repr(nextline) + else: + print "nomatch:", repr(line), repr(nextline) extralines.append(nextline) else: if line != nextline: From hpk at codespeak.net Fri Aug 1 14:05:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 1 Aug 2008 14:05:21 +0200 (CEST) Subject: [py-svn] r56894 - py/branch/event/py/test2/rep/testing Message-ID: <20080801120521.B7B58168553@codespeak.net> Author: hpk Date: Fri Aug 1 14:05:20 2008 New Revision: 56894 Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py Log: sketch test for skip reasons, fix syntax errro Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Fri Aug 1 14:05:20 2008 @@ -111,7 +111,17 @@ assert l == [ev1] def test_skip_reasons(self): - - - + py.test.skip("unify ItemTestReport and CollectionReport first") + rep = BaseReporter() + reprpath = ('xyz', 3, None) + reprun = 'hello' # see XXX + ev1 = repevent.CollectionReport(None, "skipped", reprrun, reprpath) + ev2 = repevent.ItemTestReport(None, "skipped", reprrun, reprpath) + l = rep._folded_skips() + assert len(l) == 1 + num, loc, skip_reason = l[0] + assert num == 2 + assert loc == repr_path[:2] + # XXX how to assert about the reason? + #assert skip_reason == reprrun? From hpk at codespeak.net Fri Aug 1 20:01:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 1 Aug 2008 20:01:41 +0200 (CEST) Subject: [py-svn] r56901 - in py/branch/event/py/test2: . testing Message-ID: <20080801180141.2CEB3169ECC@codespeak.net> Author: hpk Date: Fri Aug 1 20:01:37 2008 New Revision: 56901 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/config.py py/branch/event/py/test2/testing/test_config.py Log: add pickling experimentally Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Fri Aug 1 20:01:37 2008 @@ -46,6 +46,13 @@ self._config = config self.fspath = getattr(parent, 'fspath', None) + def __getstate__(self): + config = self._config + return (config, config.get_collector_trail(self)) + def __setstate__(self, (config, trail)): + newnode = config._getcollector(trail) + self.__dict__.update(newnode.__dict__) + def __repr__(self): return "<%s %r>" %(self.__class__.__name__, self.name) @@ -90,8 +97,6 @@ return [x.name for x in self.listchain()] def _getitembynames(self, namelist): - if isinstance(namelist, str): - namelist = namelist.split("/") cur = self for name in namelist: if name: Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Fri Aug 1 20:01:37 2008 @@ -39,6 +39,12 @@ self.bus = EventBus() self._setupstate = SetupState() + def __getstate__(self): + return self._makerepr([]) + def __setstate__(self, repr): + self.__init__() + self._repr = repr # this needs to be used manually after unpickling + def parse(self, args): """ parse cmdline arguments into this config object. Note that this can only be called once per testing process. Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Fri Aug 1 20:01:37 2008 @@ -461,3 +461,33 @@ py.test2.raises(ValueError, "config.get_collector_trail(col3)") + def test_config_and_collector_pickling(self): + from cPickle import Pickler, Unpickler + dir1 = self.tmpdir.ensure("somedir", dir=1) + config = py.test2.config._reparse([self.tmpdir]) + col = config._getcollector(config.topdir) + col1 = col.join(dir1.basename) + assert col1.parent is col + io = py.std.cStringIO.StringIO() + pickler = Pickler(io) + pickler.dump(config) + pickler.dump(col) + pickler.dump(col1) + pickler.dump(col) + io.seek(0) + unpickler = Unpickler(io) + newconfig = unpickler.load() + topdir = self.tmpdir.ensure("newtopdir", dir=1) + topdir.ensure("somedir", dir=1) + newconfig._initdirect(topdir, newconfig._repr) + newcol = unpickler.load() + newcol2 = unpickler.load() + newcol3 = unpickler.load() + assert newcol2._config is newconfig + assert newcol2.parent == newcol + assert newcol._config is newconfig + assert newconfig.topdir == topdir + assert newcol3 is newcol + assert newcol.fspath == topdir + assert newcol2.fspath.basename == dir1.basename + assert newcol2.fspath.relto(topdir) From hpk at codespeak.net Fri Aug 1 23:31:53 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 1 Aug 2008 23:31:53 +0200 (CEST) Subject: [py-svn] r56904 - py/branch/event/py/test2/testing Message-ID: <20080801213153.814E4169E3E@codespeak.net> Author: hpk Date: Fri Aug 1 23:31:51 2008 New Revision: 56904 Modified: py/branch/event/py/test2/testing/test_collect.py Log: removed test that produced "/" testnames Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Fri Aug 1 23:31:51 2008 @@ -49,8 +49,6 @@ assert len(l) == 3 x = col1._getitembynames(l[1:]) assert x.name == "filetest.py" - x = col1._getitembynames("/".join(l[1:])) - assert x.name == "filetest.py" l2 = x.listnames() assert len(l2) == 3 From hpk at codespeak.net Sat Aug 2 08:49:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 08:49:03 +0200 (CEST) Subject: [py-svn] r56908 - in py/branch/event/py/execnet: . testing Message-ID: <20080802064903.C458C169ECF@codespeak.net> Author: hpk Date: Sat Aug 2 08:49:01 2008 New Revision: 56908 Modified: py/branch/event/py/execnet/channel.py py/branch/event/py/execnet/gateway.py py/branch/event/py/execnet/message.py py/branch/event/py/execnet/testing/test_gateway.py Log: add experimental pickling support to execnet channels Modified: py/branch/event/py/execnet/channel.py ============================================================================== --- py/branch/event/py/execnet/channel.py (original) +++ py/branch/event/py/execnet/channel.py Sat Aug 2 08:49:01 2008 @@ -1,5 +1,7 @@ import threading, weakref, sys import Queue +from cPickle import Pickler, Unpickler + if 'Message' not in globals(): from py.__.execnet.message import Message @@ -25,8 +27,9 @@ class Channel(object): """Communication channel between two possibly remote threads of code. """ RemoteError = RemoteError + _picklememo = None # not None means that we are in pickling mode - def __init__(self, gateway, id): + def __init__(self, gateway, id, pickle=False): assert isinstance(id, int) self.gateway = gateway self.id = id @@ -34,6 +37,8 @@ self._closed = False self._receiveclosed = threading.Event() self._remoteerrors = [] + if pickle: + self._picklememo = {} def setcallback(self, callback, endmarker=NO_ENDMARKER_WANTED): queue = self._items @@ -156,6 +161,13 @@ if isinstance(item, Channel): data = Message.CHANNEL_NEW(self.id, item.id) else: + if self._picklememo is not None: + from cStringIO import StringIO + f = StringIO() + pickler = Pickler(f, protocol=-1) # use best protocol + pickler.memo = self._picklememo + pickler.dump(item) + item = f.getvalue() data = Message.CHANNEL_DATA(self.id, item) self.gateway._send(data) @@ -203,7 +215,7 @@ self.count = startcount self.finished = False - def new(self, id=None): + def new(self, id=None, pickle=False): """ create a new Channel with 'id' (or create new id if None). """ self._writelock.acquire() try: @@ -212,7 +224,7 @@ if id is None: id = self.count self.count += 2 - channel = Channel(self.gateway, id) + channel = Channel(self.gateway, id, pickle=pickle) self._channels[id] = channel return channel finally: @@ -270,14 +282,16 @@ queue.put(ENDMARKER) self._no_longer_opened(id) - def _local_receive(self, id, data): + def _local_receive(self, id, data, tryunpickle=False): # executes in receiver thread self._receivelock.acquire() try: + channel = self._channels.get(id) + if tryunpickle and channel and channel._picklememo is not None: + data = unpickle(data, channel._picklememo) try: callback, endmarker = self._callbacks[id] except KeyError: - channel = self._channels.get(id) queue = channel and channel._items if queue is None: pass # drop data @@ -319,3 +333,10 @@ state = self.channel.isclosed() and 'closed' or 'open' return '' %(self.channel.id, state) + +def unpickle(data, memo): + from cStringIO import StringIO + f = StringIO(data) + u = Unpickler(f) + u.memo = memo + return u.load() Modified: py/branch/event/py/execnet/gateway.py ============================================================================== --- py/branch/event/py/execnet/gateway.py (original) +++ py/branch/event/py/execnet/gateway.py Sat Aug 2 08:49:01 2008 @@ -232,7 +232,7 @@ exec co in loc finally: close() - self._trace("execution finished:", repr(source)[:50]) + self._trace("execution finished:", repr(source)[:500]) except (KeyboardInterrupt, SystemExit): pass except self._StopExecLoop: @@ -262,17 +262,19 @@ # High Level Interface # _____________________________________________________________________ # - def newchannel(self): + def newchannel(self): """ return new channel object. """ return self._channelfactory.new() - def remote_exec(self, source, stdout=None, stderr=None): + def remote_exec(self, source, stdout=None, stderr=None, _pickle=False): """ return channel object and connect it to a remote execution thread where the given 'source' executes and has the sister 'channel' object in its global namespace. The callback functions 'stdout' and 'stderr' get called on receival of remote stdout/stderr output strings. + _pickle: set to true to enable experimental support for + sending/receiving picklable objects through the channel. """ try: source = str(Source(source)) @@ -282,11 +284,11 @@ source = str(py.code.Source(source)) except ImportError: pass - channel = self.newchannel() + channel = self._channelfactory.new(pickle=_pickle) outid = self._newredirectchannelid(stdout) errid = self._newredirectchannelid(stderr) self._send(Message.CHANNEL_OPEN( - channel.id, (source, outid, errid))) + channel.id, (_pickle, (source, outid, errid)))) return channel def _remote_redirect(self, stdout=None, stderr=None): Modified: py/branch/event/py/execnet/message.py ============================================================================== --- py/branch/event/py/execnet/message.py (original) +++ py/branch/event/py/execnet/message.py Sat Aug 2 08:49:01 2008 @@ -62,8 +62,9 @@ class CHANNEL_OPEN(Message): def received(self, gateway): - channel = gateway._channelfactory.new(self.channelid) - gateway._local_schedulexec(channel=channel, sourcetask=self.data) + pickle, sourcetask = self.data + channel = gateway._channelfactory.new(self.channelid, pickle=pickle) + gateway._local_schedulexec(channel=channel, sourcetask=sourcetask) class CHANNEL_NEW(Message): def received(self, gateway): @@ -74,7 +75,8 @@ class CHANNEL_DATA(Message): def received(self, gateway): - gateway._channelfactory._local_receive(self.channelid, self.data) + gateway._channelfactory._local_receive(self.channelid, self.data, + tryunpickle=True) class CHANNEL_CLOSE(Message): def received(self, gateway): Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Sat Aug 2 08:49:01 2008 @@ -344,6 +344,22 @@ assert first.strip() == 'hello world' py.test.raises(EOFError, channel.receive) + def test_pickling(self): + channel = self.gw.remote_exec(""" + l1 = channel.receive() + l2 = channel.receive() + channel.send(l1 + l2) + channel.send(l2 is l1) + """, _pickle=True) + assert isinstance(channel._picklememo, dict) + l1 = ['hello'] + channel.send(l1) + channel.send(l1) + newl = channel.receive() + isl = channel.receive() + assert newl == l1 + l1 + assert isl == True + def test_confusion_from_os_write_stdout(self): channel = self.gw.remote_exec(""" import os @@ -383,7 +399,7 @@ """) text = c1.receive() assert text.find("execution disallowed") != -1 - + #class TestBlockingIssues: # def test_join_blocked_execution_gateway(self): # gateway = py.execnet.PopenGateway() @@ -411,7 +427,7 @@ c = gw.remote_exec("import os ; channel.send(os.getcwd())") x = c.receive() assert x == str(waschangedir) - + def test_many_popen(self): num = 4 l = [] From hpk at codespeak.net Sat Aug 2 08:55:01 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 08:55:01 +0200 (CEST) Subject: [py-svn] r56909 - in py/branch/event/py/execnet: . testing Message-ID: <20080802065501.C151F169E47@codespeak.net> Author: hpk Date: Sat Aug 2 08:55:01 2008 New Revision: 56909 Modified: py/branch/event/py/execnet/channel.py py/branch/event/py/execnet/testing/test_gateway.py Log: extend makefile to allow for "readable" files based on channel.receive() Modified: py/branch/event/py/execnet/channel.py ============================================================================== --- py/branch/event/py/execnet/channel.py (original) +++ py/branch/event/py/execnet/channel.py Sat Aug 2 08:55:01 2008 @@ -108,13 +108,16 @@ return self._closed def makefile(self, mode='w', proxyclose=False): - """ return a file-like object. Only supported mode right - now is 'w' for binary writes. If you want to have - a subsequent file.close() mean to close the channel - as well, then pass proxyclose=True. + """ return a file-like object. + mode: 'w' for binary writes, 'r' for binary reads + proxyclose: set to true if you want to have a + subsequent file.close() automatically close the channel. """ - assert mode == 'w', "mode %r not availabe" %(mode,) - return ChannelFile(channel=self, proxyclose=proxyclose) + if mode == "w": + return ChannelFileWrite(channel=self, proxyclose=proxyclose) + elif mode == "r": + return ChannelFileRead(channel=self, proxyclose=proxyclose) + raise ValueError("mode %r not availabe" %(mode,)) def close(self, error=None): """ close down this channel on both sides. """ @@ -313,18 +316,11 @@ for id in self._callbacks.keys(): self._close_callback(id) - -class ChannelFile: +class ChannelFile(object): def __init__(self, channel, proxyclose=True): self.channel = channel self._proxyclose = proxyclose - def write(self, out): - self.channel.send(out) - - def flush(self): - pass - def close(self): if self._proxyclose: self.channel.close() @@ -333,7 +329,41 @@ state = self.channel.isclosed() and 'closed' or 'open' return '' %(self.channel.id, state) +class ChannelFileWrite(ChannelFile): + def write(self, out): + self.channel.send(out) + + def flush(self): + pass +class ChannelFileRead(ChannelFile): + def __init__(self, channel, proxyclose=True): + super(ChannelFileRead, self).__init__(channel, proxyclose) + self._buffer = "" + + def read(self, n): + while len(self._buffer) < n: + try: + self._buffer += self.channel.receive() + except EOFError: + self.close() + break + ret = self._buffer[:n] + self._buffer = self._buffer[n:] + return ret + + def readline(self): + i = self._buffer.find("\n") + if i != -1: + return self.read(i+1) + line = self.read(len(self._buffer)+1) + while line and line[-1] != "\n": + c = self.read(1) + if not c: + break + line += c + return line + def unpickle(data, memo): from cStringIO import StringIO f = StringIO(data) Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Sat Aug 2 08:55:01 2008 @@ -315,7 +315,7 @@ s = subl[0] assert s.strip() == str(i) - def test_channel_file(self): + def test_channel_file_write(self): channel = self.gw.remote_exec(""" f = channel.makefile() print >>f, "hello world" @@ -344,6 +344,43 @@ assert first.strip() == 'hello world' py.test.raises(EOFError, channel.receive) + def test_channel_file_read(self): + channel = self.gw.remote_exec(""" + f = channel.makefile(mode='r') + s = f.read(2) + channel.send(s) + s = f.read(5) + channel.send(s) + """) + channel.send("xyabcde") + s1 = channel.receive() + s2 = channel.receive() + assert s1 == "xy" + assert s2 == "abcde" + + def test_channel_file_read_empty(self): + channel = self.gw.remote_exec("pass") + f = channel.makefile(mode="r") + s = f.read(3) + assert s == "" + s = f.read(5) + assert s == "" + + def test_channel_file_readline_remote(self): + channel = self.gw.remote_exec(""" + channel.send('123\\n45') + """) + channel.waitclose(TESTTIMEOUT) + f = channel.makefile(mode="r") + s = f.readline() + assert s == "123\n" + s = f.readline() + assert s == "45" + + def test_channel_makefile_incompatmode(self): + channel = self.gw.newchannel() + py.test2.raises(ValueError, 'channel.makefile("rw")') + def test_pickling(self): channel = self.gw.remote_exec(""" l1 = channel.receive() @@ -399,7 +436,8 @@ """) text = c1.receive() assert text.find("execution disallowed") != -1 - + + #class TestBlockingIssues: # def test_join_blocked_execution_gateway(self): # gateway = py.execnet.PopenGateway() @@ -532,4 +570,3 @@ py.test.raises(IOError, gw.remote_init_threads, 3) gw.exit() - From hpk at codespeak.net Sat Aug 2 09:49:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 09:49:21 +0200 (CEST) Subject: [py-svn] r56910 - in py/branch/event/py/test2: . rep/testing rsession rsession/testing testing Message-ID: <20080802074921.E7614169E4D@codespeak.net> Author: hpk Date: Sat Aug 2 09:49:19 2008 New Revision: 56910 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/genitem.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/master.py py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/rsession/testing/basetest.py py/branch/event/py/test2/rsession/testing/test_hostmanage.py py/branch/event/py/test2/rsession/testing/test_masterslave.py py/branch/event/py/test2/rsession/testing/test_rsession.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_session.py Log: shift EventBus from the (too much kitchen-sink) config object to the session object. - where it belongs. Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Sat Aug 2 09:49:19 2008 @@ -36,7 +36,6 @@ usage="usage: %prog [options] [query] [filenames of tests]") self._conftest = Conftest() self._initialized = False - self.bus = EventBus() self._setupstate = SetupState() def __getstate__(self): @@ -143,14 +142,12 @@ except AttributeError: return self._conftest.rget(name, path) - def initreporter(self): + def getreporter(self): if self.option.collectonly: from py.__.test2.rep.collectonly import Reporter else: from py.__.test2.rep.terminal import Reporter - rep = Reporter(self) - rep.activate(self.bus) - return rep + return Reporter(self) def initsession(self): """ return an initialized session object. """ Modified: py/branch/event/py/test2/genitem.py ============================================================================== --- py/branch/event/py/test2/genitem.py (original) +++ py/branch/event/py/test2/genitem.py Sat Aug 2 09:49:19 2008 @@ -6,11 +6,10 @@ from runner import basic_collect -def genitems(colitems, keywordexpr=None): +def genitems(bus, colitems, keywordexpr=None): """ yield Items from iterating over the given colitems. """ while colitems: next = colitems.pop(0) - bus = next._config.bus if isinstance(next, Item): if next._skipbykeyword(keywordexpr): bus.notify(repevent.DeselectedItem(next, keywordexpr)) @@ -24,7 +23,7 @@ bus.notify(repevent.CollectionStart(next)) runinfo = basic_collect(next, next._config._getcapture) if runinfo.hasresult(): - for x in genitems(runinfo.result, keywordexpr): + for x in genitems(bus, runinfo.result, keywordexpr): yield x bus.notify(repevent.CollectionFinish(next, runinfo)) Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Sat Aug 2 09:49:19 2008 @@ -29,6 +29,7 @@ path.write(py.code.Source(source)) print "wrote InlineCollect example to", path config = py.test2.config._reparse([path] + configargs) + self.session = config.initsession() return config._getcollector(path) class TestCollectonly(InlineCollect): @@ -62,8 +63,8 @@ """) stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=stringio) - rep.activate(modcol._config.bus) - cols = list(genitems([modcol])) + rep.activate(self.session.bus) + cols = list(genitems(self.session.bus, [modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ @@ -76,8 +77,8 @@ """) stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=stringio) - rep.activate(modcol._config.bus) - cols = list(genitems([modcol])) + rep.activate(self.session.bus) + cols = list(genitems(self.session.bus, [modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Sat Aug 2 09:49:19 2008 @@ -13,9 +13,9 @@ session.sessionstarts() rep = session.reporter assert isinstance(rep, TerminalReporter) - assert rep.processevent in config.bus._subscribers + assert rep.processevent in session.bus._subscribers session.sessionfinishes() - assert rep.processevent not in config.bus._subscribers + assert rep.processevent not in session.bus._subscribers def test_pass_skip_fail(self): modcol = self.getmodulecol([], """ @@ -29,7 +29,7 @@ """) stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) - for item in genitems([modcol]): + for item in genitems(self.session.bus, [modcol]): ev = basic_run_report(item, setupstate=modcol._config._setupstate, getcapture=modcol._config._getcapture @@ -50,8 +50,8 @@ """) stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) - rep.activate(modcol._config.bus) - l = list(genitems([modcol])) + rep.activate(self.session.bus) + l = list(genitems(self.session.bus, [modcol])) assert len(l) == 0 s = popvalue(stringio) print s Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Sat Aug 2 09:49:19 2008 @@ -115,34 +115,34 @@ return remotepath class HostManager(object): - def __init__(self, config, hosts=None): - self.config = config - roots = self.config.getvalue_pathlist("dist_rsync_roots") + def __init__(self, session, hosts=None): + self.session = session + roots = self.session.config.getvalue_pathlist("dist_rsync_roots") addrel = "" if roots is None: - roots = [self.config.topdir] - addrel = self.config.topdir.basename + roots = [self.session.config.topdir] + addrel = self.session.config.topdir.basename self._addrel = addrel self.roots = roots if hosts is None: - hosts = self.config.getvalue("dist_hosts") + hosts = self.session.config.getvalue("dist_hosts") hosts = [Host(x, addrel) for x in hosts] self.hosts = hosts def prepare_gateways(self): - python = self.config.getvalue("dist_remotepython") + python = self.session.config.getvalue("dist_remotepython") for host in self.hosts: host.initgateway(python=python) - self.config.bus.notify(repevent.HostGatewayReady(host, self.roots)) + self.session.bus.notify(repevent.HostGatewayReady(host, self.roots)) host.gw.host = host def init_rsync(self): self.prepare_gateways() # send each rsync root - ignores = self.config.getvalue_pathlist("dist_rsync_ignore") + ignores = self.session.config.getvalue_pathlist("dist_rsync_ignore") for root in self.roots: rsync = HostRSync(root, ignores=ignores, - verbose=self.config.option.verbose) + verbose=self.session.config.option.verbose) if self._addrel: destrelpath = "" else: @@ -150,12 +150,12 @@ for host in self.hosts: rsync.add_target_host(host, destrelpath) rsync.send(raises=False) - self.config.bus.notify(repevent.RsyncFinished()) + self.session.bus.notify(repevent.RsyncFinished()) def setup_hosts(self): self.init_rsync() for host in self.hosts: - host.node = MasterNode(host, self.config) + host.node = MasterNode(host, self.session) def teardown_hosts(self, timeout=1.0): # XXX teardown nodes and hosts @@ -163,7 +163,7 @@ def hostdown(event): if isinstance(event, repevent.HostDown): queue.put(event) - self.config.bus.subscribe(hostdown) + self.session.bus.subscribe(hostdown) pending_hosts = [] for host in self.hosts: Modified: py/branch/event/py/test2/rsession/master.py ============================================================================== --- py/branch/event/py/test2/rsession/master.py (original) +++ py/branch/event/py/test2/rsession/master.py Sat Aug 2 09:49:19 2008 @@ -5,11 +5,11 @@ from py.__.test2 import repevent class MasterNode(object): - def __init__(self, host, config): + def __init__(self, host, session): self.host = host - self.config = config - self.notify = config.bus.notify - self.channel = install_slave(host, config) + self.session = session + self.notify = session.bus.notify + self.channel = install_slave(host, session) self.channel.setcallback(self._callback) self.pending = [] @@ -39,7 +39,8 @@ # setting up slave code defaultconftestnames = ['dist_nicelevel'] from py.__.test2.rsession import slave -def install_slave(host, config): +def install_slave(host, session): + config = session.config channel = host.gw.remote_exec( py.code.Source(slave, "setup_at_slave_side(channel)")) configrepr = config._makerepr(defaultconftestnames) Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Sat Aug 2 09:49:19 2008 @@ -35,7 +35,7 @@ def sessionstarts(self): super(RSession, self).sessionstarts() - self.hm = HostManager(self.config) + self.hm = HostManager(self) self.hm.setup_hosts() def sessionfinishes(self): Modified: py/branch/event/py/test2/rsession/testing/basetest.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/basetest.py (original) +++ py/branch/event/py/test2/rsession/testing/basetest.py Sat Aug 2 09:49:19 2008 @@ -5,7 +5,7 @@ import py from py.__.test2.testing.setupdata import getexamplefile -class DirSetup: +class DirSetup(object): def setup_method(self, method): name = "%s.%s" %(self.__class__.__name__, method.func_name) self.tmpdir = py.test2.ensuretemp(name) @@ -18,6 +18,9 @@ path = getexamplefile("funcexamples.py") cls.config = py.test2.config._reparse([path.dirpath()]) cls.modulecol = cls.config._getcollector(path) + + def setup_method(self, method): + self.session = self.config.initsession() def getfunc(self, name): funcname = "func" + name Modified: py/branch/event/py/test2/rsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/rsession/testing/test_hostmanage.py Sat Aug 2 09:49:19 2008 @@ -176,13 +176,14 @@ self.source.join("conftest.py").write("\n".join(l)) config = py.test2.config._reparse([self.source]) assert config.topdir == self.source - hm = HostManager(config) + session = config.initsession() + hm = HostManager(session) assert hm.hosts return hm def test_hostmanager_custom_hosts(self): - config = py.test2.config._reparse([self.source]) - hm = HostManager(config, hosts=[1,2,3]) + session = py.test2.config._reparse([self.source]).initsession() + hm = HostManager(session, hosts=[1,2,3]) assert hm.hosts == [1,2,3] def test_hostmanager_init_rsync_topdir(self): @@ -191,7 +192,7 @@ hm = self.gethostmanager( dist_hosts = ["localhost:%s" % self.dest] ) - assert hm.config.topdir == self.source + assert hm.session.config.topdir == self.source hm.init_rsync() dest = self.dest.join(self.source.basename) assert dest.join("dir1").check() @@ -205,7 +206,7 @@ dist_hosts = ["localhost:%s" % self.dest], dist_rsync_roots = [str(self.source)] ) - assert hm.config.topdir == self.source + assert hm.session.config.topdir == self.source hm.init_rsync() dest = self.dest.join(self.source.basename) assert dest.join("dir1").check() @@ -220,8 +221,8 @@ self.source.join("conftest.py").write(py.code.Source(""" dist_rsync_roots = ['dir1/dir2'] """)) - config = py.test2.config._reparse([self.source]) - hm = HostManager(config, + session = py.test2.config._reparse([self.source]).initsession() + hm = HostManager(session, hosts=[Host("localhost:" + str(self.dest))]) hm.init_rsync() assert self.dest.join("dir2").check() @@ -236,8 +237,8 @@ self.source.join("conftest.py").write(py.code.Source(""" dist_rsync_ignore = ['dir1/dir2', 'dir5/dir6'] """)) - config = py.test2.config._reparse([self.source]) - hm = HostManager(config, + session = py.test2.config._reparse([self.source]).initsession() + hm = HostManager(session, hosts=[Host("localhost:" + str(self.dest))]) hm.init_rsync() assert self.dest.join("dir1").check() @@ -247,8 +248,8 @@ def test_hostmanage_optimise_localhost(self): hosts = [Host("localhost") for i in range(3)] - config = py.test2.config._reparse([self.source]) - hm = HostManager(config, hosts=hosts) + session = py.test2.config._reparse([self.source]).initsession() + hm = HostManager(session, hosts=hosts) hm.init_rsync() for host in hosts: assert host.inplacelocal Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Sat Aug 2 09:49:19 2008 @@ -12,41 +12,34 @@ def append(event): if isinstance(event, filterevent): queue.put(event) - self.config.bus.subscribe(append) + self.session.bus.subscribe(append) return queue - def test_node_down(self): - host = Host("localhost") - host.initgateway() - node = MasterNode(host, self.config) - assert not node.channel.isclosed() + def setup_method(self, method): + super(TestMasterSlaveConnection, self).setup_method(method) + self.host = Host("localhost") + self.host.initgateway() + self.node = MasterNode(self.host, self.session) + assert not self.node.channel.isclosed() + + def test_node_down(self): queue = self.makereportqueue(repevent.HostDown) - node.send(None) + self.node.send(None) event = queue.get(timeout=1.0) - assert event.host == host + assert event.host == self.host def test_send_one(self): - host = Host("localhost") - host.initgateway() - node = MasterNode(host, self.config) - assert not node.channel.isclosed() - queue = self.makereportqueue() - node.send(self.getfunc("passed")) + self.node.send(self.getfunc("passed")) event = queue.get(timeout=2.0) assert event.passed - assert not node.pending + assert not self.node.pending def test_send_multiple(self): - host = Host("localhost") - host.initgateway() - node = MasterNode(host, self.config) - assert not node.channel.isclosed() - queue = self.makereportqueue() for outcome in "passed failed skipped".split(): - node.send(self.getfunc(outcome)) + self.node.send(self.getfunc(outcome)) event = queue.get(timeout=2.0) assert getattr(event, outcome) - assert not node.pending + assert not self.node.pending Modified: py/branch/event/py/test2/rsession/testing/test_rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_rsession.py (original) +++ py/branch/event/py/test2/rsession/testing/test_rsession.py Sat Aug 2 09:49:19 2008 @@ -15,6 +15,9 @@ py.test.skip("rsession tests disabled for win32") class TestRSessionRemote(DirSetup, BasicRsessionTest): + def setup_method(self, method): + DirSetup.setup_method(self, method) + BasicRsessionTest.setup_method(self, method) def test_example_distribution_minus_x(self): self.source.ensure("sub", "conftest.py").write(py.code.Source(""" dist_hosts = ['localhost:%s'] @@ -89,8 +92,8 @@ hosts = [Host('localhost:%s' % self.dest)] queue = py.std.Queue.Queue() - self.config.bus.subscribe(queue.put) - hm = HostManager(self.config, hosts=hosts) + self.session.bus.subscribe(queue.put) + hm = HostManager(self.session, hosts=hosts) hm.setup_hosts() # actually run some tests Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Sat Aug 2 09:49:19 2008 @@ -8,6 +8,7 @@ import py from py.__.test2.genitem import genitems from py.__.test2 import repevent +from py.__.test2.eventbus import EventBus import py.__.test2.custompdb class Session(object): @@ -17,6 +18,7 @@ """ def __init__(self, config): self.config = config + self.bus = EventBus() def fixoptions(self): """ check, fix and determine conflicting options. """ @@ -39,17 +41,18 @@ def collect(self): colitems = self.config.getcolitems() keyword = self.config.option.keyword - for x in genitems(colitems, keyword): + for x in genitems(self.bus, colitems, keyword): yield x def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.reporter = self.config.initreporter() - self.config.bus.notify(repevent.SessionStart(self)) + self.reporter = self.config.getreporter() + self.reporter.activate(self.bus) + self.bus.notify(repevent.SessionStart(self)) if not self.config.option.nomagic: py.magic.invoke(assertion=1) self._failurelist = [] - self.config.bus.subscribe(self._processfailures) + self.bus.subscribe(self._processfailures) def _processfailures(self, ev): if ((isinstance(ev, repevent.ItemTestReport) and ev.failed) or @@ -63,9 +66,9 @@ self.config._setupstate.teardown_all() # xxx this can raise exceptions! if not self.config.option.nomagic: py.magic.revoke(assertion=1) - self.config.bus.notify(repevent.SessionFinish(self)) - self.config.bus.unsubscribe(self._processfailures) - self.reporter.deactivate(self.config.bus) + self.bus.notify(repevent.SessionFinish(self)) + self.bus.unsubscribe(self._processfailures) + self.reporter.deactivate(self.bus) return self._failurelist def main(self): @@ -78,7 +81,7 @@ break if not self.config.option.collectonly: testrep = self.runtest(item) - self.config.bus.notify(testrep) + self.bus.notify(testrep) finally: failures = self.sessionfinishes() return failures Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Sat Aug 2 09:49:19 2008 @@ -15,12 +15,12 @@ from py.__.test2 import repevent from fnmatch import fnmatch -def eventappender(config): +def eventappender(session): l = [] def app(ev): print ev l.append(ev) - config.bus.subscribe(app) + session.bus.subscribe(app) return l def initsorter_from_cmdline(args=None): @@ -61,7 +61,7 @@ for cls in py.std.inspect.getmro(event.__class__): if cls is not object: d.setdefault(cls, []).append(event) - config.bus.subscribe(app) + session.bus.subscribe(app) def get(self, cls): return self.cls2events.get(cls, []) Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Sat Aug 2 09:49:19 2008 @@ -271,8 +271,9 @@ tmp = self.tmp print "using tempdir", tmp config = py.test2.config._reparse([tmp]) - l = suptest.eventappender(config) - items = list(genitems(config.getcolitems())) + session = config.initsession() + l = suptest.eventappender(session) + items = list(genitems(session.bus, config.getcolitems())) return items, l def test_check_collect_hashes(self): Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Sat Aug 2 09:49:19 2008 @@ -57,14 +57,6 @@ assert len(deslist) == 1 assert deslist[0].item.name == "test_one" -def test_session_subscription(): - tmpdir = py.test.ensuretemp("test_session_subscription") - config = py.test2.config._reparse([tmpdir]) - old = config.bus._subscribers[:] - session = config.initsession() - session.main() - assert config.bus._subscribers == old - class TestSession: def test_terminal(self): From hpk at codespeak.net Sat Aug 2 09:55:48 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 09:55:48 +0200 (CEST) Subject: [py-svn] r56911 - in py/branch/event/py/test2: . rep/testing testing Message-ID: <20080802075548.1508F169E4B@codespeak.net> Author: hpk Date: Sat Aug 2 09:55:47 2008 New Revision: 56911 Removed: py/branch/event/py/test2/genitem.py Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_collect.py Log: merge genitem method to session, where it belongs Deleted: /py/branch/event/py/test2/genitem.py ============================================================================== --- /py/branch/event/py/test2/genitem.py Sat Aug 2 09:55:47 2008 +++ (empty file) @@ -1,29 +0,0 @@ -import py -from py.__.test2 import repevent -from py.__.test2.outcome import Exit -Item = (py.test.collect.Item, py.test2.collect.Item) -Collector = (py.test.collect.Collector, py.test2.collect.Collector) - -from runner import basic_collect - -def genitems(bus, colitems, keywordexpr=None): - """ yield Items from iterating over the given colitems. """ - while colitems: - next = colitems.pop(0) - if isinstance(next, Item): - if next._skipbykeyword(keywordexpr): - bus.notify(repevent.DeselectedItem(next, keywordexpr)) - if next._config.option.keyword_oneshot: - keywordexpr = None - else: - bus.notify(repevent.ItemStart(next)) - yield next - else: - assert isinstance(next, Collector) - bus.notify(repevent.CollectionStart(next)) - runinfo = basic_collect(next, next._config._getcapture) - if runinfo.hasresult(): - for x in genitems(bus, runinfo.result, keywordexpr): - yield x - bus.notify(repevent.CollectionFinish(next, runinfo)) - Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Sat Aug 2 09:55:47 2008 @@ -2,7 +2,6 @@ from py.__.test2.rep.collectonly import CollectonlyReporter from py.__.test2 import repevent from py.__.test2.testing import suptest -from py.__.test2.genitem import genitems def popvalue(stringio): value = stringio.getvalue().rstrip() @@ -64,7 +63,7 @@ stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=stringio) rep.activate(self.session.bus) - cols = list(genitems(self.session.bus, [modcol])) + cols = list(self.session.genitems([modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ @@ -78,7 +77,7 @@ stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=stringio) rep.activate(self.session.bus) - cols = list(genitems(self.session.bus, [modcol])) + cols = list(self.session.genitems([modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Sat Aug 2 09:55:47 2008 @@ -2,7 +2,6 @@ from py.__.test2.rep.terminal import TerminalReporter from py.__.test2 import repevent #from py.__.test2.testing import suptest -from py.__.test2.genitem import genitems from py.__.test2.runner import basic_run_report from test_collectonly import InlineCollect, popvalue, assert_stringio_contains_lines @@ -29,7 +28,7 @@ """) stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) - for item in genitems(self.session.bus, [modcol]): + for item in self.session.genitems([modcol]): ev = basic_run_report(item, setupstate=modcol._config._setupstate, getcapture=modcol._config._getcapture @@ -51,7 +50,7 @@ stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) rep.activate(self.session.bus) - l = list(genitems(self.session.bus, [modcol])) + l = list(self.session.genitems([modcol])) assert len(l) == 0 s = popvalue(stringio) print s Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Sat Aug 2 09:55:47 2008 @@ -6,11 +6,16 @@ """ import py -from py.__.test2.genitem import genitems from py.__.test2 import repevent from py.__.test2.eventbus import EventBus import py.__.test2.custompdb +# used for genitems() +from py.__.test2.outcome import Exit +Item = (py.test.collect.Item, py.test2.collect.Item) +Collector = (py.test.collect.Collector, py.test2.collect.Collector) +from runner import basic_collect + class Session(object): """ Session drives the collection and running of tests @@ -38,10 +43,31 @@ if option.keyword_oneshot and not option.keyword: raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" + def genitems(self, colitems, keywordexpr=None): + """ yield Items from iterating over the given colitems. """ + while colitems: + next = colitems.pop(0) + if isinstance(next, Item): + if next._skipbykeyword(keywordexpr): + self.bus.notify(repevent.DeselectedItem(next, keywordexpr)) + if next._config.option.keyword_oneshot: + keywordexpr = None + else: + self.bus.notify(repevent.ItemStart(next)) + yield next + else: + assert isinstance(next, Collector) + self.bus.notify(repevent.CollectionStart(next)) + runinfo = basic_collect(next, next._config._getcapture) + if runinfo.hasresult(): + for x in self.genitems(runinfo.result, keywordexpr): + yield x + self.bus.notify(repevent.CollectionFinish(next, runinfo)) + def collect(self): colitems = self.config.getcolitems() keyword = self.config.option.keyword - for x in genitems(self.bus, colitems, keyword): + for x in self.genitems(colitems, keyword): yield x def sessionstarts(self): Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Sat Aug 2 09:55:47 2008 @@ -1,7 +1,6 @@ from __future__ import generators import py from py.__.test2 import repevent, outcome -from py.__.test2.genitem import genitems from py.__.test2.doctest import DoctestText import setupdata, suptest from py.__.test2.conftesthandle import Conftest @@ -273,7 +272,7 @@ config = py.test2.config._reparse([tmp]) session = config.initsession() l = suptest.eventappender(session) - items = list(genitems(session.bus, config.getcolitems())) + items = list(session.genitems(config.getcolitems())) return items, l def test_check_collect_hashes(self): From hpk at codespeak.net Sat Aug 2 10:34:14 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 10:34:14 +0200 (CEST) Subject: [py-svn] r56912 - in py/branch/event/py/test2: . rep/testing rsession testing Message-ID: <20080802083414.C3432169E7F@codespeak.net> Author: hpk Date: Sat Aug 2 10:34:13 2008 New Revision: 56912 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/config.py py/branch/event/py/test2/item.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rsession/slave.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_collect.py Log: remove another kitchen-sink attribute of config, put "setupstate" to collection nodes. Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Sat Aug 2 10:34:13 2008 @@ -33,11 +33,42 @@ return self._config.getvalue(name, self.fspath) return property(fget) +class SetupState(object): + """ shared state for setting up/tearing down tests. """ + def __init__(self): + self.stack = [] + + def teardown_all(self): + while self.stack: + col = self.stack.pop() + col.teardown() + + def teardown_exact(self, item): + if self.stack[-1] == item: + col = self.stack.pop() + col.teardown() + + def prepare(self, colitem): + """ setup objects along the collector chain to the test-method + Teardown any unneccessary previously setup objects.""" + + needed_collectors = colitem.listchain() + while self.stack: + if self.stack == needed_collectors[:len(self.stack)]: + break + col = self.stack.pop() + col.teardown() + for col in needed_collectors[len(self.stack):]: + #print "setting up", col + col.setup() + self.stack.append(col) + class Node(object): """ base class for Nodes in the collection tree. Nodes with Children are "Collectors" and leaves are runnable Test Items. """ + _setupstate = SetupState() def __init__(self, name, parent=None, config=None): self.name = name self.parent = parent @@ -462,11 +493,12 @@ itemlist = self._name2items return [itemlist["[%d]" % num].name for num in xrange(len(itemlist))] - def _buildname2items(self): + def _buildname2items(self): d = {} - # test generators participate in - # the setup and teardown protocol - self._config._setupstate.prepare(self) + # XXX test generators are collectors yet participate in + # the test-item setup and teardown protocol + # if not for this we could probably avoid global setupstate + self._setupstate.prepare(self) for i, x in py.builtin.enumerate(self.obj()): call, args = self.getcallargs(x) if not callable(call): Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Sat Aug 2 10:34:13 2008 @@ -4,7 +4,6 @@ from conftesthandle import Conftest from py.__.test2.defaultconftest import adddefaultoptions from py.__.test2.eventbus import EventBus -from py.__.test2.item import SetupState optparse = py.compat.optparse @@ -36,7 +35,6 @@ usage="usage: %prog [options] [query] [filenames of tests]") self._conftest = Conftest() self._initialized = False - self._setupstate = SetupState() def __getstate__(self): return self._makerepr([]) Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sat Aug 2 10:34:13 2008 @@ -5,36 +5,6 @@ _dummy = object() -class SetupState(object): - """ shared state for setting up/tearing down tests. """ - def __init__(self): - self.stack = [] - - def teardown_all(self): - while self.stack: - col = self.stack.pop() - col.teardown() - - def teardown_exact(self, item): - if self.stack[-1] == item: - col = self.stack.pop() - col.teardown() - - def prepare(self, colitem): - """ setup objects along the collector chain to the test-method - Teardown any unneccessary previously setup objects.""" - - needed_collectors = colitem.listchain() - while self.stack: - if self.stack == needed_collectors[:len(self.stack)]: - break - col = self.stack.pop() - col.teardown() - for col in needed_collectors[len(self.stack):]: - #print "setting up", col - col.setup() - self.stack.append(col) - class Item(Node): def repr_path(self): """ return (filepath, testpath) tuple with Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Sat Aug 2 10:34:13 2008 @@ -30,7 +30,7 @@ rep = TerminalReporter(modcol._config, file=stringio) for item in self.session.genitems([modcol]): ev = basic_run_report(item, - setupstate=modcol._config._setupstate, + setupstate=modcol._setupstate, getcapture=modcol._config._getcapture ) rep.processevent(ev) Modified: py/branch/event/py/test2/rsession/slave.py ============================================================================== --- py/branch/event/py/test2/rsession/slave.py (original) +++ py/branch/event/py/test2/rsession/slave.py Sat Aug 2 10:34:13 2008 @@ -36,6 +36,6 @@ break item = config._getcollector(itemspec) runner = item._getrunner() - testrep = runner(item, item._config._setupstate, item._config._getcapture) + testrep = runner(item, item._setupstate, item._config._getcapture) send(testrep.dumps()) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Sat Aug 2 10:34:13 2008 @@ -89,7 +89,7 @@ def sessionfinishes(self): """ teardown any resources after a test run. """ - self.config._setupstate.teardown_all() # xxx this can raise exceptions! + py.test2.collect.Item._setupstate.teardown_all() # this can raise exceptions! if not self.config.option.nomagic: py.magic.revoke(assertion=1) self.bus.notify(repevent.SessionFinish(self)) @@ -120,7 +120,7 @@ runner = item._getrunner() pdb = self.config.option.usepdb and self.runpdb or None return runner(item, - setupstate=item._config._setupstate, - getcapture=item._config._getcapture, + setupstate=item._setupstate, + getcapture=self.config._getcapture, pdb=pdb) Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Sat Aug 2 10:34:13 2008 @@ -4,7 +4,7 @@ from py.__.test2.doctest import DoctestText import setupdata, suptest from py.__.test2.conftesthandle import Conftest -from py.__.test2.item import SetupState +from py.__.test2.collect import SetupState class DummyConfig: def __init__(self): From hpk at codespeak.net Sat Aug 2 10:52:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 10:52:32 +0200 (CEST) Subject: [py-svn] r56913 - in py/branch/event/py: . test2 test2/testing Message-ID: <20080802085232.40B22169E89@codespeak.net> Author: hpk Date: Sat Aug 2 10:52:28 2008 New Revision: 56913 Added: py/branch/event/py/test2/pycollect.py - copied, changed from r56912, py/branch/event/py/test2/collect.py Removed: py/branch/event/py/test2/doctest.py Modified: py/branch/event/py/__init__.py py/branch/event/py/test2/collect.py py/branch/event/py/test2/item.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_doctest.py Log: split python and non-python code realted collection nodes into two files, merge small item and doctest files, so that now there is collect.py and pycollect.py Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Sat Aug 2 10:52:28 2008 @@ -50,23 +50,23 @@ # for customization of collecting/running tests 'test.collect.Collector' : ('./test/collect.py', 'Collector'), - 'test2.collect.Collector' : ('./test2/collect.py', 'Collector'), 'test.collect.Directory' : ('./test/collect.py', 'Directory'), - 'test2.collect.Directory' : ('./test2/collect.py', 'Directory'), 'test.collect.Module' : ('./test/collect.py', 'Module'), - 'test2.collect.Module' : ('./test2/collect.py', 'Module'), 'test.collect.DoctestFile' : ('./test/collect.py', 'DoctestFile'), - 'test2.collect.DoctestFile' : ('./test2/collect.py', 'DoctestFile'), 'test.collect.Class' : ('./test/collect.py', 'Class'), - 'test2.collect.Class' : ('./test2/collect.py', 'Class'), 'test.collect.Instance' : ('./test/collect.py', 'Instance'), - 'test2.collect.Instance' : ('./test2/collect.py', 'Instance'), 'test.collect.Generator' : ('./test/collect.py', 'Generator'), - 'test2.collect.Generator' : ('./test2/collect.py', 'Generator'), 'test.collect.Item' : ('./test/item.py', 'Item'), - 'test2.collect.Item' : ('./test2/item.py', 'Item'), 'test.collect.Function' : ('./test/item.py', 'Function'), - 'test2.collect.Function' : ('./test2/item.py', 'Function'), + 'test2.collect.Collector' : ('./test2/collect.py', 'Collector'), + 'test2.collect.Directory' : ('./test2/collect.py', 'Directory'), + 'test2.collect.Module' : ('./test2/pycollect.py', 'Module'), + 'test2.collect.DoctestFile' : ('./test2/pycollect.py', 'DoctestFile'), + 'test2.collect.Class' : ('./test2/pycollect.py', 'Class'), + 'test2.collect.Instance' : ('./test2/pycollect.py', 'Instance'), + 'test2.collect.Generator' : ('./test2/pycollect.py', 'Generator'), + 'test2.collect.Item' : ('./test2/collect.py', 'Item'), + 'test2.collect.Function' : ('./test2/pycollect.py', 'Function'), # thread related API (still in early design phase) '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Sat Aug 2 10:52:28 2008 @@ -23,9 +23,7 @@ ... """ -from __future__ import generators import py -from py.__.test2 import pypresent def configproperty(name): def fget(self): @@ -279,249 +277,24 @@ name2items[name] = res return res -class PyobjMixin(object): - def obj(): - def fget(self): - try: - return self._obj - except AttributeError: - self._obj = obj = self._getobj() - return obj - def fset(self, value): - self._obj = value - return property(fget, fset, None, "underlying python object") - obj = obj() - - def _getobj(self): - return getattr(self.parent.obj, self.name) - - def repr_path(self): - fspath = pypresent.getrelpath(self._config.topdir, self.fspath) - modpath = pypresent.getmodpath(self) - return (fspath, modpath) - - -class PyCollectorMixin(PyobjMixin, Collector): - def funcnamefilter(self, name): - return name.startswith('test') - def classnamefilter(self, name): - return name.startswith('Test') - - def _buildname2items(self): - # NB. we avoid random getattrs and peek in the __dict__ instead - d = {} - dicts = [getattr(self.obj, '__dict__', {})] - for basecls in py.std.inspect.getmro(self.obj.__class__): - dicts.append(basecls.__dict__) - seen = {} - for dic in dicts: - for name, obj in dic.items(): - if name in seen: - continue - seen[name] = True - res = self.makeitem(name, obj) - if res is not None: - d[name] = res - return d - - def makeitem(self, name, obj, usefilters=True): - if (not usefilters or self.classnamefilter(name)) and \ - py.std.inspect.isclass(obj): - return self.Class(name, parent=self) - elif (not usefilters or self.funcnamefilter(name)) and callable(obj): - if obj.func_code.co_flags & 32: # generator function - return self.Generator(name, parent=self) - else: - return self.Function(name, parent=self) - - def _prepare(self): - if not hasattr(self, '_name2items'): - ex = getattr(self, '_name2items_exception', None) - if ex is not None: - raise ex[0], ex[1], ex[2] - try: - self._name2items = self._buildname2items() - except (SystemExit, KeyboardInterrupt): - raise - except: - self._name2items_exception = py.std.sys.exc_info() - raise - - def listdir(self): - self._prepare() - itemlist = self._name2items.values() - itemlist.sort() - return [x.name for x in itemlist] - - def join(self, name): - self._prepare() - return self._name2items.get(name, None) - -class Module(FSCollector, PyCollectorMixin): - _stickyfailure = None - +from py.__.test2.runner import basic_run_report, forked_run_report +class Item(Node): + """ a basic test item. """ def repr_path(self): - return (self.fspath, None) - - def listdir(self): - if getattr(self.obj, 'disabled', 0): - return [] - return super(Module, self).listdir() - - def join(self, name): - res = super(Module, self).join(name) - if res is None: - attr = getattr(self.obj, name, None) - if attr is not None: - res = self.makeitem(name, attr, usefilters=False) - return res + """ return (filepath, testpath) tuple with + filepath: a fspath representation (None if not applicable) + testpath: a path representation of the test path. - def _getobj(self): - failure = self._stickyfailure - if failure is not None: - raise failure[0], failure[1], failure[2] - try: - self._obj = obj = self.fspath.pyimport() - except KeyboardInterrupt: - raise - except: - self._stickyfailure = py.std.sys.exc_info() - raise - return self._obj - - def setup(self): - if hasattr(self.obj, 'setup_module'): - self.obj.setup_module(self.obj) - - def teardown(self): - if hasattr(self.obj, 'teardown_module'): - self.obj.teardown_module(self.obj) - - -class Class(PyCollectorMixin, Collector): - - def listdir(self): - if getattr(self.obj, 'disabled', 0): - return [] - return ["()"] - - def join(self, name): - assert name == '()' - return self.Instance(name, self) - - def setup(self): - setup_class = getattr(self.obj, 'setup_class', None) - if setup_class is not None: - setup_class = getattr(setup_class, 'im_func', setup_class) - setup_class(self.obj) - - def teardown(self): - teardown_class = getattr(self.obj, 'teardown_class', None) - if teardown_class is not None: - teardown_class = getattr(teardown_class, 'im_func', teardown_class) - teardown_class(self.obj) - - def _getsortvalue(self): - # try to locate the class in the source - not nice, but probably - # the most useful "solution" that we have - try: - file = (py.std.inspect.getsourcefile(self.obj) or - py.std.inspect.getfile(self.obj)) - if not file: - raise IOError - lines, lineno = py.std.inspect.findsource(self.obj) - return py.path.local(file), lineno - except IOError: - pass - -class Instance(PyCollectorMixin, Collector): - def _getobj(self): - return self.parent.obj() - def Function(self): - return getattr(self.obj, 'Function', - Collector.Function.__get__(self)) # XXX for python 2.2 - def _keywords(self): - return [] - Function = property(Function) - - -class FunctionMixin(PyobjMixin): - """ mixin for the code common to Function and Generator. - """ - _sortvalue = None - def _getsortvalue(self): - if self._sortvalue is None: - code = py.code.Code(self.obj) - self._sortvalue = code.path, code.firstlineno - return self._sortvalue - - def setup(self): - """ perform setup for this test function. """ - if hasattr(self.obj, 'im_self'): - name = 'setup_method' - else: - name = 'setup_function' - obj = self.parent.obj - setup_func_or_method = getattr(obj, name, None) - if setup_func_or_method is not None: - return setup_func_or_method(self.obj) - - def teardown(self): - """ perform teardown for this test function. """ - if hasattr(self.obj, 'im_self'): - name = 'teardown_method' - else: - name = 'teardown_function' - obj = self.parent.obj - teardown_func_or_meth = getattr(obj, name, None) - if teardown_func_or_meth is not None: - return teardown_func_or_meth(self.obj) - - def prunetraceback(self, traceback): - if not self._config.option.fulltrace: - code = py.code.Code(self.obj) - path, firstlineno = code.path, code.firstlineno - ntraceback = traceback.cut(path=path, firstlineno=firstlineno) - if ntraceback == traceback: - ntraceback = ntraceback.cut(path=path) - traceback = ntraceback.filter() - return traceback - -class Generator(FunctionMixin, PyCollectorMixin, Collector): - def listdir(self): - self._prepare() - itemlist = self._name2items - return [itemlist["[%d]" % num].name for num in xrange(len(itemlist))] - - def _buildname2items(self): - d = {} - # XXX test generators are collectors yet participate in - # the test-item setup and teardown protocol - # if not for this we could probably avoid global setupstate - self._setupstate.prepare(self) - for i, x in py.builtin.enumerate(self.obj()): - call, args = self.getcallargs(x) - if not callable(call): - raise TypeError("yielded test %r not callable" %(call,)) - name = "[%d]" % i - d[name] = self.Function(name, self, args, callobj=call) - return d - - def getcallargs(self, obj): - if isinstance(obj, (tuple, list)): - call, args = obj[0], obj[1:] - else: - call, args = obj, () - return call, args - -class DoctestFile(PyCollectorMixin, FSCollector): - def listdir(self): - return [self.fspath.basename] + this is used by reporters. + """ + xxx - def join(self, name): - from py.__.test2.doctest import DoctestText - if name == self.fspath.basename: - item = DoctestText(self.fspath.basename, parent=self) - item._content = self.fspath.read() - return item + def repr_run(self, excinfo): + """ return string failure represenation for this item. + """ + xxx + def _getrunner(self): + if self._config.option.boxed: + return forked_run_report + return basic_run_report Deleted: /py/branch/event/py/test2/doctest.py ============================================================================== --- /py/branch/event/py/test2/doctest.py Sat Aug 2 10:52:28 2008 +++ (empty file) @@ -1,33 +0,0 @@ -import py - -class DoctestText(py.test2.collect.Item): - - def _setcontent(self, content): - self._content = content - - #def buildname2items(self): - # parser = py.compat.doctest.DoctestParser() - # l = parser.get_examples(self._content) - # d = {} - # globs = {} - # locs - # for i, example in py.builtin.enumerate(l): - # ex = ExampleItem(example) - # d[str(i)] = ex - - def run(self): - mod = py.std.types.ModuleType(self.name) - #for line in s.split('\n'): - # if line.startswith(prefix): - # exec py.code.Source(line[len(prefix):]).compile() in mod.__dict__ - # line = "" - # else: - # l.append(line) - self.execute(mod, self._content) - - def execute(self, mod, docstring): - mod.__doc__ = docstring - failed, tot = py.compat.doctest.testmod(mod, verbose=1) - if failed: - py.test2.fail("doctest %s: %s failed out of %s" %( - self.fspath, failed, tot)) Modified: py/branch/event/py/test2/item.py ============================================================================== --- py/branch/event/py/test2/item.py (original) +++ py/branch/event/py/test2/item.py Sat Aug 2 10:52:28 2008 @@ -1,45 +1,4 @@ import py from py.__.test2.collect import FunctionMixin, Node -from py.__.test2.runner import basic_run_report, forked_run_report from py.__.test2 import pypresent -_dummy = object() - -class Item(Node): - def repr_path(self): - """ return (filepath, testpath) tuple with - filepath: a fspath representation (None if not applicable) - testpath: a path representation of the test path. - - this is used by reporters. - """ - xxx - - def repr_run(self, excinfo): - """ return string failure represenation for this item. - """ - xxx - - def _getrunner(self): - if self._config.option.boxed: - return forked_run_report - return basic_run_report - -class Function(FunctionMixin, Item): - """ a Function Item is responsible for setting up - and executing a Python callable test object. - """ - def __init__(self, name, parent, args=(), callobj=_dummy): - super(Function, self).__init__(name, parent) - self._args = args - if callobj is not _dummy: - self._obj = callobj - - def execute(self): - """ execute the given test function. """ - self.obj(*self._args) - - def repr_run(self, runinfo): - """ return a textual representation of run info. """ - return pypresent.python_repr_run(runinfo) - Copied: py/branch/event/py/test2/pycollect.py (from r56912, py/branch/event/py/test2/collect.py) ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/pycollect.py Sat Aug 2 10:52:28 2008 @@ -1,283 +1,9 @@ """ -Collect test items at filesystem and python module levels. - -Collectors and test items form a tree. The difference -between a collector and a test item as seen from the session -is smalll. Collectors usually return a list of child -collectors/items whereas items usually return None -indicating a successful test run. - -The is a schematic example of a tree of collectors and test items:: - - Directory - Module - Class - Instance - Function - Generator - ... - Function - Generator - Function - Directory - ... - +Python related Collect nodes. """ -from __future__ import generators import py -from py.__.test2 import pypresent - -def configproperty(name): - def fget(self): - #print "retrieving %r property from %s" %(name, self.fspath) - return self._config.getvalue(name, self.fspath) - return property(fget) - -class SetupState(object): - """ shared state for setting up/tearing down tests. """ - def __init__(self): - self.stack = [] - - def teardown_all(self): - while self.stack: - col = self.stack.pop() - col.teardown() - - def teardown_exact(self, item): - if self.stack[-1] == item: - col = self.stack.pop() - col.teardown() - - def prepare(self, colitem): - """ setup objects along the collector chain to the test-method - Teardown any unneccessary previously setup objects.""" - - needed_collectors = colitem.listchain() - while self.stack: - if self.stack == needed_collectors[:len(self.stack)]: - break - col = self.stack.pop() - col.teardown() - for col in needed_collectors[len(self.stack):]: - #print "setting up", col - col.setup() - self.stack.append(col) - -class Node(object): - """ base class for Nodes in the collection tree. - Nodes with Children are "Collectors" and - leaves are runnable Test Items. - """ - _setupstate = SetupState() - def __init__(self, name, parent=None, config=None): - self.name = name - self.parent = parent - if config is None: - config = getattr(parent, '_config') - self._config = config - self.fspath = getattr(parent, 'fspath', None) - - def __getstate__(self): - config = self._config - return (config, config.get_collector_trail(self)) - def __setstate__(self, (config, trail)): - newnode = config._getcollector(trail) - self.__dict__.update(newnode.__dict__) - - def __repr__(self): - return "<%s %r>" %(self.__class__.__name__, self.name) - - # methods for ordering nodes - - def __eq__(self, other): - if not isinstance(other, Node): - return False - return self.name == other.name and self.parent == other.parent - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((self.name, self.parent)) - - def __cmp__(self, other): - if not isinstance(other, Node): - return -1 - s1 = self._getsortvalue() - s2 = other._getsortvalue() - return cmp(s1, s2) - - def setup(self): - pass - - def teardown(self): - pass - - def listchain(self): - """ return list of all parent collectors up to self. """ - l = [self] - while 1: - x = l[-1] - if x.parent is not None: - l.append(x.parent) - else: - l.reverse() - return l - - def listnames(self): - return [x.name for x in self.listchain()] - - def _getitembynames(self, namelist): - cur = self - for name in namelist: - if name: - next = cur.join(name) - if next is None: - existingnames = cur.listdir() - msg = ("Collector %r does not have name %r " - "existing names are: %s" % - (cur, name, existingnames)) - raise AssertionError(msg) - cur = next - return cur - - def _keywords(self): - return [self.name] - - def _skipbykeyword(self, keywordexpr): - """ return True if they given keyword expression means to - skip this collector/item. - """ - if not keywordexpr: - return - chain = self.listchain() - for key in filter(None, keywordexpr.split()): - eor = key[:1] == '-' - if eor: - key = key[1:] - if not (eor ^ self._matchonekeyword(key, chain)): - return True - - def _matchonekeyword(self, key, chain): - elems = key.split(".") - # XXX O(n^2), anyone cares? - chain = [item._keywords() for item in chain if item._keywords()] - for start, _ in enumerate(chain): - if start + len(elems) > len(chain): - return False - for num, elem in enumerate(elems): - for keyword in chain[num + start]: - ok = False - if elem in keyword: - ok = True - break - if not ok: - break - if num == len(elems) - 1 and ok: - return True - return False - - def _getsortvalue(self): - return self.name - - def _get_collector_trail(self): - """ Shortcut - """ - return self._config.get_collector_trail(self) - - def prunetraceback(self, traceback): - return traceback - -class Collector(Node): - """ - Collector instances generate children through - their listdir() and join() methods and thus - form a tree. attributes:: - - parent: attribute pointing to the parent collector - (or None if this is the root collector) - name: basename of this collector object - """ - Module = configproperty('Module') - DoctestFile = configproperty('DoctestFile') - Directory = configproperty('Directory') - Class = configproperty('Class') - Instance = configproperty('Instance') - Function = configproperty('Function') - Generator = configproperty('Generator') - - def run(self): - """ deprecated: use listdir(). """ - py.std.warnings.warn("deprecated: use listdir()", category=DeprecationWarning) - return self.listdir() - - def multijoin(self, namelist): - """ return a list of colitems for the given namelist. """ - return [self.join(name) for name in namelist] - - def listdir(self): - """ returns a list of names available from this collector. - You can return an empty list. Callers of this method - must take care to catch exceptions properly. - """ - raise NotImplementedError("abstract") - - def join(self, name): - """ return a child collector or item for the given name. - If the return value is None there is no such child. - """ - raise NotImplementedError("abstract") - -class FSCollector(Collector): - def __init__(self, fspath, parent=None, config=None): - fspath = py.path.local(fspath) - super(FSCollector, self).__init__(fspath.basename, parent, config=config) - self.fspath = fspath - -class Directory(FSCollector): - def filefilter(self, path): - if path.check(file=1): - b = path.purebasename - ext = path.ext - return (b.startswith('test_') or - b.endswith('_test')) and ext in ('.txt', '.py') - - def recfilter(self, path): - if path.check(dir=1, dotfile=0): - return path.basename not in ('CVS', '_darcs', '{arch}') - - def repr_path(self): - return (self.fspath, None) - - def listdir(self): - files = [] - dirs = [] - for p in self.fspath.listdir(): - if self.filefilter(p): - files.append(p.basename) - elif self.recfilter(p): - dirs.append(p.basename) - files.sort() - dirs.sort() - return files + dirs - - def join(self, name): - name2items = self.__dict__.setdefault('_name2items', {}) - try: - res = name2items[name] - except KeyError: - p = self.fspath.join(name) - res = None - if p.check(file=1): - if p.ext == '.py': - res = self.Module(p, parent=self) - elif p.ext == '.txt': - res = self.DoctestFile(p, parent=self) - elif p.check(dir=1): - Directory = py.test2.config.getvalue('Directory', p) - res = Directory(p, parent=self) - name2items[name] = res - return res +from py.__.test2.collect import Collector, FSCollector, Item +from py.__.test2 import pypresent class PyobjMixin(object): def obj(): @@ -300,7 +26,6 @@ modpath = pypresent.getmodpath(self) return (fspath, modpath) - class PyCollectorMixin(PyobjMixin, Collector): def funcnamefilter(self, name): return name.startswith('test') @@ -514,14 +239,63 @@ call, args = obj, () return call, args +_dummy = object() +class Function(FunctionMixin, Item): + """ a Function Item is responsible for setting up + and executing a Python callable test object. + """ + def __init__(self, name, parent, args=(), callobj=_dummy): + super(Function, self).__init__(name, parent) + self._args = args + if callobj is not _dummy: + self._obj = callobj + + def execute(self): + """ execute the given test function. """ + self.obj(*self._args) + + def repr_run(self, runinfo): + """ return a textual representation of run info. """ + return pypresent.python_repr_run(runinfo) + class DoctestFile(PyCollectorMixin, FSCollector): def listdir(self): return [self.fspath.basename] def join(self, name): - from py.__.test2.doctest import DoctestText if name == self.fspath.basename: item = DoctestText(self.fspath.basename, parent=self) item._content = self.fspath.read() return item +class DoctestText(Item): + def _setcontent(self, content): + self._content = content + + #def buildname2items(self): + # parser = py.compat.doctest.DoctestParser() + # l = parser.get_examples(self._content) + # d = {} + # globs = {} + # locs + # for i, example in py.builtin.enumerate(l): + # ex = ExampleItem(example) + # d[str(i)] = ex + + def run(self): + mod = py.std.types.ModuleType(self.name) + #for line in s.split('\n'): + # if line.startswith(prefix): + # exec py.code.Source(line[len(prefix):]).compile() in mod.__dict__ + # line = "" + # else: + # l.append(line) + self.execute(mod, self._content) + + def execute(self, mod, docstring): + mod.__doc__ = docstring + failed, tot = py.compat.doctest.testmod(mod, verbose=1) + if failed: + py.test2.fail("doctest %s: %s failed out of %s" %( + self.fspath, failed, tot)) + Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Sat Aug 2 10:52:28 2008 @@ -1,10 +1,10 @@ from __future__ import generators import py from py.__.test2 import repevent, outcome -from py.__.test2.doctest import DoctestText import setupdata, suptest from py.__.test2.conftesthandle import Conftest from py.__.test2.collect import SetupState +from py.__.test2.pycollect import DoctestText class DummyConfig: def __init__(self): Modified: py/branch/event/py/test2/testing/test_doctest.py ============================================================================== --- py/branch/event/py/test2/testing/test_doctest.py (original) +++ py/branch/event/py/test2/testing/test_doctest.py Sat Aug 2 10:52:28 2008 @@ -1,6 +1,6 @@ import py -from py.__.test2.doctest import DoctestText +from py.__.test2.pycollect import DoctestText from py.__.test2.outcome import Skipped, Failed, Passed, Outcome def test_simple_docteststring(): From hpk at codespeak.net Sat Aug 2 10:53:55 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 10:53:55 +0200 (CEST) Subject: [py-svn] r56914 - py/branch/event/py/test2 Message-ID: <20080802085355.9F81B169E8B@codespeak.net> Author: hpk Date: Sat Aug 2 10:53:55 2008 New Revision: 56914 Removed: py/branch/event/py/test2/item.py Log: forgot to remove now unused file Deleted: /py/branch/event/py/test2/item.py ============================================================================== --- /py/branch/event/py/test2/item.py Sat Aug 2 10:53:55 2008 +++ (empty file) @@ -1,4 +0,0 @@ -import py -from py.__.test2.collect import FunctionMixin, Node -from py.__.test2 import pypresent - From hpk at codespeak.net Sat Aug 2 11:29:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 11:29:10 +0200 (CEST) Subject: [py-svn] r56915 - in py/branch/event/py/test2: . testing Message-ID: <20080802092910.A8BFF169E8B@codespeak.net> Author: hpk Date: Sat Aug 2 11:29:09 2008 New Revision: 56915 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/testing/test_config.py Log: enhance pickling of config objects a bit Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Sat Aug 2 11:29:09 2008 @@ -25,6 +25,8 @@ def __repr__(self): return "" %(self.__dict__,) +topdir_for_unpickling = [] + class Config(object): """ central bus for dealing with configuration/initialization data. """ Option = optparse.Option @@ -36,18 +38,29 @@ self._conftest = Conftest() self._initialized = False + # config objects are usually pickled across system + # barriers but they contain filesystem paths. + # upon getstate/setstate we take care to do everything + # relative to our "topdir". + # thus you need to call _setglobalunpicklingtopdir() + # for each new config object that you unpickle. + # (for robustness reasons it's not just a global var) def __getstate__(self): return self._makerepr([]) def __setstate__(self, repr): self.__init__() - self._repr = repr # this needs to be used manually after unpickling + if not topdir_for_unpickling: + raise ValueError("need topdir to unpickle config instance") + self._initdirect(topdir_for_unpickling.pop(), repr) + def _setglobalunpicklingtopdir(self, topdir): + topdir_for_unpickling.append(topdir) def parse(self, args): """ parse cmdline arguments into this config object. Note that this can only be called once per testing process. """ assert not self._initialized, ( - "can only parse cmdline args once per Config object") + "can only parse cmdline args at most once per Config object") self._initialized = True adddefaultoptions(self) self._conftest.setinitial(args) @@ -192,6 +205,7 @@ finally: config_per_process = py.test2.config = oldconfig + # XXX are makerepr and mergerepr really needed like this? def _makerepr(self, conftestnames, optnames=None): """ return a marshallable representation of conftest and cmdline options. Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Sat Aug 2 11:29:09 2008 @@ -476,10 +476,10 @@ pickler.dump(col) io.seek(0) unpickler = Unpickler(io) - newconfig = unpickler.load() topdir = self.tmpdir.ensure("newtopdir", dir=1) + config._setglobalunpicklingtopdir(topdir) + newconfig = unpickler.load() topdir.ensure("somedir", dir=1) - newconfig._initdirect(topdir, newconfig._repr) newcol = unpickler.load() newcol2 = unpickler.load() newcol3 = unpickler.load() From hpk at codespeak.net Sat Aug 2 12:35:42 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 2 Aug 2008 12:35:42 +0200 (CEST) Subject: [py-svn] r56917 - py/branch/event/py/execnet/testing Message-ID: <20080802103542.6B204169E4D@codespeak.net> Author: hpk Date: Sat Aug 2 12:35:42 2008 New Revision: 56917 Modified: py/branch/event/py/execnet/testing/test_gateway.py Log: add identity test for pickling Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Sat Aug 2 12:35:42 2008 @@ -397,6 +397,16 @@ assert newl == l1 + l1 assert isl == True + def test_pickling_identity(self): + channel = self.gw.remote_exec(""" + channel.send(channel.receive()) + """, _pickle=True) + l1 = ['hello'] + channel.send(l1) + l2 = channel.receive() + assert l1 == l2 + assert l1 is not l2 + def test_confusion_from_os_write_stdout(self): channel = self.gw.remote_exec(""" import os From hpk at codespeak.net Mon Aug 4 09:49:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 09:49:12 +0200 (CEST) Subject: [py-svn] r56951 - py/branch/event/py/test2 Message-ID: <20080804074912.1D224169E2B@codespeak.net> Author: hpk Date: Mon Aug 4 09:49:11 2008 New Revision: 56951 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/config.py py/branch/event/py/test2/pycollect.py Log: review and streamling config usage from collectors, add docstrings Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Mon Aug 4 09:49:11 2008 @@ -32,7 +32,7 @@ return property(fget) class SetupState(object): - """ shared state for setting up/tearing down tests. """ + """ shared state for setting up/tearing down test items or collectors. """ def __init__(self): self.stack = [] @@ -63,9 +63,20 @@ class Node(object): """ base class for Nodes in the collection tree. - Nodes with Children are "Collectors" and - leaves are runnable Test Items. + Collector nodes have children and + Item nodes are terminal. + + All nodes of the collection tree carry a _config + attribute for these reasons: + - to access custom Collection Nodes from a project + (defined in conftest's) + - to pickle themselves relatively to the "topdir" + - configuration/options for setup/teardown + stdout/stderr capturing and execution of test items """ + # XXX we keep global SetupState here because + # pycollect's Generators participate + # in setup/teardown procedures during collect. _setupstate = SetupState() def __init__(self, name, parent=None, config=None): self.name = name @@ -196,13 +207,9 @@ (or None if this is the root collector) name: basename of this collector object """ + Directory = configproperty('Directory') Module = configproperty('Module') DoctestFile = configproperty('DoctestFile') - Directory = configproperty('Directory') - Class = configproperty('Class') - Instance = configproperty('Instance') - Function = configproperty('Function') - Generator = configproperty('Generator') def run(self): """ deprecated: use listdir(). """ @@ -272,7 +279,10 @@ elif p.ext == '.txt': res = self.DoctestFile(p, parent=self) elif p.check(dir=1): - Directory = py.test2.config.getvalue('Directory', p) + # not use self.Directory here as + # dir/conftest.py shall be able to + # define Directory(dir) already + Directory = self._config.getvalue('Directory', p) res = Directory(p, parent=self) name2items[name] = res return res Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 09:49:11 2008 @@ -289,8 +289,6 @@ Session = 'py.__.test2.session' RemoteTerminalSession = 'py.__.test2.terminal.remote' RSession = 'py.__.test2.rsession.rsession' -LSession = 'py.__.test2.rsession.rsession' -CollectSession = 'py.__.test2.collectonly' # # helpers Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Mon Aug 4 09:49:11 2008 @@ -2,7 +2,7 @@ Python related Collect nodes. """ import py -from py.__.test2.collect import Collector, FSCollector, Item +from py.__.test2.collect import Collector, FSCollector, Item, configproperty from py.__.test2 import pypresent class PyobjMixin(object): @@ -27,6 +27,11 @@ return (fspath, modpath) class PyCollectorMixin(PyobjMixin, Collector): + Class = configproperty('Class') + Instance = configproperty('Instance') + Function = configproperty('Function') + Generator = configproperty('Generator') + def funcnamefilter(self, name): return name.startswith('test') def classnamefilter(self, name): @@ -164,7 +169,7 @@ return self.parent.obj() def Function(self): return getattr(self.obj, 'Function', - Collector.Function.__get__(self)) # XXX for python 2.2 + PyCollectorMixin.Function.__get__(self)) # XXX for python 2.2 def _keywords(self): return [] Function = property(Function) @@ -239,6 +244,9 @@ call, args = obj, () return call, args +# +# Test Items +# _dummy = object() class Function(FunctionMixin, Item): """ a Function Item is responsible for setting up @@ -269,6 +277,7 @@ return item class DoctestText(Item): + #XXX port changes from dist/trunk def _setcontent(self, content): self._content = content From hpk at codespeak.net Mon Aug 4 10:42:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 10:42:45 +0200 (CEST) Subject: [py-svn] r56953 - py/branch/event/py/test2 Message-ID: <20080804084245.EE696169E51@codespeak.net> Author: hpk Date: Mon Aug 4 10:42:45 2008 New Revision: 56953 Modified: py/branch/event/py/test2/conftesthandle.py Log: clearer docstring Modified: py/branch/event/py/test2/conftesthandle.py ============================================================================== --- py/branch/event/py/test2/conftesthandle.py (original) +++ py/branch/event/py/test2/conftesthandle.py Mon Aug 4 10:42:45 2008 @@ -15,25 +15,21 @@ self.setinitial([path]) def setinitial(self, args): - """ return a Conftest object initialized with a path obtained - from looking at the first (usually cmdline) argument that points - to an existing file object. - XXX note: conftest files may add command line options + """ try to find a first anchor path for looking up global values + from conftests. This function is usually called _before_ + argument parsing. conftest files may add command line options and we thus have no completely safe way of determining - which parts of the arguments are actually related to options. + which parts of the arguments are actually related to options + and which are file system paths. We just try here to get + bootstrapped ... """ current = py.path.local() for arg in args + [current]: anchor = current.join(arg, abs=1) if anchor.check(): # we found some file object - #print >>py.std.sys.stderr, "initializing conftest from", anchor - # conftest-lookups without a path actually mean - # lookups with our initial path. self._path2confmods[None] = self.getconftestmodules(anchor) - #print " -> ", conftest._path2confmods break - def getconftestmodules(self, path): """ return a list of imported conftest modules for the given path. """ try: From hpk at codespeak.net Mon Aug 4 11:24:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 11:24:08 +0200 (CEST) Subject: [py-svn] r56954 - in py/branch/event/py/test2: . testing Message-ID: <20080804092408.2A2B1169E8C@codespeak.net> Author: hpk Date: Mon Aug 4 11:24:06 2008 New Revision: 56954 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/config.py py/branch/event/py/test2/testing/test_config.py Log: streamline approach to config pickling. Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Mon Aug 4 11:24:06 2008 @@ -90,6 +90,9 @@ config = self._config return (config, config.get_collector_trail(self)) def __setstate__(self, (config, trail)): + if not config._initialized: + raise ValueError("incomplete unpickling of " + "config object, need call to _initafterpickle()?") newnode = config._getcollector(trail) self.__dict__.update(newnode.__dict__) Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 11:24:06 2008 @@ -30,30 +30,13 @@ class Config(object): """ central bus for dealing with configuration/initialization data. """ Option = optparse.Option + _initialized = False def __init__(self): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") self._conftest = Conftest() - self._initialized = False - - # config objects are usually pickled across system - # barriers but they contain filesystem paths. - # upon getstate/setstate we take care to do everything - # relative to our "topdir". - # thus you need to call _setglobalunpicklingtopdir() - # for each new config object that you unpickle. - # (for robustness reasons it's not just a global var) - def __getstate__(self): - return self._makerepr([]) - def __setstate__(self, repr): - self.__init__() - if not topdir_for_unpickling: - raise ValueError("need topdir to unpickle config instance") - self._initdirect(topdir_for_unpickling.pop(), repr) - def _setglobalunpicklingtopdir(self, topdir): - topdir_for_unpickling.append(topdir) def parse(self, args): """ parse cmdline arguments into this config object. @@ -72,6 +55,19 @@ self.topdir = gettopdir(args) self.args = args + # config objects are usually pickled across system + # barriers but they contain filesystem paths. + # upon getstate/setstate we take care to do everything + # relative to our "topdir". + def __getstate__(self): + return self._makerepr([]) + def __setstate__(self, repr): + self._repr = repr + def _initafterpickle(self, topdir): + self.__init__() + self._initdirect(topdir, self._repr) + del self._repr + def _initdirect(self, topdir, repr, coltrails=None): assert not self._initialized self._initialized = True Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Mon Aug 4 11:24:06 2008 @@ -163,17 +163,7 @@ config._mergerepr(repr2) assert config.option.verbose == 42 -def test_config_marshability(): - tmp = py.test2.ensuretemp("configmarshal") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write("a = object()") - config = py.test2.config._reparse([tmp]) - py.test2.raises(ValueError, "config._makerepr(conftestnames=['a'])") - - config.option.hello = lambda x: None - py.test2.raises(ValueError, "config._makerepr(conftestnames=[])") - config._makerepr(conftestnames=[], optnames=[]) - + def test_config_rconfig(): tmp = py.test2.ensuretemp("rconfigopt") tmp.ensure("__init__.py") @@ -461,6 +451,33 @@ py.test2.raises(ValueError, "config.get_collector_trail(col3)") + def test_config_picklability(self): + import cPickle + config = py.test2.config._reparse([self.tmpdir]) + s = cPickle.dumps(config) + newconfig = cPickle.loads(s) + assert not hasattr(newconfig, "topdir") + assert not newconfig._initialized + assert not hasattr(newconfig, 'args') + newconfig._initafterpickle(config.topdir) + assert newconfig.topdir == config.topdir + assert newconfig._initialized + assert newconfig.args == [self.tmpdir] + + def test_config_and_collector_pickling_missing_initafter(self): + from cPickle import Pickler, Unpickler + config = py.test2.config._reparse([self.tmpdir]) + col = config._getcollector(config.topdir) + io = py.std.cStringIO.StringIO() + pickler = Pickler(io) + pickler.dump(config) + pickler.dump(col) + io.seek(0) + unpickler = Unpickler(io) + newconfig = unpickler.load() + # we don't call _initafterpickle ... so + py.test.raises(ValueError, "unpickler.load()") + def test_config_and_collector_pickling(self): from cPickle import Pickler, Unpickler dir1 = self.tmpdir.ensure("somedir", dir=1) @@ -476,9 +493,9 @@ pickler.dump(col) io.seek(0) unpickler = Unpickler(io) - topdir = self.tmpdir.ensure("newtopdir", dir=1) - config._setglobalunpicklingtopdir(topdir) newconfig = unpickler.load() + topdir = self.tmpdir.ensure("newtopdir", dir=1) + newconfig._initafterpickle(topdir) topdir.ensure("somedir", dir=1) newcol = unpickler.load() newcol2 = unpickler.load() From hpk at codespeak.net Mon Aug 4 11:54:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 11:54:13 +0200 (CEST) Subject: [py-svn] r56957 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080804095413.896A4169EBC@codespeak.net> Author: hpk Date: Mon Aug 4 11:54:11 2008 New Revision: 56957 Modified: py/branch/event/py/test2/rsession/master.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: some code for sending/receiving config objects. Modified: py/branch/event/py/test2/rsession/master.py ============================================================================== --- py/branch/event/py/test2/rsession/master.py (original) +++ py/branch/event/py/test2/rsession/master.py Mon Aug 4 11:54:11 2008 @@ -36,6 +36,18 @@ # of hanging nodes and such raise +def send_and_receive_pickled_config(channel, config, remote_topdir): + channel.send((config, remote_topdir)) + backconfig = channel.receive() + backconfig._initafterpickle(config.topdir) + return backconfig + +def receive_and_send_pickled_config(channel): + config,topdir = channel.receive() + config._initafterpickle(topdir) + channel.send(config) + return config + # setting up slave code defaultconftestnames = ['dist_nicelevel'] from py.__.test2.rsession import slave Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Mon Aug 4 11:54:11 2008 @@ -1,11 +1,54 @@ import py -from py.__.test2.rsession.master import MasterNode +from py.__.test2.rsession.master import MasterNode +from py.__.test2.rsession.master import send_and_receive_pickled_config +from py.__.test2.rsession.master import receive_and_send_pickled_config from py.__.test2.rsession.hostmanage import Host from basetest import BasicRsessionTest from py.__.test2 import repevent +def test_receive_config(): + from cPickle import dumps, loads + l = [] + class MyChannel: + def receive(self): + return loads(l.pop()) + def send(self, arg): + l.append(dumps(arg)) + + channel = MyChannel() + config = py.test2.config._reparse([]) + topdir = py.test.ensuretemp("test_send_receive_config") + channel.send((config, topdir)) + newconfig = receive_and_send_pickled_config(channel) + assert newconfig.topdir == topdir + backconfig = channel.receive() + assert not backconfig._initialized + +def test_send_config(): + from cPickle import dumps, loads + l = [] + class MyChannel: + def receive(self): + return loads(l.pop(0)) + def send(self, arg): + l.append(dumps(arg)) + + channel = MyChannel() + config = py.test2.config._reparse([]) + topdir = py.test.ensuretemp("test_send_config") + channel.send(config) + newconfig = send_and_receive_pickled_config(channel, config, topdir) + + assert newconfig.topdir == config.topdir + assert newconfig.args == config.args + #assert newconfig.option == config.option + + backconfig,newtopdir = channel.receive() + assert not backconfig._initialized + assert newtopdir == topdir + class TestMasterSlaveConnection(BasicRsessionTest): def makereportqueue(self, filterevent=repevent.ItemTestReport): queue = py.std.Queue.Queue() From hpk at codespeak.net Mon Aug 4 12:10:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 12:10:37 +0200 (CEST) Subject: [py-svn] r56959 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080804101037.491EE169E4A@codespeak.net> Author: hpk Date: Mon Aug 4 12:10:36 2008 New Revision: 56959 Added: py/branch/event/py/test2/rsession/masterslave.py - copied, changed from r56957, py/branch/event/py/test2/rsession/master.py Removed: py/branch/event/py/test2/rsession/master.py py/branch/event/py/test2/rsession/slave.py Modified: py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: put code for remote nodes in one file Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Mon Aug 4 12:10:36 2008 @@ -1,6 +1,6 @@ import py import os -from py.__.test2.rsession.master import MasterNode +from py.__.test2.rsession.masterslave import MasterNode from py.__.test2 import repevent class Host(object): Deleted: /py/branch/event/py/test2/rsession/master.py ============================================================================== --- /py/branch/event/py/test2/rsession/master.py Mon Aug 4 12:10:36 2008 +++ (empty file) @@ -1,65 +0,0 @@ -""" -Node code for Master. -""" -import py -from py.__.test2 import repevent - -class MasterNode(object): - def __init__(self, host, session): - self.host = host - self.session = session - self.notify = session.bus.notify - self.channel = install_slave(host, session) - self.channel.setcallback(self._callback) - self.pending = [] - - def _callback(self, outcomestring): - if outcomestring is None: - self.notify(repevent.HostDown(self.host)) - return - item = self.pending.pop() - ev = repevent.ItemTestReport.fromdumps(outcomestring) - self.notify(ev) - - def send(self, item): - try: - if item is None: - self.channel.send(None) - else: - self.channel.send(item._get_collector_trail()) - self.pending.insert(0, item) - self.notify(repevent.SendItem(self.host, item)) - except IOError: - print "Sending error, channel IOError" - print self.channel._getremoteerror() - # XXX: this should go as soon as we'll have proper detection - # of hanging nodes and such - raise - -def send_and_receive_pickled_config(channel, config, remote_topdir): - channel.send((config, remote_topdir)) - backconfig = channel.receive() - backconfig._initafterpickle(config.topdir) - return backconfig - -def receive_and_send_pickled_config(channel): - config,topdir = channel.receive() - config._initafterpickle(topdir) - channel.send(config) - return config - -# setting up slave code -defaultconftestnames = ['dist_nicelevel'] -from py.__.test2.rsession import slave -def install_slave(host, session): - config = session.config - channel = host.gw.remote_exec( - py.code.Source(slave, "setup_at_slave_side(channel)")) - configrepr = config._makerepr(defaultconftestnames) - topdir = host.gw_remotepath - if topdir is None: - assert host.inplacelocal - topdir = config.topdir - channel.send(str(topdir)) - channel.send(configrepr) - return channel Copied: py/branch/event/py/test2/rsession/masterslave.py (from r56957, py/branch/event/py/test2/rsession/master.py) ============================================================================== --- py/branch/event/py/test2/rsession/master.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Mon Aug 4 12:10:36 2008 @@ -1,5 +1,5 @@ """ -Node code for Master. + Manage setup, running and local representation of remote nodes/processes. """ import py from py.__.test2 import repevent @@ -53,8 +53,10 @@ from py.__.test2.rsession import slave def install_slave(host, session): config = session.config - channel = host.gw.remote_exec( - py.code.Source(slave, "setup_at_slave_side(channel)")) + channel = host.gw.remote_exec(""" + from py.__.test2.rsession import masterslave + masterslave.setup_at_slave_side(channel) + """) configrepr = config._makerepr(defaultconftestnames) topdir = host.gw_remotepath if topdir is None: @@ -63,3 +65,36 @@ channel.send(str(topdir)) channel.send(configrepr) return channel + +def setup_at_slave_side(channel): + # our current dir is the topdir + import os, sys + basedir = channel.receive() + config_repr = channel.receive() + # setup defaults... + sys.path.insert(0, basedir) + import py + config = py.test2.config + assert not config._initialized + config._initdirect(basedir, config_repr) + if hasattr(os, 'nice'): + nice_level = config.getvalue('dist_nicelevel') + os.nice(nice_level) + if not config.option.nomagic: + py.magic.invoke(assertion=1) + slave_main(channel.receive, channel.send, config) + if not config.option.nomagic: + py.magic.revoke(assertion=1) + channel.close() + +def slave_main(receive, send, config): + while 1: + itemspec = receive() + if itemspec is None: + send(None) + break + item = config._getcollector(itemspec) + runner = item._getrunner() + testrep = runner(item, item._setupstate, item._config._getcapture) + send(testrep.dumps()) + Deleted: /py/branch/event/py/test2/rsession/slave.py ============================================================================== --- /py/branch/event/py/test2/rsession/slave.py Mon Aug 4 12:10:36 2008 +++ (empty file) @@ -1,41 +0,0 @@ -""" -Code that is running on the slave side for executing tests remotely. -""" - -### -### -# XXX be careful with importing, the setup_at_slave_side -# function runs on the other side. - -def setup_at_slave_side(channel): - # our current dir is the topdir - import os, sys - basedir = channel.receive() - config_repr = channel.receive() - # setup defaults... - sys.path.insert(0, basedir) - import py - config = py.test2.config - assert not config._initialized - config._initdirect(basedir, config_repr) - if hasattr(os, 'nice'): - nice_level = config.getvalue('dist_nicelevel') - os.nice(nice_level) - if not config.option.nomagic: - py.magic.invoke(assertion=1) - slave_main(channel.receive, channel.send, config) - if not config.option.nomagic: - py.magic.revoke(assertion=1) - channel.close() - -def slave_main(receive, send, config): - while 1: - itemspec = receive() - if itemspec is None: - send(None) - break - item = config._getcollector(itemspec) - runner = item._getrunner() - testrep = runner(item, item._setupstate, item._config._getcapture) - send(testrep.dumps()) - Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Mon Aug 4 12:10:36 2008 @@ -1,8 +1,9 @@ import py -from py.__.test2.rsession.master import MasterNode -from py.__.test2.rsession.master import send_and_receive_pickled_config -from py.__.test2.rsession.master import receive_and_send_pickled_config +from py.__.test2.rsession.masterslave import ( + MasterNode, send_and_receive_pickled_config, + receive_and_send_pickled_config +) from py.__.test2.rsession.hostmanage import Host from basetest import BasicRsessionTest from py.__.test2 import repevent From hpk at codespeak.net Mon Aug 4 12:48:16 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 12:48:16 +0200 (CEST) Subject: [py-svn] r56963 - in py/branch/event/py/execnet: . testing Message-ID: <20080804104816.F257B169E8A@codespeak.net> Author: hpk Date: Mon Aug 4 12:48:15 2008 New Revision: 56963 Modified: py/branch/event/py/execnet/channel.py py/branch/event/py/execnet/gateway.py py/branch/event/py/execnet/testing/test_gateway.py Log: have channel.send() fail early on unmarshallable items Modified: py/branch/event/py/execnet/channel.py ============================================================================== --- py/branch/event/py/execnet/channel.py (original) +++ py/branch/event/py/execnet/channel.py Mon Aug 4 12:48:15 2008 @@ -1,6 +1,7 @@ import threading, weakref, sys import Queue from cPickle import Pickler, Unpickler +import marshal if 'Message' not in globals(): from py.__.execnet.message import Message @@ -171,6 +172,9 @@ pickler.memo = self._picklememo pickler.dump(item) item = f.getvalue() + else: + if not isinstance(item, str): + marshal.dumps(item) # to raise early data = Message.CHANNEL_DATA(self.id, item) self.gateway._send(data) Modified: py/branch/event/py/execnet/gateway.py ============================================================================== --- py/branch/event/py/execnet/gateway.py (original) +++ py/branch/event/py/execnet/gateway.py Mon Aug 4 12:48:15 2008 @@ -149,6 +149,8 @@ try: msg.writeto(self._io) except: + # XXX on the very local side we may not want + # this catching but rather propagate excinfo = self.exc_info() self._traceex(excinfo) msg.post_sent(self, excinfo) Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Mon Aug 4 12:48:15 2008 @@ -74,6 +74,17 @@ channel = self.fac.new() py.test.raises(IOError, channel.waitclose, timeout=0.01) + def test_channel_makefile_incompatmode(self): + channel = self.fac.new() + py.test2.raises(ValueError, 'channel.makefile("rw")') + + def test_channel_send_with_pickle_false(self): + channel = self.fac.new(pickle=False) + class A: + pass + py.test.raises(ValueError, "channel.send(A)") + py.test.raises(ValueError, "channel.send(A())") + class PopenGatewayTestSetup: def setup_class(cls): cls.gw = py.execnet.PopenGateway() @@ -377,10 +388,6 @@ s = f.readline() assert s == "45" - def test_channel_makefile_incompatmode(self): - channel = self.gw.newchannel() - py.test2.raises(ValueError, 'channel.makefile("rw")') - def test_pickling(self): channel = self.gw.remote_exec(""" l1 = channel.receive() From hpk at codespeak.net Mon Aug 4 12:56:48 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 12:56:48 +0200 (CEST) Subject: [py-svn] r56964 - in py/branch/event/py/execnet: . testing Message-ID: <20080804105648.CE2C0169E7D@codespeak.net> Author: hpk Date: Mon Aug 4 12:56:48 2008 New Revision: 56964 Modified: py/branch/event/py/execnet/channel.py py/branch/event/py/execnet/testing/test_gateway.py Log: also raise ValueError if a non-picklable item is sent over a pickle channel Modified: py/branch/event/py/execnet/channel.py ============================================================================== --- py/branch/event/py/execnet/channel.py (original) +++ py/branch/event/py/execnet/channel.py Mon Aug 4 12:56:48 2008 @@ -1,6 +1,6 @@ import threading, weakref, sys import Queue -from cPickle import Pickler, Unpickler +from cPickle import Pickler, Unpickler, PickleError import marshal if 'Message' not in globals(): @@ -170,7 +170,10 @@ f = StringIO() pickler = Pickler(f, protocol=-1) # use best protocol pickler.memo = self._picklememo - pickler.dump(item) + try: + pickler.dump(item) + except PickleError, e: + raise ValueError(str(e)) item = f.getvalue() else: if not isinstance(item, str): Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Mon Aug 4 12:56:48 2008 @@ -78,13 +78,13 @@ channel = self.fac.new() py.test2.raises(ValueError, 'channel.makefile("rw")') - def test_channel_send_with_pickle_false(self): + def test_channel_send_errors_nopickling(self): channel = self.fac.new(pickle=False) class A: pass - py.test.raises(ValueError, "channel.send(A)") py.test.raises(ValueError, "channel.send(A())") + class PopenGatewayTestSetup: def setup_class(cls): cls.gw = py.execnet.PopenGateway() @@ -388,6 +388,12 @@ s = f.readline() assert s == "45" + def test_channel_send_errors_pickling(self): + channel = self.gw.remote_exec("", _pickle=True) + channel.send(object()) # works here + class A: pass + py.test.raises(ValueError, "channel.send(A)") + def test_pickling(self): channel = self.gw.remote_exec(""" l1 = channel.receive() From hpk at codespeak.net Mon Aug 4 13:15:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 13:15:32 +0200 (CEST) Subject: [py-svn] r56965 - in py/branch/event/py/test2: rsession terminal Message-ID: <20080804111532.987EF169E45@codespeak.net> Author: hpk Date: Mon Aug 4 13:15:31 2008 New Revision: 56965 Modified: py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/terminal/remote.py Log: use config pickling for remote sessions Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Mon Aug 4 13:15:31 2008 @@ -50,33 +50,24 @@ # setting up slave code defaultconftestnames = ['dist_nicelevel'] -from py.__.test2.rsession import slave def install_slave(host, session): config = session.config - channel = host.gw.remote_exec(""" + channel = host.gw.remote_exec(_pickle=True, source=""" from py.__.test2.rsession import masterslave - masterslave.setup_at_slave_side(channel) + config = masterslave.receive_and_send_pickled_config(channel) + masterslave.setup_at_slave_side(channel, config) """) - configrepr = config._makerepr(defaultconftestnames) - topdir = host.gw_remotepath - if topdir is None: + remote_topdir = host.gw_remotepath + if remote_topdir is None: assert host.inplacelocal - topdir = config.topdir - channel.send(str(topdir)) - channel.send(configrepr) + remote_topdir = config.topdir + send_and_receive_pickled_config(channel, config, remote_topdir) return channel -def setup_at_slave_side(channel): +def setup_at_slave_side(channel, config): # our current dir is the topdir - import os, sys - basedir = channel.receive() - config_repr = channel.receive() - # setup defaults... - sys.path.insert(0, basedir) - import py - config = py.test2.config - assert not config._initialized - config._initdirect(basedir, config_repr) + # XXX what about neccessary PYTHONPATHs? + import os if hasattr(os, 'nice'): nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Mon Aug 4 13:15:31 2008 @@ -96,16 +96,20 @@ return py.execnet.PopenGateway(self.executable), topdir def run_remote_session(self, failures): + from py.__.test2.rsession import masterslave gw, topdir = self._initslavegateway() - channel = gw.remote_exec(""" + channel = gw.remote_exec(_pickle=True, source=""" + print "SLAVE: initializing ..." from py.__.test2.terminal.remote import slaverun_TerminalSession - slaverun_TerminalSession(channel) + from py.__.test2.rsession import masterslave + config = masterslave.receive_and_send_pickled_config(channel) + slaverun_TerminalSession(channel, config) """, stdout=self.out, stderr=self.out) try: - print "MASTER: initiated slave terminal session ->" - repr = self.config._makerepr(conftestnames=[]) - channel.send((str(topdir), repr, failures)) - print "MASTER: send start info, topdir=%s" % (topdir,) + print "MASTER: initiating slave terminal session ->" + masterslave.send_and_receive_pickled_config(channel, self.config, topdir) + channel.send(failures) + print "MASTER: send config, topdir=%s" % (topdir,) try: return channel.receive() except channel.RemoteError, e: @@ -117,23 +121,24 @@ finally: gw.exit() -def slaverun_TerminalSession(channel): +def slaverun_TerminalSession(channel, config): """ we run this on the other side. """ - print "SLAVE: initializing ..." - topdir, repr, failures = channel.receive() - print "SLAVE: received configuration, using topdir:", topdir - config = py.test2.config - config._initdirect(topdir, repr, failures) + print "SLAVE: received configuration, using topdir:", config.topdir config.option.session = None config.option.looponfailing = False config.option.usepdb = False config.option.executable = None + failures = channel.receive() if failures: config.option.verbose = True + config._coltrails = failures + print "SLAVE: initsession()" session = config.initsession() session.shouldclose = channel.isclosed print "SLAVE: starting session.main()" failures = session.main() - failures = [ev.trail for ev in failures] + failures = [] # XXX needs fixing + #XXX needs fixing + #failures = [ev.trail for ev in failures if hasattr(ev.trail)] channel.send(failures) From hpk at codespeak.net Mon Aug 4 13:38:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 13:38:26 +0200 (CEST) Subject: [py-svn] r56966 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080804113826.2A576169EFF@codespeak.net> Author: hpk Date: Mon Aug 4 13:38:25 2008 New Revision: 56966 Modified: py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: pickle instead of marshal events for dist sessions Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Mon Aug 4 13:38:25 2008 @@ -13,28 +13,24 @@ self.channel.setcallback(self._callback) self.pending = [] - def _callback(self, outcomestring): - if outcomestring is None: + def _callback(self, ev): + if ev is None: self.notify(repevent.HostDown(self.host)) return item = self.pending.pop() - ev = repevent.ItemTestReport.fromdumps(outcomestring) self.notify(ev) def send(self, item): try: - if item is None: - self.channel.send(None) - else: - self.channel.send(item._get_collector_trail()) - self.pending.insert(0, item) - self.notify(repevent.SendItem(self.host, item)) + self.channel.send(item) except IOError: + # XXX do proper rescheduling on io errors print "Sending error, channel IOError" print self.channel._getremoteerror() - # XXX: this should go as soon as we'll have proper detection - # of hanging nodes and such raise + if item is not None: + self.pending.insert(0, item) + self.notify(repevent.SendItem(self.host, item)) def send_and_receive_pickled_config(channel, config, remote_topdir): channel.send((config, remote_topdir)) @@ -80,12 +76,10 @@ def slave_main(receive, send, config): while 1: - itemspec = receive() - if itemspec is None: + item = receive() + if item is None: send(None) break - item = config._getcollector(itemspec) runner = item._getrunner() testrep = runner(item, item._setupstate, item._config._getcapture) - send(testrep.dumps()) - + send(testrep) Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Mon Aug 4 13:38:25 2008 @@ -75,10 +75,14 @@ def test_send_one(self): queue = self.makereportqueue() + item = self.getfunc("passed") self.node.send(self.getfunc("passed")) event = queue.get(timeout=2.0) assert event.passed assert not self.node.pending + assert event.trail == item._get_collector_trail() + #assert event.item == item + #assert event.item is not item def test_send_multiple(self): queue = self.makereportqueue() From hpk at codespeak.net Mon Aug 4 13:45:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 13:45:23 +0200 (CEST) Subject: [py-svn] r56967 - py/tag/py-event-boxing-with-forkedfunc Message-ID: <20080804114523.A5496169EFF@codespeak.net> Author: hpk Date: Mon Aug 4 13:45:23 2008 New Revision: 56967 Added: py/tag/py-event-boxing-with-forkedfunc/ - copied from r56966, py/branch/event/ Modified: py/tag/py-event-boxing-with-forkedfunc/NOTES_FOR_MERGE Log: snapshot current boxing support Modified: py/tag/py-event-boxing-with-forkedfunc/NOTES_FOR_MERGE ============================================================================== --- py/branch/event/NOTES_FOR_MERGE (original) +++ py/tag/py-event-boxing-with-forkedfunc/NOTES_FOR_MERGE Mon Aug 4 13:45:23 2008 @@ -1,5 +1,7 @@ remove misc/terminal_helper.py (now py.io.TerminalWriter) apigen executor / execution? +check on py/trunk/py/test/ changes: + e.g. doctests -- after merge -- for TODO remove _mypytest.py From hpk at codespeak.net Mon Aug 4 14:13:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 14:13:31 +0200 (CEST) Subject: [py-svn] r56968 - in py/branch/event/py/test2: . rsession/testing testing Message-ID: <20080804121331.6D1A11684EA@codespeak.net> Author: hpk Date: Mon Aug 4 14:13:29 2008 New Revision: 56968 Modified: py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/testing/test_masterslave.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_repevent.py py/branch/event/py/test2/testing/test_runner.py Log: make items and ItemTestReports serialized via pickle Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Mon Aug 4 14:13:29 2008 @@ -68,36 +68,14 @@ exec "%s = property(lambda self: self.outcome == %r)\n" %(_,_) failed = property(lambda self: self.outcome.startswith("failed")) - @classmethod - def fromdumps(cls, string): - d = marshal.loads(string) - assert isinstance(d, dict) - return cls(**d) - - def __init__(self, trail, outcome, repr_run, repr_path, outerr=None): - self.trail = trail + def __init__(self, item, outcome, repr_run, repr_path, outerr=None): + self.item = item assert outcome in validoutcomes self.outcome = outcome self.repr_run = repr_run self.repr_path = repr_path self.outerr = outerr - def dumps(self): - """ marshal all possible attr to a string. """ - # as this might run in some remote process - # where we might not see exceptions we try - # to make this code here robust. - # XXX just dump __dict__, ensure debuggability otherwise - d = {} - for name, value in self.__dict__.items(): - try: - marshal.dumps(value) - except ValueError: - pass - else: - d[name] = value - return marshal.dumps(d) - # ---------------------------------------------------------------------- # Distributed Testing Events Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Mon Aug 4 14:13:29 2008 @@ -80,7 +80,7 @@ event = queue.get(timeout=2.0) assert event.passed assert not self.node.pending - assert event.trail == item._get_collector_trail() + assert event.item == item #assert event.item == item #assert event.item is not item Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Mon Aug 4 14:13:29 2008 @@ -70,14 +70,13 @@ def makereport(runinfo): item = runinfo.item - trail = item._get_collector_trail() repr_path = item.repr_path() if runinfo.outcome not in ("failed_setup", "failed_teardown"): repr_run = item.repr_run(runinfo) else: repr_run = pypresent.python_repr_run(runinfo, title="failure during setup/teardown") - return repevent.ItemTestReport(trail, runinfo.outcome, repr_run, repr_path) + return repevent.ItemTestReport(item, runinfo.outcome, repr_run, repr_path) NORESULT = object() # @@ -111,6 +110,9 @@ pdb(runinfo) return makereport(runinfo) +from cPickle import Pickler, Unpickler +from cStringIO import StringIO + def forked_run_report(item, setupstate, getcapture, pdb=None): EXITSTATUS_TESTEXIT = 4 @@ -120,13 +122,22 @@ except (KeyboardInterrupt, Exit): os._exit(EXITSTATUS_TESTEXIT) testrep = makereport(runinfo) - return testrep.dumps() + f = StringIO() + p = Pickler(f, -1) + p.dump(item._config) + p.dump(testrep) + return f.getvalue() ff = py.io.ForkedFunc(runforked) result = ff.waitfinish() print vars(result) if result.retval is not None: - return repevent.ItemTestReport.fromdumps(result.retval) + f = StringIO(result.retval) + u = Unpickler(f) + config = u.load() + config._initafterpickle(item._config.topdir) + ev = u.load() + return ev else: if result.exitstatus == EXITSTATUS_TESTEXIT: itemrepr = item.repr_path() @@ -139,6 +150,5 @@ tw.sep("!", "CRASHED with signal=%d: %s" % (result.signal, repr_path)) repr_run = tw.stringio.getvalue() - trail = item._get_collector_trail() - testrep = repevent.ItemTestReport(trail, "failed_crashed", repr_run, repr_path) + testrep = repevent.ItemTestReport(item, "failed_crashed", repr_run, repr_path) return testrep Modified: py/branch/event/py/test2/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test2/testing/test_repevent.py Mon Aug 4 14:13:29 2008 @@ -3,15 +3,6 @@ import setupdata, suptest class TestItemTestReport(object): - def test_dumps_loads(self): - for example in "filetest.py", "test_threepass.py": - sorter = suptest.events_run_example(example) - reports = sorter.get(repevent.ItemTestReport) - ev1 = reports[0] - ev2 = repevent.ItemTestReport.fromdumps(ev1.dumps()) - assert ev1.repr_path == ev2.repr_path - assert ev1.repr_run == ev2.repr_run - assert ev1.outcome == ev2.outcome def test_failing(self): sorter = suptest.events_run_example("filetest.py") Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Mon Aug 4 14:13:29 2008 @@ -7,6 +7,12 @@ class MockItem: def __init__(self, func): self.func = func + self._config = py.test2.config._reparse([]) + + def __getstate__(self): + return (self._config, ) + def __setstate__(self, repr): + self._config, = repr def _get_collector_trail(self): return "MockItem.trail" From hpk at codespeak.net Mon Aug 4 14:16:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 14:16:36 +0200 (CEST) Subject: [py-svn] r56969 - py/tag/py-event-boxing-with-forkedfunc Message-ID: <20080804121636.91AA5169EEB@codespeak.net> Author: hpk Date: Mon Aug 4 14:16:36 2008 New Revision: 56969 Removed: py/tag/py-event-boxing-with-forkedfunc/ Log: snapshot not needed From hpk at codespeak.net Mon Aug 4 14:34:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 14:34:23 +0200 (CEST) Subject: [py-svn] r56970 - in py/branch/event/py/test2: . rsession testing Message-ID: <20080804123423.05EEE16854F@codespeak.net> Author: hpk Date: Mon Aug 4 14:34:22 2008 New Revision: 56970 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_config.py Log: * remove superflous initdirect function * disable acceptance test for skips for now Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 14:34:22 2008 @@ -63,17 +63,60 @@ return self._makerepr([]) def __setstate__(self, repr): self._repr = repr - def _initafterpickle(self, topdir): - self.__init__() - self._initdirect(topdir, self._repr) - del self._repr - def _initdirect(self, topdir, repr, coltrails=None): - assert not self._initialized + def _initafterpickle(self, topdir, coltrails=None): + self.__init__() self._initialized = True self.topdir = py.path.local(topdir) - self._mergerepr(repr) + self._mergerepr(self._repr) self._coltrails = coltrails + del self._repr + + def _makerepr(self, conftestnames, optnames=None): + """ return a marshallable representation + of conftest and cmdline options. + if optnames is None, all options + on self.option will be transferred. + """ + conftestdict = {} + for name in conftestnames: + value = self.getvalue(name) + checkmarshal(name, value) + conftestdict[name] = value + cmdlineopts = {} + if optnames is None: + optnames = dir(self.option) + for name in optnames: + if not name.startswith("_"): + value = getattr(self.option, name) + checkmarshal(name, value) + cmdlineopts[name] = value + l = [] + for path in self.args: + path = py.path.local(path) + l.append(path.relto(self.topdir)) + return l, conftestdict, cmdlineopts + + def _mergerepr(self, repr): + """ merge in the conftest and cmdline option values + found in the given representation (produced + by _makerepr above). + + The repr-contained conftest values are + stored on the default conftest module (last + priority) and the cmdline options on self.option. + """ + class override: + def __init__(self, d): + self.__dict__.update(d) + self.__file__ = "" + args, conftestdict, cmdlineopts = repr + self.args = [self.topdir.join(x) for x in args] + self._conftest.setinitial(self.args) + self._conftest._path2confmods[None].append(override(conftestdict)) + for name, val in cmdlineopts.items(): + setattr(self.option, name, val) + def getcolitems(self): """ return initial collectors. """ @@ -201,52 +244,6 @@ finally: config_per_process = py.test2.config = oldconfig - # XXX are makerepr and mergerepr really needed like this? - def _makerepr(self, conftestnames, optnames=None): - """ return a marshallable representation - of conftest and cmdline options. - if optnames is None, all options - on self.option will be transferred. - """ - conftestdict = {} - for name in conftestnames: - value = self.getvalue(name) - checkmarshal(name, value) - conftestdict[name] = value - cmdlineopts = {} - if optnames is None: - optnames = dir(self.option) - for name in optnames: - if not name.startswith("_"): - value = getattr(self.option, name) - checkmarshal(name, value) - cmdlineopts[name] = value - l = [] - for path in self.args: - path = py.path.local(path) - l.append(path.relto(self.topdir)) - return l, conftestdict, cmdlineopts - - def _mergerepr(self, repr): - """ merge in the conftest and cmdline option values - found in the given representation (produced - by _makerepr above). - - The repr-contained conftest values are - stored on the default conftest module (last - priority) and the cmdline options on self.option. - """ - class override: - def __init__(self, d): - self.__dict__.update(d) - self.__file__ = "" - args, conftestdict, cmdlineopts = repr - self.args = [self.topdir.join(x) for x in args] - self._conftest.setinitial(self.args) - self._conftest._path2confmods[None].append(override(conftestdict)) - for name, val in cmdlineopts.items(): - setattr(self.option, name, val) - def get_collector_trail(self, collector): """ provide a trail relative to the topdir, which can be used to reconstruct the Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Mon Aug 4 14:34:22 2008 @@ -67,8 +67,6 @@ if hasattr(os, 'nice'): nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) - if not config.option.nomagic: - py.magic.invoke(assertion=1) slave_main(channel.receive, channel.send, config) if not config.option.nomagic: py.magic.revoke(assertion=1) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Mon Aug 4 14:34:22 2008 @@ -74,6 +74,7 @@ return p def test_skipped_reasons(self): + py.test.skip("later") p1 = self.makepyfile(test_one=""" from conftest import doskip def setup_function(func): Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Mon Aug 4 14:34:22 2008 @@ -122,22 +122,23 @@ assert gettopdir([c, Z]) == tmp -def test_config_init_direct(): - tmp = py.test2.ensuretemp("_initdirect") +def test_config_initafterpickle_some(): + tmp = py.test2.ensuretemp("test_config_initafterpickle_some") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1 ; y=2") hello = tmp.ensure("test_hello.py") config = py.test2.config._reparse([hello]) - repr = config._makerepr(conftestnames=['x', 'y']) config2 = py.test2.config._reparse([tmp.dirpath()]) config2._initialized = False # we have to do that from tests - config2._initdirect(topdir=tmp.dirpath(), repr=repr) + config2._repr = config._makerepr([]) + config2._initafterpickle(topdir=tmp.dirpath()) for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): assert col1.fspath == col2.fspath - py.test2.raises(AssertionError, "config2._initdirect(None, None)") from py.__.test2.config import Config + config3 = Config() - config3._initdirect(topdir=tmp.dirpath(), repr=repr, + config3._repr = config._makerepr(conftestnames=['x', 'y']) + config3._initafterpickle(topdir=tmp.dirpath(), coltrails=[(tmp.basename, (hello.basename,))]) assert config3.getvalue('x') == 1 assert config3.getvalue('y') == 2 From hpk at codespeak.net Mon Aug 4 14:41:05 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 14:41:05 +0200 (CEST) Subject: [py-svn] r56971 - in py/branch/event/py/test2: . testing Message-ID: <20080804124105.C371A169E22@codespeak.net> Author: hpk Date: Mon Aug 4 14:41:05 2008 New Revision: 56971 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/testing/test_config.py Log: simplify pickling some more Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 14:41:05 2008 @@ -60,7 +60,7 @@ # upon getstate/setstate we take care to do everything # relative to our "topdir". def __getstate__(self): - return self._makerepr([]) + return self._makerepr() def __setstate__(self, repr): self._repr = repr @@ -72,51 +72,18 @@ self._coltrails = coltrails del self._repr - def _makerepr(self, conftestnames, optnames=None): - """ return a marshallable representation - of conftest and cmdline options. - if optnames is None, all options - on self.option will be transferred. - """ - conftestdict = {} - for name in conftestnames: - value = self.getvalue(name) - checkmarshal(name, value) - conftestdict[name] = value - cmdlineopts = {} - if optnames is None: - optnames = dir(self.option) - for name in optnames: - if not name.startswith("_"): - value = getattr(self.option, name) - checkmarshal(name, value) - cmdlineopts[name] = value + def _makerepr(self): l = [] for path in self.args: path = py.path.local(path) l.append(path.relto(self.topdir)) - return l, conftestdict, cmdlineopts + return l, self.option def _mergerepr(self, repr): - """ merge in the conftest and cmdline option values - found in the given representation (produced - by _makerepr above). - - The repr-contained conftest values are - stored on the default conftest module (last - priority) and the cmdline options on self.option. - """ - class override: - def __init__(self, d): - self.__dict__.update(d) - self.__file__ = "" - args, conftestdict, cmdlineopts = repr + args, cmdlineopts = repr self.args = [self.topdir.join(x) for x in args] self._conftest.setinitial(self.args) - self._conftest._path2confmods[None].append(override(conftestdict)) - for name, val in cmdlineopts.items(): - setattr(self.option, name, val) - + self.option = cmdlineopts def getcolitems(self): """ return initial collectors. """ Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Mon Aug 4 14:41:05 2008 @@ -130,19 +130,12 @@ config = py.test2.config._reparse([hello]) config2 = py.test2.config._reparse([tmp.dirpath()]) config2._initialized = False # we have to do that from tests - config2._repr = config._makerepr([]) + config2._repr = config._makerepr() config2._initafterpickle(topdir=tmp.dirpath()) for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): assert col1.fspath == col2.fspath from py.__.test2.config import Config - - config3 = Config() - config3._repr = config._makerepr(conftestnames=['x', 'y']) - config3._initafterpickle(topdir=tmp.dirpath(), - coltrails=[(tmp.basename, (hello.basename,))]) - assert config3.getvalue('x') == 1 - assert config3.getvalue('y') == 2 - cols = config.getcolitems() + cols = config2.getcolitems() assert len(cols) == 1 col = cols[0] assert col.name == 'test_hello.py' @@ -154,9 +147,9 @@ tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1") config = py.test2.config._reparse([tmp]) - repr = config._makerepr(conftestnames=['x']) + repr = config._makerepr() config.option.verbose = 42 - repr2 = config._makerepr(conftestnames=[], optnames=['verbose']) + repr2 = config._makerepr() config = py.test2.config._reparse([tmp.dirpath()]) py.test2.raises(KeyError, "config.getvalue('x')") config._mergerepr(repr) @@ -177,7 +170,7 @@ """)) config = py.test2.config._reparse([tmp, "-G", "11"]) assert config.option.gdest == 11 - repr = config._makerepr(conftestnames=[]) + repr = config._makerepr() config = py.test2.config._reparse([tmp.dirpath()]) py.test2.raises(AttributeError, "config.option.gdest") config._mergerepr(repr) From hpk at codespeak.net Mon Aug 4 15:06:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 15:06:19 +0200 (CEST) Subject: [py-svn] r56972 - in py/branch/event/py/test2: . testing Message-ID: <20080804130619.87D79169E29@codespeak.net> Author: hpk Date: Mon Aug 4 15:06:18 2008 New Revision: 56972 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/config.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_config.py Log: move logic for pickling of collection nodes away from config Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Mon Aug 4 15:06:18 2008 @@ -88,7 +88,7 @@ def __getstate__(self): config = self._config - return (config, config.get_collector_trail(self)) + return (config, self._totrail()) def __setstate__(self, (config, trail)): if not config._initialized: raise ValueError("incomplete unpickling of " @@ -192,10 +192,22 @@ def _getsortvalue(self): return self.name - def _get_collector_trail(self): - """ Shortcut - """ - return self._config.get_collector_trail(self) + def _totrail(self): + """ provide a trail relative to the topdir, + which can be used to reconstruct the + collector (possibly on a different host + starting from a different topdir). + """ + chain = self.listchain() + topdir = self._config.topdir + relpath = chain[0].fspath.relto(topdir) + if not relpath: + if chain[0].fspath == topdir: + relpath = "." + else: + raise ValueError("%r not relative to topdir %s" + %(chain[0], topdir)) + return relpath, tuple([x.name for x in chain[1:]]) def prunetraceback(self, traceback): return traceback Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 15:06:18 2008 @@ -91,7 +91,8 @@ return [self._getcollector(path) for path in (trails or self.args)] def _getcollector(self, path): - if isinstance(path, tuple): + if isinstance(path, tuple): # trail + # XXX should be moved to Node._fromtrail(config, path) relpath, names = path fspath = self.topdir.join(relpath) col = self._getcollector(fspath) @@ -211,22 +212,6 @@ finally: config_per_process = py.test2.config = oldconfig - def get_collector_trail(self, collector): - """ provide a trail relative to the topdir, - which can be used to reconstruct the - collector (possibly on a different host - starting from a different topdir). - """ - chain = collector.listchain() - relpath = chain[0].fspath.relto(self.topdir) - if not relpath: - if chain[0].fspath == self.topdir: - relpath = "." - else: - raise ValueError("%r not relative to topdir %s" - %(chain[0], self.topdir)) - return relpath, tuple([x.name for x in chain[1:]]) - def _getcapture(self, path=None): if self.option.nocapture: iocapture = "no" Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Mon Aug 4 15:06:18 2008 @@ -429,3 +429,35 @@ items, events = self._genitems(x) assert len(items) == 1 assert isinstance(items[0], DoctestText) + +class TestCollector: + def setup_method(self, method): + self.tmpdir = py.test.ensuretemp("%s_%s" % + (self.__class__.__name__, method.__name__)) + + def test_totrail_and_back(self): + a = self.tmpdir.ensure("a", dir=1) + self.tmpdir.ensure("a", "__init__.py") + x = self.tmpdir.ensure("a", "trail.py") + config = py.test2.config._reparse([x]) + col = config._getcollector(x) + trail = col._totrail() + assert len(trail) == 2 + assert trail[0] == a.relto(config.topdir) + assert trail[1] == ('trail.py',) + col2 = config._getcollector(trail) + assert col2.listnames() == col.listnames() + + def test_totrail_topdir_and_beyond(self): + config = py.test2.config._reparse([self.tmpdir]) + col = config._getcollector(config.topdir) + trail = col._totrail() + assert len(trail) == 2 + assert trail[0] == '.' + assert trail[1] == () + col2 = config._getcollector(trail) + assert col2.fspath == config.topdir + assert len(col2.listchain()) == 1 + col3 = config._getcollector(config.topdir.dirpath()) + py.test2.raises(ValueError, + "col3._totrail()") Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Mon Aug 4 15:06:18 2008 @@ -418,32 +418,6 @@ for col in col.listchain(): assert col._config is config - def test_get_collector_trail_and_back(self): - a = self.tmpdir.ensure("a", dir=1) - self.tmpdir.ensure("a", "__init__.py") - x = self.tmpdir.ensure("a", "trail.py") - config = py.test2.config._reparse([x]) - col = config._getcollector(x) - trail = config.get_collector_trail(col) - assert len(trail) == 2 - assert trail[0] == a.relto(config.topdir) - assert trail[1] == ('trail.py',) - col2 = config._getcollector(trail) - assert col2.listnames() == col.listnames() - - def test_get_collector_trail_topdir_and_beyond(self): - config = py.test2.config._reparse([self.tmpdir]) - col = config._getcollector(config.topdir) - trail = config.get_collector_trail(col) - assert len(trail) == 2 - assert trail[0] == '.' - assert trail[1] == () - col2 = config._getcollector(trail) - assert col2.fspath == config.topdir - assert len(col2.listchain()) == 1 - col3 = config._getcollector(config.topdir.dirpath()) - py.test2.raises(ValueError, - "config.get_collector_trail(col3)") def test_config_picklability(self): import cPickle From hpk at codespeak.net Mon Aug 4 15:22:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 15:22:12 +0200 (CEST) Subject: [py-svn] r56974 - in py/branch/event/py/test2: . rep/testing rsession/testing testing Message-ID: <20080804132212.4A12916854D@codespeak.net> Author: hpk Date: Mon Aug 4 15:22:11 2008 New Revision: 56974 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/config.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rsession/testing/basetest.py py/branch/event/py/test2/testing/setupdata.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_config.py Log: * introduce config.getfsnode * factor trail handling into totrail/fromtrail node methods Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Mon Aug 4 15:22:11 2008 @@ -93,7 +93,7 @@ if not config._initialized: raise ValueError("incomplete unpickling of " "config object, need call to _initafterpickle()?") - newnode = config._getcollector(trail) + newnode = self._fromtrail(trail, config) self.__dict__.update(newnode.__dict__) def __repr__(self): @@ -192,6 +192,9 @@ def _getsortvalue(self): return self.name + def prunetraceback(self, traceback): + return traceback + def _totrail(self): """ provide a trail relative to the topdir, which can be used to reconstruct the @@ -209,8 +212,13 @@ %(chain[0], topdir)) return relpath, tuple([x.name for x in chain[1:]]) - def prunetraceback(self, traceback): - return traceback + @staticmethod + def _fromtrail(trail, config): + relpath, names = trail + fspath = config.topdir.join(relpath) + col = config.getfsnode(fspath) + return col._getitembynames(names) + class Collector(Node): """ Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 15:22:11 2008 @@ -64,12 +64,11 @@ def __setstate__(self, repr): self._repr = repr - def _initafterpickle(self, topdir, coltrails=None): + def _initafterpickle(self, topdir): self.__init__() self._initialized = True self.topdir = py.path.local(topdir) self._mergerepr(self._repr) - self._coltrails = coltrails del self._repr def _makerepr(self): @@ -87,28 +86,19 @@ def getcolitems(self): """ return initial collectors. """ - trails = getattr(self, '_coltrails', None) - return [self._getcollector(path) for path in (trails or self.args)] + return [self.getfsnode(path) for path in self.args] - def _getcollector(self, path): - if isinstance(path, tuple): # trail - # XXX should be moved to Node._fromtrail(config, path) - relpath, names = path - fspath = self.topdir.join(relpath) - col = self._getcollector(fspath) - else: - path = py.path.local(path) - assert path.check(), "%s: path does not exist" %(path,) - col = self._getrootcollector(path) - names = path.relto(col.fspath).split(path.sep) - return col._getitembynames(names) - - def _getrootcollector(self, path): + def getfsnode(self, path): + path = py.path.local(path) + assert path.check(), "%s: path does not exist" %(path,) + # we want our possibly custom collection tree to start at pkgroot pkgpath = path.pypkgpath() if pkgpath is None: pkgpath = path.check(file=1) and path.dirpath() or path Dir = self._conftest.rget("Directory", pkgpath) - return Dir(pkgpath, config=self) + col = Dir(pkgpath, config=self) + names = path.relto(col.fspath).split(path.sep) + return col._getitembynames(names) def getvalue_pathlist(self, name, path=None): """ return a matching value, which needs to be sequence Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Mon Aug 4 15:22:11 2008 @@ -29,7 +29,7 @@ print "wrote InlineCollect example to", path config = py.test2.config._reparse([path] + configargs) self.session = config.initsession() - return config._getcollector(path) + return config.getfsnode(path) class TestCollectonly(InlineCollect): def test_collectonly_basic(self): Modified: py/branch/event/py/test2/rsession/testing/basetest.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/basetest.py (original) +++ py/branch/event/py/test2/rsession/testing/basetest.py Mon Aug 4 15:22:11 2008 @@ -17,7 +17,7 @@ def setup_class(cls): path = getexamplefile("funcexamples.py") cls.config = py.test2.config._reparse([path.dirpath()]) - cls.modulecol = cls.config._getcollector(path) + cls.modulecol = cls.config.getfsnode(path) def setup_method(self, method): self.session = self.config.initsession() Modified: py/branch/event/py/test2/testing/setupdata.py ============================================================================== --- py/branch/event/py/test2/testing/setupdata.py (original) +++ py/branch/event/py/test2/testing/setupdata.py Mon Aug 4 15:22:11 2008 @@ -23,7 +23,7 @@ def getexamplecollector(names, tmpdir=None): fn = getexamplefile(names[0], tmpdir=tmpdir) config = py.test2.config._reparse([fn.dirpath()]) - col = config._getcollector(fn) + col = config.getfsnode(fn) return col._getitembynames(names[1:]) namecontent = { Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Mon Aug 4 15:22:11 2008 @@ -440,24 +440,24 @@ self.tmpdir.ensure("a", "__init__.py") x = self.tmpdir.ensure("a", "trail.py") config = py.test2.config._reparse([x]) - col = config._getcollector(x) + col = config.getfsnode(x) trail = col._totrail() assert len(trail) == 2 assert trail[0] == a.relto(config.topdir) assert trail[1] == ('trail.py',) - col2 = config._getcollector(trail) + col2 = py.test2.collect.Collector._fromtrail(trail, config) assert col2.listnames() == col.listnames() def test_totrail_topdir_and_beyond(self): config = py.test2.config._reparse([self.tmpdir]) - col = config._getcollector(config.topdir) + col = config.getfsnode(config.topdir) trail = col._totrail() assert len(trail) == 2 assert trail[0] == '.' assert trail[1] == () - col2 = config._getcollector(trail) + col2 = py.test2.collect.Collector._fromtrail(trail, config) assert col2.fspath == config.topdir assert len(col2.listchain()) == 1 - col3 = config._getcollector(config.topdir.dirpath()) + col3 = config.getfsnode(config.topdir.dirpath()) py.test2.raises(ValueError, "col3._totrail()") Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Mon Aug 4 15:22:11 2008 @@ -388,7 +388,7 @@ def test__getcol_global_file(self): x = self.tmpdir.ensure("x.py") config = py.test2.config._reparse([x]) - col = config._getcollector(x) + col = config.getfsnode(x) assert isinstance(col, py.test2.collect.Module) assert col.name == 'x.py' assert col.parent.name == self.tmpdir.basename @@ -399,7 +399,7 @@ def test__getcol_global_dir(self): x = self.tmpdir.ensure("a", dir=1) config = py.test2.config._reparse([x]) - col = config._getcollector(x) + col = config.getfsnode(x) assert isinstance(col, py.test2.collect.Directory) print col.listchain() assert col.name == 'a' @@ -410,7 +410,7 @@ x = self.tmpdir.ensure("x.py") self.tmpdir.ensure("__init__.py") config = py.test2.config._reparse([x]) - col = config._getcollector(x) + col = config.getfsnode(x) assert isinstance(col, py.test2.collect.Module) assert col.name == 'x.py' assert col.parent.name == x.dirpath().basename @@ -435,7 +435,7 @@ def test_config_and_collector_pickling_missing_initafter(self): from cPickle import Pickler, Unpickler config = py.test2.config._reparse([self.tmpdir]) - col = config._getcollector(config.topdir) + col = config.getfsnode(config.topdir) io = py.std.cStringIO.StringIO() pickler = Pickler(io) pickler.dump(config) @@ -450,7 +450,7 @@ from cPickle import Pickler, Unpickler dir1 = self.tmpdir.ensure("somedir", dir=1) config = py.test2.config._reparse([self.tmpdir]) - col = config._getcollector(config.topdir) + col = config.getfsnode(config.topdir) col1 = col.join(dir1.basename) assert col1.parent is col io = py.std.cStringIO.StringIO() From hpk at codespeak.net Mon Aug 4 15:49:58 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 15:49:58 +0200 (CEST) Subject: [py-svn] r56975 - py/branch/event/py/test2 Message-ID: <20080804134958.622E6169E45@codespeak.net> Author: hpk Date: Mon Aug 4 15:49:55 2008 New Revision: 56975 Modified: py/branch/event/py/test2/config.py Log: remove unused things Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 15:49:55 2008 @@ -3,7 +3,6 @@ import py from conftesthandle import Conftest from py.__.test2.defaultconftest import adddefaultoptions -from py.__.test2.eventbus import EventBus optparse = py.compat.optparse @@ -25,8 +24,6 @@ def __repr__(self): return "" %(self.__dict__,) -topdir_for_unpickling = [] - class Config(object): """ central bus for dealing with configuration/initialization data. """ Option = optparse.Option From hpk at codespeak.net Mon Aug 4 17:26:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 17:26:24 +0200 (CEST) Subject: [py-svn] r56977 - in py/branch/event/py/test2: . rsession testing Message-ID: <20080804152624.126F6169F1C@codespeak.net> Author: hpk Date: Mon Aug 4 17:26:23 2008 New Revision: 56977 Modified: py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_collect.py Log: move assertion reinterp to Module Collector instead of session Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Mon Aug 4 17:26:23 2008 @@ -120,13 +120,16 @@ return self._obj def setup(self): + if not self._config.option.nomagic: + py.magic.invoke(assertion=1) if hasattr(self.obj, 'setup_module'): self.obj.setup_module(self.obj) def teardown(self): if hasattr(self.obj, 'teardown_module'): self.obj.teardown_module(self.obj) - + if not self._config.option.nomagic: + py.magic.revoke(assertion=1) class Class(PyCollectorMixin, Collector): Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Mon Aug 4 17:26:23 2008 @@ -45,7 +45,6 @@ return config # setting up slave code -defaultconftestnames = ['dist_nicelevel'] def install_slave(host, session): config = session.config channel = host.gw.remote_exec(_pickle=True, source=""" @@ -68,9 +67,6 @@ nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) slave_main(channel.receive, channel.send, config) - if not config.option.nomagic: - py.magic.revoke(assertion=1) - channel.close() def slave_main(receive, send, config): while 1: Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Mon Aug 4 17:26:23 2008 @@ -75,8 +75,6 @@ self.reporter = self.config.getreporter() self.reporter.activate(self.bus) self.bus.notify(repevent.SessionStart(self)) - if not self.config.option.nomagic: - py.magic.invoke(assertion=1) self._failurelist = [] self.bus.subscribe(self._processfailures) @@ -90,8 +88,6 @@ def sessionfinishes(self): """ teardown any resources after a test run. """ py.test2.collect.Item._setupstate.teardown_all() # this can raise exceptions! - if not self.config.option.nomagic: - py.magic.revoke(assertion=1) self.bus.notify(repevent.SessionFinish(self)) self.bus.unsubscribe(self._processfailures) self.reporter.deactivate(self.bus) @@ -106,8 +102,7 @@ if self.shouldstop: break if not self.config.option.collectonly: - testrep = self.runtest(item) - self.bus.notify(testrep) + self.runtest(item) finally: failures = self.sessionfinishes() return failures @@ -119,8 +114,9 @@ def runtest(self, item): runner = item._getrunner() pdb = self.config.option.usepdb and self.runpdb or None - return runner(item, + testrep = runner(item, setupstate=item._setupstate, getcapture=self.config._getcapture, pdb=pdb) + self.bus.notify(testrep) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Mon Aug 4 17:26:23 2008 @@ -24,21 +24,41 @@ out, err = p1.readlines(cr=0), p2.readlines(cr=0) return Result(ret, out, err) + def makepyfile(self, **kwargs): + assert len(kwargs) == 1 + name, value = kwargs.popitem() + p = py.path.local(name).new(ext=".py") + p.write(py.code.Source(value)) + return p + def setup_method(self, method): name = self.__class__.__name__ + "_" + method.__name__ self.old = modtmpdir.mkdir(name).chdir() def teardown_method(self, method): self.old.chdir() + + def test_assertion_magic(self): + p = self.makepyfile(test_one=""" + def test_this(): + x = 0 + assert x + """) + result = self.runpytest(p) + extra = assert_lines_contain_lines(result.outlines, [ + "> assert x", + "E assert 0", + ]) + assert result.ret == 1 + def test_collectonly_simple(self): - p = py.path.local("test_one.py") - p.write(py.code.Source(""" + p = self.makepyfile(test_one=""" def test_func1(): pass class TestClass: def test_method(self): pass - """)) + """) result = self.runpytest("--collectonly", p) assert not "".join(result.errlines) assert result.ret == 0 @@ -51,13 +71,12 @@ """).strip()) def test_nested_import_error(self): - p = py.path.local("test_one.py") - p.write(py.code.Source(""" + p = self.makepyfile(test_one=""" import import_fails def test_this(): assert import_fails.a == 1 - """)) - p2 = py.path.local("import_fails.py") + """) + p2 = p.dirpath("import_fails.py") p2.write("import does_not_work") result = self.runpytest(p) extra = assert_lines_contain_lines(result.outlines, [ @@ -66,13 +85,6 @@ ]) assert result.ret == 1 - def makepyfile(self, **kwargs): - assert len(kwargs) == 1 - name, value = kwargs.popitem() - p = py.path.local(name).new(ext=".py") - p.write(py.code.Source(value)) - return p - def test_skipped_reasons(self): py.test.skip("later") p1 = self.makepyfile(test_one=""" Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Mon Aug 4 17:26:23 2008 @@ -10,6 +10,9 @@ def __init__(self): self._conftest = Conftest() self._setupstate = SetupState() + class dummyoption: + nomagic = False + self.option = dummyoption def getvalue(self, name, fspath): return self._conftest.rget(name, fspath) @@ -32,6 +35,27 @@ res2 = col.listdir() assert res1 == res2 +def test_module_assertion_setup(): + path = setupdata.getexamplefile("filetest.py") + col = py.test2.collect.Module(path, config=dummyconfig) + from py.__.magic import assertion + l = [] + py.magic.patch(assertion, "invoke", lambda: l.append(None)) + try: + col.setup() + finally: + py.magic.revert(assertion, "invoke") + x = l.pop() + assert x is None + py.magic.patch(assertion, "revoke", lambda: l.append(None)) + try: + col.teardown() + finally: + py.magic.revert(assertion, "revoke") + x = l.pop() + assert x is None + + def test_failing_import_execfile(): dest = setupdata.getexamplefile('failingimport.py') col = py.test2.collect.Module(dest, config=dummyconfig) From hpk at codespeak.net Mon Aug 4 17:38:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 17:38:18 +0200 (CEST) Subject: [py-svn] r56978 - in py/branch/event/py/test2: . testing Message-ID: <20080804153818.2FD09169EDB@codespeak.net> Author: hpk Date: Mon Aug 4 17:38:17 2008 New Revision: 56978 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_config.py Log: remove another unused method from config Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Mon Aug 4 17:38:17 2008 @@ -81,10 +81,6 @@ self._conftest.setinitial(self.args) self.option = cmdlineopts - def getcolitems(self): - """ return initial collectors. """ - return [self.getfsnode(path) for path in self.args] - def getfsnode(self, path): path = py.path.local(path) assert path.check(), "%s: path does not exist" %(path,) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Mon Aug 4 17:38:17 2008 @@ -65,7 +65,7 @@ self.bus.notify(repevent.CollectionFinish(next, runinfo)) def collect(self): - colitems = self.config.getcolitems() + colitems = [self.config.getfsnode(arg) for arg in self.config.args] keyword = self.config.option.keyword for x in self.genitems(colitems, keyword): yield x @@ -119,4 +119,3 @@ getcapture=self.config._getcapture, pdb=pdb) self.bus.notify(testrep) - Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Mon Aug 4 17:38:17 2008 @@ -5,6 +5,7 @@ from py.__.test2.conftesthandle import Conftest from py.__.test2.collect import SetupState from py.__.test2.pycollect import DoctestText +from test_config import getcolitems class DummyConfig: def __init__(self): @@ -296,7 +297,7 @@ config = py.test2.config._reparse([tmp]) session = config.initsession() l = suptest.eventappender(session) - items = list(session.genitems(config.getcolitems())) + items = list(session.genitems(getcolitems(config))) return items, l def test_check_collect_hashes(self): Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Mon Aug 4 17:38:17 2008 @@ -4,6 +4,9 @@ from py.__.test2.config import gettopdir import suptest, setupdata +def getcolitems(config): + return [config.getfsnode(arg) for arg in config.args] + def test_tmpdir(): d1 = py.test2.ensuretemp('hello') d2 = py.test2.ensuretemp('hello') @@ -132,10 +135,9 @@ config2._initialized = False # we have to do that from tests config2._repr = config._makerepr() config2._initafterpickle(topdir=tmp.dirpath()) - for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): + for col1, col2 in zip(getcolitems(config), getcolitems(config2)): assert col1.fspath == col2.fspath - from py.__.test2.config import Config - cols = config2.getcolitems() + cols = getcolitems(config2) assert len(cols) == 1 col = cols[0] assert col.name == 'test_hello.py' @@ -358,7 +360,7 @@ def test_getcolitems_onedir(self): config = py.test2.config._reparse([self.tmpdir]) - colitems = config.getcolitems() + colitems = getcolitems(config) assert len(colitems) == 1 col = colitems[0] assert isinstance(col, py.test2.collect.Directory) @@ -367,7 +369,7 @@ def test_getcolitems_twodirs(self): config = py.test2.config._reparse([self.tmpdir, self.tmpdir]) - colitems = config.getcolitems() + colitems = getcolitems(config) assert len(colitems) == 2 col1, col2 = colitems assert col1.name == col2.name @@ -376,7 +378,7 @@ def test_getcolitems_curdir_and_subdir(self): a = self.tmpdir.ensure("a", dir=1) config = py.test2.config._reparse([self.tmpdir, a]) - colitems = config.getcolitems() + colitems = getcolitems(config) assert len(colitems) == 2 col1, col2 = colitems assert col1.name == self.tmpdir.basename From hpk at codespeak.net Mon Aug 4 21:16:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 21:16:23 +0200 (CEST) Subject: [py-svn] r56981 - in py/branch/event/py/code: . testing Message-ID: <20080804191623.C2E45169F21@codespeak.net> Author: hpk Date: Mon Aug 4 21:16:21 2008 New Revision: 56981 Added: py/branch/event/py/code/excinfo_repr.py - copied, changed from r56966, py/branch/event/py/code/tbpresent.py py/branch/event/py/code/testing/test_excinfo_repr.py - copied, changed from r56966, py/branch/event/py/code/testing/test_tbpresent.py Log: refactor formatting of exception info once more Copied: py/branch/event/py/code/excinfo_repr.py (from r56966, py/branch/event/py/code/tbpresent.py) ============================================================================== --- py/branch/event/py/code/tbpresent.py (original) +++ py/branch/event/py/code/excinfo_repr.py Mon Aug 4 21:16:21 2008 @@ -4,61 +4,60 @@ import py from py.__.code import safe_repr -class TBPresenter(object): +class TBReprWriter: + def __init__(self): + self.lines = [] + def sep(self, sep, line=""): + self.lines.append(LineSep(sep, line)) + def line(self, line): + self.lines.append(line) + def __str__(self): + return "\n".join([str(x) for x in self.lines]) + +class LineSep: + def __init__(self, sep, line): + self.sep = sep + self.line = line + def __str__(self): + sep = self.sep * 10 + return self.line.join([sep, sep]) + +class LocationRepr: + def __init__(self, path, lineno, message): + self.path = str(path) + self.lineno = lineno + self.message = message + def __str__(self): + # filename and lineno output for each entry, + # using an output format that most editors unterstand + msg = self.message + if msg.find("\n") != -1: + msg = repr(msg) + return ("%s:%s: %s" %(self.path, self.lineno, msg)) + +class FormattedExcinfo(object): """ presenting information about failing Functions and Generators. """ # for traceback entries flow_marker = ">" fail_marker = "E" - def __init__(self, out=None, showlocals=False, style="long"): + def __init__(self, showlocals=False, style="long"): + self.out = TBReprWriter() self.showlocals = showlocals self.style = style - if out is None: - self.out = py.io.TerminalWriter() - else: - self.out = out - - def repr_source(self, source, marker_location=-1): - """ This one represents piece of source with possible - marker at requested position - """ - if source is None: - self.out.line("???") - return - if isinstance(source, str): - source = source.split("\n") - if marker_location < 0: - marker_location += len(source) - if marker_location < 0: - marker_location = 0 - if marker_location >= len(source): - marker_location = len(source) - 1 - for i in range(len(source)): - if i == marker_location: - prefix = self.flow_marker + " " - else: - prefix = " " - self.out.line(prefix + source[i]) - def repr_failure_explanation(self, excinfo, source): + def _getindent(self, source): + # figure out indent try: s = str(source.getstatement(len(source)-1)) except KeyboardInterrupt: raise except: s = str(source[-1]) - indent = 4 + (len(s) - len(s.lstrip())) - self.repr_exconly(excinfo, indent=indent) + return 4 + (len(s) - len(s.lstrip())) - def repr_exconly(self, excinfo, indent): - indent = " " * indent - # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - self.out.line(self.fail_marker + indent[:-1] + lines.pop(0)) - for x in lines: - self.out.line(indent + x) - def getentrysource(self, entry): + def _getentrysource(self, entry): try: source = entry.getsource() except py.error.ENOENT: @@ -68,6 +67,36 @@ def _saferepr(self, obj): return safe_repr._repr(obj) + + def repr_source(self, source, line_index=-1, excinfo=None): + """ represent source with with marker at given index. """ + if source is None: + source = py.code.Source("???") + line_index = 0 + if line_index < 0: + line_index += len(source) + for i in range(len(source)): + if i == line_index: + prefix = self.flow_marker + " " + else: + prefix = " " + line = prefix + source[i] + self.out.line(line) + + if excinfo is not None: + indent = self._getindent(source) + self.repr_exconly(excinfo, indent=indent, markall=True) + + def repr_exconly(self, excinfo, indent=4, markall=False): + indent = " " * indent + # get the real exception information out + lines = excinfo.exconly(tryshort=True).split('\n') + failindent = self.fail_marker + indent[1:] + for line in lines: + self.out.line(failindent + line) + if not markall: + failindent = indent + def repr_locals(self, f_locals): if self.showlocals: self.out.sep('- ', 'locals') @@ -87,24 +116,18 @@ def repr_tb_entry(self, entry, excinfo=None): # excinfo is not None if this is the last tb entry - source = self.getentrysource(entry) - firstsourceline = entry.getfirstlinesource() - marker_location = entry.lineno - firstsourceline + source = self._getentrysource(entry) + line_index = entry.lineno - entry.getfirstlinesource() if self.style == "long": - self.repr_source(source, marker_location) - # filename and lineno output for each entry, - # using an output format that most editors unterstand - loc = "%s:%d:" %(entry.path, entry.lineno+1) - if excinfo: - self.repr_failure_explanation(excinfo, source) - loc += " %r" % excinfo.exconly() + self.repr_source(source, line_index, excinfo) self.out.line("") - self.out.line(loc) + message = excinfo and excinfo.exconly() or "" + self.out.line(LocationRepr(entry.path, entry.lineno+1, message)) self.repr_locals(entry.locals) else: if self.style == "short": - line = source[marker_location].lstrip() + line = source[line_index].lstrip() self.out.line(' File "%s", line %d, in %s' % ( entry.path.basename, entry.lineno+1, entry.name)) self.out.line(" " + line) Copied: py/branch/event/py/code/testing/test_excinfo_repr.py (from r56966, py/branch/event/py/code/testing/test_tbpresent.py) ============================================================================== --- py/branch/event/py/code/testing/test_tbpresent.py (original) +++ py/branch/event/py/code/testing/test_excinfo_repr.py Mon Aug 4 21:16:21 2008 @@ -1,17 +1,24 @@ import py import re -from py.__.code.tbpresent import TBPresenter +from py.__.code.excinfo_repr import FormattedExcinfo -class TestTracebackPresenter: +class TestFormattedExcinfo: def setup_class(cls): cls.clstmpdir = py.test2.ensuretemp(cls.__name__) def setup_method(self, method): self.tmpdir = self.clstmpdir.ensure(method.__name__, dir=1) + def importasmod(self, source): + source = py.code.Source(source) + modpath = self.tmpdir.join("mod.py") + self.tmpdir.ensure("__init__.py") + modpath.write(source) + return modpath.pyimport() + def getpresenter(self, **kwargs): - return TBPresenter(**kwargs) + return FormattedExcinfo(**kwargs) def test_repr_source(self): pr = self.getpresenter() @@ -21,13 +28,12 @@ """).strip() pr.flow_marker = "|" pr.repr_source(source, 0) - lines = pr.out.stringio.getvalue().split("\n") - assert len(lines) == 3 - assert lines[0].startswith("|") - assert lines[0].find("def f(x)") != -1 - assert lines[1].find("pass") != -1 + lines = pr.out.lines + assert len(lines) == 2 + assert lines[0] == "| def f(x):" + assert lines[1] == " pass" - def test_repr_failure_explanation(self): + def test_repr_source_excinfo(self): """ check if indentation is right """ def f(): def g(): @@ -41,56 +47,52 @@ pr = self.getpresenter() source = py.code.Source(f) e = f() - pr.repr_failure_explanation(e, source) - assert pr.out.stringio.getvalue().startswith("E ") + pr.repr_source(source, 1, e) + assert pr.out.lines[-1].startswith("E ZeroDivisionError:") def test_repr_local(self): p = self.getpresenter(showlocals=True) loc = locals() p.repr_locals(loc) - result = p.out.stringio.getvalue() + result = str(p.out) for key in loc.keys(): assert result.find(key) != -1 - def importasmod(self, source): - source = py.code.Source(source) - modpath = self.tmpdir.join("mod.py") - self.tmpdir.ensure("__init__.py") - modpath.write(source) - return modpath.pyimport() - def test_repr_tbentry(self): mod = self.importasmod(""" def func1(): - raise ValueError("hello") + raise ValueError("hello\\nworld") def entry(): func1() """) excinfo = py.test.raises(ValueError, mod.entry) p = self.getpresenter() p.repr_tb_entry(excinfo.traceback[-2]) - s = p.out.stringio.getvalue() - lines = s.split("\n") + lines = p.out.lines # test intermittent entries assert lines[0] == " def entry():" assert lines[1] == "> func1()" assert not lines[2] - assert lines[3] == "%s:5:" %(mod.__file__,) + loc = lines[3] + assert loc.path == mod.__file__ + assert loc.lineno == 5 + assert not loc.message # test last entry p = self.getpresenter() p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - lines = s.split("\n") - print s + lines = p.out.lines assert lines[0] == " def func1():" - assert lines[1] == '> raise ValueError("hello")' + assert lines[1] == '> raise ValueError("hello\\nworld")' assert lines[2] == "E ValueError: hello" - assert not lines[3] - assert lines[4] == "%s:3: 'ValueError: hello'" %(mod.__file__,) - + assert lines[3] == "E world" + loc = lines[5] + assert loc.path == mod.__file__ + assert loc.lineno == 3 + assert loc.message == "ValueError: hello\nworld" + assert str(loc) == "%s:3: 'ValueError: hello\\nworld'" %(mod.__file__,) def test_repr_tbentry_short(self): mod = self.importasmod(""" @@ -102,24 +104,18 @@ excinfo = py.test.raises(ValueError, mod.entry) p = self.getpresenter(style="short") p.repr_tb_entry(excinfo.traceback[-2]) - s = p.out.stringio.getvalue() - lines = s.split("\n") + lines = p.out.lines basename = py.path.local(mod.__file__).basename assert lines[0] == ' File "%s", line 5, in entry' % basename assert lines[1] == ' func1()' - assert not lines[2] # test last entry - print s p = self.getpresenter(style="short") p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - - lines = s.split("\n") + lines = p.out.lines assert lines[0] == ' File "%s", line 3, in func1' % basename assert lines[1] == ' raise ValueError("hello")' assert lines[2] == 'E ValueError: hello' - assert not lines[3] def test_repr_tbentry_no(self): mod = self.importasmod(""" @@ -131,15 +127,13 @@ excinfo = py.test.raises(ValueError, mod.entry) p = self.getpresenter(style="no") p.repr_tb_entry(excinfo.traceback[-2]) - s = p.out.stringio.getvalue() - assert not s + assert not p.out.lines + p = self.getpresenter(style="no") p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - lines = s.split("\n") + lines = p.out.lines assert lines[0] == 'E ValueError: hello' - def test_repr_tb(self): mod = self.importasmod(""" def f(x): @@ -180,7 +174,7 @@ p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) p.repr_sep = lambda sep: l.append(sep) p.repr_tb(excinfo) - s = p.out.stringio.getvalue() + s = str(p.out) print l #assert re.search(".*return rec1.*x\+1", s) #assert re.search(".*return rec2.*x-1.*", s) @@ -207,7 +201,5 @@ p = self.getpresenter() p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - print s - lines = s.split("\n") - assert lines[-4] == "E assert 1 == 2" + lines = p.out.lines + assert lines[-3] == "E assert 1 == 2" From hpk at codespeak.net Mon Aug 4 21:25:50 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 21:25:50 +0200 (CEST) Subject: [py-svn] r56982 - in py/branch/event/py/code: . testing Message-ID: <20080804192550.B12CD169F3E@codespeak.net> Author: hpk Date: Mon Aug 4 21:25:50 2008 New Revision: 56982 Added: py/branch/event/py/code/format_excinfo.py - copied unchanged from r56981, py/branch/event/py/code/excinfo_repr.py py/branch/event/py/code/testing/test_format_excinfo.py - copied, changed from r56981, py/branch/event/py/code/testing/test_excinfo_repr.py Removed: py/branch/event/py/code/excinfo_repr.py py/branch/event/py/code/testing/test_excinfo_repr.py Log: rename, streamline Deleted: /py/branch/event/py/code/excinfo_repr.py ============================================================================== --- /py/branch/event/py/code/excinfo_repr.py Mon Aug 4 21:25:50 2008 +++ (empty file) @@ -1,155 +0,0 @@ -""" - Present Python tracebacks in a nice way. -""" -import py -from py.__.code import safe_repr - -class TBReprWriter: - def __init__(self): - self.lines = [] - def sep(self, sep, line=""): - self.lines.append(LineSep(sep, line)) - def line(self, line): - self.lines.append(line) - def __str__(self): - return "\n".join([str(x) for x in self.lines]) - -class LineSep: - def __init__(self, sep, line): - self.sep = sep - self.line = line - def __str__(self): - sep = self.sep * 10 - return self.line.join([sep, sep]) - -class LocationRepr: - def __init__(self, path, lineno, message): - self.path = str(path) - self.lineno = lineno - self.message = message - def __str__(self): - # filename and lineno output for each entry, - # using an output format that most editors unterstand - msg = self.message - if msg.find("\n") != -1: - msg = repr(msg) - return ("%s:%s: %s" %(self.path, self.lineno, msg)) - -class FormattedExcinfo(object): - """ presenting information about failing Functions and Generators. """ - # for traceback entries - flow_marker = ">" - fail_marker = "E" - - def __init__(self, showlocals=False, style="long"): - self.out = TBReprWriter() - self.showlocals = showlocals - self.style = style - - def _getindent(self, source): - # figure out indent - try: - s = str(source.getstatement(len(source)-1)) - except KeyboardInterrupt: - raise - except: - s = str(source[-1]) - return 4 + (len(s) - len(s.lstrip())) - - - def _getentrysource(self, entry): - try: - source = entry.getsource() - except py.error.ENOENT: - source = "???" - return source.deindent() - - def _saferepr(self, obj): - return safe_repr._repr(obj) - - - def repr_source(self, source, line_index=-1, excinfo=None): - """ represent source with with marker at given index. """ - if source is None: - source = py.code.Source("???") - line_index = 0 - if line_index < 0: - line_index += len(source) - for i in range(len(source)): - if i == line_index: - prefix = self.flow_marker + " " - else: - prefix = " " - line = prefix + source[i] - self.out.line(line) - - if excinfo is not None: - indent = self._getindent(source) - self.repr_exconly(excinfo, indent=indent, markall=True) - - def repr_exconly(self, excinfo, indent=4, markall=False): - indent = " " * indent - # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - failindent = self.fail_marker + indent[1:] - for line in lines: - self.out.line(failindent + line) - if not markall: - failindent = indent - - def repr_locals(self, f_locals): - if self.showlocals: - self.out.sep('- ', 'locals') - for name, value in f_locals.items(): - if name == '__builtins__': - self.out.line("__builtins__ = ") - else: - # This formatting could all be handled by the _repr() function, which is - # only repr.Repr in disguise, so is very configurable. - str_repr = safe_repr._repr(value) - if len(str_repr) < 70 or not isinstance(value, - (list, tuple, dict)): - self.out.line("%-10s = %s" %(name, str_repr)) - else: - self.out.line("%-10s =\\" % (name,)) - py.std.pprint.pprint(value, stream=self.out) - - def repr_tb_entry(self, entry, excinfo=None): - # excinfo is not None if this is the last tb entry - source = self._getentrysource(entry) - line_index = entry.lineno - entry.getfirstlinesource() - - if self.style == "long": - self.repr_source(source, line_index, excinfo) - self.out.line("") - message = excinfo and excinfo.exconly() or "" - self.out.line(LocationRepr(entry.path, entry.lineno+1, message)) - self.repr_locals(entry.locals) - else: - if self.style == "short": - line = source[line_index].lstrip() - self.out.line(' File "%s", line %d, in %s' % ( - entry.path.basename, entry.lineno+1, entry.name)) - self.out.line(" " + line) - if excinfo: - self.repr_exconly(excinfo, indent=4) - - def repr_sep(self, sep): - if self.style == "long": - self.out.sep(sep) - - def repr_tb(self, excinfo): - traceback = excinfo.traceback - recursionindex = traceback.recursionindex() - last = traceback[-1] - for index, entry in py.builtin.enumerate(traceback): - if last != entry: - self.repr_tb_entry(entry) - self.repr_sep("_ ") - else: - self.repr_tb_entry(entry, excinfo) - self.repr_sep("_") - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break Deleted: /py/branch/event/py/code/testing/test_excinfo_repr.py ============================================================================== --- /py/branch/event/py/code/testing/test_excinfo_repr.py Mon Aug 4 21:25:50 2008 +++ (empty file) @@ -1,205 +0,0 @@ - -import py -import re -from py.__.code.excinfo_repr import FormattedExcinfo - -class TestFormattedExcinfo: - def setup_class(cls): - cls.clstmpdir = py.test2.ensuretemp(cls.__name__) - - def setup_method(self, method): - self.tmpdir = self.clstmpdir.ensure(method.__name__, dir=1) - - def importasmod(self, source): - source = py.code.Source(source) - modpath = self.tmpdir.join("mod.py") - self.tmpdir.ensure("__init__.py") - modpath.write(source) - return modpath.pyimport() - - def getpresenter(self, **kwargs): - return FormattedExcinfo(**kwargs) - - def test_repr_source(self): - pr = self.getpresenter() - source = py.code.Source(""" - def f(x): - pass - """).strip() - pr.flow_marker = "|" - pr.repr_source(source, 0) - lines = pr.out.lines - assert len(lines) == 2 - assert lines[0] == "| def f(x):" - assert lines[1] == " pass" - - def test_repr_source_excinfo(self): - """ check if indentation is right """ - def f(): - def g(): - 1/0 - try: - g() - except: - e = py.code.ExceptionInfo() - return e - - pr = self.getpresenter() - source = py.code.Source(f) - e = f() - pr.repr_source(source, 1, e) - assert pr.out.lines[-1].startswith("E ZeroDivisionError:") - - def test_repr_local(self): - p = self.getpresenter(showlocals=True) - loc = locals() - p.repr_locals(loc) - result = str(p.out) - for key in loc.keys(): - assert result.find(key) != -1 - - def test_repr_tbentry(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello\\nworld") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-2]) - lines = p.out.lines - - # test intermittent entries - - assert lines[0] == " def entry():" - assert lines[1] == "> func1()" - assert not lines[2] - loc = lines[3] - assert loc.path == mod.__file__ - assert loc.lineno == 5 - assert not loc.message - - # test last entry - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[0] == " def func1():" - assert lines[1] == '> raise ValueError("hello\\nworld")' - assert lines[2] == "E ValueError: hello" - assert lines[3] == "E world" - loc = lines[5] - assert loc.path == mod.__file__ - assert loc.lineno == 3 - assert loc.message == "ValueError: hello\nworld" - assert str(loc) == "%s:3: 'ValueError: hello\\nworld'" %(mod.__file__,) - - def test_repr_tbentry_short(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter(style="short") - p.repr_tb_entry(excinfo.traceback[-2]) - lines = p.out.lines - basename = py.path.local(mod.__file__).basename - assert lines[0] == ' File "%s", line 5, in entry' % basename - assert lines[1] == ' func1()' - - # test last entry - p = self.getpresenter(style="short") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[0] == ' File "%s", line 3, in func1' % basename - assert lines[1] == ' raise ValueError("hello")' - assert lines[2] == 'E ValueError: hello' - - def test_repr_tbentry_no(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter(style="no") - p.repr_tb_entry(excinfo.traceback[-2]) - assert not p.out.lines - - p = self.getpresenter(style="no") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[0] == 'E ValueError: hello' - - def test_repr_tb(self): - mod = self.importasmod(""" - def f(x): - raise ValueError(x) - def entry(): - f(0) - """) - p = self.getpresenter(style="short") - excinfo = py.test.raises(ValueError, mod.entry) - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - s = p.repr_tb(excinfo) - print l - assert l[-1] == "_" - entry, excinfo2 = l[-2] - assert excinfo == excinfo2 - assert entry == excinfo.traceback[-1] - assert l[-3] == "_ " - entry, excinfo2 = l[-4] - assert excinfo2 is None - assert entry == excinfo.traceback[-2] - - def test_repr_tb_recursion(self): - mod = self.importasmod(""" - def rec2(x): - return rec1(x+1) - def rec1(x): - return rec2(x-1) - def entry(): - rec1(42) - """) - excinfo = py.test.raises(RuntimeError, mod.entry) - - for style in ("short", "long"): - p = self.getpresenter(style="short") - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - p.repr_tb(excinfo) - s = str(p.out) - print l - #assert re.search(".*return rec1.*x\+1", s) - #assert re.search(".*return rec2.*x-1.*", s) - assert len(l) < 20 - assert re.search("Recursion detected", s) - assert l[-1] == "_ " - assert l[-2][0].lineno == 4 - - def test_tb_entry_AssertionError(self): - # probably this test is a bit redundant - # as py/magic/testing/test_assertion.py - # already tests correctness of - # assertion-reinterpretation logic - mod = self.importasmod(""" - def somefunc(): - x = 1 - assert x == 2 - """) - py.magic.invoke(assertion=True) - try: - excinfo = py.test.raises(AssertionError, mod.somefunc) - finally: - py.magic.revoke(assertion=True) - - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[-3] == "E assert 1 == 2" Copied: py/branch/event/py/code/testing/test_format_excinfo.py (from r56981, py/branch/event/py/code/testing/test_excinfo_repr.py) ============================================================================== --- py/branch/event/py/code/testing/test_excinfo_repr.py (original) +++ py/branch/event/py/code/testing/test_format_excinfo.py Mon Aug 4 21:25:50 2008 @@ -1,14 +1,13 @@ import py import re -from py.__.code.excinfo_repr import FormattedExcinfo +from py.__.code.format_excinfo import FormattedExcinfo class TestFormattedExcinfo: - def setup_class(cls): - cls.clstmpdir = py.test2.ensuretemp(cls.__name__) def setup_method(self, method): - self.tmpdir = self.clstmpdir.ensure(method.__name__, dir=1) + self.tmpdir = py.test2.ensuretemp("%s_%s" %( + self.__class__.__name__, method.__name__)) def importasmod(self, source): source = py.code.Source(source) From hpk at codespeak.net Mon Aug 4 23:10:57 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 23:10:57 +0200 (CEST) Subject: [py-svn] r56984 - in py/branch/event/py/code: . testing Message-ID: <20080804211057.DBE6F169EC9@codespeak.net> Author: hpk Date: Mon Aug 4 23:10:55 2008 New Revision: 56984 Removed: py/branch/event/py/code/format_excinfo.py py/branch/event/py/code/testing/test_format_excinfo.py Modified: py/branch/event/py/code/code.py py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/code/traceback2.py Log: merge traceback formatting into excinfo.py and add a format() method that returns a nicely formatted traceback. Modified: py/branch/event/py/code/code.py ============================================================================== --- py/branch/event/py/code/code.py (original) +++ py/branch/event/py/code/code.py Mon Aug 4 23:10:55 2008 @@ -62,7 +62,11 @@ try: return self.raw.co_filename.__path__ except AttributeError: - return py.path.local(self.raw.co_filename) + p = py.path.local(self.raw.co_filename) + if not p.check() and self.raw.co_filename == "": + p = "" + return p + path = property(path, None, None, "path of this code object") def fullsource(self): Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Mon Aug 4 23:10:55 2008 @@ -1,6 +1,11 @@ -from __future__ import generators -import sys +""" + +Exception Info representation + formatting + +""" import py +from py.__.code import safe_repr +from sys import exc_info class ExceptionInfo(object): """ wraps sys.exc_info() objects and offers @@ -11,7 +16,7 @@ # NB. all attributes are private! Subclasses or other # ExceptionInfo-like classes may have different attributes. if tup is None: - tup = sys.exc_info() + tup = exc_info() if exprinfo is None and isinstance(tup[1], py.magic.AssertionError): exprinfo = tup[1].msg if exprinfo and exprinfo.startswith('assert '): @@ -42,7 +47,172 @@ """ return True if the exception is an instance of exc """ return isinstance(self.value, exc) + def _formatrepr(self, **kwargs): + formatter = FormattedExcinfo(**kwargs) + formatter.repr_tb(self) + return formatter + + def format(self, **kwargs): + repr = self._formatrepr(**kwargs) + tw = py.io.TerminalWriter() + repr.writeterminal(tw) + return tw.stringio.getvalue() + + def __str__(self): + entry = self.traceback[-1] + loc = LocationRepr(entry.path, entry.lineno + 1, self.exconly()) + return str(loc) + +class FormattedExcinfo(object): + """ presenting information about failing Functions and Generators. """ + # for traceback entries + flow_marker = ">" + fail_marker = "E" + + def __init__(self, showlocals=False, style="long"): + self.lines = [] + self.showlocals = showlocals + self.style = style + + def writeterminal(self, tw): + for line in self.lines: + if isinstance(line, LineSep): + tw.sep(line.sep, line.line) + else: + tw.line(str(line)) + + def _getindent(self, source): + # figure out indent + try: + s = str(source.getstatement(len(source)-1)) + except KeyboardInterrupt: + raise + except: + s = str(source[-1]) + return 4 + (len(s) - len(s.lstrip())) + + + def _getentrysource(self, entry): + try: + source = entry.getsource() + except py.error.ENOENT: + source = "???" + return source.deindent() + + def _saferepr(self, obj): + return safe_repr._repr(obj) + + + def repr_source(self, source, line_index=-1, excinfo=None): + """ represent source with with marker at given index. """ + if source is None: + source = py.code.Source("???") + line_index = 0 + if line_index < 0: + line_index += len(source) + for i in range(len(source)): + if i == line_index: + prefix = self.flow_marker + " " + else: + prefix = " " + line = prefix + source[i] + self._line(line) + + if excinfo is not None: + indent = self._getindent(source) + self.repr_exconly(excinfo, indent=indent, markall=True) + + def repr_exconly(self, excinfo, indent=4, markall=False): + indent = " " * indent + # get the real exception information out + lines = excinfo.exconly(tryshort=True).split('\n') + failindent = self.fail_marker + indent[1:] + for line in lines: + self._line(failindent + line) + if not markall: + failindent = indent + + def repr_locals(self, f_locals): + if self.showlocals: + self._sepline('- ', 'locals') + for name, value in f_locals.items(): + if name == '__builtins__': + self._line("__builtins__ = ") + else: + # This formatting could all be handled by the _repr() function, which is + # only repr.Repr in disguise, so is very configurable. + str_repr = safe_repr._repr(value) + if len(str_repr) < 70 or not isinstance(value, + (list, tuple, dict)): + self._line("%-10s = %s" %(name, str_repr)) + else: + self._line("%-10s =\\" % (name,)) + py.std.pprint.pprint(value, stream=self.excinfowriter) + + def repr_tb_entry(self, entry, excinfo=None): + # excinfo is not None if this is the last tb entry + source = self._getentrysource(entry) + line_index = entry.lineno - entry.getfirstlinesource() + + if self.style == "long": + self.repr_source(source, line_index, excinfo) + self._line("") + message = excinfo and excinfo.exconly() or "" + self._line(LocationRepr(entry.path, entry.lineno+1, message)) + self.repr_locals(entry.locals) + else: + if self.style == "short": + line = source[line_index].lstrip() + self._line(' File "%s", line %d, in %s' % ( + entry.path.basename, entry.lineno+1, entry.name)) + self._line(" " + line) + if excinfo: + self.repr_exconly(excinfo, indent=4) + + def repr_sep(self, sep): + if self.style == "long": + self._sepline(sep) + + def repr_tb(self, excinfo): + traceback = excinfo.traceback + recursionindex = traceback.recursionindex() + last = traceback[-1] + for index, entry in py.builtin.enumerate(traceback): + if last != entry: + self.repr_tb_entry(entry) + self.repr_sep("_ ") + else: + self.repr_tb_entry(entry, excinfo) + self.repr_sep("_") + if index == recursionindex: + self._line("Recursion detected (same locals & position)") + self._sepline("!") + break + + def _sepline(self, sep, line=""): + self.lines.append(LineSep(sep, line)) + def _line(self, line): + self.lines.append(line) def __str__(self): - # XXX wrong str - return self.exconly() + return "\n".join([str(x) for x in self.lines]) +class LineSep: + def __init__(self, sep, line): + self.sep = sep + self.line = line + def __str__(self): + sep = self.sep * 10 + return self.line.join([sep, sep]) + +class LocationRepr: + def __init__(self, path, lineno, message): + self.path = str(path) + self.lineno = lineno + self.message = message + def __str__(self): + # filename and lineno output for each entry, + # using an output format that most editors unterstand + msg = self.message + if msg.find("\n") != -1: + msg = repr(msg) + return ("%s:%s: %s" %(self.path, self.lineno, msg)) Deleted: /py/branch/event/py/code/format_excinfo.py ============================================================================== --- /py/branch/event/py/code/format_excinfo.py Mon Aug 4 23:10:55 2008 +++ (empty file) @@ -1,155 +0,0 @@ -""" - Present Python tracebacks in a nice way. -""" -import py -from py.__.code import safe_repr - -class TBReprWriter: - def __init__(self): - self.lines = [] - def sep(self, sep, line=""): - self.lines.append(LineSep(sep, line)) - def line(self, line): - self.lines.append(line) - def __str__(self): - return "\n".join([str(x) for x in self.lines]) - -class LineSep: - def __init__(self, sep, line): - self.sep = sep - self.line = line - def __str__(self): - sep = self.sep * 10 - return self.line.join([sep, sep]) - -class LocationRepr: - def __init__(self, path, lineno, message): - self.path = str(path) - self.lineno = lineno - self.message = message - def __str__(self): - # filename and lineno output for each entry, - # using an output format that most editors unterstand - msg = self.message - if msg.find("\n") != -1: - msg = repr(msg) - return ("%s:%s: %s" %(self.path, self.lineno, msg)) - -class FormattedExcinfo(object): - """ presenting information about failing Functions and Generators. """ - # for traceback entries - flow_marker = ">" - fail_marker = "E" - - def __init__(self, showlocals=False, style="long"): - self.out = TBReprWriter() - self.showlocals = showlocals - self.style = style - - def _getindent(self, source): - # figure out indent - try: - s = str(source.getstatement(len(source)-1)) - except KeyboardInterrupt: - raise - except: - s = str(source[-1]) - return 4 + (len(s) - len(s.lstrip())) - - - def _getentrysource(self, entry): - try: - source = entry.getsource() - except py.error.ENOENT: - source = "???" - return source.deindent() - - def _saferepr(self, obj): - return safe_repr._repr(obj) - - - def repr_source(self, source, line_index=-1, excinfo=None): - """ represent source with with marker at given index. """ - if source is None: - source = py.code.Source("???") - line_index = 0 - if line_index < 0: - line_index += len(source) - for i in range(len(source)): - if i == line_index: - prefix = self.flow_marker + " " - else: - prefix = " " - line = prefix + source[i] - self.out.line(line) - - if excinfo is not None: - indent = self._getindent(source) - self.repr_exconly(excinfo, indent=indent, markall=True) - - def repr_exconly(self, excinfo, indent=4, markall=False): - indent = " " * indent - # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - failindent = self.fail_marker + indent[1:] - for line in lines: - self.out.line(failindent + line) - if not markall: - failindent = indent - - def repr_locals(self, f_locals): - if self.showlocals: - self.out.sep('- ', 'locals') - for name, value in f_locals.items(): - if name == '__builtins__': - self.out.line("__builtins__ = ") - else: - # This formatting could all be handled by the _repr() function, which is - # only repr.Repr in disguise, so is very configurable. - str_repr = safe_repr._repr(value) - if len(str_repr) < 70 or not isinstance(value, - (list, tuple, dict)): - self.out.line("%-10s = %s" %(name, str_repr)) - else: - self.out.line("%-10s =\\" % (name,)) - py.std.pprint.pprint(value, stream=self.out) - - def repr_tb_entry(self, entry, excinfo=None): - # excinfo is not None if this is the last tb entry - source = self._getentrysource(entry) - line_index = entry.lineno - entry.getfirstlinesource() - - if self.style == "long": - self.repr_source(source, line_index, excinfo) - self.out.line("") - message = excinfo and excinfo.exconly() or "" - self.out.line(LocationRepr(entry.path, entry.lineno+1, message)) - self.repr_locals(entry.locals) - else: - if self.style == "short": - line = source[line_index].lstrip() - self.out.line(' File "%s", line %d, in %s' % ( - entry.path.basename, entry.lineno+1, entry.name)) - self.out.line(" " + line) - if excinfo: - self.repr_exconly(excinfo, indent=4) - - def repr_sep(self, sep): - if self.style == "long": - self.out.sep(sep) - - def repr_tb(self, excinfo): - traceback = excinfo.traceback - recursionindex = traceback.recursionindex() - last = traceback[-1] - for index, entry in py.builtin.enumerate(traceback): - if last != entry: - self.repr_tb_entry(entry) - self.repr_sep("_ ") - else: - self.repr_tb_entry(entry, excinfo) - self.repr_sep("_") - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Mon Aug 4 23:10:55 2008 @@ -1,5 +1,5 @@ import py -mypath = py.magic.autopath() +from py.__.code.excinfo import FormattedExcinfo def test_excinfo_simple(): try: @@ -184,18 +184,18 @@ msg = tbentry.reinterpret() assert msg.startswith("TypeError: ('hello' + 5)") -#def test_excinfo_getentries_type_error(): -# excinfo = py.test.raises(ValueError, h) -# entries = excinfo.getentries( -# lambda x: x.frame.code.name != 'raises', -# lambda x: x.frame.code.name != 'f') -# names = [x.frame.code.name for x in entries] -# assert names == ['h','g'] - def test_excinfo_exconly(): excinfo = py.test.raises(ValueError, h) assert excinfo.exconly().startswith('ValueError') +def test_excinfo_str(): + excinfo = py.test.raises(ValueError, h) + s = str(excinfo) + print s + assert s.startswith(__file__[:-1]) # pyc file + assert s.endswith("ValueError") + assert len(s.split(":")) == 3 + def test_excinfo_errisinstance(): excinfo = py.test.raises(ValueError, h) assert excinfo.errisinstance(ValueError) @@ -206,3 +206,221 @@ except ValueError: excinfo = py.code.ExceptionInfo() s = str(excinfo.traceback[-1]) + assert s == " File '':1 in \n ???\n" + +class TestFormattedExcinfo: + def setup_method(self, method): + self.tmpdir = py.test2.ensuretemp("%s_%s" %( + self.__class__.__name__, method.__name__)) + + def importasmod(self, source): + source = py.code.Source(source) + modpath = self.tmpdir.join("mod.py") + self.tmpdir.ensure("__init__.py") + modpath.write(source) + return modpath.pyimport() + + def test_repr_source(self): + pr = FormattedExcinfo() + source = py.code.Source(""" + def f(x): + pass + """).strip() + pr.flow_marker = "|" + pr.repr_source(source, 0) + lines = pr.lines + assert len(lines) == 2 + assert lines[0] == "| def f(x):" + assert lines[1] == " pass" + + def test_repr_source_excinfo(self): + """ check if indentation is right """ + def f(): + def g(): + 1/0 + try: + g() + except: + e = py.code.ExceptionInfo() + return e + + pr = FormattedExcinfo() + source = py.code.Source(f) + e = f() + pr.repr_source(source, 1, e) + assert pr.lines[-1].startswith("E ZeroDivisionError:") + + def test_repr_local(self): + p = FormattedExcinfo(showlocals=True) + loc = locals() + p.repr_locals(loc) + result = str(p) + for key in loc.keys(): + assert result.find(key) != -1 + + def test_repr_tbentry(self): + mod = self.importasmod(""" + def func1(): + raise ValueError("hello\\nworld") + def entry(): + func1() + """) + excinfo = py.test.raises(ValueError, mod.entry) + p = FormattedExcinfo() + p.repr_tb_entry(excinfo.traceback[-2]) + lines = p.lines + + # test intermittent entries + + assert lines[0] == " def entry():" + assert lines[1] == "> func1()" + assert not lines[2] + loc = lines[3] + assert loc.path == mod.__file__ + assert loc.lineno == 5 + assert not loc.message + + # test last entry + p = FormattedExcinfo() + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + lines = p.lines + assert lines[0] == " def func1():" + assert lines[1] == '> raise ValueError("hello\\nworld")' + assert lines[2] == "E ValueError: hello" + assert lines[3] == "E world" + loc = lines[5] + assert loc.path == mod.__file__ + assert loc.lineno == 3 + assert loc.message == "ValueError: hello\nworld" + assert str(loc) == "%s:3: 'ValueError: hello\\nworld'" %(mod.__file__,) + + def test_repr_tbentry_short(self): + mod = self.importasmod(""" + def func1(): + raise ValueError("hello") + def entry(): + func1() + """) + excinfo = py.test.raises(ValueError, mod.entry) + p = FormattedExcinfo(style="short") + p.repr_tb_entry(excinfo.traceback[-2]) + lines = p.lines + basename = py.path.local(mod.__file__).basename + assert lines[0] == ' File "%s", line 5, in entry' % basename + assert lines[1] == ' func1()' + + # test last entry + p = FormattedExcinfo(style="short") + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + lines = p.lines + assert lines[0] == ' File "%s", line 3, in func1' % basename + assert lines[1] == ' raise ValueError("hello")' + assert lines[2] == 'E ValueError: hello' + + def test_repr_tbentry_no(self): + mod = self.importasmod(""" + def func1(): + raise ValueError("hello") + def entry(): + func1() + """) + excinfo = py.test.raises(ValueError, mod.entry) + p = FormattedExcinfo(style="no") + p.repr_tb_entry(excinfo.traceback[-2]) + assert not p.lines + + p = FormattedExcinfo(style="no") + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + lines = p.lines + assert lines[0] == 'E ValueError: hello' + + def test_format(self): + mod = self.importasmod(""" + def f(x): + raise ValueError(x) + def entry(): + f(0) + """) + p = FormattedExcinfo(style="short") + excinfo = py.test.raises(ValueError, mod.entry) + l = [] + p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) + p.repr_sep = lambda sep: l.append(sep) + s = p.repr_tb(excinfo) + print l + assert l[-1] == "_" + entry, excinfo2 = l[-2] + assert excinfo == excinfo2 + assert entry == excinfo.traceback[-1] + assert l[-3] == "_ " + entry, excinfo2 = l[-4] + assert excinfo2 is None + assert entry == excinfo.traceback[-2] + + def test_repr_tb_recursion(self): + mod = self.importasmod(""" + def rec2(x): + return rec1(x+1) + def rec1(x): + return rec2(x-1) + def entry(): + rec1(42) + """) + excinfo = py.test.raises(RuntimeError, mod.entry) + + for style in ("short", "long"): + p = FormattedExcinfo(style="short") + l = [] + p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) + p.repr_sep = lambda sep: l.append(sep) + p.repr_tb(excinfo) + s = str(p) + print l + #assert re.search(".*return rec1.*x\+1", s) + #assert re.search(".*return rec2.*x-1.*", s) + assert len(l) < 20 + assert s.find("Recursion detected") != -1 + assert l[-1] == "_ " + assert l[-2][0].lineno == 4 + + def test_tb_entry_AssertionError(self): + # probably this test is a bit redundant + # as py/magic/testing/test_assertion.py + # already tests correctness of + # assertion-reinterpretation logic + mod = self.importasmod(""" + def somefunc(): + x = 1 + assert x == 2 + """) + py.magic.invoke(assertion=True) + try: + excinfo = py.test.raises(AssertionError, mod.somefunc) + finally: + py.magic.revoke(assertion=True) + + p = FormattedExcinfo() + p.repr_tb_entry(excinfo.traceback[-1], excinfo) + lines = p.lines + assert lines[-3] == "E assert 1 == 2" + + def test_excinfo_formatted(self): + mod = self.importasmod(""" + def f(x): + raise ValueError(x) + def entry(): + f(0) + """) + excinfo = py.test.raises(ValueError, mod.entry) + repr = excinfo._formatrepr() + fex = FormattedExcinfo() + for line1, line2 in zip(repr.lines, fex.lines): + assert str(line1) == str(line2) + + tw = py.io.TerminalWriter() + fex.writeterminal(tw) + lines1 = tw.stringio.getvalue().split("\n") + lines2 = excinfo.format().split("\n") + for line1, line2 in zip(repr.lines, fex.lines): + assert line1 == line2 + Deleted: /py/branch/event/py/code/testing/test_format_excinfo.py ============================================================================== --- /py/branch/event/py/code/testing/test_format_excinfo.py Mon Aug 4 23:10:55 2008 +++ (empty file) @@ -1,204 +0,0 @@ - -import py -import re -from py.__.code.format_excinfo import FormattedExcinfo - -class TestFormattedExcinfo: - - def setup_method(self, method): - self.tmpdir = py.test2.ensuretemp("%s_%s" %( - self.__class__.__name__, method.__name__)) - - def importasmod(self, source): - source = py.code.Source(source) - modpath = self.tmpdir.join("mod.py") - self.tmpdir.ensure("__init__.py") - modpath.write(source) - return modpath.pyimport() - - def getpresenter(self, **kwargs): - return FormattedExcinfo(**kwargs) - - def test_repr_source(self): - pr = self.getpresenter() - source = py.code.Source(""" - def f(x): - pass - """).strip() - pr.flow_marker = "|" - pr.repr_source(source, 0) - lines = pr.out.lines - assert len(lines) == 2 - assert lines[0] == "| def f(x):" - assert lines[1] == " pass" - - def test_repr_source_excinfo(self): - """ check if indentation is right """ - def f(): - def g(): - 1/0 - try: - g() - except: - e = py.code.ExceptionInfo() - return e - - pr = self.getpresenter() - source = py.code.Source(f) - e = f() - pr.repr_source(source, 1, e) - assert pr.out.lines[-1].startswith("E ZeroDivisionError:") - - def test_repr_local(self): - p = self.getpresenter(showlocals=True) - loc = locals() - p.repr_locals(loc) - result = str(p.out) - for key in loc.keys(): - assert result.find(key) != -1 - - def test_repr_tbentry(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello\\nworld") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-2]) - lines = p.out.lines - - # test intermittent entries - - assert lines[0] == " def entry():" - assert lines[1] == "> func1()" - assert not lines[2] - loc = lines[3] - assert loc.path == mod.__file__ - assert loc.lineno == 5 - assert not loc.message - - # test last entry - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[0] == " def func1():" - assert lines[1] == '> raise ValueError("hello\\nworld")' - assert lines[2] == "E ValueError: hello" - assert lines[3] == "E world" - loc = lines[5] - assert loc.path == mod.__file__ - assert loc.lineno == 3 - assert loc.message == "ValueError: hello\nworld" - assert str(loc) == "%s:3: 'ValueError: hello\\nworld'" %(mod.__file__,) - - def test_repr_tbentry_short(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter(style="short") - p.repr_tb_entry(excinfo.traceback[-2]) - lines = p.out.lines - basename = py.path.local(mod.__file__).basename - assert lines[0] == ' File "%s", line 5, in entry' % basename - assert lines[1] == ' func1()' - - # test last entry - p = self.getpresenter(style="short") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[0] == ' File "%s", line 3, in func1' % basename - assert lines[1] == ' raise ValueError("hello")' - assert lines[2] == 'E ValueError: hello' - - def test_repr_tbentry_no(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter(style="no") - p.repr_tb_entry(excinfo.traceback[-2]) - assert not p.out.lines - - p = self.getpresenter(style="no") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[0] == 'E ValueError: hello' - - def test_repr_tb(self): - mod = self.importasmod(""" - def f(x): - raise ValueError(x) - def entry(): - f(0) - """) - p = self.getpresenter(style="short") - excinfo = py.test.raises(ValueError, mod.entry) - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - s = p.repr_tb(excinfo) - print l - assert l[-1] == "_" - entry, excinfo2 = l[-2] - assert excinfo == excinfo2 - assert entry == excinfo.traceback[-1] - assert l[-3] == "_ " - entry, excinfo2 = l[-4] - assert excinfo2 is None - assert entry == excinfo.traceback[-2] - - def test_repr_tb_recursion(self): - mod = self.importasmod(""" - def rec2(x): - return rec1(x+1) - def rec1(x): - return rec2(x-1) - def entry(): - rec1(42) - """) - excinfo = py.test.raises(RuntimeError, mod.entry) - - for style in ("short", "long"): - p = self.getpresenter(style="short") - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - p.repr_tb(excinfo) - s = str(p.out) - print l - #assert re.search(".*return rec1.*x\+1", s) - #assert re.search(".*return rec2.*x-1.*", s) - assert len(l) < 20 - assert re.search("Recursion detected", s) - assert l[-1] == "_ " - assert l[-2][0].lineno == 4 - - def test_tb_entry_AssertionError(self): - # probably this test is a bit redundant - # as py/magic/testing/test_assertion.py - # already tests correctness of - # assertion-reinterpretation logic - mod = self.importasmod(""" - def somefunc(): - x = 1 - assert x == 2 - """) - py.magic.invoke(assertion=True) - try: - excinfo = py.test.raises(AssertionError, mod.somefunc) - finally: - py.magic.revoke(assertion=True) - - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.out.lines - assert lines[-3] == "E assert 1 == 2" Modified: py/branch/event/py/code/traceback2.py ============================================================================== --- py/branch/event/py/code/traceback2.py (original) +++ py/branch/event/py/code/traceback2.py Mon Aug 4 23:10:55 2008 @@ -90,8 +90,10 @@ name = self.frame.code.name try: line = str(self.statement).lstrip() - except EnvironmentError, e: - line = "" + except KeyboardInterrupt: + raise + except: + line = "???" return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line) def name(self): From hpk at codespeak.net Mon Aug 4 23:13:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 23:13:17 +0200 (CEST) Subject: [py-svn] r56985 - py/branch/event/py/code Message-ID: <20080804211317.0A05F169F07@codespeak.net> Author: hpk Date: Mon Aug 4 23:13:16 2008 New Revision: 56985 Modified: py/branch/event/py/code/excinfo.py Log: explicit keyword args Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Mon Aug 4 23:13:16 2008 @@ -52,8 +52,12 @@ formatter.repr_tb(self) return formatter - def format(self, **kwargs): - repr = self._formatrepr(**kwargs) + def format(self, showlocals=False, style="long"): + """ format exception info into string. + showlocals: show locals per traceback entry + style: long|short|no traceback style + """ + repr = self._formatrepr(showlocals=showlocals, style=style) tw = py.io.TerminalWriter() repr.writeterminal(tw) return tw.stringio.getvalue() From hpk at codespeak.net Mon Aug 4 23:16:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 4 Aug 2008 23:16:36 +0200 (CEST) Subject: [py-svn] r56986 - py/branch/event/py/test2 Message-ID: <20080804211636.1CF64169F31@codespeak.net> Author: hpk Date: Mon Aug 4 23:16:35 2008 New Revision: 56986 Modified: py/branch/event/py/test2/repevent.py Log: remove unused marshal import Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Mon Aug 4 23:16:35 2008 @@ -60,8 +60,6 @@ self.item = item self.keywordexpr = keywordexpr -import marshal - class ItemTestReport(BaseEvent): for _ in validoutcomes: if _ == "failed_collection": continue From hpk at codespeak.net Tue Aug 5 09:10:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 5 Aug 2008 09:10:24 +0200 (CEST) Subject: [py-svn] r56990 - in py/branch/event/py/test2: . testing Message-ID: <20080805071024.C739E169F6F@codespeak.net> Author: hpk Date: Tue Aug 5 09:10:23 2008 New Revision: 56990 Modified: py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner.py Log: docstring,asserts now tested Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Aug 5 09:10:23 2008 @@ -1,5 +1,9 @@ """ - test run functions + internal classes for + + * executing test items + * running collectors + * and generating report events about it """ import py, os, sys @@ -10,10 +14,6 @@ import py.__.test2.custompdb from py.__.test2 import pypresent -# -# internal classes (search for "public" below) -# - class RobustRun: """ a robust setup/execute/teardown protocol. """ def run(self): Modified: py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner.py (original) +++ py/branch/event/py/test2/testing/test_runner.py Tue Aug 5 09:10:23 2008 @@ -242,7 +242,3 @@ assert testrep.repr_run.find("CRASHED") != -1 assert testrep.failed_crashed assert testrep.failed - -def test_assertion(): - py.test.skip("XXX need to test that assertion reinterp is turned on and working for py function test items. ") - From hpk at codespeak.net Tue Aug 5 11:24:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 5 Aug 2008 11:24:20 +0200 (CEST) Subject: [py-svn] r56996 - py/branch/event/py/test2/testing Message-ID: <20080805092420.23692169F80@codespeak.net> Author: hpk Date: Tue Aug 5 11:24:19 2008 New Revision: 56996 Added: py/branch/event/py/test2/testing/test_runner_functional.py Removed: py/branch/event/py/test2/testing/test_runner.py Modified: py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_session.py Log: port tests, i.e. move away from mock testing here, too tiresome for refactorings imho Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Tue Aug 5 11:24:19 2008 @@ -114,3 +114,45 @@ "%s:3: 3 Skipped, reason: test" %(p3,) ]) + def test_looponfailing_looping(self): + py.test.skip("thought needed to nicely test --looponfailing") + py.test.skip("xxx check events") + o = tmpdir.ensure('looponfailing', dir=1) + tfile = o.join('test_looponfailing.py') + tfile.write(py.code.Source(""" + def test_1(): + assert 1 == 0 + """)) + print py.std.sys.executable + config = py.test2.config._reparse(['--looponfailing', str(o)]) + session = config.initsession() + failures = session.main() + + cls = config._getsessionclass() + out = py.std.Queue.Queue() + session = cls(config, out.put) + pool = py._thread.WorkerPool() + reply = pool.dispatch(session.main) + while 1: + s = out.get(timeout=5.0) + if s.find('1 failed') != -1: + break + print s + else: + py.test2.fail("did not see test_1 failure") + # XXX we would like to have a cleaner way to finish + try: + reply.get(timeout=5.0) + except IOError, e: + assert str(e).lower().find('timeout') != -1 + + def test_invoking_pdb(self): + py.test.skip("implement test for interaction with pdb") + l = [] + def testfunc(): + raise ValueError(7) + def mypdb(runinfo): + l.append(runinfo) + testrep = self.run(testfunc, pdb=mypdb) + assert len(l) == 1 + assert l[0].excinfo.exconly().find("ValueError: 7") != -1 Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Tue Aug 5 11:24:19 2008 @@ -160,39 +160,37 @@ # XXX below some code to help with inlining examples # as source code. -class TestBaseInlineCollection: +class InlineCollection: + """ helps to collect and run test functions inlining other test functions. """ def setup_method(self, method): - self.tmpdir = tmpdir.join("%s_%s_%s" % - (__name__, self.__class__.__name__, method.__name__)) + self.tmpdir = py.test2.ensuretemp("%s_%s" % + (self.__class__.__name__, method.__name__)) - def getmodulecol(self, func, funcname="testfunc"): + def makeconftest(self, source): + self.tmpdir.ensure("__init__.py") + path = self.tmpdir.ensure("conftest.py") + path.write(py.code.Source(source)) + + def getmodulecol(self, func, funcname="test_func"): funcname = getattr(func, '__name__', funcname) self.tmpdir.ensure("__init__.py") path = self.tmpdir.ensure(funcname + ".py") path.write(py.code.Source(func)) self.config = py.test2.config._reparse([path.dirpath()]) - modulecol = self.config._getcollector(path) - return modulecol + return self.config.getfsnode(path) - def makeitem(self, source, funcname="testfunc"): - modulecol = self.getmodulecol(source) + def getitem(self, source, funcname="test_func"): + modulecol = self.getmodulecol(source, funcname=funcname) item = modulecol.join(funcname) - assert item is not None, (item, funcname) + assert item is not None, "%r item not found in module:\n%s" %(funcname, source) return item + def runitem(self, func, funcname="test_func"): + item = self.getitem(func, funcname=funcname) + runner = self.getrunner() + return runner(item) + def getitems(self, source): modulecol = self.getmodulecol(source) return [modulecol.join(x) for x in modulecol.listdir()] - def runtestfunc(self, func, funcname="testfunc"): - item = self.makeitem(func, funcname=funcname) - runner = self.getrunner() - return runner(item) - - def exampleusage(self): - testrep = self.runtestfunc(""" - def testfunc(): - pass - """) - assert testrep.passed - Deleted: /py/branch/event/py/test2/testing/test_runner.py ============================================================================== --- /py/branch/event/py/test2/testing/test_runner.py Tue Aug 5 11:24:19 2008 +++ (empty file) @@ -1,244 +0,0 @@ - -import py -from py.__.test2 import runner -from py.__.test2.outcome import Exit -from py.__.test2 import pypresent - -class MockItem: - def __init__(self, func): - self.func = func - self._config = py.test2.config._reparse([]) - - def __getstate__(self): - return (self._config, ) - def __setstate__(self, repr): - self._config, = repr - - def _get_collector_trail(self): - return "MockItem.trail" - - def repr_path(self): - return "MockItem.repr_path" - - def repr_run(self, runinfo): - excinfo = runinfo.excinfo - if not excinfo: - return ("(%s)MockItem.repr_run" %(runinfo.outerr,)) - else: - assert isinstance(excinfo, py.code.ExceptionInfo) - return ("(%s)MockItem.repr_run: %s" %( - runinfo.outerr, excinfo.exconly())) - - def execute(self): - self.func() - -class MockCapture: - def reset(self): - return "out", "err" - -class MockSetupState: - def prepare(self, item): - return - def teardown_exact(self, item): - pass - def teardown_all(self): - pass - -class RunnerTests: - def run(self, func, setupstate=None, getcapture=None, pdb=None): - runner = self.getrunner() - item = MockItem(func) - if not setupstate: setupstate = MockSetupState() - if not getcapture: getcapture = MockCapture - testrep = runner(item, setupstate, getcapture, pdb=pdb) - return testrep - - def test_run_pass(self): - def test_func(): - pass - testrep = self.run(test_func) - assert testrep.passed - - def test_run_fail(self): - def testfunc(): - assert 0 - testrep = self.run(testfunc) - assert not testrep.passed - assert testrep.failed - - def test_run_skip(self): - def testfunc(): - py.test2.skip("hello") - testrep = self.run(testfunc) - assert testrep.skipped - assert not testrep.passed - assert not testrep.failed - assert not testrep.failed_setup - - def test_systemexit_does_not_bail_out(self): - def testfunc(): - raise SystemExit(42) - try: - testrep = self.run(testfunc) - except SystemExit: - py.test.fail("runner did not catch SystemExit") - assert testrep.failed - assert not testrep.failed_setup - assert testrep.repr_run.find("SystemExit") != -1 - - def test_exit_does_bail_out(self): - def testfunc(): - raise Exit() - py.test.raises(Exit, "self.run(testfunc)") - - def test_runner_handles_skip_in_prepare(self): - l = [] - class MySetupState: - def prepare(self, item): - py.test2.skip("skip") - def testfunc(): pass - def pythonreprrun(runinfo, title=None): return "" - py.magic.patch(pypresent, "python_repr_run", pythonreprrun) - try: - testrep = self.run(testfunc, setupstate=MySetupState()) - finally: - py.magic.revert(pypresent, "python_repr_run") - assert testrep.skipped - assert not testrep.failed - assert not testrep.passed - - def test_runnner_handles_setupfailure_in_prepare(self): - l = [] - class MySetupState: - def prepare(self, item): - raise ValueError(2) - def testfunc(): - pass - def pythonreprrun(runinfo, title=None): - assert runinfo.excinfo.errisinstance(ValueError) - return "pythonreprrun" - py.magic.patch(pypresent, "python_repr_run", pythonreprrun) - try: - testrep = self.run(testfunc, setupstate=MySetupState()) - finally: - py.magic.revert(pypresent, "python_repr_run") - assert testrep.failed_setup - assert testrep.failed - assert not testrep.passed - assert testrep.repr_run == "pythonreprrun" - - def test_runner_handles_capture(self): - l = [] - class MyCapture: - def __init__(self): - l.append("init") - def reset(self): - l.append("reset") - return ":".join(l), "" - def testfunc(): - l.append("run") - testrep = self.run(testfunc, getcapture=MyCapture) - assert testrep.passed - assert testrep.repr_run.find("init:run:reset") != -1 - - def testfuncfail(): - l.append("run") - assert 0 - l[:] = [] - testrep = self.run(testfuncfail, getcapture=MyCapture) - assert testrep.repr_run.find("init:run:reset") != -1 - assert testrep.failed - -class TestBasicRunner(RunnerTests): - def getrunner(self): - return runner.basic_run_report - - def test_runner_handles_setupstate(self): - l = [] - class MySetupState: - def prepare(self, item): - l.append("prepare") - def teardown_exact(self, item): - l.append("teardown_exact") - def testfunc(): - l.append("run") - testrep = self.run(testfunc, setupstate=MySetupState()) - assert l == ["prepare", "run", "teardown_exact"] - assert testrep.passed - - def testfuncfail(): - l.append("run") - assert 0 - l[:] = [] - testrep = self.run(testfuncfail, setupstate=MySetupState()) - assert l == ["prepare", "run", "teardown_exact"] - assert testrep.failed - - - def test_runnner_handles_setupfailure_in_eager_teardown(self): - l = [] - class MySetupState: - def prepare(self, item): - pass - def teardown_exact(self, item): - raise ValueError(17) - def testfunc(): - l.append(0) - pass - def pythonreprrun(runinfo, title=None): - l.append(runinfo) - py.magic.patch(pypresent, "python_repr_run", pythonreprrun) - try: - testrep = self.run(testfunc, setupstate=MySetupState()) - finally: - py.magic.revert(pypresent, "python_repr_run") - assert testrep.failed_teardown - assert testrep.failed - assert not testrep.passed - assert len(l) == 2 # means that testfunc didnt run - assert l[0] == 0 - assert l[1].excinfo.errisinstance(ValueError) - - def test_invoking_pdb(self): - l = [] - def testfunc(): - raise ValueError(7) - def mypdb(runinfo): - l.append(runinfo) - testrep = self.run(testfunc, pdb=mypdb) - assert len(l) == 1 - assert l[0].excinfo.exconly().find("ValueError: 7") != -1 - -class TestForkRunner(RunnerTests): - def setup_class(cls): - if not hasattr(py.std.os, 'fork'): - py.test.skip("need os.fork()") - - def getrunner(self): - return runner.forked_run_report - - def test_exit_does_bail_out(self): - def testfunc(): - raise Exit() - py.test.raises(Exit, "self.run(testfunc)") - - def test_suicide(self): - def testfunc(): - import os - os.kill(os.getpid(), 15) - crashes = [] - def myreport(item, result): - crashes.append((item, result)) - py.magic.patch(runner, "report_crash", myreport) - try: - self.run(testfunc) - finally: - py.magic.revert(runner, "report_crash") - assert len(crashes) == 1 - result = crashes[0][1] - assert result.signal == 15 - - testrep = runner.report_crash(*crashes[0]) - assert testrep.repr_run.find("CRASHED") != -1 - assert testrep.failed_crashed - assert testrep.failed Added: py/branch/event/py/test2/testing/test_runner_functional.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/testing/test_runner_functional.py Tue Aug 5 11:24:19 2008 @@ -0,0 +1,249 @@ +import py +from py.__.test2.testing.suptest import InlineCollection +from py.__.test2.runner import basic_run_report, forked_run_report, basic_collect_report + +class BaseTests(InlineCollection): + def test_passfunction(self): + ev = self.runitem(""" + def test_func(): + pass + """) + assert ev.passed + assert not ev.failed + + def test_failfunction(self): + ev = self.runitem(""" + def test_func(): + assert 0 + """) + assert not ev.passed + assert not ev.skipped + assert ev.failed + # assert ev.failed.location.lineno == 2 + # assert ev.failed.location.path + # assert ev.failed.reason == "AssertionError: 0" + # assert ev.failed.failurerepr + + def test_skipfunction(self): + ev = self.runitem(""" + import py + def test_func(): + py.test2.skip("hello") + """) + assert not ev.failed + assert not ev.passed + assert ev.skipped + #assert ev.skipped.reason == "hello" + #assert ev.skipped.location.lineno == 3 + #assert ev.skipped.location.path + + def test_skip_in_setup_function(self): + ev = self.runitem(""" + import py + def setup_function(func): + py.test2.skip("hello") + def test_func(): + pass + """) + print ev + assert not ev.failed + assert not ev.passed + assert ev.skipped + #assert ev.skipped.reason == "hello" + #assert ev.skipped.location.lineno == 3 + #assert ev.skipped.location.lineno == 3 + + def test_failure_in_setup_function(self): + ev = self.runitem(""" + import py + def setup_function(func): + raise ValueError(42) + def test_func(): + pass + """) + print ev + assert not ev.skipped + assert not ev.passed + assert ev.failed + + def test_failure_in_teardown_function(self): + ev = self.runitem(""" + import py + def teardown_function(func): + raise ValueError(42) + def test_func(): + pass + """) + print ev + assert not ev.skipped + assert not ev.passed + assert ev.failed + #assert ev.failed.when == "teardown" + #assert ev.failed.where.lineno == 3 + #assert ev.failed.entries + + def test_custom_failure_repr(self): + self.makeconftest(""" + import py + class Function(py.test2.collect.Function): + def getfailurerepr(self, excinfo): + return "hello" + """) + ev = self.runitem(""" + import py + def test_func(): + assert 0 + """) + assert not ev.skipped + assert not ev.passed + assert ev.failed + #assert ev.failed.when == "execute" + #assert ev.failed.where.lineno == 3 + #assert ev.failed.where.path.basename == "test_func.py" + #assert ev.failed.failurerepr == "hello" + + def test_failure_in_setup_function_ignores_custom_failure_repr(self): + self.makeconftest(""" + import py + class Function(py.test2.collect.Function): + def getfailurerepr(self, excinfo): + assert 0 + """) + ev = self.runitem(""" + import py + def setup_function(func): + raise ValueError(42) + def test_func(): + pass + """) + print ev + assert not ev.skipped + assert not ev.passed + assert ev.failed + #assert ev.failed.when == "setup" + #assert ev.failed.where.lineno == 3 + #assert ev.failed.where.path.basename == "test_func.py" + #assert instanace(ev.failed.failurerepr, PythonFailureRepr) + + def test_capture_in_func(self): + ev = self.runitem(""" + import py + def setup_function(func): + print >>py.std.sys.stderr, "in setup" + def test_func(): + print "in function" + assert 0 + def teardown_func(func): + print "in teardown" + """) + assert ev.failed + # out, err = ev.failed.outerr + # assert out == ['in function\nin teardown\n'] + # assert err == ['in setup\n'] + + def test_systemexit_does_not_bail_out(self): + try: + ev = self.runitem(""" + def test_func(): + raise SystemExit(42) + """) + except SystemExit: + py.test.fail("runner did not catch SystemExit") + assert ev.failed + assert not ev.failed_setup + #assert ev.failurerepr + + def test_exit_propagates(self): + from py.__.test2.outcome import Exit + try: + self.runitem(""" + from py.__.test2.outcome import Exit + def test_func(): + raise Exit() + """) + except Exit: + pass + else: + py.test.fail("did not raise") + +class TestExecutionNonForked(BaseTests): + def getrunner(self): + def runner(item): + return basic_run_report( + item, item._setupstate, + item._config._getcapture + ) + return runner + + def test_keyboardinterrupt_propagates(self): + from py.__.test2.outcome import Exit + try: + self.runitem(""" + def test_func(): + raise KeyboardInterrupt("fake") + """) + except KeyboardInterrupt, e: + pass + else: + py.test.fail("did not raise") + + def test_invoking_pdb(self): + py.test.skip("pdb test needs porting") + l = [] + def testfunc(): + raise ValueError(7) + def mypdb(runinfo): + l.append(runinfo) + testrep = self.run(testfunc, pdb=mypdb) + assert len(l) == 1 + assert l[0].excinfo.exconly().find("ValueError: 7") != -1 + +class TestExecutionForked(BaseTests): + def getrunner(self): + def runner(item): + return forked_run_report( + item, item._setupstate, + item._config._getcapture + ) + return runner + + def test_suicide(self): + ev = self.runitem(""" + def test_func(): + import os + os.kill(os.getpid(), 15) + """) + assert ev.failed_crashed + assert ev.failed + +class TestCollectionEvent(InlineCollection): + def setup_class(cls): + py.test.skip("xxx") + + def test_collect_result(self): + col = self.getmodulecol(""" + def test_func1(): + pass + class TestClass: + pass + """) + ev = basic_collect_report(col) + assert not ev.failed + assert not ev.skipped + assert ev.passed + res = ev.result + assert len(res) == 2 + assert l[0].name == "test_func1" + assert l[1].name == "TestClass" + + def test_skip_at_module_scope(self): + col = self.getmodulecol(""" + import py + py.test2.skip("hello") + def test_func(): + pass + """) + ev = basic_collect_report(col) + assert not ev.failed + assert not ev.passed + assert ev.skipped Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Tue Aug 5 11:24:19 2008 @@ -280,36 +280,3 @@ session = config.initsession() assert isinstance(session, RemoteTerminalSession) - def test_looponfailing_loops(self): - py.test.skip("thought needed to nicely test --looponfailing") - py.test.skip("xxx check events") - o = tmpdir.ensure('looponfailing', dir=1) - tfile = o.join('test_looponfailing.py') - tfile.write(py.code.Source(""" - def test_1(): - assert 1 == 0 - """)) - print py.std.sys.executable - config = py.test2.config._reparse(['--looponfailing', str(o)]) - session = config.initsession() - failures = session.main() - - cls = config._getsessionclass() - out = py.std.Queue.Queue() - session = cls(config, out.put) - pool = py._thread.WorkerPool() - reply = pool.dispatch(session.main) - while 1: - s = out.get(timeout=5.0) - if s.find('1 failed') != -1: - break - print s - else: - py.test2.fail("did not see test_1 failure") - # XXX we would like to have a cleaner way to finish - try: - reply.get(timeout=5.0) - except IOError, e: - assert str(e).lower().find('timeout') != -1 - - From hpk at codespeak.net Tue Aug 5 18:19:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 5 Aug 2008 18:19:08 +0200 (CEST) Subject: [py-svn] r57006 - in py/branch/event/py/test2: . rep rep/testing rsession rsession/testing testing Message-ID: <20080805161908.CEB9D169F83@codespeak.net> Author: hpk Date: Tue Aug 5 18:19:07 2008 New Revision: 57006 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/outcome.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_doctest.py py/branch/event/py/test2/testing/test_pypresent.py py/branch/event/py/test2/testing/test_repevent.py py/branch/event/py/test2/testing/test_runner_functional.py py/branch/event/py/test2/testing/test_session.py py/branch/event/py/test2/testing/test_setup_nested.py Log: unification and simplification of reporting events and reporting. Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Tue Aug 5 18:19:07 2008 @@ -219,6 +219,11 @@ col = config.getfsnode(fspath) return col._getitembynames(names) + def _getfailurerepr_py(self, excinfo, outerr): + repr = excinfo._formatrepr() + # XXX outerr + return repr + shortfailurerepr = "f" class Collector(Node): """ @@ -256,6 +261,9 @@ """ raise NotImplementedError("abstract") + def getfailurerepr(self, excinfo, outerr): + return self._getfailurerepr_py(excinfo, outerr) + class FSCollector(Collector): def __init__(self, fspath, parent=None, config=None): fspath = py.path.local(fspath) Modified: py/branch/event/py/test2/outcome.py ============================================================================== --- py/branch/event/py/test2/outcome.py (original) +++ py/branch/event/py/test2/outcome.py Tue Aug 5 18:19:07 2008 @@ -1,5 +1,5 @@ """ - Test Outcomes and helpers for creating them. + Test OutcomeExceptions and helpers for creating them. py.test.skip|fail|raises helper implementations """ @@ -7,8 +7,8 @@ import py import sys -class Outcome: - """ Outcome and its subclass instances indicate and +class OutcomeException(Exception): + """ OutcomeException and its subclass instances indicate and contain info about test and collection outcomes. """ def __init__(self, msg=None, excinfo=None): @@ -21,13 +21,13 @@ return "<%s instance>" %(self.__class__.__name__,) __str__ = __repr__ -class Passed(Outcome): +class Passed(OutcomeException): pass -class Skipped(Outcome): +class Skipped(OutcomeException): pass -class Failed(Outcome): +class Failed(OutcomeException): pass class ExceptionFailure(Failed): @@ -113,3 +113,5 @@ if not l: raise AssertionError("%r did not produce DeprecationWarning" %(func,)) return ret + + Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Tue Aug 5 18:19:07 2008 @@ -181,12 +181,15 @@ class FunctionMixin(PyobjMixin): """ mixin for the code common to Function and Generator. """ - _sortvalue = None def _getsortvalue(self): - if self._sortvalue is None: + return self.getfsloc() + + _fsloc = None + def getfsloc(self): + if self._fsloc is None: code = py.code.Code(self.obj) - self._sortvalue = code.path, code.firstlineno - return self._sortvalue + self._fsloc = code.path, code.firstlineno + return self._fsloc def setup(self): """ perform setup for this test function. """ @@ -220,6 +223,14 @@ traceback = ntraceback.filter() return traceback + def getfailurerepr(self, excinfo, outerr): + return self._getfailurerepr_py(excinfo, outerr) + #repr = excinfo._formatrepr() + ## XXX outerr + #return repr + + shortfailurerepr = "F" + class Generator(FunctionMixin, PyCollectorMixin, Collector): def listdir(self): self._prepare() Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Tue Aug 5 18:19:07 2008 @@ -1,9 +1,10 @@ from py.__.test2 import repevent -from py.__.test2.runner import RunInfo class BaseReporter(object): def __init__(self): - self._outcome2rep = {} + self._passed = [] + self._skipped = [] + self._failed = [] def activate(self, bus): bus.subscribe(self.processevent) @@ -22,40 +23,12 @@ pass def rep_ItemTestReport(self, ev): - outcome = ev.outcome - l = self._outcome2rep.setdefault(outcome, []) - l.append(ev) - - def rep_CollectionFinish(self, ev): - outcome = ev.runinfo.outcome - if outcome in ("failed", "skipped"): - key = outcome + "_collection" - l = self._outcome2rep.setdefault(key, []) - l.append(ev) - - def getreports(self, outcome=None, category=None): - """ return a list with all reports matching - the given outcome or (if outcome is None) - the category. - """ - if outcome is not None: - check = outcome - if outcome.endswith("_collection"): - check = outcome[:outcome.rfind("_")] - if check not in repevent.validoutcomes: - raise ValueError("not a valid outcome %r" %(outcome)) - l = self._outcome2rep.setdefault(outcome, []) - return l[:] - if category is not None: - if category == "failed": - l = self.getreports("failed") - l += self.getreports("failed_crashed") - l += self.getreports("failed_setup") - l += self.getreports("failed_collection") - elif category == "skipped": - l = self.getreports("skipped") - l += self.getreports("skipped_collection") - else: - l = self.getreports(category) - return l + if ev.skipped: + self._skipped.append(ev) + elif ev.failed: + self._failed.append(ev) + elif ev.passed: + self._passed.append(ev) + def rep_CollectionReport(self, ev): + return BaseReporter.rep_ItemTestReport(self, ev) Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Tue Aug 5 18:19:07 2008 @@ -32,21 +32,21 @@ msg = "DESELECTED: " self.outindent(msg + str(event.item)) - def rep_CollectionFinish(self, ev): - if ev.runinfo is not None and not ev.runinfo.hasresult(): - excinfo = ev.runinfo.excinfo - self.outindent("!!! %s !!!" % excinfo.exconly()) - self._failed.append(ev) + def rep_CollectionReport(self, ev): + super(CollectonlyReporter, self).rep_CollectionReport(ev) + if ev.failed: + self.outindent("!!! %s !!!" % ev.failed.exconly) + elif ev.skipped: + self.outindent("!!! %s !!!" % ev.skipped.exconly) self.indent = self.indent[:-len(self.INDENT)] def rep_SessionFinish(self, session): from py.__.code.tbpresent import TBPresenter - # XXX add tests - # XXX re-use runner for collection? tb = TBPresenter(out=self.out, showlocals=self.config.option.showlocals) for ev in self._failed: - if not ev.runinfo.excinfo.errisinstance((Skipped, Skipped2)): - tb.out.sep("*", "failed collection") - tb.repr_tb(ev.runinfo.excinfo) + if hasattr(ev.outcome.longrepr, 'terminalwrite'): + ev.outcome.longrepr.terminalwrite(self.out) + else: + self.out.line(str(ev.outcome.longrepr)) Reporter = CollectonlyReporter Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Tue Aug 5 18:19:07 2008 @@ -16,19 +16,19 @@ def rep_ItemTestReport(self, ev): super(TerminalReporter, self).rep_ItemTestReport(ev) - fspath, modpath = ev.repr_path + fspath = ev.colitem.fspath if fspath != self.currentfspath: self.out.line() self.out.write(fspath + " ") self.currentfspath = fspath - self.out.write(self._getletter(ev)) + self.out.write(ev.outcome.shortrepr) - def rep_CollectionFinish(self, ev): - super(TerminalReporter, self).rep_CollectionFinish(ev) - if ev.runinfo.excinfo: - fspath, modpath = ev.runinfo.item.repr_path() + def rep_CollectionReport(self, ev): + super(TerminalReporter, self).rep_CollectionReport(ev) + fspath = ev.colitem.fspath + if ev.failed or ev.skipped: self.out.line() - self.out.write(fspath + " - ImportError -") + self.out.write(fspath + " - " + str(ev.outcome.exconly)) self.currentfspath = fspath def rep_SessionStart(self, ev): @@ -36,16 +36,15 @@ def rep_SessionFinish(self, ev): self.out.line("") - for ev in self.getreports(category="failed"): - #self.out.sep("_", "entrypoint: %s" %(ev.repr_path[1],)) - try: - self.out.line(ev.repr_run) - except AttributeError: - self.out.line(ev.runinfo.repr_run()) - #self.out.sep("=") - numfailed = len(self.getreports(category="failed")) - numskipped = len(self.getreports(category="skipped")) - numpassed = len(self.getreports(category="passed")) + for ev in self._failed: + longrepr = ev.failed.longrepr + if hasattr(longrepr, 'terminalwrite'): + longrepr.terminalwrite(self.out) + else: + self.out.line(str(longrepr)) + numfailed = len(self._failed) + numskipped = len(self._skipped) + numpassed = len(self._passed) sum = numfailed + numskipped + numpassed self.out.sep("=", "%d tests of which %d failed, %d skipped" %( sum, numfailed, numskipped)) Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Tue Aug 5 18:19:07 2008 @@ -2,7 +2,6 @@ from py.__.test2.rep.base import BaseReporter from py.__.test2.eventbus import EventBus from py.__.test2 import repevent -from py.__.test2.runner import RunInfo class TestBaseReporter: def test_activate(self): @@ -36,79 +35,19 @@ assert len(l) == 1 assert l[0] is ev - def test_default_TestItemReport_one(self): - rep = BaseReporter() - ev = repevent.ItemTestReport(None, "failed", None, None) - rep.processevent(ev) - assert rep.getreports("failed") == [ev] - - def test_default_TestItemReport_all_outcomes(self): - rep = BaseReporter() - - for outcome in repevent.validoutcomes: - ev = repevent.ItemTestReport(None, outcome, None, None) + def test_TestItemReport_one(self): + for outcome in 'passed skipped failed'.split(): + rep = BaseReporter() + ev = repevent.ItemTestReport(None, **{outcome:True}) rep.processevent(ev) - rep.processevent(ev) - for outcome in repevent.validoutcomes: - l = rep.getreports(outcome) - assert len(l) == 2 - assert l[0] is l[1] - # ensure that we got a copy, not an internal list - l.pop() - l = rep.getreports(outcome) - assert len(l) == 2 - - def test_TestItemReport_getreports_unknown_outcome(self): - rep = BaseReporter() - py.test.raises(ValueError, "rep.getreports('qwe')") + assert getattr(rep, '_' + outcome) == [ev] - def test_TestItemReport_getreports(self): - rep = BaseReporter() - ev1 = repevent.ItemTestReport(None, "failed_setup", None, None) - rep.processevent(ev1) - ev2 = repevent.ItemTestReport(None, "failed_crashed", None, None) - rep.processevent(ev2) - ev3 = repevent.ItemTestReport(None, "passed", None, None) - rep.processevent(ev3) - ev4 = repevent.ItemTestReport(None, "failed", None, None) - rep.processevent(ev4) - ev5 = repevent.ItemTestReport(None, "skipped", None, None) - rep.processevent(ev5) - l = rep.getreports(category="failed") - assert len(l) == 3 - assert ev1 in l - assert ev2 in l - assert ev4 in l - l = rep.getreports(category="passed") - assert l == [ev3] - l = rep.getreports("passed") - assert l == [ev3] - l = rep.getreports(category="skipped") - assert l == [ev5] - l = rep.getreports("skipped") - assert l == [ev5] - - def test_CollectionFinish_failed(self): - rep = BaseReporter() - runinfo = RunInfo(None, "failed", None, None) - ev = repevent.CollectionFinish(None, runinfo) - rep.processevent(ev) - l = rep.getreports(category="failed") - assert l == [ev] - l = rep.getreports("failed_collection") - assert l == [ev] - - def test_skip_testitems_and_collections(self): - rep = BaseReporter() - runinfo = RunInfo(None, "skipped", None, None) - ev1 = repevent.CollectionFinish(None, runinfo) - rep.processevent(ev1) - ev2 = repevent.ItemTestReport(None, "skipped", None, None) - rep.processevent(ev2) - l = rep.getreports(category="skipped") - assert l == [ev2, ev1] - l = rep.getreports("skipped_collection") - assert l == [ev1] + def test_CollectionReport(self): + for outcome in 'passed skipped failed'.split(): + rep = BaseReporter() + ev = repevent.CollectionReport(None, None, **{outcome:True}) + rep.processevent(ev) + assert getattr(rep, '_' + outcome) == [ev] def test_skip_reasons(self): py.test.skip("unify ItemTestReport and CollectionReport first") Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Tue Aug 5 18:19:07 2008 @@ -52,7 +52,7 @@ s = popvalue(stringio) assert s == " DESELECTED: " - rep.processevent(repevent.CollectionFinish(modcol)) + rep.processevent(repevent.CollectionReport(modcol, [], passed="")) assert rep.indent == indent def test_collectonly_skipped_module(self): Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Tue Aug 5 18:19:07 2008 @@ -29,10 +29,7 @@ stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) for item in self.session.genitems([modcol]): - ev = basic_run_report(item, - setupstate=modcol._setupstate, - getcapture=modcol._config._getcapture - ) + ev = basic_run_report(item) rep.processevent(ev) s = popvalue(stringio) assert s.find("test_pass_skip_fail.py .sF") != -1 @@ -54,7 +51,7 @@ assert len(l) == 0 s = popvalue(stringio) print s - assert s.find("test_collect_fail.py - ImportError -") != -1 + assert s.find("test_collect_fail.py - ImportError: No module named") != -1 rep.processevent(repevent.SessionFinish(None)) assert_stringio_contains_lines(stringio, [ "> import xyz", Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Tue Aug 5 18:19:07 2008 @@ -60,20 +60,24 @@ self.item = item self.keywordexpr = keywordexpr -class ItemTestReport(BaseEvent): - for _ in validoutcomes: - if _ == "failed_collection": continue - exec "%s = property(lambda self: self.outcome == %r)\n" %(_,_) - failed = property(lambda self: self.outcome.startswith("failed")) - - def __init__(self, item, outcome, repr_run, repr_path, outerr=None): - self.item = item - assert outcome in validoutcomes - self.outcome = outcome - self.repr_run = repr_run - self.repr_path = repr_path - self.outerr = outerr - +class BaseReport(BaseEvent): + failed = passed = skipped = None + def __init__(self, colitem, **kwargs): + self.colitem = colitem + assert len(kwargs) == 1, kwargs + name, value = kwargs.items()[0] + setattr(self, name, value) + self.outcome = value + +class ItemTestReport(BaseReport): + """ Test Execution Report. """ + +class CollectionReport(BaseReport): + """ Collection Report. """ + def __init__(self, colitem, result, **kwargs): + super(CollectionReport, self).__init__(colitem, **kwargs) + self.result = result + # ---------------------------------------------------------------------- # Distributed Testing Events Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Tue Aug 5 18:19:07 2008 @@ -75,5 +75,5 @@ send(None) break runner = item._getrunner() - testrep = runner(item, item._setupstate, item._config._getcapture) + testrep = runner(item) send(testrep) Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Tue Aug 5 18:19:07 2008 @@ -65,7 +65,9 @@ self.host.initgateway() self.node = MasterNode(self.host, self.session) assert not self.node.channel.isclosed() - + + def teardown_method(self, method): + print "at teardown:", self.node.channel def test_node_down(self): queue = self.makereportqueue(repevent.HostDown) @@ -80,7 +82,7 @@ event = queue.get(timeout=2.0) assert event.passed assert not self.node.pending - assert event.item == item + assert event.colitem == item #assert event.item == item #assert event.item is not item Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Aug 5 18:19:07 2008 @@ -23,105 +23,120 @@ capture = self.getcapture() try: try: - outcome = "failed_setup" + when = "setup" self.setup() try: res = self.execute() finally: - outcome = "failed_teardown" + when = "teardown" self.teardown() - outcome = "failed" - outcome = "passed" + when = "execute" finally: outerr = capture.reset() except (Exit, KeyboardInterrupt): raise except: excinfo = py.code.ExceptionInfo() - return self.makeruninfo(res, outcome, excinfo, outerr) + return self.makereport(res, when, excinfo, outerr) class ItemRunner(RobustRun): - def __init__(self, item, setupstate, getcapture): + def __init__(self, item, pdb=None): self.item = item - self.setupstate = setupstate - self.getcapture = getcapture + self.setupstate = item._setupstate + self.getcapture = item._config._getcapture + self.pdb = pdb def setup(self): self.setupstate.prepare(self.item) def teardown(self): self.setupstate.teardown_exact(self.item) def execute(self): self.item.execute() - def makeruninfo(self, res, outcome, excinfo, outerr): - return RunInfo(self.item, outcome, excinfo, outerr) + def makereport(self, res, when, excinfo, outerr): + if excinfo: + if excinfo.errisinstance((Skipped,Skipped2)): + outcome = "skipped" + shortrepr = "s" + longrepr = "" + else: + outcome = "failed" + if when == "execute": + longrepr = self.item.getfailurerepr(excinfo, outerr) + shortrepr = self.item.shortfailurerepr + else: + longrepr = self.item._getfailurerepr_py(excinfo, outerr) + shortrepr = self.item.shortfailurerepr.lower() + exconly = excinfo.exconly(tryshort=True) + else: + outcome = "passed" + shortrepr = "." + longrepr = "" + exconly = "" + kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly)} + ev = repevent.ItemTestReport(self.item, **kw) + if self.pdb and kw['failed']: + self.pdb(excinfo) + return ev class CollectorRunner(RobustRun): - def __init__(self, col, getcapture): + def __init__(self, col): self.col = col - self.getcapture = getcapture + self.getcapture = col._config._getcapture def setup(self): pass def teardown(self): pass def execute(self): return [self.col.join(x) for x in self.col.listdir()] - def makeruninfo(self, res, outcome, excinfo, outerr): - #outcome += "_incollection" - return RunInfo(self.col, outcome, excinfo, outerr, result=res) - -def makereport(runinfo): - item = runinfo.item - repr_path = item.repr_path() - if runinfo.outcome not in ("failed_setup", "failed_teardown"): - repr_run = item.repr_run(runinfo) - else: - repr_run = pypresent.python_repr_run(runinfo, - title="failure during setup/teardown") - return repevent.ItemTestReport(item, runinfo.outcome, repr_run, repr_path) + def makereport(self, res, when, excinfo, outerr): + if excinfo: + exconly = excinfo.exconly(tryshort=True) + if excinfo.errisinstance((Skipped,Skipped2)): + outcome = "skipped" + shortrepr = "s" + longrepr = "" + else: + outcome = "failed" + assert when == "execute" + longrepr = self.col.getfailurerepr(excinfo, outerr) + shortrepr = "f" + else: + outcome = "passed" + shortrepr = longrepr = exconly = "" + kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly)} + return repevent.CollectionReport(self.col, res, **kw) NORESULT = object() # # public entrypoints / objects # -class RunInfo: - """ info on test or collector runs. """ - def __init__(self, item, outcome, excinfo, outerr, result=NORESULT): - assert outcome in repevent.validoutcomes - if excinfo is not None and excinfo.errisinstance((Skipped, Skipped2)): - assert outcome.split('_')[0] == "failed" - outcome = "skipped" - self.item = item - self.outcome = outcome - self.excinfo = excinfo - self.outerr = outerr - self.result = result - def hasresult(self): - return self.result is not NORESULT - - def repr_run(self): - return pypresent.python_repr_run(self, - title=self.outcome.replace("_", " ")) - -def basic_collect(collector, getcapture): - return CollectorRunner(collector, getcapture).run() - -def basic_run_report(item, setupstate, getcapture, pdb=None): - runinfo = ItemRunner(item, setupstate, getcapture).run() - if pdb is not None and runinfo.outcome in ('failed', 'failed_setup'): - pdb(runinfo) - return makereport(runinfo) + +class OutcomeRepr(object): + def __init__(self, when, shortrepr, longrepr, exconly): + self.when = when + self.shortrepr = shortrepr + self.longrepr = longrepr + self.exconly = exconly + def __str__(self): + return " Author: hpk Date: Tue Aug 5 18:28:28 2008 New Revision: 57008 Modified: py/branch/event/py/test2/repevent.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_session.py Log: remove duplicate CollectionFinish event Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Tue Aug 5 18:28:28 2008 @@ -33,18 +33,6 @@ self.session = session self.timeend = time.time() -# ---------------------------------------------------------------------- -# Events related to collecting Collectors -# ---------------------------------------------------------------------- - -class CollectionStart(BaseEvent): - def __init__(self, collector): - self.collector = collector - -class CollectionFinish(BaseEvent): - def __init__(self, collector, runinfo=None): - self.collector = collector - self.runinfo = runinfo # ---------------------------------------------------------------------- # Events related to collecting and executing test Items @@ -72,12 +60,15 @@ class ItemTestReport(BaseReport): """ Test Execution Report. """ +class CollectionStart(BaseEvent): + def __init__(self, collector): + self.collector = collector + class CollectionReport(BaseReport): """ Collection Report. """ def __init__(self, colitem, result, **kwargs): super(CollectionReport, self).__init__(colitem, **kwargs) self.result = result - # ---------------------------------------------------------------------- # Distributed Testing Events Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Tue Aug 5 18:28:28 2008 @@ -62,9 +62,7 @@ if ev.passed: for x in self.genitems(ev.result, keywordexpr): yield x - self.bus.notify(repevent.CollectionFinish(next)) - else: - self.bus.notify(ev) + self.bus.notify(ev) def collect(self): colitems = [self.config.getfsnode(arg) for arg in self.config.args] Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Tue Aug 5 18:28:28 2008 @@ -204,14 +204,13 @@ assert len(itemstarted) == 3 assert not sorter.get(repevent.ItemTestReport) started = sorter.get(repevent.CollectionStart) - colnotpassed = sorter.get(repevent.CollectionReport) - finished = sorter.get(repevent.CollectionFinish) + \ - sorter.get(repevent.CollectionReport) + finished = sorter.get(repevent.CollectionReport) assert len(started) == len(finished) assert len(started) == 8 - assert len(colnotpassed) == 2 - assert colnotpassed[0].failed - assert colnotpassed[1].skipped + colfail = [x for x in finished if x.failed] + colskipped = [x for x in finished if x.skipped] + assert len(colfail) == 1 + assert len(colskipped) == 1 def test_pdb_run(self): tfile = suptest.makeuniquepyfile(""" From hpk at codespeak.net Tue Aug 5 19:39:14 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 5 Aug 2008 19:39:14 +0200 (CEST) Subject: [py-svn] r57011 - in py/branch/event/py/test2: . rep rep/testing testing Message-ID: <20080805173914.66C7A168054@codespeak.net> Author: hpk Date: Tue Aug 5 19:39:13 2008 New Revision: 57011 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py Log: various fixes, making skip reporting work Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Tue Aug 5 19:39:13 2008 @@ -42,7 +42,7 @@ col.teardown() def teardown_exact(self, item): - if self.stack[-1] == item: + if self.stack and self.stack[-1] == item: col = self.stack.pop() col.teardown() Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Tue Aug 5 19:39:13 2008 @@ -31,4 +31,19 @@ self._passed.append(ev) def rep_CollectionReport(self, ev): - return BaseReporter.rep_ItemTestReport(self, ev) + if ev.skipped: + self._skipped.append(ev) + elif ev.failed: + self._failed.append(ev) + else: + pass # don't record passed collections + + def _folded_skips(self): + d = {} + for event in self._skipped: + key = event.outcome.where + (event.outcome.exconly,) + d.setdefault(key, []).append(event) + l = [] + for key, events in d.iteritems(): + l.append((len(events),) + key) + return l Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Tue Aug 5 19:39:13 2008 @@ -42,11 +42,7 @@ def rep_SessionFinish(self, session): from py.__.code.tbpresent import TBPresenter - tb = TBPresenter(out=self.out, showlocals=self.config.option.showlocals) for ev in self._failed: - if hasattr(ev.outcome.longrepr, 'terminalwrite'): - ev.outcome.longrepr.terminalwrite(self.out) - else: - self.out.line(str(ev.outcome.longrepr)) + ev.writelongrepr(self.out) Reporter = CollectonlyReporter Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Tue Aug 5 19:39:13 2008 @@ -32,16 +32,16 @@ self.currentfspath = fspath def rep_SessionStart(self, ev): - self.out.sep("=", "session starts") + self.out.sep("=", "test session starts") def rep_SessionFinish(self, ev): self.out.line("") for ev in self._failed: - longrepr = ev.failed.longrepr - if hasattr(longrepr, 'terminalwrite'): - longrepr.terminalwrite(self.out) - else: - self.out.line(str(longrepr)) + ev.writelongrepr(self.out) + + self.out.sep("_", "skipped test summary") + for num, fspath, lineno, reason in self._folded_skips(): + self.out.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) numfailed = len(self._failed) numskipped = len(self._skipped) numpassed = len(self._passed) @@ -49,17 +49,4 @@ self.out.sep("=", "%d tests of which %d failed, %d skipped" %( sum, numfailed, numskipped)) - def _getletter(self, ev): - if ev.passed: - return "." - elif ev.skipped: - return "s" - elif ev.failed_setup: - return "f" - elif ev.failed_crashed: - return "C" - elif ev.failed: - return "F" - - Reporter = TerminalReporter Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Tue Aug 5 19:39:13 2008 @@ -2,6 +2,7 @@ from py.__.test2.rep.base import BaseReporter from py.__.test2.eventbus import EventBus from py.__.test2 import repevent +from py.__.test2.runner import OutcomeRepr class TestBaseReporter: def test_activate(self): @@ -43,24 +44,27 @@ assert getattr(rep, '_' + outcome) == [ev] def test_CollectionReport(self): - for outcome in 'passed skipped failed'.split(): + for outcome in 'skipped failed'.split(): rep = BaseReporter() ev = repevent.CollectionReport(None, None, **{outcome:True}) rep.processevent(ev) assert getattr(rep, '_' + outcome) == [ev] def test_skip_reasons(self): - py.test.skip("unify ItemTestReport and CollectionReport first") rep = BaseReporter() - reprpath = ('xyz', 3, None) - reprun = 'hello' # see XXX - ev1 = repevent.CollectionReport(None, "skipped", reprrun, reprpath) - ev2 = repevent.ItemTestReport(None, "skipped", reprrun, reprpath) + reprpath = ('xyz', 3) + exconly = "justso" + out1 = OutcomeRepr(None, None, None, exconly, where=reprpath) + out2 = OutcomeRepr(None, None, None, exconly, where=reprpath) + ev1 = repevent.CollectionReport(None, None, skipped=out1) + ev2 = repevent.ItemTestReport(None, skipped=out2) + rep.processevent(ev1) + rep.processevent(ev2) + assert len(rep._skipped) == 2 l = rep._folded_skips() assert len(l) == 1 - num, loc, skip_reason = l[0] + num, fspath, lineno, reason = l[0] assert num == 2 - assert loc == repr_path[:2] - # XXX how to assert about the reason? - #assert skip_reason == reprrun? - + assert fspath == reprpath[0] + assert lineno == reprpath[1] + assert reason == exconly Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Tue Aug 5 19:39:13 2008 @@ -56,6 +56,13 @@ name, value = kwargs.items()[0] setattr(self, name, value) self.outcome = value + + def writelongrepr(self, out): + longrepr = self.failed.longrepr + if hasattr(longrepr, 'terminalwrite'): + longrepr.terminalwrite(self.out) + else: + out.line(str(longrepr)) class ItemTestReport(BaseReport): """ Test Execution Report. """ Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Aug 5 19:39:13 2008 @@ -52,7 +52,11 @@ def execute(self): self.item.execute() def makereport(self, res, when, excinfo, outerr): + where = None if excinfo: + excinfo.traceback = self.item.prunetraceback(excinfo.traceback) + entry = excinfo.traceback.getcrashentry() + where = entry.path, entry.lineno if excinfo.errisinstance((Skipped,Skipped2)): outcome = "skipped" shortrepr = "s" @@ -71,7 +75,7 @@ shortrepr = "." longrepr = "" exconly = "" - kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly)} + kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly, where)} ev = repevent.ItemTestReport(self.item, **kw) if self.pdb and kw['failed']: self.pdb(excinfo) @@ -90,6 +94,9 @@ def makereport(self, res, when, excinfo, outerr): if excinfo: exconly = excinfo.exconly(tryshort=True) + excinfo.traceback = self.col.prunetraceback(excinfo.traceback) + entry = excinfo.traceback.getcrashentry() + where = entry.path, entry.lineno if excinfo.errisinstance((Skipped,Skipped2)): outcome = "skipped" shortrepr = "s" @@ -102,7 +109,8 @@ else: outcome = "passed" shortrepr = longrepr = exconly = "" - kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly)} + where = None + kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly, where)} return repevent.CollectionReport(self.col, res, **kw) NORESULT = object() @@ -111,11 +119,12 @@ # class OutcomeRepr(object): - def __init__(self, when, shortrepr, longrepr, exconly): + def __init__(self, when, shortrepr, longrepr, exconly, where): self.when = when self.shortrepr = shortrepr self.longrepr = longrepr self.exconly = exconly + self.where = where def __str__(self): return " Author: hpk Date: Tue Aug 5 19:57:03 2008 New Revision: 57013 Modified: py/branch/event/py/test2/runner.py Log: share reporting code between events Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Aug 5 19:57:03 2008 @@ -16,6 +16,11 @@ class RobustRun: """ a robust setup/execute/teardown protocol. """ + def __init__(self, colitem, pdb=None): + self.colitem = colitem + self.getcapture = colitem._config._getcapture + self.pdb = pdb + def run(self): """ return result of running setup, execution, teardown procedures. """ excinfo = None @@ -39,79 +44,55 @@ excinfo = py.code.ExceptionInfo() return self.makereport(res, when, excinfo, outerr) + def getkw(self, when, excinfo, outerr): + exconly = excinfo.exconly(tryshort=True) + excinfo.traceback = self.colitem.prunetraceback(excinfo.traceback) + entry = excinfo.traceback.getcrashentry() + where = entry.path, entry.lineno + if excinfo.errisinstance((Skipped,Skipped2)): + outcome = "skipped" + shortrepr = "s" + longrepr = "" + else: + outcome = "failed" + if when == "execute": + longrepr = self.colitem.getfailurerepr(excinfo, outerr) + shortrepr = self.colitem.shortfailurerepr + else: + longrepr = self.colitem._getfailurerepr_py(excinfo, outerr) + shortrepr = self.colitem.shortfailurerepr.lower() + kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly, where)} + return kw + class ItemRunner(RobustRun): - def __init__(self, item, pdb=None): - self.item = item - self.setupstate = item._setupstate - self.getcapture = item._config._getcapture - self.pdb = pdb def setup(self): - self.setupstate.prepare(self.item) + self.colitem._setupstate.prepare(self.colitem) def teardown(self): - self.setupstate.teardown_exact(self.item) + self.colitem._setupstate.teardown_exact(self.colitem) def execute(self): - self.item.execute() + self.colitem.execute() def makereport(self, res, when, excinfo, outerr): - where = None if excinfo: - excinfo.traceback = self.item.prunetraceback(excinfo.traceback) - entry = excinfo.traceback.getcrashentry() - where = entry.path, entry.lineno - if excinfo.errisinstance((Skipped,Skipped2)): - outcome = "skipped" - shortrepr = "s" - longrepr = "" - else: - outcome = "failed" - if when == "execute": - longrepr = self.item.getfailurerepr(excinfo, outerr) - shortrepr = self.item.shortfailurerepr - else: - longrepr = self.item._getfailurerepr_py(excinfo, outerr) - shortrepr = self.item.shortfailurerepr.lower() - exconly = excinfo.exconly(tryshort=True) + kw = self.getkw(when, excinfo, outerr) + if self.pdb and kw['failed']: + self.pdb(excinfo) else: - outcome = "passed" - shortrepr = "." - longrepr = "" - exconly = "" - kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly, where)} - ev = repevent.ItemTestReport(self.item, **kw) - if self.pdb and kw['failed']: - self.pdb(excinfo) - return ev + kw = {'passed': OutcomeRepr(when, '.', "", "", None)} + return repevent.ItemTestReport(self.colitem, **kw) class CollectorRunner(RobustRun): - def __init__(self, col): - self.col = col - self.getcapture = col._config._getcapture def setup(self): pass def teardown(self): pass def execute(self): - return [self.col.join(x) for x in self.col.listdir()] + return [self.colitem.join(x) for x in self.colitem.listdir()] def makereport(self, res, when, excinfo, outerr): if excinfo: - exconly = excinfo.exconly(tryshort=True) - excinfo.traceback = self.col.prunetraceback(excinfo.traceback) - entry = excinfo.traceback.getcrashentry() - where = entry.path, entry.lineno - if excinfo.errisinstance((Skipped,Skipped2)): - outcome = "skipped" - shortrepr = "s" - longrepr = "" - else: - outcome = "failed" - assert when == "execute" - longrepr = self.col.getfailurerepr(excinfo, outerr) - shortrepr = "f" + kw = self.getkw(when, excinfo, outerr) else: - outcome = "passed" - shortrepr = longrepr = exconly = "" - where = None - kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly, where)} - return repevent.CollectionReport(self.col, res, **kw) + kw = {'passed': OutcomeRepr(when, '', "", "", None)} + return repevent.CollectionReport(self.colitem, res, **kw) NORESULT = object() # From hpk at codespeak.net Tue Aug 5 21:25:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 5 Aug 2008 21:25:22 +0200 (CEST) Subject: [py-svn] r57014 - in py/branch/event/py/test2: . rep rep/testing testing Message-ID: <20080805192522.1EAF5169F84@codespeak.net> Author: hpk Date: Tue Aug 5 21:25:20 2008 New Revision: 57014 Modified: py/branch/event/py/test2/defaultconftest.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_session.py Log: * get rid of DeselectedItem event in favour of using normal "skips" * introduce --showskipsummary so that by default the skip summary is not shown Modified: py/branch/event/py/test2/defaultconftest.py ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test2/defaultconftest.py Tue Aug 5 21:25:20 2008 @@ -49,6 +49,9 @@ Option('-l', '--showlocals', action="store_true", dest="showlocals", default=False, help="show locals in tracebacks (disabled by default)."), + Option('--showskipsummary', + action="store_true", dest="showskipsummary", default=False, + help="show summary of skip reasons and locations (disabled by default)."), Option('', '--pdb', action="store_true", dest="usepdb", default=False, help="start pdb (the Python debugger) on errors."), Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Tue Aug 5 21:25:20 2008 @@ -121,6 +121,7 @@ def setup(self): if not self._config.option.nomagic: + #print "*" * 20, "INVOKE assertion", self py.magic.invoke(assertion=1) if hasattr(self.obj, 'setup_module'): self.obj.setup_module(self.obj) @@ -129,6 +130,7 @@ if hasattr(self.obj, 'teardown_module'): self.obj.teardown_module(self.obj) if not self._config.option.nomagic: + #print "*" * 20, "revoke assertion", self py.magic.revoke(assertion=1) class Class(PyCollectorMixin, Collector): Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Tue Aug 5 21:25:20 2008 @@ -28,10 +28,6 @@ def rep_ItemStart(self, event): self.outindent(event.item) - def rep_DeselectedItem(self, event): - msg = "DESELECTED: " - self.outindent(msg + str(event.item)) - def rep_CollectionReport(self, ev): super(CollectonlyReporter, self).rep_CollectionReport(ev) if ev.failed: Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Tue Aug 5 21:25:20 2008 @@ -39,9 +39,13 @@ for ev in self._failed: ev.writelongrepr(self.out) - self.out.sep("_", "skipped test summary") - for num, fspath, lineno, reason in self._folded_skips(): - self.out.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) + if self.config.option.showskipsummary: + folded_skips = self._folded_skips() + if folded_skips: + self.out.sep("_", "skipped test summary") + for num, fspath, lineno, reason in folded_skips: + self.out.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) + numfailed = len(self._failed) numskipped = len(self._skipped) numpassed = len(self._passed) Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Tue Aug 5 21:25:20 2008 @@ -48,10 +48,6 @@ rep.processevent(repevent.ItemStart(item)) s = popvalue(stringio) assert s == " " - rep.processevent(repevent.DeselectedItem(item, None)) - s = popvalue(stringio) - assert s == " DESELECTED: " - rep.processevent(repevent.CollectionReport(modcol, [], passed="")) assert rep.indent == indent Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Tue Aug 5 21:25:20 2008 @@ -43,11 +43,6 @@ self.item = item self.time = timestamp() -class DeselectedItem(BaseEvent): - def __init__(self, item, keywordexpr): - self.item = item - self.keywordexpr = keywordexpr - class BaseReport(BaseEvent): failed = passed = skipped = None def __init__(self, colitem, **kwargs): Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Aug 5 21:25:20 2008 @@ -14,7 +14,7 @@ import py.__.test2.custompdb from py.__.test2 import pypresent -class RobustRun: +class RobustRun(object): """ a robust setup/execute/teardown protocol. """ def __init__(self, colitem, pdb=None): self.colitem = colitem @@ -94,6 +94,20 @@ kw = {'passed': OutcomeRepr(when, '', "", "", None)} return repevent.CollectionReport(self.colitem, res, **kw) +class SkipRunner(RobustRun): + def __init__(self, colitem, msg): + super(SkipRunner, self).__init__(colitem) + self.msg = msg + def setup(self): pass + def execute(self): + __tracebackhide__ = True + py.test2.skip(self.msg) + def teardown(self): pass + def makereport(self, res, when, excinfo, outerr): + assert excinfo + kw = self.getkw(when, excinfo, outerr) + return repevent.ItemTestReport(self.colitem, **kw) + NORESULT = object() # # public entrypoints / objects @@ -110,6 +124,10 @@ return " Author: hpk Date: Tue Aug 5 21:50:37 2008 New Revision: 57015 Modified: py/branch/event/py/test2/defaultconftest.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/testing/acceptance_test.py Log: * always show skip summary if all tests passed * use relative paths for reporting Modified: py/branch/event/py/test2/defaultconftest.py ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test2/defaultconftest.py Tue Aug 5 21:50:37 2008 @@ -51,7 +51,7 @@ help="show locals in tracebacks (disabled by default)."), Option('--showskipsummary', action="store_true", dest="showskipsummary", default=False, - help="show summary of skip reasons and locations (disabled by default)."), + help="always show summary of skipped tests"), Option('', '--pdb', action="store_true", dest="usepdb", default=False, help="start pdb (the Python debugger) on errors."), Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Tue Aug 5 21:50:37 2008 @@ -4,12 +4,14 @@ from py.__.test2.rep.base import BaseReporter from py.__.test2.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped +from py.__.test2.pypresent import getrelpath class TerminalReporter(BaseReporter): def __init__(self, config, file=None): super(TerminalReporter, self).__init__() self.config = config self.currentfspath = None + self.curdir = py.path.local() if file is None: file = py.std.sys.stdout self.out = py.io.TerminalWriter(file) @@ -18,8 +20,9 @@ super(TerminalReporter, self).rep_ItemTestReport(ev) fspath = ev.colitem.fspath if fspath != self.currentfspath: + relpath = getrelpath(self.curdir, fspath) self.out.line() - self.out.write(fspath + " ") + self.out.write(relpath + " ") self.currentfspath = fspath self.out.write(ev.outcome.shortrepr) @@ -28,7 +31,8 @@ fspath = ev.colitem.fspath if ev.failed or ev.skipped: self.out.line() - self.out.write(fspath + " - " + str(ev.outcome.exconly)) + relpath = getrelpath(self.curdir, fspath) + self.out.write(relpath + " - " + str(ev.outcome.exconly)) self.currentfspath = fspath def rep_SessionStart(self, ev): @@ -39,7 +43,7 @@ for ev in self._failed: ev.writelongrepr(self.out) - if self.config.option.showskipsummary: + if not self._failed or self.config.option.showskipsummary: folded_skips = self._folded_skips() if folded_skips: self.out.sep("_", "skipped test summary") Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Tue Aug 5 21:50:37 2008 @@ -105,7 +105,7 @@ def doskip(): py.test.skip('test') """) - result = self.runpytest("--showskipsummary") + result = self.runpytest() extra = assert_lines_contain_lines(result.outlines, [ "*test_one.py ss", "*test_two.py - Skipped*", @@ -113,6 +113,41 @@ "*conftest.py:3: *3* Skipped: 'test'", ]) + def test_no_skip_summary_if_failure(self): + p1 = self.makepyfile(test_one=""" + import py + def test_ok(): + pass + def test_fail(): + assert 0 + def test_skip(): + py.test2.skip("dontshow") + """) + result = self.runpytest() + extra = assert_lines_contain_lines(result.outlines, [ + "*test_one.py .Fs", + "=* 3 tests of which 1 failed, 1 skipped *=" + ]) + assert str(extra).find("skip test summary") == -1 + + def test_passes(self): + p1 = self.makepyfile(test_one=""" + def test_passes(): + pass + class TestClass: + def test_method(self): + pass + """) + old = p1.dirpath().chdir() + try: + result = self.runpytest() + finally: + old.chdir() + extra = assert_lines_contain_lines(result.outlines, [ + "test_one.py ..", + "===* 2 tests of which 0 failed, 0 skipped *===" + ]) + def test_looponfailing_looping(self): py.test.skip("thought needed to nicely test --looponfailing") py.test.skip("xxx check events") From hpk at codespeak.net Tue Aug 5 21:59:07 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 5 Aug 2008 21:59:07 +0200 (CEST) Subject: [py-svn] r57016 - in py/branch/event/py/test2/rep: . testing Message-ID: <20080805195907.D38BD169F50@codespeak.net> Author: hpk Date: Tue Aug 5 21:59:06 2008 New Revision: 57016 Removed: py/branch/event/py/test2/rep/reporter.py py/branch/event/py/test2/rep/testing/test_reporter.py Log: remove old reporter logic and tests Deleted: /py/branch/event/py/test2/rep/reporter.py ============================================================================== --- /py/branch/event/py/test2/rep/reporter.py Tue Aug 5 21:59:06 2008 +++ (empty file) @@ -1,447 +0,0 @@ - -""" reporter - different reporter for different purposes ;-) - Still lacks: - - 1. Hanging nodes are not done well -""" - -import py - -from py.__.test2 import repevent -from py.__.test2 import outcome - -import sys - -from time import time as now - - -def choose_reporter(reporterclass, config): - option = config.option - if option.startserver or option.runbrowser: - from py.__.test2.rsession.web import WebReporter - return WebReporter - if option.restreport: - from py.__.test2.rsession.rest import RestReporter - return RestReporter - else: - return reporterclass - -# -# -# -# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted -# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted -# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted -# XXXXXXXXXXXXXXXXXXXXXXXXXXXXX below is to be deleted -# -# - - -class TestReporter(object): - """ Simple test reporter which tracks failures - and also calls arbitrary provided function, - useful for tests - """ - def __init__(self, reportfun): - self.reportfun = reportfun - self.flag = False - - def report(self, event): - #if event.is_failure(): - # self.flag = True - self.reportfun(event) - - __call__ = report - - def was_failure(self): - return self.flag - -class AbstractReporter(object): - def __init__(self, config, hosts): - self.config = config - self.hosts = hosts - self.failed_tests_outcome = [] - self.skipped_tests_outcome = [] - self.out = getout(py.std.sys.stdout) - self.presenter = Presenter(self.out, config) - self.to_rsync = {} - - def get_item_name(self, event, colitem): - return "/".join(colitem.listnames()) - - def report(self, what): - repfun = getattr(self, "report_" + what.__class__.__name__, - self.report_unknown) - try: - return repfun(what) - except (KeyboardInterrupt, SystemExit): - raise - except: - print "Internal reporting problem" - excinfo = py.code.ExceptionInfo() - for i in excinfo.traceback: - print str(i)[2:-1] - print excinfo - # XXX reenable test before removing below line and - # run it with raise - raise - - __call__ = report - - def report_unknown(self, what): - if self.config.option.verbose: - print "Unknown report: %s" % what - - def report_SendItem(self, item): - address = item.host.hostname - assert isinstance(item.host.hostname, str) - if self.config.option.verbose: - print "Sending %s to %s" % (item.item, - address) - - def report_HostRSyncing(self, item): - hostrepr = self._hostrepr(item.host) - if item.synced: - if (item.host.hostname == "localhost" and - item.root == item.remotepath): - print "%15s: skipping inplace rsync of %r" %( - hostrepr, item.remotepath) - else: - print "%15s: skip duplicate rsync to %r" % ( - hostrepr, item.remotepath) - else: - print "%15s: rsync %r to remote %r" % (hostrepr, - item.root.basename, - item.remotepath) - - def report_HostGatewayReady(self, item): - self.to_rsync[item.host] = len(item.roots) - hostrepr = self._hostrepr(item.host) - self.out.write("%15s: gateway initialised (remote topdir: %s)\n"\ - % (hostrepr, item.host.gw_remotepath)) - - def report_HostRSyncRootReady(self, item): - self.to_rsync[item.host] -= 1 - if not self.to_rsync[item.host]: - self._host_ready(item) - - def _host_ready(self, item): - self.hosts_to_rsync -= 1 - hostrepr = self._hostrepr(item.host) - if self.hosts_to_rsync: - print "%15s: READY (still %d to go)" % (hostrepr, - self.hosts_to_rsync) - else: - print "%15s: READY" % hostrepr - - def report_TestStarted(self, item): - topdir = item.config.topdir - hostreprs = [self._hostrepr(host) for host in item.hosts] - txt = " Test started, hosts: %s " % ", ".join(hostreprs) - self.hosts_to_rsync = len(item.hosts) - self.out.sep("=", txt) - self.timestart = item.timestart - self.out.write("local top directory: %s\n" % topdir) - for i, root in py.builtin.enumerate(item.roots): - outof = "%d/%d" %(i+1, len(item.roots)) - self.out.write("local RSync root [%s]: %s\n" % - (outof, root)) - - def report_RsyncFinished(self, item): - self.timersync = item.time - - def report_ImmediateFailure(self, event): - self.out.line() - self.repr_failure(event.item, event.outcome) - - def report_SessionFinish(self, item): - self.out.line() - assert hasattr(self, 'timestart') - self.timeend = item.timeend - self.skips() - self.failures() - if hasattr(self, 'nodes'): # XXX: Testing - self.hangs() - self.summary() - return len(self.failed_tests_outcome) > 0 - - report_InterruptedExecution = report_SessionFinish - report_CrashedExecution = report_SessionFinish - - def hangs(self): - h = [] - if self.config.option.exitfirst: - # reporting hanging nodes in that case makes no sense at all - # but we should share some code in all reporters than - return - for node in self.nodes: - h += [(i, node.channel.gateway.sshaddress) for i in node.pending] - if h: - self.out.sep("=", " HANGING NODES ") - for i, node in h: - self.out.line("%s on %s" % (" ".join(i.listnames()), node)) - - def failures(self): - if self.failed_tests_outcome: - self.out.sep("=", " FAILURES ") - for event in self.failed_tests_outcome: - if isinstance(event, repevent.ItemFinish): - host = self.gethost(event) - self.out.sep('_', "%s on %s" % - (" ".join(event.item.listnames()), host)) - if event.outcome.signal: - self.presenter.repr_item_info(event.item) - self.repr_signal(event.item, event.outcome) - else: - self.repr_failure(event.item, event.outcome) - else: - self.out.sep('_', " ".join(event.item.listnames())) - out = outcome.SerializableOutcome(excinfo=event.excinfo) - self.repr_failure(event.item, outcome.ReprOutcome(out.make_repr())) - - def gethost(self, event): - return event.host.hostname - - def repr_failure(self, item, outcome): - excinfo = outcome.excinfo - traceback = excinfo.traceback - if not traceback: - self.out.line("empty traceback from item %r" % (item,)) - return - - handler = getattr(self.presenter, 'repr_failure_tb%s' % self.config.option.tbstyle) - handler(item, excinfo, traceback, lambda: self.repr_out_err(outcome)) - - def repr_out_err(self, outcome): - if outcome.stdout: - self.out.sep('-', " Captured process stdout: ") - self.out.write(outcome.stdout) - if outcome.stderr: - self.out.sep('-', " Captured process stderr: ") - self.out.write(outcome.stderr) - - def repr_signal(self, item, outcome): - signal = outcome.signal - self.out.line("Received signal: %d" % outcome.signal) - self.repr_out_err(outcome) - - def _hostrepr(self, host): - return host.hostid - - def skips(self): - # XXX review and test below, fix too many lines of - # skips that happen in the same file/lineno - # (this is correct in py-dist) - texts = {} - for event in self.skipped_tests_outcome: - colitem = event.item - if isinstance(event, repevent.ItemFinish): - outcome = event.outcome - text = outcome.skipped.value - itemname = repr(outcome.skipped.traceback[-2]).split("\n")[0] - elif isinstance(event, repevent.DeselectedItem): - text = str(event.excinfo.value) - itemname = "/".join(colitem.listnames()) - if text not in texts: - texts[text] = [itemname] - else: - texts[text].append(itemname) - - if texts: - self.out.line() - self.out.sep('_', 'reasons for skipped tests') - for text, items in texts.items(): - for item in items: - self.out.line('Skipped in %s' % item) - self.out.line("reason: %s" % text[1:-1]) - self.out.line() - - def summary(self): - def gather(dic): - # XXX hack to handle dicts & ints here, get rid of it - if isinstance(dic, int): - return dic - total = 0 - for key, val in dic.iteritems(): - total += val - return total - - def create_str(name, count): - if count: - return ", %d %s" % (count, name) - return "" - - total_passed = gather(self.passed) - total_failed = gather(self.failed) - total_skipped = gather(self.skipped) - total = total_passed + total_failed + total_skipped - skipped_str = create_str("skipped", total_skipped) - failed_str = create_str("failed", total_failed) - self.out.line() - self.print_summary(total, skipped_str, failed_str) - - def print_summary(self, total, skipped_str, failed_str): - self.out.sep("=", " %d test run%s%s in %.2fs (rsync: %.2f)" % - (total, skipped_str, failed_str, self.timeend - self.timestart, - self.timersync - self.timestart)) - - def report_DeselectedItem(self, event): - #event.outcome.excinfo.source = - self.skipped_tests_outcome.append(event) - - def report_FailedTryiter(self, event): - pass - # XXX: right now we do not do anything with it - - def report_ItemFinish(self, event): - host = event.host - hostrepr = self._hostrepr(host) - if event.outcome.passed: - self.passed[host] += 1 - sys.stdout.write("%15s: PASSED " % hostrepr) - elif event.outcome.skipped: - self.skipped_tests_outcome.append(event) - self.skipped[host] += 1 - sys.stdout.write("%15s: SKIPPED " % hostrepr) - else: - self.failed[host] += 1 - self.failed_tests_outcome.append(event) - sys.stdout.write("%15s: " % hostrepr) - ansi_print("FAILED", esc=(31,1), newline=False, file=sys.stdout) - sys.stdout.write(" ") - # we should have printed 20 characters to this point - itempath = ".".join(event.item.listnames()[1:-1]) - funname = event.item.listnames()[-1] - lgt = get_terminal_width() - 20 - # mark the function name, to be sure - to_display = len(itempath) + len(funname) + 1 - if to_display > lgt: - sys.stdout.write("..." + itempath[to_display-lgt+4:]) - else: - sys.stdout.write(itempath) - sys.stdout.write(" ") - ansi_print(funname, esc=32, file=sys.stdout) - - def report_Nodes(self, event): - self.nodes = event.nodes - - def was_failure(self): - return sum(self.failed.values()) > 0 - -class RemoteReporter(AbstractReporter): - def __init__(self, config, hosts): - super(RemoteReporter, self).__init__(config, hosts) - self.failed = dict([(host, 0) for host in hosts]) - self.skipped = dict([(host, 0) for host in hosts]) - self.passed = dict([(host, 0) for host in hosts]) - - def get_item_name(self, event, colitem): - return event.host.hostname + ":" + \ - "/".join(colitem.listnames()) - - def report_FailedTryiter(self, event): - self.out.line("FAILED TO LOAD MODULE: %s\n" % "/".join(event.item.listnames())) - self.failed_tests_outcome.append(event) - # argh! bad hack, need to fix it - self.failed[self.hosts[0]] += 1 - - def report_DeselectedItem(self, event): - self.out.line("Skipped (%s) %s\n" % (str(event.excinfo.value), "/". - join(event.item.listnames()))) - -class LocalReporter(AbstractReporter): - def __init__(self, config, hosts=None): - assert not hosts - super(LocalReporter, self).__init__(config, hosts) - self.failed = 0 - self.skipped = 0 - self.passed = 0 - - def report_TestStarted(self, item): - colitems = item.config.getcolitems() - txt = " test process starts " - self.out.sep("=", txt) - self.timestart = item.timestart - self.out.line("executable: %s (%s)" % - (py.std.sys.executable, repr_pythonversion())) - rev = py.__package__.getrev() - self.out.line("using py lib: %s " % ( - py.path.local(py.__file__).dirpath(), rev)) - config = item.config - if config.option.traceconfig or config.option.verbose: - - for x in colitems: - self.out.line("test target: %s" %(x.fspath,)) - - conftestmodules = config._conftest.getconftestmodules(None) - for i,x in py.builtin.enumerate(conftestmodules): - self.out.line("initial conf %d: %s" %(i, x.__file__)) - - def get_item_name(self, event, colitem): - return "/".join(colitem.listnames()) - - def print_summary(self, total, skipped_str, failed_str): - self.out.sep("=", " %d test run%s%s in %.2fs" % - (total, skipped_str, failed_str, self.timeend - self.timestart)) - - def report_DeselectedItem(self, event): - #self.show_item(event.item, False) - if isinstance(event.item, py.test2.collect.Module): - self.out.write("- skipped (%s)" % event.excinfo.value) - else: - self.out.write("s") - self.skipped_tests_outcome.append(event) - - def report_FailedTryiter(self, event): - #self.show_item(event.item, False) - self.out.write("- FAILED TO LOAD MODULE") - self.failed_tests_outcome.append(event) - self.failed += 1 - - def report_ItemFinish(self, event): - if event.outcome.passed: - self.passed += 1 - self.out.write(".") - elif event.outcome.skipped: - self.skipped_tests_outcome.append(event) - self.skipped += 1 - self.out.write("s") - else: - self.failed += 1 - self.failed_tests_outcome.append(event) - self.out.write("F") - - def report_ItemStart(self, event): - # XXX - event.item.start = now() - self.show_item(event.item) - - def show_item(self, item, count_elems = True): - if isinstance(item, py.test2.collect.Module): - self.show_Module(item) - if self.config.option.verbose > 0 and\ - isinstance(item, py.test2.collect.Item): - self.show_ItemVerbose(item) - - def show_ItemVerbose(self, item): - realpath, lineno = item._getpathlineno() - location = "%s:%d" % (realpath.basename, lineno+1) - self.out.write("%-20s %s " % (location, item._getmodpath())) - - def show_Module(self, mod): - lgt = len(list(mod._tryiter())) - if self.config.option.verbose == 0: - base = getrelpath(py.path.local(), mod.fspath) - self.out.write("\n%s[%d] " % (base, lgt)) - else: - self.out.line() - self.out.line('+ testmodule: %s[%d]' % (mod.fspath, lgt)) - - def gethost(self, event): - return 'localhost' - - def hangs(self): - pass - - def was_failure(self): - return self.failed > 0 Deleted: /py/branch/event/py/test2/rep/testing/test_reporter.py ============================================================================== --- /py/branch/event/py/test2/rep/testing/test_reporter.py Tue Aug 5 21:59:06 2008 +++ (empty file) @@ -1,282 +0,0 @@ -""" reporter tests. - -XXX there are a few disabled reporting tests because -they test for exact formatting as far as i can see. -I think it's rather better to directly invoke a -reporter and pass it some hand-prepared events to see -that running the reporter doesn't break shallowly. - -Otherwise, i suppose that some "visual" testing can usually be driven -manually by user-input. And when passing particular events -to a reporter it's also easier to check for one line -instead of having to know the order in which things are printed -etc. - - -""" - - -import py, os - -py.test.skip("rewrite reporter tests completely, take existing as hints") - -from py.__.test2.session import AbstractSession, itemgen -from py.__.test2.reporter import RemoteReporter, LocalReporter, choose_reporter -from py.__.test2 import repevent -from py.__.test2.outcome import ReprOutcome, SerializableOutcome -from py.__.test2.rsession.hostmanage import Host -from py.__.test2.box import Box -from py.__.test2.rsession.testing.basetest import BasicRsessionTest -import sys -from StringIO import StringIO - -class MockSession(object): - def __init__(self, reporter): - self.reporter = reporter - - def start(self, item): - self.reporter(repevent.ItemStart(item)) - - def finish(self, item): - pass - -class DummyGateway(object): - def __init__(self, host): - self.host = host - -class DummyChannel(object): - def __init__(self, host): - self.gateway = DummyGateway(host) - -class AbstractTestReporter(BasicRsessionTest): - def prepare_outcomes(self): - # possible outcomes - try: - 1/0 - except: - exc = py.code.ExceptionInfo() - - try: - py.test.skip("xxx") - except: - skipexc = py.code.ExceptionInfo() - - outcomes = [SerializableOutcome(()), - SerializableOutcome(skipped=skipexc), - SerializableOutcome(excinfo=exc), - SerializableOutcome()] - - outcomes = [ReprOutcome(outcome.make_repr()) for outcome in outcomes] - outcomes[3].signal = 11 - outcomes[0].passed = False - - return outcomes - - def report_received_item_outcome(self): - item = self.getexample("pass") - outcomes = self.prepare_outcomes() - - def boxfun(config, item, outcomes): - hosts = self.get_hosts() - r = self.reporter(config, hosts) - if hosts: - ch = DummyChannel(hosts[0]) - else: - ch = None - for outcome in outcomes: - r.report(repevent.ItemFinish(ch, item, outcome)) - - cap = py.io.StdCaptureFD() - boxfun(self.config, item, outcomes) - out, err = cap.reset() - assert not err - return out - - def _test_module(self): - funcitem = self.getexample("pass") - moditem = self.getmod() - outcomes = self.prepare_outcomes() - - def boxfun(config, item, funcitem, outcomes): - hosts = self.get_hosts() - r = self.reporter(config, hosts) - r.report(repevent.ItemStart(item)) - if hosts: - ch = DummyChannel(hosts[0]) - else: - ch = None - for outcome in outcomes: - r.report(repevent.ItemFinish(ch, funcitem, outcome)) - - cap = py.io.StdCaptureFD() - boxfun(self.config, moditem, funcitem, outcomes) - out, err = cap.reset() - assert not err - return out - - def _test_full_module(self): - tmpdir = py.test2.ensuretemp("repmod") - tmpdir.ensure("__init__.py") - tmpdir.ensure("test_one.py").write(py.code.Source(""" - def test_x(): - pass - """)) - tmpdir.ensure("test_two.py").write(py.code.Source(""" - import py - py.test.skip("reason") - """)) - tmpdir.ensure("test_three.py").write(py.code.Source(""" - sadsadsa - """)) - - def boxfun(): - config = py.test2.config._reparse([str(tmpdir)]) - rootcol = py.test2.collect.Directory(tmpdir) - hosts = self.get_hosts() - r = self.reporter(config, hosts) - list(itemgen(MockSession(r), [rootcol], r.report)) - - cap = py.io.StdCaptureFD() - boxfun() - out, err = cap.reset() - assert not err - return out - - def test_failed_to_load(self): - tmpdir = py.test2.ensuretemp("failedtoload") - tmpdir.ensure("__init__.py") - tmpdir.ensure("test_three.py").write(py.code.Source(""" - sadsadsa - """)) - def boxfun(): - config = py.test2.config._reparse([str(tmpdir)]) - rootcol = py.test2.collect.Directory(tmpdir) - hosts = self.get_hosts() - r = self.reporter(config, hosts) - r.report(repevent.TestStarted(hosts, config, ["a"])) - r.report(repevent.RsyncFinished()) - list(itemgen(MockSession(r), [rootcol], r.report)) - r.report(repevent.TestSessionFinish()) - return r - - cap = py.io.StdCaptureFD() - r = boxfun() - out, err = cap.reset() - assert not err - assert out.find("1 failed in") != -1 - assert out.find("NameError: name 'sadsadsa' is not defined") != -1 - - def _test_verbose(self): - tmpdir = py.test2.ensuretemp("reporterverbose") - tmpdir.ensure("__init__.py") - tmpdir.ensure("test_one.py").write("def test_x(): pass") - cap = py.io.StdCaptureFD() - config = py.test2.config._reparse([str(tmpdir), '-v']) - hosts = self.get_hosts() - r = self.reporter(config, hosts) - r.report(repevent.TestStarted(hosts, config, [])) - r.report(repevent.RsyncFinished()) - rootcol = py.test2.collect.Directory(tmpdir) - list(itemgen(MockSession(r), [rootcol], r.report)) - r.report(repevent.TestSessionFinish()) - out, err = cap.reset() - assert not err - for i in ['+ testmodule:', 'test_one.py[1]']: # XXX finish - assert i in out - - def _test_still_to_go(self): - tmpdir = py.test2.ensuretemp("stilltogo") - tmpdir.ensure("__init__.py") - cap = py.io.StdCaptureFD() - config = py.test2.config._reparse([str(tmpdir)]) - hosts = [Host(i) for i in ["host1", "host2", "host3"]] - for host in hosts: - host.gw_remotepath = '' - r = self.reporter(config, hosts) - r.report(repevent.TestStarted(hosts, config, ["a", "b", "c"])) - for host in hosts: - r.report(repevent.HostGatewayReady(host, ["a", "b", "c"])) - for host in hosts: - for root in ["a", "b", "c"]: - r.report(repevent.HostRSyncRootReady(host, root)) - out, err = cap.reset() - assert not err - expected1 = "Test started, hosts: host1[0], host2[0], host3[0]" - assert out.find(expected1) != -1 - for expected in py.code.Source(""" - host1[0]: READY (still 2 to go) - host2[0]: READY (still 1 to go) - host3[0]: READY - """).lines: - expected = expected.strip() - assert out.find(expected) != -1 - -class TestLocalReporter(AbstractTestReporter): - reporter = LocalReporter - - def get_hosts(self): - return None - - def test_report_received_item_outcome(self): - assert self.report_received_item_outcome() == 'FsF.' - - def test_verbose(self): - self._test_verbose() - - def test_module(self): - output = self._test_module() - assert output.find("test_one") != -1 - assert output.endswith("FsF."), output - - def test_full_module(self): - py.test.skip("fix exact output matching test") - received = self._test_full_module() - expected_lst = ["repmod/test_one.py", "FAILED TO LOAD MODULE", - "skipped", "reason"] - for i in expected_lst: - assert received.find(i) != -1 - -class TestRemoteReporter(AbstractTestReporter): - reporter = RemoteReporter - - def get_hosts(self): - return [Host("host")] - - def test_still_to_go(self): - self._test_still_to_go() - - def test_report_received_item_outcome(self): - val = self.report_received_item_outcome() - expected_lst = ["host", "FAILED", - "funcpass", "test_one", - "SKIPPED", - "PASSED"] - for expected in expected_lst: - assert val.find(expected) != -1 - - def test_module(self): - val = self._test_module() - expected_lst = ["host", "FAILED", - "funcpass", "test_one", - "SKIPPED", - "PASSED"] - for expected in expected_lst: - assert val.find(expected) != -1 - - def test_full_module(self): - py.test.skip("fix exact output matching test") - val = self._test_full_module() - assert val.find("FAILED TO LOAD MODULE: repmod/test_three.py\n"\ - "\nSkipped ('reason') repmod/test_two.py") != -1 - -def test_reporter_choice(): - from py.__.test2.rsession.web import WebReporter - from py.__.test2.rsession.rest import RestReporter - choices = [ - (['-d', '--rest'], RestReporter), - (['-w'], WebReporter), - (['-r'], WebReporter)] - for opts, reporter in choices: - config = py.test2.config._reparse(['xxx'] + opts) - assert choose_reporter(None, config) is reporter - From hpk at codespeak.net Wed Aug 6 00:56:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 00:56:10 +0200 (CEST) Subject: [py-svn] r57020 - py/branch/event/py/test2 Message-ID: <20080805225610.8572B1683E2@codespeak.net> Author: hpk Date: Wed Aug 6 00:56:08 2008 New Revision: 57020 Added: py/branch/event/py/test2/async.py Log: experimental async session Added: py/branch/event/py/test2/async.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/async.py Wed Aug 6 00:56:08 2008 @@ -0,0 +1,104 @@ +""" + + EXPERIMENTAL async session (for dist/non-dist unification) + +""" + +import py +from py.__.test2 import repevent +from py.__.test2.eventbus import EventBus +import py.__.test2.custompdb + +# used for genitems() +from py.__.test2.outcome import Exit +Item = (py.test.collect.Item, py.test2.collect.Item) +Collector = (py.test.collect.Collector, py.test2.collect.Collector) +from runner import basic_run_report, basic_collect_report, skip_report +import Queue + +class AsyncSession(object): + """ + Session drives the collection and running of tests + and generates test events for reporters. + """ + def __init__(self, config): + self.config = config + self.bus = EventBus() + self.eventqueue = [] + self._colnum = self._testnum = 0 + + def fixoptions(self): + """ check, fix and determine conflicting options. """ + option = self.config.option + if option.runbrowser and not option.startserver: + #print "--runbrowser implies --startserver" + option.startserver = True + if self.config.getvalue("dist_boxed") and option.dist: + option.boxed = True + # conflicting options + if option.looponfailing and option.usepdb: + raise ValueError, "--looponfailing together with --pdb not supported." + if option.looponfailing and option.dist: + raise ValueError, "--looponfailing together with --dist not supported." + if option.executable and option.usepdb: + raise ValueError, "--exec together with --pdb not supported." + if option.keyword_oneshot and not option.keyword: + raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" + + def main(self): + self.sessionstarts() + colitems = [self.config.getfsnode(arg) for arg in self.config.args] + shouldstop = False + try: + while colitems and not shouldstop: + colitem = colitems.pop(0) + if isinstance(colitem, Item): + self.sendtest(colitem) + else: + self.sendcol(colitem) + new, shouldstop = self.receive() + colitems.extend(new) + finally: + self.sessionfinishes() + + def receive(self): + ev = self.getevent() # with timeout + colitems = [] + shouldstop = False + if isinstance(ev, repevent.CollectionReport): + self._colnum -= 1 + if ev.passed: + colitems.extend(ev.result) + else: + self.bus.notify(ev) + elif isinstance(ev, repevent.ItemTestReport): + self._testnum -= 1 + self.bus.notify(ev) + if ev.failed and self.config.option.exitfirst: + shouldstop = True + return colitems, shouldstop + + def getevent(self): + return self.eventqueue.pop() + + def sendtest(self, colitem): + self._testnum += 1 + ev = basic_run_report(colitem) + self.eventqueue.append(ev) + + def sendcol(self, colitem): + self._colnum += 1 + ev = basic_collect_report(colitem) + self.eventqueue.append(ev) + + def sessionstarts(self): + """ setup any neccessary resources ahead of the test run. """ + self.reporter = self.config.getreporter() + self.reporter.activate(self.bus) + self.bus.notify(repevent.SessionStart(self)) + + def sessionfinishes(self): + """ teardown any resources after a test run. """ + self.bus.notify(repevent.SessionFinish(self)) + self.reporter.deactivate(self.bus) + From hpk at codespeak.net Wed Aug 6 07:43:58 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 07:43:58 +0200 (CEST) Subject: [py-svn] r57022 - py/branch/event/py/test2 Message-ID: <20080806054358.81D821684E7@codespeak.net> Author: hpk Date: Wed Aug 6 07:43:56 2008 New Revision: 57022 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/runner.py Log: remove repr_run/repr_path methods Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Aug 6 07:43:56 2008 @@ -282,9 +282,6 @@ if path.check(dir=1, dotfile=0): return path.basename not in ('CVS', '_darcs', '{arch}') - def repr_path(self): - return (self.fspath, None) - def listdir(self): files = [] dirs = [] @@ -321,20 +318,6 @@ from py.__.test2.runner import basic_run_report, forked_run_report class Item(Node): """ a basic test item. """ - def repr_path(self): - """ return (filepath, testpath) tuple with - filepath: a fspath representation (None if not applicable) - testpath: a path representation of the test path. - - this is used by reporters. - """ - xxx - - def repr_run(self, excinfo): - """ return string failure represenation for this item. - """ - xxx - def _getrunner(self): if self._config.option.boxed: return forked_run_report Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Wed Aug 6 07:43:56 2008 @@ -21,11 +21,6 @@ def _getobj(self): return getattr(self.parent.obj, self.name) - def repr_path(self): - fspath = pypresent.getrelpath(self._config.topdir, self.fspath) - modpath = pypresent.getmodpath(self) - return (fspath, modpath) - class PyCollectorMixin(PyobjMixin, Collector): Class = configproperty('Class') Instance = configproperty('Instance') @@ -90,9 +85,6 @@ class Module(FSCollector, PyCollectorMixin): _stickyfailure = None - def repr_path(self): - return (self.fspath, None) - def listdir(self): if getattr(self.obj, 'disabled', 0): return [] @@ -278,10 +270,6 @@ """ execute the given test function. """ self.obj(*self._args) - def repr_run(self, runinfo): - """ return a textual representation of run info. """ - return pypresent.python_repr_run(runinfo) - class DoctestFile(PyCollectorMixin, FSCollector): def listdir(self): return [self.fspath.basename] Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Wed Aug 6 07:43:56 2008 @@ -163,8 +163,7 @@ return ev else: if result.exitstatus == EXITSTATUS_TESTEXIT: - itemrepr = item.repr_path() - raise Exit("forked test item %s raised Exit" %(itemrepr,)) + raise Exit("forked test item %s raised Exit" %(item,)) return report_crash(item, result) def report_crash(item, result): From hpk at codespeak.net Wed Aug 6 09:48:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 09:48:17 +0200 (CEST) Subject: [py-svn] r57023 - in py/branch/event/py/test2: rep rep/testing testing Message-ID: <20080806074817.98B76168530@codespeak.net> Author: hpk Date: Wed Aug 6 09:48:15 2008 New Revision: 57023 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py Log: add some header info, improve "acceptance" test machinery Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 09:48:15 2008 @@ -4,7 +4,7 @@ from py.__.test2.rep.base import BaseReporter from py.__.test2.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped -from py.__.test2.pypresent import getrelpath +from py.__.test2.pypresent import getrelpath, repr_pythonversion class TerminalReporter(BaseReporter): def __init__(self, config, file=None): @@ -37,6 +37,8 @@ def rep_SessionStart(self, ev): self.out.sep("=", "test session starts") + self._sessionstarttime = py.std.time.time() + self.out_hostinfo() def rep_SessionFinish(self, ev): self.out.line("") @@ -49,12 +51,39 @@ self.out.sep("_", "skipped test summary") for num, fspath, lineno, reason in folded_skips: self.out.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) + #self.out.sep("=", "test session summary") + + + session_duration = py.std.time.time() - self._sessionstarttime + + #self.out.line("session wallclock duration: %.2f seconds" %( + # py.std.time.time() - self._sessionstarttime)) numfailed = len(self._failed) numskipped = len(self._skipped) numpassed = len(self._passed) sum = numfailed + numskipped + numpassed - self.out.sep("=", "%d tests of which %d failed, %d skipped" %( - sum, numfailed, numskipped)) + self.out.line() + self.out.sep("=", "%d tests in %.2f seconds" %(sum, session_duration)) + self.out_hostinfo() + self.out.line() + if numskipped == 0: + self.out.sep("*", "skips: no skips :)") + else: + self.out.sep("*", "skips: %d" %(numskipped)) + if numfailed == 0: + self.out.sep("*", "failures: no failures :)") + else: + self.out.sep("*", "failures: %d" %(numfailed)) + self.out.line() + + #self.out.sep("=", "%d tests of which %d failed, %d skipped" %( + # sum, numfailed, numskipped)) + + def out_hostinfo(self): + self.out.line("host 0: %s %s - Python %s" % + (py.std.sys.platform, + py.std.sys.executable, + repr_pythonversion())) Reporter = TerminalReporter Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Wed Aug 6 09:48:15 2008 @@ -33,6 +33,7 @@ rep.processevent(ev) s = popvalue(stringio) assert s.find("test_pass_skip_fail.py .sF") != -1 + rep.processevent(repevent.SessionStart(None)) rep.processevent(repevent.SessionFinish(None)) assert_stringio_contains_lines(stringio, [ " def test_func():", @@ -52,6 +53,7 @@ s = popvalue(stringio) print s assert s.find("test_collect_fail.py - ImportError: No module named") != -1 + rep.processevent(repevent.SessionStart(None)) rep.processevent(repevent.SessionFinish(None)) assert_stringio_contains_lines(stringio, [ "> import xyz", Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 09:48:15 2008 @@ -25,11 +25,13 @@ return Result(ret, out, err) def makepyfile(self, **kwargs): - assert len(kwargs) == 1 - name, value = kwargs.popitem() - p = py.path.local(name).new(ext=".py") - p.write(py.code.Source(value)) - return p + ret = None + for name, value in kwargs.iteritems(): + p = py.path.local(name).new(ext=".py") + p.write(py.code.Source(value)) + if ret is None: + ret = p + return ret def setup_method(self, method): name = self.__class__.__name__ + "_" + method.__name__ @@ -71,13 +73,14 @@ """).strip()) def test_nested_import_error(self): - p = self.makepyfile(test_one=""" - import import_fails - def test_this(): - assert import_fails.a == 1 - """) - p2 = p.dirpath("import_fails.py") - p2.write("import does_not_work") + p = self.makepyfile( + test_one=""" + import import_fails + def test_this(): + assert import_fails.a == 1 + """, + import_fails="import does_not_work" + ) result = self.runpytest(p) extra = assert_lines_contain_lines(result.outlines, [ "> import import_fails", @@ -86,25 +89,27 @@ assert result.ret == 1 def test_skipped_reasons(self): - p1 = self.makepyfile(test_one=""" - from conftest import doskip - def setup_function(func): - doskip() - def test_func(): - pass - class TestClass: - def test_method(self): + p1 = self.makepyfile( + test_one=""" + from conftest import doskip + def setup_function(func): doskip() - """) - p2 = self.makepyfile(test_two=""" - from conftest import doskip - doskip() - """) - p3 = self.makepyfile(conftest=""" - import py - def doskip(): - py.test.skip('test') - """) + def test_func(): + pass + class TestClass: + def test_method(self): + doskip() + """, + test_two = """ + from conftest import doskip + doskip() + """, + conftest = """ + import py + def doskip(): + py.test.skip('test') + """ + ) result = self.runpytest() extra = assert_lines_contain_lines(result.outlines, [ "*test_one.py ss", @@ -124,11 +129,7 @@ py.test2.skip("dontshow") """) result = self.runpytest() - extra = assert_lines_contain_lines(result.outlines, [ - "*test_one.py .Fs", - "=* 3 tests of which 1 failed, 1 skipped *=" - ]) - assert str(extra).find("skip test summary") == -1 + assert str(result.outlines).find("skip test summary") == -1 def test_passes(self): p1 = self.makepyfile(test_one=""" @@ -145,7 +146,25 @@ old.chdir() extra = assert_lines_contain_lines(result.outlines, [ "test_one.py ..", - "===* 2 tests of which 0 failed, 0 skipped *===" + "* skips: no skips*", + "* failures: no failures*", + ]) + + def test_header_trailer_info(self): + p1 = self.makepyfile(test_one=""" + def test_passes(): + pass + """) + result = self.runpytest() + verinfo = ".".join(map(str, py.std.sys.version_info[:3])) + extra = assert_lines_contain_lines(result.outlines, [ + "*===== test session starts ====*", + "*test_one.py .", + "==* 1 tests in *.[0-9][0-9] seconds *", + "host 0: %s %s - Python %s*" %( + py.std.sys.platform, py.std.sys.executable, verinfo), + "* no skips :)*", + "* no failures :)*", ]) def test_looponfailing_looping(self): Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Wed Aug 6 09:48:15 2008 @@ -139,6 +139,7 @@ lines1 = lines1[:] nextline = None for line in lines2: + nomatchprinted = False while lines1: nextline = lines1.pop(0) if line == nextline: @@ -146,9 +147,12 @@ break elif fnmatch(nextline, line): print "fnmatch:", repr(line) + print " with:", repr(nextline) break else: - print "nomatch:", repr(line) + if not nomatchprinted: + print "nomatch:", repr(line) + nomatchprinted = True print " and:", repr(nextline) extralines.append(nextline) else: From hpk at codespeak.net Wed Aug 6 11:27:56 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 11:27:56 +0200 (CEST) Subject: [py-svn] r57024 - in py/branch/event/py: code code/testing test2 test2/rep test2/testing Message-ID: <20080806092756.C0B7F168531@codespeak.net> Author: hpk Date: Wed Aug 6 11:27:55 2008 New Revision: 57024 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_repevent.py Log: traceback presentation Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Wed Aug 6 11:27:55 2008 @@ -59,7 +59,7 @@ """ repr = self._formatrepr(showlocals=showlocals, style=style) tw = py.io.TerminalWriter() - repr.writeterminal(tw) + repr.toterminal(tw) return tw.stringio.getvalue() def __str__(self): @@ -78,7 +78,7 @@ self.showlocals = showlocals self.style = style - def writeterminal(self, tw): + def toterminal(self, tw): for line in self.lines: if isinstance(line, LineSep): tw.sep(line.sep, line.line) @@ -100,7 +100,7 @@ try: source = entry.getsource() except py.error.ENOENT: - source = "???" + source = py.code.Source("???") return source.deindent() def _saferepr(self, obj): @@ -159,6 +159,7 @@ line_index = entry.lineno - entry.getfirstlinesource() if self.style == "long": + self._line("") self.repr_source(source, line_index, excinfo) self._line("") message = excinfo and excinfo.exconly() or "" @@ -217,6 +218,7 @@ # filename and lineno output for each entry, # using an output format that most editors unterstand msg = self.message - if msg.find("\n") != -1: - msg = repr(msg) + i = msg.find("\n") + if i != -1: + msg = msg[:i] return ("%s:%s: %s" %(self.path, self.lineno, msg)) Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Wed Aug 6 11:27:55 2008 @@ -1,6 +1,14 @@ import py from py.__.code.excinfo import FormattedExcinfo +class TWMock: + def __init__(self): + self.lines = [] + def sep(self, sep, line): + self.lines.append((sep, line)) + def line(self, line): + self.lines.append(line) + def test_excinfo_simple(): try: raise ValueError @@ -272,10 +280,11 @@ # test intermittent entries - assert lines[0] == " def entry():" - assert lines[1] == "> func1()" - assert not lines[2] - loc = lines[3] + assert lines[0] == "" + assert lines[1] == " def entry():" + assert lines[2] == "> func1()" + assert not lines[3] + loc = lines[4] assert loc.path == mod.__file__ assert loc.lineno == 5 assert not loc.message @@ -284,15 +293,16 @@ p = FormattedExcinfo() p.repr_tb_entry(excinfo.traceback[-1], excinfo) lines = p.lines - assert lines[0] == " def func1():" - assert lines[1] == '> raise ValueError("hello\\nworld")' - assert lines[2] == "E ValueError: hello" - assert lines[3] == "E world" - loc = lines[5] + assert lines[0] == "" + assert lines[1] == " def func1():" + assert lines[2] == '> raise ValueError("hello\\nworld")' + assert lines[3] == "E ValueError: hello" + assert lines[4] == "E world" + loc = lines[6] assert loc.path == mod.__file__ assert loc.lineno == 3 assert loc.message == "ValueError: hello\nworld" - assert str(loc) == "%s:3: 'ValueError: hello\\nworld'" %(mod.__file__,) + assert str(loc) == "%s:3: ValueError: hello" %(mod.__file__,) def test_repr_tbentry_short(self): mod = self.importasmod(""" @@ -418,9 +428,36 @@ assert str(line1) == str(line2) tw = py.io.TerminalWriter() - fex.writeterminal(tw) + fex.toterminal(tw) lines1 = tw.stringio.getvalue().split("\n") lines2 = excinfo.format().split("\n") for line1, line2 in zip(repr.lines, fex.lines): assert line1 == line2 + + def test_toterminal(self): + mod = self.importasmod(""" + def g(x): + raise ValueError(x) + def f(): + g(3) + """) + excinfo = py.test.raises(ValueError, mod.f) + excinfo.traceback = excinfo.traceback.filter() + repr = excinfo._formatrepr() + tw = TWMock() + + repr.toterminal(tw) + assert tw.lines + assert tw.lines.pop(0) == "" + assert tw.lines[0] == " def f():" + assert tw.lines[1] == "> g(3)" + assert tw.lines[2] == "" + assert tw.lines[3].endswith("mod.py:5: ") + assert tw.lines[4] == ("_ ", "") + assert tw.lines[5] == "" + assert tw.lines[6] == " def g(x):" + assert tw.lines[7] == "> raise ValueError(x)" + assert tw.lines[8] == "E ValueError: 3" + assert tw.lines[9] == "" + assert tw.lines[10].endswith("mod.py:3: ValueError: 3") Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 11:27:55 2008 @@ -42,8 +42,11 @@ def rep_SessionFinish(self, ev): self.out.line("") - for ev in self._failed: - ev.writelongrepr(self.out) + if self._failed: + self.out.sep("=", "FAILURES") + for ev in self._failed: + self.out.sep("_") + ev.writelongrepr(self.out) if not self._failed or self.config.option.showskipsummary: folded_skips = self._folded_skips() Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Wed Aug 6 11:27:55 2008 @@ -54,8 +54,8 @@ def writelongrepr(self, out): longrepr = self.failed.longrepr - if hasattr(longrepr, 'terminalwrite'): - longrepr.terminalwrite(self.out) + if hasattr(longrepr, 'toterminal'): + longrepr.toterminal(out) else: out.line(str(longrepr)) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 11:27:55 2008 @@ -28,7 +28,8 @@ ret = None for name, value in kwargs.iteritems(): p = py.path.local(name).new(ext=".py") - p.write(py.code.Source(value)) + source = py.code.Source(value) + p.write(str(py.code.Source(value)).lstrip()) if ret is None: ret = p return ret @@ -115,7 +116,7 @@ "*test_one.py ss", "*test_two.py - Skipped*", "___* skipped test summary *_", - "*conftest.py:3: *3* Skipped: 'test'", + "*conftest.py:2: *3* Skipped: 'test'", ]) def test_no_skip_summary_if_failure(self): @@ -167,6 +168,35 @@ "* no failures :)*", ]) + def test_traceback_failure(self): + p1 = self.makepyfile(test_fail=""" + def g(): + return 2 + def f(x): + assert x == g() + def test_onefails(): + f(3) + """) + result = self.runpytest(p1) + assert_lines_contain_lines(result.outlines, [ + "test_fail.py F", + "====* FAILURES *====", + "____*____", + " def test_onefails():", + "> f(3)", + "", + "*test_fail.py:6: ", + "_ _ _ *", + #"", + " def f(x):", + "> assert x == g()", + "E assert 3 == 2", + "E + where 2 = g()", + "", + "*test_fail.py:4: AssertionError: assert 3 == 2", + ]) + + def test_looponfailing_looping(self): py.test.skip("thought needed to nicely test --looponfailing") py.test.skip("xxx check events") Modified: py/branch/event/py/test2/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test2/testing/test_repevent.py Wed Aug 6 11:27:55 2008 @@ -1,6 +1,7 @@ from py.__.test2 import repevent import setupdata, suptest +from py.__.code.testing.test_excinfo import TWMock class TestItemTestReport(object): @@ -9,7 +10,11 @@ reports = sorter.get(repevent.ItemTestReport) ev = reports[0] assert ev.failed - #assert ev.repr_run.find("AssertionError") != -1 + twmock = TWMock() + ev.writelongrepr(twmock) + assert ('_', '') in twmock.lines + + ##assert ev.repr_run.find("AssertionError") != -1 filepath = ev.colitem.fspath #filepath , modpath = ev.itemrepr_path assert str(filepath).endswith("filetest.py") From hpk at codespeak.net Wed Aug 6 14:50:06 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 14:50:06 +0200 (CEST) Subject: [py-svn] r57025 - in py/branch/event/py: code code/testing io io/testing test2 test2/rep test2/testing Message-ID: <20080806125006.85B861684DC@codespeak.net> Author: hpk Date: Wed Aug 6 14:50:01 2008 New Revision: 57025 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/tbpresent.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/io/terminalwriter.py py/branch/event/py/io/testing/test_terminalwriter.py py/branch/event/py/test2/collect.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_repevent.py py/branch/event/py/test2/testing/test_runner_functional.py Log: * introduce general structured (and picklable) exception representation new py.code.ExceptionInfo().format() method * streamline py.io.TerminalWriter API Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Wed Aug 6 14:50:01 2008 @@ -47,24 +47,18 @@ """ return True if the exception is an instance of exc """ return isinstance(self.value, exc) - def _formatrepr(self, **kwargs): - formatter = FormattedExcinfo(**kwargs) - formatter.repr_tb(self) - return formatter - - def format(self, showlocals=False, style="long"): - """ format exception info into string. + def getrepr(self, showlocals=False, style="long", tbfilter=True): + """ return str()able representation of this exception info. showlocals: show locals per traceback entry style: long|short|no traceback style + tbfilter: hide entries (where __tracebackhide__ is true) """ - repr = self._formatrepr(showlocals=showlocals, style=style) - tw = py.io.TerminalWriter() - repr.toterminal(tw) - return tw.stringio.getvalue() + fmt = FormattedExcinfo(showlocals=showlocals, style=style, tbfilter=tbfilter) + return fmt.repr_excinfo(self) def __str__(self): entry = self.traceback[-1] - loc = LocationRepr(entry.path, entry.lineno + 1, self.exconly()) + loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) return str(loc) class FormattedExcinfo(object): @@ -73,20 +67,13 @@ flow_marker = ">" fail_marker = "E" - def __init__(self, showlocals=False, style="long"): - self.lines = [] + def __init__(self, showlocals=False, style="long", tbfilter=True): self.showlocals = showlocals self.style = style - - def toterminal(self, tw): - for line in self.lines: - if isinstance(line, LineSep): - tw.sep(line.sep, line.line) - else: - tw.line(str(line)) + self.tbfilter = tbfilter def _getindent(self, source): - # figure out indent + # figure out indent for given source try: s = str(source.getstatement(len(source)-1)) except KeyboardInterrupt: @@ -95,7 +82,6 @@ s = str(source[-1]) return 4 + (len(s) - len(s.lstrip())) - def _getentrysource(self, entry): try: source = entry.getsource() @@ -106,9 +92,9 @@ def _saferepr(self, obj): return safe_repr._repr(obj) - - def repr_source(self, source, line_index=-1, excinfo=None): - """ represent source with with marker at given index. """ + def get_source(self, source, line_index=-1, excinfo=None): + """ return formatted and marked up source lines. """ + lines = [] if source is None: source = py.code.Source("???") line_index = 0 @@ -120,105 +106,165 @@ else: prefix = " " line = prefix + source[i] - self._line(line) - + lines.append(line) if excinfo is not None: indent = self._getindent(source) - self.repr_exconly(excinfo, indent=indent, markall=True) + lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) + return lines - def repr_exconly(self, excinfo, indent=4, markall=False): + def get_exconly(self, excinfo, indent=4, markall=False): + lines = [] indent = " " * indent # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') + exlines = excinfo.exconly(tryshort=True).split('\n') failindent = self.fail_marker + indent[1:] - for line in lines: - self._line(failindent + line) + for line in exlines: + lines.append(failindent + line) if not markall: failindent = indent + return lines def repr_locals(self, f_locals): if self.showlocals: - self._sepline('- ', 'locals') + lines = [] for name, value in f_locals.items(): if name == '__builtins__': - self._line("__builtins__ = ") + lines.append("__builtins__ = ") else: # This formatting could all be handled by the _repr() function, which is # only repr.Repr in disguise, so is very configurable. str_repr = safe_repr._repr(value) - if len(str_repr) < 70 or not isinstance(value, - (list, tuple, dict)): - self._line("%-10s = %s" %(name, str_repr)) - else: - self._line("%-10s =\\" % (name,)) - py.std.pprint.pprint(value, stream=self.excinfowriter) + #if len(str_repr) < 70 or not isinstance(value, + # (list, tuple, dict)): + lines.append("%-10s = %s" %(name, str_repr)) + #else: + # self._line("%-10s =\\" % (name,)) + # # XXX + # py.std.pprint.pprint(value, stream=self.excinfowriter) + return ReprLocals(lines) - def repr_tb_entry(self, entry, excinfo=None): + def repr_traceback_entry(self, entry, excinfo=None): # excinfo is not None if this is the last tb entry source = self._getentrysource(entry) line_index = entry.lineno - entry.getfirstlinesource() if self.style == "long": - self._line("") - self.repr_source(source, line_index, excinfo) - self._line("") + lines = self.get_source(source, line_index, excinfo) message = excinfo and excinfo.exconly() or "" - self._line(LocationRepr(entry.path, entry.lineno+1, message)) - self.repr_locals(entry.locals) + filelocrepr = ReprFileLocation(entry.path, entry.lineno+1, message) + localsrepr = self.repr_locals(entry.locals) + return ReprEntry(lines, localsrepr, filelocrepr) else: + lines = [] if self.style == "short": line = source[line_index].lstrip() - self._line(' File "%s", line %d, in %s' % ( + lines.append(' File "%s", line %d, in %s' % ( entry.path.basename, entry.lineno+1, entry.name)) - self._line(" " + line) + lines.append(" " + line) if excinfo: - self.repr_exconly(excinfo, indent=4) + lines.extend(self.get_exconly(excinfo, indent=4)) + return ReprEntry(lines, None, None) - def repr_sep(self, sep): - if self.style == "long": - self._sepline(sep) - - def repr_tb(self, excinfo): + def repr_traceback(self, excinfo): traceback = excinfo.traceback + if self.tbfilter: + traceback = traceback.filter() recursionindex = traceback.recursionindex() last = traceback[-1] + entries = [] + extraline = None for index, entry in py.builtin.enumerate(traceback): - if last != entry: - self.repr_tb_entry(entry) - self.repr_sep("_ ") - else: - self.repr_tb_entry(entry, excinfo) - self.repr_sep("_") + einfo = (last == entry) and excinfo or None + reprentry = self.repr_traceback_entry(entry, einfo) + entries.append(reprentry) if index == recursionindex: - self._line("Recursion detected (same locals & position)") - self._sepline("!") + extraline = "!!! Recursion detected (same locals & position)" break + return ReprTraceback(entries, extraline, style=self.style) + + def repr_excinfo(self, excinfo): + reprtraceback = self.repr_traceback(excinfo) + exconly = excinfo.exconly(tryshort=True) + return ReprExceptionInfo(reprtraceback, exconly) - def _sepline(self, sep, line=""): - self.lines.append(LineSep(sep, line)) - def _line(self, line): - self.lines.append(line) +class Repr: def __str__(self): - return "\n".join([str(x) for x in self.lines]) + tw = py.io.TerminalWriter(stringio=True) + self.toterminal(tw) + return tw.stringio.getvalue().strip() + + def __repr__(self): + return "<%s instance at %0x>" %(self.__class__, id(self)) + +class ReprExceptionInfo(Repr): + def __init__(self, reprtraceback, exconly): + self.reprtraceback = reprtraceback + self.exconly = exconly + + def toterminal(self, tw): + self.reprtraceback.toterminal(tw) + +class ReprTraceback(Repr): + entrysep = "_ " + + def __init__(self, reprentries, extraline, style): + self.reprentries = reprentries + self.extraline = extraline + self.style = style + + def toterminal(self, tw): + sepok = False + for entry in self.reprentries: + if sepok and self.style == "long": + tw.sep(self.entrysep) + tw.line("") + sepok = True + entry.toterminal(tw) + if self.extraline: + tw.line(extraline) + +class ReprEntry(Repr): + localssep = "_ " + + def __init__(self, lines, reprlocals, filelocrepr): + self.lines = lines + self.reprlocals = reprlocals + self.reprfileloc = filelocrepr + + def toterminal(self, tw): + for line in self.lines: + tw.line(line) + if self.reprlocals: + tw.sep(self.localssep) + self.reprlocals.toterminal(tw) + if self.reprfileloc: + tw.line("") + self.reprfileloc.toterminal(tw) -class LineSep: - def __init__(self, sep, line): - self.sep = sep - self.line = line def __str__(self): - sep = self.sep * 10 - return self.line.join([sep, sep]) + return "%s\n%s\n%s" % ("\n".join(self.lines), + self.reprlocals, + self.reprfileloc) -class LocationRepr: +class ReprFileLocation(Repr): def __init__(self, path, lineno, message): self.path = str(path) self.lineno = lineno self.message = message - def __str__(self): + + def toterminal(self, tw): # filename and lineno output for each entry, # using an output format that most editors unterstand msg = self.message i = msg.find("\n") if i != -1: msg = msg[:i] - return ("%s:%s: %s" %(self.path, self.lineno, msg)) + tw.line("%s:%s: %s" %(self.path, self.lineno, msg)) + +class ReprLocals(Repr): + def __init__(self, lines): + self.lines = lines + + def toterminal(self, tw): + for line in self.lines: + tw.line(line) Modified: py/branch/event/py/code/tbpresent.py ============================================================================== --- py/branch/event/py/code/tbpresent.py (original) +++ py/branch/event/py/code/tbpresent.py Wed Aug 6 14:50:01 2008 @@ -14,7 +14,7 @@ self.showlocals = showlocals self.style = style if out is None: - self.out = py.io.TerminalWriter() + self.out = py.io.TerminalWriter(stringio=True) else: self.out = out Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Wed Aug 6 14:50:01 2008 @@ -1,10 +1,10 @@ import py -from py.__.code.excinfo import FormattedExcinfo +from py.__.code.excinfo import FormattedExcinfo, ReprExceptionInfo class TWMock: def __init__(self): self.lines = [] - def sep(self, sep, line): + def sep(self, sep, line=None): self.lines.append((sep, line)) def line(self, line): self.lines.append(line) @@ -171,14 +171,6 @@ assert entry.frame.code.path == co.path assert entry.lineno == co.firstlineno + 2 assert entry.frame.code.name == 'g' - - #def test_traceback_display_func(self): - # tb = self.excinfo.traceback - # for x in tb: - # x.setdisplay(lambda entry: entry.frame.code.name + '\n') - ## l = tb.display().rstrip().split('\n') - # assert l == ['setup_method', 'h', 'g', 'f'] - def hello(x): x + 5 @@ -228,6 +220,16 @@ modpath.write(source) return modpath.pyimport() + def excinfo_from_exec(self, source): + source = py.code.Source(source).strip() + try: + exec source.compile() + except KeyboardInterrupt: + raise + except: + return py.code.ExceptionInfo() + assert 0, "did not raise" + def test_repr_source(self): pr = FormattedExcinfo() source = py.code.Source(""" @@ -235,76 +237,73 @@ pass """).strip() pr.flow_marker = "|" - pr.repr_source(source, 0) - lines = pr.lines + lines = pr.get_source(source, 0) assert len(lines) == 2 assert lines[0] == "| def f(x):" assert lines[1] == " pass" def test_repr_source_excinfo(self): """ check if indentation is right """ - def f(): - def g(): - 1/0 - try: - g() - except: - e = py.code.ExceptionInfo() - return e - pr = FormattedExcinfo() - source = py.code.Source(f) - e = f() - pr.repr_source(source, 1, e) - assert pr.lines[-1].startswith("E ZeroDivisionError:") + excinfo = self.excinfo_from_exec(""" + def f(): + assert 0 + f() + """) + pr = FormattedExcinfo() + source = pr._getentrysource(excinfo.traceback[-1]) + lines = pr.get_source(source, 1, excinfo) + print lines + assert lines == [ + ' def f():', + '> assert 0', + 'E assert 0' + ] def test_repr_local(self): p = FormattedExcinfo(showlocals=True) - loc = locals() - p.repr_locals(loc) - result = str(p) - for key in loc.keys(): - assert result.find(key) != -1 + loc = {'x': 3, '__builtins__': __builtins__} + reprlocals = p.repr_locals(loc) + assert reprlocals.lines + print reprlocals.lines + assert reprlocals.lines == [ + '__builtins__ = ', + 'x = 3' + ] - def test_repr_tbentry(self): + def test_repr_tracebackentry_lines(self): mod = self.importasmod(""" def func1(): raise ValueError("hello\\nworld") - def entry(): - func1() """) - excinfo = py.test.raises(ValueError, mod.entry) + excinfo = py.test.raises(ValueError, mod.func1) + excinfo.traceback = excinfo.traceback.filter() p = FormattedExcinfo() - p.repr_tb_entry(excinfo.traceback[-2]) - lines = p.lines - - # test intermittent entries + reprtb = p.repr_traceback_entry(excinfo.traceback[-1]) + print reprtb + + # test as intermittent entry + lines = reprtb.lines + assert lines[0] == ' def func1():' + assert lines[1] == '> raise ValueError("hello\\nworld")' - assert lines[0] == "" - assert lines[1] == " def entry():" - assert lines[2] == "> func1()" - assert not lines[3] - loc = lines[4] - assert loc.path == mod.__file__ - assert loc.lineno == 5 - assert not loc.message + # test as last entry + p = FormattedExcinfo(showlocals=True) + repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) + lines = repr_entry.lines + assert lines[0] == ' def func1():' + assert lines[1] == '> raise ValueError("hello\\nworld")' + assert lines[2] == 'E ValueError: hello' + assert lines[3] == 'E world' + assert not lines[4:] - # test last entry - p = FormattedExcinfo() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.lines - assert lines[0] == "" - assert lines[1] == " def func1():" - assert lines[2] == '> raise ValueError("hello\\nworld")' - assert lines[3] == "E ValueError: hello" - assert lines[4] == "E world" - loc = lines[6] + loc = repr_entry.reprlocals is not None + loc = repr_entry.reprfileloc assert loc.path == mod.__file__ assert loc.lineno == 3 - assert loc.message == "ValueError: hello\nworld" - assert str(loc) == "%s:3: ValueError: hello" %(mod.__file__,) + #assert loc.message == "ValueError: hello" - def test_repr_tbentry_short(self): + def test_repr_tracebackentry_short(self): mod = self.importasmod(""" def func1(): raise ValueError("hello") @@ -313,21 +312,21 @@ """) excinfo = py.test.raises(ValueError, mod.entry) p = FormattedExcinfo(style="short") - p.repr_tb_entry(excinfo.traceback[-2]) - lines = p.lines + reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) + lines = reprtb.lines basename = py.path.local(mod.__file__).basename assert lines[0] == ' File "%s", line 5, in entry' % basename assert lines[1] == ' func1()' # test last entry p = FormattedExcinfo(style="short") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.lines + reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) + lines = reprtb.lines assert lines[0] == ' File "%s", line 3, in func1' % basename assert lines[1] == ' raise ValueError("hello")' assert lines[2] == 'E ValueError: hello' - def test_repr_tbentry_no(self): + def test_repr_tracebackentry_no(self): mod = self.importasmod(""" def func1(): raise ValueError("hello") @@ -336,38 +335,50 @@ """) excinfo = py.test.raises(ValueError, mod.entry) p = FormattedExcinfo(style="no") - p.repr_tb_entry(excinfo.traceback[-2]) - assert not p.lines + p.repr_traceback_entry(excinfo.traceback[-2]) p = FormattedExcinfo(style="no") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.lines + reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) + lines = reprentry.lines assert lines[0] == 'E ValueError: hello' + assert not lines[1:] - def test_format(self): + def test_repr_traceback_tbfilter(self): mod = self.importasmod(""" def f(x): raise ValueError(x) def entry(): f(0) """) - p = FormattedExcinfo(style="short") excinfo = py.test.raises(ValueError, mod.entry) - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - s = p.repr_tb(excinfo) - print l - assert l[-1] == "_" - entry, excinfo2 = l[-2] - assert excinfo == excinfo2 - assert entry == excinfo.traceback[-1] - assert l[-3] == "_ " - entry, excinfo2 = l[-4] - assert excinfo2 is None - assert entry == excinfo.traceback[-2] - - def test_repr_tb_recursion(self): + p = FormattedExcinfo(tbfilter=True) + reprtb = p.repr_traceback(excinfo) + assert len(reprtb.reprentries) == 2 + p = FormattedExcinfo(tbfilter=False) + reprtb = p.repr_traceback(excinfo) + assert len(reprtb.reprentries) == 3 + + def test_repr_traceback_and_excinfo(self): + mod = self.importasmod(""" + def f(x): + raise ValueError(x) + def entry(): + f(0) + """) + excinfo = py.test.raises(ValueError, mod.entry) + + for style in ("long", "short"): + p = FormattedExcinfo(style=style) + reprtb = p.repr_traceback(excinfo) + assert len(reprtb.reprentries) == 2 + assert reprtb.style == style + assert not reprtb.extraline + repr = p.repr_excinfo(excinfo) + assert repr.reprtraceback + assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries) + assert repr.exconly == "ValueError: 0" + + def test_repr_traceback_recursion(self): mod = self.importasmod(""" def rec2(x): return rec1(x+1) @@ -378,20 +389,10 @@ """) excinfo = py.test.raises(RuntimeError, mod.entry) - for style in ("short", "long"): + for style in ("short", "long", "no"): p = FormattedExcinfo(style="short") - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - p.repr_tb(excinfo) - s = str(p) - print l - #assert re.search(".*return rec1.*x\+1", s) - #assert re.search(".*return rec2.*x-1.*", s) - assert len(l) < 20 - assert s.find("Recursion detected") != -1 - assert l[-1] == "_ " - assert l[-2][0].lineno == 4 + reprtb = p.repr_traceback(excinfo) + assert reprtb.extraline == "!!! Recursion detected (same locals & position)" def test_tb_entry_AssertionError(self): # probably this test is a bit redundant @@ -410,11 +411,11 @@ py.magic.revoke(assertion=True) p = FormattedExcinfo() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - lines = p.lines - assert lines[-3] == "E assert 1 == 2" + reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) + lines = reprentry.lines + assert lines[-1] == "E assert 1 == 2" - def test_excinfo_formatted(self): + def test_reprexcinfo__formatrepr(self): mod = self.importasmod(""" def f(x): raise ValueError(x) @@ -422,20 +423,14 @@ f(0) """) excinfo = py.test.raises(ValueError, mod.entry) - repr = excinfo._formatrepr() - fex = FormattedExcinfo() - for line1, line2 in zip(repr.lines, fex.lines): - assert str(line1) == str(line2) - - tw = py.io.TerminalWriter() - fex.toterminal(tw) - lines1 = tw.stringio.getvalue().split("\n") - lines2 = excinfo.format().split("\n") - for line1, line2 in zip(repr.lines, fex.lines): - assert line1 == line2 - - def test_toterminal(self): + for style in ("short", "long", "no"): + for showlocals in (True, False): + repr = excinfo.getrepr(style=style, showlocals=showlocals) + assert isinstance(repr, ReprExceptionInfo) + assert repr.reprtraceback.style == style + + def test_toterminal_long(self): mod = self.importasmod(""" def g(x): raise ValueError(x) @@ -444,20 +439,44 @@ """) excinfo = py.test.raises(ValueError, mod.f) excinfo.traceback = excinfo.traceback.filter() - repr = excinfo._formatrepr() + repr = excinfo.getrepr() tw = TWMock() - repr.toterminal(tw) - assert tw.lines - assert tw.lines.pop(0) == "" + assert tw.lines[0] == "" + tw.lines.pop(0) assert tw.lines[0] == " def f():" assert tw.lines[1] == "> g(3)" assert tw.lines[2] == "" assert tw.lines[3].endswith("mod.py:5: ") - assert tw.lines[4] == ("_ ", "") + assert tw.lines[4] == ("_ ", None) assert tw.lines[5] == "" assert tw.lines[6] == " def g(x):" assert tw.lines[7] == "> raise ValueError(x)" assert tw.lines[8] == "E ValueError: 3" assert tw.lines[9] == "" assert tw.lines[10].endswith("mod.py:3: ValueError: 3") + + + def test_format_excinfo(self): + mod = self.importasmod(""" + def g(x): + raise ValueError(x) + def f(): + g(3) + """) + excinfo = py.test.raises(ValueError, mod.f) + def format_and_str(kw): + tw = py.io.TerminalWriter(stringio=True) + repr = excinfo.getrepr(**kw) + repr.toterminal(tw) + assert tw.stringio.getvalue() + + for style in ("long", "short", "no"): + for showlocals in (True, False): + for tbfilter in (True, False): + kw = {'style': style, + 'showlocals': showlocals, + 'tbfilter': tbfilter + } + yield format_and_str, kw + Modified: py/branch/event/py/io/terminalwriter.py ============================================================================== --- py/branch/event/py/io/terminalwriter.py (original) +++ py/branch/event/py/io/terminalwriter.py Wed Aug 6 14:50:01 2008 @@ -43,15 +43,14 @@ file.flush() class TerminalWriter(object): - def __init__(self, file=None): - if file is None: - self.stringio = file = py.std.cStringIO.StringIO() - elif hasattr(file, 'send'): - file = WriteFile(file.send) + def __init__(self, file=None, stringio=False): + if file is None: + if stringio: + self.stringio = file = py.std.cStringIO.StringIO() + else: + file = py.std.sys.stdout elif callable(file): file = WriteFile(file) - else: - file = py.io.dupfile(file) self._file = file self.fullwidth = get_terminal_width() Modified: py/branch/event/py/io/testing/test_terminalwriter.py ============================================================================== --- py/branch/event/py/io/testing/test_terminalwriter.py (original) +++ py/branch/event/py/io/testing/test_terminalwriter.py Wed Aug 6 14:50:01 2008 @@ -20,7 +20,7 @@ def test_terminalwriter_default_instantiation(): - tw = py.io.TerminalWriter() + tw = py.io.TerminalWriter(stringio=True) assert hasattr(tw, 'stringio') class BaseTests: @@ -47,29 +47,20 @@ class TestStringIO(BaseTests): def getwriter(self): - self.tw = py.io.TerminalWriter() + self.tw = py.io.TerminalWriter(stringio=True) return self.tw def getlines(self): io = self.tw.stringio io.seek(0) return io.readlines() -class TestChannelLikeFile(BaseTests): +class TestCallableFile(BaseTests): def getwriter(self): - self.writes = l = [] - class A: - def send(self, msg): - l.append(msg) - return py.io.TerminalWriter(A()) + self.writes = [] + return py.io.TerminalWriter(self.writes.append) + def getlines(self): io = py.std.cStringIO.StringIO() io.write("".join(self.writes)) io.seek(0) return io.readlines() - -class TestCallableFile(TestChannelLikeFile): - def getwriter(self): - self.writes = [] - return py.io.TerminalWriter(self.writes.append) - - Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Aug 6 14:50:01 2008 @@ -220,7 +220,7 @@ return col._getitembynames(names) def _getfailurerepr_py(self, excinfo, outerr): - repr = excinfo._formatrepr() + repr = excinfo.getrepr() # XXX outerr return repr shortfailurerepr = "f" Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Wed Aug 6 14:50:01 2008 @@ -39,6 +39,6 @@ def rep_SessionFinish(self, session): from py.__.code.tbpresent import TBPresenter for ev in self._failed: - ev.writelongrepr(self.out) + ev.toterminal(self.out) Reporter = CollectonlyReporter Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 14:50:01 2008 @@ -46,7 +46,7 @@ self.out.sep("=", "FAILURES") for ev in self._failed: self.out.sep("_") - ev.writelongrepr(self.out) + ev.toterminal(self.out) if not self._failed or self.config.option.showskipsummary: folded_skips = self._folded_skips() Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Wed Aug 6 14:50:01 2008 @@ -52,7 +52,7 @@ setattr(self, name, value) self.outcome = value - def writelongrepr(self, out): + def toterminal(self, out): longrepr = self.failed.longrepr if hasattr(longrepr, 'toterminal'): longrepr.toterminal(out) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 14:50:01 2008 @@ -182,6 +182,7 @@ "test_fail.py F", "====* FAILURES *====", "____*____", + "", " def test_onefails():", "> f(3)", "", Modified: py/branch/event/py/test2/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test2/testing/test_repevent.py Wed Aug 6 14:50:01 2008 @@ -5,14 +5,19 @@ class TestItemTestReport(object): - def test_failing(self): + def test_toterminal(self): sorter = suptest.events_run_example("filetest.py") reports = sorter.get(repevent.ItemTestReport) ev = reports[0] assert ev.failed twmock = TWMock() - ev.writelongrepr(twmock) - assert ('_', '') in twmock.lines + ev.toterminal(twmock) + assert twmock.lines + twmock = TWMock() + ev.failed.longrepr = "hello" + ev.toterminal(twmock) + assert twmock.lines[0] == "hello" + assert not twmock.lines[1:] ##assert ev.repr_run.find("AssertionError") != -1 filepath = ev.colitem.fspath Modified: py/branch/event/py/test2/testing/test_runner_functional.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner_functional.py (original) +++ py/branch/event/py/test2/testing/test_runner_functional.py Wed Aug 6 14:50:01 2008 @@ -1,7 +1,7 @@ import py from py.__.test2.testing.suptest import InlineCollection from py.__.test2.runner import basic_run_report, forked_run_report, basic_collect_report -from py.__.code.excinfo import FormattedExcinfo +from py.__.code.excinfo import ReprExceptionInfo class BaseTests(InlineCollection): def test_passfunction(self): @@ -23,7 +23,7 @@ assert not ev.skipped assert ev.failed assert ev.failed.when == "execute" - assert isinstance(ev.failed.longrepr, FormattedExcinfo) + assert isinstance(ev.failed.longrepr, ReprExceptionInfo) assert str(ev.failed.shortrepr) == "F" def test_skipfunction(self): From hpk at codespeak.net Wed Aug 6 15:23:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 15:23:52 +0200 (CEST) Subject: [py-svn] r57026 - in py/branch/event/py: code code/testing test2 test2/testing Message-ID: <20080806132352.EEF1C1684DF@codespeak.net> Author: hpk Date: Wed Aug 6 15:23:51 2008 New Revision: 57026 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/test2/collect.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_session.py py/branch/event/py/test2/testing/test_setup_nested.py Log: * allow to add sections to ReprExceptionInfo * provide out/err sections on captured output/err Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Wed Aug 6 15:23:51 2008 @@ -200,9 +200,16 @@ def __init__(self, reprtraceback, exconly): self.reprtraceback = reprtraceback self.exconly = exconly + self.sections = [] + + def addsection(self, name, content, sep="-"): + self.sections.append((name, content, sep)) def toterminal(self, tw): self.reprtraceback.toterminal(tw) + for name, content, sep in self.sections: + tw.sep(sep, name) + tw.line(content) class ReprTraceback(Repr): entrysep = "_ " Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Wed Aug 6 15:23:51 2008 @@ -378,6 +378,19 @@ assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries) assert repr.exconly == "ValueError: 0" + def test_repr_excinfo_addouterr(self): + mod = self.importasmod(""" + def entry(): + raise ValueError() + """) + excinfo = py.test.raises(ValueError, mod.entry) + repr = excinfo.getrepr() + repr.addsection("title", "content") + twmock = TWMock() + repr.toterminal(twmock) + assert twmock.lines[-1] == "content" + assert twmock.lines[-2] == ("-", "title") + def test_repr_traceback_recursion(self): mod = self.importasmod(""" def rec2(x): Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Aug 6 15:23:51 2008 @@ -221,9 +221,11 @@ def _getfailurerepr_py(self, excinfo, outerr): repr = excinfo.getrepr() - # XXX outerr + for secname, content in zip(["out", "err"], outerr): + if content: + repr.addsection("Captured std%s" % secname, content.rstrip()) return repr - shortfailurerepr = "f" + shortfailurerepr = "F" class Collector(Node): """ Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Wed Aug 6 15:23:51 2008 @@ -219,9 +219,6 @@ def getfailurerepr(self, excinfo, outerr): return self._getfailurerepr_py(excinfo, outerr) - #repr = excinfo._formatrepr() - ## XXX outerr - #return repr shortfailurerepr = "F" Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 15:23:51 2008 @@ -197,7 +197,29 @@ "*test_fail.py:4: AssertionError: assert 3 == 2", ]) - + def test_capturing_outerr(self): + p1 = self.makepyfile(test_one=""" + import sys + def test_capturing(): + print 42 + print >>sys.stderr, 23 + def test_capturing_error(): + print 1 + print >>sys.stderr, 2 + raise ValueError + """) + result = self.runpytest(p1) + assert_lines_contain_lines(result.outlines, [ + "test_one.py .F", + "====* FAILURES *====", + "____*____", + "*test_one.py:8: ValueError", + "*--- Captured stdout ---*", + "1", + "*--- Captured stderr ---*", + "2", + ]) + def test_looponfailing_looping(self): py.test.skip("thought needed to nicely test --looponfailing") py.test.skip("xxx check events") Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Wed Aug 6 15:23:51 2008 @@ -86,34 +86,6 @@ i = out.find('TypeError') assert i != -1 - def test_Function_capturing(self): - tfile = suptest.makeuniquepyfile(""" - import py - print "module level output" - def test_capturing(): - print 42 - print >>py.std.sys.stderr, 23 - def test_capturing_error(): - print 1 - print >>py.std.sys.stderr, 2 - raise ValueError - """) - sorter = suptest.events_from_cmdline([tfile.dirpath()]) - passed, skipped, failed = sorter.listoutcomes() - assert len(passed) == 1 - assert len(failed) == 1 - - py.test.skip("implement CAPTURING reporting") - ev_list = sorter.get(repevent.ItemTestReport) - ev1, ev2 = ev_list - out, err = ev1.outcome.outerr - assert out == "42\n" - assert err == "23\n" - - out, err = ev2.outcome.outerr - assert out == "1\n" - assert err == "2\n" - def test_raises_output(self): sorter = suptest.events_from_runsource(''' import py Modified: py/branch/event/py/test2/testing/test_setup_nested.py ============================================================================== --- py/branch/event/py/test2/testing/test_setup_nested.py (original) +++ py/branch/event/py/test2/testing/test_setup_nested.py Wed Aug 6 15:23:51 2008 @@ -123,7 +123,4 @@ """) rep = sorter.getreport("test_one") assert rep.passed - py.test.skip("implement report capturing") - assert rep.repr_run.find("check\n") != -1 - assert rep.repr_run.find("e\n") != -1 From hpk at codespeak.net Wed Aug 6 15:26:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 15:26:29 +0200 (CEST) Subject: [py-svn] r57027 - in py/branch/event/py/test2: rep testing Message-ID: <20080806132629.C04ED1684F1@codespeak.net> Author: hpk Date: Wed Aug 6 15:26:29 2008 New Revision: 57027 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/testing/acceptance_test.py Log: no hostinfo at the end Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 15:26:29 2008 @@ -68,7 +68,7 @@ sum = numfailed + numskipped + numpassed self.out.line() self.out.sep("=", "%d tests in %.2f seconds" %(sum, session_duration)) - self.out_hostinfo() + #self.out_hostinfo() self.out.line() if numskipped == 0: self.out.sep("*", "skips: no skips :)") Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 15:26:29 2008 @@ -160,10 +160,10 @@ verinfo = ".".join(map(str, py.std.sys.version_info[:3])) extra = assert_lines_contain_lines(result.outlines, [ "*===== test session starts ====*", - "*test_one.py .", - "==* 1 tests in *.[0-9][0-9] seconds *", "host 0: %s %s - Python %s*" %( py.std.sys.platform, py.std.sys.executable, verinfo), + "*test_one.py .", + "==* 1 tests in *.[0-9][0-9] seconds *", "* no skips :)*", "* no failures :)*", ]) From hpk at codespeak.net Wed Aug 6 16:53:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 16:53:20 +0200 (CEST) Subject: [py-svn] r57028 - py/branch/event/py/test2 Message-ID: <20080806145320.8E3CE1684EC@codespeak.net> Author: hpk Date: Wed Aug 6 16:53:18 2008 New Revision: 57028 Modified: py/branch/event/py/test2/pycollect.py Log: provide more info Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Wed Aug 6 16:53:18 2008 @@ -237,7 +237,7 @@ for i, x in py.builtin.enumerate(self.obj()): call, args = self.getcallargs(x) if not callable(call): - raise TypeError("yielded test %r not callable" %(call,)) + raise TypeError("%r yielded non callable test %r" %(self.obj, call,)) name = "[%d]" % i d[name] = self.Function(name, self, args, callobj=call) return d From hpk at codespeak.net Wed Aug 6 16:54:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 16:54:00 +0200 (CEST) Subject: [py-svn] r57030 - in py/branch/event/py/code: . testing Message-ID: <20080806145400.D0E6F16850A@codespeak.net> Author: hpk Date: Wed Aug 6 16:53:59 2008 New Revision: 57030 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py Log: provide optional nicely formatted info on function args in tracebacks Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Wed Aug 6 16:53:59 2008 @@ -47,13 +47,14 @@ """ return True if the exception is an instance of exc """ return isinstance(self.value, exc) - def getrepr(self, showlocals=False, style="long", tbfilter=True): + def getrepr(self, showlocals=False, style="long", tbfilter=True, funcargs=False): """ return str()able representation of this exception info. showlocals: show locals per traceback entry style: long|short|no traceback style tbfilter: hide entries (where __tracebackhide__ is true) """ - fmt = FormattedExcinfo(showlocals=showlocals, style=style, tbfilter=tbfilter) + fmt = FormattedExcinfo(showlocals=showlocals, style=style, + tbfilter=tbfilter, funcargs=funcargs) return fmt.repr_excinfo(self) def __str__(self): @@ -67,10 +68,11 @@ flow_marker = ">" fail_marker = "E" - def __init__(self, showlocals=False, style="long", tbfilter=True): + def __init__(self, showlocals=False, style="long", tbfilter=True, funcargs=False): self.showlocals = showlocals self.style = style self.tbfilter = tbfilter + self.funcargs = funcargs def _getindent(self, source): # figure out indent for given source @@ -92,6 +94,13 @@ def _saferepr(self, obj): return safe_repr._repr(obj) + def repr_args(self, entry): + if self.funcargs: + args = [] + for argname, argvalue in entry.frame.getargs(): + args.append((argname, self._saferepr(argvalue))) + return ReprFuncArgs(args) + def get_source(self, source, line_index=-1, excinfo=None): """ return formatted and marked up source lines. """ lines = [] @@ -148,14 +157,15 @@ source = self._getentrysource(entry) line_index = entry.lineno - entry.getfirstlinesource() + lines = [] if self.style == "long": - lines = self.get_source(source, line_index, excinfo) + reprargs = self.repr_args(entry) + lines.extend(self.get_source(source, line_index, excinfo)) message = excinfo and excinfo.exconly() or "" filelocrepr = ReprFileLocation(entry.path, entry.lineno+1, message) localsrepr = self.repr_locals(entry.locals) - return ReprEntry(lines, localsrepr, filelocrepr) + return ReprEntry(lines, reprargs, localsrepr, filelocrepr) else: - lines = [] if self.style == "short": line = source[line_index].lstrip() lines.append(' File "%s", line %d, in %s' % ( @@ -163,7 +173,7 @@ lines.append(" " + line) if excinfo: lines.extend(self.get_exconly(excinfo, indent=4)) - return ReprEntry(lines, None, None) + return ReprEntry(lines, None, None, None) def repr_traceback(self, excinfo): traceback = excinfo.traceback @@ -233,12 +243,15 @@ class ReprEntry(Repr): localssep = "_ " - def __init__(self, lines, reprlocals, filelocrepr): + def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr): self.lines = lines + self.reprfuncargs = reprfuncargs self.reprlocals = reprlocals self.reprfileloc = filelocrepr def toterminal(self, tw): + if self.reprfuncargs: + self.reprfuncargs.toterminal(tw) for line in self.lines: tw.line(line) if self.reprlocals: @@ -275,3 +288,25 @@ def toterminal(self, tw): for line in self.lines: tw.line(line) + +class ReprFuncArgs(Repr): + def __init__(self, args): + self.args = args + + def toterminal(self, tw): + if self.args: + linesofar = "" + for name, value in self.args: + ns = "%s = %s" %(name, value) + if len(ns) + len(linesofar) + 2 > tw.fullwidth: + if linesofar: + tw.line(linesofar) + linesofar = ns + else: + if linesofar: + linesofar += ", " + ns + else: + linesofar = ns + if linesofar: + tw.line(linesofar) + tw.line("") Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Wed Aug 6 16:53:59 2008 @@ -8,6 +8,7 @@ self.lines.append((sep, line)) def line(self, line): self.lines.append(line) + fullwidth = 80 def test_excinfo_simple(): try: @@ -303,6 +304,30 @@ assert loc.lineno == 3 #assert loc.message == "ValueError: hello" + def test_repr_tracebackentry_lines(self): + mod = self.importasmod(""" + def func1(m, x, y, z): + raise ValueError("hello\\nworld") + """) + excinfo = py.test.raises(ValueError, mod.func1, "m"*90, 5, 13, "z"*120) + excinfo.traceback = excinfo.traceback.filter() + entry = excinfo.traceback[-1] + p = FormattedExcinfo(funcargs=True) + reprfuncargs = p.repr_args(entry) + assert reprfuncargs.args[0] == ('m', repr("m"*90)) + assert reprfuncargs.args[1] == ('x', '5') + assert reprfuncargs.args[2] == ('y', '13') + assert reprfuncargs.args[3] == ('z', repr("z" * 120)) + + p = FormattedExcinfo(funcargs=True) + repr_entry = p.repr_traceback_entry(entry) + assert repr_entry.reprfuncargs.args == reprfuncargs.args + tw = TWMock() + repr_entry.toterminal(tw) + assert tw.lines[0] == "m = " + repr('m' * 90) + assert tw.lines[1] == "x = 5, y = 13" + assert tw.lines[2] == "z = " + repr('z' * 120) + def test_repr_tracebackentry_short(self): mod = self.importasmod(""" def func1(): @@ -428,7 +453,7 @@ lines = reprentry.lines assert lines[-1] == "E assert 1 == 2" - def test_reprexcinfo__formatrepr(self): + def test_reprexcinfo_getrepr(self): mod = self.importasmod(""" def f(x): raise ValueError(x) @@ -483,13 +508,19 @@ repr = excinfo.getrepr(**kw) repr.toterminal(tw) assert tw.stringio.getvalue() - + + for combo in self.allcombos(): + yield format_and_str, combo + + def allcombos(self): for style in ("long", "short", "no"): for showlocals in (True, False): for tbfilter in (True, False): - kw = {'style': style, - 'showlocals': showlocals, - 'tbfilter': tbfilter - } - yield format_and_str, kw + for funcargs in (True, False): + kw = {'style': style, + 'showlocals': showlocals, + 'funcargs': funcargs, + 'tbfilter': tbfilter + } + yield kw From hpk at codespeak.net Wed Aug 6 16:58:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 16:58:31 +0200 (CEST) Subject: [py-svn] r57031 - py/branch/event/py/test2 Message-ID: <20080806145831.184541684EC@codespeak.net> Author: hpk Date: Wed Aug 6 16:58:31 2008 New Revision: 57031 Modified: py/branch/event/py/test2/collect.py Log: show func args by default Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Aug 6 16:58:31 2008 @@ -220,7 +220,7 @@ return col._getitembynames(names) def _getfailurerepr_py(self, excinfo, outerr): - repr = excinfo.getrepr() + repr = excinfo.getrepr(funcargs=True) for secname, content in zip(["out", "err"], outerr): if content: repr.addsection("Captured std%s" % secname, content.rstrip()) From hpk at codespeak.net Wed Aug 6 17:12:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 17:12:44 +0200 (CEST) Subject: [py-svn] r57032 - in py/branch/event/py: code code/testing test2 test2/testing Message-ID: <20080806151244.1FEE3168515@codespeak.net> Author: hpk Date: Wed Aug 6 17:12:43 2008 New Revision: 57032 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/test2/collect.py py/branch/event/py/test2/testing/acceptance_test.py Log: make showlocals work + tests Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Wed Aug 6 17:12:43 2008 @@ -133,10 +133,12 @@ failindent = indent return lines - def repr_locals(self, f_locals): + def repr_locals(self, locals): if self.showlocals: lines = [] - for name, value in f_locals.items(): + items = locals.items() + items.sort() + for name, value in items: if name == '__builtins__': lines.append("__builtins__ = ") else: @@ -255,7 +257,7 @@ for line in self.lines: tw.line(line) if self.reprlocals: - tw.sep(self.localssep) + tw.sep(self.localssep, "Locals") self.reprlocals.toterminal(tw) if self.reprfileloc: tw.line("") Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Wed Aug 6 17:12:43 2008 @@ -263,14 +263,14 @@ def test_repr_local(self): p = FormattedExcinfo(showlocals=True) - loc = {'x': 3, '__builtins__': __builtins__} + loc = {'y': 5, 'z': 7, 'x': 3, '__builtins__': __builtins__} reprlocals = p.repr_locals(loc) assert reprlocals.lines print reprlocals.lines - assert reprlocals.lines == [ - '__builtins__ = ', - 'x = 3' - ] + assert reprlocals.lines[0] == '__builtins__ = ' + assert reprlocals.lines[1] == 'x = 3' + assert reprlocals.lines[2] == 'y = 5' + assert reprlocals.lines[3] == 'z = 7' def test_repr_tracebackentry_lines(self): mod = self.importasmod(""" Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Aug 6 17:12:43 2008 @@ -220,7 +220,8 @@ return col._getitembynames(names) def _getfailurerepr_py(self, excinfo, outerr): - repr = excinfo.getrepr(funcargs=True) + repr = excinfo.getrepr(funcargs=True, + showlocals=self._config.option.showlocals) for secname, content in zip(["out", "err"], outerr): if content: repr.addsection("Captured std%s" % secname, content.rstrip()) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 17:12:43 2008 @@ -219,6 +219,20 @@ "*--- Captured stderr ---*", "2", ]) + + def test_showlocals(self): + p1 = self.makepyfile(test_one=""" + def test_showlocals(): + x = 3 + y = "x" * 5000 + assert 0 + """) + result = self.runpytest(p1, '-l') + assert_lines_contain_lines(result.outlines, [ + "_ _ * Locals *", + "x* = 3", + "y* = 'xxxxxx*" + ]) def test_looponfailing_looping(self): py.test.skip("thought needed to nicely test --looponfailing") From hpk at codespeak.net Wed Aug 6 17:23:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 17:23:03 +0200 (CEST) Subject: [py-svn] r57033 - py/branch/event/py/test2 Message-ID: <20080806152303.6A759168516@codespeak.net> Author: hpk Date: Wed Aug 6 17:23:03 2008 New Revision: 57033 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/runner.py Log: prunetraceback in a better place Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Aug 6 17:23:03 2008 @@ -192,7 +192,7 @@ def _getsortvalue(self): return self.name - def prunetraceback(self, traceback): + def _prunetraceback(self, traceback): return traceback def _totrail(self): @@ -220,12 +220,14 @@ return col._getitembynames(names) def _getfailurerepr_py(self, excinfo, outerr): + excinfo.traceback = self._prunetraceback(excinfo.traceback) repr = excinfo.getrepr(funcargs=True, showlocals=self._config.option.showlocals) for secname, content in zip(["out", "err"], outerr): if content: repr.addsection("Captured std%s" % secname, content.rstrip()) return repr + shortfailurerepr = "F" class Collector(Node): Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Wed Aug 6 17:23:03 2008 @@ -207,7 +207,7 @@ if teardown_func_or_meth is not None: return teardown_func_or_meth(self.obj) - def prunetraceback(self, traceback): + def _prunetraceback(self, traceback): if not self._config.option.fulltrace: code = py.code.Code(self.obj) path, firstlineno = code.path, code.firstlineno Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Wed Aug 6 17:23:03 2008 @@ -46,7 +46,6 @@ def getkw(self, when, excinfo, outerr): exconly = excinfo.exconly(tryshort=True) - excinfo.traceback = self.colitem.prunetraceback(excinfo.traceback) entry = excinfo.traceback.getcrashentry() where = entry.path, entry.lineno if excinfo.errisinstance((Skipped,Skipped2)): From hpk at codespeak.net Wed Aug 6 18:13:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 18:13:23 +0200 (CEST) Subject: [py-svn] r57034 - in py/branch/event/py: code code/testing test2 test2/rep test2/rep/testing test2/testing Message-ID: <20080806161323.6B60F168502@codespeak.net> Author: hpk Date: Wed Aug 6 18:13:21 2008 New Revision: 57034 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_session.py Log: simplify outcomerepr Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Wed Aug 6 18:13:21 2008 @@ -47,6 +47,13 @@ """ return True if the exception is an instance of exc """ return isinstance(self.value, exc) + def _getreprcrash(self): + exconly = self.exconly(tryshort=True) + entry = self.traceback.getcrashentry() + path, lineno = entry.path, entry.lineno + reprcrash = ReprFileLocation(path, lineno, exconly) + return reprcrash + def getrepr(self, showlocals=False, style="long", tbfilter=True, funcargs=False): """ return str()able representation of this exception info. showlocals: show locals per traceback entry @@ -196,8 +203,8 @@ def repr_excinfo(self, excinfo): reprtraceback = self.repr_traceback(excinfo) - exconly = excinfo.exconly(tryshort=True) - return ReprExceptionInfo(reprtraceback, exconly) + reprcrash = excinfo._getreprcrash() + return ReprExceptionInfo(reprtraceback, reprcrash) class Repr: def __str__(self): @@ -209,9 +216,9 @@ return "<%s instance at %0x>" %(self.__class__, id(self)) class ReprExceptionInfo(Repr): - def __init__(self, reprtraceback, exconly): + def __init__(self, reprtraceback, reprcrash): self.reprtraceback = reprtraceback - self.exconly = exconly + self.reprcrash = reprcrash self.sections = [] def addsection(self, name, content, sep="-"): Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Wed Aug 6 18:13:21 2008 @@ -401,7 +401,8 @@ repr = p.repr_excinfo(excinfo) assert repr.reprtraceback assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries) - assert repr.exconly == "ValueError: 0" + assert repr.reprcrash.path.endswith("mod.py") + assert repr.reprcrash.message == "ValueError: 0" def test_repr_excinfo_addouterr(self): mod = self.importasmod(""" @@ -416,6 +417,18 @@ assert twmock.lines[-1] == "content" assert twmock.lines[-2] == ("-", "title") + def test_repr_excinfo_reprcrash(self): + mod = self.importasmod(""" + def entry(): + raise ValueError() + """) + excinfo = py.test.raises(ValueError, mod.entry) + repr = excinfo.getrepr() + assert repr.reprcrash.path.endswith("mod.py") + assert repr.reprcrash.lineno == 2 + assert repr.reprcrash.message == "ValueError" + assert str(repr.reprcrash).endswith("mod.py:2: ValueError") + def test_repr_traceback_recursion(self): mod = self.importasmod(""" def rec2(x): Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Wed Aug 6 18:13:21 2008 @@ -41,7 +41,8 @@ def _folded_skips(self): d = {} for event in self._skipped: - key = event.outcome.where + (event.outcome.exconly,) + longrepr = event.outcome.longrepr + key = longrepr.path, longrepr.lineno, longrepr.message d.setdefault(key, []).append(event) l = [] for key, events in d.iteritems(): Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Wed Aug 6 18:13:21 2008 @@ -31,9 +31,9 @@ def rep_CollectionReport(self, ev): super(CollectonlyReporter, self).rep_CollectionReport(ev) if ev.failed: - self.outindent("!!! %s !!!" % ev.failed.exconly) + self.outindent("!!! %s !!!" % ev.failed.longrepr.reprcrash.message) elif ev.skipped: - self.outindent("!!! %s !!!" % ev.skipped.exconly) + self.outindent("!!! %s !!!" % ev.skipped.longrepr.message) self.indent = self.indent[:-len(self.INDENT)] def rep_SessionFinish(self, session): Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 18:13:21 2008 @@ -30,9 +30,13 @@ super(TerminalReporter, self).rep_CollectionReport(ev) fspath = ev.colitem.fspath if ev.failed or ev.skipped: + if ev.skipped: + msg = ev.outcome.longrepr.message + else: + msg = ev.outcome.longrepr.reprcrash.message self.out.line() relpath = getrelpath(self.curdir, fspath) - self.out.write(relpath + " - " + str(ev.outcome.exconly)) + self.out.write(relpath + " - " + str(msg)) self.currentfspath = fspath def rep_SessionStart(self, ev): Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Wed Aug 6 18:13:21 2008 @@ -52,10 +52,12 @@ def test_skip_reasons(self): rep = BaseReporter() - reprpath = ('xyz', 3) - exconly = "justso" - out1 = OutcomeRepr(None, None, None, exconly, where=reprpath) - out2 = OutcomeRepr(None, None, None, exconly, where=reprpath) + class longrepr: + path = 'xyz' + lineno = 3 + message = "justso" + out1 = OutcomeRepr(None, None, longrepr) + out2 = OutcomeRepr(None, None, longrepr) ev1 = repevent.CollectionReport(None, None, skipped=out1) ev2 = repevent.ItemTestReport(None, skipped=out2) rep.processevent(ev1) @@ -65,6 +67,6 @@ assert len(l) == 1 num, fspath, lineno, reason = l[0] assert num == 2 - assert fspath == reprpath[0] - assert lineno == reprpath[1] - assert reason == exconly + assert fspath == longrepr.path + assert lineno == longrepr.lineno + assert reason == longrepr.message Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Wed Aug 6 18:13:21 2008 @@ -45,13 +45,10 @@ return self.makereport(res, when, excinfo, outerr) def getkw(self, when, excinfo, outerr): - exconly = excinfo.exconly(tryshort=True) - entry = excinfo.traceback.getcrashentry() - where = entry.path, entry.lineno if excinfo.errisinstance((Skipped,Skipped2)): outcome = "skipped" shortrepr = "s" - longrepr = "" + longrepr = excinfo._getreprcrash() else: outcome = "failed" if when == "execute": @@ -60,7 +57,7 @@ else: longrepr = self.colitem._getfailurerepr_py(excinfo, outerr) shortrepr = self.colitem.shortfailurerepr.lower() - kw = { outcome: OutcomeRepr(when, shortrepr, longrepr, exconly, where)} + kw = { outcome: OutcomeRepr(when, shortrepr, longrepr)} return kw class ItemRunner(RobustRun): @@ -76,7 +73,7 @@ if self.pdb and kw['failed']: self.pdb(excinfo) else: - kw = {'passed': OutcomeRepr(when, '.', "", "", None)} + kw = {'passed': OutcomeRepr(when, '.', "")} return repevent.ItemTestReport(self.colitem, **kw) class CollectorRunner(RobustRun): @@ -90,7 +87,7 @@ if excinfo: kw = self.getkw(when, excinfo, outerr) else: - kw = {'passed': OutcomeRepr(when, '', "", "", None)} + kw = {'passed': OutcomeRepr(when, '', "")} return repevent.CollectionReport(self.colitem, res, **kw) class SkipRunner(RobustRun): @@ -113,12 +110,10 @@ # class OutcomeRepr(object): - def __init__(self, when, shortrepr, longrepr, exconly, where): + def __init__(self, when, shortrepr, longrepr): self.when = when self.shortrepr = shortrepr self.longrepr = longrepr - self.exconly = exconly - self.where = where def __str__(self): return " Author: hpk Date: Wed Aug 6 19:41:24 2008 New Revision: 57036 Removed: py/branch/event/py/code/tbpresent.py py/branch/event/py/code/testing/test_tbpresent.py py/branch/event/py/test2/pypresent.py py/branch/event/py/test2/testing/test_pypresent.py Modified: py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py Log: remove previous traceback presentation logic now that the new is nicely in place. Deleted: /py/branch/event/py/code/tbpresent.py ============================================================================== --- /py/branch/event/py/code/tbpresent.py Wed Aug 6 19:41:24 2008 +++ (empty file) @@ -1,132 +0,0 @@ -""" - Present Python tracebacks in a nice way. -""" -import py -from py.__.code import safe_repr - -class TBPresenter(object): - """ presenting information about failing Functions and Generators. """ - # for traceback entries - flow_marker = ">" - fail_marker = "E" - - def __init__(self, out=None, showlocals=False, style="long"): - self.showlocals = showlocals - self.style = style - if out is None: - self.out = py.io.TerminalWriter(stringio=True) - else: - self.out = out - - def repr_source(self, source, marker_location=-1): - """ This one represents piece of source with possible - marker at requested position - """ - if source is None: - self.out.line("???") - return - if isinstance(source, str): - source = source.split("\n") - if marker_location < 0: - marker_location += len(source) - if marker_location < 0: - marker_location = 0 - if marker_location >= len(source): - marker_location = len(source) - 1 - for i in range(len(source)): - if i == marker_location: - prefix = self.flow_marker + " " - else: - prefix = " " - self.out.line(prefix + source[i]) - - def repr_failure_explanation(self, excinfo, source): - try: - s = str(source.getstatement(len(source)-1)) - except KeyboardInterrupt: - raise - except: - s = str(source[-1]) - indent = 4 + (len(s) - len(s.lstrip())) - self.repr_exconly(excinfo, indent=indent) - - def repr_exconly(self, excinfo, indent): - indent = " " * indent - # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - self.out.line(self.fail_marker + indent[:-1] + lines.pop(0)) - for x in lines: - self.out.line(indent + x) - - def getentrysource(self, entry): - try: - source = entry.getsource() - except py.error.ENOENT: - source = "???" - return source.deindent() - - def _saferepr(self, obj): - return safe_repr._repr(obj) - - def repr_locals(self, f_locals): - if self.showlocals: - self.out.sep('- ', 'locals') - for name, value in f_locals.items(): - if name == '__builtins__': - self.out.line("__builtins__ = ") - else: - # This formatting could all be handled by the _repr() function, which is - # only repr.Repr in disguise, so is very configurable. - str_repr = safe_repr._repr(value) - if len(str_repr) < 70 or not isinstance(value, - (list, tuple, dict)): - self.out.line("%-10s = %s" %(name, str_repr)) - else: - self.out.line("%-10s =\\" % (name,)) - py.std.pprint.pprint(value, stream=self.out) - - def repr_tb_entry(self, entry, excinfo=None): - # excinfo is not None if this is the last tb entry - source = self.getentrysource(entry) - firstsourceline = entry.getfirstlinesource() - marker_location = entry.lineno - firstsourceline - - if self.style == "long": - self.repr_source(source, marker_location) - # filename and lineno output for each entry, - # using an output format that most editors unterstand - loc = "%s:%d:" %(entry.path, entry.lineno+1) - if excinfo: - self.repr_failure_explanation(excinfo, source) - loc += " %r" % excinfo.exconly() - self.out.line("") - self.out.line(loc) - self.repr_locals(entry.locals) - else: - if self.style == "short": - line = source[marker_location].lstrip() - self.out.line(' File "%s", line %d, in %s' % ( - entry.path.basename, entry.lineno+1, entry.name)) - self.out.line(" " + line) - if excinfo: - self.repr_exconly(excinfo, indent=4) - - def repr_sep(self, sep): - if self.style == "long": - self.out.sep(sep) - - def repr_tb(self, excinfo): - traceback = excinfo.traceback - recursionindex = traceback.recursionindex() - last = traceback[-1] - for index, entry in py.builtin.enumerate(traceback): - if last != entry: - self.repr_tb_entry(entry) - self.repr_sep("_ ") - else: - self.repr_tb_entry(entry, excinfo) - self.repr_sep("_") - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break Deleted: /py/branch/event/py/code/testing/test_tbpresent.py ============================================================================== --- /py/branch/event/py/code/testing/test_tbpresent.py Wed Aug 6 19:41:24 2008 +++ (empty file) @@ -1,213 +0,0 @@ - -import py -import re -from py.__.code.tbpresent import TBPresenter - -class TestTracebackPresenter: - def setup_class(cls): - cls.clstmpdir = py.test2.ensuretemp(cls.__name__) - - def setup_method(self, method): - self.tmpdir = self.clstmpdir.ensure(method.__name__, dir=1) - - def getpresenter(self, **kwargs): - return TBPresenter(**kwargs) - - def test_repr_source(self): - pr = self.getpresenter() - source = py.code.Source(""" - def f(x): - pass - """).strip() - pr.flow_marker = "|" - pr.repr_source(source, 0) - lines = pr.out.stringio.getvalue().split("\n") - assert len(lines) == 3 - assert lines[0].startswith("|") - assert lines[0].find("def f(x)") != -1 - assert lines[1].find("pass") != -1 - - def test_repr_failure_explanation(self): - """ check if indentation is right """ - def f(): - def g(): - 1/0 - try: - g() - except: - e = py.code.ExceptionInfo() - return e - - pr = self.getpresenter() - source = py.code.Source(f) - e = f() - pr.repr_failure_explanation(e, source) - assert pr.out.stringio.getvalue().startswith("E ") - - def test_repr_local(self): - p = self.getpresenter(showlocals=True) - loc = locals() - p.repr_locals(loc) - result = p.out.stringio.getvalue() - for key in loc.keys(): - assert result.find(key) != -1 - - def importasmod(self, source): - source = py.code.Source(source) - modpath = self.tmpdir.join("mod.py") - self.tmpdir.ensure("__init__.py") - modpath.write(source) - return modpath.pyimport() - - def test_repr_tbentry(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-2]) - s = p.out.stringio.getvalue() - lines = s.split("\n") - - # test intermittent entries - - assert lines[0] == " def entry():" - assert lines[1] == "> func1()" - assert not lines[2] - assert lines[3] == "%s:5:" %(mod.__file__,) - - # test last entry - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - lines = s.split("\n") - print s - assert lines[0] == " def func1():" - assert lines[1] == '> raise ValueError("hello")' - assert lines[2] == "E ValueError: hello" - assert not lines[3] - assert lines[4] == "%s:3: 'ValueError: hello'" %(mod.__file__,) - - - def test_repr_tbentry_short(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter(style="short") - p.repr_tb_entry(excinfo.traceback[-2]) - s = p.out.stringio.getvalue() - lines = s.split("\n") - basename = py.path.local(mod.__file__).basename - assert lines[0] == ' File "%s", line 5, in entry' % basename - assert lines[1] == ' func1()' - assert not lines[2] - - # test last entry - print s - p = self.getpresenter(style="short") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - - lines = s.split("\n") - assert lines[0] == ' File "%s", line 3, in func1' % basename - assert lines[1] == ' raise ValueError("hello")' - assert lines[2] == 'E ValueError: hello' - assert not lines[3] - - def test_repr_tbentry_no(self): - mod = self.importasmod(""" - def func1(): - raise ValueError("hello") - def entry(): - func1() - """) - excinfo = py.test.raises(ValueError, mod.entry) - p = self.getpresenter(style="no") - p.repr_tb_entry(excinfo.traceback[-2]) - s = p.out.stringio.getvalue() - assert not s - p = self.getpresenter(style="no") - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - lines = s.split("\n") - assert lines[0] == 'E ValueError: hello' - - - def test_repr_tb(self): - mod = self.importasmod(""" - def f(x): - raise ValueError(x) - def entry(): - f(0) - """) - p = self.getpresenter(style="short") - excinfo = py.test.raises(ValueError, mod.entry) - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - s = p.repr_tb(excinfo) - print l - assert l[-1] == "_" - entry, excinfo2 = l[-2] - assert excinfo == excinfo2 - assert entry == excinfo.traceback[-1] - assert l[-3] == "_ " - entry, excinfo2 = l[-4] - assert excinfo2 is None - assert entry == excinfo.traceback[-2] - - def test_repr_tb_recursion(self): - mod = self.importasmod(""" - def rec2(x): - return rec1(x+1) - def rec1(x): - return rec2(x-1) - def entry(): - rec1(42) - """) - excinfo = py.test.raises(RuntimeError, mod.entry) - - for style in ("short", "long"): - p = self.getpresenter(style="short") - l = [] - p.repr_tb_entry = lambda entry, excinfo=None: l.append((entry,excinfo)) - p.repr_sep = lambda sep: l.append(sep) - p.repr_tb(excinfo) - s = p.out.stringio.getvalue() - print l - #assert re.search(".*return rec1.*x\+1", s) - #assert re.search(".*return rec2.*x-1.*", s) - assert len(l) < 20 - assert re.search("Recursion detected", s) - assert l[-1] == "_ " - assert l[-2][0].lineno == 4 - - def test_tb_entry_AssertionError(self): - # probably this test is a bit redundant - # as py/magic/testing/test_assertion.py - # already tests correctness of - # assertion-reinterpretation logic - mod = self.importasmod(""" - def somefunc(): - x = 1 - assert x == 2 - """) - py.magic.invoke(assertion=True) - try: - excinfo = py.test.raises(AssertionError, mod.somefunc) - finally: - py.magic.revoke(assertion=True) - - p = self.getpresenter() - p.repr_tb_entry(excinfo.traceback[-1], excinfo) - s = p.out.stringio.getvalue() - print s - lines = s.split("\n") - assert lines[-4] == "E assert 1 == 2" Deleted: /py/branch/event/py/test2/pypresent.py ============================================================================== --- /py/branch/event/py/test2/pypresent.py Wed Aug 6 19:41:24 2008 +++ (empty file) @@ -1,83 +0,0 @@ -""" - Some specifics about reporting Python tracebacks in py.test -""" -import py -from py.__.code.tbpresent import TBPresenter - -class PythonFunctionPresenter(TBPresenter): - """ presenting information about failing Functions and Generators. """ - def __init__(self, config): - self._config = config - super(PythonFunctionPresenter, self).__init__( - showlocals=config.option.showlocals, - style=config.option.tbstyle, - ) - - def header(self, runinfo, title=None): - item = runinfo.item - if title is None: - modpath = item.repr_path()[1] - title = "entrypoint: %s" % modpath - self.out.sep("_", title) - self.out.line("") - - if isinstance(item, py.test2.collect.Function) and item.name[-1] == "]": - if runinfo.outcome == "failed" and not self._config.option.fulltrace: - # we are the header of a failed generated function - args = self._saferepr(item._args) - line = "%s%s -> %s%s" %(item.parent.name, item.name, - item.obj.__name__, args) - self.out.line(line) - - def repr_run(self, runinfo, title=None): - item = runinfo.item - excinfo = runinfo.excinfo - outerr = runinfo.outerr - self.header(runinfo, title) - if excinfo: - excinfo.traceback = item.prunetraceback(excinfo.traceback) - self.repr_tb(excinfo) - for name, value in zip(["recorded stdout", "recorded stderr"], outerr): - if value: - self.out.sep("- ", "%s, len=%d" %(name, len(value))) - self.out.line(value.rstrip()) - self.repr_sep("_") - -def python_repr_run(runinfo, title=None): - p = PythonFunctionPresenter(runinfo.item._config) - p.repr_run(runinfo, title=title) - return p.out.stringio.getvalue() - -def getmodpath(pycolitem): - """ return dotted module path for the given colitem. """ - colitems = pycolitem.listchain() - while colitems: - colitem = colitems.pop(0) - if isinstance(colitem, colitem.Module): - parts = [colitem.obj.__name__] - for colitem in colitems: - if colitem.name[0] in '([': - parts[-1] += colitem.name - else: - parts.append(colitem.name) - return ".".join(parts) - return colitem.name - -def repr_pythonversion(): - v = py.std.sys.version_info - try: - return "%s.%s.%s-%s-%s" % v - except (TypeError, ValueError): - return str(v) - -def getrelpath(source, dest): - base = source.common(dest) - if not base: - return None - # with posix local paths '/' is always a common base - relsource = source.relto(base) - reldest = dest.relto(base) - n = relsource.count(source.sep) - target = dest.sep.join(('..', )*n + (reldest, )) - return target - Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Wed Aug 6 19:41:24 2008 @@ -1,4 +1,5 @@ from py.__.test2 import repevent +import sys class BaseReporter(object): def __init__(self): @@ -48,3 +49,37 @@ for key, events in d.iteritems(): l.append((len(events),) + key) return l + + +def getmodpath(pycolitem): + """ return dotted module path for the given colitem. """ + colitems = pycolitem.listchain() + while colitems: + colitem = colitems.pop(0) + if isinstance(colitem, colitem.Module): + parts = [colitem.obj.__name__] + for colitem in colitems: + if colitem.name[0] in '([': + parts[-1] += colitem.name + else: + parts.append(colitem.name) + return ".".join(parts) + return colitem.name + +def repr_pythonversion(): + v = sys.version_info + try: + return "%s.%s.%s-%s-%s" % v + except (TypeError, ValueError): + return str(v) + +def getrelpath(source, dest): + base = source.common(dest) + if not base: + return None + # with posix local paths '/' is always a common base + relsource = source.relto(base) + reldest = dest.relto(base) + n = relsource.count(source.sep) + target = dest.sep.join(('..', )*n + (reldest, )) + return target Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 19:41:24 2008 @@ -4,7 +4,7 @@ from py.__.test2.rep.base import BaseReporter from py.__.test2.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped -from py.__.test2.pypresent import getrelpath, repr_pythonversion +from py.__.test2.rep.base import getrelpath, repr_pythonversion class TerminalReporter(BaseReporter): def __init__(self, config, file=None): Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Wed Aug 6 19:41:24 2008 @@ -3,6 +3,9 @@ from py.__.test2.eventbus import EventBus from py.__.test2 import repevent from py.__.test2.runner import OutcomeRepr +from py.__.test2.rep.base import getrelpath, getmodpath, repr_pythonversion +from py.__.test2.testing import setupdata +import sys class TestBaseReporter: def test_activate(self): @@ -70,3 +73,35 @@ assert fspath == longrepr.path assert lineno == longrepr.lineno assert reason == longrepr.message + +def test_getmodpath_cases(): + tmpdir = py.test.ensuretemp("test_getmodpath_cases") + pkgdir = tmpdir.join("pkg") + pkgdir.ensure("__init__.py") + nopkgdir = tmpdir.ensure("nopkg", dir=1) + def checkpkg(names, expected): + fcol = setupdata.getexamplecollector(names, tmpdir=pkgdir) + assert getmodpath(fcol) == pkgdir.basename + "." + expected + def checknopkg(names, expected): + fcol = setupdata.getexamplecollector(names, tmpdir=nopkgdir) + assert getmodpath(fcol) == expected + + for names in ( + 'mod.py test_f1 mod.test_f1', + 'mod.py TestA () test_m1 mod.TestA().test_m1', + 'mod.py test_g1 mod.test_g1', + 'mod.py test_g1 [0] mod.test_g1[0]', + ): + names = names.split() + expected = names.pop() + yield checkpkg, names, expected + yield checknopkg, names, expected + +def test_repr_python_version(): + py.magic.patch(sys, 'version_info', (2, 5, 1, 'final', 0)) + try: + assert repr_pythonversion() == "2.5.1-final-0" + py.std.sys.version_info = x = (2,3) + assert repr_pythonversion() == str(x) + finally: + py.magic.revert(sys, 'version_info') Deleted: /py/branch/event/py/test2/testing/test_pypresent.py ============================================================================== --- /py/branch/event/py/test2/testing/test_pypresent.py Wed Aug 6 19:41:24 2008 +++ (empty file) @@ -1,76 +0,0 @@ -import py -from py.__.test2 import pypresent -from py.__.test2.testing import setupdata -import suptest, setupdata -import sys - - -class TestPresenter: - def setup_class(cls): - py.test.skip("recheck pypresent") - cls.tmpdir = py.test2.ensuretemp(cls.__name__) - - def getpresenter(self, cmdlinearg=None): - args = [self.tmpdir] - if cmdlinearg: - args.append(cmdlinearg) - config = py.test2.config._reparse(args) - return pypresent.FuncPresenter(config) - - def test_repr_pruning_tb_generated_test(self): - itemtestreport,fn = suptest.getItemTestReport(""" - def test_gen(): - def check(x): - assert x - yield check, 0 - """) - s = itemtestreport.repr_run - print s - lines = s.split("\n") - - suptest.assert_lines_contain_lines(lines, py.code.Source(""" - test_gen[0] -> check(0,) - def check(x): - """).strip()) - - assert lines[0].find("test_0.test_0.test_gen[0]") != -1 - assert lines[2].find("test_gen[0] -> check(0,)") != -1 - assert lines[3].find("def check(x):") != -1 - - def test_python_repr_run(self): - py.test.skip("xxx test/refactor pypresent...") - runinfo = RunInfo(item, "failed", excinfo, ("", "")) - pypresent.python_repr_run(runinfo, title="hello") - - -def test_getmodpath_cases(): - tmpdir = py.test.ensuretemp("test_getmodpath_cases") - pkgdir = tmpdir.join("pkg") - pkgdir.ensure("__init__.py") - nopkgdir = tmpdir.ensure("nopkg", dir=1) - def checkpkg(names, expected): - fcol = setupdata.getexamplecollector(names, tmpdir=pkgdir) - assert pypresent.getmodpath(fcol) == pkgdir.basename + "." + expected - def checknopkg(names, expected): - fcol = setupdata.getexamplecollector(names, tmpdir=nopkgdir) - assert pypresent.getmodpath(fcol) == expected - - for names in ( - 'mod.py test_f1 mod.test_f1', - 'mod.py TestA () test_m1 mod.TestA().test_m1', - 'mod.py test_g1 mod.test_g1', - 'mod.py test_g1 [0] mod.test_g1[0]', - ): - names = names.split() - expected = names.pop() - yield checkpkg, names, expected - yield checknopkg, names, expected - -def test_repr_python_version(): - py.magic.patch(py.std.sys, 'version_info', (2, 5, 1, 'final', 0)) - try: - assert pypresent.repr_pythonversion() == "2.5.1-final-0" - py.std.sys.version_info = x = (2,3) - assert pypresent.repr_pythonversion() == str(x) - finally: - py.magic.revert(py.std.sys, 'version_info') From hpk at codespeak.net Wed Aug 6 19:42:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 19:42:35 +0200 (CEST) Subject: [py-svn] r57037 - py/branch/event/py/code/testing Message-ID: <20080806174235.5AE69168507@codespeak.net> Author: hpk Date: Wed Aug 6 19:42:34 2008 New Revision: 57037 Removed: py/branch/event/py/code/testing/test_cpython_features.py Modified: py/branch/event/py/code/testing/test_code.py Log: merge tests Modified: py/branch/event/py/code/testing/test_code.py ============================================================================== --- py/branch/event/py/code/testing/test_code.py (original) +++ py/branch/event/py/code/testing/test_code.py Wed Aug 6 19:42:34 2008 @@ -1,5 +1,6 @@ from __future__ import generators import py +import new def test_newcode(): source = "i = 3" @@ -55,3 +56,17 @@ s = py.code.Source(newco) assert str(s) == source + +def test_new_code_object_carries_filename_through(): + class mystr(str): + pass + filename = mystr("dummy") + co = compile("hello\n", filename, 'exec') + assert not isinstance(co.co_filename, mystr) + c2 = new.code(co.co_argcount, co.co_nlocals, co.co_stacksize, + co.co_flags, co.co_code, co.co_consts, + co.co_names, co.co_varnames, + filename, + co.co_name, co.co_firstlineno, co.co_lnotab, + co.co_freevars, co.co_cellvars) + assert c2.co_filename is filename Deleted: /py/branch/event/py/code/testing/test_cpython_features.py ============================================================================== --- /py/branch/event/py/code/testing/test_cpython_features.py Wed Aug 6 19:42:34 2008 +++ (empty file) @@ -1,16 +0,0 @@ - -import new - -def test_new_code_object_carries_filename_through(): - class mystr(str): - pass - filename = mystr("dummy") - co = compile("hello\n", filename, 'exec') - assert not isinstance(co.co_filename, mystr) - c2 = new.code(co.co_argcount, co.co_nlocals, co.co_stacksize, - co.co_flags, co.co_code, co.co_consts, - co.co_names, co.co_varnames, - filename, - co.co_name, co.co_firstlineno, co.co_lnotab, - co.co_freevars, co.co_cellvars) - assert c2.co_filename is filename From hpk at codespeak.net Wed Aug 6 19:58:42 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 19:58:42 +0200 (CEST) Subject: [py-svn] r57038 - in py/branch/event/py/test2: rep testing Message-ID: <20080806175842.22A4C16850D@codespeak.net> Author: hpk Date: Wed Aug 6 19:58:40 2008 New Revision: 57038 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py Log: cleanup of SessionFinish reporting Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 19:58:40 2008 @@ -46,46 +46,44 @@ def rep_SessionFinish(self, ev): self.out.line("") + self.summary_failures() + self.summary_skips() + self.summary_stats() + + + # + # summaries for SessionFinish + # + + def summary_failures(self): if self._failed: self.out.sep("=", "FAILURES") for ev in self._failed: self.out.sep("_") ev.toterminal(self.out) - if not self._failed or self.config.option.showskipsummary: - folded_skips = self._folded_skips() - if folded_skips: - self.out.sep("_", "skipped test summary") - for num, fspath, lineno, reason in folded_skips: - self.out.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) - #self.out.sep("=", "test session summary") - - + def summary_stats(self): session_duration = py.std.time.time() - self._sessionstarttime - - #self.out.line("session wallclock duration: %.2f seconds" %( - # py.std.time.time() - self._sessionstarttime)) - numfailed = len(self._failed) numskipped = len(self._skipped) numpassed = len(self._passed) - sum = numfailed + numskipped + numpassed - self.out.line() - self.out.sep("=", "%d tests in %.2f seconds" %(sum, session_duration)) - #self.out_hostinfo() + sum = numfailed + numpassed self.out.line() - if numskipped == 0: - self.out.sep("*", "skips: no skips :)") - else: - self.out.sep("*", "skips: %d" %(numskipped)) + self.out.sep("=", "%d/%d passed + %d skips in %.2f seconds" % + (numpassed, sum, numskipped, session_duration)) if numfailed == 0: - self.out.sep("*", "failures: no failures :)") + self.out.sep("=", "failures: no failures :)") else: - self.out.sep("*", "failures: %d" %(numfailed)) + self.out.sep("=", "failures: %d" %(numfailed)) self.out.line() - - #self.out.sep("=", "%d tests of which %d failed, %d skipped" %( - # sum, numfailed, numskipped)) + + def summary_skips(self): + if not self._failed or self.config.option.showskipsummary: + folded_skips = self._folded_skips() + if folded_skips: + self.out.sep("_", "skipped test summary") + for num, fspath, lineno, reason in folded_skips: + self.out.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) def out_hostinfo(self): self.out.line("host 0: %s %s - Python %s" % Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 19:58:40 2008 @@ -147,7 +147,6 @@ old.chdir() extra = assert_lines_contain_lines(result.outlines, [ "test_one.py ..", - "* skips: no skips*", "* failures: no failures*", ]) @@ -163,8 +162,7 @@ "host 0: %s %s - Python %s*" %( py.std.sys.platform, py.std.sys.executable, verinfo), "*test_one.py .", - "==* 1 tests in *.[0-9][0-9] seconds *", - "* no skips :)*", + "==* 1/1 passed + 0 skips in *.[0-9][0-9] seconds *", "* no failures :)*", ]) Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Wed Aug 6 19:58:40 2008 @@ -130,6 +130,7 @@ """ assert that lines2 are contained (linearly) in lines1. return a list of extralines found. """ + __tracebackhide__ = True if isinstance(lines2, str): lines2 = py.code.Source(lines2) if isinstance(lines2, py.code.Source): From hpk at codespeak.net Wed Aug 6 20:33:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 20:33:09 +0200 (CEST) Subject: [py-svn] r57039 - in py/branch/event/py/io: . testing Message-ID: <20080806183309.9BEE5168535@codespeak.net> Author: hpk Date: Wed Aug 6 20:33:08 2008 New Revision: 57039 Modified: py/branch/event/py/io/terminalwriter.py py/branch/event/py/io/testing/test_terminalwriter.py Log: add markup and ansi-color codes for terminal printing Modified: py/branch/event/py/io/terminalwriter.py ============================================================================== --- py/branch/event/py/io/terminalwriter.py (original) +++ py/branch/event/py/io/terminalwriter.py Wed Aug 6 20:33:08 2008 @@ -42,7 +42,29 @@ if flush: file.flush() +def ansi_print(text, esc, file=None, newline=True, flush=False): + if file is None: + file = sys.stderr + text = text.rstrip() + if esc and sys.platform != "win32" and file.isatty(): + if not isinstance(esc, tuple): + esc = (esc,) + text = (''.join(['\x1b[%sm' % cod for cod in esc]) + + text + + '\x1b[0m') # ANSI color code "reset" + if newline: + text += '\n' + file.write(text) + if flush: + file.flush() + class TerminalWriter(object): + _esctable = dict(black=30, red=31, green=32, yellow=33, + blue=34, purple=35, cyan=36, white=37, + Black=40, Red=41, Green=42, Yellow=43, + Blue=44, Purple=45, Cyan=46, White=47, + bold=1, light=2, blink=5, invert=7) + def __init__(self, file=None, stringio=False): if file is None: if stringio: @@ -54,6 +76,20 @@ self._file = file self.fullwidth = get_terminal_width() + def _escaped(self, text, esc): + if esc and sys.platform != "win32": + if hasattr(self._file, 'isatty') and self._file.isatty(): + text = (''.join(['\x1b[%sm' % cod for cod in esc]) + + text +'\x1b[0m') + return text + + def markup(self, text, **kw): + esc = [] + for name in kw: + if name in self._esctable: + esc.append(self._esctable[name]) + return self._escaped(text, tuple(esc)) + def sep(self, sepchar, title=None, fullwidth=None): if not fullwidth: fullwidth = self.fullwidth Modified: py/branch/event/py/io/testing/test_terminalwriter.py ============================================================================== --- py/branch/event/py/io/testing/test_terminalwriter.py (original) +++ py/branch/event/py/io/testing/test_terminalwriter.py Wed Aug 6 20:33:08 2008 @@ -45,6 +45,18 @@ assert len(l) == 1 assert l[0] == "-" * 26 + " hello " + "-" * 27 + "\n" + def test__escaped(self): + tw = self.getwriter() + text2 = tw._escaped("hello", (31)) + assert text2.find("hello") != -1 + + def test_markup(self): + tw = self.getwriter() + for bold in (True, False): + for color in ("red", "green"): + text2 = tw.markup("hello", color=color, bold=bold) + assert text2.find("hello") != -1 + class TestStringIO(BaseTests): def getwriter(self): self.tw = py.io.TerminalWriter(stringio=True) From hpk at codespeak.net Wed Aug 6 20:44:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 20:44:45 +0200 (CEST) Subject: [py-svn] r57040 - in py/branch/event/py: code io test2/rep Message-ID: <20080806184445.199991684B1@codespeak.net> Author: hpk Date: Wed Aug 6 20:44:44 2008 New Revision: 57040 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/io/terminalwriter.py py/branch/event/py/test2/rep/terminal.py Log: using ansi colors at some places in the test output Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Wed Aug 6 20:44:44 2008 @@ -262,7 +262,8 @@ if self.reprfuncargs: self.reprfuncargs.toterminal(tw) for line in self.lines: - tw.line(line) + red = line.startswith("E ") + tw.line(tw.markup(bold=True, red=red, text=line)) if self.reprlocals: tw.sep(self.localssep, "Locals") self.reprlocals.toterminal(tw) Modified: py/branch/event/py/io/terminalwriter.py ============================================================================== --- py/branch/event/py/io/terminalwriter.py (original) +++ py/branch/event/py/io/terminalwriter.py Wed Aug 6 20:44:44 2008 @@ -86,7 +86,7 @@ def markup(self, text, **kw): esc = [] for name in kw: - if name in self._esctable: + if kw[name] and name in self._esctable: esc.append(self._esctable[name]) return self._escaped(text, tuple(esc)) Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 20:44:44 2008 @@ -69,12 +69,13 @@ numpassed = len(self._passed) sum = numfailed + numpassed self.out.line() - self.out.sep("=", "%d/%d passed + %d skips in %.2f seconds" % - (numpassed, sum, numskipped, session_duration)) + self.out.sep("=", self.out.markup(bold=True, + text="%d/%d passed + %d skips in %.2f seconds" % + (numpassed, sum, numskipped, session_duration))) if numfailed == 0: - self.out.sep("=", "failures: no failures :)") + self.out.sep("=", self.out.markup(green=True, text="failures: no failures :)")) else: - self.out.sep("=", "failures: %d" %(numfailed)) + self.out.sep("=", self.out.markup(red=True, text="failures: %d" %(numfailed))) self.out.line() def summary_skips(self): From hpk at codespeak.net Wed Aug 6 20:47:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 20:47:44 +0200 (CEST) Subject: [py-svn] r57041 - py/branch/event/py/code/testing Message-ID: <20080806184744.536051684B1@codespeak.net> Author: hpk Date: Wed Aug 6 20:47:42 2008 New Revision: 57041 Modified: py/branch/event/py/code/testing/test_excinfo.py Log: mock tw.markup function() Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Wed Aug 6 20:47:42 2008 @@ -8,6 +8,9 @@ self.lines.append((sep, line)) def line(self, line): self.lines.append(line) + def markup(self, text, **kw): + return text + fullwidth = 80 def test_excinfo_simple(): From hpk at codespeak.net Wed Aug 6 21:04:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 21:04:41 +0200 (CEST) Subject: [py-svn] r57043 - in py/branch/event/py: io io/testing test2/rep Message-ID: <20080806190441.5B8CE16850D@codespeak.net> Author: hpk Date: Wed Aug 6 21:04:39 2008 New Revision: 57043 Modified: py/branch/event/py/io/terminalwriter.py py/branch/event/py/io/testing/test_terminalwriter.py py/branch/event/py/test2/rep/terminal.py Log: some more fun markup - colors now help to navigate through the (nicer also for other reasons) tracebacks :) Modified: py/branch/event/py/io/terminalwriter.py ============================================================================== --- py/branch/event/py/io/terminalwriter.py (original) +++ py/branch/event/py/io/terminalwriter.py Wed Aug 6 21:04:39 2008 @@ -86,11 +86,13 @@ def markup(self, text, **kw): esc = [] for name in kw: - if kw[name] and name in self._esctable: + if name not in self._esctable: + raise ValueError("unknown markup: %r" %(name,)) + if kw[name]: esc.append(self._esctable[name]) return self._escaped(text, tuple(esc)) - def sep(self, sepchar, title=None, fullwidth=None): + def sep(self, sepchar, title=None, fullwidth=None, **kw): if not fullwidth: fullwidth = self.fullwidth # the goal is to have the line be as long as possible @@ -112,14 +114,16 @@ # trailing space is not important at the end of the line if len(line) + len(sepchar.rstrip()) <= fullwidth: line += sepchar.rstrip() - self.line(line) + + self.line(line, **kw) def write(self, s): self._file.write(str(s)) self._file.flush() - def line(self, s=''): + def line(self, s='', **kw): if s: + s = self.markup(s, **kw) self._file.write(s + '\n') else: self._file.write('\n') Modified: py/branch/event/py/io/testing/test_terminalwriter.py ============================================================================== --- py/branch/event/py/io/testing/test_terminalwriter.py (original) +++ py/branch/event/py/io/testing/test_terminalwriter.py Wed Aug 6 21:04:39 2008 @@ -54,8 +54,10 @@ tw = self.getwriter() for bold in (True, False): for color in ("red", "green"): - text2 = tw.markup("hello", color=color, bold=bold) + text2 = tw.markup("hello", **{color: True, 'bold': bold}) assert text2.find("hello") != -1 + py.test.raises(ValueError, "tw.markup('x', wronkw=3)") + py.test.raises(ValueError, "tw.markup('x', wronkw=0)") class TestStringIO(BaseTests): def getwriter(self): Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Wed Aug 6 21:04:39 2008 @@ -40,7 +40,7 @@ self.currentfspath = fspath def rep_SessionStart(self, ev): - self.out.sep("=", "test session starts") + self.out.sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() self.out_hostinfo() @@ -69,13 +69,12 @@ numpassed = len(self._passed) sum = numfailed + numpassed self.out.line() - self.out.sep("=", self.out.markup(bold=True, - text="%d/%d passed + %d skips in %.2f seconds" % - (numpassed, sum, numskipped, session_duration))) + self.out.sep("=", "%d/%d passed + %d skips in %.2f seconds" % + (numpassed, sum, numskipped, session_duration), bold=True) if numfailed == 0: - self.out.sep("=", self.out.markup(green=True, text="failures: no failures :)")) + self.out.sep("=", "failures: no failures :)", green=True) else: - self.out.sep("=", self.out.markup(red=True, text="failures: %d" %(numfailed))) + self.out.sep("=", "failures: %d" %(numfailed), red=True) self.out.line() def summary_skips(self): From hpk at codespeak.net Wed Aug 6 23:02:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 6 Aug 2008 23:02:52 +0200 (CEST) Subject: [py-svn] r57044 - in py/branch/event/py/test2: . testing Message-ID: <20080806210252.0F6B216852C@codespeak.net> Author: hpk Date: Wed Aug 6 23:02:49 2008 New Revision: 57044 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_doctest.py Log: integrate doctests running into new reporting architecture (and port doctest changes from trunk rev 56285) Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Wed Aug 6 23:02:49 2008 @@ -228,6 +228,8 @@ repr.addsection("Captured std%s" % secname, content.rstrip()) return repr + getfailurerepr = _getfailurerepr_py + shortfailurerepr = "F" class Collector(Node): Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Wed Aug 6 23:02:49 2008 @@ -267,45 +267,52 @@ """ execute the given test function. """ self.obj(*self._args) -class DoctestFile(PyCollectorMixin, FSCollector): +class DoctestFile(FSCollector): def listdir(self): return [self.fspath.basename] def join(self, name): if name == self.fspath.basename: - item = DoctestText(self.fspath.basename, parent=self) - item._content = self.fspath.read() - return item + return DoctestFileContent(name, self) -class DoctestText(Item): - #XXX port changes from dist/trunk - def _setcontent(self, content): - self._content = content +from py.__.code.excinfo import Repr, ReprFileLocation - #def buildname2items(self): - # parser = py.compat.doctest.DoctestParser() - # l = parser.get_examples(self._content) - # d = {} - # globs = {} - # locs - # for i, example in py.builtin.enumerate(l): - # ex = ExampleItem(example) - # d[str(i)] = ex - - def run(self): - mod = py.std.types.ModuleType(self.name) - #for line in s.split('\n'): - # if line.startswith(prefix): - # exec py.code.Source(line[len(prefix):]).compile() in mod.__dict__ - # line = "" - # else: - # l.append(line) - self.execute(mod, self._content) - - def execute(self, mod, docstring): - mod.__doc__ = docstring - failed, tot = py.compat.doctest.testmod(mod, verbose=1) - if failed: - py.test2.fail("doctest %s: %s failed out of %s" %( - self.fspath, failed, tot)) +class ReprFailDoctest(Repr): + def __init__(self, reprlocation, lines): + self.reprlocation = reprlocation + self.lines = lines + def toterminal(self, tw): + for line in self.lines: + tw.line(line) + self.reprlocation.toterminal(tw) + +class DoctestFileContent(Item): + def getfailurerepr(self, excinfo, outerr): + if excinfo.errisinstance(py.compat.doctest.DocTestFailure): + doctestfailure = excinfo.value + example = doctestfailure.example + test = doctestfailure.test + filename = test.filename + lineno = example.lineno + 1 + message = excinfo.type.__name__ + reprlocation = ReprFileLocation(filename, lineno, message) + checker = py.compat.doctest.OutputChecker() + REPORT_UDIFF = py.compat.doctest.REPORT_UDIFF + filelines = py.path.local(filename).readlines(cr=0) + i = max(0, lineno - 10) + lines = [] + for line in filelines[i:lineno]: + lines.append("%03d %s" % (i+1, line)) + i += 1 + lines += checker.output_difference(example, + doctestfailure.got, REPORT_UDIFF).split("\n") + return ReprFailDoctest(reprlocation, lines) + #elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): + else: + return super(DoctestFileContent, self).getfailurerepr(excinfo, outerr) + + def execute(self): + failed, tot = py.compat.doctest.testfile( + str(self.fspath), module_relative=False, + raise_on_error=True, verbose=0) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Wed Aug 6 23:02:49 2008 @@ -25,9 +25,13 @@ return Result(ret, out, err) def makepyfile(self, **kwargs): + return self._makefile('.py', **kwargs) + def maketxtfile(self, **kwargs): + return self._makefile('.txt', **kwargs) + def _makefile(self, ext, **kwargs): ret = None for name, value in kwargs.iteritems(): - p = py.path.local(name).new(ext=".py") + p = py.path.local(name).new(ext=ext) source = py.code.Source(value) p.write(str(py.code.Source(value)).lstrip()) if ret is None: @@ -231,6 +235,23 @@ "x* = 3", "y* = 'xxxxxx*" ]) + + def test_doctest_simple_failing(self): + p = self.maketxtfile(doc=""" + >>> i = 0 + >>> i + 1 + 2 + """) + result = self.runpytest(p) + assert_lines_contain_lines(result.outlines, [ + '001 >>> i = 0', + '002 >>> i + 1', + 'Expected:', + " 2", + "Got:", + " 1", + "*doc.txt:2: DocTestFailure" + ]) def test_looponfailing_looping(self): py.test.skip("thought needed to nicely test --looponfailing") Modified: py/branch/event/py/test2/testing/test_doctest.py ============================================================================== --- py/branch/event/py/test2/testing/test_doctest.py (original) +++ py/branch/event/py/test2/testing/test_doctest.py Wed Aug 6 23:02:49 2008 @@ -1,24 +1,31 @@ import py -from py.__.test2.pycollect import DoctestText -from py.__.test2.outcome import Failed +from py.__.test2.outcome import Failed + +def setup_module(mod): + mod.tmp = py.test.ensuretemp(__name__) def test_simple_docteststring(): - testitem = DoctestText(name="dummy", parent=None, config=42) - testitem._setcontent(""" - >>> i = 0 - >>> i + 1 - 1 - """) - res = testitem.run() + p = tmp.join("test_simple_docteststring") + p.write(py.code.Source(""" + >>> i = 0 + >>> i + 1 + 1 + """)) + testitem = py.test.collect.DoctestFile(p).join(p.basename) + res = testitem.execute() assert res is None -def test_simple_docteststring_failing(): - testitem = DoctestText(name="dummy2", parent=None, config=42) - testitem._setcontent(""" - >>> i = 0 - >>> i + 1 - 2 - """) - py.test2.raises(Failed, "testitem.run()") - + +def test_doctest_unexpected_exception(): + py.test.skip("implement nice doctest repr for unexpected exceptions") + p = tmp.join("test_doctest_unexpected_exception") + p.write(py.code.Source(""" + >>> i = 0 + >>> x + 2 + """)) + testitem = py.test.collect.DoctestFile(p).join(p.basename) + excinfo = py.test.raises(Failed, "testitem.execute()") + repr = testitem.getfailurerepr(excinfo, ("", "")) + assert repr.reprlocation From hpk at codespeak.net Thu Aug 7 08:29:05 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 08:29:05 +0200 (CEST) Subject: [py-svn] r57047 - in py/branch/event/py/code: . testing Message-ID: <20080807062905.5F3611684E5@codespeak.net> Author: hpk Date: Thu Aug 7 08:29:03 2008 New Revision: 57047 Modified: py/branch/event/py/code/code.py py/branch/event/py/code/source.py py/branch/event/py/code/testing/test_code.py py/branch/event/py/code/testing/test_source.py Log: streamline representation of paths from generated code objects (finally) Modified: py/branch/event/py/code/code.py ============================================================================== --- py/branch/event/py/code/code.py (original) +++ py/branch/event/py/code/code.py Thu Aug 7 08:29:03 2008 @@ -59,12 +59,13 @@ def path(self): """ return a py.path.local object wrapping the source of the code """ + fn = self.raw.co_filename try: - return self.raw.co_filename.__path__ + return fn.__path__ except AttributeError: p = py.path.local(self.raw.co_filename) - if not p.check() and self.raw.co_filename == "": - p = "" + if not p.check(): + p = self.raw.co_filename return p path = property(path, None, None, "path of this code object") Modified: py/branch/event/py/code/source.py ============================================================================== --- py/branch/event/py/code/source.py (original) +++ py/branch/event/py/code/source.py Thu Aug 7 08:29:03 2008 @@ -166,16 +166,20 @@ def __str__(self): return "\n".join(self.lines) - def compile(self, filename=None, mode='exec', - flag=generators.compiler_flag, dont_inherit=0): + def compile(self, filename=None, mode='exec', flag=generators.compiler_flag, + dont_inherit=0, _genframe=None): """ return compiled code object. if filename is None invent an artificial filename which displays the source/line position of the caller frame. """ if not filename or py.path.local(filename).check(file=0): - frame = sys._getframe(1) # the caller - filename = '%s<%s:%d>' % (filename, frame.f_code.co_filename, - frame.f_lineno) + if _genframe is None: + _genframe = sys._getframe(1) # the caller + fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno + if not filename: + filename = '' % (fn, lineno) + else: + filename = '' % (filename, fn, lineno) source = "\n".join(self.lines) + '\n' try: co = cpy_compile(source, filename, mode, flag) @@ -209,13 +213,14 @@ also have this special subclass-of-string filename. """ + _genframe = sys._getframe(1) # the caller s = Source(source) - co = s.compile(filename, mode, flags) + co = s.compile(filename, mode, flags, _genframe=_genframe) return co # -# various helper functions +# helper functions # class MyStr(str): """ custom string which allows to add attributes. """ Modified: py/branch/event/py/code/testing/test_code.py ============================================================================== --- py/branch/event/py/code/testing/test_code.py (original) +++ py/branch/event/py/code/testing/test_code.py Thu Aug 7 08:29:03 2008 @@ -70,3 +70,10 @@ co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars, co.co_cellvars) assert c2.co_filename is filename + +def test_code_gives_back_name_for_not_existing_file(): + name = 'abc-123' + co_code = compile("pass\n", name, 'exec') + assert co_code.co_filename == name + code = py.code.Code(co_code) + assert str(code.path) == name Modified: py/branch/event/py/code/testing/test_source.py ============================================================================== --- py/branch/event/py/code/testing/test_source.py (original) +++ py/branch/event/py/code/testing/test_source.py Thu Aug 7 08:29:03 2008 @@ -199,6 +199,24 @@ #print "block", str(block) assert str(stmt).strip().startswith('assert') + def test_compilefuncs_and_path_sanity(self): + def check(comp, name): + co = comp(self.source, name) + if not name: + expected = "" %(mypath, mylineno+2+1) + else: + expected = "" % (name, mypath, mylineno+2+1) + fn = co.co_filename + assert fn == expected + + mycode = py.code.Code(self.test_compilefuncs_and_path_sanity) + mylineno = mycode.firstlineno + mypath = mycode.path + + for comp in py.code.compile, py.code.Source.compile: + for name in '', None, 'my': + yield check, comp, name + def test_offsetless_synerr(self): py.test.raises(SyntaxError, py.code.compile, "lambda a,a: 0", mode='eval') From hpk at codespeak.net Thu Aug 7 10:04:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 10:04:10 +0200 (CEST) Subject: [py-svn] r57048 - in py/branch/event/py/code: . testing Message-ID: <20080807080410.D650816850B@codespeak.net> Author: hpk Date: Thu Aug 7 10:04:09 2008 New Revision: 57048 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py Log: excinfo repr Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Thu Aug 7 10:04:09 2008 @@ -26,6 +26,9 @@ self.typename = self.type.__module__ + '.' + self.type.__name__ self.traceback = py.code.Traceback(tb) + def __repr__(self): + return "" % (self.typename, len(self.traceback)) + def exconly(self, tryshort=False): """ return the exception as a string Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Thu Aug 7 10:04:09 2008 @@ -192,6 +192,11 @@ excinfo = py.test.raises(ValueError, h) assert excinfo.exconly().startswith('ValueError') +def test_excinfo_repr(): + excinfo = py.test.raises(ValueError, h) + s = repr(excinfo) + assert s == "" + def test_excinfo_str(): excinfo = py.test.raises(ValueError, h) s = str(excinfo) From hpk at codespeak.net Thu Aug 7 10:06:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 10:06:35 +0200 (CEST) Subject: [py-svn] r57049 - py/branch/event/py/test2/testing Message-ID: <20080807080635.344B4168524@codespeak.net> Author: hpk Date: Thu Aug 7 10:06:34 2008 New Revision: 57049 Modified: py/branch/event/py/test2/testing/test_collect.py Log: fix doctest class Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Thu Aug 7 10:06:34 2008 @@ -4,8 +4,8 @@ import setupdata, suptest from py.__.test2.conftesthandle import Conftest from py.__.test2.collect import SetupState -from py.__.test2.pycollect import DoctestText from test_config import getcolitems +from py.__.test2.pycollect import DoctestFileContent class DummyConfig: def __init__(self): @@ -448,7 +448,7 @@ #print "checking that %s returns custom items" % (x,) items, events = self._genitems(x) assert len(items) == 1 - assert isinstance(items[0], DoctestText) + assert isinstance(items[0], DoctestFileContent) class TestCollector: def setup_method(self, method): From hpk at codespeak.net Thu Aug 7 10:07:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 10:07:11 +0200 (CEST) Subject: [py-svn] r57050 - in py/branch/event/py/test2: . testing Message-ID: <20080807080711.017D0168524@codespeak.net> Author: hpk Date: Thu Aug 7 10:07:11 2008 New Revision: 57050 Modified: py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_runner_functional.py Log: * fix pdb on skips * add repr to runners Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Thu Aug 7 10:07:11 2008 @@ -21,6 +21,9 @@ self.getcapture = colitem._config._getcapture self.pdb = pdb + def __repr__(self): + return "<%s colitem=%s>" %(self.__class__.__name__, self.colitem) + def run(self): """ return result of running setup, execution, teardown procedures. """ excinfo = None @@ -70,7 +73,7 @@ def makereport(self, res, when, excinfo, outerr): if excinfo: kw = self.getkw(when, excinfo, outerr) - if self.pdb and kw['failed']: + if self.pdb and kw.get('failed', 0): self.pdb(excinfo) else: kw = {'passed': OutcomeRepr(when, '.', "")} Modified: py/branch/event/py/test2/testing/test_runner_functional.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner_functional.py (original) +++ py/branch/event/py/test2/testing/test_runner_functional.py Thu Aug 7 10:07:11 2008 @@ -1,6 +1,7 @@ import py from py.__.test2.testing.suptest import InlineCollection from py.__.test2.runner import basic_run_report, forked_run_report, basic_collect_report +from py.__.test2.runner import RobustRun from py.__.code.excinfo import ReprExceptionInfo class BaseTests(InlineCollection): @@ -187,18 +188,26 @@ else: py.test.fail("did not raise") - def test_invoking_pdb(self): + def test_pdb_on_fail(self): l = [] - def mypdb(excinfo): - l.append(excinfo) ev = self.runitem(""" def test_func(): assert 0 - """, pdb=mypdb) + """, pdb=l.append) assert ev.failed assert ev.failed.when == "execute" assert len(l) == 1 + def test_pdb_on_skip(self): + l = [] + ev = self.runitem(""" + import py + def test_func(): + py.test.skip("hello") + """, pdb=l.append) + assert len(l) == 0 + assert ev.skipped + class TestExecutionForked(BaseTests): def getrunner(self): return forked_run_report @@ -240,3 +249,12 @@ assert not ev.failed assert not ev.passed assert ev.skipped + + +class TestRunnerRepr(InlineCollection): + def test_runner_repr(self): + item = self.getitem("def test_func(): pass") + robustrun = RobustRun(item) + r = repr(robustrun) + assert r + assert r.find(item.name) != -1 From hpk at codespeak.net Thu Aug 7 10:35:27 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 10:35:27 +0200 (CEST) Subject: [py-svn] r57051 - py/branch/event/py/test2/rsession/testing Message-ID: <20080807083527.F3C07168480@codespeak.net> Author: hpk Date: Thu Aug 7 10:35:27 2008 New Revision: 57051 Modified: py/branch/event/py/test2/rsession/testing/test_hostmanage.py Log: fix option access Modified: py/branch/event/py/test2/rsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/rsession/testing/test_hostmanage.py Thu Aug 7 10:35:27 2008 @@ -85,8 +85,8 @@ host.gw.exit() def test_initgateway_ssh_and_remotepath(self): - option = py.test2.config.option - if getattr(option, 'sshtarget', None) is None: + from py.__.conftest import option + if not option.sshtarget: py.test.skip("no known ssh target, use -S to set one") host = Host("%s" % (option.sshtarget, )) # this test should be careful to not write/rsync anything From hpk at codespeak.net Thu Aug 7 12:09:43 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 12:09:43 +0200 (CEST) Subject: [py-svn] r57054 - in py/branch/event/py/code: . testing Message-ID: <20080807100943.B912016857B@codespeak.net> Author: hpk Date: Thu Aug 7 12:09:41 2008 New Revision: 57054 Modified: py/branch/event/py/code/code.py py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_code.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/code/traceback2.py Log: try harder to get at source code for traceback entries Modified: py/branch/event/py/code/code.py ============================================================================== --- py/branch/event/py/code/code.py (original) +++ py/branch/event/py/code/code.py Thu Aug 7 12:09:41 2008 @@ -58,13 +58,15 @@ ) def path(self): - """ return a py.path.local object wrapping the source of the code """ + """ return a py.path.local object pointing to the source code """ fn = self.raw.co_filename try: return fn.__path__ except AttributeError: p = py.path.local(self.raw.co_filename) - if not p.check(): + if not p.check(file=1): + # XXX maybe try harder like the weird logic + # in the standard lib [linecache.updatecache] does? p = self.raw.co_filename return p @@ -77,6 +79,9 @@ try: return fn.__source__ except AttributeError: + path = self.path + if not isinstance(path, py.path.local): + return None return py.code.Source(self.path.read(mode="rU")) fullsource = property(fullsource, None, None, "full source containing this code object") Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Thu Aug 7 12:09:41 2008 @@ -95,9 +95,8 @@ return 4 + (len(s) - len(s.lstrip())) def _getentrysource(self, entry): - try: - source = entry.getsource() - except py.error.ENOENT: + source = entry.getsource() + if source is None: source = py.code.Source("???") return source.deindent() Modified: py/branch/event/py/code/testing/test_code.py ============================================================================== --- py/branch/event/py/code/testing/test_code.py (original) +++ py/branch/event/py/code/testing/test_code.py Thu Aug 7 12:09:41 2008 @@ -77,3 +77,5 @@ assert co_code.co_filename == name code = py.code.Code(co_code) assert str(code.path) == name + assert code.fullsource is None + Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Thu Aug 7 12:09:41 2008 @@ -217,6 +217,31 @@ s = str(excinfo.traceback[-1]) assert s == " File '':1 in \n ???\n" +def test_entrysource_Queue_example(): + import Queue + try: + Queue.Queue().get(timeout=0.001) + except Queue.Empty: + excinfo = py.code.ExceptionInfo() + entry = excinfo.traceback[-1] + source = entry.getsource() + assert source is not None + s = str(source).strip() + assert s.startswith("def get") + +def test_codepath_Queue_example(): + py.test.skip("try harder to get at the paths of code objects.") + import Queue + try: + Queue.Queue().get(timeout=0.001) + except Queue.Empty: + excinfo = py.code.ExceptionInfo() + entry = excinfo.traceback[-1] + path = entry.path + assert isinstance(path, py.path.local) + assert path.basename == "Queue.py" + assert path.check() + class TestFormattedExcinfo: def setup_method(self, method): self.tmpdir = py.test2.ensuretemp("%s_%s" %( @@ -269,6 +294,17 @@ 'E assert 0' ] + + def test_repr_source_not_existing(self): + pr = FormattedExcinfo() + co = compile("raise ValueError()", "", "exec") + try: + exec co + except ValueError: + excinfo = py.code.ExceptionInfo() + repr = pr.repr_excinfo(excinfo) + assert repr.reprtraceback.reprentries[1].lines[0] == "> ???" + def test_repr_local(self): p = FormattedExcinfo(showlocals=True) loc = {'y': 5, 'z': 7, 'x': 3, '__builtins__': __builtins__} Modified: py/branch/event/py/code/traceback2.py ============================================================================== --- py/branch/event/py/code/traceback2.py (original) +++ py/branch/event/py/code/traceback2.py Thu Aug 7 12:09:41 2008 @@ -52,6 +52,13 @@ def getsource(self): """ return failing source code. """ source = self.frame.code.fullsource + if source is None: + try: + sourcelines, lineno = py.std.inspect.findsource(self.frame.code.raw) + except IOError: + return None + source = py.code.Source() + source.lines = map(str.rstrip, sourcelines) start = self.getfirstlinesource() end = self.lineno try: From hpk at codespeak.net Thu Aug 7 12:31:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 12:31:30 +0200 (CEST) Subject: [py-svn] r57055 - py/branch/event/py/execnet/testing Message-ID: <20080807103130.CB3B516857E@codespeak.net> Author: hpk Date: Thu Aug 7 12:31:29 2008 New Revision: 57055 Modified: py/branch/event/py/execnet/testing/test_gateway.py Log: adding a few tests regarding callback endmarkers Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Thu Aug 7 12:31:29 2008 @@ -302,6 +302,19 @@ assert isinstance(l[2], channel.__class__) assert l[3] == 999 + def test_channel_endmarker_callback_error(self): + from Queue import Queue + q = Queue() + channel = self.gw.remote_exec(source=''' + raise ValueError() + ''') + channel.setcallback(q.put, endmarker=999) + val = q.get(TESTTIMEOUT) + assert val == 999 + err = channel._getremoteerror() + assert err + assert str(err).find("ValueError") != -1 + def test_remote_redirect_stdout(self): out = py.std.StringIO.StringIO() handle = self.gw._remote_redirect(stdout=out) @@ -460,6 +473,22 @@ text = c1.receive() assert text.find("execution disallowed") != -1 + +def test_channel_endmarker_remote_killterm(): + gw = py.execnet.PopenGateway() + from Queue import Queue + q = Queue() + channel = gw.remote_exec(source=''' + import os + os.kill(os.getpid(), 15) + ''') + channel.setcallback(q.put, endmarker=999) + val = q.get(TESTTIMEOUT) + assert val == 999 + err = channel._getremoteerror() + py.test.skip("implement information on causes/signals " + "remote gateways") + #class TestBlockingIssues: # def test_join_blocked_execution_gateway(self): From hpk at codespeak.net Thu Aug 7 12:35:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 12:35:28 +0200 (CEST) Subject: [py-svn] r57056 - py/branch/event/py/execnet/testing Message-ID: <20080807103528.16324169DFB@codespeak.net> Author: hpk Date: Thu Aug 7 12:35:27 2008 New Revision: 57056 Modified: py/branch/event/py/execnet/testing/test_gateway.py Log: teardown local gateway when remote is killed Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Thu Aug 7 12:35:27 2008 @@ -476,18 +476,21 @@ def test_channel_endmarker_remote_killterm(): gw = py.execnet.PopenGateway() - from Queue import Queue - q = Queue() - channel = gw.remote_exec(source=''' - import os - os.kill(os.getpid(), 15) - ''') - channel.setcallback(q.put, endmarker=999) - val = q.get(TESTTIMEOUT) - assert val == 999 - err = channel._getremoteerror() - py.test.skip("implement information on causes/signals " - "remote gateways") + try: + from Queue import Queue + q = Queue() + channel = gw.remote_exec(source=''' + import os + os.kill(os.getpid(), 15) + ''') + channel.setcallback(q.put, endmarker=999) + val = q.get(TESTTIMEOUT) + assert val == 999 + err = channel._getremoteerror() + finally: + gw.exit() + py.test.skip("provide information on causes/signals " + "of dying remote gateways") #class TestBlockingIssues: From hpk at codespeak.net Thu Aug 7 13:16:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 13:16:21 +0200 (CEST) Subject: [py-svn] r57057 - in py/branch/event/py/test2: . rsession rsession/testing testing Message-ID: <20080807111621.D8D86169E22@codespeak.net> Author: hpk Date: Thu Aug 7 13:16:19 2008 New Revision: 57057 Modified: py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py py/branch/event/py/test2/testing/setupdata.py Log: make masterslave testing more robust, add HostCrashed event which detects things that go wrong on the other side. Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Thu Aug 7 13:16:19 2008 @@ -33,6 +33,9 @@ self.session = session self.timeend = time.time() +class InternalException(BaseEvent): + def __init__(self, excinfo): + self.excinfo = excinfo # ---------------------------------------------------------------------- # Events related to collecting and executing test Items @@ -98,8 +101,15 @@ self.roots = roots class HostDown(BaseEvent): - def __init__(self, host): + def __init__(self, host, pending): self.host = host + self.pending = pending + +class HostCrashed(BaseEvent): + def __init__(self, host, pending, error): + self.host = host + self.pending = pending + self.error = error class HostRSyncRootReady(BaseEvent): def __init__(self, host, root): Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Thu Aug 7 13:16:19 2008 @@ -5,19 +5,34 @@ from py.__.test2 import repevent class MasterNode(object): + ENDMARK = -1 + def __init__(self, host, session): self.host = host self.session = session self.notify = session.bus.notify self.channel = install_slave(host, session) - self.channel.setcallback(self._callback) + self.channel.setcallback(self._callback, endmarker=self.ENDMARK) self.pending = [] - + self._down = False + def _callback(self, ev): - if ev is None: - self.notify(repevent.HostDown(self.host)) - return - item = self.pending.pop() + try: + if ev == self.ENDMARK: + err = self.channel._getremoteerror() + ev = repevent.HostCrashed(self.host, self.pending, err) + self.session.bus.notify(ev) + return + if ev is None: + self.notify(repevent.HostDown(self.host, self.pending)) + self._down = True + return + item = self.pending.pop() + except KeyboardInterrupt: + raise + except: + excinfo = py.code.ExceptionInfo() + ev = repevent.InternalException(excinfo) self.notify(ev) def send(self, item): @@ -32,6 +47,14 @@ self.pending.insert(0, item) self.notify(repevent.SendItem(self.host, item)) +# +# config objects need to be exchanged and unified between +# two sides so that collectors and items referencing the +# config will be regarded equal - and also for an item +# to be reconstructed by its names. +# +# + def send_and_receive_pickled_config(channel, config, remote_topdir): channel.send((config, remote_topdir)) backconfig = channel.receive() Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Thu Aug 7 13:16:19 2008 @@ -52,12 +52,24 @@ class TestMasterSlaveConnection(BasicRsessionTest): def makereportqueue(self, filterevent=repevent.ItemTestReport): - queue = py.std.Queue.Queue() + self.queue = py.std.Queue.Queue() + self.events = [] + self.filterevent = filterevent def append(event): - if isinstance(event, filterevent): - queue.put(event) + self.events.append(event) + if not filterevent or isinstance(event, filterevent): + self.queue.put(event) self.session.bus.subscribe(append) - return queue + return self.queue + + def getevent(self, TIMEOUT=2.0): + try: + return self.queue.get(timeout=TIMEOUT) + except py.std.Queue.Empty: + print "node channel", self.node.channel + print "remoteerror", self.node.channel._getremoteerror() + print "allevents", self.events + py.test.fail("did not see %r events" % (self.filterevent,)) def setup_method(self, method): super(TestMasterSlaveConnection, self).setup_method(method) @@ -69,27 +81,45 @@ def teardown_method(self, method): print "at teardown:", self.node.channel + def test_crash_invalid_item(self): + self.makereportqueue(repevent.HostCrashed) + self.node.send(123) # invalid item + ev = self.getevent() + assert ev.host == self.host + assert ev.pending == [123] + assert str(ev.error).find("AttributeError") != -1 + + def test_crash_killed(self): + self.makereportqueue(repevent.HostCrashed) + item = self.getfunc("kill15") + self.node.send(item) # invalid item + ev = self.getevent() + assert ev.host == self.host + assert ev.pending == [item] + assert not ev.error # currently this means crashed + def test_node_down(self): - queue = self.makereportqueue(repevent.HostDown) + self.makereportqueue(repevent.HostDown) self.node.send(None) - event = queue.get(timeout=1.0) + event = self.getevent() assert event.host == self.host + assert event.pending == [] def test_send_one(self): - queue = self.makereportqueue() + self.makereportqueue() item = self.getfunc("passed") self.node.send(self.getfunc("passed")) - event = queue.get(timeout=2.0) - assert event.passed + ev = self.getevent() + assert ev.passed assert not self.node.pending - assert event.colitem == item + assert ev.colitem == item #assert event.item == item #assert event.item is not item def test_send_multiple(self): - queue = self.makereportqueue() + self.makereportqueue() for outcome in "passed failed skipped".split(): self.node.send(self.getfunc(outcome)) - event = queue.get(timeout=2.0) - assert getattr(event, outcome) + ev = self.getevent() + assert getattr(ev, outcome) assert not self.node.pending Modified: py/branch/event/py/test2/testing/setupdata.py ============================================================================== --- py/branch/event/py/test2/testing/setupdata.py (original) +++ py/branch/event/py/test2/testing/setupdata.py Thu Aug 7 13:16:19 2008 @@ -159,6 +159,10 @@ def funchang(): import time time.sleep(1000) + + def funckill15(): + import os + os.kill(os.getpid(), 15) """, 'test_generative.py': """ From hpk at codespeak.net Thu Aug 7 13:34:16 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 13:34:16 +0200 (CEST) Subject: [py-svn] r57058 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080807113416.637E7169E43@codespeak.net> Author: hpk Date: Thu Aug 7 13:34:15 2008 New Revision: 57058 Modified: py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: working more on robustness of functiona testing of nodes Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Thu Aug 7 13:34:15 2008 @@ -20,8 +20,9 @@ try: if ev == self.ENDMARK: err = self.channel._getremoteerror() - ev = repevent.HostCrashed(self.host, self.pending, err) - self.session.bus.notify(ev) + if not self._down or err: + ev = repevent.HostCrashed(self.host, self.pending, err) + self.session.bus.notify(ev) return if ev is None: self.notify(repevent.HostDown(self.host, self.pending)) Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Thu Aug 7 13:34:15 2008 @@ -51,28 +51,25 @@ assert newtopdir == topdir class TestMasterSlaveConnection(BasicRsessionTest): - def makereportqueue(self, filterevent=repevent.ItemTestReport): - self.queue = py.std.Queue.Queue() - self.events = [] - self.filterevent = filterevent - def append(event): - self.events.append(event) - if not filterevent or isinstance(event, filterevent): - self.queue.put(event) - self.session.bus.subscribe(append) - return self.queue - - def getevent(self, TIMEOUT=2.0): - try: - return self.queue.get(timeout=TIMEOUT) - except py.std.Queue.Empty: - print "node channel", self.node.channel - print "remoteerror", self.node.channel._getremoteerror() - print "allevents", self.events - py.test.fail("did not see %r events" % (self.filterevent,)) + def getevent(self, eventtype=repevent.ItemTestReport, timeout=2.0): + events = [] + while 1: + try: + ev = self.queue.get(timeout=timeout) + except py.std.Queue.Empty: + print "node channel", self.node.channel + print "remoteerror", self.node.channel._getremoteerror() + print "seen events", events + raise IOError("did not see %r events" % (eventtype)) + else: + if isinstance(ev, eventtype): + return ev + events.append(ev) def setup_method(self, method): super(TestMasterSlaveConnection, self).setup_method(method) + self.queue = py.std.Queue.Queue() + self.session.bus.subscribe(self.queue.put) self.host = Host("localhost") self.host.initgateway() self.node = MasterNode(self.host, self.session) @@ -80,33 +77,33 @@ def teardown_method(self, method): print "at teardown:", self.node.channel + self.session.bus.unsubscribe(self.queue.put) def test_crash_invalid_item(self): - self.makereportqueue(repevent.HostCrashed) self.node.send(123) # invalid item - ev = self.getevent() + ev = self.getevent(repevent.HostCrashed) assert ev.host == self.host assert ev.pending == [123] assert str(ev.error).find("AttributeError") != -1 def test_crash_killed(self): - self.makereportqueue(repevent.HostCrashed) item = self.getfunc("kill15") self.node.send(item) # invalid item - ev = self.getevent() + ev = self.getevent(repevent.HostCrashed) assert ev.host == self.host assert ev.pending == [item] assert not ev.error # currently this means crashed def test_node_down(self): - self.makereportqueue(repevent.HostDown) self.node.send(None) - event = self.getevent() + event = self.getevent(repevent.HostDown) assert event.host == self.host assert event.pending == [] + self.node._callback(self.node.ENDMARK) + py.test.raises(IOError, + "self.getevent(repevent.HostCrashed, timeout=0.01)") def test_send_one(self): - self.makereportqueue() item = self.getfunc("passed") self.node.send(self.getfunc("passed")) ev = self.getevent() @@ -117,7 +114,6 @@ #assert event.item is not item def test_send_multiple(self): - self.makereportqueue() for outcome in "passed failed skipped".split(): self.node.send(self.getfunc(outcome)) ev = self.getevent() From hpk at codespeak.net Thu Aug 7 15:24:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 15:24:11 +0200 (CEST) Subject: [py-svn] r57059 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080807132411.10BA3169F3E@codespeak.net> Author: hpk Date: Thu Aug 7 15:24:10 2008 New Revision: 57059 Modified: py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/rsession/testing/test_rsession.py Log: properly wait for completion of dist tests Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Thu Aug 7 15:24:10 2008 @@ -53,7 +53,7 @@ self.gw_remotepath = channel.receive() def __str__(self): - return "" % (self.hostname, self.relpath) + return "" % (self.hostid, self.hostname, self.relpath) __repr__ = __str__ def __hash__(self): @@ -157,26 +157,33 @@ for host in self.hosts: host.node = MasterNode(host, self.session) - def teardown_hosts(self, timeout=1.0): - # XXX teardown nodes and hosts + def wait_for_completion(self, maxtimeout=1.0): queue = py.std.Queue.Queue() - def hostdown(event): - if isinstance(event, repevent.HostDown): - queue.put(event) - self.session.bus.subscribe(hostdown) - - pending_hosts = [] - for host in self.hosts: - if not host.node.channel.isclosed(): - host.node.channel.send(None) - pending_hosts.append(host) - - while pending_hosts: - event = queue.get(timeout=timeout) - if event.host not in pending_hosts: - print "got random HostDown of", event.host - else: - pending_hosts.remove(event.host) + self.session.bus.subscribe(queue.put) + #remaining = [] + pending_going_down = [] + while self.hosts: + numitems = 0 + for host in self.hosts: + if host not in pending_going_down: + if not host.node.pending: + # reschedule tests + host.node.send(None) + pending_going_down.append(host) + else: + numitems += len(host.node.pending) + ev = queue.get(timeout=maxtimeout) + if isinstance(ev, repevent.HostDown): + assert not ev.pending + self.hosts.remove(ev.host) + elif isinstance(ev, repevent.HostCrashed): + print "host crashed", ev.host + print "lost items", ev.pending + #XXX remaining.extend(ev.pending) + pass + #else: + # print "ignoring event", ev + #print "items remaining", numitems def trysendtest(self, item): for host in self.hosts: Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Thu Aug 7 15:24:10 2008 @@ -39,7 +39,7 @@ self.hm.setup_hosts() def sessionfinishes(self): - self.hm.teardown_hosts() + self.hm.wait_for_completion() super(RSession, self).sessionfinishes() def runtest(self, item): Modified: py/branch/event/py/test2/rsession/testing/test_rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_rsession.py (original) +++ py/branch/event/py/test2/rsession/testing/test_rsession.py Thu Aug 7 15:24:10 2008 @@ -111,7 +111,7 @@ if isinstance(item, repevent.ItemTestReport): events.append(item) print "got all events", events - hm.teardown_hosts() + hm.wait_for_completion() passed = [ev for ev in events if ev.passed] skipped = [ev for ev in events From hpk at codespeak.net Thu Aug 7 15:57:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 15:57:44 +0200 (CEST) Subject: [py-svn] r57061 - in py/branch/event/py/test2: . testing Message-ID: <20080807135744.921F62A00DF@codespeak.net> Author: hpk Date: Thu Aug 7 15:57:41 2008 New Revision: 57061 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/testing/test_config.py Log: topdir for a file is its dirpath Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Thu Aug 7 15:57:41 2008 @@ -238,6 +238,8 @@ assert p, "cannot determine common basedir of %s" %(args,) pkgdir = p.pypkgpath() if pkgdir is None: + if p.check(file=1): + p = p.dirpath() return p else: return pkgdir.dirpath() Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Thu Aug 7 15:57:41 2008 @@ -114,6 +114,8 @@ assert gettopdir([tmp]) == tmp topdir =gettopdir([tmp.join("hello"), tmp.join("world")]) assert topdir == tmp + somefile = tmp.ensure("somefile.py") + assert gettopdir([somefile]) == tmp def test_gettopdir_pypkg(): tmp = py.test2.ensuretemp("topdir2") From hpk at codespeak.net Thu Aug 7 16:37:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 16:37:02 +0200 (CEST) Subject: [py-svn] r57062 - in py/branch/event/py/test2: . testing Message-ID: <20080807143702.85C72169EF1@codespeak.net> Author: hpk Date: Thu Aug 7 16:37:00 2008 New Revision: 57062 Modified: py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_config.py Log: optionally write eventlog Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Thu Aug 7 16:37:00 2008 @@ -24,6 +24,14 @@ def __init__(self, config): self.config = config self.bus = EventBus() + eventlog = self.config.option.eventlog + if eventlog: + self.eventlog = py.path.local(eventlog) + f = self.eventlog.open("w") + def eventwrite(ev): + print >>f, ev + f.flush() + self.bus.subscribe(eventwrite) def fixoptions(self): """ check, fix and determine conflicting options. """ Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Thu Aug 7 16:37:00 2008 @@ -3,6 +3,7 @@ from py.__.test2.config import gettopdir import suptest, setupdata +from py.__.test2 import repevent def getcolitems(config): return [config.getfsnode(arg) for arg in config.args] @@ -195,6 +196,15 @@ config = py.test2.config._reparse([self.tmpdir, '--dist']) assert config._getsessionname() == 'RSession' + def test_session_eventlog(self): + eventlog = self.tmpdir.join("test_session_eventlog") + config = py.test2.config._reparse([self.tmpdir, + '--eventlog=%s' % eventlog]) + session = config.initsession() + session.bus.notify(repevent.SessionStart(session)) + s = eventlog.read() + assert s.find("SessionStart") != -1 + def test_implied_lsession(self): #optnames = 'startserver runbrowser apigen=x rest boxed'.split() #for x in optnames: From hpk at codespeak.net Thu Aug 7 17:09:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 17:09:22 +0200 (CEST) Subject: [py-svn] r57063 - in py/branch/event/py/test2/rep: . testing Message-ID: <20080807150922.BB152169EE9@codespeak.net> Author: hpk Date: Thu Aug 7 17:09:15 2008 New Revision: 57063 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_terminal.py Log: report more events, small refactoring Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Thu Aug 7 17:09:15 2008 @@ -14,17 +14,42 @@ self.curdir = py.path.local() if file is None: file = py.std.sys.stdout - self.out = py.io.TerminalWriter(file) + self._tw = py.io.TerminalWriter(file) + + def write_fspath_result(self, fspath, res): + if fspath != self.currentfspath: + self._tw.line() + relpath = getrelpath(self.curdir, fspath) + self._tw.write(relpath + " ") + self.currentfspath = fspath + self._tw.write(res) + + def write_line(self, line): + if self.currentfspath: + self._tw.line() + self.currentfspath = None + self._tw.line(line) + + def rep_InternalException(self, ev): + for line in str(ev.excinfo.getrepr()).split("\n"): + self.write_line("InternalException: " + line) + + def rep_HostGatewayReady(self, ev): + self.write_line("HostReady: %s" %(ev.host,)) + + def rep_HostCrashed(self, ev): + host = ev.host + error = ev.error + if not error: + error = "TERMINATED unexpectedly" + self.write_line("HostCrashed %s: pending=%r" %(host.hostid, ev.pending)) + for line in str(error).split("\n"): + self.write_line("HostCrashed %s: %s" %(host.hostid, line)) def rep_ItemTestReport(self, ev): super(TerminalReporter, self).rep_ItemTestReport(ev) fspath = ev.colitem.fspath - if fspath != self.currentfspath: - relpath = getrelpath(self.curdir, fspath) - self.out.line() - self.out.write(relpath + " ") - self.currentfspath = fspath - self.out.write(ev.outcome.shortrepr) + self.write_fspath_result(fspath, ev.outcome.shortrepr) def rep_CollectionReport(self, ev): super(TerminalReporter, self).rep_CollectionReport(ev) @@ -34,18 +59,15 @@ msg = ev.outcome.longrepr.message else: msg = ev.outcome.longrepr.reprcrash.message - self.out.line() - relpath = getrelpath(self.curdir, fspath) - self.out.write(relpath + " - " + str(msg)) - self.currentfspath = fspath + self.write_fspath_result(fspath, "- " + str(msg)) def rep_SessionStart(self, ev): - self.out.sep("=", "test session starts", bold=True) + self._tw.sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() self.out_hostinfo() def rep_SessionFinish(self, ev): - self.out.line("") + self._tw.line("") self.summary_failures() self.summary_skips() self.summary_stats() @@ -57,10 +79,10 @@ def summary_failures(self): if self._failed: - self.out.sep("=", "FAILURES") + self._tw.sep("=", "FAILURES") for ev in self._failed: - self.out.sep("_") - ev.toterminal(self.out) + self._tw.sep("_") + ev.toterminal(self._tw) def summary_stats(self): session_duration = py.std.time.time() - self._sessionstarttime @@ -68,27 +90,27 @@ numskipped = len(self._skipped) numpassed = len(self._passed) sum = numfailed + numpassed - self.out.line() - self.out.sep("=", "%d/%d passed + %d skips in %.2f seconds" % + self._tw.line() + self._tw.sep("=", "%d/%d passed + %d skips in %.2f seconds" % (numpassed, sum, numskipped, session_duration), bold=True) if numfailed == 0: - self.out.sep("=", "failures: no failures :)", green=True) + self._tw.sep("=", "failures: no failures :)", green=True) else: - self.out.sep("=", "failures: %d" %(numfailed), red=True) - self.out.line() + self._tw.sep("=", "failures: %d" %(numfailed), red=True) + self._tw.line() def summary_skips(self): if not self._failed or self.config.option.showskipsummary: folded_skips = self._folded_skips() if folded_skips: - self.out.sep("_", "skipped test summary") + self._tw.sep("_", "skipped test summary") for num, fspath, lineno, reason in folded_skips: - self.out.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) + self._tw.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) def out_hostinfo(self): - self.out.line("host 0: %s %s - Python %s" % - (py.std.sys.platform, - py.std.sys.executable, - repr_pythonversion())) + self._tw.line("host 0: %s %s - Python %s" % + (py.std.sys.platform, + py.std.sys.executable, + repr_pythonversion())) Reporter = TerminalReporter Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Thu Aug 7 17:09:15 2008 @@ -4,6 +4,7 @@ #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report from test_collectonly import InlineCollect, popvalue, assert_stringio_contains_lines +from py.__.test2.rsession.hostmanage import Host class TestTerminal(InlineCollect): def test_session_reporter_subscription(self): @@ -59,3 +60,39 @@ "> import xyz", "E ImportError: No module named xyz" ]) + + def test_internal_exception(self): + modcol = self.getmodulecol([], "def test_one(): pass") + stringio = py.std.cStringIO.StringIO() + rep = TerminalReporter(modcol._config, file=stringio) + excinfo = py.test2.raises(ValueError, "raise ValueError('hello')") + rep.processevent(repevent.InternalException(excinfo)) + s = popvalue(stringio) + assert s.find("InternalException:") != -1 + + def test_hostready_crash(self): + modcol = self.getmodulecol([], """ + def test_one(): + pass + """) + stringio = py.std.cStringIO.StringIO() + host1 = Host("localhost") + rep = TerminalReporter(modcol._config, file=stringio) + rep.processevent(repevent.HostGatewayReady(host1, None)) + s = popvalue(stringio) + assert s.find("HostReady") != -1 + rep.processevent(repevent.HostCrashed(host1, [], "myerror")) + s = popvalue(stringio) + assert s.find("HostCrashed") != -1 + assert s.find("myerror") != -1 + + def test_writeline(self): + modcol = self.getmodulecol([], "def test_one(): pass") + stringio = py.std.cStringIO.StringIO() + rep = TerminalReporter(modcol._config, file=stringio) + rep.write_fspath_result(py.path.local("xy.py"), '.') + rep.write_line("hello world") + lines = popvalue(stringio).split('\n') + assert not lines[0] + assert lines[1].endswith("xy.py .") + assert lines[2] == "hello world" From hpk at codespeak.net Thu Aug 7 17:09:49 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 17:09:49 +0200 (CEST) Subject: [py-svn] r57064 - py/branch/event/py/test2 Message-ID: <20080807150949.11D7A169EF1@codespeak.net> Author: hpk Date: Thu Aug 7 17:09:48 2008 New Revision: 57064 Modified: py/branch/event/py/test2/defaultconftest.py Log: that should have been commited with 57062 Modified: py/branch/event/py/test2/defaultconftest.py ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test2/defaultconftest.py Thu Aug 7 17:09:48 2008 @@ -55,6 +55,9 @@ Option('', '--pdb', action="store_true", dest="usepdb", default=False, help="start pdb (the Python debugger) on errors."), + Option('', '--eventlog', + action="store", dest="eventlog", default=None, + help="write reporting events to given file."), Option('', '--tb', action="store", dest="tbstyle", default='long', type="choice", choices=['long', 'short', 'no'], @@ -105,4 +108,3 @@ action="store", dest="session", default=None, help="lookup given sessioname in conftest.py files and use it."), ) - From hpk at codespeak.net Thu Aug 7 17:30:07 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 17:30:07 +0200 (CEST) Subject: [py-svn] r57065 - in py/branch/event/py/test2: . rsession rsession/testing testing Message-ID: <20080807153007.92CBC168458@codespeak.net> Author: hpk Date: Thu Aug 7 17:30:04 2008 New Revision: 57065 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/rsession/testing/test_rsession.py py/branch/event/py/test2/testing/acceptance_test.py Log: dist-testing begins to work as expected Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Thu Aug 7 17:30:04 2008 @@ -209,7 +209,7 @@ relpath = "." else: raise ValueError("%r not relative to topdir %s" - %(chain[0], topdir)) + %(chain[0].fspath, topdir)) return relpath, tuple([x.name for x in chain[1:]]) @staticmethod Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Thu Aug 7 17:30:04 2008 @@ -154,12 +154,12 @@ def setup_hosts(self): self.init_rsync() + self.queue = py.std.Queue.Queue() + self.session.bus.subscribe(self.queue.put) for host in self.hosts: host.node = MasterNode(host, self.session) - def wait_for_completion(self, maxtimeout=1.0): - queue = py.std.Queue.Queue() - self.session.bus.subscribe(queue.put) + def wait_for_completion(self, maxtimeout=2.0): #remaining = [] pending_going_down = [] while self.hosts: @@ -172,13 +172,15 @@ pending_going_down.append(host) else: numitems += len(host.node.pending) - ev = queue.get(timeout=maxtimeout) + ev = self.queue.get(timeout=maxtimeout) if isinstance(ev, repevent.HostDown): assert not ev.pending self.hosts.remove(ev.host) + pending_going_down.remove(ev.host) elif isinstance(ev, repevent.HostCrashed): - print "host crashed", ev.host - print "lost items", ev.pending + if ev.host in pending_going_down: + pending_going_down.remove(ev.host) + self.hosts.remove(ev.host) #XXX remaining.extend(ev.pending) pass #else: Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Thu Aug 7 17:30:04 2008 @@ -28,7 +28,10 @@ self.notify(repevent.HostDown(self.host, self.pending)) self._down = True return - item = self.pending.pop() + if isinstance(ev, repevent.ItemTestReport): + item = self.pending.pop() + else: + raise ev except KeyboardInterrupt: raise except: Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Thu Aug 7 17:30:04 2008 @@ -47,6 +47,7 @@ sent = self.hm.trysendtest(item) if not sent and not self.shouldstop: self.sleepabit() + self.runtest(item) def sleepabit(self): py.std.time.sleep(0.1) Modified: py/branch/event/py/test2/rsession/testing/test_rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_rsession.py (original) +++ py/branch/event/py/test2/rsession/testing/test_rsession.py Thu Aug 7 17:30:04 2008 @@ -107,7 +107,7 @@ num_hosts = len(hm.hosts) events = [] while len(events) < 4 * num_hosts: - item = queue.get(timeout=0.5) + item = queue.get(timeout=1.0) if isinstance(item, repevent.ItemTestReport): events.append(item) print "got all events", events Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Thu Aug 7 17:30:04 2008 @@ -22,6 +22,9 @@ stdout=p1.open("w"), stderr=p2.open("w")) ret = popen.wait() out, err = p1.readlines(cr=0), p2.readlines(cr=0) + if err: + for line in err: + print >>py.std.sys.stderr, line return Result(ret, out, err) def makepyfile(self, **kwargs): @@ -252,6 +255,38 @@ " 1", "*doc.txt:2: DocTestFailure" ]) + + def test_dist_tests_with_crash(self): + p1 = self.makepyfile( + test_one=""" + import py + def test_fail0(): + assert 0 + def test_fail1(): + raise ValueError() + def test_ok(): + pass + def test_skip(): + py.test.skip("hello") + def test_crash(): + import time + import os + time.sleep(0.5) + os.kill(os.getpid(), 15) + """, + conftest=""" + dist_hosts = ['localhost'] * 3 + """ + ) + result = self.runpytest(p1, '-d') + assert_lines_contain_lines(result.outlines, [ + "HostReady*localhost*", + "HostReady*localhost*", + "HostReady*localhost*", + "HostCrashed*localhost*", + "*1/3 passed + 1 skip*", + "*failures: 2*", + ]) def test_looponfailing_looping(self): py.test.skip("thought needed to nicely test --looponfailing") From hpk at codespeak.net Thu Aug 7 17:46:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 17:46:02 +0200 (CEST) Subject: [py-svn] r57066 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080807154602.D5499168439@codespeak.net> Author: hpk Date: Thu Aug 7 17:46:00 2008 New Revision: 57066 Modified: py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: masternode now sends events to a specified notify method Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Thu Aug 7 17:46:00 2008 @@ -157,7 +157,9 @@ self.queue = py.std.Queue.Queue() self.session.bus.subscribe(self.queue.put) for host in self.hosts: - host.node = MasterNode(host, self.session) + host.node = MasterNode(host, + self.session.config, + self.session.bus.notify) def wait_for_completion(self, maxtimeout=2.0): #remaining = [] Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Thu Aug 7 17:46:00 2008 @@ -7,22 +7,29 @@ class MasterNode(object): ENDMARK = -1 - def __init__(self, host, session): + def __init__(self, host, config, notify): self.host = host - self.session = session - self.notify = session.bus.notify - self.channel = install_slave(host, session) - self.channel.setcallback(self._callback, endmarker=self.ENDMARK) + self.config = config + self.notify = notify + self.channel = install_slave(host, config) + self.channel.setcallback(self.callback, endmarker=self.ENDMARK) self.pending = [] self._down = False - def _callback(self, ev): + def callback(self, ev): + """ this gets called for each item we receive from + the other side and if the channel closes. + + Note that the callback runs in the receiver + thread of execnet gateways - we need to + avoid raising exceptions or doing heavy work. + """ try: if ev == self.ENDMARK: err = self.channel._getremoteerror() if not self._down or err: ev = repevent.HostCrashed(self.host, self.pending, err) - self.session.bus.notify(ev) + self.notify(ev) return if ev is None: self.notify(repevent.HostDown(self.host, self.pending)) @@ -42,11 +49,9 @@ def send(self, item): try: self.channel.send(item) - except IOError: - # XXX do proper rescheduling on io errors - print "Sending error, channel IOError" - print self.channel._getremoteerror() - raise + except IOError, ex: + ev = repevent.HostCrashed(self.host, self.pending, ex) + self.notify(ev) if item is not None: self.pending.insert(0, item) self.notify(repevent.SendItem(self.host, item)) @@ -72,8 +77,7 @@ return config # setting up slave code -def install_slave(host, session): - config = session.config +def install_slave(host, config): channel = host.gw.remote_exec(_pickle=True, source=""" from py.__.test2.rsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Thu Aug 7 17:46:00 2008 @@ -72,7 +72,8 @@ self.session.bus.subscribe(self.queue.put) self.host = Host("localhost") self.host.initgateway() - self.node = MasterNode(self.host, self.session) + self.node = MasterNode(self.host, self.session.config, + self.session.bus.notify) assert not self.node.channel.isclosed() def teardown_method(self, method): @@ -99,7 +100,7 @@ event = self.getevent(repevent.HostDown) assert event.host == self.host assert event.pending == [] - self.node._callback(self.node.ENDMARK) + self.node.callback(self.node.ENDMARK) py.test.raises(IOError, "self.getevent(repevent.HostCrashed, timeout=0.01)") From hpk at codespeak.net Thu Aug 7 17:49:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 17:49:52 +0200 (CEST) Subject: [py-svn] r57067 - in py/branch/event/py/test2: . rep rep/testing rsession Message-ID: <20080807154952.07227169E9E@codespeak.net> Author: hpk Date: Thu Aug 7 17:49:49 2008 New Revision: 57067 Modified: py/branch/event/py/test2/rep/rest.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_rest.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rep/web.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/hostmanage.py Log: rename HostGatewayReady -> HostReady Modified: py/branch/event/py/test2/rep/rest.py ============================================================================== --- py/branch/event/py/test2/rep/rest.py (original) +++ py/branch/event/py/test2/rep/rest.py Thu Aug 7 17:49:49 2008 @@ -66,7 +66,7 @@ def report_ImmediateFailure(self, item): pass - def report_HostGatewayReady(self, item): + def report_HostReady(self, item): self.to_rsync[item.host] = len(item.roots) def report_ItemStart(self, event): Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Thu Aug 7 17:49:49 2008 @@ -34,7 +34,7 @@ for line in str(ev.excinfo.getrepr()).split("\n"): self.write_line("InternalException: " + line) - def rep_HostGatewayReady(self, ev): + def rep_HostReady(self, ev): self.write_line("HostReady: %s" %(ev.host,)) def rep_HostCrashed(self, ev): Modified: py/branch/event/py/test2/rep/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_rest.py (original) +++ py/branch/event/py/test2/rep/testing/test_rest.py Thu Aug 7 17:49:49 2008 @@ -64,7 +64,7 @@ def test_report_HostRSyncRootReady(self): h = Host('localhost') reporter.hosts_to_rsync = 1 - reporter.report(repevent.HostGatewayReady(h, ["a"])) + reporter.report(repevent.HostReady(h, ["a"])) event = repevent.HostRSyncRootReady(h, "a") reporter.report(event) assert stdout.getvalue() == '::\n\n localhost: READY\n\n' Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Thu Aug 7 17:49:49 2008 @@ -78,7 +78,7 @@ stringio = py.std.cStringIO.StringIO() host1 = Host("localhost") rep = TerminalReporter(modcol._config, file=stringio) - rep.processevent(repevent.HostGatewayReady(host1, None)) + rep.processevent(repevent.HostReady(host1, None)) s = popvalue(stringio) assert s.find("HostReady") != -1 rep.processevent(repevent.HostCrashed(host1, [], "myerror")) Modified: py/branch/event/py/test2/rep/web.py ============================================================================== --- py/branch/event/py/test2/rep/web.py (original) +++ py/branch/event/py/test2/rep/web.py Thu Aug 7 17:49:49 2008 @@ -300,7 +300,7 @@ def _host_ready(self, event): self.pending_events.put(event) - def report_HostGatewayReady(self, item): + def report_HostReady(self, item): self.to_rsync[item.host] = len(item.roots) def report_HostRSyncRootReady(self, item): Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Thu Aug 7 17:49:49 2008 @@ -95,7 +95,7 @@ def __init__(self): self.time = timestamp() -class HostGatewayReady(BaseEvent): +class HostReady(BaseEvent): def __init__(self, host, roots): self.host = host self.roots = roots Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Thu Aug 7 17:49:49 2008 @@ -133,7 +133,7 @@ python = self.session.config.getvalue("dist_remotepython") for host in self.hosts: host.initgateway(python=python) - self.session.bus.notify(repevent.HostGatewayReady(host, self.roots)) + self.session.bus.notify(repevent.HostReady(host, self.roots)) host.gw.host = host def init_rsync(self): From hpk at codespeak.net Thu Aug 7 18:29:39 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 18:29:39 +0200 (CEST) Subject: [py-svn] r57068 - in py/branch/event/py/test2: . rep rep/testing rsession rsession/testing Message-ID: <20080807162939.6D3C9169EEE@codespeak.net> Author: hpk Date: Thu Aug 7 18:29:36 2008 New Revision: 57068 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: unify HostDown and HostCrashed events Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Thu Aug 7 18:29:36 2008 @@ -37,14 +37,13 @@ def rep_HostReady(self, ev): self.write_line("HostReady: %s" %(ev.host,)) - def rep_HostCrashed(self, ev): + def rep_HostDown(self, ev): host = ev.host error = ev.error - if not error: - error = "TERMINATED unexpectedly" - self.write_line("HostCrashed %s: pending=%r" %(host.hostid, ev.pending)) - for line in str(error).split("\n"): - self.write_line("HostCrashed %s: %s" %(host.hostid, line)) + if error or ev.pending: + self.write_line("HostDown %s: pending=%r" %(host.hostid, ev.pending)) + for line in str(error).split("\n"): + self.write_line("Error: %s" %(line)) def rep_ItemTestReport(self, ev): super(TerminalReporter, self).rep_ItemTestReport(ev) Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Thu Aug 7 18:29:36 2008 @@ -81,9 +81,9 @@ rep.processevent(repevent.HostReady(host1, None)) s = popvalue(stringio) assert s.find("HostReady") != -1 - rep.processevent(repevent.HostCrashed(host1, [], "myerror")) + rep.processevent(repevent.HostDown(host1, [], "myerror")) s = popvalue(stringio) - assert s.find("HostCrashed") != -1 + assert s.find("HostDown") != -1 assert s.find("myerror") != -1 def test_writeline(self): Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Thu Aug 7 18:29:36 2008 @@ -34,7 +34,9 @@ self.timeend = time.time() class InternalException(BaseEvent): - def __init__(self, excinfo): + def __init__(self, excinfo=None): + if excinfo is None: + excinfo = py.code.ExceptionInfo() self.excinfo = excinfo # ---------------------------------------------------------------------- @@ -79,11 +81,27 @@ # Distributed Testing Events # ---------------------------------------------------------------------- +class HostReady(BaseEvent): + def __init__(self, host, roots): + self.host = host + self.roots = roots + +class HostDown(BaseEvent): + def __init__(self, host, pending, error): + self.host = host + self.pending = pending + self.error = error + class SendItem(BaseEvent): def __init__(self, host, item): self.item = item self.host = host + +# +# events related to rsyncing +# + class HostRSyncing(BaseEvent): def __init__(self, host, root, remotepath, synced): self.host = host @@ -95,22 +113,6 @@ def __init__(self): self.time = timestamp() -class HostReady(BaseEvent): - def __init__(self, host, roots): - self.host = host - self.roots = roots - -class HostDown(BaseEvent): - def __init__(self, host, pending): - self.host = host - self.pending = pending - -class HostCrashed(BaseEvent): - def __init__(self, host, pending, error): - self.host = host - self.pending = pending - self.error = error - class HostRSyncRootReady(BaseEvent): def __init__(self, host, root): self.host = host Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Thu Aug 7 18:29:36 2008 @@ -178,13 +178,9 @@ if isinstance(ev, repevent.HostDown): assert not ev.pending self.hosts.remove(ev.host) - pending_going_down.remove(ev.host) - elif isinstance(ev, repevent.HostCrashed): if ev.host in pending_going_down: pending_going_down.remove(ev.host) - self.hosts.remove(ev.host) #XXX remaining.extend(ev.pending) - pass #else: # print "ignoring event", ev #print "items remaining", numitems Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Thu Aug 7 18:29:36 2008 @@ -27,13 +27,14 @@ try: if ev == self.ENDMARK: err = self.channel._getremoteerror() - if not self._down or err: - ev = repevent.HostCrashed(self.host, self.pending, err) - self.notify(ev) + if not self._down: + if not err: + err = "TERMINATED" + self.notify(repevent.HostDown(self.host, self.pending, err)) return if ev is None: - self.notify(repevent.HostDown(self.host, self.pending)) self._down = True + self.notify(repevent.HostDown(self.host, self.pending, None)) return if isinstance(ev, repevent.ItemTestReport): item = self.pending.pop() @@ -47,11 +48,7 @@ self.notify(ev) def send(self, item): - try: - self.channel.send(item) - except IOError, ex: - ev = repevent.HostCrashed(self.host, self.pending, ex) - self.notify(ev) + self.channel.send(item) if item is not None: self.pending.insert(0, item) self.notify(repevent.SendItem(self.host, item)) Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Thu Aug 7 18:29:36 2008 @@ -82,7 +82,7 @@ def test_crash_invalid_item(self): self.node.send(123) # invalid item - ev = self.getevent(repevent.HostCrashed) + ev = self.getevent(repevent.HostDown) assert ev.host == self.host assert ev.pending == [123] assert str(ev.error).find("AttributeError") != -1 @@ -90,19 +90,27 @@ def test_crash_killed(self): item = self.getfunc("kill15") self.node.send(item) # invalid item - ev = self.getevent(repevent.HostCrashed) + ev = self.getevent(repevent.HostDown) assert ev.host == self.host assert ev.pending == [item] - assert not ev.error # currently this means crashed + assert str(ev.error).find("TERMINATED") != -1 def test_node_down(self): self.node.send(None) event = self.getevent(repevent.HostDown) assert event.host == self.host assert event.pending == [] + assert not event.error self.node.callback(self.node.ENDMARK) - py.test.raises(IOError, - "self.getevent(repevent.HostCrashed, timeout=0.01)") + excinfo = py.test.raises(IOError, + "self.getevent(repevent.HostDown, timeout=0.01)") + + def test_send_on_closed_channel(self): + item = self.getfunc("passed") + self.node.channel.close() + py.test2.raises(IOError, "self.node.send(item)") + #ev = self.getevent(repevent.InternalException) + #assert ev.excinfo.errisinstance(IOError) def test_send_one(self): item = self.getfunc("passed") From hpk at codespeak.net Thu Aug 7 19:20:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 19:20:45 +0200 (CEST) Subject: [py-svn] r57080 - in py/branch/event/py/test2: rsession testing Message-ID: <20080807172045.69F48169E92@codespeak.net> Author: hpk Date: Thu Aug 7 19:20:44 2008 New Revision: 57080 Modified: py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/testing/acceptance_test.py Log: py.test2 -d test2 passes Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Thu Aug 7 19:20:44 2008 @@ -176,7 +176,6 @@ numitems += len(host.node.pending) ev = self.queue.get(timeout=maxtimeout) if isinstance(ev, repevent.HostDown): - assert not ev.pending self.hosts.remove(ev.host) if ev.host in pending_going_down: pending_going_down.remove(ev.host) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Thu Aug 7 19:20:44 2008 @@ -283,7 +283,8 @@ "HostReady*localhost*", "HostReady*localhost*", "HostReady*localhost*", - "HostCrashed*localhost*", + "HostDown*localhost*", + "Error: TERMINATED", "*1/3 passed + 1 skip*", "*failures: 2*", ]) From hpk at codespeak.net Thu Aug 7 19:34:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 19:34:04 +0200 (CEST) Subject: [py-svn] r57081 - in py/branch/event/py/test2: . rep testing Message-ID: <20080807173404.13594169EC4@codespeak.net> Author: hpk Date: Thu Aug 7 19:34:03 2008 New Revision: 57081 Modified: py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/test_repevent.py py/branch/event/py/test2/testing/test_runner_functional.py Log: remove duplication of attributes Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Thu Aug 7 19:34:03 2008 @@ -31,9 +31,9 @@ def rep_CollectionReport(self, ev): super(CollectonlyReporter, self).rep_CollectionReport(ev) if ev.failed: - self.outindent("!!! %s !!!" % ev.failed.longrepr.reprcrash.message) + self.outindent("!!! %s !!!" % ev.outcome.longrepr.reprcrash.message) elif ev.skipped: - self.outindent("!!! %s !!!" % ev.skipped.longrepr.message) + self.outindent("!!! %s !!!" % ev.outcome.longrepr.message) self.indent = self.indent[:-len(self.INDENT)] def rep_SessionFinish(self, session): Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Thu Aug 7 19:34:03 2008 @@ -54,11 +54,11 @@ self.colitem = colitem assert len(kwargs) == 1, kwargs name, value = kwargs.items()[0] - setattr(self, name, value) + setattr(self, name, True) self.outcome = value def toterminal(self, out): - longrepr = self.failed.longrepr + longrepr = self.outcome.longrepr if hasattr(longrepr, 'toterminal'): longrepr.toterminal(out) else: Modified: py/branch/event/py/test2/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test2/testing/test_repevent.py Thu Aug 7 19:34:03 2008 @@ -14,7 +14,7 @@ ev.toterminal(twmock) assert twmock.lines twmock = TWMock() - ev.failed.longrepr = "hello" + ev.outcome.longrepr = "hello" ev.toterminal(twmock) assert twmock.lines[0] == "hello" assert not twmock.lines[1:] Modified: py/branch/event/py/test2/testing/test_runner_functional.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner_functional.py (original) +++ py/branch/event/py/test2/testing/test_runner_functional.py Thu Aug 7 19:34:03 2008 @@ -12,8 +12,8 @@ """) assert ev.passed assert not ev.failed - assert ev.passed.shortrepr == "." - assert not ev.passed.longrepr + assert ev.outcome.shortrepr == "." + assert not ev.outcome.longrepr def test_failfunction(self): ev = self.runitem(""" @@ -23,9 +23,9 @@ assert not ev.passed assert not ev.skipped assert ev.failed - assert ev.failed.when == "execute" - assert isinstance(ev.failed.longrepr, ReprExceptionInfo) - assert str(ev.failed.shortrepr) == "F" + assert ev.outcome.when == "execute" + assert isinstance(ev.outcome.longrepr, ReprExceptionInfo) + assert str(ev.outcome.shortrepr) == "F" def test_skipfunction(self): ev = self.runitem(""" @@ -71,7 +71,7 @@ assert not ev.skipped assert not ev.passed assert ev.failed - assert ev.failed.when == "setup" + assert ev.outcome.when == "setup" def test_failure_in_teardown_function(self): ev = self.runitem(""" @@ -85,9 +85,9 @@ assert not ev.skipped assert not ev.passed assert ev.failed - #assert ev.failed.when == "teardown" - #assert ev.failed.where.lineno == 3 - #assert ev.failed.entries + #assert ev.outcome.when == "teardown" + #assert ev.outcome.where.lineno == 3 + #assert ev.outcome.entries def test_custom_failure_repr(self): self.makeconftest(""" @@ -104,7 +104,7 @@ assert not ev.skipped assert not ev.passed assert ev.failed - #assert ev.failed.when == "execute" + #assert ev.outcome.when == "execute" #assert ev.failed.where.lineno == 3 #assert ev.failed.where.path.basename == "test_func.py" #assert ev.failed.failurerepr == "hello" @@ -127,9 +127,9 @@ assert not ev.skipped assert not ev.passed assert ev.failed - #assert ev.failed.when == "setup" - #assert ev.failed.where.lineno == 3 - #assert ev.failed.where.path.basename == "test_func.py" + #assert ev.outcome.when == "setup" + #assert ev.outcome.where.lineno == 3 + #assert ev.outcome.where.path.basename == "test_func.py" #assert instanace(ev.failed.failurerepr, PythonFailureRepr) def test_capture_in_func(self): @@ -157,7 +157,7 @@ except SystemExit: py.test.fail("runner did not catch SystemExit") assert ev.failed - assert ev.failed.when == "execute" + assert ev.outcome.when == "execute" def test_exit_propagates(self): from py.__.test2.outcome import Exit @@ -195,7 +195,7 @@ assert 0 """, pdb=l.append) assert ev.failed - assert ev.failed.when == "execute" + assert ev.outcome.when == "execute" assert len(l) == 1 def test_pdb_on_skip(self): @@ -219,7 +219,7 @@ os.kill(os.getpid(), 15) """) assert ev.failed - assert ev.failed.when == "???" + assert ev.outcome.when == "???" class TestCollectionEvent(InlineCollection): def test_collect_result(self): From hpk at codespeak.net Thu Aug 7 21:30:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 21:30:19 +0200 (CEST) Subject: [py-svn] r57087 - py/branch/event/py/test2/rsession Message-ID: <20080807193019.E180A169E83@codespeak.net> Author: hpk Date: Thu Aug 7 21:30:13 2008 New Revision: 57087 Modified: py/branch/event/py/test2/rsession/masterslave.py Log: simplify slave side Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Thu Aug 7 21:30:13 2008 @@ -94,14 +94,11 @@ if hasattr(os, 'nice'): nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) - slave_main(channel.receive, channel.send, config) - -def slave_main(receive, send, config): while 1: - item = receive() + item = channel.receive() if item is None: - send(None) + channel.send(None) break runner = item._getrunner() testrep = runner(item) - send(testrep) + channel.send(testrep) From hpk at codespeak.net Thu Aug 7 21:52:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 Aug 2008 21:52:21 +0200 (CEST) Subject: [py-svn] r57088 - py/branch/event/py/io Message-ID: <20080807195221.7296B169EA3@codespeak.net> Author: hpk Date: Thu Aug 7 21:52:14 2008 New Revision: 57088 Modified: py/branch/event/py/io/terminalwriter.py Log: remove duplicate func Modified: py/branch/event/py/io/terminalwriter.py ============================================================================== --- py/branch/event/py/io/terminalwriter.py (original) +++ py/branch/event/py/io/terminalwriter.py Thu Aug 7 21:52:14 2008 @@ -26,22 +26,7 @@ terminal_width = get_terminal_width() -def ansi_print(text, esc, file=None, newline=True, flush=False): - if file is None: - file = sys.stderr - text = text.rstrip() - if esc and sys.platform != "win32" and file.isatty(): - if not isinstance(esc, tuple): - esc = (esc,) - text = (''.join(['\x1b[%sm' % cod for cod in esc]) + - text + - '\x1b[0m') # ANSI color code "reset" - if newline: - text += '\n' - file.write(text) - if flush: - file.flush() - +# XXX unify with _escaped func below def ansi_print(text, esc, file=None, newline=True, flush=False): if file is None: file = sys.stderr From hpk at codespeak.net Fri Aug 8 00:13:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 00:13:02 +0200 (CEST) Subject: [py-svn] r57090 - py/branch/event/py/test2 Message-ID: <20080807221302.2B921169E27@codespeak.net> Author: hpk Date: Fri Aug 8 00:13:02 2008 New Revision: 57090 Modified: py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/runner.py Log: remove bad imports, grrr pyc files Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Fri Aug 8 00:13:02 2008 @@ -3,7 +3,6 @@ """ import py from py.__.test2.collect import Collector, FSCollector, Item, configproperty -from py.__.test2 import pypresent class PyobjMixin(object): def obj(): Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Aug 8 00:13:02 2008 @@ -12,7 +12,6 @@ from py.__.test2.outcome import Skipped, Exit from py.__.test.outcome import Skipped as Skipped2 import py.__.test2.custompdb -from py.__.test2 import pypresent class RobustRun(object): """ a robust setup/execute/teardown protocol. """ From guido at codespeak.net Fri Aug 8 09:20:38 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 8 Aug 2008 09:20:38 +0200 (CEST) Subject: [py-svn] r57093 - py/branch/guido-svnwc-xml-status/py/path/svn/testing Message-ID: <20080808072038.9F707169EA3@codespeak.net> Author: guido Date: Fri Aug 8 09:20:37 2008 New Revision: 57093 Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py Log: Small improvement in execution speed of the conflict test, made that it re-uses the repo created in test setup rather than creating its own. Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py Fri Aug 8 09:20:37 2008 @@ -157,20 +157,21 @@ self.root.revert(rec=1) def test_status_conflict(self): - repo, wc = getrepowc('conflicttestrepo', 'conflictwc') - wccopy = py.path.svnwc(py.test.ensuretemp('conflictwccopy')) + wc = self.root + wccopy = py.path.svnwc( + py.test.ensuretemp('test_status_conflict_wccopy')) wccopy.checkout(wc.url) - p = wc.ensure('samplefile', file=1) + p = wc.ensure('conflictsamplefile', file=1) p.write('foo') - wc.commit('added samplefile') + wc.commit('added conflictsamplefile') wccopy.update() - assert wccopy.join('samplefile').check() + assert wccopy.join('conflictsamplefile').check() p.write('bar') wc.commit('wrote some data') - wccopy.join('samplefile').write('baz') + wccopy.join('conflictsamplefile').write('baz') wccopy.update() s = wccopy.status() - assert [x.basename for x in s.conflict] == ['samplefile'] + assert [x.basename for x in s.conflict] == ['conflictsamplefile'] def test_status_external(self): otherrepo, otherwc = getrepowc('externalrepo', 'externalwc') From hpk at codespeak.net Fri Aug 8 10:30:53 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 10:30:53 +0200 (CEST) Subject: [py-svn] r57094 - py/branch/event/py/test2/rep Message-ID: <20080808083053.4B319169F0B@codespeak.net> Author: hpk Date: Fri Aug 8 10:30:51 2008 New Revision: 57094 Modified: py/branch/event/py/test2/rep/collectonly.py Log: another old import, re-grrr pyc files Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Fri Aug 8 10:30:51 2008 @@ -37,7 +37,6 @@ self.indent = self.indent[:-len(self.INDENT)] def rep_SessionFinish(self, session): - from py.__.code.tbpresent import TBPresenter for ev in self._failed: ev.toterminal(self.out) From hpk at codespeak.net Fri Aug 8 12:58:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 12:58:00 +0200 (CEST) Subject: [py-svn] r57095 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080808105800.3D992169F0A@codespeak.net> Author: hpk Date: Fri Aug 8 12:57:59 2008 New Revision: 57095 Added: py/branch/event/py/test2/rsession/pickle.py py/branch/event/py/test2/rsession/testing/test_pickle.py Log: experimental ImmutablePickler with the purpose of sanitzing pickling/unpickling of collection trees Added: py/branch/event/py/test2/rsession/pickle.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rsession/pickle.py Fri Aug 8 12:57:59 2008 @@ -0,0 +1,29 @@ + +from cPickle import Pickler, Unpickler +from cStringIO import StringIO + +class ImmutablePickler: + def __init__(self): + self.picklememo = {} + self.unpicklememo = {} + + def dumps(self, obj): + f = StringIO() + pickler = Pickler(f) + pickler.memo = self.picklememo + pickler.dump(obj) + self.unpicklememo.update(dict( + [(str(x), y) + for x, y in self.picklememo.values()])) + return f.getvalue() + + def loads(self, string): + f = StringIO(string) + unpickler = Unpickler(f) + unpickler.memo = self.unpicklememo + res = unpickler.load() + self.picklememo.update(dict( + [(id(obj), (int(x), obj)) + for x, obj in self.unpicklememo.items()])) + return res + Added: py/branch/event/py/test2/rsession/testing/test_pickle.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rsession/testing/test_pickle.py Fri Aug 8 12:57:59 2008 @@ -0,0 +1,23 @@ + +import py +from py.__.test2.rsession.pickle import ImmutablePickler + +class A: + pass + +def test_pickle_and_back_IS_same(): + p1 = ImmutablePickler() + p2 = ImmutablePickler() + + def check(obj): + s1 = p1.dumps(obj) + d2 = p2.loads(s1) + s2 = p2.dumps(d2) + obj_back = p1.loads(s2) + assert obj is obj_back + + a1 = A() + a2 = A() + a2.a1 = a1 + for obj in {1:2}, [1,2,3], a1, a2: + yield check, obj From hpk at codespeak.net Fri Aug 8 13:29:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 13:29:19 +0200 (CEST) Subject: [py-svn] r57097 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080808112919.4A8FA1683E3@codespeak.net> Author: hpk Date: Fri Aug 8 13:29:18 2008 New Revision: 57097 Modified: py/branch/event/py/test2/rsession/pickle.py py/branch/event/py/test2/rsession/testing/test_pickle.py Log: allow for protocols other than 0, extend hack Modified: py/branch/event/py/test2/rsession/pickle.py ============================================================================== --- py/branch/event/py/test2/rsession/pickle.py (original) +++ py/branch/event/py/test2/rsession/pickle.py Fri Aug 8 13:29:18 2008 @@ -1,20 +1,26 @@ -from cPickle import Pickler, Unpickler from cStringIO import StringIO +from cPickle import Pickler, Unpickler class ImmutablePickler: - def __init__(self): + def __init__(self, protocol=0): self.picklememo = {} self.unpicklememo = {} + self.protocol = protocol def dumps(self, obj): f = StringIO() - pickler = Pickler(f) + pickler = Pickler(f, self.protocol) pickler.memo = self.picklememo pickler.dump(obj) - self.unpicklememo.update(dict( + if self.protocol == 0: + self.unpicklememo.update(dict( [(str(x), y) - for x, y in self.picklememo.values()])) + for x, y in self.picklememo.itervalues()])) + else: + self.unpicklememo.update(dict( + [(x, y) + for x, y in self.picklememo.itervalues()])) return f.getvalue() def loads(self, string): Modified: py/branch/event/py/test2/rsession/testing/test_pickle.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_pickle.py (original) +++ py/branch/event/py/test2/rsession/testing/test_pickle.py Fri Aug 8 13:29:18 2008 @@ -6,10 +6,10 @@ pass def test_pickle_and_back_IS_same(): - p1 = ImmutablePickler() - p2 = ImmutablePickler() - def check(obj): + def pickle_band_back_IS_same(obj, proto): + p1 = ImmutablePickler(protocol=proto) + p2 = ImmutablePickler(protocol=proto) s1 = p1.dumps(obj) d2 = p2.loads(s1) s2 = p2.dumps(d2) @@ -19,5 +19,6 @@ a1 = A() a2 = A() a2.a1 = a1 - for obj in {1:2}, [1,2,3], a1, a2: - yield check, obj + for proto in 0,1,2,-1: + for obj in {1:2}, [1,2,3], a1, a2: + yield pickle_band_back_IS_same, obj, proto From hpk at codespeak.net Fri Aug 8 13:34:16 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 13:34:16 +0200 (CEST) Subject: [py-svn] r57098 - py/branch/event/py/test2/rsession Message-ID: <20080808113416.46C0D169F8D@codespeak.net> Author: hpk Date: Fri Aug 8 13:34:15 2008 New Revision: 57098 Modified: py/branch/event/py/test2/rsession/pickle.py Log: defer updates of memo for when its actually needed Modified: py/branch/event/py/test2/rsession/pickle.py ============================================================================== --- py/branch/event/py/test2/rsession/pickle.py (original) +++ py/branch/event/py/test2/rsession/pickle.py Fri Aug 8 13:34:15 2008 @@ -4,32 +4,38 @@ class ImmutablePickler: def __init__(self, protocol=0): - self.picklememo = {} - self.unpicklememo = {} - self.protocol = protocol + self._picklememo = {} + self._unpicklememo = {} + self._protocol = protocol def dumps(self, obj): f = StringIO() - pickler = Pickler(f, self.protocol) - pickler.memo = self.picklememo + pickler = Pickler(f, self._protocol) + self._updatepicklememo() + pickler.memo = self._picklememo pickler.dump(obj) - if self.protocol == 0: - self.unpicklememo.update(dict( - [(str(x), y) - for x, y in self.picklememo.itervalues()])) - else: - self.unpicklememo.update(dict( - [(x, y) - for x, y in self.picklememo.itervalues()])) + return f.getvalue() def loads(self, string): f = StringIO(string) unpickler = Unpickler(f) - unpickler.memo = self.unpicklememo + self._updateunpicklememo() + unpickler.memo = self._unpicklememo res = unpickler.load() - self.picklememo.update(dict( - [(id(obj), (int(x), obj)) - for x, obj in self.unpicklememo.items()])) return res + def _updatepicklememo(self): + self._picklememo.update(dict( + [(id(obj), (int(x), obj)) + for x, obj in self._unpicklememo.items()])) + + def _updateunpicklememo(self): + if self._protocol == 0: + self._unpicklememo.update(dict( + [(str(x), y) + for x, y in self._picklememo.itervalues()])) + else: + self._unpicklememo.update(dict( + [(x, y) + for x, y in self._picklememo.itervalues()])) From hpk at codespeak.net Fri Aug 8 13:55:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 13:55:22 +0200 (CEST) Subject: [py-svn] r57099 - in py/branch/event/py/execnet: . testing Message-ID: <20080808115522.129EE169F4D@codespeak.net> Author: hpk Date: Fri Aug 8 13:55:21 2008 New Revision: 57099 Modified: py/branch/event/py/execnet/channel.py py/branch/event/py/execnet/gateway.py py/branch/event/py/execnet/message.py py/branch/event/py/execnet/testing/test_gateway.py Log: remove pickling support for execnet. mostly revert revs 56964, 56963, 56917, 56908 the approach is not complete and i think it's better to implement an "overlay" channel that is capable of pickling and can implement special policies. at least for now. Modified: py/branch/event/py/execnet/channel.py ============================================================================== --- py/branch/event/py/execnet/channel.py (original) +++ py/branch/event/py/execnet/channel.py Fri Aug 8 13:55:21 2008 @@ -1,8 +1,5 @@ import threading, weakref, sys import Queue -from cPickle import Pickler, Unpickler, PickleError -import marshal - if 'Message' not in globals(): from py.__.execnet.message import Message @@ -28,9 +25,8 @@ class Channel(object): """Communication channel between two possibly remote threads of code. """ RemoteError = RemoteError - _picklememo = None # not None means that we are in pickling mode - def __init__(self, gateway, id, pickle=False): + def __init__(self, gateway, id): assert isinstance(id, int) self.gateway = gateway self.id = id @@ -38,8 +34,6 @@ self._closed = False self._receiveclosed = threading.Event() self._remoteerrors = [] - if pickle: - self._picklememo = {} def setcallback(self, callback, endmarker=NO_ENDMARKER_WANTED): queue = self._items @@ -165,19 +159,6 @@ if isinstance(item, Channel): data = Message.CHANNEL_NEW(self.id, item.id) else: - if self._picklememo is not None: - from cStringIO import StringIO - f = StringIO() - pickler = Pickler(f, protocol=-1) # use best protocol - pickler.memo = self._picklememo - try: - pickler.dump(item) - except PickleError, e: - raise ValueError(str(e)) - item = f.getvalue() - else: - if not isinstance(item, str): - marshal.dumps(item) # to raise early data = Message.CHANNEL_DATA(self.id, item) self.gateway._send(data) @@ -225,7 +206,7 @@ self.count = startcount self.finished = False - def new(self, id=None, pickle=False): + def new(self, id=None): """ create a new Channel with 'id' (or create new id if None). """ self._writelock.acquire() try: @@ -234,7 +215,7 @@ if id is None: id = self.count self.count += 2 - channel = Channel(self.gateway, id, pickle=pickle) + channel = Channel(self.gateway, id) self._channels[id] = channel return channel finally: @@ -292,16 +273,14 @@ queue.put(ENDMARKER) self._no_longer_opened(id) - def _local_receive(self, id, data, tryunpickle=False): + def _local_receive(self, id, data): # executes in receiver thread self._receivelock.acquire() try: - channel = self._channels.get(id) - if tryunpickle and channel and channel._picklememo is not None: - data = unpickle(data, channel._picklememo) try: callback, endmarker = self._callbacks[id] except KeyError: + channel = self._channels.get(id) queue = channel and channel._items if queue is None: pass # drop data @@ -371,9 +350,3 @@ line += c return line -def unpickle(data, memo): - from cStringIO import StringIO - f = StringIO(data) - u = Unpickler(f) - u.memo = memo - return u.load() Modified: py/branch/event/py/execnet/gateway.py ============================================================================== --- py/branch/event/py/execnet/gateway.py (original) +++ py/branch/event/py/execnet/gateway.py Fri Aug 8 13:55:21 2008 @@ -149,8 +149,6 @@ try: msg.writeto(self._io) except: - # XXX on the very local side we may not want - # this catching but rather propagate excinfo = self.exc_info() self._traceex(excinfo) msg.post_sent(self, excinfo) @@ -234,7 +232,7 @@ exec co in loc finally: close() - self._trace("execution finished:", repr(source)[:500]) + self._trace("execution finished:", repr(source)[:50]) except (KeyboardInterrupt, SystemExit): pass except self._StopExecLoop: @@ -264,19 +262,17 @@ # High Level Interface # _____________________________________________________________________ # - def newchannel(self): + def newchannel(self): """ return new channel object. """ return self._channelfactory.new() - def remote_exec(self, source, stdout=None, stderr=None, _pickle=False): + def remote_exec(self, source, stdout=None, stderr=None): """ return channel object and connect it to a remote execution thread where the given 'source' executes and has the sister 'channel' object in its global namespace. The callback functions 'stdout' and 'stderr' get called on receival of remote stdout/stderr output strings. - _pickle: set to true to enable experimental support for - sending/receiving picklable objects through the channel. """ try: source = str(Source(source)) @@ -286,11 +282,11 @@ source = str(py.code.Source(source)) except ImportError: pass - channel = self._channelfactory.new(pickle=_pickle) + channel = self.newchannel() outid = self._newredirectchannelid(stdout) errid = self._newredirectchannelid(stderr) self._send(Message.CHANNEL_OPEN( - channel.id, (_pickle, (source, outid, errid)))) + channel.id, (source, outid, errid))) return channel def _remote_redirect(self, stdout=None, stderr=None): Modified: py/branch/event/py/execnet/message.py ============================================================================== --- py/branch/event/py/execnet/message.py (original) +++ py/branch/event/py/execnet/message.py Fri Aug 8 13:55:21 2008 @@ -62,9 +62,8 @@ class CHANNEL_OPEN(Message): def received(self, gateway): - pickle, sourcetask = self.data - channel = gateway._channelfactory.new(self.channelid, pickle=pickle) - gateway._local_schedulexec(channel=channel, sourcetask=sourcetask) + channel = gateway._channelfactory.new(self.channelid) + gateway._local_schedulexec(channel=channel, sourcetask=self.data) class CHANNEL_NEW(Message): def received(self, gateway): @@ -75,8 +74,7 @@ class CHANNEL_DATA(Message): def received(self, gateway): - gateway._channelfactory._local_receive(self.channelid, self.data, - tryunpickle=True) + gateway._channelfactory._local_receive(self.channelid, self.data) class CHANNEL_CLOSE(Message): def received(self, gateway): Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Fri Aug 8 13:55:21 2008 @@ -78,12 +78,6 @@ channel = self.fac.new() py.test2.raises(ValueError, 'channel.makefile("rw")') - def test_channel_send_errors_nopickling(self): - channel = self.fac.new(pickle=False) - class A: - pass - py.test.raises(ValueError, "channel.send(A())") - class PopenGatewayTestSetup: def setup_class(cls): @@ -401,37 +395,9 @@ s = f.readline() assert s == "45" - def test_channel_send_errors_pickling(self): - channel = self.gw.remote_exec("", _pickle=True) - channel.send(object()) # works here - class A: pass - py.test.raises(ValueError, "channel.send(A)") - - def test_pickling(self): - channel = self.gw.remote_exec(""" - l1 = channel.receive() - l2 = channel.receive() - channel.send(l1 + l2) - channel.send(l2 is l1) - """, _pickle=True) - assert isinstance(channel._picklememo, dict) - l1 = ['hello'] - channel.send(l1) - channel.send(l1) - newl = channel.receive() - isl = channel.receive() - assert newl == l1 + l1 - assert isl == True - - def test_pickling_identity(self): - channel = self.gw.remote_exec(""" - channel.send(channel.receive()) - """, _pickle=True) - l1 = ['hello'] - channel.send(l1) - l2 = channel.receive() - assert l1 == l2 - assert l1 is not l2 + def test_channel_makefile_incompatmode(self): + channel = self.gw.newchannel() + py.test2.raises(ValueError, 'channel.makefile("rw")') def test_confusion_from_os_write_stdout(self): channel = self.gw.remote_exec(""" @@ -520,7 +486,7 @@ c = gw.remote_exec("import os ; channel.send(os.getcwd())") x = c.receive() assert x == str(waschangedir) - + def test_many_popen(self): num = 4 l = [] From hpk at codespeak.net Fri Aug 8 15:26:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 15:26:30 +0200 (CEST) Subject: [py-svn] r57100 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080808132630.A4093169E0B@codespeak.net> Author: hpk Date: Fri Aug 8 15:26:29 2008 New Revision: 57100 Modified: py/branch/event/py/test2/rsession/pickle.py py/branch/event/py/test2/rsession/testing/test_pickle.py Log: introduce a new transparent overlay PickleChannel that helps to maintain object identity between two sides. Modified: py/branch/event/py/test2/rsession/pickle.py ============================================================================== --- py/branch/event/py/test2/rsession/pickle.py (original) +++ py/branch/event/py/test2/rsession/pickle.py Fri Aug 8 15:26:29 2008 @@ -1,6 +1,7 @@ from cStringIO import StringIO from cPickle import Pickler, Unpickler +from py.__.execnet.channel import Channel class ImmutablePickler: def __init__(self, protocol=0): @@ -14,7 +15,6 @@ self._updatepicklememo() pickler.memo = self._picklememo pickler.dump(obj) - return f.getvalue() def loads(self, string): @@ -39,3 +39,45 @@ self._unpicklememo.update(dict( [(x, y) for x, y in self._picklememo.itervalues()])) + +NO_ENDMARKER_WANTED = object() + +class PickleChannel(object): + def __init__(self, channel): + self._channel = channel + self._ipickle = ImmutablePickler() # xxx proto optimization? + + def send(self, obj): + if not isinstance(obj, Channel): + pickled_obj = self._ipickle.dumps(obj) + self._channel.send(pickled_obj) + else: + self._channel.send(obj) + + def receive(self): + pickled_obj = self._channel.receive() + return self._unpickle(pickled_obj) + + def _unpickle(self, pickled_obj): + if isinstance(pickled_obj, self._channel.__class__): + return pickled_obj + return self._ipickle.loads(pickled_obj) + + def waitclose(self): + return self._channel.waitclose() + + def setcallback(self, callback, endmarker=NO_ENDMARKER_WANTED): + if endmarker is NO_ENDMARKER_WANTED: + def unpickle_callback(pickled_obj): + obj = self._unpickle(pickled_obj) + callback(obj) + self._channel.setcallback(unpickle_callback) + else: + uniqueendmarker = object() + def unpickle_callback_endmarked(pickled_obj): + if pickled_obj is uniqueendmarker: + callback(endmarker) + else: + obj = self._unpickle(pickled_obj) + callback(obj) + self._channel.setcallback(unpickle_callback_endmarked, uniqueendmarker) Modified: py/branch/event/py/test2/rsession/testing/test_pickle.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_pickle.py (original) +++ py/branch/event/py/test2/rsession/testing/test_pickle.py Fri Aug 8 15:26:29 2008 @@ -1,10 +1,9 @@ import py -from py.__.test2.rsession.pickle import ImmutablePickler +from py.__.test2.rsession.pickle import ImmutablePickler, PickleChannel class A: pass - def test_pickle_and_back_IS_same(): def pickle_band_back_IS_same(obj, proto): @@ -22,3 +21,92 @@ for proto in 0,1,2,-1: for obj in {1:2}, [1,2,3], a1, a2: yield pickle_band_back_IS_same, obj, proto + + +TESTTIMEOUT = 2.0 +class TestPickleChannelFunctional: + def setup_class(cls): + cls.gw = py.execnet.PopenGateway() + cls.gw.remote_init_threads(5) + + def teardown_class(cls): + cls.gw.exit() + + def test_popen_send_instance(self): + channel = self.gw.remote_exec(""" + from py.__.test2.rsession.pickle import PickleChannel + channel = PickleChannel(channel) + from py.__.test2.rsession.testing.test_pickle import A + a1 = A() + a1.hello = 10 + channel.send(a1) + a2 = channel.receive() + channel.send(a2 is a1) + """) + channel = PickleChannel(channel) + a_received = channel.receive() + assert isinstance(a_received, A) + assert a_received.hello == 10 + channel.send(a_received) + remote_a2_is_a1 = channel.receive() + assert remote_a2_is_a1 + + + def test_popen_with_callback(self): + channel = self.gw.remote_exec(""" + from py.__.test2.rsession.pickle import PickleChannel + channel = PickleChannel(channel) + from py.__.test2.rsession.testing.test_pickle import A + a1 = A() + a1.hello = 10 + channel.send(a1) + a2 = channel.receive() + channel.send(a2 is a1) + """) + channel = PickleChannel(channel) + queue = py.std.Queue.Queue() + channel.setcallback(queue.put) + a_received = queue.get(timeout=TESTTIMEOUT) + assert isinstance(a_received, A) + assert a_received.hello == 10 + channel.send(a_received) + #remote_a2_is_a1 = queue.get(timeout=TESTTIMEOUT) + #assert remote_a2_is_a1 + + def test_popen_with_callback_with_endmarker(self): + channel = self.gw.remote_exec(""" + from py.__.test2.rsession.pickle import PickleChannel + channel = PickleChannel(channel) + from py.__.test2.rsession.testing.test_pickle import A + a1 = A() + a1.hello = 10 + channel.send(a1) + a2 = channel.receive() + channel.send(a2 is a1) + """) + channel = PickleChannel(channel) + queue = py.std.Queue.Queue() + channel.setcallback(queue.put, endmarker=-1) + + a_received = queue.get(timeout=TESTTIMEOUT) + assert isinstance(a_received, A) + assert a_received.hello == 10 + channel.send(a_received) + remote_a2_is_a1 = queue.get(timeout=TESTTIMEOUT) + assert remote_a2_is_a1 + endmarker = queue.get(timeout=TESTTIMEOUT) + assert endmarker == -1 + + def test_popen_with_newchannel(self): + channel = self.gw.remote_exec(""" + from py.__.test2.rsession.pickle import PickleChannel + channel = PickleChannel(channel) + newchannel = channel.receive() + newchannel.send(42) + """) + channel = PickleChannel(channel) + newchannel = self.gw.newchannel() + channel.send(newchannel) + channel.waitclose() + res = newchannel.receive() + assert res == 42 From hpk at codespeak.net Fri Aug 8 15:50:50 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 15:50:50 +0200 (CEST) Subject: [py-svn] r57101 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080808135050.8099B169FC4@codespeak.net> Author: hpk Date: Fri Aug 8 15:50:48 2008 New Revision: 57101 Modified: py/branch/event/py/test2/rsession/pickle.py py/branch/event/py/test2/rsession/testing/test_pickle.py Log: add various proxy methods Modified: py/branch/event/py/test2/rsession/pickle.py ============================================================================== --- py/branch/event/py/test2/rsession/pickle.py (original) +++ py/branch/event/py/test2/rsession/pickle.py Fri Aug 8 15:50:48 2008 @@ -63,8 +63,17 @@ return pickled_obj return self._ipickle.loads(pickled_obj) - def waitclose(self): - return self._channel.waitclose() + def _getremoteerror(self): + return self._channel._getremoteerror() + + def close(self): + return self._channel.close() + + def isclosed(self): + return self._channel.isclosed() + + def waitclose(self, timeout=None): + return self._channel.waitclose(timeout=timeout) def setcallback(self, callback, endmarker=NO_ENDMARKER_WANTED): if endmarker is NO_ENDMARKER_WANTED: Modified: py/branch/event/py/test2/rsession/testing/test_pickle.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_pickle.py (original) +++ py/branch/event/py/test2/rsession/testing/test_pickle.py Fri Aug 8 15:50:48 2008 @@ -110,3 +110,16 @@ channel.waitclose() res = newchannel.receive() assert res == 42 + + def test_popen_with_various_methods(self): + channel = self.gw.remote_exec(""" + from py.__.test2.rsession.pickle import PickleChannel + channel = PickleChannel(channel) + channel.receive() + """) + channel = PickleChannel(channel) + assert not channel.isclosed() + assert not channel._getremoteerror() + channel.send(2) + channel.waitclose(timeout=2) + From hpk at codespeak.net Fri Aug 8 16:08:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 16:08:36 +0200 (CEST) Subject: [py-svn] r57102 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080808140836.6019E169F4D@codespeak.net> Author: hpk Date: Fri Aug 8 16:08:35 2008 New Revision: 57102 Modified: py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: use new pickle machinery Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Fri Aug 8 16:08:35 2008 @@ -3,6 +3,7 @@ """ import py from py.__.test2 import repevent +from py.__.test2.rsession.pickle import PickleChannel class MasterNode(object): ENDMARK = -1 @@ -64,8 +65,10 @@ def send_and_receive_pickled_config(channel, config, remote_topdir): channel.send((config, remote_topdir)) backconfig = channel.receive() - backconfig._initafterpickle(config.topdir) - return backconfig + assert config is backconfig + return backconfig + #backconfig._initafterpickle(config.topdir) + #return backconfig def receive_and_send_pickled_config(channel): config,topdir = channel.receive() @@ -75,11 +78,14 @@ # setting up slave code def install_slave(host, config): - channel = host.gw.remote_exec(_pickle=True, source=""" + channel = host.gw.remote_exec(source=""" + from py.__.test2.rsession.pickle import PickleChannel + channel = PickleChannel(channel) from py.__.test2.rsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) masterslave.setup_at_slave_side(channel, config) """) + channel = PickleChannel(channel) remote_topdir = host.gw_remotepath if remote_topdir is None: assert host.inplacelocal Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Fri Aug 8 16:08:35 2008 @@ -9,7 +9,7 @@ from py.__.test2 import repevent -def test_receive_config(): +def xxxtest_receive_config(): from cPickle import dumps, loads l = [] class MyChannel: @@ -27,7 +27,7 @@ backconfig = channel.receive() assert not backconfig._initialized -def test_send_config(): +def xxxtest_send_config(): from cPickle import dumps, loads l = [] class MyChannel: From hpk at codespeak.net Fri Aug 8 22:39:57 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 Aug 2008 22:39:57 +0200 (CEST) Subject: [py-svn] r57112 - in py/branch/event/py/test2: . rsession rsession/testing testing Message-ID: <20080808203957.C3B74169E97@codespeak.net> Author: hpk Date: Fri Aug 8 22:39:57 2008 New Revision: 57112 Added: py/branch/event/py/test2/rsession/mypickle.py - copied, changed from r57101, py/branch/event/py/test2/rsession/pickle.py py/branch/event/py/test2/rsession/testing/test_mypickle.py - copied, changed from r57101, py/branch/event/py/test2/rsession/testing/test_pickle.py Removed: py/branch/event/py/test2/rsession/pickle.py py/branch/event/py/test2/rsession/testing/test_pickle.py Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/rsession/testing/test_rsession.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_collect.py Log: * implement, fix and carefully test immutable pickling hopefully this is stable now. * distributed test runs now work much more quickly although work and probably some bug fixing on the actual test distribution remains Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Fri Aug 8 22:39:57 2008 @@ -86,18 +86,18 @@ self._config = config self.fspath = getattr(parent, 'fspath', None) + # + # note to myself: Pickling is uh. + # def __getstate__(self): - config = self._config - return (config, self._totrail()) - def __setstate__(self, (config, trail)): - if not config._initialized: - raise ValueError("incomplete unpickling of " - "config object, need call to _initafterpickle()?") - newnode = self._fromtrail(trail, config) + return (self.name, self.parent) + def __setstate__(self, (name, parent)): + newnode = parent.join(name) self.__dict__.update(newnode.__dict__) + #self.__init__(name=name, parent=parent) def __repr__(self): - return "<%s %r>" %(self.__class__.__name__, self.name) + return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None)) # methods for ordering nodes @@ -277,6 +277,36 @@ super(FSCollector, self).__init__(fspath.basename, parent, config=config) self.fspath = fspath + def __getstate__(self): + if self.parent is None: + # the root node needs to pickle more context info + topdir = self._config.topdir + relpath = self.fspath.relto(topdir) + if not relpath: + if self.fspath == topdir: + relpath = "." + else: + raise ValueError("%r not relative to topdir %s" + %(self.fspath, topdir)) + return (self.name, self._config, relpath) + else: + return (self.name, self.parent) + + def __setstate__(self, picklestate): + if len(picklestate) == 3: + # root node + name, config, relpath = picklestate + if not config._initialized: + raise ValueError("incomplete unpickling of " + "config object, need call to _initafterpickle()?") + fspath = config.topdir.join(relpath) + fsnode = config.getfsnode(fspath) + self.__dict__.update(fsnode.__dict__) + else: + name, parent = picklestate + self.__init__(parent.fspath.join(name), parent=parent) + + class Directory(FSCollector): def filefilter(self, path): if path.check(file=1): Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Fri Aug 8 22:39:57 2008 @@ -154,14 +154,14 @@ def setup_hosts(self): self.init_rsync() - self.queue = py.std.Queue.Queue() - self.session.bus.subscribe(self.queue.put) for host in self.hosts: host.node = MasterNode(host, self.session.config, self.session.bus.notify) def wait_for_completion(self, maxtimeout=2.0): + self.queue = py.std.Queue.Queue() + self.session.bus.subscribe(self.queue.put) #remaining = [] pending_going_down = [] while self.hosts: Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Fri Aug 8 22:39:57 2008 @@ -3,7 +3,7 @@ """ import py from py.__.test2 import repevent -from py.__.test2.rsession.pickle import PickleChannel +from py.__.test2.rsession.mypickle import PickleChannel class MasterNode(object): ENDMARK = -1 @@ -40,18 +40,21 @@ if isinstance(ev, repevent.ItemTestReport): item = self.pending.pop() else: - raise ev + import pdb + pdb.post_mortem(ev._excinfo[2]) + raise ValueError("received unexpected object") except KeyboardInterrupt: raise except: excinfo = py.code.ExceptionInfo() + print "!" * 20, excinfo ev = repevent.InternalException(excinfo) self.notify(ev) def send(self, item): self.channel.send(item) if item is not None: - self.pending.insert(0, item) + self.pending.append(item) self.notify(repevent.SendItem(self.host, item)) # @@ -67,8 +70,6 @@ backconfig = channel.receive() assert config is backconfig return backconfig - #backconfig._initafterpickle(config.topdir) - #return backconfig def receive_and_send_pickled_config(channel): config,topdir = channel.receive() @@ -79,7 +80,7 @@ # setting up slave code def install_slave(host, config): channel = host.gw.remote_exec(source=""" - from py.__.test2.rsession.pickle import PickleChannel + from py.__.test2.rsession.mypickle import PickleChannel channel = PickleChannel(channel) from py.__.test2.rsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) Copied: py/branch/event/py/test2/rsession/mypickle.py (from r57101, py/branch/event/py/test2/rsession/pickle.py) ============================================================================== --- py/branch/event/py/test2/rsession/pickle.py (original) +++ py/branch/event/py/test2/rsession/mypickle.py Fri Aug 8 22:39:57 2008 @@ -1,28 +1,74 @@ +""" + + Pickling support for two processes that want to exchange + *immutable* object instances. Immutable in the sense + that the receiving side of an object can modify its + copy but when it sends it back the original sending + side will continue to see its unmodified version + (and no actual state will go over the wire). + + This module also implements an experimental + execnet pickling channel using this idea. + +""" from cStringIO import StringIO -from cPickle import Pickler, Unpickler +from pickle import Pickler, Unpickler +import py from py.__.execnet.channel import Channel +import os +#debug = open("log-mypickle-%d" % os.getpid(), 'w') + +class MyPickler(Pickler): + """ Pickler with a custom memoize() + to take care of unique ID creation. + See the usage in ImmutablePickler + """ + def __init__(self, file, protocol, uneven): + Pickler.__init__(self, file, protocol) + self.uneven = uneven + + def memoize(self, obj): + if self.fast: + return + assert id(obj) not in self.memo + memo_len = len(self.memo) + key = memo_len * 2 + self.uneven + self.write(self.put(key)) + self.memo[id(obj)] = key, obj class ImmutablePickler: - def __init__(self, protocol=0): + def __init__(self, uneven, protocol=0): + """ ImmutablePicklers are instantiated in Pairs. + The two sides need to create unique IDs + while pickling their objects. This is + done by using either even or uneven + numbers, depending on the instantiation + parameter. + """ self._picklememo = {} self._unpicklememo = {} self._protocol = protocol + self.uneven = uneven and 1 or 0 def dumps(self, obj): f = StringIO() - pickler = Pickler(f, self._protocol) - self._updatepicklememo() + pickler = MyPickler(f, self._protocol, uneven=self.uneven) pickler.memo = self._picklememo pickler.dump(obj) + self._updateunpicklememo() + #print >>debug, "dumped", obj + #print >>debug, "picklememo", self._picklememo return f.getvalue() def loads(self, string): f = StringIO(string) unpickler = Unpickler(f) - self._updateunpicklememo() unpickler.memo = self._unpicklememo res = unpickler.load() + self._updatepicklememo() + #print >>debug, "loaded", res + #print >>debug, "unpicklememo", self._unpicklememo return res def _updatepicklememo(self): @@ -31,21 +77,37 @@ for x, obj in self._unpicklememo.items()])) def _updateunpicklememo(self): - if self._protocol == 0: - self._unpicklememo.update(dict( - [(str(x), y) - for x, y in self._picklememo.itervalues()])) - else: - self._unpicklememo.update(dict( - [(x, y) - for x, y in self._picklememo.itervalues()])) + for key,obj in self._picklememo.values(): + key = str(key) + if key in self._unpicklememo: + assert self._unpicklememo[key] is obj + self._unpicklememo[key] = obj NO_ENDMARKER_WANTED = object() +class UnpickleError(Exception): + """ Problems while unpickling. """ + def __init__(self, formatted): + self.formatted = formatted + super(UnpickleError, self).__init__(formatted) + def __str__(self): + return self.formatted + class PickleChannel(object): + """ PickleChannels wrap execnet channels + and allow to send/receive by using + "immutable pickling". + """ + _unpicklingerror = None def __init__(self, channel): self._channel = channel - self._ipickle = ImmutablePickler() # xxx proto optimization? + # we use the fact that each side of a + # gateway connection counts with uneven + # or even numbers depending on which + # side it is (for the purpose of creating + # unique ids - which is what we need it here for) + uneven = channel.gateway._channelfactory.count % 2 + self._ipickle = ImmutablePickler(uneven=uneven) def send(self, obj): if not isinstance(obj, Channel): @@ -64,7 +126,7 @@ return self._ipickle.loads(pickled_obj) def _getremoteerror(self): - return self._channel._getremoteerror() + return self._unpicklingerror or self._channel._getremoteerror() def close(self): return self._channel.close() @@ -81,12 +143,20 @@ obj = self._unpickle(pickled_obj) callback(obj) self._channel.setcallback(unpickle_callback) - else: - uniqueendmarker = object() - def unpickle_callback_endmarked(pickled_obj): - if pickled_obj is uniqueendmarker: - callback(endmarker) - else: - obj = self._unpickle(pickled_obj) - callback(obj) - self._channel.setcallback(unpickle_callback_endmarked, uniqueendmarker) + return + uniqueendmarker = object() + def unpickle_callback(pickled_obj): + if pickled_obj is uniqueendmarker: + return callback(endmarker) + try: + obj = self._unpickle(pickled_obj) + except KeyboardInterrupt: + raise + except: + excinfo = py.code.ExceptionInfo() + formatted = str(excinfo.getrepr(showlocals=True,funcargs=True)) + self._unpicklingerror = UnpickleError(formatted) + callback(endmarker) + else: + callback(obj) + self._channel.setcallback(unpickle_callback, uniqueendmarker) Deleted: /py/branch/event/py/test2/rsession/pickle.py ============================================================================== --- /py/branch/event/py/test2/rsession/pickle.py Fri Aug 8 22:39:57 2008 +++ (empty file) @@ -1,92 +0,0 @@ - -from cStringIO import StringIO -from cPickle import Pickler, Unpickler -from py.__.execnet.channel import Channel - -class ImmutablePickler: - def __init__(self, protocol=0): - self._picklememo = {} - self._unpicklememo = {} - self._protocol = protocol - - def dumps(self, obj): - f = StringIO() - pickler = Pickler(f, self._protocol) - self._updatepicklememo() - pickler.memo = self._picklememo - pickler.dump(obj) - return f.getvalue() - - def loads(self, string): - f = StringIO(string) - unpickler = Unpickler(f) - self._updateunpicklememo() - unpickler.memo = self._unpicklememo - res = unpickler.load() - return res - - def _updatepicklememo(self): - self._picklememo.update(dict( - [(id(obj), (int(x), obj)) - for x, obj in self._unpicklememo.items()])) - - def _updateunpicklememo(self): - if self._protocol == 0: - self._unpicklememo.update(dict( - [(str(x), y) - for x, y in self._picklememo.itervalues()])) - else: - self._unpicklememo.update(dict( - [(x, y) - for x, y in self._picklememo.itervalues()])) - -NO_ENDMARKER_WANTED = object() - -class PickleChannel(object): - def __init__(self, channel): - self._channel = channel - self._ipickle = ImmutablePickler() # xxx proto optimization? - - def send(self, obj): - if not isinstance(obj, Channel): - pickled_obj = self._ipickle.dumps(obj) - self._channel.send(pickled_obj) - else: - self._channel.send(obj) - - def receive(self): - pickled_obj = self._channel.receive() - return self._unpickle(pickled_obj) - - def _unpickle(self, pickled_obj): - if isinstance(pickled_obj, self._channel.__class__): - return pickled_obj - return self._ipickle.loads(pickled_obj) - - def _getremoteerror(self): - return self._channel._getremoteerror() - - def close(self): - return self._channel.close() - - def isclosed(self): - return self._channel.isclosed() - - def waitclose(self, timeout=None): - return self._channel.waitclose(timeout=timeout) - - def setcallback(self, callback, endmarker=NO_ENDMARKER_WANTED): - if endmarker is NO_ENDMARKER_WANTED: - def unpickle_callback(pickled_obj): - obj = self._unpickle(pickled_obj) - callback(obj) - self._channel.setcallback(unpickle_callback) - else: - uniqueendmarker = object() - def unpickle_callback_endmarked(pickled_obj): - if pickled_obj is uniqueendmarker: - callback(endmarker) - else: - obj = self._unpickle(pickled_obj) - callback(obj) - self._channel.setcallback(unpickle_callback_endmarked, uniqueendmarker) Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Fri Aug 8 22:39:57 2008 @@ -16,9 +16,6 @@ def fixoptions(self): super(RSession, self).fixoptions() option = self.config.option - if option.nocapture: - print "Cannot use nocapture with distributed testing" - py.std.sys.exit(1) config = self.config try: config.getvalue('dist_hosts') Copied: py/branch/event/py/test2/rsession/testing/test_mypickle.py (from r57101, py/branch/event/py/test2/rsession/testing/test_pickle.py) ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_pickle.py (original) +++ py/branch/event/py/test2/rsession/testing/test_mypickle.py Fri Aug 8 22:39:57 2008 @@ -1,14 +1,13 @@ import py -from py.__.test2.rsession.pickle import ImmutablePickler, PickleChannel +from py.__.test2.rsession.mypickle import ImmutablePickler, PickleChannel, UnpickleError class A: pass def test_pickle_and_back_IS_same(): - def pickle_band_back_IS_same(obj, proto): - p1 = ImmutablePickler(protocol=proto) - p2 = ImmutablePickler(protocol=proto) + p1 = ImmutablePickler(uneven=False, protocol=proto) + p2 = ImmutablePickler(uneven=True, protocol=proto) s1 = p1.dumps(obj) d2 = p2.loads(s1) s2 = p2.dumps(d2) @@ -18,10 +17,45 @@ a1 = A() a2 = A() a2.a1 = a1 - for proto in 0,1,2,-1: + for proto in (0,1,2, -1): for obj in {1:2}, [1,2,3], a1, a2: yield pickle_band_back_IS_same, obj, proto +def test_pickling_twice_before_unpickling(): + p1 = ImmutablePickler(uneven=False) + p2 = ImmutablePickler(uneven=True) + + a1 = A() + a2 = A() + a3 = A() + a3.a1 = a1 + a2.a1 = a1 + s1 = p1.dumps(a1) + a1.a3 = a3 + s2 = p1.dumps(a2) + other_a1 = p2.loads(s1) + other_a2 = p2.loads(s2) + back_a1 = p1.loads(p2.dumps(other_a1)) + other_a3 = p2.loads(p1.dumps(a3)) + back_a3 = p1.loads(p2.dumps(other_a3)) + back_a2 = p1.loads(p2.dumps(other_a2)) + back_a1 = p1.loads(p2.dumps(other_a1)) + assert back_a1 is a1 + assert back_a2 is a2 + +def test_pickling_concurrently(): + p1 = ImmutablePickler(uneven=False) + p2 = ImmutablePickler(uneven=True) + + a1 = A() + a1.hasattr = 42 + a2 = A() + + s1 = p1.dumps(a1) + s2 = p2.dumps(a2) + other_a1 = p2.loads(s1) + other_a2 = p1.loads(s2) + a1_back = p1.loads(p2.dumps(other_a1)) TESTTIMEOUT = 2.0 class TestPickleChannelFunctional: @@ -34,9 +68,9 @@ def test_popen_send_instance(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel + from py.__.test2.rsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_pickle import A + from py.__.test2.rsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -51,12 +85,36 @@ remote_a2_is_a1 = channel.receive() assert remote_a2_is_a1 - + def test_send_concurrent(self): + channel = self.gw.remote_exec(""" + from py.__.test2.rsession.mypickle import PickleChannel + channel = PickleChannel(channel) + from py.__.test2.rsession.testing.test_mypickle import A + l = [A() for i in range(10)] + channel.send(l) + other_l = channel.receive() + channel.send((l, other_l)) + """) + channel = PickleChannel(channel) + l = [A() for i in range(10)] + channel.send(l) + other_l = channel.receive() + channel.send(other_l) + ret = channel.receive() + assert ret[0] is other_l + assert ret[1] is l + + #s1 = p1.dumps(a1) + #s2 = p2.dumps(a2) + #other_a1 = p2.loads(s1) + #other_a2 = p1.loads(s2) + #a1_back = p1.loads(p2.dumps(other_a1)) + def test_popen_with_callback(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel + from py.__.test2.rsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_pickle import A + from py.__.test2.rsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -75,9 +133,9 @@ def test_popen_with_callback_with_endmarker(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel + from py.__.test2.rsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_pickle import A + from py.__.test2.rsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -97,9 +155,28 @@ endmarker = queue.get(timeout=TESTTIMEOUT) assert endmarker == -1 + def test_popen_with_callback_with_endmarker_and_unpickling_error(self): + channel = self.gw.remote_exec(""" + from py.__.test2.rsession.mypickle import PickleChannel + channel = PickleChannel(channel) + from py.__.test2.rsession.testing.test_mypickle import A + a1 = A() + channel.receive() + channel.send(a1) + """) + channel = PickleChannel(channel) + queue = py.std.Queue.Queue() + channel.send("start") + del channel._ipickle._unpicklememo + channel.setcallback(queue.put, endmarker=-1) + next = queue.get(timeout=TESTTIMEOUT) + assert next == -1 + error = channel._getremoteerror() + assert isinstance(error, UnpickleError) + def test_popen_with_newchannel(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel + from py.__.test2.rsession.mypickle import PickleChannel channel = PickleChannel(channel) newchannel = channel.receive() newchannel.send(42) @@ -113,7 +190,7 @@ def test_popen_with_various_methods(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel + from py.__.test2.rsession.mypickle import PickleChannel channel = PickleChannel(channel) channel.receive() """) Deleted: /py/branch/event/py/test2/rsession/testing/test_pickle.py ============================================================================== --- /py/branch/event/py/test2/rsession/testing/test_pickle.py Fri Aug 8 22:39:57 2008 +++ (empty file) @@ -1,125 +0,0 @@ - -import py -from py.__.test2.rsession.pickle import ImmutablePickler, PickleChannel - -class A: - pass -def test_pickle_and_back_IS_same(): - - def pickle_band_back_IS_same(obj, proto): - p1 = ImmutablePickler(protocol=proto) - p2 = ImmutablePickler(protocol=proto) - s1 = p1.dumps(obj) - d2 = p2.loads(s1) - s2 = p2.dumps(d2) - obj_back = p1.loads(s2) - assert obj is obj_back - - a1 = A() - a2 = A() - a2.a1 = a1 - for proto in 0,1,2,-1: - for obj in {1:2}, [1,2,3], a1, a2: - yield pickle_band_back_IS_same, obj, proto - - -TESTTIMEOUT = 2.0 -class TestPickleChannelFunctional: - def setup_class(cls): - cls.gw = py.execnet.PopenGateway() - cls.gw.remote_init_threads(5) - - def teardown_class(cls): - cls.gw.exit() - - def test_popen_send_instance(self): - channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel - channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_pickle import A - a1 = A() - a1.hello = 10 - channel.send(a1) - a2 = channel.receive() - channel.send(a2 is a1) - """) - channel = PickleChannel(channel) - a_received = channel.receive() - assert isinstance(a_received, A) - assert a_received.hello == 10 - channel.send(a_received) - remote_a2_is_a1 = channel.receive() - assert remote_a2_is_a1 - - - def test_popen_with_callback(self): - channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel - channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_pickle import A - a1 = A() - a1.hello = 10 - channel.send(a1) - a2 = channel.receive() - channel.send(a2 is a1) - """) - channel = PickleChannel(channel) - queue = py.std.Queue.Queue() - channel.setcallback(queue.put) - a_received = queue.get(timeout=TESTTIMEOUT) - assert isinstance(a_received, A) - assert a_received.hello == 10 - channel.send(a_received) - #remote_a2_is_a1 = queue.get(timeout=TESTTIMEOUT) - #assert remote_a2_is_a1 - - def test_popen_with_callback_with_endmarker(self): - channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel - channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_pickle import A - a1 = A() - a1.hello = 10 - channel.send(a1) - a2 = channel.receive() - channel.send(a2 is a1) - """) - channel = PickleChannel(channel) - queue = py.std.Queue.Queue() - channel.setcallback(queue.put, endmarker=-1) - - a_received = queue.get(timeout=TESTTIMEOUT) - assert isinstance(a_received, A) - assert a_received.hello == 10 - channel.send(a_received) - remote_a2_is_a1 = queue.get(timeout=TESTTIMEOUT) - assert remote_a2_is_a1 - endmarker = queue.get(timeout=TESTTIMEOUT) - assert endmarker == -1 - - def test_popen_with_newchannel(self): - channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel - channel = PickleChannel(channel) - newchannel = channel.receive() - newchannel.send(42) - """) - channel = PickleChannel(channel) - newchannel = self.gw.newchannel() - channel.send(newchannel) - channel.waitclose() - res = newchannel.receive() - assert res == 42 - - def test_popen_with_various_methods(self): - channel = self.gw.remote_exec(""" - from py.__.test2.rsession.pickle import PickleChannel - channel = PickleChannel(channel) - channel.receive() - """) - channel = PickleChannel(channel) - assert not channel.isclosed() - assert not channel._getremoteerror() - channel.send(2) - channel.waitclose(timeout=2) - Modified: py/branch/event/py/test2/rsession/testing/test_rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_rsession.py (original) +++ py/branch/event/py/test2/rsession/testing/test_rsession.py Fri Aug 8 22:39:57 2008 @@ -14,10 +14,53 @@ if py.std.sys.platform == "win32": py.test.skip("rsession tests disabled for win32") -class TestRSessionRemote(DirSetup, BasicRsessionTest): + +def eventreader(session): + queue = py.std.Queue.Queue() + session.bus.subscribe(queue.put) + def readevent(eventtype=repevent.ItemTestReport, timeout=2.0): + events = [] + while 1: + try: + ev = queue.get(timeout=timeout) + except py.std.Queue.Empty: + print "seen events", events + raise IOError("did not see %r events" % (eventtype)) + else: + if isinstance(ev, eventtype): + return ev + events.append(ev) + return readevent + +class TestRSessionRemote(DirSetup, BasicRsessionTest): def setup_method(self, method): DirSetup.setup_method(self, method) BasicRsessionTest.setup_method(self, method) + + def test_dist_some_tests(self): + self.source.ensure("conftest.py").write(py.code.Source(""" + dist_hosts = ['localhost'] + """)) + self.source.ensure("sub", "test_one.py").write(py.code.Source(""" + def test_1(): + pass + def test_x(): + import py + py.test2.skip("aaa") + def test_fail(): + assert 0 + """)) + config = py.test2.config._reparse([self.source.join("sub")]) + rsession = RSession(config) + readevent = eventreader(rsession) + rsession.main() + ev = readevent(repevent.ItemTestReport) + assert ev.passed + ev = readevent(repevent.ItemTestReport) + assert ev.skipped + ev = readevent(repevent.ItemTestReport) + assert ev.failed + def test_example_distribution_minus_x(self): self.source.ensure("sub", "conftest.py").write(py.code.Source(""" dist_hosts = ['localhost:%s'] Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Fri Aug 8 22:39:57 2008 @@ -166,7 +166,7 @@ # XXX below some code to help with inlining examples # as source code. -class InlineCollection: +class InlineCollection(object): """ helps to collect and run test functions inlining other test functions. """ def setup_method(self, method): self.tmpdir = py.test2.ensuretemp("%s_%s" % Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Fri Aug 8 22:39:57 2008 @@ -481,3 +481,57 @@ col3 = config.getfsnode(config.topdir.dirpath()) py.test2.raises(ValueError, "col3._totrail()") + + +from py.__.test2.rsession.mypickle import ImmutablePickler +class PickleTransport: + def __init__(self): + self.p1 = ImmutablePickler(uneven=0) + self.p2 = ImmutablePickler(uneven=1) + + def p1_to_p2(self, obj): + return self.p2.loads(self.p1.dumps(obj)) + + def p2_to_p1(self, obj): + return self.p1.loads(self.p2.dumps(obj)) + +class TestPickling(suptest.InlineCollection): + def setup_method(self, method): + super(TestPickling, self).setup_method(method) + pt = PickleTransport() + self.p1_to_p2 = pt.p1_to_p2 + self.p2_to_p1 = pt.p2_to_p1 + + def unifyconfig(self, config): + p2config = self.p1_to_p2(config) + p2config._initafterpickle(config.topdir) + return p2config + + def test_pickle_config(self): + config1 = py.test2.config._reparse([]) + p2config = self.unifyconfig(config1) + assert p2config.topdir == config1.topdir + config_back = self.p2_to_p1(p2config) + assert config_back is config1 + + def test_pickle_module(self): + modcol1 = self.getmodulecol("def test_one(): pass") + self.unifyconfig(modcol1._config) + + modcol2a = self.p1_to_p2(modcol1) + modcol2b = self.p1_to_p2(modcol1) + assert modcol2a is modcol2b + + modcol1_back = self.p2_to_p1(modcol2a) + assert modcol1_back + + def test_pickle_func(self): + modcol1 = self.getmodulecol("def test_one(): pass") + self.unifyconfig(modcol1._config) + item = modcol1.join("test_one") + item2a = self.p1_to_p2(item) + assert item is not item2a # of course + assert item2a.name == item.name + modback = self.p2_to_p1(item2a.parent) + assert modback is modcol1 + From hpk at codespeak.net Sat Aug 9 00:35:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 00:35:21 +0200 (CEST) Subject: [py-svn] r57114 - in py/branch/event/py/test2/rep: . testing Message-ID: <20080808223521.5D43E169FFD@codespeak.net> Author: hpk Date: Sat Aug 9 00:35:21 2008 New Revision: 57114 Modified: py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/testing/test_basereporter.py Log: test and fix getrelpath Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Sat Aug 9 00:35:21 2008 @@ -73,13 +73,18 @@ except (TypeError, ValueError): return str(v) -def getrelpath(source, dest): - base = source.common(dest) - if not base: - return None - # with posix local paths '/' is always a common base - relsource = source.relto(base) +def getrelpath(curdir, dest): + base = curdir.common(dest) + if not base: # can be the case on windows + return dest + curdir2base = curdir.relto(base) reldest = dest.relto(base) - n = relsource.count(source.sep) - target = dest.sep.join(('..', )*n + (reldest, )) + if curdir2base: + n = curdir2base.count(curdir.sep) + 1 + else: + n = 0 + l = ['..'] * n + if reldest: + l.append(reldest) + target = dest.sep.join(l) return target Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Sat Aug 9 00:35:21 2008 @@ -105,3 +105,13 @@ assert repr_pythonversion() == str(x) finally: py.magic.revert(sys, 'version_info') + +def test_getrelpath(): + curdir = py.path.local() + sep = curdir.sep + s = getrelpath(curdir, curdir.join("hello", "world")) + assert s == "hello" + sep + "world" + + s = getrelpath(curdir, curdir.dirpath().join("sister")) + assert s == ".." + sep + "sister" + assert getrelpath(curdir, curdir.dirpath()) == ".." From hpk at codespeak.net Sat Aug 9 12:36:25 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 12:36:25 +0200 (CEST) Subject: [py-svn] r57126 - py/branch/event/py/test2/testing Message-ID: <20080809103625.1693F169FDE@codespeak.net> Author: hpk Date: Sat Aug 9 12:36:24 2008 New Revision: 57126 Modified: py/branch/event/py/test2/testing/test_collect.py Log: avoid using same filename Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Sat Aug 9 12:36:24 2008 @@ -301,7 +301,7 @@ return items, l def test_check_collect_hashes(self): - one = self.tmp.ensure("test_one.py") + one = self.tmp.ensure("test_check_collect_hashes.py") one.write(py.code.Source(""" def test_1(): pass @@ -309,7 +309,7 @@ def test_2(): pass """)) - one.copy(self.tmp.join("test_two.py")) + one.copy(self.tmp.join("test_check_collect_hashes_2.py")) items, events = self._genitems() assert len(items) == 4 for numi, i in enumerate(items): From hpk at codespeak.net Sat Aug 9 12:57:50 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 12:57:50 +0200 (CEST) Subject: [py-svn] r57127 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080809105750.926C416A002@codespeak.net> Author: hpk Date: Sat Aug 9 12:57:48 2008 New Revision: 57127 Modified: py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_masterslave.py Log: implement sendlist, shutdown, remove uneeded tests Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Sat Aug 9 12:57:48 2008 @@ -170,7 +170,7 @@ if host not in pending_going_down: if not host.node.pending: # reschedule tests - host.node.send(None) + host.node.shutdown() pending_going_down.append(host) else: numitems += len(host.node.pending) Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Sat Aug 9 12:57:48 2008 @@ -52,23 +52,26 @@ self.notify(ev) def send(self, item): + assert item is not None self.channel.send(item) - if item is not None: - self.pending.append(item) - self.notify(repevent.SendItem(self.host, item)) + self.pending.append(item) + + def sendlist(self, itemlist): + self.channel.send(itemlist) + self.pending.extend(itemlist) + + def shutdown(self): + self.channel.send(None) # -# config objects need to be exchanged and unified between -# two sides so that collectors and items referencing the -# config will be regarded equal - and also for an item -# to be reconstructed by its names. -# +# a config needs to be available on the other side for options +# and for reconstructing collection trees (topdir, conftest access) # def send_and_receive_pickled_config(channel, config, remote_topdir): channel.send((config, remote_topdir)) backconfig = channel.receive() - assert config is backconfig + assert config is backconfig # ImmutablePickling :) return backconfig def receive_and_send_pickled_config(channel): @@ -102,10 +105,17 @@ nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) while 1: - item = channel.receive() - if item is None: + task = channel.receive() + if task is None: # shutdown channel.send(None) break - runner = item._getrunner() - testrep = runner(item) - channel.send(testrep) + if isinstance(task, list): + for item in task: + runtest(channel, item) + else: + runtest(channel, task) + +def runtest(channel, item): + runner = item._getrunner() + testrep = runner(item) + channel.send(testrep) Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Sat Aug 9 12:57:48 2008 @@ -1,55 +1,10 @@ import py -from py.__.test2.rsession.masterslave import ( - MasterNode, send_and_receive_pickled_config, - receive_and_send_pickled_config -) +from py.__.test2.rsession.masterslave import MasterNode from py.__.test2.rsession.hostmanage import Host from basetest import BasicRsessionTest from py.__.test2 import repevent - -def xxxtest_receive_config(): - from cPickle import dumps, loads - l = [] - class MyChannel: - def receive(self): - return loads(l.pop()) - def send(self, arg): - l.append(dumps(arg)) - - channel = MyChannel() - config = py.test2.config._reparse([]) - topdir = py.test.ensuretemp("test_send_receive_config") - channel.send((config, topdir)) - newconfig = receive_and_send_pickled_config(channel) - assert newconfig.topdir == topdir - backconfig = channel.receive() - assert not backconfig._initialized - -def xxxtest_send_config(): - from cPickle import dumps, loads - l = [] - class MyChannel: - def receive(self): - return loads(l.pop(0)) - def send(self, arg): - l.append(dumps(arg)) - - channel = MyChannel() - config = py.test2.config._reparse([]) - topdir = py.test.ensuretemp("test_send_config") - channel.send(config) - newconfig = send_and_receive_pickled_config(channel, config, topdir) - - assert newconfig.topdir == config.topdir - assert newconfig.args == config.args - #assert newconfig.option == config.option - - backconfig,newtopdir = channel.receive() - assert not backconfig._initialized - assert newtopdir == topdir - class TestMasterSlaveConnection(BasicRsessionTest): def getevent(self, eventtype=repevent.ItemTestReport, timeout=2.0): events = [] @@ -69,16 +24,15 @@ def setup_method(self, method): super(TestMasterSlaveConnection, self).setup_method(method) self.queue = py.std.Queue.Queue() - self.session.bus.subscribe(self.queue.put) self.host = Host("localhost") self.host.initgateway() self.node = MasterNode(self.host, self.session.config, - self.session.bus.notify) + self.queue.put) assert not self.node.channel.isclosed() def teardown_method(self, method): print "at teardown:", self.node.channel - self.session.bus.unsubscribe(self.queue.put) + self.host.gw.exit() def test_crash_invalid_item(self): self.node.send(123) # invalid item @@ -96,7 +50,7 @@ assert str(ev.error).find("TERMINATED") != -1 def test_node_down(self): - self.node.send(None) + self.node.shutdown() event = self.getevent(repevent.HostDown) assert event.host == self.host assert event.pending == [] @@ -122,9 +76,19 @@ #assert event.item == item #assert event.item is not item - def test_send_multiple(self): + def test_send_some(self): for outcome in "passed failed skipped".split(): self.node.send(self.getfunc(outcome)) ev = self.getevent() assert getattr(ev, outcome) assert not self.node.pending + + def test_send_list(self): + l = [] + for outcome in "passed failed skipped".split(): + l.append(self.getfunc(outcome)) + self.node.sendlist(l) + for outcome in "passed failed skipped".split(): + ev = self.getevent() + assert getattr(ev, outcome) + assert not self.node.pending From hpk at codespeak.net Sat Aug 9 13:19:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 13:19:04 +0200 (CEST) Subject: [py-svn] r57128 - in py/branch/event/py/test2: . rep rep/testing Message-ID: <20080809111904.279EC16A003@codespeak.net> Author: hpk Date: Sat Aug 9 13:19:03 2008 New Revision: 57128 Modified: py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/collectonly.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/session.py Log: refactor reporter init Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Sat Aug 9 13:19:03 2008 @@ -2,15 +2,17 @@ import sys class BaseReporter(object): - def __init__(self): + def __init__(self, bus=None): self._passed = [] self._skipped = [] self._failed = [] - - def activate(self, bus): - bus.subscribe(self.processevent) - def deactivate(self, bus): - bus.unsubscribe(self.processevent) + self._bus = bus + if bus: + self._bus.subscribe(self.processevent) + + def deactivate(self): + if self._bus: + self._bus.unsubscribe(self.processevent) def processevent(self, ev): evname = ev.__class__.__name__ Modified: py/branch/event/py/test2/rep/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/rep/collectonly.py Sat Aug 9 13:19:03 2008 @@ -9,8 +9,8 @@ class CollectonlyReporter(BaseReporter): INDENT = " " - def __init__(self, config, out=None): - super(CollectonlyReporter, self).__init__() + def __init__(self, config, out=None, bus=None): + super(CollectonlyReporter, self).__init__(bus=bus) self.config = config if out is None: out = py.std.sys.stdout Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sat Aug 9 13:19:03 2008 @@ -7,8 +7,8 @@ from py.__.test2.rep.base import getrelpath, repr_pythonversion class TerminalReporter(BaseReporter): - def __init__(self, config, file=None): - super(TerminalReporter, self).__init__() + def __init__(self, config, file=None, bus=None): + super(TerminalReporter, self).__init__(bus=bus) self.config = config self.currentfspath = None self.curdir = py.path.local() Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Sat Aug 9 13:19:03 2008 @@ -10,11 +10,10 @@ class TestBaseReporter: def test_activate(self): bus = EventBus() - rep = BaseReporter() - rep.activate(bus) + rep = BaseReporter(bus=bus) assert bus._subscribers assert rep.processevent in bus._subscribers - rep.deactivate(bus) + rep.deactivate() assert not bus._subscribers def test_dispatch_to_matching_method(self): Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Sat Aug 9 13:19:03 2008 @@ -57,8 +57,7 @@ py.test2.skip("nomod") """) stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) - rep.activate(self.session.bus) + rep = CollectonlyReporter(modcol._config, bus=self.session.bus, out=stringio) cols = list(self.session.genitems([modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ @@ -71,8 +70,7 @@ raise ValueError(0) """) stringio = py.std.cStringIO.StringIO() - rep = CollectonlyReporter(modcol._config, out=stringio) - rep.activate(self.session.bus) + rep = CollectonlyReporter(modcol._config, bus=self.session.bus, out=stringio) cols = list(self.session.genitems([modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Sat Aug 9 13:19:03 2008 @@ -47,8 +47,7 @@ import xyz """) stringio = py.std.cStringIO.StringIO() - rep = TerminalReporter(modcol._config, file=stringio) - rep.activate(self.session.bus) + rep = TerminalReporter(modcol._config, bus=self.session.bus, file=stringio) l = list(self.session.genitems([modcol])) assert len(l) == 0 s = popvalue(stringio) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Sat Aug 9 13:19:03 2008 @@ -81,8 +81,7 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.reporter = self.config.getreporter() - self.reporter.activate(self.bus) + self.reporter = self.config.initreporter(self.bus) self.bus.notify(repevent.SessionStart(self)) self._failurelist = [] self.bus.subscribe(self._processfailures) @@ -98,7 +97,7 @@ py.test2.collect.Item._setupstate.teardown_all() # this can raise exceptions! self.bus.notify(repevent.SessionFinish(self)) self.bus.unsubscribe(self._processfailures) - self.reporter.deactivate(self.bus) + self.reporter.deactivate() return self._failurelist def main(self): From hpk at codespeak.net Sat Aug 9 13:28:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 13:28:04 +0200 (CEST) Subject: [py-svn] r57129 - py/branch/event/py/test2 Message-ID: <20080809112804.CCD0D169F56@codespeak.net> Author: hpk Date: Sat Aug 9 13:28:04 2008 New Revision: 57129 Modified: py/branch/event/py/test2/config.py Log: forgot this with the last test Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Sat Aug 9 13:28:04 2008 @@ -143,12 +143,13 @@ except AttributeError: return self._conftest.rget(name, path) - def getreporter(self): + def initreporter(self, bus): if self.option.collectonly: from py.__.test2.rep.collectonly import Reporter else: from py.__.test2.rep.terminal import Reporter - return Reporter(self) + rep = Reporter(self, bus=bus) + return rep def initsession(self): """ return an initialized session object. """ From hpk at codespeak.net Sat Aug 9 13:58:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 13:58:37 +0200 (CEST) Subject: [py-svn] r57130 - in py/branch/event/py/test2: . rep rsession testing Message-ID: <20080809115837.7B3A8169E4A@codespeak.net> Author: hpk Date: Sat Aug 9 13:58:34 2008 New Revision: 57130 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/acceptance_test.py Log: have keyboardinterrupt work Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sat Aug 9 13:58:34 2008 @@ -67,8 +67,11 @@ def rep_SessionFinish(self, ev): self._tw.line("") - self.summary_failures() - self.summary_skips() + if not ev.exitstatus: + self.summary_failures() + self.summary_skips() + elif ev.exitstatus == 2: + self._tw.sep("!", "KEYBOARD INTERRUPT") self.summary_stats() Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sat Aug 9 13:58:34 2008 @@ -29,8 +29,9 @@ self.timestart = time.time() class SessionFinish(BaseEvent): - def __init__(self, session): + def __init__(self, session, exitstatus=0): self.session = session + self.exitstatus = exitstatus self.timeend = time.time() class InternalException(BaseEvent): Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Sat Aug 9 13:58:34 2008 @@ -35,9 +35,9 @@ self.hm = HostManager(self) self.hm.setup_hosts() - def sessionfinishes(self): + def sessionfinishes(self, exitstatus=0): self.hm.wait_for_completion() - super(RSession, self).sessionfinishes() + super(RSession, self).sessionfinishes(exitstatus) def runtest(self, item): # let the host manager distribute tests Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Sat Aug 9 13:58:34 2008 @@ -92,10 +92,9 @@ if self.config.option.exitfirst: self.shouldstop = True - def sessionfinishes(self): + def sessionfinishes(self, exitstatus=0): """ teardown any resources after a test run. """ - py.test2.collect.Item._setupstate.teardown_all() # this can raise exceptions! - self.bus.notify(repevent.SessionFinish(self)) + self.bus.notify(repevent.SessionFinish(self, exitstatus=exitstatus)) self.bus.unsubscribe(self._processfailures) self.reporter.deactivate() return self._failurelist @@ -104,15 +103,21 @@ """ main loop for running tests. """ self.shouldstop = False self.sessionstarts() + exitstatus = 0 try: for item in self.collect(): if self.shouldstop: break if not self.config.option.collectonly: self.runtest(item) - finally: - failures = self.sessionfinishes() - return failures + py.test2.collect.Item._setupstate.teardown_all() + except KeyboardInterrupt: + exitstatus = 2 + except: + self.bus.notify(repevent.InternalError()) + exitstatus = 3 + failures = self.sessionfinishes(exitstatus=exitstatus) + return failures def runpdb(self, excinfo): py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sat Aug 9 13:58:34 2008 @@ -288,6 +288,21 @@ "*1/3 passed + 1 skip*", "*failures: 2*", ]) + + def test_keyboard_interrupt(self): + p1 = self.makepyfile(test_one=""" + import py + def test_fail(): + raise ValueError() + def test_inter(): + raise KeyboardInterrupt() + """) + result = self.runpytest(p1) + assert_lines_contain_lines(result.outlines, [ + #"*test_inter() INTERRUPTED", + "*KEYBOARD INTERRUPT*", + "*0/1 passed*", + ]) def test_looponfailing_looping(self): py.test.skip("thought needed to nicely test --looponfailing") From hpk at codespeak.net Sat Aug 9 15:21:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 15:21:36 +0200 (CEST) Subject: [py-svn] r57131 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080809132136.B9772169E4A@codespeak.net> Author: hpk Date: Sat Aug 9 15:21:35 2008 New Revision: 57131 Added: py/branch/event/py/test2/rsession/async.py py/branch/event/py/test2/rsession/testing/test_async.py Log: start of a nice new full featured AsyncSession with nice unit tests Added: py/branch/event/py/test2/rsession/async.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rsession/async.py Sat Aug 9 15:21:35 2008 @@ -0,0 +1,161 @@ +""" + + EXPERIMENTAL async session (for dist/non-dist unification) + +""" + +import py +from py.__.test2 import repevent +from py.__.test2.eventbus import EventBus +import py.__.test2.custompdb + +# used for genitems() +from py.__.test2.outcome import Exit +Item = (py.test.collect.Item, py.test2.collect.Item) +Collector = (py.test.collect.Collector, py.test2.collect.Collector) +from py.__.test2.runner import basic_run_report, basic_collect_report, skip_report +import Queue + +class AsyncSession(object): + """ + Session drives the collection and running of tests + and generates test events for reporters. + """ + MAXITEMSPERHOST = 10 + def __init__(self, config): + self.config = config + self.bus = EventBus() + self.queue = py.std.Queue.Queue() + self.host2pending = {} + self.hosts = [] + + def fixoptions(self): + """ check, fix and determine conflicting options. """ + option = self.config.option + if option.runbrowser and not option.startserver: + #print "--runbrowser implies --startserver" + option.startserver = True + if self.config.getvalue("dist_boxed") and option.dist: + option.boxed = True + # conflicting options + if option.looponfailing and option.usepdb: + raise ValueError, "--looponfailing together with --pdb not supported." + if option.looponfailing and option.dist: + raise ValueError, "--looponfailing together with --dist not supported." + if option.executable and option.usepdb: + raise ValueError, "--exec together with --pdb not supported." + if option.keyword_oneshot and not option.keyword: + raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" + + def main(self): + self.sessionstarts() + colitems = [self.config.getfsnode(arg) for arg in self.config.args] + exitstatus = self.loop(colitems) + self.sessionfinishes(exitstatus=exitstatus) + + def loop(self, colitems): + exitstatus = 0 + try: + while self.hosts: + if colitems: + self.triggertesting(colitems) + colitems = [] + ev = self.queue.get() + self.bus.notify(ev) + if isinstance(ev, repevent.HostDown): + self.reschedule_hostdown(ev) + elif isinstance(ev, repevent.RescheduleItems): + self.reschedule(ev.colitems) + elif isinstance(ev, repevent.ItemTestReport): + all_completed = self.processresult(ev) + if all_completed: + exit = self.act_on_completed_tests() + if exit is not None: + exitstatus = exit + break + elif isinstance(ev, repevent.CollectionReport): + if ev.passed: + colitems = ev.result + except KeyboardInterrupt: + exitstatus = 2 + except: + self.bus.notify(repevent.InternalException()) + exitstatus = 3 + return exitstatus + + def act_on_completed_tests(self): + self.triggershutdown() # XXX looponfailing + + def triggershutdown(self): + for host in self.hosts: + host.node.shutdown() + + def addhost(self, host): + assert host not in self.host2pending + assert host not in self.hosts + self.host2pending[host] = [] + self.hosts.append(host) + + def removehost(self, host): + self.hosts.remove(host) + return self.host2pending.pop(host) + + def processresult(self, ev): + assert isinstance(ev, repevent.ItemTestReport) + completed = True + toremove = ev.colitem + for host, pending in self.host2pending.items(): + if toremove is not None and toremove in pending: + pending.remove(toremove) + toremove = None + if pending: + completed = False + if toremove is None: + break + return completed + + def triggertesting(self, colitems): + senditems = [] + for next in colitems: + if isinstance(next, Item): + senditems.append(next) + else: + ev = basic_collect_report(next) + self.queue.put(ev) + self.sendtestitems(senditems) + + def reschedule_hostdown(self, ev): + pending = self.removehost(ev.host) + self.reschedule(pending) + + def reschedule(self, items): + self.sendtestitems(items) + + def sendtestitems(self, tosend): + for host, pending in self.host2pending.items(): + if not tosend: + break + room = self.MAXITEMSPERHOST - len(pending) + if room > 0: + sending = tosend[:room] + host.node.sendlist(sending) + pending.extend(sending) + tosend[:] = tosend[room:] # update inplace + if tosend: + # we have some left, give it to the main loop + self.queue.put(repevent.RescheduleItems(tosend)) + + def sessionstarts(self): + """ setup any neccessary resources ahead of the test run. """ + self.reporter = self.config.getreporter() + self.reporter.activate(self.bus) + self.bus.notify(repevent.SessionStart(self)) + self.queue = py.std.Queue.Queue() + self.hm = HostManager(self) + self.hm.setup_hosts(self) + + def sessionfinishes(self): + """ teardown any resources after a test run. """ + self.bus.notify(repevent.SessionFinish(self)) + self.reporter.deactivate(self.bus) + self.hm.teardown_hosts() Added: py/branch/event/py/test2/rsession/testing/test_async.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/rsession/testing/test_async.py Sat Aug 9 15:21:35 2008 @@ -0,0 +1,87 @@ +from py.__.test2.testing.suptest import InlineCollection +from py.__.test2.rsession.async import AsyncSession +from py.__.test2.rsession.hostmanage import Host +from py.__.test2 import repevent +import py + +def run(item): + runner = item._getrunner() + return runner(item) + +class MockNode: + def __init__(self): + self.sent = [] + + def sendlist(self, items): + self.sent.append(items) + +def dumpqueue(queue): + while queue.qsize(): + print queue.get() + +class TestAsyncSession(InlineCollection): + def test_add_remove_host(self): + item = self.getitem("def test_func(): pass") + rep = run(item) + session = AsyncSession(item._config) + host = Host("localhost") + assert not session.hosts + session.addhost(host) + assert len(session.hosts) == 1 + session.removehost(host) + py.test2.raises(Exception, "session.removehost(host)") + + def test_processresult(self): + item = self.getitem("def test_func(): pass") + rep = run(item) + session = AsyncSession(item._config) + host = Host("localhost") + session.addhost(host) + session.host2pending[host].append(item) + all_completed = session.processresult(rep) + assert all_completed + assert not session.host2pending[host] + + def test_triggertesting_collect(self): + modcol = self.getmodulecol(""" + def test_func(): + pass + """) + session = AsyncSession(modcol._config) + session.triggertesting([modcol]) + rep = session.queue.get(block=False) + assert isinstance(rep, repevent.CollectionReport) + assert len(rep.result) == 1 + + def test_triggertesting_item(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) + host1 = Host("localhost") + host1.node = MockNode() + host2 = Host("localhost") + host2.node = MockNode() + session.addhost(host1) + session.addhost(host2) + session.triggertesting([item] * (session.MAXITEMSPERHOST*2 + 1)) + host1_sent = host1.node.sent[0] + host2_sent = host2.node.sent[0] + assert host1_sent == [item] * session.MAXITEMSPERHOST + assert host2_sent == [item] * session.MAXITEMSPERHOST + assert session.host2pending[host1] == host1_sent + assert session.host2pending[host2] == host2_sent + ev = session.queue.get(block=False) + assert isinstance(ev, repevent.RescheduleItems) + assert ev.items == [item] + + def test_reschedule_hostdown(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) + host1 = Host("localhost") + host1.node = MockNode() + session.addhost(host1) + ev = repevent.HostDown(host1, [], None) + session.queue.put(ev) + exitstatus = session.loop([item]) + dumpqueue(session.queue) + py.test.skip("handle all hosts down") + assert exitstatus == 4 From hpk at codespeak.net Sat Aug 9 16:11:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 16:11:04 +0200 (CEST) Subject: [py-svn] r57134 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080809141104.0C80516857D@codespeak.net> Author: hpk Date: Sat Aug 9 16:11:04 2008 New Revision: 57134 Modified: py/branch/event/py/test2/rsession/async.py py/branch/event/py/test2/rsession/testing/test_async.py Log: more tests, refined exitstatus handling Modified: py/branch/event/py/test2/rsession/async.py ============================================================================== --- py/branch/event/py/test2/rsession/async.py (original) +++ py/branch/event/py/test2/rsession/async.py Sat Aug 9 16:11:04 2008 @@ -22,6 +22,12 @@ and generates test events for reporters. """ MAXITEMSPERHOST = 10 + EXIT_OK = 0 + EXIT_TESTSFAILED = 1 + EXIT_INTERRUPTED = 2 + EXIT_INTERNALERROR = 3 + EXIT_NOHOSTS = 4 + def __init__(self, config): self.config = config self.bus = EventBus() @@ -54,33 +60,34 @@ self.sessionfinishes(exitstatus=exitstatus) def loop(self, colitems): - exitstatus = 0 + exitstatus = self.EXIT_OK try: - while self.hosts: + while 1: if colitems: self.triggertesting(colitems) colitems = [] ev = self.queue.get() self.bus.notify(ev) if isinstance(ev, repevent.HostDown): - self.reschedule_hostdown(ev) + self.handle_hostdown(ev) elif isinstance(ev, repevent.RescheduleItems): + if not self.hosts: + exitstatus = self.EXIT_NOHOSTS + break self.reschedule(ev.colitems) elif isinstance(ev, repevent.ItemTestReport): all_completed = self.processresult(ev) if all_completed: - exit = self.act_on_completed_tests() - if exit is not None: - exitstatus = exit - break + self.act_on_completed_tests() + break elif isinstance(ev, repevent.CollectionReport): if ev.passed: - colitems = ev.result + colitems = ev.result except KeyboardInterrupt: - exitstatus = 2 + exitstatus = self.EXIT_INTERRUPTED except: self.bus.notify(repevent.InternalException()) - exitstatus = 3 + exitstatus = self.EXIT_INTERNALERROR return exitstatus def act_on_completed_tests(self): @@ -106,8 +113,10 @@ toremove = ev.colitem for host, pending in self.host2pending.items(): if toremove is not None and toremove in pending: - pending.remove(toremove) - toremove = None + pending.remove(toremove) + if ev.failed and self.config.option.exitfirst: + break + toremove = None if pending: completed = False if toremove is None: @@ -124,7 +133,7 @@ self.queue.put(ev) self.sendtestitems(senditems) - def reschedule_hostdown(self, ev): + def handle_hostdown(self, ev): pending = self.removehost(ev.host) self.reschedule(pending) Modified: py/branch/event/py/test2/rsession/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async.py (original) +++ py/branch/event/py/test2/rsession/testing/test_async.py Sat Aug 9 16:11:04 2008 @@ -1,6 +1,7 @@ from py.__.test2.testing.suptest import InlineCollection from py.__.test2.rsession.async import AsyncSession from py.__.test2.rsession.hostmanage import Host +from py.__.test2.runner import basic_collect_report from py.__.test2 import repevent import py @@ -15,6 +16,9 @@ def sendlist(self, items): self.sent.append(items) + def shutdown(self): + self._shutdown=True + def dumpqueue(queue): while queue.qsize(): print queue.get() @@ -28,7 +32,9 @@ assert not session.hosts session.addhost(host) assert len(session.hosts) == 1 - session.removehost(host) + session.host2pending[host].append(item) + pending = session.removehost(host) + assert pending == [item] py.test2.raises(Exception, "session.removehost(host)") def test_processresult(self): @@ -73,7 +79,23 @@ assert isinstance(ev, repevent.RescheduleItems) assert ev.items == [item] - def test_reschedule_hostdown(self): + def test_keyboardinterrupt(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) + def raise_(): raise KeyboardInterrupt() + session.queue.get = raise_ + exitstatus = session.loop([]) + assert exitstatus == session.EXIT_INTERRUPTED + + def test_internalerror(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) + def raise_(): raise ValueError() + session.queue.get = raise_ + exitstatus = session.loop([]) + assert exitstatus == session.EXIT_INTERNALERROR + + def test_hostdown(self): item = self.getitem("def test_func(): pass") session = AsyncSession(item._config) host1 = Host("localhost") @@ -83,5 +105,40 @@ session.queue.put(ev) exitstatus = session.loop([item]) dumpqueue(session.queue) - py.test.skip("handle all hosts down") - assert exitstatus == 4 + assert exitstatus == session.EXIT_NOHOSTS + + def test_exit_completed_tests(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) + host1 = Host("localhost") + host1.node = MockNode() + session.addhost(host1) + session.triggertesting([item]) + ev = run(item) + session.queue.put(ev) + exitstatus = session.loop([]) + dumpqueue(session.queue) + assert exitstatus == session.EXIT_OK + + def test_exit_on_first_failing(self): + modcol = self.getmodulecol(""" + def test_fail(): + assert 0 + def test_pass(): + pass + """) + modcol._config.option.exitfirst = True + session = AsyncSession(modcol._config) + host1 = Host("localhost") + host1.node = MockNode() + session.addhost(host1) + items = basic_collect_report(modcol).result + session.triggertesting(items) + ev1 = run(items[0]) + ev2 = run(items[1]) + session.queue.put(ev1) + session.queue.put(ev2) + exitstatus = session.loop([]) + assert session.queue.qsize() == 1 + dumpqueue(session.queue) + #assert exitstatus == session.EXIT_TESTSFAILED From hpk at codespeak.net Sat Aug 9 17:12:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 17:12:45 +0200 (CEST) Subject: [py-svn] r57140 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080809151245.3F1B816A02B@codespeak.net> Author: hpk Date: Sat Aug 9 17:12:44 2008 New Revision: 57140 Modified: py/branch/event/py/test2/rsession/async.py py/branch/event/py/test2/rsession/testing/test_async.py Log: exitfirst and more exitcodes Modified: py/branch/event/py/test2/rsession/async.py ============================================================================== --- py/branch/event/py/test2/rsession/async.py (original) +++ py/branch/event/py/test2/rsession/async.py Sat Aug 9 17:12:44 2008 @@ -34,6 +34,7 @@ self.queue = py.std.Queue.Queue() self.host2pending = {} self.hosts = [] + self._testsfailed = False def fixoptions(self): """ check, fix and determine conflicting options. """ @@ -88,6 +89,8 @@ except: self.bus.notify(repevent.InternalException()) exitstatus = self.EXIT_INTERNALERROR + if exitstatus == 0 and self._testsfailed: + exitstatus = self.EXIT_TESTSFAILED return exitstatus def act_on_completed_tests(self): @@ -114,8 +117,10 @@ for host, pending in self.host2pending.items(): if toremove is not None and toremove in pending: pending.remove(toremove) - if ev.failed and self.config.option.exitfirst: - break + if ev.failed: + self._testsfailed = True + if self.config.option.exitfirst: + break toremove = None if pending: completed = False Modified: py/branch/event/py/test2/rsession/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async.py (original) +++ py/branch/event/py/test2/rsession/testing/test_async.py Sat Aug 9 17:12:44 2008 @@ -107,8 +107,7 @@ dumpqueue(session.queue) assert exitstatus == session.EXIT_NOHOSTS - def test_exit_completed_tests(self): - item = self.getitem("def test_func(): pass") + def runthrough(self, item): session = AsyncSession(item._config) host1 = Host("localhost") host1.node = MockNode() @@ -118,8 +117,18 @@ session.queue.put(ev) exitstatus = session.loop([]) dumpqueue(session.queue) + return session, exitstatus + + def test_exit_completed_tests_ok(self): + item = self.getitem("def test_func(): pass") + session, exitstatus = self.runthrough(item) assert exitstatus == session.EXIT_OK + def test_exit_completed_tests_fail(self): + item = self.getitem("def test_func(): 0/0") + session, exitstatus = self.runthrough(item) + assert exitstatus == session.EXIT_TESTSFAILED + def test_exit_on_first_failing(self): modcol = self.getmodulecol(""" def test_fail(): @@ -141,4 +150,4 @@ exitstatus = session.loop([]) assert session.queue.qsize() == 1 dumpqueue(session.queue) - #assert exitstatus == session.EXIT_TESTSFAILED + assert exitstatus == session.EXIT_TESTSFAILED From hpk at codespeak.net Sat Aug 9 17:34:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 17:34:22 +0200 (CEST) Subject: [py-svn] r57142 - py/branch/event/py/test2/rsession/testing Message-ID: <20080809153422.8C715169EA8@codespeak.net> Author: hpk Date: Sat Aug 9 17:34:22 2008 New Revision: 57142 Modified: py/branch/event/py/test2/rsession/testing/test_async.py Log: test event propagation Modified: py/branch/event/py/test2/rsession/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async.py (original) +++ py/branch/event/py/test2/rsession/testing/test_async.py Sat Aug 9 17:34:22 2008 @@ -103,8 +103,11 @@ session.addhost(host1) ev = repevent.HostDown(host1, [], None) session.queue.put(ev) + events = [] + session.bus.subscribe(events.append) exitstatus = session.loop([item]) dumpqueue(session.queue) + assert events[0] == ev # this tests event propagation assert exitstatus == session.EXIT_NOHOSTS def runthrough(self, item): From hpk at codespeak.net Sat Aug 9 17:55:57 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 17:55:57 +0200 (CEST) Subject: [py-svn] r57143 - in py/branch/event/py/test2/rsession: . testing Message-ID: <20080809155557.A1DB816A061@codespeak.net> Author: hpk Date: Sat Aug 9 17:55:56 2008 New Revision: 57143 Modified: py/branch/event/py/test2/rsession/async.py py/branch/event/py/test2/rsession/testing/test_async.py Log: prune deselected Modified: py/branch/event/py/test2/rsession/async.py ============================================================================== --- py/branch/event/py/test2/rsession/async.py (original) +++ py/branch/event/py/test2/rsession/async.py Sat Aug 9 17:55:56 2008 @@ -129,6 +129,7 @@ return completed def triggertesting(self, colitems): + colitems = self.prunedeselected(colitems) senditems = [] for next in colitems: if isinstance(next, Item): @@ -138,6 +139,20 @@ self.queue.put(ev) self.sendtestitems(senditems) + def prunedeselected(self, colitems): + remaining = [] + deselected = [] + keywordexpr = self.config.option.keyword + for colitem in colitems: + if isinstance(colitem, Item): + if colitem._skipbykeyword(keywordexpr): + deselected.append(colitem) + continue + remaining.append(colitem) + if deselected: + self.queue.put(repevent.Deselected(deselected)) + return remaining + def handle_hostdown(self, ev): pending = self.removehost(ev.host) self.reschedule(pending) @@ -158,7 +173,7 @@ if tosend: # we have some left, give it to the main loop self.queue.put(repevent.RescheduleItems(tosend)) - + def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ self.reporter = self.config.getreporter() Modified: py/branch/event/py/test2/rsession/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async.py (original) +++ py/branch/event/py/test2/rsession/testing/test_async.py Sat Aug 9 17:55:56 2008 @@ -154,3 +154,29 @@ assert session.queue.qsize() == 1 dumpqueue(session.queue) assert exitstatus == session.EXIT_TESTSFAILED + + def test_dselected(self): + modcol = self.getmodulecol(""" + def test_fail(): + assert 0 + def test_pass(): + pass + """) + session = AsyncSession(modcol._config) + + modcol._config.option.keyword = modcol.name + dsel = session.prunedeselected([modcol]) + assert dsel == [modcol] + items = [modcol.join(x) for x in modcol.listdir()] + remaining = session.prunedeselected(items) + assert remaining == [] + ev = session.queue.get(block=False) + assert isinstance(ev, repevent.Deselected) + assert ev.items == items + + modcol._config.option.keyword = "test_fail" + remaining = session.prunedeselected(items) + assert remaining == [items[0]] + ev = session.queue.get(block=False) + assert isinstance(ev, repevent.Deselected) + assert ev.items == [items[1]] From hpk at codespeak.net Sat Aug 9 18:32:34 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 18:32:34 +0200 (CEST) Subject: [py-svn] r57144 - in py/branch/event/py/test2: . rep rsession rsession/testing Message-ID: <20080809163234.D214116A049@codespeak.net> Author: hpk Date: Sat Aug 9 18:32:34 2008 New Revision: 57144 Modified: py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/async.py py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/rsession/testing/test_async.py py/branch/event/py/test2/session.py Log: somewhat of a snapshot checkin. async session mostly works (yay), need to fade out rsession now. Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sat Aug 9 18:32:34 2008 @@ -67,7 +67,7 @@ def rep_SessionFinish(self, ev): self._tw.line("") - if not ev.exitstatus: + if ev.exitstatus == 0 or ev.exitstatus == 1: self.summary_failures() self.summary_skips() elif ev.exitstatus == 2: Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sat Aug 9 18:32:34 2008 @@ -49,6 +49,11 @@ self.item = item self.time = timestamp() +class Deselected(BaseEvent): + def __init__(self, items): + self.items = items + + class BaseReport(BaseEvent): failed = passed = skipped = None def __init__(self, colitem, **kwargs): @@ -82,6 +87,10 @@ # Distributed Testing Events # ---------------------------------------------------------------------- +class RescheduleItems(BaseEvent): + def __init__(self, items): + self.items = items + class HostReady(BaseEvent): def __init__(self, host, roots): self.host = host Modified: py/branch/event/py/test2/rsession/async.py ============================================================================== --- py/branch/event/py/test2/rsession/async.py (original) +++ py/branch/event/py/test2/rsession/async.py Sat Aug 9 18:32:34 2008 @@ -8,6 +8,7 @@ from py.__.test2 import repevent from py.__.test2.eventbus import EventBus import py.__.test2.custompdb +from py.__.test2.rsession.hostmanage import HostManager # used for genitems() from py.__.test2.outcome import Exit @@ -55,8 +56,8 @@ raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" def main(self): - self.sessionstarts() colitems = [self.config.getfsnode(arg) for arg in self.config.args] + self.sessionstarts() exitstatus = self.loop(colitems) self.sessionfinishes(exitstatus=exitstatus) @@ -75,7 +76,7 @@ if not self.hosts: exitstatus = self.EXIT_NOHOSTS break - self.reschedule(ev.colitems) + self.reschedule(ev.items) elif isinstance(ev, repevent.ItemTestReport): all_completed = self.processresult(ev) if all_completed: @@ -91,6 +92,8 @@ exitstatus = self.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = self.EXIT_TESTSFAILED + #while self.queue.qsize(): + # print self.queue.get() return exitstatus def act_on_completed_tests(self): @@ -176,15 +179,19 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.reporter = self.config.getreporter() - self.reporter.activate(self.bus) + self.reporter = self.config.initreporter(self.bus) self.bus.notify(repevent.SessionStart(self)) self.queue = py.std.Queue.Queue() self.hm = HostManager(self) - self.hm.setup_hosts(self) + self.hm.setup_hosts(notify=self.queue.put) + for host in self.hm.hosts: + self.addhost(host) - def sessionfinishes(self): + def sessionfinishes(self, exitstatus): """ teardown any resources after a test run. """ - self.bus.notify(repevent.SessionFinish(self)) - self.reporter.deactivate(self.bus) - self.hm.teardown_hosts() + self.bus.notify(repevent.SessionFinish(self, exitstatus=exitstatus)) + self.reporter.deactivate() + #for host in self.hosts: + # host.shutdown() + for host in self.hosts: + host.gw.exit() Modified: py/branch/event/py/test2/rsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/rsession/hostmanage.py Sat Aug 9 18:32:34 2008 @@ -152,7 +152,9 @@ rsync.send(raises=False) self.session.bus.notify(repevent.RsyncFinished()) - def setup_hosts(self): + def setup_hosts(self, notify=None): + if notify is None: + self.notify = self.session.bus.notify self.init_rsync() for host in self.hosts: host.node = MasterNode(host, Modified: py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- py/branch/event/py/test2/rsession/rsession.py (original) +++ py/branch/event/py/test2/rsession/rsession.py Sat Aug 9 18:32:34 2008 @@ -36,7 +36,7 @@ self.hm.setup_hosts() def sessionfinishes(self, exitstatus=0): - self.hm.wait_for_completion() + #self.hm.wait_for_completion() super(RSession, self).sessionfinishes(exitstatus) def runtest(self, item): Modified: py/branch/event/py/test2/rsession/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async.py (original) +++ py/branch/event/py/test2/rsession/testing/test_async.py Sat Aug 9 18:32:34 2008 @@ -95,6 +95,19 @@ exitstatus = session.loop([]) assert exitstatus == session.EXIT_INTERNALERROR + def test_rescheduleevent_and_completion(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) + host1 = Host("localhost") + host1.node = MockNode() + session.addhost(host1) + ev = repevent.RescheduleItems([item]) + session.queue.put(ev) + session.queue.put(run(item)) + exitstatus = session.loop([]) + assert exitstatus == session.EXIT_OK + assert host1.node.sent == [[item]] + def test_hostdown(self): item = self.getitem("def test_func(): pass") session = AsyncSession(item._config) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Sat Aug 9 18:32:34 2008 @@ -114,7 +114,7 @@ except KeyboardInterrupt: exitstatus = 2 except: - self.bus.notify(repevent.InternalError()) + self.bus.notify(repevent.InternalException()) exitstatus = 3 failures = self.sessionfinishes(exitstatus=exitstatus) return failures From hpk at codespeak.net Sat Aug 9 18:57:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 18:57:18 +0200 (CEST) Subject: [py-svn] r57145 - in py/branch/event/py/test2: . rep rep/testing rsession rsession/testing testing Message-ID: <20080809165718.61696169E47@codespeak.net> Author: hpk Date: Sat Aug 9 18:57:15 2008 New Revision: 57145 Added: py/branch/event/py/test2/rsession/testing/test_async_functional.py - copied, changed from r57112, py/branch/event/py/test2/rsession/testing/test_rsession.py Removed: py/branch/event/py/test2/rsession/rsession.py py/branch/event/py/test2/rsession/testing/test_rsession.py Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/rsession/async.py py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/rsession/testing/test_async.py py/branch/event/py/test2/rsession/testing/test_masterslave.py py/branch/event/py/test2/testing/test_config.py Log: * getting rid of "pending" attributes on nodes * various fixes Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Sat Aug 9 18:57:15 2008 @@ -178,7 +178,7 @@ """ return default session name as determined from options. """ name = 'Session' if self.option.dist: - name = 'RSession' + name = 'AsyncSession' else: if self.option.looponfailing or self.option.executable: name = 'RemoteTerminalSession' @@ -217,7 +217,7 @@ Session = 'py.__.test2.session' RemoteTerminalSession = 'py.__.test2.terminal.remote' -RSession = 'py.__.test2.rsession.rsession' +AsyncSession = 'py.__.test2.rsession.async' # # helpers Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sat Aug 9 18:57:15 2008 @@ -40,8 +40,8 @@ def rep_HostDown(self, ev): host = ev.host error = ev.error - if error or ev.pending: - self.write_line("HostDown %s: pending=%r" %(host.hostid, ev.pending)) + if error: + self.write_line("HostDown %s: " %(host.hostid, )) for line in str(error).split("\n"): self.write_line("Error: %s" %(line)) Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Sat Aug 9 18:57:15 2008 @@ -80,7 +80,7 @@ rep.processevent(repevent.HostReady(host1, None)) s = popvalue(stringio) assert s.find("HostReady") != -1 - rep.processevent(repevent.HostDown(host1, [], "myerror")) + rep.processevent(repevent.HostDown(host1, "myerror")) s = popvalue(stringio) assert s.find("HostDown") != -1 assert s.find("myerror") != -1 Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sat Aug 9 18:57:15 2008 @@ -97,9 +97,8 @@ self.roots = roots class HostDown(BaseEvent): - def __init__(self, host, pending, error): + def __init__(self, host, error): self.host = host - self.pending = pending self.error = error class SendItem(BaseEvent): Modified: py/branch/event/py/test2/rsession/async.py ============================================================================== --- py/branch/event/py/test2/rsession/async.py (original) +++ py/branch/event/py/test2/rsession/async.py Sat Aug 9 18:57:15 2008 @@ -86,6 +86,8 @@ if ev.passed: colitems = ev.result except KeyboardInterrupt: + #print py.code.ExceptionInfo().getrepr(funcargs=True) + #raise exitstatus = self.EXIT_INTERRUPTED except: self.bus.notify(repevent.InternalException()) Modified: py/branch/event/py/test2/rsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/rsession/masterslave.py Sat Aug 9 18:57:15 2008 @@ -14,7 +14,6 @@ self.notify = notify self.channel = install_slave(host, config) self.channel.setcallback(self.callback, endmarker=self.ENDMARK) - self.pending = [] self._down = False def callback(self, ev): @@ -31,18 +30,12 @@ if not self._down: if not err: err = "TERMINATED" - self.notify(repevent.HostDown(self.host, self.pending, err)) + self.notify(repevent.HostDown(self.host, err)) return if ev is None: self._down = True - self.notify(repevent.HostDown(self.host, self.pending, None)) + self.notify(repevent.HostDown(self.host, None)) return - if isinstance(ev, repevent.ItemTestReport): - item = self.pending.pop() - else: - import pdb - pdb.post_mortem(ev._excinfo[2]) - raise ValueError("received unexpected object") except KeyboardInterrupt: raise except: @@ -54,11 +47,9 @@ def send(self, item): assert item is not None self.channel.send(item) - self.pending.append(item) def sendlist(self, itemlist): self.channel.send(itemlist) - self.pending.extend(itemlist) def shutdown(self): self.channel.send(None) Deleted: /py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- /py/branch/event/py/test2/rsession/rsession.py Sat Aug 9 18:57:15 2008 +++ (empty file) @@ -1,50 +0,0 @@ -""" - Remote Session Base Class -""" - -import os -import py - -from py.__.test2.session import Session -from py.__.test2.rsession.hostmanage import HostManager - -class RSession(Session): - """ A session that sends tests to remote places. - It reuses the Session's main() loop and overrides - runtest() for implementing the sending. - """ - def fixoptions(self): - super(RSession, self).fixoptions() - option = self.config.option - config = self.config - try: - config.getvalue('dist_hosts') - except KeyError: - print "For running distributed tests you need to specify" - print "dist_hosts in a local conftest.py file, for example:" - print - print " dist_hosts = ['localhost'] * 4 # for 3 processors" - print " dist_hosts = ['you at remote.com', '...'] # for testing on ssh accounts" - print " # with your remote ssh accounts" - print - print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" - raise SystemExit - - def sessionstarts(self): - super(RSession, self).sessionstarts() - self.hm = HostManager(self) - self.hm.setup_hosts() - - def sessionfinishes(self, exitstatus=0): - #self.hm.wait_for_completion() - super(RSession, self).sessionfinishes(exitstatus) - - def runtest(self, item): - # let the host manager distribute tests - sent = self.hm.trysendtest(item) - if not sent and not self.shouldstop: - self.sleepabit() - self.runtest(item) - - def sleepabit(self): - py.std.time.sleep(0.1) Modified: py/branch/event/py/test2/rsession/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async.py (original) +++ py/branch/event/py/test2/rsession/testing/test_async.py Sat Aug 9 18:57:15 2008 @@ -114,7 +114,7 @@ host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) - ev = repevent.HostDown(host1, [], None) + ev = repevent.HostDown(host1, None) session.queue.put(ev) events = [] session.bus.subscribe(events.append) Copied: py/branch/event/py/test2/rsession/testing/test_async_functional.py (from r57112, py/branch/event/py/test2/rsession/testing/test_rsession.py) ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_rsession.py (original) +++ py/branch/event/py/test2/rsession/testing/test_async_functional.py Sat Aug 9 18:57:15 2008 @@ -4,15 +4,14 @@ import py from py.__.test2 import repevent -from py.__.test2.rsession.rsession import RSession +from py.__.test2.rsession.async import AsyncSession from py.__.test2.rsession.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup from py.__.test2.testing import suptest def setup_module(mod): - if py.std.sys.platform == "win32": - py.test.skip("rsession tests disabled for win32") + py.test.skip("XXX") def eventreader(session): @@ -32,7 +31,7 @@ events.append(ev) return readevent -class TestRSessionRemote(DirSetup, BasicRsessionTest): +class TestAsyncFunctional(DirSetup, BasicRsessionTest): def setup_method(self, method): DirSetup.setup_method(self, method) BasicRsessionTest.setup_method(self, method) @@ -51,7 +50,7 @@ assert 0 """)) config = py.test2.config._reparse([self.source.join("sub")]) - rsession = RSession(config) + rsession = AsyncSession(config) readevent = eventreader(rsession) rsession.main() ev = readevent(repevent.ItemTestReport) @@ -61,41 +60,6 @@ ev = readevent(repevent.ItemTestReport) assert ev.failed - def test_example_distribution_minus_x(self): - self.source.ensure("sub", "conftest.py").write(py.code.Source(""" - dist_hosts = ['localhost:%s'] - """ % self.dest)) - self.source.ensure("sub", "__init__.py") - self.source.ensure("sub", "test_one.py").write(py.code.Source(""" - def test_1(): - pass - def test_x(): - import py - py.test2.skip("aaa") - def test_2(): - assert 0 - def test_3(): - import time - time.sleep(0.5) - def test_4(): - assert 0 - """)) - config = py.test2.config._reparse([self.source.join("sub"), '-x', '--dist']) - rsession = RSession(config) - sorter = suptest.events_from_session(rsession) - testevents = sorter.get(repevent.ItemTestReport) - - py.test.skip("XXX need for different way to test -x --dist") - assert len([x for x in testevents if x.passed]) == 2 - assert len([x for x in testevents if x.failed]) == 1 - assert len([x for x in testevents if x.skipped]) == 1 - - #passed, skipped, failed = sorter.listoutcomes() - #assert len(passed) == 1 - #assert len(skipped) == 1 - #assert len(failed) == 1 - #assert failed[0].item.name == "test_2" - def test_distribution_rsync_roots_example(self): destdir = py.test2.ensuretemp("example_dist_destdir") subdir = "sub_example_dist" @@ -124,7 +88,7 @@ config = py.test2.config._reparse([tmpdir.join(subdir)]) assert config.topdir == tmpdir assert not tmpdir.join("__init__.py").check() - rsession = RSession(config) + rsession = AsyncSession(config) sorter = suptest.events_from_session(rsession) testevents = sorter.get(repevent.ItemTestReport) assert len([x for x in testevents if x.passed]) == 2 Modified: py/branch/event/py/test2/rsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/rsession/testing/test_masterslave.py Sat Aug 9 18:57:15 2008 @@ -38,7 +38,6 @@ self.node.send(123) # invalid item ev = self.getevent(repevent.HostDown) assert ev.host == self.host - assert ev.pending == [123] assert str(ev.error).find("AttributeError") != -1 def test_crash_killed(self): @@ -46,14 +45,12 @@ self.node.send(item) # invalid item ev = self.getevent(repevent.HostDown) assert ev.host == self.host - assert ev.pending == [item] assert str(ev.error).find("TERMINATED") != -1 def test_node_down(self): self.node.shutdown() event = self.getevent(repevent.HostDown) assert event.host == self.host - assert event.pending == [] assert not event.error self.node.callback(self.node.ENDMARK) excinfo = py.test.raises(IOError, @@ -71,7 +68,6 @@ self.node.send(self.getfunc("passed")) ev = self.getevent() assert ev.passed - assert not self.node.pending assert ev.colitem == item #assert event.item == item #assert event.item is not item @@ -81,7 +77,6 @@ self.node.send(self.getfunc(outcome)) ev = self.getevent() assert getattr(ev, outcome) - assert not self.node.pending def test_send_list(self): l = [] @@ -91,4 +86,3 @@ for outcome in "passed failed skipped".split(): ev = self.getevent() assert getattr(ev, outcome) - assert not self.node.pending Deleted: /py/branch/event/py/test2/rsession/testing/test_rsession.py ============================================================================== --- /py/branch/event/py/test2/rsession/testing/test_rsession.py Sat Aug 9 18:57:15 2008 +++ (empty file) @@ -1,195 +0,0 @@ - -""" Tests various aspects of rsession, like ssh hosts setup/teardown -""" - -import py -from py.__.test2 import repevent -from py.__.test2.rsession.rsession import RSession -from py.__.test2.rsession.hostmanage import HostManager, Host -from basetest import BasicRsessionTest, DirSetup - -from py.__.test2.testing import suptest - -def setup_module(mod): - if py.std.sys.platform == "win32": - py.test.skip("rsession tests disabled for win32") - - -def eventreader(session): - queue = py.std.Queue.Queue() - session.bus.subscribe(queue.put) - def readevent(eventtype=repevent.ItemTestReport, timeout=2.0): - events = [] - while 1: - try: - ev = queue.get(timeout=timeout) - except py.std.Queue.Empty: - print "seen events", events - raise IOError("did not see %r events" % (eventtype)) - else: - if isinstance(ev, eventtype): - return ev - events.append(ev) - return readevent - -class TestRSessionRemote(DirSetup, BasicRsessionTest): - def setup_method(self, method): - DirSetup.setup_method(self, method) - BasicRsessionTest.setup_method(self, method) - - def test_dist_some_tests(self): - self.source.ensure("conftest.py").write(py.code.Source(""" - dist_hosts = ['localhost'] - """)) - self.source.ensure("sub", "test_one.py").write(py.code.Source(""" - def test_1(): - pass - def test_x(): - import py - py.test2.skip("aaa") - def test_fail(): - assert 0 - """)) - config = py.test2.config._reparse([self.source.join("sub")]) - rsession = RSession(config) - readevent = eventreader(rsession) - rsession.main() - ev = readevent(repevent.ItemTestReport) - assert ev.passed - ev = readevent(repevent.ItemTestReport) - assert ev.skipped - ev = readevent(repevent.ItemTestReport) - assert ev.failed - - def test_example_distribution_minus_x(self): - self.source.ensure("sub", "conftest.py").write(py.code.Source(""" - dist_hosts = ['localhost:%s'] - """ % self.dest)) - self.source.ensure("sub", "__init__.py") - self.source.ensure("sub", "test_one.py").write(py.code.Source(""" - def test_1(): - pass - def test_x(): - import py - py.test2.skip("aaa") - def test_2(): - assert 0 - def test_3(): - import time - time.sleep(0.5) - def test_4(): - assert 0 - """)) - config = py.test2.config._reparse([self.source.join("sub"), '-x', '--dist']) - rsession = RSession(config) - sorter = suptest.events_from_session(rsession) - testevents = sorter.get(repevent.ItemTestReport) - - py.test.skip("XXX need for different way to test -x --dist") - assert len([x for x in testevents if x.passed]) == 2 - assert len([x for x in testevents if x.failed]) == 1 - assert len([x for x in testevents if x.skipped]) == 1 - - #passed, skipped, failed = sorter.listoutcomes() - #assert len(passed) == 1 - #assert len(skipped) == 1 - #assert len(failed) == 1 - #assert failed[0].item.name == "test_2" - - def test_distribution_rsync_roots_example(self): - destdir = py.test2.ensuretemp("example_dist_destdir") - subdir = "sub_example_dist" - tmpdir = self.source - tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" - dist_hosts = ["localhost:%s"] - dist_rsync_roots = ["%s", "../py"] - """ % (destdir, tmpdir.join(subdir), ))) - tmpdir.ensure(subdir, "__init__.py") - tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" - def test_1(): - pass - def test_2(): - assert 0 - def test_3(): - raise ValueError(23) - def test_4(someargs): - pass - def test_5(): - assert __file__ != '%s' - #def test_6(): - # import py - # assert py.__file__ != '%s' - """ % (tmpdir.join(subdir), py.__file__))) - destdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) - config = py.test2.config._reparse([tmpdir.join(subdir)]) - assert config.topdir == tmpdir - assert not tmpdir.join("__init__.py").check() - rsession = RSession(config) - sorter = suptest.events_from_session(rsession) - testevents = sorter.get(repevent.ItemTestReport) - assert len([x for x in testevents if x.passed]) == 2 - assert len([x for x in testevents if x.failed]) == 3 - assert len([x for x in testevents if x.skipped]) == 0 - - def test_setup_teardown_run_ssh(self): - hosts = [Host('localhost:%s' % self.dest)] - - queue = py.std.Queue.Queue() - self.session.bus.subscribe(queue.put) - hm = HostManager(self.session, hosts=hosts) - hm.setup_hosts() - - # actually run some tests - for host in hm.hosts: - node = host.node - node.send(self.getfunc("passed")) - node.send(self.getfunc("failed")) - node.send(self.getfunc("skipped")) - node.send(self.getfunc("print")) - - num_hosts = len(hm.hosts) - events = [] - while len(events) < 4 * num_hosts: - item = queue.get(timeout=1.0) - if isinstance(item, repevent.ItemTestReport): - events.append(item) - print "got all events", events - hm.wait_for_completion() - passed = [ev for ev in events - if ev.passed] - skipped = [ev for ev in events - if ev.skipped] - assert len(passed) == 2 * num_hosts - assert len(skipped) == num_hosts - assert len(events) == 4 * num_hosts - # one of passed for each node has non-empty stdout - #passed_stdout = [i for i in passed if i.outcome.stdout.find('samfing') != -1] - #assert len(passed_stdout) == len(nodes), passed - - def test_nice_level(self): - """ Tests if nice level behaviour is ok - """ - hosts = [Host('localhost:%s' % self.dest)] - tmpdir = self.source - tmpdir.ensure("__init__.py") - tmpdir.ensure("conftest.py").write(py.code.Source(""" - dist_hosts = ['localhost:%s'] - dist_nicelevel = 10 - """ % self.dest)) - tmpdir.ensure("test_one.py").write("""def test_nice(): - import os - assert os.nice(0) == 10 - """) - - config = py.test2.config._reparse([tmpdir]) - rsession = RSession(config) - sorter = suptest.events_from_session(rsession) - testevents = sorter.get(repevent.ItemTestReport) - passevents = [x for x in testevents if x.passed] - assert len(passevents) == 1 - -def test_rsession_no_disthost(): - tmpdir = py.test2.ensuretemp("rsession_no_disthost") - tmpdir.ensure("conftest.py") - config = py.test2.config._reparse([str(tmpdir), '-d']) - py.test2.raises(SystemExit, "config.initsession()") Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Sat Aug 9 18:57:15 2008 @@ -194,7 +194,7 @@ def test_sessionname_dist(self): config = py.test2.config._reparse([self.tmpdir, '--dist']) - assert config._getsessionname() == 'RSession' + assert config._getsessionname() == 'AsyncSession' def test_session_eventlog(self): eventlog = self.tmpdir.join("test_session_eventlog") @@ -213,7 +213,7 @@ for x in 'startserver runbrowser rest'.split(): config = py.test2.config._reparse([self.tmpdir, '--dist', '--%s' % x]) - assert config._getsessionname() == 'RSession' + assert config._getsessionname() == 'AsyncSession' def test_implied_different_sessions(self): config = py.test2.config._reparse([self.tmpdir, '--looponfailing']) @@ -221,7 +221,7 @@ config = py.test2.config._reparse([self.tmpdir, '--exec=x']) assert config._getsessionname() == 'RemoteTerminalSession' config = py.test2.config._reparse([self.tmpdir, '--dist', '--exec=x']) - assert config._getsessionname() == 'RSession' + assert config._getsessionname() == 'AsyncSession' def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" @@ -270,10 +270,6 @@ config.initsession() assert config.option.boxed - def test_dist_session_no_capturedisable(self): - config = py.test2.config._reparse([self.tmpdir, '-d', '-s']) - py.test2.raises(SystemExit, "config.initsession()") - def test_getvalue_pathlist(self): tmpdir = self.tmpdir somepath = tmpdir.join("x", "y", "z") @@ -321,6 +317,7 @@ py.test2.raises((ValueError, SystemExit), """ config.initsession() """) + py.test.skip("check on conflict options") conflict_options = ( '--looponfailing --pdb', '--dist --pdb', From hpk at codespeak.net Sat Aug 9 19:02:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 19:02:17 +0200 (CEST) Subject: [py-svn] r57146 - in py/branch/event/py/test2: . dist dist/testing rep rep/testing rsession terminal testing Message-ID: <20080809170217.5E43516A049@codespeak.net> Author: hpk Date: Sat Aug 9 19:02:16 2008 New Revision: 57146 Added: py/branch/event/py/test2/dist/ - copied from r57093, py/branch/event/py/test2/rsession/ py/branch/event/py/test2/dist/async.py - copied, changed from r57145, py/branch/event/py/test2/rsession/async.py py/branch/event/py/test2/dist/hostmanage.py - copied, changed from r57145, py/branch/event/py/test2/rsession/hostmanage.py py/branch/event/py/test2/dist/masterslave.py - copied, changed from r57145, py/branch/event/py/test2/rsession/masterslave.py py/branch/event/py/test2/dist/mypickle.py - copied unchanged from r57145, py/branch/event/py/test2/rsession/mypickle.py py/branch/event/py/test2/dist/testing/ - copied from r57145, py/branch/event/py/test2/rsession/testing/ Removed: py/branch/event/py/test2/dist/rsession.py py/branch/event/py/test2/rsession/ Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/dist/testing/test_async.py py/branch/event/py/test2/dist/testing/test_async_functional.py py/branch/event/py/test2/dist/testing/test_hostmanage.py py/branch/event/py/test2/dist/testing/test_masterslave.py py/branch/event/py/test2/dist/testing/test_mypickle.py py/branch/event/py/test2/rep/testing/test_rest.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rep/testing/test_web.py py/branch/event/py/test2/rep/testing/test_webjs.py py/branch/event/py/test2/rep/web.py py/branch/event/py/test2/rep/webjs.py py/branch/event/py/test2/terminal/remote.py py/branch/event/py/test2/testing/test_collect.py Log: rename rsession to "dist" for distributed Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Sat Aug 9 19:02:16 2008 @@ -217,7 +217,7 @@ Session = 'py.__.test2.session' RemoteTerminalSession = 'py.__.test2.terminal.remote' -AsyncSession = 'py.__.test2.rsession.async' +AsyncSession = 'py.__.test2.dist.async' # # helpers Copied: py/branch/event/py/test2/dist/async.py (from r57145, py/branch/event/py/test2/rsession/async.py) ============================================================================== --- py/branch/event/py/test2/rsession/async.py (original) +++ py/branch/event/py/test2/dist/async.py Sat Aug 9 19:02:16 2008 @@ -8,7 +8,7 @@ from py.__.test2 import repevent from py.__.test2.eventbus import EventBus import py.__.test2.custompdb -from py.__.test2.rsession.hostmanage import HostManager +from py.__.test2.dist.hostmanage import HostManager # used for genitems() from py.__.test2.outcome import Exit Copied: py/branch/event/py/test2/dist/hostmanage.py (from r57145, py/branch/event/py/test2/rsession/hostmanage.py) ============================================================================== --- py/branch/event/py/test2/rsession/hostmanage.py (original) +++ py/branch/event/py/test2/dist/hostmanage.py Sat Aug 9 19:02:16 2008 @@ -1,6 +1,6 @@ import py import os -from py.__.test2.rsession.masterslave import MasterNode +from py.__.test2.dist.masterslave import MasterNode from py.__.test2 import repevent class Host(object): Copied: py/branch/event/py/test2/dist/masterslave.py (from r57145, py/branch/event/py/test2/rsession/masterslave.py) ============================================================================== --- py/branch/event/py/test2/rsession/masterslave.py (original) +++ py/branch/event/py/test2/dist/masterslave.py Sat Aug 9 19:02:16 2008 @@ -3,7 +3,7 @@ """ import py from py.__.test2 import repevent -from py.__.test2.rsession.mypickle import PickleChannel +from py.__.test2.dist.mypickle import PickleChannel class MasterNode(object): ENDMARK = -1 @@ -74,9 +74,9 @@ # setting up slave code def install_slave(host, config): channel = host.gw.remote_exec(source=""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession import masterslave + from py.__.test2.dist import masterslave config = masterslave.receive_and_send_pickled_config(channel) masterslave.setup_at_slave_side(channel, config) """) Deleted: /py/branch/event/py/test2/rsession/rsession.py ============================================================================== --- /py/branch/event/py/test2/rsession/rsession.py Sat Aug 9 19:02:16 2008 +++ (empty file) @@ -1,53 +0,0 @@ -""" - Remote Session Base Class -""" - -import os -import py - -from py.__.test2.session import Session -from py.__.test2.rsession.hostmanage import HostManager - -class RSession(Session): - """ A session that sends tests to remote places. - It reuses the Session's main() loop and overrides - runtest() for implementing the sending. - """ - def fixoptions(self): - super(RSession, self).fixoptions() - option = self.config.option - if option.nocapture: - print "Cannot use nocapture with distributed testing" - py.std.sys.exit(1) - config = self.config - try: - config.getvalue('dist_hosts') - except KeyError: - print "For running distributed tests you need to specify" - print "dist_hosts in a local conftest.py file, for example:" - print - print " dist_hosts = ['localhost'] * 4 # for 3 processors" - print " dist_hosts = ['you at remote.com', '...'] # for testing on ssh accounts" - print " # with your remote ssh accounts" - print - print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" - raise SystemExit - - def sessionstarts(self): - super(RSession, self).sessionstarts() - self.hm = HostManager(self) - self.hm.setup_hosts() - - def sessionfinishes(self): - self.hm.wait_for_completion() - super(RSession, self).sessionfinishes() - - def runtest(self, item): - # let the host manager distribute tests - sent = self.hm.trysendtest(item) - if not sent and not self.shouldstop: - self.sleepabit() - self.runtest(item) - - def sleepabit(self): - py.std.time.sleep(0.1) Modified: py/branch/event/py/test2/dist/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async.py (original) +++ py/branch/event/py/test2/dist/testing/test_async.py Sat Aug 9 19:02:16 2008 @@ -1,6 +1,6 @@ from py.__.test2.testing.suptest import InlineCollection -from py.__.test2.rsession.async import AsyncSession -from py.__.test2.rsession.hostmanage import Host +from py.__.test2.dist.async import AsyncSession +from py.__.test2.dist.hostmanage import Host from py.__.test2.runner import basic_collect_report from py.__.test2 import repevent import py Modified: py/branch/event/py/test2/dist/testing/test_async_functional.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_async_functional.py (original) +++ py/branch/event/py/test2/dist/testing/test_async_functional.py Sat Aug 9 19:02:16 2008 @@ -1,11 +1,11 @@ -""" Tests various aspects of rsession, like ssh hosts setup/teardown +""" Tests various aspects of dist, like ssh hosts setup/teardown """ import py from py.__.test2 import repevent -from py.__.test2.rsession.async import AsyncSession -from py.__.test2.rsession.hostmanage import HostManager, Host +from py.__.test2.dist.async import AsyncSession +from py.__.test2.dist.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup from py.__.test2.testing import suptest @@ -50,9 +50,9 @@ assert 0 """)) config = py.test2.config._reparse([self.source.join("sub")]) - rsession = AsyncSession(config) - readevent = eventreader(rsession) - rsession.main() + dist = AsyncSession(config) + readevent = eventreader(dist) + dist.main() ev = readevent(repevent.ItemTestReport) assert ev.passed ev = readevent(repevent.ItemTestReport) @@ -88,8 +88,8 @@ config = py.test2.config._reparse([tmpdir.join(subdir)]) assert config.topdir == tmpdir assert not tmpdir.join("__init__.py").check() - rsession = AsyncSession(config) - sorter = suptest.events_from_session(rsession) + dist = AsyncSession(config) + sorter = suptest.events_from_session(dist) testevents = sorter.get(repevent.ItemTestReport) assert len([x for x in testevents if x.passed]) == 2 assert len([x for x in testevents if x.failed]) == 3 @@ -146,14 +146,14 @@ """) config = py.test2.config._reparse([tmpdir]) - rsession = RSession(config) - sorter = suptest.events_from_session(rsession) + dist = RSession(config) + sorter = suptest.events_from_session(dist) testevents = sorter.get(repevent.ItemTestReport) passevents = [x for x in testevents if x.passed] assert len(passevents) == 1 -def test_rsession_no_disthost(): - tmpdir = py.test2.ensuretemp("rsession_no_disthost") +def test_dist_no_disthost(): + tmpdir = py.test2.ensuretemp("dist_no_disthost") tmpdir.ensure("conftest.py") config = py.test2.config._reparse([str(tmpdir), '-d']) py.test2.raises(SystemExit, "config.initsession()") Modified: py/branch/event/py/test2/dist/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/dist/testing/test_hostmanage.py Sat Aug 9 19:02:16 2008 @@ -4,8 +4,8 @@ import py from basetest import DirSetup -from py.__.test2.rsession.hostmanage import HostRSync, Host, HostManager -from py.__.test2.rsession.hostmanage import sethomedir, gethomedir, getpath_relto_home +from py.__.test2.dist.hostmanage import HostRSync, Host, HostManager +from py.__.test2.dist.hostmanage import sethomedir, gethomedir, getpath_relto_home from py.__.test2 import repevent class TestHost(DirSetup): Modified: py/branch/event/py/test2/dist/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/dist/testing/test_masterslave.py Sat Aug 9 19:02:16 2008 @@ -1,7 +1,7 @@ import py -from py.__.test2.rsession.masterslave import MasterNode -from py.__.test2.rsession.hostmanage import Host +from py.__.test2.dist.masterslave import MasterNode +from py.__.test2.dist.hostmanage import Host from basetest import BasicRsessionTest from py.__.test2 import repevent Modified: py/branch/event/py/test2/dist/testing/test_mypickle.py ============================================================================== --- py/branch/event/py/test2/rsession/testing/test_mypickle.py (original) +++ py/branch/event/py/test2/dist/testing/test_mypickle.py Sat Aug 9 19:02:16 2008 @@ -1,6 +1,6 @@ import py -from py.__.test2.rsession.mypickle import ImmutablePickler, PickleChannel, UnpickleError +from py.__.test2.dist.mypickle import ImmutablePickler, PickleChannel, UnpickleError class A: pass @@ -68,9 +68,9 @@ def test_popen_send_instance(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_mypickle import A + from py.__.test2.dist.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -87,9 +87,9 @@ def test_send_concurrent(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_mypickle import A + from py.__.test2.dist.testing.test_mypickle import A l = [A() for i in range(10)] channel.send(l) other_l = channel.receive() @@ -112,9 +112,9 @@ def test_popen_with_callback(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_mypickle import A + from py.__.test2.dist.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -133,9 +133,9 @@ def test_popen_with_callback_with_endmarker(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_mypickle import A + from py.__.test2.dist.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -157,9 +157,9 @@ def test_popen_with_callback_with_endmarker_and_unpickling_error(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.rsession.testing.test_mypickle import A + from py.__.test2.dist.testing.test_mypickle import A a1 = A() channel.receive() channel.send(a1) @@ -176,7 +176,7 @@ def test_popen_with_newchannel(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) newchannel = channel.receive() newchannel.send(42) @@ -190,7 +190,7 @@ def test_popen_with_various_methods(self): channel = self.gw.remote_exec(""" - from py.__.test2.rsession.mypickle import PickleChannel + from py.__.test2.dist.mypickle import PickleChannel channel = PickleChannel(channel) channel.receive() """) Modified: py/branch/event/py/test2/rep/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_rest.py (original) +++ py/branch/event/py/test2/rep/testing/test_rest.py Sat Aug 9 19:02:16 2008 @@ -9,9 +9,9 @@ from py.__.test2.testing.test_reporter import AbstractTestReporter,\ DummyChannel from py.__.test2 import repevent -from py.__.test2.rsession.rest import RestReporter, NoLinkWriter +from py.__.test2.dist.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * -from py.__.test2.rsession.hostmanage import Host +from py.__.test2.dist.hostmanage import Host from py.__.test2.outcome import SerializableOutcome class Container(object): Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Sat Aug 9 19:02:16 2008 @@ -4,7 +4,7 @@ #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report from test_collectonly import InlineCollect, popvalue, assert_stringio_contains_lines -from py.__.test2.rsession.hostmanage import Host +from py.__.test2.dist.hostmanage import Host class TestTerminal(InlineCollect): def test_session_reporter_subscription(self): Modified: py/branch/event/py/test2/rep/testing/test_web.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_web.py (original) +++ py/branch/event/py/test2/rep/testing/test_web.py Sat Aug 9 19:02:16 2008 @@ -15,14 +15,14 @@ mod.commproxy.USE_MOCHIKIT = False mod.rpython2javascript = rpython2javascript mod.commproxy = mod.commproxy - from py.__.test2.rsession.web import TestHandler as _TestHandler - from py.__.test2.rsession.web import MultiQueue + from py.__.test2.dist.web import TestHandler as _TestHandler + from py.__.test2.dist.web import MultiQueue mod._TestHandler = _TestHandler mod.MultiQueue = MultiQueue def test_js_generate(): - from py.__.test2.rsession import webjs - from py.__.test2.rsession.web import FUNCTION_LIST, IMPORTED_PYPY + from py.__.test2.dist import webjs + from py.__.test2.dist.web import FUNCTION_LIST, IMPORTED_PYPY source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False) assert source Modified: py/branch/event/py/test2/rep/testing/test_webjs.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_webjs.py (original) +++ py/branch/event/py/test2/rep/testing/test_webjs.py Sat Aug 9 19:02:16 2008 @@ -12,8 +12,8 @@ mod.dom = dom mod.schedule_callbacks = schedule_callbacks - from py.__.test2.rsession import webjs - from py.__.test2.rsession.web import exported_methods + from py.__.test2.dist import webjs + from py.__.test2.dist.web import exported_methods mod.webjs = webjs mod.exported_methods = exported_methods mod.here = py.magic.autopath().dirpath() @@ -28,8 +28,8 @@ mod.dom = dom dom.window = dom.Window(html) dom.document = dom.window.document - from py.__.test2.rsession import webjs - from py.__.test2.rsession.web import exported_methods + from py.__.test2.dist import webjs + from py.__.test2.dist.web import exported_methods mod.webjs = webjs mod.exported_methods = exported_methods Modified: py/branch/event/py/test2/rep/web.py ============================================================================== --- py/branch/event/py/test2/rep/web.py (original) +++ py/branch/event/py/test2/rep/web.py Sat Aug 9 19:02:16 2008 @@ -14,10 +14,10 @@ import socket import py -from py.__.test2.rsession.rsession import RSession +from py.__.test2.dist.dist import RSession from py.__.test2 import repevent from py.__.test2 import collect -from py.__.test2.rsession.webdata import json +from py.__.test2.dist.webdata import json DATADIR = py.path.local(__file__).dirpath("webdata") FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", @@ -59,7 +59,7 @@ return d class MultiQueue(object): - """ a tailor-made queue (internally using Queue) for py.test2.rsession.web + """ a tailor-made queue (internally using Queue) for py.test2.dist.web API-wise the main difference is that the get() method gets a sessid argument, which is used to determine what data to feed to the client @@ -406,7 +406,7 @@ web_name = py.path.local(__file__).dirpath().join("webjs.py") if IMPORTED_PYPY and web_name.mtime() > js_name.mtime() or \ (not js_name.check()): - from py.__.test2.rsession import webjs + from py.__.test2.dist import webjs javascript_source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False) Modified: py/branch/event/py/test2/rep/webjs.py ============================================================================== --- py/branch/event/py/test2/rep/webjs.py (original) +++ py/branch/event/py/test2/rep/webjs.py Sat Aug 9 19:02:16 2008 @@ -3,7 +3,7 @@ """ import py -from py.__.test2.rsession.web import exported_methods +from py.__.test2.dist.web import exported_methods try: from pypy.translator.js.modules import dom from pypy.translator.js.helper import __show_traceback Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Sat Aug 9 19:02:16 2008 @@ -96,12 +96,12 @@ return py.execnet.PopenGateway(self.executable), topdir def run_remote_session(self, failures): - from py.__.test2.rsession import masterslave + from py.__.test2.dist import masterslave gw, topdir = self._initslavegateway() channel = gw.remote_exec(_pickle=True, source=""" print "SLAVE: initializing ..." from py.__.test2.terminal.remote import slaverun_TerminalSession - from py.__.test2.rsession import masterslave + from py.__.test2.dist import masterslave config = masterslave.receive_and_send_pickled_config(channel) slaverun_TerminalSession(channel, config) """, stdout=self.out, stderr=self.out) Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Sat Aug 9 19:02:16 2008 @@ -483,7 +483,7 @@ "col3._totrail()") -from py.__.test2.rsession.mypickle import ImmutablePickler +from py.__.test2.dist.mypickle import ImmutablePickler class PickleTransport: def __init__(self): self.p1 = ImmutablePickler(uneven=0) From hpk at codespeak.net Sat Aug 9 19:13:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 19:13:28 +0200 (CEST) Subject: [py-svn] r57147 - py/branch/event/py/test2/dist/testing Message-ID: <20080809171328.617BF16A02B@codespeak.net> Author: hpk Date: Sat Aug 9 19:13:27 2008 New Revision: 57147 Modified: py/branch/event/py/test2/dist/testing/test_mypickle.py Log: avoid random timing problem in test Modified: py/branch/event/py/test2/dist/testing/test_mypickle.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_mypickle.py (original) +++ py/branch/event/py/test2/dist/testing/test_mypickle.py Sat Aug 9 19:13:27 2008 @@ -94,6 +94,7 @@ channel.send(l) other_l = channel.receive() channel.send((l, other_l)) + channel.receive() """) channel = PickleChannel(channel) l = [A() for i in range(10)] @@ -103,6 +104,7 @@ ret = channel.receive() assert ret[0] is other_l assert ret[1] is l + channel.send(None) #s1 = p1.dumps(a1) #s2 = p2.dumps(a2) From hpk at codespeak.net Sat Aug 9 20:20:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 20:20:29 +0200 (CEST) Subject: [py-svn] r57149 - in py/branch/event/py/test2/dist: . testing Message-ID: <20080809182029.844EC169E3B@codespeak.net> Author: hpk Date: Sat Aug 9 20:20:29 2008 New Revision: 57149 Modified: py/branch/event/py/test2/dist/hostmanage.py py/branch/event/py/test2/dist/testing/test_hostmanage.py Log: fix and test setting of notification hook. Modified: py/branch/event/py/test2/dist/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dist/hostmanage.py (original) +++ py/branch/event/py/test2/dist/hostmanage.py Sat Aug 9 20:20:29 2008 @@ -154,12 +154,12 @@ def setup_hosts(self, notify=None): if notify is None: - self.notify = self.session.bus.notify + notify = self.session.bus.notify self.init_rsync() for host in self.hosts: host.node = MasterNode(host, self.session.config, - self.session.bus.notify) + notify) def wait_for_completion(self, maxtimeout=2.0): self.queue = py.std.Queue.Queue() Modified: py/branch/event/py/test2/dist/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/dist/testing/test_hostmanage.py Sat Aug 9 20:20:29 2008 @@ -256,6 +256,18 @@ assert host.gw_remotepath is None assert not host.relpath + def test_hostmanage_setup_hosts(self): + hosts = [Host("localhost") for i in range(3)] + session = py.test2.config._reparse([self.source]).initsession() + hm = HostManager(session, hosts=hosts) + queue = py.std.Queue.Queue() + hm.setup_hosts(notify=queue.put) + for host in hm.hosts: + host.node.shutdown() + for host in hm.hosts: + event = queue.get(timeout=2.0) + assert isinstance(event, repevent.HostDown) + def XXXtest_ssh_rsync_samehost_twice(self): #XXX we have no easy way to have a temp directory remotely! option = py.test2.config.option From hpk at codespeak.net Sat Aug 9 20:22:27 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 20:22:27 +0200 (CEST) Subject: [py-svn] r57150 - in py/branch/event/py/test2: . dist dist/testing Message-ID: <20080809182227.003E3169F04@codespeak.net> Author: hpk Date: Sat Aug 9 20:22:27 2008 New Revision: 57150 Modified: py/branch/event/py/test2/dist/async.py py/branch/event/py/test2/dist/testing/test_async.py py/branch/event/py/test2/repevent.py Log: fix busy waiting issue (hopefully) Modified: py/branch/event/py/test2/dist/async.py ============================================================================== --- py/branch/event/py/test2/dist/async.py (original) +++ py/branch/event/py/test2/dist/async.py Sat Aug 9 20:22:27 2008 @@ -64,11 +64,13 @@ def loop(self, colitems): exitstatus = self.EXIT_OK try: + wait_for_event = False while 1: - if colitems: - self.triggertesting(colitems) + if not wait_for_event and colitems: + self.triggertesting(colitems) colitems = [] ev = self.queue.get() + wait_for_event = False self.bus.notify(ev) if isinstance(ev, repevent.HostDown): self.handle_hostdown(ev) @@ -76,7 +78,8 @@ if not self.hosts: exitstatus = self.EXIT_NOHOSTS break - self.reschedule(ev.items) + colitems.extend(ev.items) + wait_for_event = True elif isinstance(ev, repevent.ItemTestReport): all_completed = self.processresult(ev) if all_completed: @@ -84,12 +87,13 @@ break elif isinstance(ev, repevent.CollectionReport): if ev.passed: - colitems = ev.result + colitems.extend(ev.result) except KeyboardInterrupt: #print py.code.ExceptionInfo().getrepr(funcargs=True) #raise exitstatus = self.EXIT_INTERRUPTED except: + #raise self.bus.notify(repevent.InternalException()) exitstatus = self.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: @@ -160,10 +164,7 @@ def handle_hostdown(self, ev): pending = self.removehost(ev.host) - self.reschedule(pending) - - def reschedule(self, items): - self.sendtestitems(items) + self.queue.put(repevent.RescheduleItems(pending)) def sendtestitems(self, tosend): for host, pending in self.host2pending.items(): Modified: py/branch/event/py/test2/dist/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async.py (original) +++ py/branch/event/py/test2/dist/testing/test_async.py Sat Aug 9 20:22:27 2008 @@ -103,8 +103,10 @@ session.addhost(host1) ev = repevent.RescheduleItems([item]) session.queue.put(ev) + session.queue.put(repevent.NOP()) session.queue.put(run(item)) exitstatus = session.loop([]) + dumpqueue(session.queue) assert exitstatus == session.EXIT_OK assert host1.node.sent == [[item]] @@ -158,14 +160,24 @@ host1.node = MockNode() session.addhost(host1) items = basic_collect_report(modcol).result + + # trigger testing - this sends tests to host1 session.triggertesting(items) + + # run tests ourselves and produce reports ev1 = run(items[0]) ev2 = run(items[1]) session.queue.put(ev1) session.queue.put(ev2) + + # now call the loop exitstatus = session.loop([]) + + # and see that it only looked for one result assert session.queue.qsize() == 1 dumpqueue(session.queue) + + # and exits properly assert exitstatus == session.EXIT_TESTSFAILED def test_dselected(self): @@ -193,3 +205,36 @@ ev = session.queue.get(block=False) assert isinstance(ev, repevent.Deselected) assert ev.items == [items[1]] + + + def test_no_busy_reschedule_items(self): + item = self.getitem("def test_func(): 0/0") + session = AsyncSession(item._config) + + # set up a host that is full of pending items + host = Host("localhost") + host.node = MockNode() + session.addhost(host) + session.host2pending[host].extend([item] * session.MAXITEMSPERHOST) + + # check that the loop does not busy wait + # i.e. puts a RescheduleEvent into the queue + # just after having read one and finding no + # place to distribute tests to + events = [repevent.RescheduleItems([item])] + def mocked_queue_get(): + return events.pop() + oldget = session.queue.get + session.queue.get = mocked_queue_get + exitstatus = session.loop([]) + assert not session.queue.qsize(), oldget() + + def test_initial_reschedule(self): + item = self.getitem("def test_func(): 0/0") + session = AsyncSession(item._config) + + # set up a host that is full of pending items + host = Host("localhost") + host.node = MockNode() + session.addhost(host) + session.host2pending[host].extend([item] * session.MAXITEMSPERHOST) Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sat Aug 9 20:22:27 2008 @@ -19,6 +19,9 @@ def timestamp(): return time.time() +class NOP(BaseEvent): + pass + # ---------------------------------------------------------------------- # Basic Live Reporting Events # ---------------------------------------------------------------------- From hpk at codespeak.net Sat Aug 9 20:38:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 20:38:26 +0200 (CEST) Subject: [py-svn] r57151 - in py/branch/event/py/test2: dist/testing rep Message-ID: <20080809183826.3E1B416A023@codespeak.net> Author: hpk Date: Sat Aug 9 20:38:25 2008 New Revision: 57151 Modified: py/branch/event/py/test2/dist/testing/test_async.py py/branch/event/py/test2/rep/terminal.py Log: split tests Modified: py/branch/event/py/test2/dist/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async.py (original) +++ py/branch/event/py/test2/dist/testing/test_async.py Sat Aug 9 20:38:25 2008 @@ -110,20 +110,32 @@ assert exitstatus == session.EXIT_OK assert host1.node.sent == [[item]] - def test_hostdown(self): + def test_no_hosts_remaining_for_tests(self): item = self.getitem("def test_func(): pass") + # setup a session with one host session = AsyncSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) + + # setup a HostDown event ev = repevent.HostDown(host1, None) session.queue.put(ev) + exitstatus = session.loop([item]) + dumpqueue(session.queue) + assert exitstatus == session.EXIT_NOHOSTS + + def test_event_propagation(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) events = [] session.bus.subscribe(events.append) + ev = repevent.HostDown(Host("localhost"), None) + session.queue.put(ev) exitstatus = session.loop([item]) - dumpqueue(session.queue) assert events[0] == ev # this tests event propagation - assert exitstatus == session.EXIT_NOHOSTS + # the host that we signalled wasn't added + assert exitstatus == session.EXIT_INTERNALERROR def runthrough(self, item): session = AsyncSession(item._config) Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sat Aug 9 20:38:25 2008 @@ -41,9 +41,7 @@ host = ev.host error = ev.error if error: - self.write_line("HostDown %s: " %(host.hostid, )) - for line in str(error).split("\n"): - self.write_line("Error: %s" %(line)) + self.write_line("HostDown %s: %s" %(host.hostid, error)) def rep_ItemTestReport(self, ev): super(TerminalReporter, self).rep_ItemTestReport(ev) From hpk at codespeak.net Sat Aug 9 21:18:33 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 9 Aug 2008 21:18:33 +0200 (CEST) Subject: [py-svn] r57152 - in py/branch/event/py/test2: dist dist/testing testing Message-ID: <20080809191833.14AD116A015@codespeak.net> Author: hpk Date: Sat Aug 9 21:18:31 2008 New Revision: 57152 Modified: py/branch/event/py/test2/dist/async.py py/branch/event/py/test2/dist/testing/test_async.py py/branch/event/py/test2/testing/acceptance_test.py Log: fix hostdown/reschedule issues factor out a "loop_once" method also to ease testing Modified: py/branch/event/py/test2/dist/async.py ============================================================================== --- py/branch/event/py/test2/dist/async.py (original) +++ py/branch/event/py/test2/dist/async.py Sat Aug 9 21:18:31 2008 @@ -17,6 +17,15 @@ from py.__.test2.runner import basic_run_report, basic_collect_report, skip_report import Queue +class LoopState(object): + def __init__(self, colitems, dowork=True): + self.colitems = colitems + self.exitstatus = None + # loopstate.dowork is False after reschedule events + # because otherwise we might very busily loop + # waiting for a host to become ready. + self.dowork = dowork + class AsyncSession(object): """ Session drives the collection and running of tests @@ -61,33 +70,41 @@ exitstatus = self.loop(colitems) self.sessionfinishes(exitstatus=exitstatus) + def loop_once(self, loopstate): + colitems = loopstate.colitems + if loopstate.dowork and loopstate.colitems: + self.triggertesting(colitems) + colitems[:] = [] + ev = self.queue.get() + loopstate.dowork = True + self.bus.notify(ev) + if isinstance(ev, repevent.HostDown): + pending = self.removehost(ev.host) + colitems.extend(pending) + elif isinstance(ev, repevent.RescheduleItems): + if not self.hosts: + loopstate.exitstatus = self.EXIT_NOHOSTS + return + colitems.extend(ev.items) + loopstate.dowork = False + elif isinstance(ev, repevent.ItemTestReport): + all_completed = self.processresult(ev) + if all_completed: + self.act_on_completed_tests() + loopstate.exitstatus = self.EXIT_OK + return + elif isinstance(ev, repevent.CollectionReport): + if ev.passed: + colitems.extend(ev.result) + def loop(self, colitems): - exitstatus = self.EXIT_OK try: - wait_for_event = False + loopstate = LoopState(colitems) while 1: - if not wait_for_event and colitems: - self.triggertesting(colitems) - colitems = [] - ev = self.queue.get() - wait_for_event = False - self.bus.notify(ev) - if isinstance(ev, repevent.HostDown): - self.handle_hostdown(ev) - elif isinstance(ev, repevent.RescheduleItems): - if not self.hosts: - exitstatus = self.EXIT_NOHOSTS - break - colitems.extend(ev.items) - wait_for_event = True - elif isinstance(ev, repevent.ItemTestReport): - all_completed = self.processresult(ev) - if all_completed: - self.act_on_completed_tests() - break - elif isinstance(ev, repevent.CollectionReport): - if ev.passed: - colitems.extend(ev.result) + self.loop_once(loopstate) + if loopstate.exitstatus is not None: + exitstatus = loopstate.exitstatus + break except KeyboardInterrupt: #print py.code.ExceptionInfo().getrepr(funcargs=True) #raise @@ -162,10 +179,6 @@ self.queue.put(repevent.Deselected(deselected)) return remaining - def handle_hostdown(self, ev): - pending = self.removehost(ev.host) - self.queue.put(repevent.RescheduleItems(pending)) - def sendtestitems(self, tosend): for host, pending in self.host2pending.items(): if not tosend: Modified: py/branch/event/py/test2/dist/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async.py (original) +++ py/branch/event/py/test2/dist/testing/test_async.py Sat Aug 9 21:18:31 2008 @@ -1,5 +1,5 @@ from py.__.test2.testing.suptest import InlineCollection -from py.__.test2.dist.async import AsyncSession +from py.__.test2.dist.async import AsyncSession, LoopState from py.__.test2.dist.hostmanage import Host from py.__.test2.runner import basic_collect_report from py.__.test2 import repevent @@ -121,10 +121,35 @@ # setup a HostDown event ev = repevent.HostDown(host1, None) session.queue.put(ev) + + # call the loop which should come back + # because it doesn't have any hosts left exitstatus = session.loop([item]) dumpqueue(session.queue) assert exitstatus == session.EXIT_NOHOSTS + def test_hostdown_causes_reschedule_pending(self): + item = self.getitem("def test_func(): pass") + + # setup a session with two hosts + session = AsyncSession(item._config) + host1 = Host("localhost") + host1.node = MockNode() + session.addhost(host1) + host2 = Host("localhost") + host2.node = MockNode() + session.addhost(host2) + + # have one test pending for a host that goes down + session.host2pending[host1].append(item) + ev = repevent.HostDown(host1, None) + session.queue.put(ev) + + loopstate = LoopState([]) + session.loop_once(loopstate) + + assert loopstate.colitems == [item] + def test_event_propagation(self): item = self.getitem("def test_func(): pass") session = AsyncSession(item._config) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sat Aug 9 21:18:31 2008 @@ -283,8 +283,7 @@ "HostReady*localhost*", "HostReady*localhost*", "HostReady*localhost*", - "HostDown*localhost*", - "Error: TERMINATED", + "HostDown*localhost*TERMINATED*", "*1/3 passed + 1 skip*", "*failures: 2*", ]) From hpk at codespeak.net Sun Aug 10 13:33:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 13:33:30 +0200 (CEST) Subject: [py-svn] r57159 - py/branch/event/py/test2/dist Message-ID: <20080810113330.F059D16A0FE@codespeak.net> Author: hpk Date: Sun Aug 10 13:33:30 2008 New Revision: 57159 Modified: py/branch/event/py/test2/dist/async.py Log: remove one method, small optimization Modified: py/branch/event/py/test2/dist/async.py ============================================================================== --- py/branch/event/py/test2/dist/async.py (original) +++ py/branch/event/py/test2/dist/async.py Sun Aug 10 13:33:30 2008 @@ -90,7 +90,7 @@ elif isinstance(ev, repevent.ItemTestReport): all_completed = self.processresult(ev) if all_completed: - self.act_on_completed_tests() + self.triggershutdown() loopstate.exitstatus = self.EXIT_OK return elif isinstance(ev, repevent.CollectionReport): @@ -106,22 +106,14 @@ exitstatus = loopstate.exitstatus break except KeyboardInterrupt: - #print py.code.ExceptionInfo().getrepr(funcargs=True) - #raise exitstatus = self.EXIT_INTERRUPTED except: - #raise self.bus.notify(repevent.InternalException()) exitstatus = self.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = self.EXIT_TESTSFAILED - #while self.queue.qsize(): - # print self.queue.get() return exitstatus - def act_on_completed_tests(self): - self.triggershutdown() # XXX looponfailing - def triggershutdown(self): for host in self.hosts: host.node.shutdown() @@ -166,9 +158,11 @@ self.sendtestitems(senditems) def prunedeselected(self, colitems): + keywordexpr = self.config.option.keyword + if not keywordexpr: + return colitems remaining = [] deselected = [] - keywordexpr = self.config.option.keyword for colitem in colitems: if isinstance(colitem, Item): if colitem._skipbykeyword(keywordexpr): From hpk at codespeak.net Sun Aug 10 17:09:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 17:09:52 +0200 (CEST) Subject: [py-svn] r57163 - py/branch/event/py/test2 Message-ID: <20080810150952.10499498204@codespeak.net> Author: hpk Date: Sun Aug 10 17:09:50 2008 New Revision: 57163 Modified: py/branch/event/py/test2/collect.py Log: insert assertion Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Sun Aug 10 17:09:50 2008 @@ -93,6 +93,7 @@ return (self.name, self.parent) def __setstate__(self, (name, parent)): newnode = parent.join(name) + assert newnode is not None, (self, name, parent) self.__dict__.update(newnode.__dict__) #self.__init__(name=name, parent=parent) From hpk at codespeak.net Sun Aug 10 17:10:33 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 17:10:33 +0200 (CEST) Subject: [py-svn] r57164 - py/branch/event/py/test2/testing Message-ID: <20080810151033.B6DDD498204@codespeak.net> Author: hpk Date: Sun Aug 10 17:10:33 2008 New Revision: 57164 Modified: py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_runner_functional.py Log: refactor and use testing machinery better Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sun Aug 10 17:10:33 2008 @@ -1,5 +1,5 @@ import py -from suptest import assert_lines_contain_lines +from suptest import assert_lines_contain_lines, FileCreation def setup_module(mod): mod.modtmpdir = py.test2.ensuretemp(mod.__name__) @@ -10,7 +10,7 @@ self.outlines = outlines self.errlines = errlines -class TestPyTest: +class TestPyTest(FileCreation): def runpytest(self, *args): pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") cmdargs = [py.std.sys.executable, pytestcmd] + list(args) @@ -27,23 +27,10 @@ print >>py.std.sys.stderr, line return Result(ret, out, err) - def makepyfile(self, **kwargs): - return self._makefile('.py', **kwargs) - def maketxtfile(self, **kwargs): - return self._makefile('.txt', **kwargs) - def _makefile(self, ext, **kwargs): - ret = None - for name, value in kwargs.iteritems(): - p = py.path.local(name).new(ext=ext) - source = py.code.Source(value) - p.write(str(py.code.Source(value)).lstrip()) - if ret is None: - ret = p - return ret - def setup_method(self, method): - name = self.__class__.__name__ + "_" + method.__name__ - self.old = modtmpdir.mkdir(name).chdir() + super(TestPyTest, self).setup_method(method) + self.old = self.tmpdir.chdir() + def teardown_method(self, method): self.old.chdir() Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Sun Aug 10 17:10:33 2008 @@ -165,39 +165,50 @@ # XXX below some code to help with inlining examples # as source code. - -class InlineCollection(object): - """ helps to collect and run test functions inlining other test functions. """ +# +class FileCreation(object): def setup_method(self, method): self.tmpdir = py.test2.ensuretemp("%s_%s" % (self.__class__.__name__, method.__name__)) + def makepyfile(self, **kwargs): + return self._makefile('.py', **kwargs) + def maketxtfile(self, **kwargs): + return self._makefile('.txt', **kwargs) + def _makefile(self, ext, **kwargs): + ret = None + for name, value in kwargs.iteritems(): + p = self.tmpdir.join(name).new(ext=ext) + source = py.code.Source(value) + p.write(str(py.code.Source(value)).lstrip()) + if ret is None: + ret = p + return ret + +class InlineCollection(FileCreation): + """ helps to collect and run test functions inlining other test functions. """ - def makeconftest(self, source): - self.tmpdir.ensure("__init__.py") - path = self.tmpdir.ensure("conftest.py") - path.write(py.code.Source(source)) - - def getmodulecol(self, func, funcname="test_func"): - funcname = getattr(func, '__name__', funcname) + def getmodulecol(self, source): self.tmpdir.ensure("__init__.py") - path = self.tmpdir.ensure(funcname + ".py") - source = py.code.Source(func).strip() - path.write(str(source) + "\n") - self.config = py.test2.config._reparse([path.dirpath()]) + path = self.makepyfile(funcname=py.code.Source(source).strip()) + self.config = self.parseconfig(path) return self.config.getfsnode(path) + def parseconfig(self, *args): + return py.test2.config._reparse(list(args)) + + def getitems(self, source): + modulecol = self.getmodulecol(source) + return [modulecol.join(x) for x in modulecol.listdir()] + def getitem(self, source, funcname="test_func"): - modulecol = self.getmodulecol(source, funcname=funcname) + modulecol = self.getmodulecol(source) item = modulecol.join(funcname) assert item is not None, "%r item not found in module:\n%s" %(funcname, source) return item - def runitem(self, func, funcname="test_func", **kwargs): + def runitem(self, func, funcname="test_func", **runnerargs): item = self.getitem(func, funcname=funcname) runner = self.getrunner() - return runner(item, **kwargs) + return runner(item, **runnerargs) - def getitems(self, source): - modulecol = self.getmodulecol(source) - return [modulecol.join(x) for x in modulecol.listdir()] Modified: py/branch/event/py/test2/testing/test_runner_functional.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner_functional.py (original) +++ py/branch/event/py/test2/testing/test_runner_functional.py Sun Aug 10 17:10:33 2008 @@ -90,7 +90,7 @@ #assert ev.outcome.entries def test_custom_failure_repr(self): - self.makeconftest(""" + self.makepyfile(conftest=""" import py class Function(py.test2.collect.Function): def getfailurerepr(self, excinfo, outerr): @@ -110,7 +110,7 @@ #assert ev.failed.failurerepr == "hello" def test_failure_in_setup_function_ignores_custom_failure_repr(self): - self.makeconftest(""" + self.makepyfile(conftest=""" import py class Function(py.test2.collect.Function): def getfailurerepr(self, excinfo): From hpk at codespeak.net Sun Aug 10 17:13:50 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 17:13:50 +0200 (CEST) Subject: [py-svn] r57165 - in py/branch/event/py/test2/dist: . testing Message-ID: <20080810151350.1A5E0498204@codespeak.net> Author: hpk Date: Sun Aug 10 17:13:49 2008 New Revision: 57165 Modified: py/branch/event/py/test2/dist/async.py py/branch/event/py/test2/dist/testing/test_async.py py/branch/event/py/test2/dist/testing/test_async_functional.py Log: major rework and refinement of the dist-testing loop. hosts can crash, crashed tests get reschuled and all of this is carefully tested :) Modified: py/branch/event/py/test2/dist/async.py ============================================================================== --- py/branch/event/py/test2/dist/async.py (original) +++ py/branch/event/py/test2/dist/async.py Sun Aug 10 17:13:49 2008 @@ -15,18 +15,22 @@ Item = (py.test.collect.Item, py.test2.collect.Item) Collector = (py.test.collect.Collector, py.test2.collect.Collector) from py.__.test2.runner import basic_run_report, basic_collect_report, skip_report +from py.__.test2.session import Session + import Queue class LoopState(object): - def __init__(self, colitems, dowork=True): + def __init__(self, colitems): self.colitems = colitems self.exitstatus = None # loopstate.dowork is False after reschedule events # because otherwise we might very busily loop # waiting for a host to become ready. - self.dowork = dowork + self.dowork = True + self.shuttingdown = False + self.testsfailed = False -class AsyncSession(object): +class AsyncSession(Session): """ Session drives the collection and running of tests and generates test events for reporters. @@ -39,10 +43,11 @@ EXIT_NOHOSTS = 4 def __init__(self, config): - self.config = config - self.bus = EventBus() + super(AsyncSession, self).__init__(config=config) + self.queue = py.std.Queue.Queue() self.host2pending = {} + self.item2host = {} self.hosts = [] self._testsfailed = False @@ -64,6 +69,21 @@ if option.keyword_oneshot and not option.keyword: raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" + config = self.config + try: + config.getvalue('dist_hosts') + except KeyError: + print "For running ad-hoc distributed tests you need to specify" + print "dist_hosts in a local conftest.py file, for example:" + print "for example:" + print + print " dist_hosts = ['localhost'] * 4 # for 3 processors" + print " dist_hosts = ['you at remote.com', '...'] # for testing on ssh accounts" + print " # with your remote ssh accounts" + print + print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" + raise SystemExit + def main(self): colitems = [self.config.getfsnode(arg) for arg in self.config.args] self.sessionstarts() @@ -71,6 +91,8 @@ self.sessionfinishes(exitstatus=exitstatus) def loop_once(self, loopstate): + if loopstate.shuttingdown: + return self.loop_once_shutdown(loopstate) colitems = loopstate.colitems if loopstate.dowork and loopstate.colitems: self.triggertesting(colitems) @@ -82,21 +104,37 @@ pending = self.removehost(ev.host) colitems.extend(pending) elif isinstance(ev, repevent.RescheduleItems): - if not self.hosts: - loopstate.exitstatus = self.EXIT_NOHOSTS - return colitems.extend(ev.items) - loopstate.dowork = False + loopstate.dowork = False # avoid busywait elif isinstance(ev, repevent.ItemTestReport): - all_completed = self.processresult(ev) - if all_completed: - self.triggershutdown() - loopstate.exitstatus = self.EXIT_OK - return + self.removeitem(ev.colitem) + if ev.failed: + loopstate.testsfailed = True elif isinstance(ev, repevent.CollectionReport): if ev.passed: colitems.extend(ev.result) + # termination conditions + if ((loopstate.testsfailed and self.config.option.exitfirst) or + (not self.item2host and not colitems and not self.queue.qsize())): + self.triggershutdown() + loopstate.shuttingdown = True + elif not self.host2pending: + loopstate.exitstatus = self.EXIT_NOHOSTS + + def loop_once_shutdown(self, loopstate): + ev = self.queue.get() + self.bus.notify(ev) + if isinstance(ev, repevent.HostDown): + self.removehost(ev.host) + if not self.host2pending: + # finished + if loopstate.testsfailed: + loopstate.exitstatus = self.EXIT_TESTSFAILED + else: + loopstate.exitstatus = self.EXIT_OK + print "finished" + def loop(self, colitems): try: loopstate = LoopState(colitems) @@ -128,26 +166,8 @@ self.hosts.remove(host) return self.host2pending.pop(host) - def processresult(self, ev): - assert isinstance(ev, repevent.ItemTestReport) - completed = True - toremove = ev.colitem - for host, pending in self.host2pending.items(): - if toremove is not None and toremove in pending: - pending.remove(toremove) - if ev.failed: - self._testsfailed = True - if self.config.option.exitfirst: - break - toremove = None - if pending: - completed = False - if toremove is None: - break - return completed - def triggertesting(self, colitems): - colitems = self.prunedeselected(colitems) + colitems = self.filteritems(colitems) senditems = [] for next in colitems: if isinstance(next, Item): @@ -155,9 +175,32 @@ else: ev = basic_collect_report(next) self.queue.put(ev) - self.sendtestitems(senditems) + self.senditems(senditems) + + def senditems(self, tosend): + if not tosend: + return + for host, pending in self.host2pending.items(): + room = self.MAXITEMSPERHOST - len(pending) + if room > 0: + sending = tosend[:room] + host.node.sendlist(sending) + for item in sending: + self.item2host[item] = host + pending.extend(sending) + tosend[:] = tosend[room:] # update inplace + if not tosend: + break + if tosend: + # we have some left, give it to the main loop + self.queue.put(repevent.RescheduleItems(tosend)) + + def removeitem(self, item): + host = self.item2host.pop(item) + self.host2pending[host].remove(item) - def prunedeselected(self, colitems): + def filteritems(self, colitems): + """ return items to process (some may be deselected)""" keywordexpr = self.config.option.keyword if not keywordexpr: return colitems @@ -173,25 +216,11 @@ self.queue.put(repevent.Deselected(deselected)) return remaining - def sendtestitems(self, tosend): - for host, pending in self.host2pending.items(): - if not tosend: - break - room = self.MAXITEMSPERHOST - len(pending) - if room > 0: - sending = tosend[:room] - host.node.sendlist(sending) - pending.extend(sending) - tosend[:] = tosend[room:] # update inplace - if tosend: - # we have some left, give it to the main loop - self.queue.put(repevent.RescheduleItems(tosend)) def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ self.reporter = self.config.initreporter(self.bus) self.bus.notify(repevent.SessionStart(self)) - self.queue = py.std.Queue.Queue() self.hm = HostManager(self) self.hm.setup_hosts(notify=self.queue.put) for host in self.hm.hosts: Modified: py/branch/event/py/test2/dist/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async.py (original) +++ py/branch/event/py/test2/dist/testing/test_async.py Sun Aug 10 17:13:49 2008 @@ -37,16 +37,19 @@ assert pending == [item] py.test2.raises(Exception, "session.removehost(host)") - def test_processresult(self): + def test_senditems_removeitems(self): item = self.getitem("def test_func(): pass") rep = run(item) session = AsyncSession(item._config) host = Host("localhost") + host.node = MockNode() session.addhost(host) - session.host2pending[host].append(item) - all_completed = session.processresult(rep) - assert all_completed - assert not session.host2pending[host] + session.senditems([item]) + assert session.host2pending[host] == [item] + assert session.item2host[item] == host + session.removeitem(item) + assert not session.host2pending[host] + assert not session.item2host def test_triggertesting_collect(self): modcol = self.getmodulecol(""" @@ -95,20 +98,28 @@ exitstatus = session.loop([]) assert exitstatus == session.EXIT_INTERNALERROR - def test_rescheduleevent_and_completion(self): + def test_rescheduleevent(self): item = self.getitem("def test_func(): pass") session = AsyncSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) ev = repevent.RescheduleItems([item]) + loopstate = LoopState([]) session.queue.put(ev) + session.loop_once(loopstate) + # check that RescheduleEvents are not immediately + # rescheduled if there are no hosts + assert loopstate.dowork == False session.queue.put(repevent.NOP()) - session.queue.put(run(item)) - exitstatus = session.loop([]) - dumpqueue(session.queue) - assert exitstatus == session.EXIT_OK + session.loop_once(loopstate) + session.queue.put(repevent.NOP()) + session.loop_once(loopstate) assert host1.node.sent == [[item]] + session.queue.put(run(item)) + session.loop_once(loopstate) + assert loopstate.shuttingdown + assert not loopstate.testsfailed def test_no_hosts_remaining_for_tests(self): item = self.getitem("def test_func(): pass") @@ -153,26 +164,32 @@ def test_event_propagation(self): item = self.getitem("def test_func(): pass") session = AsyncSession(item._config) - events = [] - session.bus.subscribe(events.append) + ev = repevent.HostDown(Host("localhost"), None) + events = [] ; session.bus.subscribe(events.append) session.queue.put(ev) - exitstatus = session.loop([item]) - assert events[0] == ev # this tests event propagation - # the host that we signalled wasn't added - assert exitstatus == session.EXIT_INTERNALERROR + py.test.raises(ValueError, "session.loop_once(LoopState([]))") + assert events[0] == ev def runthrough(self, item): session = AsyncSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) - session.triggertesting([item]) + loopstate = LoopState([item]) + + session.queue.put(repevent.NOP()) + session.loop_once(loopstate) + + assert host1.node.sent == [[item]] ev = run(item) session.queue.put(ev) - exitstatus = session.loop([]) + session.loop_once(loopstate) + assert loopstate.shuttingdown + session.queue.put(repevent.HostDown(host1, None)) + session.loop_once(loopstate) dumpqueue(session.queue) - return session, exitstatus + return session, loopstate.exitstatus def test_exit_completed_tests_ok(self): item = self.getitem("def test_func(): pass") @@ -204,20 +221,15 @@ # run tests ourselves and produce reports ev1 = run(items[0]) ev2 = run(items[1]) - session.queue.put(ev1) + session.queue.put(ev1) # a failing one session.queue.put(ev2) - # now call the loop - exitstatus = session.loop([]) - - # and see that it only looked for one result - assert session.queue.qsize() == 1 - dumpqueue(session.queue) - - # and exits properly - assert exitstatus == session.EXIT_TESTSFAILED + loopstate = LoopState(items) + session.loop_once(loopstate) + assert loopstate.testsfailed + assert loopstate.shuttingdown - def test_dselected(self): + def test_filteritems(self): modcol = self.getmodulecol(""" def test_fail(): assert 0 @@ -227,51 +239,66 @@ session = AsyncSession(modcol._config) modcol._config.option.keyword = modcol.name - dsel = session.prunedeselected([modcol]) + dsel = session.filteritems([modcol]) assert dsel == [modcol] items = [modcol.join(x) for x in modcol.listdir()] - remaining = session.prunedeselected(items) + remaining = session.filteritems(items) assert remaining == [] ev = session.queue.get(block=False) assert isinstance(ev, repevent.Deselected) assert ev.items == items modcol._config.option.keyword = "test_fail" - remaining = session.prunedeselected(items) + remaining = session.filteritems(items) assert remaining == [items[0]] ev = session.queue.get(block=False) assert isinstance(ev, repevent.Deselected) assert ev.items == [items[1]] - - def test_no_busy_reschedule_items(self): - item = self.getitem("def test_func(): 0/0") + def test_hostdown_shutdown_after_completion(self): + item = self.getitem("def test_func(): pass") session = AsyncSession(item._config) - # set up a host that is full of pending items host = Host("localhost") host.node = MockNode() session.addhost(host) - session.host2pending[host].extend([item] * session.MAXITEMSPERHOST) - - # check that the loop does not busy wait - # i.e. puts a RescheduleEvent into the queue - # just after having read one and finding no - # place to distribute tests to - events = [repevent.RescheduleItems([item])] - def mocked_queue_get(): - return events.pop() - oldget = session.queue.get - session.queue.get = mocked_queue_get - exitstatus = session.loop([]) - assert not session.queue.qsize(), oldget() - - def test_initial_reschedule(self): - item = self.getitem("def test_func(): 0/0") - session = AsyncSession(item._config) + session.senditems([item]) + session.queue.put(run(item)) + loopstate = LoopState([]) + session.loop_once(loopstate) + assert host.node._shutdown is True + assert loopstate.exitstatus is None, "loop did not wait for hostdown" + assert loopstate.shuttingdown + session.queue.put(repevent.HostDown(host, None)) + session.loop_once(loopstate) + assert loopstate.exitstatus == 0 - # set up a host that is full of pending items + def test_nopending_but_collection_remains(self): + modcol = self.getmodulecol(""" + def test_fail(): + assert 0 + def test_pass(): + pass + """) + session = AsyncSession(modcol._config) host = Host("localhost") host.node = MockNode() session.addhost(host) - session.host2pending[host].extend([item] * session.MAXITEMSPERHOST) + + colreport = basic_collect_report(modcol) + item1, item2 = colreport.result + session.senditems([item1]) + # host2pending will become empty when the loop sees + # the report + session.queue.put(run(item1)) + + # but we have a collection pending + session.queue.put(colreport) + + loopstate = LoopState([]) + session.loop_once(loopstate) + assert loopstate.exitstatus is None, "loop did not care for collection report" + assert not loopstate.colitems + session.loop_once(loopstate) + assert loopstate.colitems == colreport.result + assert loopstate.exitstatus is None, "loop did not care for colitems" Modified: py/branch/event/py/test2/dist/testing/test_async_functional.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async_functional.py (original) +++ py/branch/event/py/test2/dist/testing/test_async_functional.py Sun Aug 10 17:13:49 2008 @@ -7,13 +7,8 @@ from py.__.test2.dist.async import AsyncSession from py.__.test2.dist.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup +from py.__.test2.testing import suptest -from py.__.test2.testing import suptest - -def setup_module(mod): - py.test.skip("XXX") - - def eventreader(session): queue = py.std.Queue.Queue() session.bus.subscribe(queue.put) @@ -27,20 +22,28 @@ raise IOError("did not see %r events" % (eventtype)) else: if isinstance(ev, eventtype): + #print "other events seen", events return ev events.append(ev) return readevent -class TestAsyncFunctional(DirSetup, BasicRsessionTest): - def setup_method(self, method): - DirSetup.setup_method(self, method) - BasicRsessionTest.setup_method(self, method) +class TestAsyncFunctional(suptest.InlineCollection): + def test_dist_no_disthost(self): + config = self.parseconfig(self.tmpdir, '-d') + py.test2.raises(SystemExit, "config.initsession()") + + def test_session_eventlog_dist(self): + self.makepyfile(conftest="dist_hosts=['localhost']\n") + eventlog = self.tmpdir.join("mylog") + config = self.parseconfig(self.tmpdir, '-d', '--eventlog=%s' % eventlog) + session = config.initsession() + session.bus.notify(repevent.SessionStart(session)) + s = eventlog.read() + assert s.find("SessionStart") != -1 def test_dist_some_tests(self): - self.source.ensure("conftest.py").write(py.code.Source(""" - dist_hosts = ['localhost'] - """)) - self.source.ensure("sub", "test_one.py").write(py.code.Source(""" + self.makepyfile(conftest="dist_hosts=['localhost']\n") + p1 = self.makepyfile(test_one=""" def test_1(): pass def test_x(): @@ -48,8 +51,8 @@ py.test2.skip("aaa") def test_fail(): assert 0 - """)) - config = py.test2.config._reparse([self.source.join("sub")]) + """) + config = self.parseconfig('-d', p1) dist = AsyncSession(config) readevent = eventreader(dist) dist.main() @@ -59,12 +62,17 @@ assert ev.skipped ev = readevent(repevent.ItemTestReport) assert ev.failed + # see that the host is really down + ev = readevent(repevent.HostDown) + assert ev.host.hostname == "localhost" + ev = readevent(repevent.SessionFinish) def test_distribution_rsync_roots_example(self): + py.test.skip("testing for root rsync needs rework") destdir = py.test2.ensuretemp("example_dist_destdir") subdir = "sub_example_dist" - tmpdir = self.source - tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" + sourcedir = self.tmpdir.mkdir("source") + sourcedir.ensure(subdir, "conftest.py").write(py.code.Source(""" dist_hosts = ["localhost:%s"] dist_rsync_roots = ["%s", "../py"] """ % (destdir, tmpdir.join(subdir), ))) @@ -95,65 +103,20 @@ assert len([x for x in testevents if x.failed]) == 3 assert len([x for x in testevents if x.skipped]) == 0 - def test_setup_teardown_run_ssh(self): - hosts = [Host('localhost:%s' % self.dest)] - - queue = py.std.Queue.Queue() - self.session.bus.subscribe(queue.put) - hm = HostManager(self.session, hosts=hosts) - hm.setup_hosts() - - # actually run some tests - for host in hm.hosts: - node = host.node - node.send(self.getfunc("passed")) - node.send(self.getfunc("failed")) - node.send(self.getfunc("skipped")) - node.send(self.getfunc("print")) - - num_hosts = len(hm.hosts) - events = [] - while len(events) < 4 * num_hosts: - item = queue.get(timeout=1.0) - if isinstance(item, repevent.ItemTestReport): - events.append(item) - print "got all events", events - hm.wait_for_completion() - passed = [ev for ev in events - if ev.passed] - skipped = [ev for ev in events - if ev.skipped] - assert len(passed) == 2 * num_hosts - assert len(skipped) == num_hosts - assert len(events) == 4 * num_hosts - # one of passed for each node has non-empty stdout - #passed_stdout = [i for i in passed if i.outcome.stdout.find('samfing') != -1] - #assert len(passed_stdout) == len(nodes), passed - def test_nice_level(self): - """ Tests if nice level behaviour is ok - """ - hosts = [Host('localhost:%s' % self.dest)] - tmpdir = self.source - tmpdir.ensure("__init__.py") - tmpdir.ensure("conftest.py").write(py.code.Source(""" - dist_hosts = ['localhost:%s'] - dist_nicelevel = 10 - """ % self.dest)) - tmpdir.ensure("test_one.py").write("""def test_nice(): - import os - assert os.nice(0) == 10 + """ Tests if nice level behaviour is ok """ + self.makepyfile(conftest=""" + dist_hosts=['localhost'] + dist_nicelevel = 10 """) - - config = py.test2.config._reparse([tmpdir]) - dist = RSession(config) - sorter = suptest.events_from_session(dist) - testevents = sorter.get(repevent.ItemTestReport) - passevents = [x for x in testevents if x.passed] - assert len(passevents) == 1 - -def test_dist_no_disthost(): - tmpdir = py.test2.ensuretemp("dist_no_disthost") - tmpdir.ensure("conftest.py") - config = py.test2.config._reparse([str(tmpdir), '-d']) - py.test2.raises(SystemExit, "config.initsession()") + p1 = self.makepyfile(test_nice=""" + def test_nice(): + import os + assert os.nice(0) == 10 + """) + config = self.parseconfig('-d', p1) + session = config.initsession() + events = suptest.events_from_session(session) + ev = events.getreport('test_nice') + assert ev.passed + From hpk at codespeak.net Sun Aug 10 17:25:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 17:25:17 +0200 (CEST) Subject: [py-svn] r57166 - py/branch/event/py/test2/dist/testing Message-ID: <20080810152517.DA4F9498204@codespeak.net> Author: hpk Date: Sun Aug 10 17:25:17 2008 New Revision: 57166 Modified: py/branch/event/py/test2/dist/testing/test_mypickle.py Log: remove test bug Modified: py/branch/event/py/test2/dist/testing/test_mypickle.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_mypickle.py (original) +++ py/branch/event/py/test2/dist/testing/test_mypickle.py Sun Aug 10 17:25:17 2008 @@ -94,6 +94,7 @@ channel.send(l) other_l = channel.receive() channel.send((l, other_l)) + channel.send(channel.receive()) channel.receive() """) channel = PickleChannel(channel) @@ -104,6 +105,8 @@ ret = channel.receive() assert ret[0] is other_l assert ret[1] is l + back = channel.receive() + assert other_l is other_l channel.send(None) #s1 = p1.dumps(a1) From hpk at codespeak.net Sun Aug 10 17:30:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 17:30:23 +0200 (CEST) Subject: [py-svn] r57167 - py/branch/event/py/test2/dist Message-ID: <20080810153023.A2D6E169FA3@codespeak.net> Author: hpk Date: Sun Aug 10 17:30:23 2008 New Revision: 57167 Modified: py/branch/event/py/test2/dist/async.py Log: remove left over print Modified: py/branch/event/py/test2/dist/async.py ============================================================================== --- py/branch/event/py/test2/dist/async.py (original) +++ py/branch/event/py/test2/dist/async.py Sun Aug 10 17:30:23 2008 @@ -133,7 +133,6 @@ loopstate.exitstatus = self.EXIT_TESTSFAILED else: loopstate.exitstatus = self.EXIT_OK - print "finished" def loop(self, colitems): try: From hpk at codespeak.net Sun Aug 10 17:38:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 17:38:08 +0200 (CEST) Subject: [py-svn] r57168 - in py/branch/event/py/test2/dist: . testing Message-ID: <20080810153808.962F0169FAA@codespeak.net> Author: hpk Date: Sun Aug 10 17:38:08 2008 New Revision: 57168 Modified: py/branch/event/py/test2/dist/async.py py/branch/event/py/test2/dist/testing/test_async.py Log: cleanup shutdown for exitfirst Modified: py/branch/event/py/test2/dist/async.py ============================================================================== --- py/branch/event/py/test2/dist/async.py (original) +++ py/branch/event/py/test2/dist/async.py Sun Aug 10 17:38:08 2008 @@ -123,9 +123,11 @@ loopstate.exitstatus = self.EXIT_NOHOSTS def loop_once_shutdown(self, loopstate): + # once we are in shutdown mode we dont send + # events other than HostDown upstream ev = self.queue.get() - self.bus.notify(ev) if isinstance(ev, repevent.HostDown): + self.bus.notify(ev) self.removehost(ev.host) if not self.host2pending: # finished Modified: py/branch/event/py/test2/dist/testing/test_async.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async.py (original) +++ py/branch/event/py/test2/dist/testing/test_async.py Sun Aug 10 17:38:08 2008 @@ -229,6 +229,23 @@ assert loopstate.testsfailed assert loopstate.shuttingdown + def test_shuttingdown_filters_events(self): + item = self.getitem("def test_func(): pass") + session = AsyncSession(item._config) + host = Host("localhost") + session.addhost(host) + loopstate = LoopState([]) + loopstate.shuttingdown = True + l = [] + session.bus.subscribe(l.append) + session.queue.put(run(item)) + session.loop_once(loopstate) + assert not l + ev = repevent.HostDown(host) + session.queue.put(ev) + session.loop_once(loopstate) + assert l == [ev] + def test_filteritems(self): modcol = self.getmodulecol(""" def test_fail(): From hpk at codespeak.net Sun Aug 10 17:40:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 17:40:02 +0200 (CEST) Subject: [py-svn] r57169 - in py/branch/event/py/test2: . dist dist/testing testing Message-ID: <20080810154002.D565C169FAA@codespeak.net> Author: hpk Date: Sun Aug 10 17:40:02 2008 New Revision: 57169 Added: py/branch/event/py/test2/dist/dsession.py - copied, changed from r57168, py/branch/event/py/test2/dist/async.py py/branch/event/py/test2/dist/testing/test_dsession.py - copied, changed from r57168, py/branch/event/py/test2/dist/testing/test_async.py py/branch/event/py/test2/dist/testing/test_functional_dsession.py - copied, changed from r57165, py/branch/event/py/test2/dist/testing/test_async_functional.py Removed: py/branch/event/py/test2/dist/async.py py/branch/event/py/test2/dist/testing/test_async.py py/branch/event/py/test2/dist/testing/test_async_functional.py Modified: py/branch/event/py/test2/async.py py/branch/event/py/test2/config.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/test_config.py Log: rename the somewhat odd "async" to DSession Modified: py/branch/event/py/test2/async.py ============================================================================== --- py/branch/event/py/test2/async.py (original) +++ py/branch/event/py/test2/async.py Sun Aug 10 17:40:02 2008 @@ -1,6 +1,6 @@ """ - EXPERIMENTAL async session (for dist/non-dist unification) + EXPERIMENTAL dsession session (for dist/non-dist unification) """ @@ -16,7 +16,7 @@ from runner import basic_run_report, basic_collect_report, skip_report import Queue -class AsyncSession(object): +class DSession(object): """ Session drives the collection and running of tests and generates test events for reporters. Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Sun Aug 10 17:40:02 2008 @@ -178,7 +178,7 @@ """ return default session name as determined from options. """ name = 'Session' if self.option.dist: - name = 'AsyncSession' + name = 'DSession' else: if self.option.looponfailing or self.option.executable: name = 'RemoteTerminalSession' @@ -217,7 +217,7 @@ Session = 'py.__.test2.session' RemoteTerminalSession = 'py.__.test2.terminal.remote' -AsyncSession = 'py.__.test2.dist.async' +DSession = 'py.__.test2.dist.dsession' # # helpers Deleted: /py/branch/event/py/test2/dist/async.py ============================================================================== --- /py/branch/event/py/test2/dist/async.py Sun Aug 10 17:40:02 2008 +++ (empty file) @@ -1,237 +0,0 @@ -""" - - EXPERIMENTAL async session (for dist/non-dist unification) - -""" - -import py -from py.__.test2 import repevent -from py.__.test2.eventbus import EventBus -import py.__.test2.custompdb -from py.__.test2.dist.hostmanage import HostManager - -# used for genitems() -from py.__.test2.outcome import Exit -Item = (py.test.collect.Item, py.test2.collect.Item) -Collector = (py.test.collect.Collector, py.test2.collect.Collector) -from py.__.test2.runner import basic_run_report, basic_collect_report, skip_report -from py.__.test2.session import Session - -import Queue - -class LoopState(object): - def __init__(self, colitems): - self.colitems = colitems - self.exitstatus = None - # loopstate.dowork is False after reschedule events - # because otherwise we might very busily loop - # waiting for a host to become ready. - self.dowork = True - self.shuttingdown = False - self.testsfailed = False - -class AsyncSession(Session): - """ - Session drives the collection and running of tests - and generates test events for reporters. - """ - MAXITEMSPERHOST = 10 - EXIT_OK = 0 - EXIT_TESTSFAILED = 1 - EXIT_INTERRUPTED = 2 - EXIT_INTERNALERROR = 3 - EXIT_NOHOSTS = 4 - - def __init__(self, config): - super(AsyncSession, self).__init__(config=config) - - self.queue = py.std.Queue.Queue() - self.host2pending = {} - self.item2host = {} - self.hosts = [] - self._testsfailed = False - - def fixoptions(self): - """ check, fix and determine conflicting options. """ - option = self.config.option - if option.runbrowser and not option.startserver: - #print "--runbrowser implies --startserver" - option.startserver = True - if self.config.getvalue("dist_boxed") and option.dist: - option.boxed = True - # conflicting options - if option.looponfailing and option.usepdb: - raise ValueError, "--looponfailing together with --pdb not supported." - if option.looponfailing and option.dist: - raise ValueError, "--looponfailing together with --dist not supported." - if option.executable and option.usepdb: - raise ValueError, "--exec together with --pdb not supported." - if option.keyword_oneshot and not option.keyword: - raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" - - config = self.config - try: - config.getvalue('dist_hosts') - except KeyError: - print "For running ad-hoc distributed tests you need to specify" - print "dist_hosts in a local conftest.py file, for example:" - print "for example:" - print - print " dist_hosts = ['localhost'] * 4 # for 3 processors" - print " dist_hosts = ['you at remote.com', '...'] # for testing on ssh accounts" - print " # with your remote ssh accounts" - print - print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" - raise SystemExit - - def main(self): - colitems = [self.config.getfsnode(arg) for arg in self.config.args] - self.sessionstarts() - exitstatus = self.loop(colitems) - self.sessionfinishes(exitstatus=exitstatus) - - def loop_once(self, loopstate): - if loopstate.shuttingdown: - return self.loop_once_shutdown(loopstate) - colitems = loopstate.colitems - if loopstate.dowork and loopstate.colitems: - self.triggertesting(colitems) - colitems[:] = [] - ev = self.queue.get() - loopstate.dowork = True - self.bus.notify(ev) - if isinstance(ev, repevent.HostDown): - pending = self.removehost(ev.host) - colitems.extend(pending) - elif isinstance(ev, repevent.RescheduleItems): - colitems.extend(ev.items) - loopstate.dowork = False # avoid busywait - elif isinstance(ev, repevent.ItemTestReport): - self.removeitem(ev.colitem) - if ev.failed: - loopstate.testsfailed = True - elif isinstance(ev, repevent.CollectionReport): - if ev.passed: - colitems.extend(ev.result) - - # termination conditions - if ((loopstate.testsfailed and self.config.option.exitfirst) or - (not self.item2host and not colitems and not self.queue.qsize())): - self.triggershutdown() - loopstate.shuttingdown = True - elif not self.host2pending: - loopstate.exitstatus = self.EXIT_NOHOSTS - - def loop_once_shutdown(self, loopstate): - # once we are in shutdown mode we dont send - # events other than HostDown upstream - ev = self.queue.get() - if isinstance(ev, repevent.HostDown): - self.bus.notify(ev) - self.removehost(ev.host) - if not self.host2pending: - # finished - if loopstate.testsfailed: - loopstate.exitstatus = self.EXIT_TESTSFAILED - else: - loopstate.exitstatus = self.EXIT_OK - - def loop(self, colitems): - try: - loopstate = LoopState(colitems) - while 1: - self.loop_once(loopstate) - if loopstate.exitstatus is not None: - exitstatus = loopstate.exitstatus - break - except KeyboardInterrupt: - exitstatus = self.EXIT_INTERRUPTED - except: - self.bus.notify(repevent.InternalException()) - exitstatus = self.EXIT_INTERNALERROR - if exitstatus == 0 and self._testsfailed: - exitstatus = self.EXIT_TESTSFAILED - return exitstatus - - def triggershutdown(self): - for host in self.hosts: - host.node.shutdown() - - def addhost(self, host): - assert host not in self.host2pending - assert host not in self.hosts - self.host2pending[host] = [] - self.hosts.append(host) - - def removehost(self, host): - self.hosts.remove(host) - return self.host2pending.pop(host) - - def triggertesting(self, colitems): - colitems = self.filteritems(colitems) - senditems = [] - for next in colitems: - if isinstance(next, Item): - senditems.append(next) - else: - ev = basic_collect_report(next) - self.queue.put(ev) - self.senditems(senditems) - - def senditems(self, tosend): - if not tosend: - return - for host, pending in self.host2pending.items(): - room = self.MAXITEMSPERHOST - len(pending) - if room > 0: - sending = tosend[:room] - host.node.sendlist(sending) - for item in sending: - self.item2host[item] = host - pending.extend(sending) - tosend[:] = tosend[room:] # update inplace - if not tosend: - break - if tosend: - # we have some left, give it to the main loop - self.queue.put(repevent.RescheduleItems(tosend)) - - def removeitem(self, item): - host = self.item2host.pop(item) - self.host2pending[host].remove(item) - - def filteritems(self, colitems): - """ return items to process (some may be deselected)""" - keywordexpr = self.config.option.keyword - if not keywordexpr: - return colitems - remaining = [] - deselected = [] - for colitem in colitems: - if isinstance(colitem, Item): - if colitem._skipbykeyword(keywordexpr): - deselected.append(colitem) - continue - remaining.append(colitem) - if deselected: - self.queue.put(repevent.Deselected(deselected)) - return remaining - - - def sessionstarts(self): - """ setup any neccessary resources ahead of the test run. """ - self.reporter = self.config.initreporter(self.bus) - self.bus.notify(repevent.SessionStart(self)) - self.hm = HostManager(self) - self.hm.setup_hosts(notify=self.queue.put) - for host in self.hm.hosts: - self.addhost(host) - - def sessionfinishes(self, exitstatus): - """ teardown any resources after a test run. """ - self.bus.notify(repevent.SessionFinish(self, exitstatus=exitstatus)) - self.reporter.deactivate() - #for host in self.hosts: - # host.shutdown() - for host in self.hosts: - host.gw.exit() Copied: py/branch/event/py/test2/dist/dsession.py (from r57168, py/branch/event/py/test2/dist/async.py) ============================================================================== --- py/branch/event/py/test2/dist/async.py (original) +++ py/branch/event/py/test2/dist/dsession.py Sun Aug 10 17:40:02 2008 @@ -1,6 +1,6 @@ """ - EXPERIMENTAL async session (for dist/non-dist unification) + EXPERIMENTAL dsession session (for dist/non-dist unification) """ @@ -30,7 +30,7 @@ self.shuttingdown = False self.testsfailed = False -class AsyncSession(Session): +class DSession(Session): """ Session drives the collection and running of tests and generates test events for reporters. @@ -43,7 +43,7 @@ EXIT_NOHOSTS = 4 def __init__(self, config): - super(AsyncSession, self).__init__(config=config) + super(DSession, self).__init__(config=config) self.queue = py.std.Queue.Queue() self.host2pending = {} Deleted: /py/branch/event/py/test2/dist/testing/test_async.py ============================================================================== --- /py/branch/event/py/test2/dist/testing/test_async.py Sun Aug 10 17:40:02 2008 +++ (empty file) @@ -1,321 +0,0 @@ -from py.__.test2.testing.suptest import InlineCollection -from py.__.test2.dist.async import AsyncSession, LoopState -from py.__.test2.dist.hostmanage import Host -from py.__.test2.runner import basic_collect_report -from py.__.test2 import repevent -import py - -def run(item): - runner = item._getrunner() - return runner(item) - -class MockNode: - def __init__(self): - self.sent = [] - - def sendlist(self, items): - self.sent.append(items) - - def shutdown(self): - self._shutdown=True - -def dumpqueue(queue): - while queue.qsize(): - print queue.get() - -class TestAsyncSession(InlineCollection): - def test_add_remove_host(self): - item = self.getitem("def test_func(): pass") - rep = run(item) - session = AsyncSession(item._config) - host = Host("localhost") - assert not session.hosts - session.addhost(host) - assert len(session.hosts) == 1 - session.host2pending[host].append(item) - pending = session.removehost(host) - assert pending == [item] - py.test2.raises(Exception, "session.removehost(host)") - - def test_senditems_removeitems(self): - item = self.getitem("def test_func(): pass") - rep = run(item) - session = AsyncSession(item._config) - host = Host("localhost") - host.node = MockNode() - session.addhost(host) - session.senditems([item]) - assert session.host2pending[host] == [item] - assert session.item2host[item] == host - session.removeitem(item) - assert not session.host2pending[host] - assert not session.item2host - - def test_triggertesting_collect(self): - modcol = self.getmodulecol(""" - def test_func(): - pass - """) - session = AsyncSession(modcol._config) - session.triggertesting([modcol]) - rep = session.queue.get(block=False) - assert isinstance(rep, repevent.CollectionReport) - assert len(rep.result) == 1 - - def test_triggertesting_item(self): - item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) - host1 = Host("localhost") - host1.node = MockNode() - host2 = Host("localhost") - host2.node = MockNode() - session.addhost(host1) - session.addhost(host2) - session.triggertesting([item] * (session.MAXITEMSPERHOST*2 + 1)) - host1_sent = host1.node.sent[0] - host2_sent = host2.node.sent[0] - assert host1_sent == [item] * session.MAXITEMSPERHOST - assert host2_sent == [item] * session.MAXITEMSPERHOST - assert session.host2pending[host1] == host1_sent - assert session.host2pending[host2] == host2_sent - ev = session.queue.get(block=False) - assert isinstance(ev, repevent.RescheduleItems) - assert ev.items == [item] - - def test_keyboardinterrupt(self): - item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) - def raise_(): raise KeyboardInterrupt() - session.queue.get = raise_ - exitstatus = session.loop([]) - assert exitstatus == session.EXIT_INTERRUPTED - - def test_internalerror(self): - item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) - def raise_(): raise ValueError() - session.queue.get = raise_ - exitstatus = session.loop([]) - assert exitstatus == session.EXIT_INTERNALERROR - - def test_rescheduleevent(self): - item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) - host1 = Host("localhost") - host1.node = MockNode() - session.addhost(host1) - ev = repevent.RescheduleItems([item]) - loopstate = LoopState([]) - session.queue.put(ev) - session.loop_once(loopstate) - # check that RescheduleEvents are not immediately - # rescheduled if there are no hosts - assert loopstate.dowork == False - session.queue.put(repevent.NOP()) - session.loop_once(loopstate) - session.queue.put(repevent.NOP()) - session.loop_once(loopstate) - assert host1.node.sent == [[item]] - session.queue.put(run(item)) - session.loop_once(loopstate) - assert loopstate.shuttingdown - assert not loopstate.testsfailed - - def test_no_hosts_remaining_for_tests(self): - item = self.getitem("def test_func(): pass") - # setup a session with one host - session = AsyncSession(item._config) - host1 = Host("localhost") - host1.node = MockNode() - session.addhost(host1) - - # setup a HostDown event - ev = repevent.HostDown(host1, None) - session.queue.put(ev) - - # call the loop which should come back - # because it doesn't have any hosts left - exitstatus = session.loop([item]) - dumpqueue(session.queue) - assert exitstatus == session.EXIT_NOHOSTS - - def test_hostdown_causes_reschedule_pending(self): - item = self.getitem("def test_func(): pass") - - # setup a session with two hosts - session = AsyncSession(item._config) - host1 = Host("localhost") - host1.node = MockNode() - session.addhost(host1) - host2 = Host("localhost") - host2.node = MockNode() - session.addhost(host2) - - # have one test pending for a host that goes down - session.host2pending[host1].append(item) - ev = repevent.HostDown(host1, None) - session.queue.put(ev) - - loopstate = LoopState([]) - session.loop_once(loopstate) - - assert loopstate.colitems == [item] - - def test_event_propagation(self): - item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) - - ev = repevent.HostDown(Host("localhost"), None) - events = [] ; session.bus.subscribe(events.append) - session.queue.put(ev) - py.test.raises(ValueError, "session.loop_once(LoopState([]))") - assert events[0] == ev - - def runthrough(self, item): - session = AsyncSession(item._config) - host1 = Host("localhost") - host1.node = MockNode() - session.addhost(host1) - loopstate = LoopState([item]) - - session.queue.put(repevent.NOP()) - session.loop_once(loopstate) - - assert host1.node.sent == [[item]] - ev = run(item) - session.queue.put(ev) - session.loop_once(loopstate) - assert loopstate.shuttingdown - session.queue.put(repevent.HostDown(host1, None)) - session.loop_once(loopstate) - dumpqueue(session.queue) - return session, loopstate.exitstatus - - def test_exit_completed_tests_ok(self): - item = self.getitem("def test_func(): pass") - session, exitstatus = self.runthrough(item) - assert exitstatus == session.EXIT_OK - - def test_exit_completed_tests_fail(self): - item = self.getitem("def test_func(): 0/0") - session, exitstatus = self.runthrough(item) - assert exitstatus == session.EXIT_TESTSFAILED - - def test_exit_on_first_failing(self): - modcol = self.getmodulecol(""" - def test_fail(): - assert 0 - def test_pass(): - pass - """) - modcol._config.option.exitfirst = True - session = AsyncSession(modcol._config) - host1 = Host("localhost") - host1.node = MockNode() - session.addhost(host1) - items = basic_collect_report(modcol).result - - # trigger testing - this sends tests to host1 - session.triggertesting(items) - - # run tests ourselves and produce reports - ev1 = run(items[0]) - ev2 = run(items[1]) - session.queue.put(ev1) # a failing one - session.queue.put(ev2) - # now call the loop - loopstate = LoopState(items) - session.loop_once(loopstate) - assert loopstate.testsfailed - assert loopstate.shuttingdown - - def test_shuttingdown_filters_events(self): - item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) - host = Host("localhost") - session.addhost(host) - loopstate = LoopState([]) - loopstate.shuttingdown = True - l = [] - session.bus.subscribe(l.append) - session.queue.put(run(item)) - session.loop_once(loopstate) - assert not l - ev = repevent.HostDown(host) - session.queue.put(ev) - session.loop_once(loopstate) - assert l == [ev] - - def test_filteritems(self): - modcol = self.getmodulecol(""" - def test_fail(): - assert 0 - def test_pass(): - pass - """) - session = AsyncSession(modcol._config) - - modcol._config.option.keyword = modcol.name - dsel = session.filteritems([modcol]) - assert dsel == [modcol] - items = [modcol.join(x) for x in modcol.listdir()] - remaining = session.filteritems(items) - assert remaining == [] - ev = session.queue.get(block=False) - assert isinstance(ev, repevent.Deselected) - assert ev.items == items - - modcol._config.option.keyword = "test_fail" - remaining = session.filteritems(items) - assert remaining == [items[0]] - ev = session.queue.get(block=False) - assert isinstance(ev, repevent.Deselected) - assert ev.items == [items[1]] - - def test_hostdown_shutdown_after_completion(self): - item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) - - host = Host("localhost") - host.node = MockNode() - session.addhost(host) - session.senditems([item]) - session.queue.put(run(item)) - loopstate = LoopState([]) - session.loop_once(loopstate) - assert host.node._shutdown is True - assert loopstate.exitstatus is None, "loop did not wait for hostdown" - assert loopstate.shuttingdown - session.queue.put(repevent.HostDown(host, None)) - session.loop_once(loopstate) - assert loopstate.exitstatus == 0 - - def test_nopending_but_collection_remains(self): - modcol = self.getmodulecol(""" - def test_fail(): - assert 0 - def test_pass(): - pass - """) - session = AsyncSession(modcol._config) - host = Host("localhost") - host.node = MockNode() - session.addhost(host) - - colreport = basic_collect_report(modcol) - item1, item2 = colreport.result - session.senditems([item1]) - # host2pending will become empty when the loop sees - # the report - session.queue.put(run(item1)) - - # but we have a collection pending - session.queue.put(colreport) - - loopstate = LoopState([]) - session.loop_once(loopstate) - assert loopstate.exitstatus is None, "loop did not care for collection report" - assert not loopstate.colitems - session.loop_once(loopstate) - assert loopstate.colitems == colreport.result - assert loopstate.exitstatus is None, "loop did not care for colitems" Deleted: /py/branch/event/py/test2/dist/testing/test_async_functional.py ============================================================================== --- /py/branch/event/py/test2/dist/testing/test_async_functional.py Sun Aug 10 17:40:02 2008 +++ (empty file) @@ -1,122 +0,0 @@ - -""" Tests various aspects of dist, like ssh hosts setup/teardown -""" - -import py -from py.__.test2 import repevent -from py.__.test2.dist.async import AsyncSession -from py.__.test2.dist.hostmanage import HostManager, Host -from basetest import BasicRsessionTest, DirSetup -from py.__.test2.testing import suptest - -def eventreader(session): - queue = py.std.Queue.Queue() - session.bus.subscribe(queue.put) - def readevent(eventtype=repevent.ItemTestReport, timeout=2.0): - events = [] - while 1: - try: - ev = queue.get(timeout=timeout) - except py.std.Queue.Empty: - print "seen events", events - raise IOError("did not see %r events" % (eventtype)) - else: - if isinstance(ev, eventtype): - #print "other events seen", events - return ev - events.append(ev) - return readevent - -class TestAsyncFunctional(suptest.InlineCollection): - def test_dist_no_disthost(self): - config = self.parseconfig(self.tmpdir, '-d') - py.test2.raises(SystemExit, "config.initsession()") - - def test_session_eventlog_dist(self): - self.makepyfile(conftest="dist_hosts=['localhost']\n") - eventlog = self.tmpdir.join("mylog") - config = self.parseconfig(self.tmpdir, '-d', '--eventlog=%s' % eventlog) - session = config.initsession() - session.bus.notify(repevent.SessionStart(session)) - s = eventlog.read() - assert s.find("SessionStart") != -1 - - def test_dist_some_tests(self): - self.makepyfile(conftest="dist_hosts=['localhost']\n") - p1 = self.makepyfile(test_one=""" - def test_1(): - pass - def test_x(): - import py - py.test2.skip("aaa") - def test_fail(): - assert 0 - """) - config = self.parseconfig('-d', p1) - dist = AsyncSession(config) - readevent = eventreader(dist) - dist.main() - ev = readevent(repevent.ItemTestReport) - assert ev.passed - ev = readevent(repevent.ItemTestReport) - assert ev.skipped - ev = readevent(repevent.ItemTestReport) - assert ev.failed - # see that the host is really down - ev = readevent(repevent.HostDown) - assert ev.host.hostname == "localhost" - ev = readevent(repevent.SessionFinish) - - def test_distribution_rsync_roots_example(self): - py.test.skip("testing for root rsync needs rework") - destdir = py.test2.ensuretemp("example_dist_destdir") - subdir = "sub_example_dist" - sourcedir = self.tmpdir.mkdir("source") - sourcedir.ensure(subdir, "conftest.py").write(py.code.Source(""" - dist_hosts = ["localhost:%s"] - dist_rsync_roots = ["%s", "../py"] - """ % (destdir, tmpdir.join(subdir), ))) - tmpdir.ensure(subdir, "__init__.py") - tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" - def test_1(): - pass - def test_2(): - assert 0 - def test_3(): - raise ValueError(23) - def test_4(someargs): - pass - def test_5(): - assert __file__ != '%s' - #def test_6(): - # import py - # assert py.__file__ != '%s' - """ % (tmpdir.join(subdir), py.__file__))) - destdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) - config = py.test2.config._reparse([tmpdir.join(subdir)]) - assert config.topdir == tmpdir - assert not tmpdir.join("__init__.py").check() - dist = AsyncSession(config) - sorter = suptest.events_from_session(dist) - testevents = sorter.get(repevent.ItemTestReport) - assert len([x for x in testevents if x.passed]) == 2 - assert len([x for x in testevents if x.failed]) == 3 - assert len([x for x in testevents if x.skipped]) == 0 - - def test_nice_level(self): - """ Tests if nice level behaviour is ok """ - self.makepyfile(conftest=""" - dist_hosts=['localhost'] - dist_nicelevel = 10 - """) - p1 = self.makepyfile(test_nice=""" - def test_nice(): - import os - assert os.nice(0) == 10 - """) - config = self.parseconfig('-d', p1) - session = config.initsession() - events = suptest.events_from_session(session) - ev = events.getreport('test_nice') - assert ev.passed - Copied: py/branch/event/py/test2/dist/testing/test_dsession.py (from r57168, py/branch/event/py/test2/dist/testing/test_async.py) ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async.py (original) +++ py/branch/event/py/test2/dist/testing/test_dsession.py Sun Aug 10 17:40:02 2008 @@ -1,5 +1,5 @@ from py.__.test2.testing.suptest import InlineCollection -from py.__.test2.dist.async import AsyncSession, LoopState +from py.__.test2.dist.dsession import DSession, LoopState from py.__.test2.dist.hostmanage import Host from py.__.test2.runner import basic_collect_report from py.__.test2 import repevent @@ -23,11 +23,11 @@ while queue.qsize(): print queue.get() -class TestAsyncSession(InlineCollection): +class TestDSession(InlineCollection): def test_add_remove_host(self): item = self.getitem("def test_func(): pass") rep = run(item) - session = AsyncSession(item._config) + session = DSession(item._config) host = Host("localhost") assert not session.hosts session.addhost(host) @@ -40,7 +40,7 @@ def test_senditems_removeitems(self): item = self.getitem("def test_func(): pass") rep = run(item) - session = AsyncSession(item._config) + session = DSession(item._config) host = Host("localhost") host.node = MockNode() session.addhost(host) @@ -56,7 +56,7 @@ def test_func(): pass """) - session = AsyncSession(modcol._config) + session = DSession(modcol._config) session.triggertesting([modcol]) rep = session.queue.get(block=False) assert isinstance(rep, repevent.CollectionReport) @@ -64,7 +64,7 @@ def test_triggertesting_item(self): item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) + session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() host2 = Host("localhost") @@ -84,7 +84,7 @@ def test_keyboardinterrupt(self): item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) + session = DSession(item._config) def raise_(): raise KeyboardInterrupt() session.queue.get = raise_ exitstatus = session.loop([]) @@ -92,7 +92,7 @@ def test_internalerror(self): item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) + session = DSession(item._config) def raise_(): raise ValueError() session.queue.get = raise_ exitstatus = session.loop([]) @@ -100,7 +100,7 @@ def test_rescheduleevent(self): item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) + session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) @@ -124,7 +124,7 @@ def test_no_hosts_remaining_for_tests(self): item = self.getitem("def test_func(): pass") # setup a session with one host - session = AsyncSession(item._config) + session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) @@ -143,7 +143,7 @@ item = self.getitem("def test_func(): pass") # setup a session with two hosts - session = AsyncSession(item._config) + session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) @@ -163,7 +163,7 @@ def test_event_propagation(self): item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) + session = DSession(item._config) ev = repevent.HostDown(Host("localhost"), None) events = [] ; session.bus.subscribe(events.append) @@ -172,7 +172,7 @@ assert events[0] == ev def runthrough(self, item): - session = AsyncSession(item._config) + session = DSession(item._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) @@ -209,7 +209,7 @@ pass """) modcol._config.option.exitfirst = True - session = AsyncSession(modcol._config) + session = DSession(modcol._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) @@ -231,7 +231,7 @@ def test_shuttingdown_filters_events(self): item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) + session = DSession(item._config) host = Host("localhost") session.addhost(host) loopstate = LoopState([]) @@ -253,7 +253,7 @@ def test_pass(): pass """) - session = AsyncSession(modcol._config) + session = DSession(modcol._config) modcol._config.option.keyword = modcol.name dsel = session.filteritems([modcol]) @@ -274,7 +274,7 @@ def test_hostdown_shutdown_after_completion(self): item = self.getitem("def test_func(): pass") - session = AsyncSession(item._config) + session = DSession(item._config) host = Host("localhost") host.node = MockNode() @@ -297,7 +297,7 @@ def test_pass(): pass """) - session = AsyncSession(modcol._config) + session = DSession(modcol._config) host = Host("localhost") host.node = MockNode() session.addhost(host) Copied: py/branch/event/py/test2/dist/testing/test_functional_dsession.py (from r57165, py/branch/event/py/test2/dist/testing/test_async_functional.py) ============================================================================== --- py/branch/event/py/test2/dist/testing/test_async_functional.py (original) +++ py/branch/event/py/test2/dist/testing/test_functional_dsession.py Sun Aug 10 17:40:02 2008 @@ -4,7 +4,7 @@ import py from py.__.test2 import repevent -from py.__.test2.dist.async import AsyncSession +from py.__.test2.dist.dsession import DSession from py.__.test2.dist.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup from py.__.test2.testing import suptest @@ -53,7 +53,7 @@ assert 0 """) config = self.parseconfig('-d', p1) - dist = AsyncSession(config) + dist = DSession(config) readevent = eventreader(dist) dist.main() ev = readevent(repevent.ItemTestReport) @@ -96,7 +96,7 @@ config = py.test2.config._reparse([tmpdir.join(subdir)]) assert config.topdir == tmpdir assert not tmpdir.join("__init__.py").check() - dist = AsyncSession(config) + dist = DSession(config) sorter = suptest.events_from_session(dist) testevents = sorter.get(repevent.ItemTestReport) assert len([x for x in testevents if x.passed]) == 2 Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sun Aug 10 17:40:02 2008 @@ -100,7 +100,7 @@ self.roots = roots class HostDown(BaseEvent): - def __init__(self, host, error): + def __init__(self, host, error=None): self.host = host self.error = error Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Sun Aug 10 17:40:02 2008 @@ -194,7 +194,7 @@ def test_sessionname_dist(self): config = py.test2.config._reparse([self.tmpdir, '--dist']) - assert config._getsessionname() == 'AsyncSession' + assert config._getsessionname() == 'DSession' def test_session_eventlog(self): eventlog = self.tmpdir.join("test_session_eventlog") @@ -213,7 +213,7 @@ for x in 'startserver runbrowser rest'.split(): config = py.test2.config._reparse([self.tmpdir, '--dist', '--%s' % x]) - assert config._getsessionname() == 'AsyncSession' + assert config._getsessionname() == 'DSession' def test_implied_different_sessions(self): config = py.test2.config._reparse([self.tmpdir, '--looponfailing']) @@ -221,7 +221,7 @@ config = py.test2.config._reparse([self.tmpdir, '--exec=x']) assert config._getsessionname() == 'RemoteTerminalSession' config = py.test2.config._reparse([self.tmpdir, '--dist', '--exec=x']) - assert config._getsessionname() == 'AsyncSession' + assert config._getsessionname() == 'DSession' def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" From hpk at codespeak.net Sun Aug 10 18:05:53 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 18:05:53 +0200 (CEST) Subject: [py-svn] r57170 - in py/branch/event/py/test2/dist: . testing Message-ID: <20080810160553.D25F916A092@codespeak.net> Author: hpk Date: Sun Aug 10 18:05:52 2008 New Revision: 57170 Modified: py/branch/event/py/test2/dist/dsession.py py/branch/event/py/test2/dist/testing/test_dsession.py Log: for control-c we need to wake up sometimes Modified: py/branch/event/py/test2/dist/dsession.py ============================================================================== --- py/branch/event/py/test2/dist/dsession.py (original) +++ py/branch/event/py/test2/dist/dsession.py Sun Aug 10 18:05:52 2008 @@ -45,7 +45,7 @@ def __init__(self, config): super(DSession, self).__init__(config=config) - self.queue = py.std.Queue.Queue() + self.queue = Queue.Queue() self.host2pending = {} self.item2host = {} self.hosts = [] @@ -97,7 +97,13 @@ if loopstate.dowork and loopstate.colitems: self.triggertesting(colitems) colitems[:] = [] - ev = self.queue.get() + # we use a timeout here so that control-C gets through + while 1: + try: + ev = self.queue.get(timeout=2.0) + break + except Queue.Empty: + continue loopstate.dowork = True self.bus.notify(ev) if isinstance(ev, repevent.HostDown): Modified: py/branch/event/py/test2/dist/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dist/testing/test_dsession.py Sun Aug 10 18:05:52 2008 @@ -85,7 +85,7 @@ def test_keyboardinterrupt(self): item = self.getitem("def test_func(): pass") session = DSession(item._config) - def raise_(): raise KeyboardInterrupt() + def raise_(timeout=None): raise KeyboardInterrupt() session.queue.get = raise_ exitstatus = session.loop([]) assert exitstatus == session.EXIT_INTERRUPTED From hpk at codespeak.net Sun Aug 10 18:28:33 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 18:28:33 +0200 (CEST) Subject: [py-svn] r57171 - in py/branch/event/py/test2: . dist dist/testing Message-ID: <20080810162833.BC7A5498204@codespeak.net> Author: hpk Date: Sun Aug 10 18:28:33 2008 New Revision: 57171 Modified: py/branch/event/py/test2/dist/mypickle.py py/branch/event/py/test2/dist/testing/test_mypickle.py py/branch/event/py/test2/runner.py Log: * introduce selfmemoize() to immutable picklers * fix -d --boxing (although boxing is probably soon to get substituted) Modified: py/branch/event/py/test2/dist/mypickle.py ============================================================================== --- py/branch/event/py/test2/dist/mypickle.py (original) +++ py/branch/event/py/test2/dist/mypickle.py Sun Aug 10 18:28:33 2008 @@ -51,6 +51,16 @@ self._protocol = protocol self.uneven = uneven and 1 or 0 + def selfmemoize(self, obj): + # this is for feeding objects to ourselfes + # which be the case e.g. if you want to pickle + # from a forked process back to the original + f = StringIO() + pickler = MyPickler(f, self._protocol, uneven=self.uneven) + pickler.memo = self._picklememo + pickler.memoize(obj) + self._updateunpicklememo() + def dumps(self, obj): f = StringIO() pickler = MyPickler(f, self._protocol, uneven=self.uneven) Modified: py/branch/event/py/test2/dist/testing/test_mypickle.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_mypickle.py (original) +++ py/branch/event/py/test2/dist/testing/test_mypickle.py Sun Aug 10 18:28:33 2008 @@ -57,6 +57,13 @@ other_a2 = p1.loads(s2) a1_back = p1.loads(p2.dumps(other_a1)) +def test_self_memoize(): + p1 = ImmutablePickler(uneven=False) + a1 = A() + p1.selfmemoize(a1) + x = p1.loads(p1.dumps(a1)) + assert x is a1 + TESTTIMEOUT = 2.0 class TestPickleChannelFunctional: def setup_class(cls): Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Sun Aug 10 18:28:33 2008 @@ -11,6 +11,7 @@ from py.__.test2 import repevent from py.__.test2.outcome import Skipped, Exit from py.__.test.outcome import Skipped as Skipped2 +from py.__.test2.dist.mypickle import ImmutablePickler import py.__.test2.custompdb class RobustRun(object): @@ -136,27 +137,19 @@ def forked_run_report(item, pdb=None): EXITSTATUS_TESTEXIT = 4 + ipickle = ImmutablePickler(uneven=0) + ipickle.selfmemoize(item._config) def runforked(): try: testrep = basic_run_report(item) except (KeyboardInterrupt, Exit): os._exit(EXITSTATUS_TESTEXIT) - f = StringIO() - p = Pickler(f, -1) - p.dump(item._config) - p.dump(testrep) - return f.getvalue() + return ipickle.dumps(testrep) ff = py.io.ForkedFunc(runforked) result = ff.waitfinish() - print vars(result) if result.retval is not None: - f = StringIO(result.retval) - u = Unpickler(f) - config = u.load() - config._initafterpickle(item._config.topdir) - ev = u.load() - return ev + return ipickle.loads(result.retval) else: if result.exitstatus == EXITSTATUS_TESTEXIT: raise Exit("forked test item %s raised Exit" %(item,)) From hpk at codespeak.net Sun Aug 10 18:41:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 18:41:20 +0200 (CEST) Subject: [py-svn] r57172 - in py/branch/event/py/test2: . dist rep rep/testing testing Message-ID: <20080810164120.20DDE4980EC@codespeak.net> Author: hpk Date: Sun Aug 10 18:41:19 2008 New Revision: 57172 Modified: py/branch/event/py/test2/dist/hostmanage.py py/branch/event/py/test2/rep/rest.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_rest.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rep/web.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/acceptance_test.py Log: rename HostGatewayReady back to original name Modified: py/branch/event/py/test2/dist/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dist/hostmanage.py (original) +++ py/branch/event/py/test2/dist/hostmanage.py Sun Aug 10 18:41:19 2008 @@ -133,7 +133,7 @@ python = self.session.config.getvalue("dist_remotepython") for host in self.hosts: host.initgateway(python=python) - self.session.bus.notify(repevent.HostReady(host, self.roots)) + self.session.bus.notify(repevent.HostGatewayReady(host, self.roots)) host.gw.host = host def init_rsync(self): Modified: py/branch/event/py/test2/rep/rest.py ============================================================================== --- py/branch/event/py/test2/rep/rest.py (original) +++ py/branch/event/py/test2/rep/rest.py Sun Aug 10 18:41:19 2008 @@ -66,7 +66,7 @@ def report_ImmediateFailure(self, item): pass - def report_HostReady(self, item): + def report_HostGatewayReady(self, item): self.to_rsync[item.host] = len(item.roots) def report_ItemStart(self, event): Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Sun Aug 10 18:41:19 2008 @@ -34,8 +34,8 @@ for line in str(ev.excinfo.getrepr()).split("\n"): self.write_line("InternalException: " + line) - def rep_HostReady(self, ev): - self.write_line("HostReady: %s" %(ev.host,)) + def rep_HostGatewayReady(self, ev): + self.write_line("HostGatewayReady: %s" %(ev.host,)) def rep_HostDown(self, ev): host = ev.host Modified: py/branch/event/py/test2/rep/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_rest.py (original) +++ py/branch/event/py/test2/rep/testing/test_rest.py Sun Aug 10 18:41:19 2008 @@ -64,7 +64,7 @@ def test_report_HostRSyncRootReady(self): h = Host('localhost') reporter.hosts_to_rsync = 1 - reporter.report(repevent.HostReady(h, ["a"])) + reporter.report(repevent.HostGatewayReady(h, ["a"])) event = repevent.HostRSyncRootReady(h, "a") reporter.report(event) assert stdout.getvalue() == '::\n\n localhost: READY\n\n' Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Sun Aug 10 18:41:19 2008 @@ -77,9 +77,9 @@ stringio = py.std.cStringIO.StringIO() host1 = Host("localhost") rep = TerminalReporter(modcol._config, file=stringio) - rep.processevent(repevent.HostReady(host1, None)) + rep.processevent(repevent.HostGatewayReady(host1, None)) s = popvalue(stringio) - assert s.find("HostReady") != -1 + assert s.find("HostGatewayReady") != -1 rep.processevent(repevent.HostDown(host1, "myerror")) s = popvalue(stringio) assert s.find("HostDown") != -1 Modified: py/branch/event/py/test2/rep/web.py ============================================================================== --- py/branch/event/py/test2/rep/web.py (original) +++ py/branch/event/py/test2/rep/web.py Sun Aug 10 18:41:19 2008 @@ -300,7 +300,7 @@ def _host_ready(self, event): self.pending_events.put(event) - def report_HostReady(self, item): + def report_HostGatewayReady(self, item): self.to_rsync[item.host] = len(item.roots) def report_HostRSyncRootReady(self, item): Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sun Aug 10 18:41:19 2008 @@ -94,7 +94,7 @@ def __init__(self, items): self.items = items -class HostReady(BaseEvent): +class HostGatewayReady(BaseEvent): def __init__(self, host, roots): self.host = host self.roots = roots Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sun Aug 10 18:41:19 2008 @@ -267,9 +267,9 @@ ) result = self.runpytest(p1, '-d') assert_lines_contain_lines(result.outlines, [ - "HostReady*localhost*", - "HostReady*localhost*", - "HostReady*localhost*", + "HostGatewayReady*localhost*", + "HostGatewayReady*localhost*", + "HostGatewayReady*localhost*", "HostDown*localhost*TERMINATED*", "*1/3 passed + 1 skip*", "*failures: 2*", From hpk at codespeak.net Sun Aug 10 19:04:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 19:04:13 +0200 (CEST) Subject: [py-svn] r57173 - in py/branch/event/py/test2: dist/testing testing Message-ID: <20080810170413.9FFBB498204@codespeak.net> Author: hpk Date: Sun Aug 10 19:04:12 2008 New Revision: 57173 Modified: py/branch/event/py/test2/dist/testing/test_functional_dsession.py py/branch/event/py/test2/testing/test_runner_functional.py Log: platform skips Modified: py/branch/event/py/test2/dist/testing/test_functional_dsession.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_functional_dsession.py (original) +++ py/branch/event/py/test2/dist/testing/test_functional_dsession.py Sun Aug 10 19:04:12 2008 @@ -8,6 +8,7 @@ from py.__.test2.dist.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup from py.__.test2.testing import suptest +import os def eventreader(session): queue = py.std.Queue.Queue() @@ -105,6 +106,8 @@ def test_nice_level(self): """ Tests if nice level behaviour is ok """ + if not hasattr(os, 'nice'): + py.test.skip("no os.nice() available") self.makepyfile(conftest=""" dist_hosts=['localhost'] dist_nicelevel = 10 Modified: py/branch/event/py/test2/testing/test_runner_functional.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner_functional.py (original) +++ py/branch/event/py/test2/testing/test_runner_functional.py Sun Aug 10 19:04:12 2008 @@ -210,6 +210,8 @@ class TestExecutionForked(BaseTests): def getrunner(self): + if not hasattr(py.std.os, 'fork'): + py.test.skip("no os.fork available") return forked_run_report def test_suicide(self): From hpk at codespeak.net Sun Aug 10 19:07:56 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 19:07:56 +0200 (CEST) Subject: [py-svn] r57174 - py/branch/event/py/path/local Message-ID: <20080810170756.A06CA16A161@codespeak.net> Author: hpk Date: Sun Aug 10 19:07:55 2008 New Revision: 57174 Modified: py/branch/event/py/path/local/local.py Log: ignore any error when trying to delete older directories Modified: py/branch/event/py/path/local/local.py ============================================================================== --- py/branch/event/py/path/local/local.py (original) +++ py/branch/event/py/path/local/local.py Sun Aug 10 19:07:55 2008 @@ -657,7 +657,9 @@ pass # assume that it means that there is no 'lf' try: path.remove(rec=1) - except py.error.Error: + except KeyboardInterrupt: + raise + except: # this might be py.error.Error, WindowsError ... pass # make link... From hpk at codespeak.net Sun Aug 10 19:11:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 19:11:44 +0200 (CEST) Subject: [py-svn] r57175 - py/branch/event/py/test2/testing Message-ID: <20080810171144.DB6E316A165@codespeak.net> Author: hpk Date: Sun Aug 10 19:11:44 2008 New Revision: 57175 Modified: py/branch/event/py/test2/testing/acceptance_test.py Log: have two dist acceptance tests, one with kill and one without Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sun Aug 10 19:11:44 2008 @@ -243,7 +243,36 @@ "*doc.txt:2: DocTestFailure" ]) + def test_dist_testing(self): + p1 = self.makepyfile( + test_one=""" + import py + def test_fail0(): + assert 0 + def test_fail1(): + raise ValueError() + def test_ok(): + pass + def test_skip(): + py.test.skip("hello") + """, + conftest=""" + dist_hosts = ['localhost'] * 3 + """ + ) + result = self.runpytest(p1, '-d') + assert_lines_contain_lines(result.outlines, [ + "HostGatewayReady*localhost*", + "HostGatewayReady*localhost*", + "HostGatewayReady*localhost*", + "*1/3 passed + 1 skip*", + "*failures: 2*", + ]) + def test_dist_tests_with_crash(self): + if not hasattr(py.std.os, 'kill'): + py.test.skip("no os.kill") + p1 = self.makepyfile( test_one=""" import py From hpk at codespeak.net Sun Aug 10 19:14:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 19:14:19 +0200 (CEST) Subject: [py-svn] r57176 - py/branch/event/py/test2/dist/testing Message-ID: <20080810171419.597B916A165@codespeak.net> Author: hpk Date: Sun Aug 10 19:14:19 2008 New Revision: 57176 Modified: py/branch/event/py/test2/dist/testing/test_masterslave.py Log: another platform skip Modified: py/branch/event/py/test2/dist/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/dist/testing/test_masterslave.py Sun Aug 10 19:14:19 2008 @@ -41,8 +41,10 @@ assert str(ev.error).find("AttributeError") != -1 def test_crash_killed(self): + if not hasattr(py.std.os, 'kill'): + py.test.skip("no os.kill") item = self.getfunc("kill15") - self.node.send(item) # invalid item + self.node.send(item) ev = self.getevent(repevent.HostDown) assert ev.host == self.host assert str(ev.error).find("TERMINATED") != -1 From hpk at codespeak.net Sun Aug 10 19:43:14 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 19:43:14 +0200 (CEST) Subject: [py-svn] r57177 - in py/branch/event/py/code: . testing Message-ID: <20080810174314.D3977498204@codespeak.net> Author: hpk Date: Sun Aug 10 19:43:12 2008 New Revision: 57177 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py Log: in long tracebacks don't repeat the long error message in the parseable file location Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Sun Aug 10 19:43:12 2008 @@ -172,7 +172,7 @@ if self.style == "long": reprargs = self.repr_args(entry) lines.extend(self.get_source(source, line_index, excinfo)) - message = excinfo and excinfo.exconly() or "" + message = excinfo and excinfo.typename or "" filelocrepr = ReprFileLocation(entry.path, entry.lineno+1, message) localsrepr = self.repr_locals(entry.locals) return ReprEntry(lines, reprargs, localsrepr, filelocrepr) Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Sun Aug 10 19:43:12 2008 @@ -549,7 +549,7 @@ assert tw.lines[7] == "> raise ValueError(x)" assert tw.lines[8] == "E ValueError: 3" assert tw.lines[9] == "" - assert tw.lines[10].endswith("mod.py:3: ValueError: 3") + assert tw.lines[10].endswith("mod.py:3: exceptions.ValueError") def test_format_excinfo(self): From hpk at codespeak.net Sun Aug 10 19:54:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 19:54:35 +0200 (CEST) Subject: [py-svn] r57178 - in py/branch/event/py/code: . testing Message-ID: <20080810175435.2043449823B@codespeak.net> Author: hpk Date: Sun Aug 10 19:54:34 2008 New Revision: 57178 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py Log: i think the type.__name__ is enough for "typename" Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Sun Aug 10 19:54:34 2008 @@ -23,7 +23,7 @@ self._striptext = 'AssertionError: ' self._excinfo = tup self.type, self.value, tb = self._excinfo - self.typename = self.type.__module__ + '.' + self.type.__name__ + self.typename = self.type.__name__ self.traceback = py.code.Traceback(tb) def __repr__(self): Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Sun Aug 10 19:54:34 2008 @@ -195,7 +195,7 @@ def test_excinfo_repr(): excinfo = py.test.raises(ValueError, h) s = repr(excinfo) - assert s == "" + assert s == "" def test_excinfo_str(): excinfo = py.test.raises(ValueError, h) @@ -549,7 +549,7 @@ assert tw.lines[7] == "> raise ValueError(x)" assert tw.lines[8] == "E ValueError: 3" assert tw.lines[9] == "" - assert tw.lines[10].endswith("mod.py:3: exceptions.ValueError") + assert tw.lines[10].endswith("mod.py:3: ValueError") def test_format_excinfo(self): From hpk at codespeak.net Sun Aug 10 19:56:55 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 19:56:55 +0200 (CEST) Subject: [py-svn] r57179 - in py/branch/event/py/test2: . testing Message-ID: <20080810175655.67E7149823B@codespeak.net> Author: hpk Date: Sun Aug 10 19:56:54 2008 New Revision: 57179 Modified: py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/acceptance_test.py Log: fix output tests Modified: py/branch/event/py/test2/repevent.py ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/repevent.py Sun Aug 10 19:56:54 2008 @@ -99,6 +99,11 @@ self.host = host self.roots = roots +class HostUp(BaseEvent): + def __init__(self, host, info): + self.host = host + self.info = info + class HostDown(BaseEvent): def __init__(self, host, error=None): self.host = host Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Sun Aug 10 19:56:54 2008 @@ -186,7 +186,7 @@ "E assert 3 == 2", "E + where 2 = g()", "", - "*test_fail.py:4: AssertionError: assert 3 == 2", + "*test_fail.py:4: AssertionError" ]) def test_capturing_outerr(self): From hpk at codespeak.net Sun Aug 10 21:06:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 21:06:12 +0200 (CEST) Subject: [py-svn] r57180 - py/branch/event/py/execnet/testing Message-ID: <20080810190612.2263A498230@codespeak.net> Author: hpk Date: Sun Aug 10 21:06:10 2008 New Revision: 57180 Modified: py/branch/event/py/execnet/testing/test_gateway.py Log: * refine tests about dying remote processes * add a test to not accidentally leave the debug flag in Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Sun Aug 10 21:06:10 2008 @@ -458,7 +458,6 @@ py.test.skip("provide information on causes/signals " "of dying remote gateways") - #class TestBlockingIssues: # def test_join_blocked_execution_gateway(self): # gateway = py.execnet.PopenGateway() @@ -512,29 +511,47 @@ ret = channel.receive() assert ret == 42 - def disabled_test_remote_is_killed_while_sending(self): + def test_remote_is_killed_while_sending(self): + py.test.skip("fix needed: dying remote process does not cause waitclose() to fail") + if not hasattr(py.std.os, 'kill'): + py.test.skip("no os.kill") gw = py.execnet.PopenGateway() channel = gw.remote_exec(""" import os import time - channel.send(os.getppid()) channel.send(os.getpid()) while 1: channel.send('#'*1000) time.sleep(10) """) - parent = channel.receive() remote = channel.receive() - assert parent == os.getpid() - time.sleep(0.5) - os.kill(remote, signal.SIGKILL) - time.sleep(1) + time.sleep(0.2) + os.kill(remote, 9) + # the following things should crash, but don't + # currently channel.waitclose(TESTTIMEOUT) - py.test.raises(EOFError, channel.receive) - #channel.waitclose(TESTTIMEOUT) - #channel.send('#') - - + py.test.raises(EOFError, channel.send, None) + +def test_endmarker_delivery_on_remote_killterm(): + if not hasattr(py.std.os, 'kill'): + py.test.skip("no os.kill()") + gw = py.execnet.PopenGateway() + try: + from Queue import Queue + q = Queue() + channel = gw.remote_exec(source=''' + import os + os.kill(os.getpid(), 15) + ''') + channel.setcallback(q.put, endmarker=999) + val = q.get(TESTTIMEOUT) + assert val == 999 + err = channel._getremoteerror() + finally: + gw.exit() + py.test.skip("provide information on causes/signals " + "of dying remote gateways") + class SocketGatewaySetup: def setup_class(cls): @@ -591,3 +608,7 @@ py.test.raises(IOError, gw.remote_init_threads, 3) gw.exit() + +def test_nodebug(): + from py.__.execnet import gateway + assert not gateway.debug From hpk at codespeak.net Sun Aug 10 21:23:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 10 Aug 2008 21:23:46 +0200 (CEST) Subject: [py-svn] r57181 - py/branch/event/py/execnet Message-ID: <20080810192346.01D4916A162@codespeak.net> Author: hpk Date: Sun Aug 10 21:23:45 2008 New Revision: 57181 Modified: py/branch/event/py/execnet/gateway.py Log: make sure that we notify callbacks if we can't send to the other side anymore. Modified: py/branch/event/py/execnet/gateway.py ============================================================================== --- py/branch/event/py/execnet/gateway.py (original) +++ py/branch/event/py/execnet/gateway.py Sun Aug 10 21:23:45 2008 @@ -136,8 +136,15 @@ self._traceex(exc_info()) break finally: + # XXX we need to signal fatal error states to + # channels/callbacks, particularly ones + # where the other side just died. self._stopexec() - self._stopsend() + try: + self._stopsend() + except IOError: + self._trace('IOError on _stopsend()') + pass self._channelfactory._finished_receiving() self._trace('leaving %r' % threading.currentThread()) From hpk at codespeak.net Mon Aug 11 16:47:38 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 11 Aug 2008 16:47:38 +0200 (CEST) Subject: [py-svn] r57196 - py/branch/reporter-merge Message-ID: <20080811144738.B9BC316A216@codespeak.net> Author: hpk Date: Mon Aug 11 16:47:36 2008 New Revision: 57196 Added: py/branch/reporter-merge/ - copied from r56734, py/branch/reporter-merge/ Log: restoring branch that apparently is still in use by exarkun From hpk at codespeak.net Tue Aug 12 17:44:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 17:44:02 +0200 (CEST) Subject: [py-svn] r57206 - py/branch/event/py/execnet/testing Message-ID: <20080812154402.B5A5116A0E5@codespeak.net> Author: hpk Date: Tue Aug 12 17:43:59 2008 New Revision: 57206 Modified: py/branch/event/py/execnet/testing/test_gateway.py Log: refine skipped test Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Tue Aug 12 17:43:59 2008 @@ -511,7 +511,7 @@ ret = channel.receive() assert ret == 42 - def test_remote_is_killed_while_sending(self): + def test_waitclose_on_remote_killed(self): py.test.skip("fix needed: dying remote process does not cause waitclose() to fail") if not hasattr(py.std.os, 'kill'): py.test.skip("no os.kill") @@ -521,16 +521,13 @@ import time channel.send(os.getpid()) while 1: - channel.send('#'*1000) - time.sleep(10) + channel.send("#" * 100) """) - remote = channel.receive() - time.sleep(0.2) - os.kill(remote, 9) - # the following things should crash, but don't - # currently - channel.waitclose(TESTTIMEOUT) + remotepid = channel.receive() + os.kill(remotepid, 9) + py.test.raises(channel.RemoteError, "channel.waitclose(TESTTIMEOUT)") py.test.raises(EOFError, channel.send, None) + py.test.raises(EOFError, channel.receive) def test_endmarker_delivery_on_remote_killterm(): if not hasattr(py.std.os, 'kill'): From hpk at codespeak.net Tue Aug 12 18:26:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 18:26:00 +0200 (CEST) Subject: [py-svn] r57207 - in py/branch/event/py/test2: . dist dsession dsession/testing rep rep/testing terminal testing Message-ID: <20080812162600.AFC0516A164@codespeak.net> Author: hpk Date: Tue Aug 12 18:26:00 2008 New Revision: 57207 Added: py/branch/event/py/test2/dsession/ - copied from r57206, py/branch/event/py/test2/dist/ Removed: py/branch/event/py/test2/dist/ Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/hostmanage.py py/branch/event/py/test2/dsession/masterslave.py py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/dsession/testing/test_functional_dsession.py py/branch/event/py/test2/dsession/testing/test_hostmanage.py py/branch/event/py/test2/dsession/testing/test_masterslave.py py/branch/event/py/test2/dsession/testing/test_mypickle.py py/branch/event/py/test2/rep/testing/test_rest.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rep/testing/test_web.py py/branch/event/py/test2/rep/testing/test_webjs.py py/branch/event/py/test2/rep/web.py py/branch/event/py/test2/rep/webjs.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/terminal/remote.py py/branch/event/py/test2/testing/test_collect.py Log: rename dist directory to dsession Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Tue Aug 12 18:26:00 2008 @@ -217,7 +217,7 @@ Session = 'py.__.test2.session' RemoteTerminalSession = 'py.__.test2.terminal.remote' -DSession = 'py.__.test2.dist.dsession' +DSession = 'py.__.test2.dsession.dsession' # # helpers Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dist/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Tue Aug 12 18:26:00 2008 @@ -8,7 +8,7 @@ from py.__.test2 import repevent from py.__.test2.eventbus import EventBus import py.__.test2.custompdb -from py.__.test2.dist.hostmanage import HostManager +from py.__.test2.dsession.hostmanage import HostManager # used for genitems() from py.__.test2.outcome import Exit Modified: py/branch/event/py/test2/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dist/hostmanage.py (original) +++ py/branch/event/py/test2/dsession/hostmanage.py Tue Aug 12 18:26:00 2008 @@ -1,6 +1,6 @@ import py import os -from py.__.test2.dist.masterslave import MasterNode +from py.__.test2.dsession.masterslave import MasterNode from py.__.test2 import repevent class Host(object): Modified: py/branch/event/py/test2/dsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/dist/masterslave.py (original) +++ py/branch/event/py/test2/dsession/masterslave.py Tue Aug 12 18:26:00 2008 @@ -3,7 +3,7 @@ """ import py from py.__.test2 import repevent -from py.__.test2.dist.mypickle import PickleChannel +from py.__.test2.dsession.mypickle import PickleChannel class MasterNode(object): ENDMARK = -1 @@ -74,9 +74,9 @@ # setting up slave code def install_slave(host, config): channel = host.gw.remote_exec(source=""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dist import masterslave + from py.__.test2.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) masterslave.setup_at_slave_side(channel, config) """) Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Tue Aug 12 18:26:00 2008 @@ -1,6 +1,6 @@ from py.__.test2.testing.suptest import InlineCollection -from py.__.test2.dist.dsession import DSession, LoopState -from py.__.test2.dist.hostmanage import Host +from py.__.test2.dsession.dsession import DSession, LoopState +from py.__.test2.dsession.hostmanage import Host from py.__.test2.runner import basic_collect_report from py.__.test2 import repevent import py Modified: py/branch/event/py/test2/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_functional_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_functional_dsession.py Tue Aug 12 18:26:00 2008 @@ -4,8 +4,8 @@ import py from py.__.test2 import repevent -from py.__.test2.dist.dsession import DSession -from py.__.test2.dist.hostmanage import HostManager, Host +from py.__.test2.dsession.dsession import DSession +from py.__.test2.dsession.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup from py.__.test2.testing import suptest import os @@ -54,9 +54,9 @@ assert 0 """) config = self.parseconfig('-d', p1) - dist = DSession(config) - readevent = eventreader(dist) - dist.main() + dsession = DSession(config) + readevent = eventreader(dsession) + dsession.main() ev = readevent(repevent.ItemTestReport) assert ev.passed ev = readevent(repevent.ItemTestReport) Modified: py/branch/event/py/test2/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/dsession/testing/test_hostmanage.py Tue Aug 12 18:26:00 2008 @@ -4,8 +4,8 @@ import py from basetest import DirSetup -from py.__.test2.dist.hostmanage import HostRSync, Host, HostManager -from py.__.test2.dist.hostmanage import sethomedir, gethomedir, getpath_relto_home +from py.__.test2.dsession.hostmanage import HostRSync, Host, HostManager +from py.__.test2.dsession.hostmanage import sethomedir, gethomedir, getpath_relto_home from py.__.test2 import repevent class TestHost(DirSetup): Modified: py/branch/event/py/test2/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/dsession/testing/test_masterslave.py Tue Aug 12 18:26:00 2008 @@ -1,7 +1,7 @@ import py -from py.__.test2.dist.masterslave import MasterNode -from py.__.test2.dist.hostmanage import Host +from py.__.test2.dsession.masterslave import MasterNode +from py.__.test2.dsession.hostmanage import Host from basetest import BasicRsessionTest from py.__.test2 import repevent Modified: py/branch/event/py/test2/dsession/testing/test_mypickle.py ============================================================================== --- py/branch/event/py/test2/dist/testing/test_mypickle.py (original) +++ py/branch/event/py/test2/dsession/testing/test_mypickle.py Tue Aug 12 18:26:00 2008 @@ -1,6 +1,6 @@ import py -from py.__.test2.dist.mypickle import ImmutablePickler, PickleChannel, UnpickleError +from py.__.test2.dsession.mypickle import ImmutablePickler, PickleChannel, UnpickleError class A: pass @@ -75,9 +75,9 @@ def test_popen_send_instance(self): channel = self.gw.remote_exec(""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dist.testing.test_mypickle import A + from py.__.test2.dsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -94,9 +94,9 @@ def test_send_concurrent(self): channel = self.gw.remote_exec(""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dist.testing.test_mypickle import A + from py.__.test2.dsession.testing.test_mypickle import A l = [A() for i in range(10)] channel.send(l) other_l = channel.receive() @@ -124,9 +124,9 @@ def test_popen_with_callback(self): channel = self.gw.remote_exec(""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dist.testing.test_mypickle import A + from py.__.test2.dsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -145,9 +145,9 @@ def test_popen_with_callback_with_endmarker(self): channel = self.gw.remote_exec(""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dist.testing.test_mypickle import A + from py.__.test2.dsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -169,9 +169,9 @@ def test_popen_with_callback_with_endmarker_and_unpickling_error(self): channel = self.gw.remote_exec(""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dist.testing.test_mypickle import A + from py.__.test2.dsession.testing.test_mypickle import A a1 = A() channel.receive() channel.send(a1) @@ -188,7 +188,7 @@ def test_popen_with_newchannel(self): channel = self.gw.remote_exec(""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) newchannel = channel.receive() newchannel.send(42) @@ -202,7 +202,7 @@ def test_popen_with_various_methods(self): channel = self.gw.remote_exec(""" - from py.__.test2.dist.mypickle import PickleChannel + from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) channel.receive() """) Modified: py/branch/event/py/test2/rep/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_rest.py (original) +++ py/branch/event/py/test2/rep/testing/test_rest.py Tue Aug 12 18:26:00 2008 @@ -9,9 +9,9 @@ from py.__.test2.testing.test_reporter import AbstractTestReporter,\ DummyChannel from py.__.test2 import repevent -from py.__.test2.dist.rest import RestReporter, NoLinkWriter +from py.__.test2.dsession.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * -from py.__.test2.dist.hostmanage import Host +from py.__.test2.dsession.hostmanage import Host from py.__.test2.outcome import SerializableOutcome class Container(object): Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Tue Aug 12 18:26:00 2008 @@ -4,7 +4,7 @@ #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report from test_collectonly import InlineCollect, popvalue, assert_stringio_contains_lines -from py.__.test2.dist.hostmanage import Host +from py.__.test2.dsession.hostmanage import Host class TestTerminal(InlineCollect): def test_session_reporter_subscription(self): Modified: py/branch/event/py/test2/rep/testing/test_web.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_web.py (original) +++ py/branch/event/py/test2/rep/testing/test_web.py Tue Aug 12 18:26:00 2008 @@ -15,14 +15,14 @@ mod.commproxy.USE_MOCHIKIT = False mod.rpython2javascript = rpython2javascript mod.commproxy = mod.commproxy - from py.__.test2.dist.web import TestHandler as _TestHandler - from py.__.test2.dist.web import MultiQueue + from py.__.test2.dsession.web import TestHandler as _TestHandler + from py.__.test2.dsession.web import MultiQueue mod._TestHandler = _TestHandler mod.MultiQueue = MultiQueue def test_js_generate(): - from py.__.test2.dist import webjs - from py.__.test2.dist.web import FUNCTION_LIST, IMPORTED_PYPY + from py.__.test2.dsession import webjs + from py.__.test2.dsession.web import FUNCTION_LIST, IMPORTED_PYPY source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False) assert source Modified: py/branch/event/py/test2/rep/testing/test_webjs.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_webjs.py (original) +++ py/branch/event/py/test2/rep/testing/test_webjs.py Tue Aug 12 18:26:00 2008 @@ -12,8 +12,8 @@ mod.dom = dom mod.schedule_callbacks = schedule_callbacks - from py.__.test2.dist import webjs - from py.__.test2.dist.web import exported_methods + from py.__.test2.dsession import webjs + from py.__.test2.dsession.web import exported_methods mod.webjs = webjs mod.exported_methods = exported_methods mod.here = py.magic.autopath().dirpath() @@ -28,8 +28,8 @@ mod.dom = dom dom.window = dom.Window(html) dom.document = dom.window.document - from py.__.test2.dist import webjs - from py.__.test2.dist.web import exported_methods + from py.__.test2.dsession import webjs + from py.__.test2.dsession.web import exported_methods mod.webjs = webjs mod.exported_methods = exported_methods Modified: py/branch/event/py/test2/rep/web.py ============================================================================== --- py/branch/event/py/test2/rep/web.py (original) +++ py/branch/event/py/test2/rep/web.py Tue Aug 12 18:26:00 2008 @@ -14,10 +14,10 @@ import socket import py -from py.__.test2.dist.dist import RSession +from py.__.test2.dsession.dsession import RSession from py.__.test2 import repevent from py.__.test2 import collect -from py.__.test2.dist.webdata import json +from py.__.test2.dsession.webdata import json DATADIR = py.path.local(__file__).dirpath("webdata") FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", @@ -59,7 +59,7 @@ return d class MultiQueue(object): - """ a tailor-made queue (internally using Queue) for py.test2.dist.web + """ a tailor-made queue (internally using Queue) for py.test2.dsession.web API-wise the main difference is that the get() method gets a sessid argument, which is used to determine what data to feed to the client @@ -406,7 +406,7 @@ web_name = py.path.local(__file__).dirpath().join("webjs.py") if IMPORTED_PYPY and web_name.mtime() > js_name.mtime() or \ (not js_name.check()): - from py.__.test2.dist import webjs + from py.__.test2.dsession import webjs javascript_source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False) Modified: py/branch/event/py/test2/rep/webjs.py ============================================================================== --- py/branch/event/py/test2/rep/webjs.py (original) +++ py/branch/event/py/test2/rep/webjs.py Tue Aug 12 18:26:00 2008 @@ -3,7 +3,7 @@ """ import py -from py.__.test2.dist.web import exported_methods +from py.__.test2.dsession.web import exported_methods try: from pypy.translator.js.modules import dom from pypy.translator.js.helper import __show_traceback Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Aug 12 18:26:00 2008 @@ -11,7 +11,7 @@ from py.__.test2 import repevent from py.__.test2.outcome import Skipped, Exit from py.__.test.outcome import Skipped as Skipped2 -from py.__.test2.dist.mypickle import ImmutablePickler +from py.__.test2.dsession.mypickle import ImmutablePickler import py.__.test2.custompdb class RobustRun(object): Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Tue Aug 12 18:26:00 2008 @@ -96,12 +96,12 @@ return py.execnet.PopenGateway(self.executable), topdir def run_remote_session(self, failures): - from py.__.test2.dist import masterslave + from py.__.test2.dsession import masterslave gw, topdir = self._initslavegateway() channel = gw.remote_exec(_pickle=True, source=""" print "SLAVE: initializing ..." from py.__.test2.terminal.remote import slaverun_TerminalSession - from py.__.test2.dist import masterslave + from py.__.test2.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) slaverun_TerminalSession(channel, config) """, stdout=self.out, stderr=self.out) Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Tue Aug 12 18:26:00 2008 @@ -483,7 +483,7 @@ "col3._totrail()") -from py.__.test2.dist.mypickle import ImmutablePickler +from py.__.test2.dsession.mypickle import ImmutablePickler class PickleTransport: def __init__(self): self.p1 = ImmutablePickler(uneven=0) From hpk at codespeak.net Tue Aug 12 18:41:54 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 18:41:54 +0200 (CEST) Subject: [py-svn] r57208 - in py/branch/event/py/test2: . dsession dsession/testing rep rep/testing testing Message-ID: <20080812164154.5806716A066@codespeak.net> Author: hpk Date: Tue Aug 12 18:41:50 2008 New Revision: 57208 Added: py/branch/event/py/test2/event.py - copied, changed from r57206, py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/test_event.py - copied, changed from r57206, py/branch/event/py/test2/testing/test_eventbus.py Removed: py/branch/event/py/test2/eventbus.py py/branch/event/py/test2/repevent.py py/branch/event/py/test2/testing/test_eventbus.py Modified: py/branch/event/py/test2/async.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/hostmanage.py py/branch/event/py/test2/dsession/masterslave.py py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/dsession/testing/test_functional_dsession.py py/branch/event/py/test2/dsession/testing/test_hostmanage.py py/branch/event/py/test2/dsession/testing/test_masterslave.py py/branch/event/py/test2/rep/base.py py/branch/event/py/test2/rep/rest.py py/branch/event/py/test2/rep/terminal.py py/branch/event/py/test2/rep/testing/test_basereporter.py py/branch/event/py/test2/rep/testing/test_collectonly.py py/branch/event/py/test2/rep/testing/test_rest.py py/branch/event/py/test2/rep/testing/test_terminal.py py/branch/event/py/test2/rep/web.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_collect.py py/branch/event/py/test2/testing/test_config.py py/branch/event/py/test2/testing/test_repevent.py py/branch/event/py/test2/testing/test_session.py Log: rename repevent -> event, merge with eventbus Modified: py/branch/event/py/test2/async.py ============================================================================== --- py/branch/event/py/test2/async.py (original) +++ py/branch/event/py/test2/async.py Tue Aug 12 18:41:50 2008 @@ -5,8 +5,8 @@ """ import py -from py.__.test2 import repevent -from py.__.test2.eventbus import EventBus +from py.__.test2 import event +from py.__.test2.event import EventBus import py.__.test2.custompdb # used for genitems() @@ -65,13 +65,13 @@ ev = self.getevent() # with timeout colitems = [] shouldstop = False - if isinstance(ev, repevent.CollectionReport): + if isinstance(ev, event.CollectionReport): self._colnum -= 1 if ev.passed: colitems.extend(ev.result) else: self.bus.notify(ev) - elif isinstance(ev, repevent.ItemTestReport): + elif isinstance(ev, event.ItemTestReport): self._testnum -= 1 self.bus.notify(ev) if ev.failed and self.config.option.exitfirst: @@ -95,10 +95,10 @@ """ setup any neccessary resources ahead of the test run. """ self.reporter = self.config.getreporter() self.reporter.activate(self.bus) - self.bus.notify(repevent.SessionStart(self)) + self.bus.notify(event.SessionStart(self)) def sessionfinishes(self): """ teardown any resources after a test run. """ - self.bus.notify(repevent.SessionFinish(self)) + self.bus.notify(event.SessionFinish(self)) self.reporter.deactivate(self.bus) Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Tue Aug 12 18:41:50 2008 @@ -5,8 +5,8 @@ """ import py -from py.__.test2 import repevent -from py.__.test2.eventbus import EventBus +from py.__.test2 import event +from py.__.test2.event import EventBus import py.__.test2.custompdb from py.__.test2.dsession.hostmanage import HostManager @@ -106,17 +106,17 @@ continue loopstate.dowork = True self.bus.notify(ev) - if isinstance(ev, repevent.HostDown): + if isinstance(ev, event.HostDown): pending = self.removehost(ev.host) colitems.extend(pending) - elif isinstance(ev, repevent.RescheduleItems): + elif isinstance(ev, event.RescheduleItems): colitems.extend(ev.items) loopstate.dowork = False # avoid busywait - elif isinstance(ev, repevent.ItemTestReport): + elif isinstance(ev, event.ItemTestReport): self.removeitem(ev.colitem) if ev.failed: loopstate.testsfailed = True - elif isinstance(ev, repevent.CollectionReport): + elif isinstance(ev, event.CollectionReport): if ev.passed: colitems.extend(ev.result) @@ -132,7 +132,7 @@ # once we are in shutdown mode we dont send # events other than HostDown upstream ev = self.queue.get() - if isinstance(ev, repevent.HostDown): + if isinstance(ev, event.HostDown): self.bus.notify(ev) self.removehost(ev.host) if not self.host2pending: @@ -153,7 +153,7 @@ except KeyboardInterrupt: exitstatus = self.EXIT_INTERRUPTED except: - self.bus.notify(repevent.InternalException()) + self.bus.notify(event.InternalException()) exitstatus = self.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: exitstatus = self.EXIT_TESTSFAILED @@ -200,7 +200,7 @@ break if tosend: # we have some left, give it to the main loop - self.queue.put(repevent.RescheduleItems(tosend)) + self.queue.put(event.RescheduleItems(tosend)) def removeitem(self, item): host = self.item2host.pop(item) @@ -220,14 +220,14 @@ continue remaining.append(colitem) if deselected: - self.queue.put(repevent.Deselected(deselected)) + self.queue.put(event.Deselected(deselected)) return remaining def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ self.reporter = self.config.initreporter(self.bus) - self.bus.notify(repevent.SessionStart(self)) + self.bus.notify(event.SessionStart(self)) self.hm = HostManager(self) self.hm.setup_hosts(notify=self.queue.put) for host in self.hm.hosts: @@ -235,7 +235,7 @@ def sessionfinishes(self, exitstatus): """ teardown any resources after a test run. """ - self.bus.notify(repevent.SessionFinish(self, exitstatus=exitstatus)) + self.bus.notify(event.SessionFinish(self, exitstatus=exitstatus)) self.reporter.deactivate() #for host in self.hosts: # host.shutdown() Modified: py/branch/event/py/test2/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/hostmanage.py (original) +++ py/branch/event/py/test2/dsession/hostmanage.py Tue Aug 12 18:41:50 2008 @@ -1,7 +1,7 @@ import py import os from py.__.test2.dsession.masterslave import MasterNode -from py.__.test2 import repevent +from py.__.test2 import event class Host(object): """ Host location representation for distributed testing. """ @@ -98,12 +98,12 @@ synced = key in self._synced if notify: notify( - repevent.HostRSyncing(host, py.path.local(self._sourcedir), + event.HostRSyncing(host, py.path.local(self._sourcedir), remotepath, synced)) def hostrsynced(host=host): if notify: notify( - repevent.HostRSyncRootReady(host, self._sourcedir)) + event.HostRSyncRootReady(host, self._sourcedir)) if key in self._synced: hostrsynced() return @@ -133,7 +133,7 @@ python = self.session.config.getvalue("dist_remotepython") for host in self.hosts: host.initgateway(python=python) - self.session.bus.notify(repevent.HostGatewayReady(host, self.roots)) + self.session.bus.notify(event.HostGatewayReady(host, self.roots)) host.gw.host = host def init_rsync(self): @@ -150,7 +150,7 @@ for host in self.hosts: rsync.add_target_host(host, destrelpath) rsync.send(raises=False) - self.session.bus.notify(repevent.RsyncFinished()) + self.session.bus.notify(event.RsyncFinished()) def setup_hosts(self, notify=None): if notify is None: @@ -177,7 +177,7 @@ else: numitems += len(host.node.pending) ev = self.queue.get(timeout=maxtimeout) - if isinstance(ev, repevent.HostDown): + if isinstance(ev, event.HostDown): self.hosts.remove(ev.host) if ev.host in pending_going_down: pending_going_down.remove(ev.host) Modified: py/branch/event/py/test2/dsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/dsession/masterslave.py (original) +++ py/branch/event/py/test2/dsession/masterslave.py Tue Aug 12 18:41:50 2008 @@ -2,7 +2,7 @@ Manage setup, running and local representation of remote nodes/processes. """ import py -from py.__.test2 import repevent +from py.__.test2 import event from py.__.test2.dsession.mypickle import PickleChannel class MasterNode(object): @@ -30,18 +30,18 @@ if not self._down: if not err: err = "TERMINATED" - self.notify(repevent.HostDown(self.host, err)) + self.notify(event.HostDown(self.host, err)) return if ev is None: self._down = True - self.notify(repevent.HostDown(self.host, None)) + self.notify(event.HostDown(self.host, None)) return except KeyboardInterrupt: raise except: excinfo = py.code.ExceptionInfo() print "!" * 20, excinfo - ev = repevent.InternalException(excinfo) + ev = event.InternalException(excinfo) self.notify(ev) def send(self, item): Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Tue Aug 12 18:41:50 2008 @@ -2,7 +2,7 @@ from py.__.test2.dsession.dsession import DSession, LoopState from py.__.test2.dsession.hostmanage import Host from py.__.test2.runner import basic_collect_report -from py.__.test2 import repevent +from py.__.test2 import event import py def run(item): @@ -59,7 +59,7 @@ session = DSession(modcol._config) session.triggertesting([modcol]) rep = session.queue.get(block=False) - assert isinstance(rep, repevent.CollectionReport) + assert isinstance(rep, event.CollectionReport) assert len(rep.result) == 1 def test_triggertesting_item(self): @@ -79,7 +79,7 @@ assert session.host2pending[host1] == host1_sent assert session.host2pending[host2] == host2_sent ev = session.queue.get(block=False) - assert isinstance(ev, repevent.RescheduleItems) + assert isinstance(ev, event.RescheduleItems) assert ev.items == [item] def test_keyboardinterrupt(self): @@ -104,16 +104,16 @@ host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) - ev = repevent.RescheduleItems([item]) + ev = event.RescheduleItems([item]) loopstate = LoopState([]) session.queue.put(ev) session.loop_once(loopstate) # check that RescheduleEvents are not immediately # rescheduled if there are no hosts assert loopstate.dowork == False - session.queue.put(repevent.NOP()) + session.queue.put(event.NOP()) session.loop_once(loopstate) - session.queue.put(repevent.NOP()) + session.queue.put(event.NOP()) session.loop_once(loopstate) assert host1.node.sent == [[item]] session.queue.put(run(item)) @@ -130,7 +130,7 @@ session.addhost(host1) # setup a HostDown event - ev = repevent.HostDown(host1, None) + ev = event.HostDown(host1, None) session.queue.put(ev) # call the loop which should come back @@ -153,7 +153,7 @@ # have one test pending for a host that goes down session.host2pending[host1].append(item) - ev = repevent.HostDown(host1, None) + ev = event.HostDown(host1, None) session.queue.put(ev) loopstate = LoopState([]) @@ -165,7 +165,7 @@ item = self.getitem("def test_func(): pass") session = DSession(item._config) - ev = repevent.HostDown(Host("localhost"), None) + ev = event.HostDown(Host("localhost"), None) events = [] ; session.bus.subscribe(events.append) session.queue.put(ev) py.test.raises(ValueError, "session.loop_once(LoopState([]))") @@ -178,7 +178,7 @@ session.addhost(host1) loopstate = LoopState([item]) - session.queue.put(repevent.NOP()) + session.queue.put(event.NOP()) session.loop_once(loopstate) assert host1.node.sent == [[item]] @@ -186,7 +186,7 @@ session.queue.put(ev) session.loop_once(loopstate) assert loopstate.shuttingdown - session.queue.put(repevent.HostDown(host1, None)) + session.queue.put(event.HostDown(host1, None)) session.loop_once(loopstate) dumpqueue(session.queue) return session, loopstate.exitstatus @@ -241,7 +241,7 @@ session.queue.put(run(item)) session.loop_once(loopstate) assert not l - ev = repevent.HostDown(host) + ev = event.HostDown(host) session.queue.put(ev) session.loop_once(loopstate) assert l == [ev] @@ -262,14 +262,14 @@ remaining = session.filteritems(items) assert remaining == [] ev = session.queue.get(block=False) - assert isinstance(ev, repevent.Deselected) + assert isinstance(ev, event.Deselected) assert ev.items == items modcol._config.option.keyword = "test_fail" remaining = session.filteritems(items) assert remaining == [items[0]] ev = session.queue.get(block=False) - assert isinstance(ev, repevent.Deselected) + assert isinstance(ev, event.Deselected) assert ev.items == [items[1]] def test_hostdown_shutdown_after_completion(self): @@ -286,7 +286,7 @@ assert host.node._shutdown is True assert loopstate.exitstatus is None, "loop did not wait for hostdown" assert loopstate.shuttingdown - session.queue.put(repevent.HostDown(host, None)) + session.queue.put(event.HostDown(host, None)) session.loop_once(loopstate) assert loopstate.exitstatus == 0 Modified: py/branch/event/py/test2/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_functional_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_functional_dsession.py Tue Aug 12 18:41:50 2008 @@ -3,7 +3,7 @@ """ import py -from py.__.test2 import repevent +from py.__.test2 import event from py.__.test2.dsession.dsession import DSession from py.__.test2.dsession.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup @@ -13,7 +13,7 @@ def eventreader(session): queue = py.std.Queue.Queue() session.bus.subscribe(queue.put) - def readevent(eventtype=repevent.ItemTestReport, timeout=2.0): + def readevent(eventtype=event.ItemTestReport, timeout=2.0): events = [] while 1: try: @@ -38,7 +38,7 @@ eventlog = self.tmpdir.join("mylog") config = self.parseconfig(self.tmpdir, '-d', '--eventlog=%s' % eventlog) session = config.initsession() - session.bus.notify(repevent.SessionStart(session)) + session.bus.notify(event.SessionStart(session)) s = eventlog.read() assert s.find("SessionStart") != -1 @@ -57,16 +57,16 @@ dsession = DSession(config) readevent = eventreader(dsession) dsession.main() - ev = readevent(repevent.ItemTestReport) + ev = readevent(event.ItemTestReport) assert ev.passed - ev = readevent(repevent.ItemTestReport) + ev = readevent(event.ItemTestReport) assert ev.skipped - ev = readevent(repevent.ItemTestReport) + ev = readevent(event.ItemTestReport) assert ev.failed # see that the host is really down - ev = readevent(repevent.HostDown) + ev = readevent(event.HostDown) assert ev.host.hostname == "localhost" - ev = readevent(repevent.SessionFinish) + ev = readevent(event.SessionFinish) def test_distribution_rsync_roots_example(self): py.test.skip("testing for root rsync needs rework") @@ -99,7 +99,7 @@ assert not tmpdir.join("__init__.py").check() dist = DSession(config) sorter = suptest.events_from_session(dist) - testevents = sorter.get(repevent.ItemTestReport) + testevents = sorter.get(event.ItemTestReport) assert len([x for x in testevents if x.passed]) == 2 assert len([x for x in testevents if x.failed]) == 3 assert len([x for x in testevents if x.skipped]) == 0 Modified: py/branch/event/py/test2/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/dsession/testing/test_hostmanage.py Tue Aug 12 18:41:50 2008 @@ -6,7 +6,7 @@ from basetest import DirSetup from py.__.test2.dsession.hostmanage import HostRSync, Host, HostManager from py.__.test2.dsession.hostmanage import sethomedir, gethomedir, getpath_relto_home -from py.__.test2 import repevent +from py.__.test2 import event class TestHost(DirSetup): def _gethostinfo(self, relpath=""): @@ -133,13 +133,13 @@ rsync.add_target_host(h1, notify=events.append) assert events l = [x for x in events - if isinstance(x, repevent.HostRSyncing)] + if isinstance(x, event.HostRSyncing)] assert len(l) == 1 ev = l[0] assert ev.host == h1 assert ev.root == ev.remotepath l = [x for x in events - if isinstance(x, repevent.HostRSyncRootReady)] + if isinstance(x, event.HostRSyncRootReady)] assert len(l) == 1 ev = l[0] assert ev.root == self.source @@ -265,8 +265,8 @@ for host in hm.hosts: host.node.shutdown() for host in hm.hosts: - event = queue.get(timeout=2.0) - assert isinstance(event, repevent.HostDown) + ev = queue.get(timeout=2.0) + assert isinstance(ev, event.HostDown) def XXXtest_ssh_rsync_samehost_twice(self): #XXX we have no easy way to have a temp directory remotely! Modified: py/branch/event/py/test2/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test2/dsession/testing/test_masterslave.py Tue Aug 12 18:41:50 2008 @@ -3,10 +3,10 @@ from py.__.test2.dsession.masterslave import MasterNode from py.__.test2.dsession.hostmanage import Host from basetest import BasicRsessionTest -from py.__.test2 import repevent +from py.__.test2 import event class TestMasterSlaveConnection(BasicRsessionTest): - def getevent(self, eventtype=repevent.ItemTestReport, timeout=2.0): + def getevent(self, eventtype=event.ItemTestReport, timeout=2.0): events = [] while 1: try: @@ -36,7 +36,7 @@ def test_crash_invalid_item(self): self.node.send(123) # invalid item - ev = self.getevent(repevent.HostDown) + ev = self.getevent(event.HostDown) assert ev.host == self.host assert str(ev.error).find("AttributeError") != -1 @@ -45,24 +45,24 @@ py.test.skip("no os.kill") item = self.getfunc("kill15") self.node.send(item) - ev = self.getevent(repevent.HostDown) + ev = self.getevent(event.HostDown) assert ev.host == self.host assert str(ev.error).find("TERMINATED") != -1 def test_node_down(self): self.node.shutdown() - event = self.getevent(repevent.HostDown) - assert event.host == self.host - assert not event.error + ev = self.getevent(event.HostDown) + assert ev.host == self.host + assert not ev.error self.node.callback(self.node.ENDMARK) excinfo = py.test.raises(IOError, - "self.getevent(repevent.HostDown, timeout=0.01)") + "self.getevent(event.HostDown, timeout=0.01)") def test_send_on_closed_channel(self): item = self.getfunc("passed") self.node.channel.close() py.test2.raises(IOError, "self.node.send(item)") - #ev = self.getevent(repevent.InternalException) + #ev = self.getevent(event.InternalException) #assert ev.excinfo.errisinstance(IOError) def test_send_one(self): Copied: py/branch/event/py/test2/event.py (from r57206, py/branch/event/py/test2/repevent.py) ============================================================================== --- py/branch/event/py/test2/repevent.py (original) +++ py/branch/event/py/test2/event.py Tue Aug 12 18:41:50 2008 @@ -7,8 +7,23 @@ from py.__.test2.outcome import Skipped from py.__.test.outcome import Skipped as Skipped2 -validoutcomes = ("passed skipped failed failed_setup failed_collection " - "failed_teardown failed_crashed").split() +class EventBus(object): + """ General Event Bus for distributing events. """ + def __init__(self): + self._subscribers = [] + + def subscribe(self, callback): + """ subscribe given callback to bus events. """ + self._subscribers.append(callback) + + def unsubscribe(self, callback): + """ unsubscribe given callback from bus events. """ + self._subscribers.remove(callback) + + def notify(self, event): + for subscriber in self._subscribers: + subscriber(event) + class BaseEvent(object): def __repr__(self): Deleted: /py/branch/event/py/test2/eventbus.py ============================================================================== --- /py/branch/event/py/test2/eventbus.py Tue Aug 12 18:41:50 2008 +++ (empty file) @@ -1,19 +0,0 @@ - -class EventBus(object): - """ General Event Bus for distributing and processing events. """ - def __init__(self): - self._subscribers = [] - - def subscribe(self, callback): - """ subscribe given callback to bus events. """ - self._subscribers.append(callback) - - def unsubscribe(self, callback): - """ unsubscribe given callback from bus events. """ - self._subscribers.remove(callback) - - def notify(self, event): - for subscriber in self._subscribers: - subscriber(event) - - Modified: py/branch/event/py/test2/rep/base.py ============================================================================== --- py/branch/event/py/test2/rep/base.py (original) +++ py/branch/event/py/test2/rep/base.py Tue Aug 12 18:41:50 2008 @@ -1,4 +1,4 @@ -from py.__.test2 import repevent +from py.__.test2 import event import sys class BaseReporter(object): Modified: py/branch/event/py/test2/rep/rest.py ============================================================================== --- py/branch/event/py/test2/rep/rest.py (original) +++ py/branch/event/py/test2/rep/rest.py Tue Aug 12 18:41:50 2008 @@ -6,7 +6,7 @@ import sys from StringIO import StringIO from py.__.test2.reporter import AbstractReporter -from py.__.test2 import repevent +from py.__.test2 import event from py.__.rest.rst import * class RestReporter(AbstractReporter): @@ -128,11 +128,11 @@ texts = {} for event in self.skipped_tests_outcome: colitem = event.item - if isinstance(event, repevent.ItemFinish): + if isinstance(event, event.ItemFinish): outcome = event.outcome text = outcome.skipped itemname = self.get_item_name(event, colitem) - elif isinstance(event, repevent.DeselectedItem): + elif isinstance(event, event.DeselectedItem): text = str(event.excinfo.value) itemname = "/".join(colitem.listnames()) if text not in texts: @@ -159,7 +159,7 @@ for i, event in enumerate(self.failed_tests_outcome): if i > 0: self.add_rest(Transition()) - if isinstance(event, repevent.ItemFinish): + if isinstance(event, event.ItemFinish): host = self.get_host(event) itempath = self.get_path_from_item(event.item) root = self.get_rootpath(event.item) Modified: py/branch/event/py/test2/rep/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/rep/terminal.py Tue Aug 12 18:41:50 2008 @@ -1,6 +1,6 @@ import py import sys -from py.__.test2 import repevent +from py.__.test2 import event from py.__.test2.rep.base import BaseReporter from py.__.test2.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped Modified: py/branch/event/py/test2/rep/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/rep/testing/test_basereporter.py Tue Aug 12 18:41:50 2008 @@ -1,7 +1,7 @@ import py from py.__.test2.rep.base import BaseReporter -from py.__.test2.eventbus import EventBus -from py.__.test2 import repevent +from py.__.test2.event import EventBus +from py.__.test2 import event from py.__.test2.runner import OutcomeRepr from py.__.test2.rep.base import getrelpath, getmodpath, repr_pythonversion from py.__.test2.testing import setupdata @@ -22,7 +22,7 @@ def rep_SessionStart(self, ev): l.append(ev) rep = MyReporter() - ev = repevent.SessionStart(None) + ev = event.SessionStart(None) rep.processevent(ev) assert len(l) == 1 assert l[0] is ev @@ -33,7 +33,7 @@ def rep(self, ev): l.append(ev) rep = MyReporter() - ev = repevent.SessionStart(None) + ev = event.SessionStart(None) rep.processevent(ev) assert len(l) == 1 assert l[0] is ev @@ -41,14 +41,14 @@ def test_TestItemReport_one(self): for outcome in 'passed skipped failed'.split(): rep = BaseReporter() - ev = repevent.ItemTestReport(None, **{outcome:True}) + ev = event.ItemTestReport(None, **{outcome:True}) rep.processevent(ev) assert getattr(rep, '_' + outcome) == [ev] def test_CollectionReport(self): for outcome in 'skipped failed'.split(): rep = BaseReporter() - ev = repevent.CollectionReport(None, None, **{outcome:True}) + ev = event.CollectionReport(None, None, **{outcome:True}) rep.processevent(ev) assert getattr(rep, '_' + outcome) == [ev] @@ -60,8 +60,8 @@ message = "justso" out1 = OutcomeRepr(None, None, longrepr) out2 = OutcomeRepr(None, None, longrepr) - ev1 = repevent.CollectionReport(None, None, skipped=out1) - ev2 = repevent.ItemTestReport(None, skipped=out2) + ev1 = event.CollectionReport(None, None, skipped=out1) + ev2 = event.ItemTestReport(None, skipped=out2) rep.processevent(ev1) rep.processevent(ev2) assert len(rep._skipped) == 2 Modified: py/branch/event/py/test2/rep/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/rep/testing/test_collectonly.py Tue Aug 12 18:41:50 2008 @@ -1,6 +1,6 @@ import py from py.__.test2.rep.collectonly import CollectonlyReporter -from py.__.test2 import repevent +from py.__.test2 import event from py.__.test2.testing import suptest def popvalue(stringio): @@ -40,15 +40,15 @@ stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, out=stringio) indent = rep.indent - rep.processevent(repevent.CollectionStart(modcol)) + rep.processevent(event.CollectionStart(modcol)) s = popvalue(stringio) assert s == "" item = modcol.join("test_func") - rep.processevent(repevent.ItemStart(item)) + rep.processevent(event.ItemStart(item)) s = popvalue(stringio) assert s == " " - rep.processevent(repevent.CollectionReport(modcol, [], passed="")) + rep.processevent(event.CollectionReport(modcol, [], passed="")) assert rep.indent == indent def test_collectonly_skipped_module(self): Modified: py/branch/event/py/test2/rep/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_rest.py (original) +++ py/branch/event/py/test2/rep/testing/test_rest.py Tue Aug 12 18:41:50 2008 @@ -8,7 +8,7 @@ from py.__.test2.testing.test_reporter import AbstractTestReporter,\ DummyChannel -from py.__.test2 import repevent +from py.__.test2 import event from py.__.test2.dsession.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * from py.__.test2.dsession.hostmanage import Host @@ -44,7 +44,7 @@ self.config.option.verbose = False def test_report_SendItem(self): - event = repevent.SendItem(item='foo/bar.py', channel=ch) + event = event.SendItem(item='foo/bar.py', channel=ch) reporter.report(event) assert stdout.getvalue() == '' stdout.seek(0) @@ -55,7 +55,7 @@ 'localhost\n\n') def test_report_HostRSyncing(self): - event = repevent.HostRSyncing(Host('localhost:/foo/bar'), "a", + event = event.HostRSyncing(Host('localhost:/foo/bar'), "a", "b", False) reporter.report(event) assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' @@ -64,13 +64,13 @@ def test_report_HostRSyncRootReady(self): h = Host('localhost') reporter.hosts_to_rsync = 1 - reporter.report(repevent.HostGatewayReady(h, ["a"])) - event = repevent.HostRSyncRootReady(h, "a") + reporter.report(event.HostGatewayReady(h, ["a"])) + event = event.HostRSyncRootReady(h, "a") reporter.report(event) assert stdout.getvalue() == '::\n\n localhost: READY\n\n' def test_report_TestStarted(self): - event = repevent.TestStarted([Host('localhost'), + event = event.TestStarted([Host('localhost'), Host('foo.com')], "aa", ["a", "b"]) reporter.report(event) @@ -92,7 +92,7 @@ return ['package', 'foo', 'bar.py'] parent = Container(parent=None, fspath=py.path.local('.')) - event = repevent.ItemStart(item=FakeModule(parent)) + event = event.ItemStart(item=FakeModule(parent)) reporter.report(event) assert stdout.getvalue() == """\ Testing module foo/bar.py (2 items) @@ -114,7 +114,7 @@ def test_ItemFinish_PASSED(self): outcome = SerializableOutcome() item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = repevent.ItemFinish(channel=ch, outcome=outcome, item=item) + event = event.ItemFinish(channel=ch, outcome=outcome, item=item) reporter.report(event) assert stdout.getvalue() == ('* localhost\\: **PASSED** ' 'foo.py/bar()/baz\n\n') @@ -122,7 +122,7 @@ def test_ItemFinish_SKIPPED(self): outcome = SerializableOutcome(skipped="reason") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = repevent.ItemFinish(channel=ch, outcome=outcome, item=item) + event = event.ItemFinish(channel=ch, outcome=outcome, item=item) reporter.report(event) assert stdout.getvalue() == ('* localhost\\: **SKIPPED** ' 'foo.py/bar()/baz\n\n') @@ -130,7 +130,7 @@ def test_ItemFinish_FAILED(self): outcome = SerializableOutcome(excinfo="xxx") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = repevent.ItemFinish(channel=ch, outcome=outcome, item=item) + event = event.ItemFinish(channel=ch, outcome=outcome, item=item) reporter.report(event) assert stdout.getvalue() == """\ * localhost\: **FAILED** `traceback0`_ foo.py/bar()/baz @@ -162,7 +162,7 @@ parent = Container(parent=None, fspath=py.path.local('.')) item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz'], parent=parent, fspath=py.path.local('foo')) - event = repevent.ItemFinish(channel=ch, outcome=outcome, + event = event.ItemFinish(channel=ch, outcome=outcome, item=item) reporter.report(event) reporter.timestart = 10 @@ -175,10 +175,10 @@ assert out.find('') > -1 def test_skips(self): - class FakeOutcome(Container, repevent.ItemFinish): + class FakeOutcome(Container, event.ItemFinish): pass - class FakeTryiter(Container, repevent.DeselectedItem): + class FakeTryiter(Container, event.DeselectedItem): pass reporter.skips() @@ -200,7 +200,7 @@ """ def test_failures(self): - class FakeOutcome(Container, repevent.ItemFinish): + class FakeOutcome(Container, event.ItemFinish): pass parent = Container(parent=None, fspath=py.path.local('.')) Modified: py/branch/event/py/test2/rep/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/rep/testing/test_terminal.py Tue Aug 12 18:41:50 2008 @@ -1,6 +1,6 @@ import py from py.__.test2.rep.terminal import TerminalReporter -from py.__.test2 import repevent +from py.__.test2 import event #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report from test_collectonly import InlineCollect, popvalue, assert_stringio_contains_lines @@ -34,8 +34,8 @@ rep.processevent(ev) s = popvalue(stringio) assert s.find("test_pass_skip_fail.py .sF") != -1 - rep.processevent(repevent.SessionStart(None)) - rep.processevent(repevent.SessionFinish(None)) + rep.processevent(event.SessionStart(None)) + rep.processevent(event.SessionFinish(None)) assert_stringio_contains_lines(stringio, [ " def test_func():", "> assert 0", @@ -53,8 +53,8 @@ s = popvalue(stringio) print s assert s.find("test_collect_fail.py - ImportError: No module named") != -1 - rep.processevent(repevent.SessionStart(None)) - rep.processevent(repevent.SessionFinish(None)) + rep.processevent(event.SessionStart(None)) + rep.processevent(event.SessionFinish(None)) assert_stringio_contains_lines(stringio, [ "> import xyz", "E ImportError: No module named xyz" @@ -65,7 +65,7 @@ stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) excinfo = py.test2.raises(ValueError, "raise ValueError('hello')") - rep.processevent(repevent.InternalException(excinfo)) + rep.processevent(event.InternalException(excinfo)) s = popvalue(stringio) assert s.find("InternalException:") != -1 @@ -77,10 +77,10 @@ stringio = py.std.cStringIO.StringIO() host1 = Host("localhost") rep = TerminalReporter(modcol._config, file=stringio) - rep.processevent(repevent.HostGatewayReady(host1, None)) + rep.processevent(event.HostGatewayReady(host1, None)) s = popvalue(stringio) assert s.find("HostGatewayReady") != -1 - rep.processevent(repevent.HostDown(host1, "myerror")) + rep.processevent(event.HostDown(host1, "myerror")) s = popvalue(stringio) assert s.find("HostDown") != -1 assert s.find("myerror") != -1 Modified: py/branch/event/py/test2/rep/web.py ============================================================================== --- py/branch/event/py/test2/rep/web.py (original) +++ py/branch/event/py/test2/rep/web.py Tue Aug 12 18:41:50 2008 @@ -15,7 +15,7 @@ import py from py.__.test2.dsession.dsession import RSession -from py.__.test2 import repevent +from py.__.test2 import event from py.__.test2 import collect from py.__.test2.dsession.webdata import json @@ -207,7 +207,7 @@ self.end_event.set() return {} # some dispatcher here - if isinstance(event, repevent.ItemFinish): + if isinstance(event, event.ItemFinish): args = {} outcome = event.outcome for key, val in outcome.__dict__.iteritems(): @@ -235,22 +235,22 @@ args['hostkey'] = event.channel.gateway.host.hostid else: args['hostkey'] = '' - elif isinstance(event, repevent.ItemStart): + elif isinstance(event, event.ItemStart): args = add_item(event) - elif isinstance(event, repevent.TestSessionFinish): + elif isinstance(event, event.TestSessionFinish): args = {} args['run'] = str(self.all) args['fails'] = str(len(self.fail_reasons)) args['skips'] = str(len(self.skip_reasons)) - elif isinstance(event, repevent.SendItem): + elif isinstance(event, event.SendItem): args = add_item(event) args['hostkey'] = event.channel.gateway.host.hostid - elif isinstance(event, repevent.HostRSyncRootReady): + elif isinstance(event, event.HostRSyncRootReady): self.ready_hosts[event.host] = True args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid} - elif isinstance(event, repevent.FailedTryiter): + elif isinstance(event, event.FailedTryiter): args = add_item(event) - elif isinstance(event, repevent.DeselectedItem): + elif isinstance(event, event.DeselectedItem): args = add_item(event) args['reason'] = str(event.excinfo.value) else: Deleted: /py/branch/event/py/test2/repevent.py ============================================================================== --- /py/branch/event/py/test2/repevent.py Tue Aug 12 18:41:50 2008 +++ (empty file) @@ -1,137 +0,0 @@ -""" - test collection and execution events -""" - -import py -import time -from py.__.test2.outcome import Skipped -from py.__.test.outcome import Skipped as Skipped2 - -validoutcomes = ("passed skipped failed failed_setup failed_collection " - "failed_teardown failed_crashed").split() - -class BaseEvent(object): - def __repr__(self): - l = ["%s=%s" %(key, value) - for key, value in self.__dict__.items()] - return "<%s %s>" %(self.__class__.__name__, " ".join(l),) - -def timestamp(): - return time.time() - -class NOP(BaseEvent): - pass - -# ---------------------------------------------------------------------- -# Basic Live Reporting Events -# ---------------------------------------------------------------------- - -class SessionStart(BaseEvent): - def __init__(self, session): - self.session = session - self.timestart = time.time() - -class SessionFinish(BaseEvent): - def __init__(self, session, exitstatus=0): - self.session = session - self.exitstatus = exitstatus - self.timeend = time.time() - -class InternalException(BaseEvent): - def __init__(self, excinfo=None): - if excinfo is None: - excinfo = py.code.ExceptionInfo() - self.excinfo = excinfo - -# ---------------------------------------------------------------------- -# Events related to collecting and executing test Items -# ---------------------------------------------------------------------- - -class ItemStart(BaseEvent): - def __init__(self, item): - self.item = item - self.time = timestamp() - -class Deselected(BaseEvent): - def __init__(self, items): - self.items = items - - -class BaseReport(BaseEvent): - failed = passed = skipped = None - def __init__(self, colitem, **kwargs): - self.colitem = colitem - assert len(kwargs) == 1, kwargs - name, value = kwargs.items()[0] - setattr(self, name, True) - self.outcome = value - - def toterminal(self, out): - longrepr = self.outcome.longrepr - if hasattr(longrepr, 'toterminal'): - longrepr.toterminal(out) - else: - out.line(str(longrepr)) - -class ItemTestReport(BaseReport): - """ Test Execution Report. """ - -class CollectionStart(BaseEvent): - def __init__(self, collector): - self.collector = collector - -class CollectionReport(BaseReport): - """ Collection Report. """ - def __init__(self, colitem, result, **kwargs): - super(CollectionReport, self).__init__(colitem, **kwargs) - self.result = result - -# ---------------------------------------------------------------------- -# Distributed Testing Events -# ---------------------------------------------------------------------- - -class RescheduleItems(BaseEvent): - def __init__(self, items): - self.items = items - -class HostGatewayReady(BaseEvent): - def __init__(self, host, roots): - self.host = host - self.roots = roots - -class HostUp(BaseEvent): - def __init__(self, host, info): - self.host = host - self.info = info - -class HostDown(BaseEvent): - def __init__(self, host, error=None): - self.host = host - self.error = error - -class SendItem(BaseEvent): - def __init__(self, host, item): - self.item = item - self.host = host - - -# -# events related to rsyncing -# - -class HostRSyncing(BaseEvent): - def __init__(self, host, root, remotepath, synced): - self.host = host - self.root = root - self.remotepath = remotepath - self.synced = synced - -class RsyncFinished(BaseEvent): - def __init__(self): - self.time = timestamp() - -class HostRSyncRootReady(BaseEvent): - def __init__(self, host, root): - self.host = host - self.root = root - Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Tue Aug 12 18:41:50 2008 @@ -8,7 +8,7 @@ import py, os, sys -from py.__.test2 import repevent +from py.__.test2 import event from py.__.test2.outcome import Skipped, Exit from py.__.test.outcome import Skipped as Skipped2 from py.__.test2.dsession.mypickle import ImmutablePickler @@ -77,7 +77,7 @@ self.pdb(excinfo) else: kw = {'passed': OutcomeRepr(when, '.', "")} - return repevent.ItemTestReport(self.colitem, **kw) + return event.ItemTestReport(self.colitem, **kw) class CollectorRunner(RobustRun): def setup(self): @@ -91,7 +91,7 @@ kw = self.getkw(when, excinfo, outerr) else: kw = {'passed': OutcomeRepr(when, '', "")} - return repevent.CollectionReport(self.colitem, res, **kw) + return event.CollectionReport(self.colitem, res, **kw) class SkipRunner(RobustRun): def __init__(self, colitem, msg): @@ -105,7 +105,7 @@ def makereport(self, res, when, excinfo, outerr): assert excinfo kw = self.getkw(when, excinfo, outerr) - return repevent.ItemTestReport(self.colitem, **kw) + return event.ItemTestReport(self.colitem, **kw) NORESULT = object() # @@ -162,4 +162,4 @@ ("%s:%s: CRASHED with signal %d" %(path, lineno, result.signal)), ] outcomerepr = OutcomeRepr(when="???", shortrepr="X", longrepr=longrepr) - return repevent.ItemTestReport(item, failed=outcomerepr) + return event.ItemTestReport(item, failed=outcomerepr) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Tue Aug 12 18:41:50 2008 @@ -6,8 +6,8 @@ """ import py -from py.__.test2 import repevent -from py.__.test2.eventbus import EventBus +from py.__.test2 import event +from py.__.test2.event import EventBus import py.__.test2.custompdb # used for genitems() @@ -62,11 +62,11 @@ if next._config.option.keyword_oneshot: keywordexpr = None else: - self.bus.notify(repevent.ItemStart(next)) + self.bus.notify(event.ItemStart(next)) yield next else: assert isinstance(next, Collector) - self.bus.notify(repevent.CollectionStart(next)) + self.bus.notify(event.CollectionStart(next)) ev = basic_collect_report(next) if ev.passed: for x in self.genitems(ev.result, keywordexpr): @@ -82,19 +82,19 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ self.reporter = self.config.initreporter(self.bus) - self.bus.notify(repevent.SessionStart(self)) + self.bus.notify(event.SessionStart(self)) self._failurelist = [] self.bus.subscribe(self._processfailures) def _processfailures(self, ev): - if isinstance(ev, repevent.BaseReport) and ev.failed: + if isinstance(ev, event.BaseReport) and ev.failed: self._failurelist.append(ev) if self.config.option.exitfirst: self.shouldstop = True def sessionfinishes(self, exitstatus=0): """ teardown any resources after a test run. """ - self.bus.notify(repevent.SessionFinish(self, exitstatus=exitstatus)) + self.bus.notify(event.SessionFinish(self, exitstatus=exitstatus)) self.bus.unsubscribe(self._processfailures) self.reporter.deactivate() return self._failurelist @@ -114,7 +114,7 @@ except KeyboardInterrupt: exitstatus = 2 except: - self.bus.notify(repevent.InternalException()) + self.bus.notify(event.InternalException()) exitstatus = 3 failures = self.sessionfinishes(exitstatus=exitstatus) return failures Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Tue Aug 12 18:41:50 2008 @@ -12,7 +12,7 @@ eventappender(config): for getting all events in a list: """ import py -from py.__.test2 import repevent +from py.__.test2 import event from fnmatch import fnmatch def eventappender(session): @@ -70,7 +70,7 @@ passed = [] skipped = [] failed = [] - for ev in self.get(repevent.ItemTestReport): + for ev in self.get(event.ItemTestReport): if ev.passed: passed.append(ev) elif ev.skipped: @@ -90,7 +90,7 @@ def getfailedcollections(self): l = [] - for ev in self.get(repevent.CollectionReport): + for ev in self.get(event.CollectionReport): if ev.failed: l.append(ev) return l @@ -98,7 +98,7 @@ def getreport(self, inamepart): """ return a testreport whose dotted import path matches """ l = [] - for rep in self.get(repevent.ItemTestReport): + for rep in self.get(event.ItemTestReport): if inamepart in rep.colitem.listnames(): l.append(rep) if len(l) != 1: @@ -122,7 +122,7 @@ tfile = makeuniquepyfile(source) sorter = events_from_cmdline([tfile, "--tb=%s" %tb]) # get failure base info - failevents = sorter.get(repevent.ItemTestReport) + failevents = sorter.get(event.ItemTestReport) assert len(failevents) == 1 return failevents[0],tfile Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Tue Aug 12 18:41:50 2008 @@ -1,6 +1,6 @@ from __future__ import generators import py -from py.__.test2 import repevent, outcome +from py.__.test2 import event, outcome import setupdata, suptest from py.__.test2.conftesthandle import Conftest from py.__.test2.collect import SetupState @@ -329,7 +329,7 @@ items, events = self._genitems() assert len(items) == 0 skips = [x for x in events - if isinstance(x, repevent.CollectionReport) + if isinstance(x, event.CollectionReport) and x.colitem.name == 'subdir'] assert len(skips) == 1 assert skips[0].skipped @@ -344,7 +344,7 @@ self.tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") items, events = self._genitems() failures = [x for x in events - if isinstance(x, repevent.CollectionReport) + if isinstance(x, event.CollectionReport) and x.failed] assert len(failures) == 1 ev = failures[0] @@ -356,11 +356,11 @@ py.test2.skip('xxx') """)) items, events = self._genitems() - funcs = [x for x in items if isinstance(x, repevent.ItemStart)] + funcs = [x for x in items if isinstance(x, event.ItemStart)] assert not funcs assert not items l = [x for x in events - if isinstance(x, repevent.CollectionReport) + if isinstance(x, event.CollectionReport) and x.colitem.name == 'test_module.py'] assert len(l) == 1 ev = l[0] Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Tue Aug 12 18:41:50 2008 @@ -3,7 +3,7 @@ from py.__.test2.config import gettopdir import suptest, setupdata -from py.__.test2 import repevent +from py.__.test2 import event def getcolitems(config): return [config.getfsnode(arg) for arg in config.args] @@ -201,7 +201,7 @@ config = py.test2.config._reparse([self.tmpdir, '--eventlog=%s' % eventlog]) session = config.initsession() - session.bus.notify(repevent.SessionStart(session)) + session.bus.notify(event.SessionStart(session)) s = eventlog.read() assert s.find("SessionStart") != -1 Copied: py/branch/event/py/test2/testing/test_event.py (from r57206, py/branch/event/py/test2/testing/test_eventbus.py) ============================================================================== --- py/branch/event/py/test2/testing/test_eventbus.py (original) +++ py/branch/event/py/test2/testing/test_event.py Tue Aug 12 18:41:50 2008 @@ -1,5 +1,8 @@ import py -from py.__.test2.eventbus import EventBus +from py.__.test2.event import EventBus +from py.__.test2 import event +import suptest +from py.__.code.testing.test_excinfo import TWMock class TestEventBus: def test_simple(self): @@ -32,3 +35,24 @@ bus.notify(2) assert l == [1] + +class TestItemTestReport(object): + def test_toterminal(self): + sorter = suptest.events_run_example("filetest.py") + reports = sorter.get(event.ItemTestReport) + ev = reports[0] + assert ev.failed + twmock = TWMock() + ev.toterminal(twmock) + assert twmock.lines + twmock = TWMock() + ev.outcome.longrepr = "hello" + ev.toterminal(twmock) + assert twmock.lines[0] == "hello" + assert not twmock.lines[1:] + + ##assert ev.repr_run.find("AssertionError") != -1 + filepath = ev.colitem.fspath + #filepath , modpath = ev.itemrepr_path + assert str(filepath).endswith("filetest.py") + #assert modpath.endswith("filetest.test_one") Deleted: /py/branch/event/py/test2/testing/test_eventbus.py ============================================================================== --- /py/branch/event/py/test2/testing/test_eventbus.py Tue Aug 12 18:41:50 2008 +++ (empty file) @@ -1,34 +0,0 @@ -import py -from py.__.test2.eventbus import EventBus - -class TestEventBus: - def test_simple(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(1) - bus.notify(2) - bus.notify(3) - assert l == [1,2,3] - - def test_multi_sub(self): - bus = EventBus() - l1 = [] - l2 = [] - bus.subscribe(l1.append) - bus.subscribe(l2.append) - bus.notify(1) - bus.notify(2) - bus.notify(3) - assert l1 == [1,2,3] - assert l2 == [1,2,3] - - def test_remove(self): - bus = EventBus() - l = [] - bus.subscribe(l.append) - bus.notify(1) - bus.unsubscribe(l.append) - bus.notify(2) - assert l == [1] - Modified: py/branch/event/py/test2/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test2/testing/test_repevent.py Tue Aug 12 18:41:50 2008 @@ -1,13 +1,15 @@ -from py.__.test2 import repevent +from py.__.test2 import event import setupdata, suptest from py.__.code.testing.test_excinfo import TWMock + + class TestItemTestReport(object): def test_toterminal(self): sorter = suptest.events_run_example("filetest.py") - reports = sorter.get(repevent.ItemTestReport) + reports = sorter.get(event.ItemTestReport) ev = reports[0] assert ev.failed twmock = TWMock() Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Tue Aug 12 18:41:50 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2 import repevent +from py.__.test2 import event import suptest, setupdata def setup_module(mod): @@ -169,11 +169,11 @@ sorter = suptest.events_from_cmdline([p.dirpath(), '--collectonly']) - itemstarted = sorter.get(repevent.ItemStart) + itemstarted = sorter.get(event.ItemStart) assert len(itemstarted) == 3 - assert not sorter.get(repevent.ItemTestReport) - started = sorter.get(repevent.CollectionStart) - finished = sorter.get(repevent.CollectionReport) + assert not sorter.get(event.ItemTestReport) + started = sorter.get(event.CollectionStart) + finished = sorter.get(event.CollectionReport) assert len(started) == len(finished) assert len(started) == 8 colfail = [x for x in finished if x.failed] From hpk at codespeak.net Tue Aug 12 18:49:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 18:49:04 +0200 (CEST) Subject: [py-svn] r57209 - in py/branch/event/py/test2: . rep report report/testing Message-ID: <20080812164904.F30CB16A066@codespeak.net> Author: hpk Date: Tue Aug 12 18:49:02 2008 New Revision: 57209 Added: py/branch/event/py/test2/report/ - copied from r57208, py/branch/event/py/test2/rep/ Removed: py/branch/event/py/test2/rep/ Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/report/collectonly.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_basereporter.py py/branch/event/py/test2/report/testing/test_collectonly.py py/branch/event/py/test2/report/testing/test_terminal.py Log: rename rep -> report. (it's great to have tests so that you can easily rename :) Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Tue Aug 12 18:49:02 2008 @@ -145,9 +145,9 @@ def initreporter(self, bus): if self.option.collectonly: - from py.__.test2.rep.collectonly import Reporter + from py.__.test2.report.collectonly import Reporter else: - from py.__.test2.rep.terminal import Reporter + from py.__.test2.report.terminal import Reporter rep = Reporter(self, bus=bus) return rep Modified: py/branch/event/py/test2/report/collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/collectonly.py (original) +++ py/branch/event/py/test2/report/collectonly.py Tue Aug 12 18:49:02 2008 @@ -2,7 +2,7 @@ """ --collectonly session, not to spread logic all over the place """ import py -from py.__.test2.rep.base import BaseReporter +from py.__.test2.report.base import BaseReporter from py.__.test2.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/rep/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Tue Aug 12 18:49:02 2008 @@ -1,10 +1,10 @@ import py import sys from py.__.test2 import event -from py.__.test2.rep.base import BaseReporter +from py.__.test2.report.base import BaseReporter from py.__.test2.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped -from py.__.test2.rep.base import getrelpath, repr_pythonversion +from py.__.test2.report.base import getrelpath, repr_pythonversion class TerminalReporter(BaseReporter): def __init__(self, config, file=None, bus=None): Modified: py/branch/event/py/test2/report/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/report/testing/test_basereporter.py Tue Aug 12 18:49:02 2008 @@ -1,9 +1,9 @@ import py -from py.__.test2.rep.base import BaseReporter +from py.__.test2.report.base import BaseReporter from py.__.test2.event import EventBus from py.__.test2 import event from py.__.test2.runner import OutcomeRepr -from py.__.test2.rep.base import getrelpath, getmodpath, repr_pythonversion +from py.__.test2.report.base import getrelpath, getmodpath, repr_pythonversion from py.__.test2.testing import setupdata import sys Modified: py/branch/event/py/test2/report/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/report/testing/test_collectonly.py Tue Aug 12 18:49:02 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2.rep.collectonly import CollectonlyReporter +from py.__.test2.report.collectonly import CollectonlyReporter from py.__.test2 import event from py.__.test2.testing import suptest Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/rep/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Tue Aug 12 18:49:02 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2.rep.terminal import TerminalReporter +from py.__.test2.report.terminal import TerminalReporter from py.__.test2 import event #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report From hpk at codespeak.net Tue Aug 12 18:50:50 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 18:50:50 +0200 (CEST) Subject: [py-svn] r57210 - py/branch/event/py/test2 Message-ID: <20080812165050.CD3F416A066@codespeak.net> Author: hpk Date: Tue Aug 12 18:50:50 2008 New Revision: 57210 Removed: py/branch/event/py/test2/async.py Log: this was a scratchbook for dsession but is superseded now. Deleted: /py/branch/event/py/test2/async.py ============================================================================== --- /py/branch/event/py/test2/async.py Tue Aug 12 18:50:50 2008 +++ (empty file) @@ -1,104 +0,0 @@ -""" - - EXPERIMENTAL dsession session (for dist/non-dist unification) - -""" - -import py -from py.__.test2 import event -from py.__.test2.event import EventBus -import py.__.test2.custompdb - -# used for genitems() -from py.__.test2.outcome import Exit -Item = (py.test.collect.Item, py.test2.collect.Item) -Collector = (py.test.collect.Collector, py.test2.collect.Collector) -from runner import basic_run_report, basic_collect_report, skip_report -import Queue - -class DSession(object): - """ - Session drives the collection and running of tests - and generates test events for reporters. - """ - def __init__(self, config): - self.config = config - self.bus = EventBus() - self.eventqueue = [] - self._colnum = self._testnum = 0 - - def fixoptions(self): - """ check, fix and determine conflicting options. """ - option = self.config.option - if option.runbrowser and not option.startserver: - #print "--runbrowser implies --startserver" - option.startserver = True - if self.config.getvalue("dist_boxed") and option.dist: - option.boxed = True - # conflicting options - if option.looponfailing and option.usepdb: - raise ValueError, "--looponfailing together with --pdb not supported." - if option.looponfailing and option.dist: - raise ValueError, "--looponfailing together with --dist not supported." - if option.executable and option.usepdb: - raise ValueError, "--exec together with --pdb not supported." - if option.keyword_oneshot and not option.keyword: - raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" - - def main(self): - self.sessionstarts() - colitems = [self.config.getfsnode(arg) for arg in self.config.args] - shouldstop = False - try: - while colitems and not shouldstop: - colitem = colitems.pop(0) - if isinstance(colitem, Item): - self.sendtest(colitem) - else: - self.sendcol(colitem) - new, shouldstop = self.receive() - colitems.extend(new) - finally: - self.sessionfinishes() - - def receive(self): - ev = self.getevent() # with timeout - colitems = [] - shouldstop = False - if isinstance(ev, event.CollectionReport): - self._colnum -= 1 - if ev.passed: - colitems.extend(ev.result) - else: - self.bus.notify(ev) - elif isinstance(ev, event.ItemTestReport): - self._testnum -= 1 - self.bus.notify(ev) - if ev.failed and self.config.option.exitfirst: - shouldstop = True - return colitems, shouldstop - - def getevent(self): - return self.eventqueue.pop() - - def sendtest(self, colitem): - self._testnum += 1 - ev = basic_run_report(colitem) - self.eventqueue.append(ev) - - def sendcol(self, colitem): - self._colnum += 1 - ev = basic_collect_report(colitem) - self.eventqueue.append(ev) - - def sessionstarts(self): - """ setup any neccessary resources ahead of the test run. """ - self.reporter = self.config.getreporter() - self.reporter.activate(self.bus) - self.bus.notify(event.SessionStart(self)) - - def sessionfinishes(self): - """ teardown any resources after a test run. """ - self.bus.notify(event.SessionFinish(self)) - self.reporter.deactivate(self.bus) - From hpk at codespeak.net Tue Aug 12 18:55:49 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 18:55:49 +0200 (CEST) Subject: [py-svn] r57211 - py/branch/event/py/test2/dsession Message-ID: <20080812165549.B991816A0B4@codespeak.net> Author: hpk Date: Tue Aug 12 18:55:49 2008 New Revision: 57211 Modified: py/branch/event/py/test2/dsession/dsession.py Log: remove unused imports Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Tue Aug 12 18:55:49 2008 @@ -6,15 +6,11 @@ import py from py.__.test2 import event -from py.__.test2.event import EventBus import py.__.test2.custompdb from py.__.test2.dsession.hostmanage import HostManager - -# used for genitems() -from py.__.test2.outcome import Exit Item = (py.test.collect.Item, py.test2.collect.Item) Collector = (py.test.collect.Collector, py.test2.collect.Collector) -from py.__.test2.runner import basic_run_report, basic_collect_report, skip_report +from py.__.test2.runner import basic_run_report, basic_collect_report from py.__.test2.session import Session import Queue From hpk at codespeak.net Tue Aug 12 19:34:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 19:34:29 +0200 (CEST) Subject: [py-svn] r57212 - py/branch/event/py/test2/testing Message-ID: <20080812173429.3ADFA16A0D9@codespeak.net> Author: hpk Date: Tue Aug 12 19:34:28 2008 New Revision: 57212 Modified: py/branch/event/py/test2/testing/acceptance_test.py Log: using pexpect to test basic pdb interaction Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Tue Aug 12 19:34:28 2008 @@ -1,6 +1,10 @@ import py from suptest import assert_lines_contain_lines, FileCreation +pydir = py.path.local(py.__file__).dirpath() +pytestpath = pydir.join("bin", "py.test2") +EXPECTTIMEOUT=1.0 + def setup_module(mod): mod.modtmpdir = py.test2.ensuretemp(mod.__name__) @@ -318,46 +322,27 @@ "*KEYBOARD INTERRUPT*", "*0/1 passed*", ]) + + def getspawn(self): + try: + import pexpect + except ImportError: + py.test.skip("need pexpect") + return pexpect.spawn - def test_looponfailing_looping(self): - py.test.skip("thought needed to nicely test --looponfailing") - py.test.skip("xxx check events") - o = tmpdir.ensure('looponfailing', dir=1) - tfile = o.join('test_looponfailing.py') - tfile.write(py.code.Source(""" + def test_pdb_interaction(self): + spawn = self.getspawn() + self.makepyfile(test_one=""" def test_1(): assert 1 == 0 - """)) - print py.std.sys.executable - config = py.test2.config._reparse(['--looponfailing', str(o)]) - session = config.initsession() - failures = session.main() - - cls = config._getsessionclass() - out = py.std.Queue.Queue() - session = cls(config, out.put) - pool = py._thread.WorkerPool() - reply = pool.dispatch(session.main) - while 1: - s = out.get(timeout=5.0) - if s.find('1 failed') != -1: - break - print s - else: - py.test2.fail("did not see test_1 failure") - # XXX we would like to have a cleaner way to finish - try: - reply.get(timeout=5.0) - except IOError, e: - assert str(e).lower().find('timeout') != -1 - - def test_invoking_pdb(self): - py.test.skip("implement test for interaction with pdb") - l = [] - def testfunc(): - raise ValueError(7) - def mypdb(runinfo): - l.append(runinfo) - testrep = self.run(testfunc, pdb=mypdb) - assert len(l) == 1 - assert l[0].excinfo.exconly().find("ValueError: 7") != -1 + """) + + child = spawn("%s %s --pdb test_one.py" % (py.std.sys.executable, + pytestpath)) + child.expect("(Pdb)", timeout=EXPECTTIMEOUT) + child.sendline("l") + child.expect(".*def test_1.*", timeout=EXPECTTIMEOUT) + child.sendeof() + child.expect("failures: 1", timeout=EXPECTTIMEOUT) + child.wait() + From hpk at codespeak.net Tue Aug 12 21:07:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 21:07:11 +0200 (CEST) Subject: [py-svn] r57213 - py/branch/event/py/test2/testing Message-ID: <20080812190711.AED2516A109@codespeak.net> Author: hpk Date: Tue Aug 12 21:07:08 2008 New Revision: 57213 Modified: py/branch/event/py/test2/testing/acceptance_test.py Log: add a skipped acceptance test for --looponfailing Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Tue Aug 12 21:07:08 2008 @@ -14,7 +14,7 @@ self.outlines = outlines self.errlines = errlines -class TestPyTest(FileCreation): +class AcceptBase(FileCreation): def runpytest(self, *args): pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") cmdargs = [py.std.sys.executable, pytestcmd] + list(args) @@ -32,12 +32,13 @@ return Result(ret, out, err) def setup_method(self, method): - super(TestPyTest, self).setup_method(method) + super(AcceptBase, self).setup_method(method) self.old = self.tmpdir.chdir() def teardown_method(self, method): self.old.chdir() +class TestPyTest(AcceptBase): def test_assertion_magic(self): p = self.makepyfile(test_one=""" def test_this(): @@ -323,13 +324,14 @@ "*0/1 passed*", ]) +class TestInteractive(AcceptBase): def getspawn(self): try: import pexpect except ImportError: py.test.skip("need pexpect") return pexpect.spawn - + def test_pdb_interaction(self): spawn = self.getspawn() self.makepyfile(test_one=""" @@ -346,3 +348,26 @@ child.expect("failures: 1", timeout=EXPECTTIMEOUT) child.wait() + + def test_simple_looponfailing_interaction(self): + spawn = self.getspawn() + test_one = self.makepyfile(test_one=""" + def test_1(): + assert 1 == 0 + """) + + child = spawn("%s %s --looponfailing test_one.py" % (py.std.sys.executable, + str(pytestpath)[:-1])) + child.timeout = EXPECTTIMEOUT + child.expect("assert 1 == 0") + child.expect("test_one.py:") + child.expect("failures: 1") + + test_one.write(py.code.Source(""" + def test_1(): + assert 1 == 1 + """)) + child.expect("file change detected", timeout=2.0) + child.expect("failures: no failures :)") + child.kill() + From hpk at codespeak.net Tue Aug 12 22:02:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 22:02:37 +0200 (CEST) Subject: [py-svn] r57214 - in py/branch/event/py/test2: . dsession dsession/testing testing Message-ID: <20080812200237.C73E016A168@codespeak.net> Author: hpk Date: Tue Aug 12 22:02:35 2008 New Revision: 57214 Modified: py/branch/event/py/test2/cmdline.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/acceptance_test.py Log: fix handling of crashing tests (do not reschedule beause it is too likely to also crash other hosts) Modified: py/branch/event/py/test2/cmdline.py ============================================================================== --- py/branch/event/py/test2/cmdline.py (original) +++ py/branch/event/py/test2/cmdline.py Tue Aug 12 22:02:35 2008 @@ -11,17 +11,8 @@ config = py.test2.config config.parse(args) session = config.initsession() - try: - failures = session.main() - if failures: - raise SystemExit, 1 - except KeyboardInterrupt: - if not config.option.verbose: - print - print "KeyboardInterrupt (-v to see traceback)" - raise SystemExit, 2 - else: - raise + exitstatus = session.main() + raise SystemExit(exitstatus) def warn_about_missing_assertion(): try: Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Tue Aug 12 22:02:35 2008 @@ -12,6 +12,7 @@ Collector = (py.test.collect.Collector, py.test2.collect.Collector) from py.__.test2.runner import basic_run_report, basic_collect_report from py.__.test2.session import Session +from py.__.test2.runner import OutcomeRepr import Queue @@ -85,6 +86,7 @@ self.sessionstarts() exitstatus = self.loop(colitems) self.sessionfinishes(exitstatus=exitstatus) + return exitstatus def loop_once(self, loopstate): if loopstate.shuttingdown: @@ -104,7 +106,10 @@ self.bus.notify(ev) if isinstance(ev, event.HostDown): pending = self.removehost(ev.host) - colitems.extend(pending) + if pending: + crashitem = pending.pop(0) + self.handle_crashitem(crashitem, ev.host) + colitems.extend(pending) elif isinstance(ev, event.RescheduleItems): colitems.extend(ev.items) loopstate.dowork = False # avoid busywait @@ -167,7 +172,10 @@ def removehost(self, host): self.hosts.remove(host) - return self.host2pending.pop(host) + pending = self.host2pending.pop(host) + for item in pending: + del self.item2host[item] + return pending def triggertesting(self, colitems): colitems = self.filteritems(colitems) @@ -219,6 +227,11 @@ self.queue.put(event.Deselected(deselected)) return remaining + def handle_crashitem(self, item, host): + longrepr = "%r CRASHED THE HOST %r" %(item, host) + outcome = OutcomeRepr(when="execute", shortrepr="c", longrepr=longrepr) + rep = event.ItemTestReport(item, failed=outcome) + self.bus.notify(rep) def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Tue Aug 12 22:02:35 2008 @@ -29,12 +29,14 @@ rep = run(item) session = DSession(item._config) host = Host("localhost") + host.node = MockNode() assert not session.hosts session.addhost(host) assert len(session.hosts) == 1 - session.host2pending[host].append(item) + session.senditems([item]) pending = session.removehost(host) assert pending == [item] + assert item not in session.item2host py.test2.raises(Exception, "session.removehost(host)") def test_senditems_removeitems(self): @@ -133,17 +135,23 @@ ev = event.HostDown(host1, None) session.queue.put(ev) - # call the loop which should come back - # because it doesn't have any hosts left - exitstatus = session.loop([item]) + loopstate = LoopState([item]) + loopstate.dowork = False + session.loop_once(loopstate) dumpqueue(session.queue) - assert exitstatus == session.EXIT_NOHOSTS + assert loopstate.exitstatus == session.EXIT_NOHOSTS def test_hostdown_causes_reschedule_pending(self): - item = self.getitem("def test_func(): pass") + modcol = self.getmodulecol(""" + def test_crash(): + assert 0 + def test_fail(): + x + """) + item1, item2 = [modcol.join(x) for x in modcol.listdir()] # setup a session with two hosts - session = DSession(item._config) + session = DSession(item1._config) host1 = Host("localhost") host1.node = MockNode() session.addhost(host1) @@ -152,14 +160,21 @@ session.addhost(host2) # have one test pending for a host that goes down - session.host2pending[host1].append(item) - ev = event.HostDown(host1, None) + session.senditems([item1, item2]) + host = session.item2host[item1] + ev = event.HostDown(host, None) session.queue.put(ev) + events = [] ; session.bus.subscribe(events.append) loopstate = LoopState([]) session.loop_once(loopstate) - - assert loopstate.colitems == [item] + + assert loopstate.colitems == [item2] # do not reschedule crash item + testrep = [x for x in events if isinstance(x, event.ItemTestReport)][0] + assert testrep.failed + assert testrep.colitem == item1 + assert str(testrep.outcome.longrepr).find("CRASHED") != -1 + assert str(testrep.outcome.longrepr).find(host.hostname) != -1 def test_event_propagation(self): item = self.getitem("def test_func(): pass") Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Tue Aug 12 22:02:35 2008 @@ -116,8 +116,10 @@ except: self.bus.notify(event.InternalException()) exitstatus = 3 - failures = self.sessionfinishes(exitstatus=exitstatus) - return failures + if self._failurelist and exitstatus == 0: + exitstatus=1 + self.sessionfinishes(exitstatus=exitstatus) + return exitstatus def runpdb(self, excinfo): py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Tue Aug 12 22:02:35 2008 @@ -117,6 +117,7 @@ "___* skipped test summary *_", "*conftest.py:2: *3* Skipped: 'test'", ]) + assert result.ret == 0 def test_no_skip_summary_if_failure(self): p1 = self.makepyfile(test_one=""" @@ -130,6 +131,7 @@ """) result = self.runpytest() assert str(result.outlines).find("skip test summary") == -1 + assert result.ret == 1 def test_passes(self): p1 = self.makepyfile(test_one=""" @@ -148,6 +150,7 @@ "test_one.py ..", "* failures: no failures*", ]) + assert result.ret == 0 def test_header_trailer_info(self): p1 = self.makepyfile(test_one=""" @@ -273,6 +276,7 @@ "*1/3 passed + 1 skip*", "*failures: 2*", ]) + assert result.ret == 1 def test_dist_tests_with_crash(self): if not hasattr(py.std.os, 'kill'): @@ -305,9 +309,10 @@ "HostGatewayReady*localhost*", "HostGatewayReady*localhost*", "HostDown*localhost*TERMINATED*", - "*1/3 passed + 1 skip*", - "*failures: 2*", + "*1/4 passed + 1 skip*", + "*failures: 3*", ]) + assert result.ret == 1 def test_keyboard_interrupt(self): p1 = self.makepyfile(test_one=""" @@ -350,6 +355,7 @@ def test_simple_looponfailing_interaction(self): + py.test.skip("implement --looponfailing") spawn = self.getspawn() test_one = self.makepyfile(test_one=""" def test_1(): From hpk at codespeak.net Tue Aug 12 22:42:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 12 Aug 2008 22:42:41 +0200 (CEST) Subject: [py-svn] r57215 - in py/branch/event/py/test2/dsession: . testing Message-ID: <20080812204241.87F0416A170@codespeak.net> Author: hpk Date: Tue Aug 12 22:42:39 2008 New Revision: 57215 Added: py/branch/event/py/test2/dsession/filechange.py - copied, changed from r57209, py/branch/event/py/test2/terminal/remote.py py/branch/event/py/test2/dsession/testing/test_filechange.py Log: adding a helper class for looponfailing to detect filechanges Copied: py/branch/event/py/test2/dsession/filechange.py (from r57209, py/branch/event/py/test2/terminal/remote.py) ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/dsession/filechange.py Tue Aug 12 22:42:39 2008 @@ -1,144 +1,41 @@ -from __future__ import generators import py -from py.__.test2.session import Session -from py.__.test2.outcome import Failed, Passed, Skipped -def checkpyfilechange(rootdir, statcache={}): - """ wait until project files are changed. """ - def fil(p): - return p.ext in ('.py', '.c', '.h') - #fil = lambda x: x.check(fnmatch='*.py') - def rec(p): +class StatRecorder: + def __init__(self, rootdirlist): + self.rootdirlist = rootdirlist + self.statcache = {} + self.check() # snapshot state + + def fil(self, p): + return p.ext in ('.py', '.txt', '.c', '.h') + def rec(self, p): return p.check(dotfile=0) - changed = False - for path in rootdir.visit(fil, rec): - oldstat = statcache.get(path, None) - try: - statcache[path] = curstat = path.stat() - except py.error.ENOENT: - if oldstat: - del statcache[path] - print "# WARN: race condition on", path - else: - if oldstat: - if oldstat.mtime != curstat.mtime or \ - oldstat.size != curstat.size: - changed = True - print "# MODIFIED", path - else: - changed = True - return changed -def getfailureitems(failures): - l = [] - for rootpath, names in failures: - root = py.path.local(rootpath) - if root.check(dir=1): - current = py.test2.collect.Directory(root).Directory(root) - elif root.check(file=1): - current = py.test2.collect.Module(root).Module(root) - # root is fspath of names[0] -> pop names[0] - # slicing works with empty lists - names = names[1:] - while names: - name = names.pop(0) - try: - current = current.join(name) - except NameError: - print "WARNING: could not find %s on %r" %(name, current) - break - else: - l.append(current) - return l + def check(self): + changed = False + statcache = self.statcache + newstat = {} + for rootdir in self.rootdirlist: + for path in rootdir.visit(self.fil, self.rec): + oldstat = statcache.get(path, None) + if oldstat is not None: + del statcache[path] + try: + newstat[path] = curstat = path.stat() + except py.error.ENOENT: + if oldstat: + del statcache[path] + changed = True + else: + if oldstat: + if oldstat.mtime != curstat.mtime or \ + oldstat.size != curstat.size: + changed = True + print "# MODIFIED", path + else: + changed = True + if statcache: + changed = True + self.statcache = newstat + return changed -class RemoteTerminalSession(Session): - def __init__(self, config, file=None): - super(RemoteTerminalSession, self).__init__(config=config) - self._setexecutable() - if file is None: - file = py.std.sys.stdout - self._file = file - self.out = py.io.TerminalWriter(file) - - def _setexecutable(self): - name = self.config.option.executable - if name is None: - executable = py.std.sys.executable - else: - executable = py.path.local.sysfind(name) - assert executable is not None, executable - self.executable = executable - - def main(self): - rootdir = self.config.topdir - wasfailing = False - failures = [] - while 1: - if self.config.option.looponfailing and (failures or not wasfailing): - while not checkpyfilechange(rootdir): - py.std.time.sleep(4.4) - wasfailing = len(failures) - failures = self.run_remote_session(failures) - if not self.config.option.looponfailing: - break - print "#" * 60 - print "# looponfailing: mode: %d failures args" % len(failures) - for root, names in failures: - name = "/".join(names) # XXX - print "Failure at: %r" % (name,) - print "# watching py files below %s" % rootdir - print "# ", "^" * len(str(rootdir)) - return failures - - def _initslavegateway(self): - print "* opening PopenGateway: ", self.executable - topdir = self.config.topdir - return py.execnet.PopenGateway(self.executable), topdir - - def run_remote_session(self, failures): - from py.__.test2.dsession import masterslave - gw, topdir = self._initslavegateway() - channel = gw.remote_exec(_pickle=True, source=""" - print "SLAVE: initializing ..." - from py.__.test2.terminal.remote import slaverun_TerminalSession - from py.__.test2.dsession import masterslave - config = masterslave.receive_and_send_pickled_config(channel) - slaverun_TerminalSession(channel, config) - """, stdout=self.out, stderr=self.out) - try: - print "MASTER: initiating slave terminal session ->" - masterslave.send_and_receive_pickled_config(channel, self.config, topdir) - channel.send(failures) - print "MASTER: send config, topdir=%s" % (topdir,) - try: - return channel.receive() - except channel.RemoteError, e: - print "*" * 70 - print "ERROR while waiting for proper slave startup" - print "*" * 70 - print e - return [] - finally: - gw.exit() - -def slaverun_TerminalSession(channel, config): - """ we run this on the other side. """ - print "SLAVE: received configuration, using topdir:", config.topdir - config.option.session = None - config.option.looponfailing = False - config.option.usepdb = False - config.option.executable = None - failures = channel.receive() - if failures: - config.option.verbose = True - config._coltrails = failures - - print "SLAVE: initsession()" - session = config.initsession() - session.shouldclose = channel.isclosed - print "SLAVE: starting session.main()" - failures = session.main() - failures = [] # XXX needs fixing - #XXX needs fixing - #failures = [ev.trail for ev in failures if hasattr(ev.trail)] - channel.send(failures) Added: py/branch/event/py/test2/dsession/testing/test_filechange.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/dsession/testing/test_filechange.py Tue Aug 12 22:42:39 2008 @@ -0,0 +1,42 @@ +import py +from py.__.test2.dsession.filechange import StatRecorder + +def test_filechange(): + tmp = py.test.ensuretemp("test_filechange") + hello = tmp.ensure("hello.py") + sd = StatRecorder([tmp]) + changed = sd.check() + assert not changed + + hello.write("world") + changed = sd.check() + assert changed + + tmp.ensure("new.py") + changed = sd.check() + assert changed + + tmp.join("new.py").remove() + changed = sd.check() + assert changed + + tmp.join("a", "b", "c.py").ensure() + changed = sd.check() + assert changed + + tmp.join("a", "c.txt").ensure() + changed = sd.check() + assert changed + changed = sd.check() + assert not changed + + tmp.join("a").remove() + changed = sd.check() + assert changed + + + + + + + From hpk at codespeak.net Wed Aug 13 09:10:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 09:10:44 +0200 (CEST) Subject: [py-svn] r57217 - in py/branch/event/py/test2: . dsession dsession/testing Message-ID: <20080813071044.06EEB16A084@codespeak.net> Author: hpk Date: Wed Aug 13 09:10:43 2008 New Revision: 57217 Modified: py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/outcome.py py/branch/event/py/test2/session.py Log: factor exitcodes out into outcome.py Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Wed Aug 13 09:10:43 2008 @@ -13,6 +13,7 @@ from py.__.test2.runner import basic_run_report, basic_collect_report from py.__.test2.session import Session from py.__.test2.runner import OutcomeRepr +from py.__.test2 import outcome import Queue @@ -33,11 +34,6 @@ and generates test events for reporters. """ MAXITEMSPERHOST = 10 - EXIT_OK = 0 - EXIT_TESTSFAILED = 1 - EXIT_INTERRUPTED = 2 - EXIT_INTERNALERROR = 3 - EXIT_NOHOSTS = 4 def __init__(self, config): super(DSession, self).__init__(config=config) @@ -127,7 +123,7 @@ self.triggershutdown() loopstate.shuttingdown = True elif not self.host2pending: - loopstate.exitstatus = self.EXIT_NOHOSTS + loopstate.exitstatus = outcome.EXIT_NOHOSTS def loop_once_shutdown(self, loopstate): # once we are in shutdown mode we dont send @@ -139,9 +135,9 @@ if not self.host2pending: # finished if loopstate.testsfailed: - loopstate.exitstatus = self.EXIT_TESTSFAILED + loopstate.exitstatus = outcome.EXIT_TESTSFAILED else: - loopstate.exitstatus = self.EXIT_OK + loopstate.exitstatus = outcome.EXIT_OK def loop(self, colitems): try: @@ -152,12 +148,12 @@ exitstatus = loopstate.exitstatus break except KeyboardInterrupt: - exitstatus = self.EXIT_INTERRUPTED + exitstatus = outcome.EXIT_INTERRUPTED except: self.bus.notify(event.InternalException()) - exitstatus = self.EXIT_INTERNALERROR + exitstatus = outcome.EXIT_INTERNALERROR if exitstatus == 0 and self._testsfailed: - exitstatus = self.EXIT_TESTSFAILED + exitstatus = outcome.EXIT_TESTSFAILED return exitstatus def triggershutdown(self): Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Wed Aug 13 09:10:43 2008 @@ -3,6 +3,7 @@ from py.__.test2.dsession.hostmanage import Host from py.__.test2.runner import basic_collect_report from py.__.test2 import event +from py.__.test2 import outcome import py def run(item): @@ -90,7 +91,7 @@ def raise_(timeout=None): raise KeyboardInterrupt() session.queue.get = raise_ exitstatus = session.loop([]) - assert exitstatus == session.EXIT_INTERRUPTED + assert exitstatus == outcome.EXIT_INTERRUPTED def test_internalerror(self): item = self.getitem("def test_func(): pass") @@ -98,7 +99,7 @@ def raise_(): raise ValueError() session.queue.get = raise_ exitstatus = session.loop([]) - assert exitstatus == session.EXIT_INTERNALERROR + assert exitstatus == outcome.EXIT_INTERNALERROR def test_rescheduleevent(self): item = self.getitem("def test_func(): pass") @@ -139,7 +140,7 @@ loopstate.dowork = False session.loop_once(loopstate) dumpqueue(session.queue) - assert loopstate.exitstatus == session.EXIT_NOHOSTS + assert loopstate.exitstatus == outcome.EXIT_NOHOSTS def test_hostdown_causes_reschedule_pending(self): modcol = self.getmodulecol(""" @@ -209,12 +210,12 @@ def test_exit_completed_tests_ok(self): item = self.getitem("def test_func(): pass") session, exitstatus = self.runthrough(item) - assert exitstatus == session.EXIT_OK + assert exitstatus == outcome.EXIT_OK def test_exit_completed_tests_fail(self): item = self.getitem("def test_func(): 0/0") session, exitstatus = self.runthrough(item) - assert exitstatus == session.EXIT_TESTSFAILED + assert exitstatus == outcome.EXIT_TESTSFAILED def test_exit_on_first_failing(self): modcol = self.getmodulecol(""" Modified: py/branch/event/py/test2/outcome.py ============================================================================== --- py/branch/event/py/test2/outcome.py (original) +++ py/branch/event/py/test2/outcome.py Wed Aug 13 09:10:43 2008 @@ -115,3 +115,9 @@ return ret +# exitcodes for the command line +EXIT_OK = 0 +EXIT_TESTSFAILED = 1 +EXIT_INTERRUPTED = 2 +EXIT_INTERNALERROR = 3 +EXIT_NOHOSTS = 4 Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Aug 13 09:10:43 2008 @@ -6,7 +6,7 @@ """ import py -from py.__.test2 import event +from py.__.test2 import event, outcome from py.__.test2.event import EventBus import py.__.test2.custompdb @@ -103,7 +103,7 @@ """ main loop for running tests. """ self.shouldstop = False self.sessionstarts() - exitstatus = 0 + exitstatus = outcome.EXIT_OK try: for item in self.collect(): if self.shouldstop: @@ -112,12 +112,12 @@ self.runtest(item) py.test2.collect.Item._setupstate.teardown_all() except KeyboardInterrupt: - exitstatus = 2 + exitstatus = outcome.EXIT_INTERRUPTED except: self.bus.notify(event.InternalException()) - exitstatus = 3 + exitstatus = outcome.EXIT_INTERNALERROR if self._failurelist and exitstatus == 0: - exitstatus=1 + exitstatus = outcome.EXIT_TESTSFAILED self.sessionfinishes(exitstatus=exitstatus) return exitstatus From hpk at codespeak.net Wed Aug 13 09:28:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 09:28:35 +0200 (CEST) Subject: [py-svn] r57218 - py/branch/event/py/test2/dsession Message-ID: <20080813072835.9CF4C16A0E8@codespeak.net> Author: hpk Date: Wed Aug 13 09:28:35 2008 New Revision: 57218 Modified: py/branch/event/py/test2/dsession/hostmanage.py Log: remove dead code Modified: py/branch/event/py/test2/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/hostmanage.py (original) +++ py/branch/event/py/test2/dsession/hostmanage.py Wed Aug 13 09:28:35 2008 @@ -161,39 +161,6 @@ self.session.config, notify) - def wait_for_completion(self, maxtimeout=2.0): - self.queue = py.std.Queue.Queue() - self.session.bus.subscribe(self.queue.put) - #remaining = [] - pending_going_down = [] - while self.hosts: - numitems = 0 - for host in self.hosts: - if host not in pending_going_down: - if not host.node.pending: - # reschedule tests - host.node.shutdown() - pending_going_down.append(host) - else: - numitems += len(host.node.pending) - ev = self.queue.get(timeout=maxtimeout) - if isinstance(ev, event.HostDown): - self.hosts.remove(ev.host) - if ev.host in pending_going_down: - pending_going_down.remove(ev.host) - #XXX remaining.extend(ev.pending) - #else: - # print "ignoring event", ev - #print "items remaining", numitems - - def trysendtest(self, item): - for host in self.hosts: - node = getattr(host, 'node', None) - if node and len(node.pending) < 15: # XXX use config - node.send(item) - return True - - # # helpers # From hpk at codespeak.net Wed Aug 13 10:34:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 10:34:37 +0200 (CEST) Subject: [py-svn] r57219 - in py/branch/event/py/test2/dsession: . testing Message-ID: <20080813083437.AFB5316A058@codespeak.net> Author: hpk Date: Wed Aug 13 10:34:36 2008 New Revision: 57219 Added: py/branch/event/py/test2/dsession/looponfailing.py - copied, changed from r57215, py/branch/event/py/test2/dsession/filechange.py py/branch/event/py/test2/dsession/testing/test_looponfailing.py - copied, changed from r57215, py/branch/event/py/test2/dsession/testing/test_filechange.py Removed: py/branch/event/py/test2/dsession/filechange.py py/branch/event/py/test2/dsession/testing/test_filechange.py Log: move to common helper for looponfailing Deleted: /py/branch/event/py/test2/dsession/filechange.py ============================================================================== --- /py/branch/event/py/test2/dsession/filechange.py Wed Aug 13 10:34:36 2008 +++ (empty file) @@ -1,41 +0,0 @@ -import py - -class StatRecorder: - def __init__(self, rootdirlist): - self.rootdirlist = rootdirlist - self.statcache = {} - self.check() # snapshot state - - def fil(self, p): - return p.ext in ('.py', '.txt', '.c', '.h') - def rec(self, p): - return p.check(dotfile=0) - - def check(self): - changed = False - statcache = self.statcache - newstat = {} - for rootdir in self.rootdirlist: - for path in rootdir.visit(self.fil, self.rec): - oldstat = statcache.get(path, None) - if oldstat is not None: - del statcache[path] - try: - newstat[path] = curstat = path.stat() - except py.error.ENOENT: - if oldstat: - del statcache[path] - changed = True - else: - if oldstat: - if oldstat.mtime != curstat.mtime or \ - oldstat.size != curstat.size: - changed = True - print "# MODIFIED", path - else: - changed = True - if statcache: - changed = True - self.statcache = newstat - return changed - Copied: py/branch/event/py/test2/dsession/looponfailing.py (from r57215, py/branch/event/py/test2/dsession/filechange.py) ============================================================================== --- py/branch/event/py/test2/dsession/filechange.py (original) +++ py/branch/event/py/test2/dsession/looponfailing.py Wed Aug 13 10:34:36 2008 @@ -1,4 +1,5 @@ import py +from py.__.test2 import event class StatRecorder: def __init__(self, rootdirlist): @@ -39,3 +40,19 @@ self.statcache = newstat return changed + +class EventRecorder(object): + def __init__(self, bus): + self.events = [] + self.bus = bus + self.bus.subscribe(self.events.append) + + def getfailures(self): + return [ev for ev in self.events + if isinstance(ev, event.BaseReport) and \ + ev.failed] + def clear(self): + self.events[:] = [] + + def unsubscribe(self): + self.bus.unsubscribe(self.events.append) Deleted: /py/branch/event/py/test2/dsession/testing/test_filechange.py ============================================================================== --- /py/branch/event/py/test2/dsession/testing/test_filechange.py Wed Aug 13 10:34:36 2008 +++ (empty file) @@ -1,42 +0,0 @@ -import py -from py.__.test2.dsession.filechange import StatRecorder - -def test_filechange(): - tmp = py.test.ensuretemp("test_filechange") - hello = tmp.ensure("hello.py") - sd = StatRecorder([tmp]) - changed = sd.check() - assert not changed - - hello.write("world") - changed = sd.check() - assert changed - - tmp.ensure("new.py") - changed = sd.check() - assert changed - - tmp.join("new.py").remove() - changed = sd.check() - assert changed - - tmp.join("a", "b", "c.py").ensure() - changed = sd.check() - assert changed - - tmp.join("a", "c.txt").ensure() - changed = sd.check() - assert changed - changed = sd.check() - assert not changed - - tmp.join("a").remove() - changed = sd.check() - assert changed - - - - - - - Copied: py/branch/event/py/test2/dsession/testing/test_looponfailing.py (from r57215, py/branch/event/py/test2/dsession/testing/test_filechange.py) ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_filechange.py (original) +++ py/branch/event/py/test2/dsession/testing/test_looponfailing.py Wed Aug 13 10:34:36 2008 @@ -1,5 +1,6 @@ import py -from py.__.test2.dsession.filechange import StatRecorder +from py.__.test2.dsession.looponfailing import StatRecorder, EventRecorder +from py.__.test2 import event def test_filechange(): tmp = py.test.ensuretemp("test_filechange") @@ -34,8 +35,27 @@ changed = sd.check() assert changed +def test_eventrecorder(): + bus = event.EventBus() + recorder = EventRecorder(bus) + bus.notify(event.NOP()) + assert recorder.events + assert not recorder.getfailures() + rep = event.ItemTestReport(None, failed=True) + bus.notify(rep) + failures = recorder.getfailures() + assert failures == [rep] + recorder.clear() + assert not recorder.events + assert not recorder.getfailures() + recorder.unsubscribe() + bus.notify(rep) + assert not recorder.events + assert not recorder.getfailures() + + From hpk at codespeak.net Wed Aug 13 11:03:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 11:03:28 +0200 (CEST) Subject: [py-svn] r57220 - in py/branch/event/py/test2: . report report/testing Message-ID: <20080813090328.59AB516A0A2@codespeak.net> Author: hpk Date: Wed Aug 13 11:03:26 2008 New Revision: 57220 Modified: py/branch/event/py/test2/event.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_terminal.py Log: support for reporting looponfailinginfo events Modified: py/branch/event/py/test2/event.py ============================================================================== --- py/branch/event/py/test2/event.py (original) +++ py/branch/event/py/test2/event.py Wed Aug 13 11:03:26 2008 @@ -101,6 +101,11 @@ super(CollectionReport, self).__init__(colitem, **kwargs) self.result = result +class LooponfailingInfo(BaseEvent): + def __init__(self, failreports, rootdirs): + self.failreports = failreports + self.rootdirs = rootdirs + # ---------------------------------------------------------------------- # Distributed Testing Events # ---------------------------------------------------------------------- Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Wed Aug 13 11:03:26 2008 @@ -25,6 +25,7 @@ self._tw.write(res) def write_line(self, line): + line = str(line) if self.currentfspath: self._tw.line() self.currentfspath = None @@ -72,6 +73,21 @@ self._tw.sep("!", "KEYBOARD INTERRUPT") self.summary_stats() + def rep_LooponfailingInfo(self, ev): + self._tw.sep("#", "LOOPONFAILING") + if ev.failreports: + self.write_line("") + for report in ev.failreports: + if isinstance(report.colitem, py.test2.collect.Function): + loc = report.outcome.longrepr.reprcrash + else: + loc = str(report.colitem) + self.write_line(loc) + self.write_line("") + self.write_line("watching files below:") + for rootdir in ev.rootdirs: + self.write_line(" %s" %(rootdir,)) + self._tw.sep("#", "waiting for changes") # # summaries for SessionFinish Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Wed Aug 13 11:03:26 2008 @@ -95,3 +95,23 @@ assert not lines[0] assert lines[1].endswith("xy.py .") assert lines[2] == "hello world" + + def test_looponfailingreport(self): + modcol = self.getmodulecol([], """ + def test_fail(): + assert 0 + def test_fail2(): + raise ValueError() + """) + stringio = py.std.cStringIO.StringIO() + rep = TerminalReporter(modcol._config, file=stringio) + reports = [basic_run_report(modcol.join(x)) + for x in modcol.listdir()] + rep.processevent(event.LooponfailingInfo(reports, [modcol._config.topdir])) + assert_stringio_contains_lines(stringio, [ + "*test_looponfailingreport.py:2: assert 0", + "*test_looponfailingreport.py:4: ValueError*", + "*%s*" % (modcol._config.topdir), + "*waiting*", + ]) + From hpk at codespeak.net Wed Aug 13 11:14:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 11:14:17 +0200 (CEST) Subject: [py-svn] r57221 - in py/branch/event/py/test2/dsession: . testing Message-ID: <20080813091417.632FA16A0C8@codespeak.net> Author: hpk Date: Wed Aug 13 11:14:16 2008 New Revision: 57221 Modified: py/branch/event/py/test2/dsession/looponfailing.py py/branch/event/py/test2/dsession/testing/test_looponfailing.py Log: add waitonchange support Modified: py/branch/event/py/test2/dsession/looponfailing.py ============================================================================== --- py/branch/event/py/test2/dsession/looponfailing.py (original) +++ py/branch/event/py/test2/dsession/looponfailing.py Wed Aug 13 11:14:16 2008 @@ -12,6 +12,13 @@ def rec(self, p): return p.check(dotfile=0) + def waitonchange(self, checkinterval=1.0): + while 1: + changed = self.check() + if changed: + return + py.std.time.sleep(checkinterval) + def check(self): changed = False statcache = self.statcache Modified: py/branch/event/py/test2/dsession/testing/test_looponfailing.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_looponfailing.py (original) +++ py/branch/event/py/test2/dsession/testing/test_looponfailing.py Wed Aug 13 11:14:16 2008 @@ -34,6 +34,17 @@ tmp.join("a").remove() changed = sd.check() assert changed + +def test_waitonchange(): + tmp = py.test.ensuretemp("test_waitonchange") + sd = StatRecorder([tmp]) + + wp = py._thread.WorkerPool(1) + reply = wp.dispatch(sd.waitonchange, checkinterval=0.2) + py.std.time.sleep(0.05) + tmp.ensure("newfile.py") + reply.get(timeout=0.5) + wp.shutdown() def test_eventrecorder(): bus = event.EventBus() From hpk at codespeak.net Wed Aug 13 18:22:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 18:22:46 +0200 (CEST) Subject: [py-svn] r57228 - in py: dist/py/apigen release/0.9.x/py/apigen trunk/py/apigen Message-ID: <20080813162246.C801C16A059@codespeak.net> Author: hpk Date: Wed Aug 13 18:22:46 2008 New Revision: 57228 Modified: py/dist/py/apigen/linker.py py/release/0.9.x/py/apigen/linker.py py/trunk/py/apigen/linker.py Log: fix always-true assertion Modified: py/dist/py/apigen/linker.py ============================================================================== --- py/dist/py/apigen/linker.py (original) +++ py/dist/py/apigen/linker.py Wed Aug 13 18:22:46 2008 @@ -30,7 +30,7 @@ return LazyHref(self, linkid) def set_link(self, linkid, target): - assert (linkid not in self._linkid2target, + assert linkid not in self._linkid2target, ( 'linkid %r already used' % (linkid,)) self._linkid2target[linkid] = target Modified: py/release/0.9.x/py/apigen/linker.py ============================================================================== --- py/release/0.9.x/py/apigen/linker.py (original) +++ py/release/0.9.x/py/apigen/linker.py Wed Aug 13 18:22:46 2008 @@ -30,7 +30,7 @@ return LazyHref(self, linkid) def set_link(self, linkid, target): - assert (linkid not in self._linkid2target, + assert linkid not in self._linkid2target, ( 'linkid %r already used' % (linkid,)) self._linkid2target[linkid] = target Modified: py/trunk/py/apigen/linker.py ============================================================================== --- py/trunk/py/apigen/linker.py (original) +++ py/trunk/py/apigen/linker.py Wed Aug 13 18:22:46 2008 @@ -30,7 +30,7 @@ return LazyHref(self, linkid) def set_link(self, linkid, target): - assert (linkid not in self._linkid2target, + assert linkid not in self._linkid2target, ( 'linkid %r already used' % (linkid,)) self._linkid2target[linkid] = target @@ -63,11 +63,8 @@ def __init__(self): self._linkid2target = {} - def get_lazyhref(self, linkid, anchor=None): - href = '%s://%s' % (TEMPLINK_PROTO, linkid) - if anchor: - href += '#' + anchor - return href + def get_lazyhref(self, linkid): + return '%s://%s' % (TEMPLINK_PROTO, linkid) def set_link(self, linkid, target): assert linkid not in self._linkid2target @@ -75,18 +72,13 @@ def get_target(self, tempurl, fromlocation=None): assert tempurl.startswith('%s://' % (TEMPLINK_PROTO,)) - anchor = None - if '#' in tempurl: - tempurl, anchor = tempurl.split('#', 1) - linkid = tempurl.split('://', 1)[1] + linkid = '://'.join(tempurl.split('://')[1:]) linktarget = self._linkid2target[linkid] if fromlocation is not None: linktarget = relpath(fromlocation, linktarget) - if anchor is not None: - linktarget += '#' + anchor return linktarget - _reg_tempurl = py.std.re.compile('(["\'])(%s:\/\/[^"\'\s#]*)(["\'#])' % ( + _reg_tempurl = py.std.re.compile('["\'](%s:\/\/[^"\s]*)["\']' % ( TEMPLINK_PROTO,)) def replace_dirpath(self, dirpath, stoponerrors=True): """ replace temporary links in all html files in dirpath and below """ @@ -96,19 +88,16 @@ match = self._reg_tempurl.search(html) if not match: break - tempurl = match.group(2) - pre = match.group(1) - post = match.group(3) + tempurl = match.group(1) try: - html = html.replace(match.group(0), pre + - self.get_target(tempurl, - fpath.relto(dirpath)) + post) + html = html.replace('"' + tempurl + '"', + '"' + self.get_target(tempurl, + fpath.relto(dirpath)) + '"') except KeyError: if stoponerrors: raise - html = html.replace(match.group(0), pre + - 'apigen.notfound://%s' % (tempurl,) + - post) + html = html.replace('"' + tempurl + '"', + '"apigen.notfound://%s"' % (tempurl,)) fpath.write(html) From hpk at codespeak.net Wed Aug 13 18:54:14 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 18:54:14 +0200 (CEST) Subject: [py-svn] r57229 - in py/branch/event/py/test2: . dsession report terminal testing Message-ID: <20080813165414.68B6E169FCF@codespeak.net> Author: hpk Date: Wed Aug 13 18:54:12 2008 New Revision: 57229 Removed: py/branch/event/py/test2/terminal/ Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/report/base.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/test_config.py Log: remove old way to do looponfailing, introduce new way to dsession. Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Wed Aug 13 18:54:12 2008 @@ -156,6 +156,7 @@ cls = self._getsessionclass() session = cls(self) session.fixoptions() + session.reporter = self.initreporter(session.bus) return session def _getsessionclass(self): Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Wed Aug 13 18:54:12 2008 @@ -14,6 +14,8 @@ from py.__.test2.session import Session from py.__.test2.runner import OutcomeRepr from py.__.test2 import outcome +from py.__.test2.dsession import looponfailing + import Queue @@ -55,8 +57,6 @@ # conflicting options if option.looponfailing and option.usepdb: raise ValueError, "--looponfailing together with --pdb not supported." - if option.looponfailing and option.dist: - raise ValueError, "--looponfailing together with --dist not supported." if option.executable and option.usepdb: raise ValueError, "--exec together with --pdb not supported." if option.keyword_oneshot and not option.keyword: @@ -79,9 +79,38 @@ def main(self): colitems = [self.config.getfsnode(arg) for arg in self.config.args] - self.sessionstarts() + if self.config.option.looponfailing: + self.runsession_looponfailing(colitems) + exitstatus = 0 + else: + exitstatus = self.runsession(colitems) + return exitstatus + + def runsession_looponfailing(self, initialcolitems): + eventrecorder = looponfailing.EventRecorder(self.bus) + statrecorder = looponfailing.StatRecorder([self.config.topdir]) + colitems = initialcolitems + while 1: + self.reporter.startnewrun() + self.runsession(colitems[:]) + reports = eventrecorder.getfailures() + eventrecorder.clear() + self.bus.notify(event.LooponfailingInfo(reports, [self.config.topdir])) + if reports: + colitems = [rep.colitem for rep in reports] + else: + if colitems != initialcolitems: + colitems = initialcolitems + continue # runagain + statrecorder.waitonchange(checkinterval=2.0) + + + def runsession(self, colitems): + self.bus.notify(event.SessionStart(self)) + self.setup_hosts() exitstatus = self.loop(colitems) - self.sessionfinishes(exitstatus=exitstatus) + self.bus.notify(event.SessionFinish(self, exitstatus=exitstatus)) + self.teardown_hosts() return exitstatus def loop_once(self, loopstate): @@ -229,20 +258,14 @@ rep = event.ItemTestReport(item, failed=outcome) self.bus.notify(rep) - def sessionstarts(self): + def setup_hosts(self): """ setup any neccessary resources ahead of the test run. """ - self.reporter = self.config.initreporter(self.bus) - self.bus.notify(event.SessionStart(self)) self.hm = HostManager(self) self.hm.setup_hosts(notify=self.queue.put) for host in self.hm.hosts: self.addhost(host) - def sessionfinishes(self, exitstatus): + def teardown_hosts(self): """ teardown any resources after a test run. """ - self.bus.notify(event.SessionFinish(self, exitstatus=exitstatus)) - self.reporter.deactivate() - #for host in self.hosts: - # host.shutdown() for host in self.hosts: host.gw.exit() Modified: py/branch/event/py/test2/report/base.py ============================================================================== --- py/branch/event/py/test2/report/base.py (original) +++ py/branch/event/py/test2/report/base.py Wed Aug 13 18:54:12 2008 @@ -3,13 +3,16 @@ class BaseReporter(object): def __init__(self, bus=None): - self._passed = [] - self._skipped = [] - self._failed = [] + self.startnewrun() self._bus = bus if bus: self._bus.subscribe(self.processevent) + def startnewrun(self): + self._passed = [] + self._skipped = [] + self._failed = [] + def deactivate(self): if self._bus: self._bus.unsubscribe(self.processevent) Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Wed Aug 13 18:54:12 2008 @@ -10,11 +10,15 @@ def __init__(self, config, file=None, bus=None): super(TerminalReporter, self).__init__(bus=bus) self.config = config - self.currentfspath = None self.curdir = py.path.local() if file is None: file = py.std.sys.stdout self._tw = py.io.TerminalWriter(file) + self.startnewrun() + + def startnewrun(self): + self.currentfspath = None + super(TerminalReporter, self).startnewrun() def write_fspath_result(self, fspath, res): if fspath != self.currentfspath: @@ -76,12 +80,11 @@ def rep_LooponfailingInfo(self, ev): self._tw.sep("#", "LOOPONFAILING") if ev.failreports: - self.write_line("") for report in ev.failreports: - if isinstance(report.colitem, py.test2.collect.Function): + try: loc = report.outcome.longrepr.reprcrash - else: - loc = str(report.colitem) + except AttributeError: + loc = str(report.outcome.longrepr)[:50] self.write_line(loc) self.write_line("") self.write_line("watching files below:") Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Aug 13 18:54:12 2008 @@ -81,7 +81,6 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.reporter = self.config.initreporter(self.bus) self.bus.notify(event.SessionStart(self)) self._failurelist = [] self.bus.subscribe(self._processfailures) Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Wed Aug 13 18:54:12 2008 @@ -227,8 +227,7 @@ self.tmpdir.join("conftest.py").write(py.code.Source(""" from py.__.test2.session import Session class MySession(Session): - def __init__(self, config, reporter=None): - self.config = config + pass """)) config = py.test2.config._reparse(["--session=MySession", self.tmpdir]) session = config.initsession() From hpk at codespeak.net Wed Aug 13 20:03:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 20:03:22 +0200 (CEST) Subject: [py-svn] r57230 - py/branch/event/py/test2/terminal Message-ID: <20080813180322.32F24169FF9@codespeak.net> Author: hpk Date: Wed Aug 13 20:03:20 2008 New Revision: 57230 Added: py/branch/event/py/test2/terminal/ (props changed) - copied from r57228, py/branch/event/py/test2/terminal/ Modified: py/branch/event/py/test2/terminal/remote.py Log: re-adding and fixing old way of looponfailing Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Wed Aug 13 20:03:20 2008 @@ -2,6 +2,7 @@ import py from py.__.test2.session import Session from py.__.test2.outcome import Failed, Passed, Skipped +from py.__.test2.dsession.mypickle import PickleChannel def checkpyfilechange(rootdir, statcache={}): """ wait until project files are changed. """ @@ -98,13 +99,16 @@ def run_remote_session(self, failures): from py.__.test2.dsession import masterslave gw, topdir = self._initslavegateway() - channel = gw.remote_exec(_pickle=True, source=""" + channel = gw.remote_exec(source=""" + from py.__.test2.dsession.mypickle import PickleChannel + channel = PickleChannel(channel) print "SLAVE: initializing ..." from py.__.test2.terminal.remote import slaverun_TerminalSession from py.__.test2.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) slaverun_TerminalSession(channel, config) """, stdout=self.out, stderr=self.out) + channel = PickleChannel(channel) try: print "MASTER: initiating slave terminal session ->" masterslave.send_and_receive_pickled_config(channel, self.config, topdir) @@ -137,7 +141,7 @@ session = config.initsession() session.shouldclose = channel.isclosed print "SLAVE: starting session.main()" - failures = session.main() + session.main() failures = [] # XXX needs fixing #XXX needs fixing #failures = [ev.trail for ev in failures if hasattr(ev.trail)] From hpk at codespeak.net Wed Aug 13 21:09:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 21:09:21 +0200 (CEST) Subject: [py-svn] r57232 - in py/branch/event/py/test2: . dsession dsession/testing report report/testing terminal testing Message-ID: <20080813190921.E901E16A050@codespeak.net> Author: hpk Date: Wed Aug 13 21:09:18 2008 New Revision: 57232 Modified: py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/mypickle.py py/branch/event/py/test2/dsession/testing/test_functional_dsession.py py/branch/event/py/test2/event.py py/branch/event/py/test2/report/base.py py/branch/event/py/test2/report/collectonly.py py/branch/event/py/test2/report/rest.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_basereporter.py py/branch/event/py/test2/report/testing/test_terminal.py py/branch/event/py/test2/report/web.py py/branch/event/py/test2/report/webjs.py py/branch/event/py/test2/session.py py/branch/event/py/test2/terminal/remote.py py/branch/event/py/test2/testing/test_config.py Log: * SessionStart/Finish -> TestrunStart/Finish * clear reporting counters on TestrunStart * some improvements to old-style looponfailing Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Wed Aug 13 21:09:18 2008 @@ -91,7 +91,6 @@ statrecorder = looponfailing.StatRecorder([self.config.topdir]) colitems = initialcolitems while 1: - self.reporter.startnewrun() self.runsession(colitems[:]) reports = eventrecorder.getfailures() eventrecorder.clear() @@ -106,10 +105,10 @@ def runsession(self, colitems): - self.bus.notify(event.SessionStart(self)) + self.bus.notify(event.TestrunStart()) self.setup_hosts() exitstatus = self.loop(colitems) - self.bus.notify(event.SessionFinish(self, exitstatus=exitstatus)) + self.bus.notify(event.TestrunFinish(exitstatus=exitstatus)) self.teardown_hosts() return exitstatus Modified: py/branch/event/py/test2/dsession/mypickle.py ============================================================================== --- py/branch/event/py/test2/dsession/mypickle.py (original) +++ py/branch/event/py/test2/dsession/mypickle.py Wed Aug 13 21:09:18 2008 @@ -118,6 +118,7 @@ # unique ids - which is what we need it here for) uneven = channel.gateway._channelfactory.count % 2 self._ipickle = ImmutablePickler(uneven=uneven) + self.RemoteError = channel.RemoteError def send(self, obj): if not isinstance(obj, Channel): Modified: py/branch/event/py/test2/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_functional_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_functional_dsession.py Wed Aug 13 21:09:18 2008 @@ -38,9 +38,9 @@ eventlog = self.tmpdir.join("mylog") config = self.parseconfig(self.tmpdir, '-d', '--eventlog=%s' % eventlog) session = config.initsession() - session.bus.notify(event.SessionStart(session)) + session.bus.notify(event.TestrunStart()) s = eventlog.read() - assert s.find("SessionStart") != -1 + assert s.find("TestrunStart") != -1 def test_dist_some_tests(self): self.makepyfile(conftest="dist_hosts=['localhost']\n") @@ -66,7 +66,7 @@ # see that the host is really down ev = readevent(event.HostDown) assert ev.host.hostname == "localhost" - ev = readevent(event.SessionFinish) + ev = readevent(event.TestrunFinish) def test_distribution_rsync_roots_example(self): py.test.skip("testing for root rsync needs rework") Modified: py/branch/event/py/test2/event.py ============================================================================== --- py/branch/event/py/test2/event.py (original) +++ py/branch/event/py/test2/event.py Wed Aug 13 21:09:18 2008 @@ -41,14 +41,12 @@ # Basic Live Reporting Events # ---------------------------------------------------------------------- -class SessionStart(BaseEvent): - def __init__(self, session): - self.session = session +class TestrunStart(BaseEvent): + def __init__(self): self.timestart = time.time() -class SessionFinish(BaseEvent): - def __init__(self, session, exitstatus=0): - self.session = session +class TestrunFinish(BaseEvent): + def __init__(self, exitstatus=0): self.exitstatus = exitstatus self.timeend = time.time() Modified: py/branch/event/py/test2/report/base.py ============================================================================== --- py/branch/event/py/test2/report/base.py (original) +++ py/branch/event/py/test2/report/base.py Wed Aug 13 21:09:18 2008 @@ -3,12 +3,12 @@ class BaseReporter(object): def __init__(self, bus=None): - self.startnewrun() + self._reset() self._bus = bus if bus: self._bus.subscribe(self.processevent) - def startnewrun(self): + def _reset(self): self._passed = [] self._skipped = [] self._failed = [] @@ -44,6 +44,9 @@ else: pass # don't record passed collections + def rep_TestrunStart(self, ev): + self._reset() + def _folded_skips(self): d = {} for event in self._skipped: Modified: py/branch/event/py/test2/report/collectonly.py ============================================================================== --- py/branch/event/py/test2/report/collectonly.py (original) +++ py/branch/event/py/test2/report/collectonly.py Wed Aug 13 21:09:18 2008 @@ -36,7 +36,7 @@ self.outindent("!!! %s !!!" % ev.outcome.longrepr.message) self.indent = self.indent[:-len(self.INDENT)] - def rep_SessionFinish(self, session): + def rep_TestrunFinish(self, session): for ev in self._failed: ev.toterminal(self.out) Modified: py/branch/event/py/test2/report/rest.py ============================================================================== --- py/branch/event/py/test2/report/rest.py (original) +++ py/branch/event/py/test2/report/rest.py Wed Aug 13 21:09:18 2008 @@ -58,7 +58,7 @@ self.add_rest(Title(txt, abovechar='=', belowchar='=')) self.timestart = event.timestart - def report_TestSessionFinish(self, item): + def report_TestTestrunFinish(self, item): self.timeend = item.timeend self.summary() return len(self.failed_tests_outcome) > 0 Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Wed Aug 13 21:09:18 2008 @@ -14,11 +14,10 @@ if file is None: file = py.std.sys.stdout self._tw = py.io.TerminalWriter(file) - self.startnewrun() - def startnewrun(self): + def _reset(self): self.currentfspath = None - super(TerminalReporter, self).startnewrun() + super(TerminalReporter, self)._reset() def write_fspath_result(self, fspath, res): if fspath != self.currentfspath: @@ -63,12 +62,13 @@ msg = ev.outcome.longrepr.reprcrash.message self.write_fspath_result(fspath, "- " + str(msg)) - def rep_SessionStart(self, ev): + def rep_TestrunStart(self, ev): + super(TerminalReporter, self).rep_TestrunStart(ev) self._tw.sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() self.out_hostinfo() - def rep_SessionFinish(self, ev): + def rep_TestrunFinish(self, ev): self._tw.line("") if ev.exitstatus == 0 or ev.exitstatus == 1: self.summary_failures() @@ -93,7 +93,7 @@ self._tw.sep("#", "waiting for changes") # - # summaries for SessionFinish + # summaries for TestrunFinish # def summary_failures(self): Modified: py/branch/event/py/test2/report/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/report/testing/test_basereporter.py Wed Aug 13 21:09:18 2008 @@ -19,10 +19,10 @@ def test_dispatch_to_matching_method(self): l = [] class MyReporter(BaseReporter): - def rep_SessionStart(self, ev): + def rep_TestrunStart(self, ev): l.append(ev) rep = MyReporter() - ev = event.SessionStart(None) + ev = event.TestrunStart() rep.processevent(ev) assert len(l) == 1 assert l[0] is ev @@ -33,7 +33,7 @@ def rep(self, ev): l.append(ev) rep = MyReporter() - ev = event.SessionStart(None) + ev = event.NOP() rep.processevent(ev) assert len(l) == 1 assert l[0] is ev Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Wed Aug 13 21:09:18 2008 @@ -15,7 +15,7 @@ assert isinstance(rep, TerminalReporter) assert rep.processevent in session.bus._subscribers session.sessionfinishes() - assert rep.processevent not in session.bus._subscribers + #assert rep.processevent not in session.bus._subscribers def test_pass_skip_fail(self): modcol = self.getmodulecol([], """ @@ -29,13 +29,13 @@ """) stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) + rep.processevent(event.TestrunStart()) for item in self.session.genitems([modcol]): ev = basic_run_report(item) rep.processevent(ev) s = popvalue(stringio) assert s.find("test_pass_skip_fail.py .sF") != -1 - rep.processevent(event.SessionStart(None)) - rep.processevent(event.SessionFinish(None)) + rep.processevent(event.TestrunFinish()) assert_stringio_contains_lines(stringio, [ " def test_func():", "> assert 0", @@ -48,13 +48,13 @@ """) stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, bus=self.session.bus, file=stringio) + rep.processevent(event.TestrunStart()) l = list(self.session.genitems([modcol])) assert len(l) == 0 s = popvalue(stringio) print s assert s.find("test_collect_fail.py - ImportError: No module named") != -1 - rep.processevent(event.SessionStart(None)) - rep.processevent(event.SessionFinish(None)) + rep.processevent(event.TestrunFinish()) assert_stringio_contains_lines(stringio, [ "> import xyz", "E ImportError: No module named xyz" Modified: py/branch/event/py/test2/report/web.py ============================================================================== --- py/branch/event/py/test2/report/web.py (original) +++ py/branch/event/py/test2/report/web.py Wed Aug 13 21:09:18 2008 @@ -237,7 +237,7 @@ args['hostkey'] = '' elif isinstance(event, event.ItemStart): args = add_item(event) - elif isinstance(event, event.TestSessionFinish): + elif isinstance(event, event.TestTestrunFinish): args = {} args['run'] = str(self.all) args['fails'] = str(len(self.fail_reasons)) @@ -321,12 +321,12 @@ self.start_event.set() self.pending_events.put(event) - def report_TestSessionFinish(self, event): + def report_TestTestrunFinish(self, event): self.pending_events.put(event) kill_server() - report_InterruptedExecution = report_TestSessionFinish - report_CrashedExecution = report_TestSessionFinish + report_InterruptedExecution = report_TestTestrunFinish + report_CrashedExecution = report_TestTestrunFinish def report(self, what): repfun = getattr(self, "report_" + what.__class__.__name__, Modified: py/branch/event/py/test2/report/webjs.py ============================================================================== --- py/branch/event/py/test2/report/webjs.py (original) +++ py/branch/event/py/test2/report/webjs.py Wed Aug 13 21:09:18 2008 @@ -199,7 +199,7 @@ return True add_received_item_outcome(msg, module_part) - elif msg['type'] == 'TestSessionFinish': + elif msg['type'] == 'TestTestrunFinish': text = "FINISHED %s run, %s failures, %s skipped" % (msg['run'], msg['fails'], msg['skips']) glob.finished = True dom.document.title = "Py.test %s" % text Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Aug 13 21:09:18 2008 @@ -81,7 +81,7 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.bus.notify(event.SessionStart(self)) + self.bus.notify(event.TestrunStart()) self._failurelist = [] self.bus.subscribe(self._processfailures) @@ -93,9 +93,9 @@ def sessionfinishes(self, exitstatus=0): """ teardown any resources after a test run. """ - self.bus.notify(event.SessionFinish(self, exitstatus=exitstatus)) + self.bus.notify(event.TestrunFinish(exitstatus=exitstatus)) self.bus.unsubscribe(self._processfailures) - self.reporter.deactivate() + #self.reporter.deactivate() return self._failurelist def main(self): Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Wed Aug 13 21:09:18 2008 @@ -3,6 +3,7 @@ from py.__.test2.session import Session from py.__.test2.outcome import Failed, Passed, Skipped from py.__.test2.dsession.mypickle import PickleChannel +from py.__.test2.report.terminal import TerminalReporter def checkpyfilechange(rootdir, statcache={}): """ wait until project files are changed. """ @@ -84,8 +85,8 @@ break print "#" * 60 print "# looponfailing: mode: %d failures args" % len(failures) - for root, names in failures: - name = "/".join(names) # XXX + for ev in failures: + name = "/".join(ev.colitem.listnames()) # XXX print "Failure at: %r" % (name,) print "# watching py files below %s" % rootdir print "# ", "^" * len(str(rootdir)) @@ -109,16 +110,24 @@ slaverun_TerminalSession(channel, config) """, stdout=self.out, stderr=self.out) channel = PickleChannel(channel) + reporter = TerminalReporter(self.config) try: print "MASTER: initiating slave terminal session ->" masterslave.send_and_receive_pickled_config(channel, self.config, topdir) + print "sending", failures channel.send(failures) print "MASTER: send config, topdir=%s" % (topdir,) try: - return channel.receive() + while 1: + ev = channel.receive() + if ev is None: + break + reporter.processevent(ev) + reporter.deactivate() + return reporter._failed except channel.RemoteError, e: print "*" * 70 - print "ERROR while waiting for proper slave startup" + print "ERROR in slave operations" print "*" * 70 print e return [] @@ -128,21 +137,20 @@ def slaverun_TerminalSession(channel, config): """ we run this on the other side. """ print "SLAVE: received configuration, using topdir:", config.topdir - config.option.session = None + #config.option.session = None config.option.looponfailing = False config.option.usepdb = False config.option.executable = None failures = channel.receive() - if failures: - config.option.verbose = True - config._coltrails = failures - print "SLAVE: initsession()" session = config.initsession() + session.reporter.deactivate() session.shouldclose = channel.isclosed print "SLAVE: starting session.main()" + def sendevent(ev): + channel.send(ev) + session.bus.subscribe(sendevent) session.main() - failures = [] # XXX needs fixing - #XXX needs fixing - #failures = [ev.trail for ev in failures if hasattr(ev.trail)] - channel.send(failures) + session.bus.unsubscribe(sendevent) + channel.send(None) + Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Wed Aug 13 21:09:18 2008 @@ -201,9 +201,9 @@ config = py.test2.config._reparse([self.tmpdir, '--eventlog=%s' % eventlog]) session = config.initsession() - session.bus.notify(event.SessionStart(session)) + session.bus.notify(event.TestrunStart()) s = eventlog.read() - assert s.find("SessionStart") != -1 + assert s.find("TestrunStart") != -1 def test_implied_lsession(self): #optnames = 'startserver runbrowser apigen=x rest boxed'.split() From hpk at codespeak.net Wed Aug 13 22:42:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 13 Aug 2008 22:42:31 +0200 (CEST) Subject: [py-svn] r57233 - in py/branch/event/py/test2: . dsession dsession/testing report testing Message-ID: <20080813204231.B907E16A077@codespeak.net> Author: hpk Date: Wed Aug 13 22:42:28 2008 New Revision: 57233 Modified: py/branch/event/py/test2/defaultconftest.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/report/base.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_session.py Log: unify keyword/deselection of test handling, doing it directly via the Deselected event, not through skipped tests. Modified: py/branch/event/py/test2/defaultconftest.py ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test2/defaultconftest.py Wed Aug 13 22:42:28 2008 @@ -41,11 +41,8 @@ help="disable catching of sys.stdout/stderr output."), Option('-k', action="store", dest="keyword", default='', - help="only run test items matching the given (google-style) " + help="only run test items matching the given " "keyword expression."), - Option('-j', '--keyword-oneshot', - action='store_true', dest='keyword_oneshot', default=False, - help="combined with -k, runs all tests after first hit"), Option('-l', '--showlocals', action="store_true", dest="showlocals", default=False, help="show locals in tracebacks (disabled by default)."), Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Wed Aug 13 22:42:28 2008 @@ -59,8 +59,6 @@ raise ValueError, "--looponfailing together with --pdb not supported." if option.executable and option.usepdb: raise ValueError, "--exec together with --pdb not supported." - if option.keyword_oneshot and not option.keyword: - raise ValueError, "--keyword-oneshot makes sense only when --keyword is supplied" config = self.config try: @@ -234,23 +232,6 @@ host = self.item2host.pop(item) self.host2pending[host].remove(item) - def filteritems(self, colitems): - """ return items to process (some may be deselected)""" - keywordexpr = self.config.option.keyword - if not keywordexpr: - return colitems - remaining = [] - deselected = [] - for colitem in colitems: - if isinstance(colitem, Item): - if colitem._skipbykeyword(keywordexpr): - deselected.append(colitem) - continue - remaining.append(colitem) - if deselected: - self.queue.put(event.Deselected(deselected)) - return remaining - def handle_crashitem(self, item, host): longrepr = "%r CRASHED THE HOST %r" %(item, host) outcome = OutcomeRepr(when="execute", shortrepr="c", longrepr=longrepr) Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Wed Aug 13 22:42:28 2008 @@ -275,16 +275,19 @@ dsel = session.filteritems([modcol]) assert dsel == [modcol] items = [modcol.join(x) for x in modcol.listdir()] + events = [] ; session.bus.subscribe(events.append) remaining = session.filteritems(items) assert remaining == [] - ev = session.queue.get(block=False) + + ev = events[-1] assert isinstance(ev, event.Deselected) assert ev.items == items modcol._config.option.keyword = "test_fail" remaining = session.filteritems(items) assert remaining == [items[0]] - ev = session.queue.get(block=False) + + ev = events[-1] assert isinstance(ev, event.Deselected) assert ev.items == [items[1]] Modified: py/branch/event/py/test2/report/base.py ============================================================================== --- py/branch/event/py/test2/report/base.py (original) +++ py/branch/event/py/test2/report/base.py Wed Aug 13 22:42:28 2008 @@ -12,6 +12,7 @@ self._passed = [] self._skipped = [] self._failed = [] + self._deselected = [] def deactivate(self): if self._bus: @@ -47,6 +48,9 @@ def rep_TestrunStart(self, ev): self._reset() + def rep_Deselected(self, ev): + self._deselected.extend(ev.items) + def _folded_skips(self): d = {} for event in self._skipped: Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Wed Aug 13 22:42:28 2008 @@ -75,6 +75,8 @@ self.summary_skips() elif ev.exitstatus == 2: self._tw.sep("!", "KEYBOARD INTERRUPT") + self._tw.line() + self.summary_deselected() self.summary_stats() def rep_LooponfailingInfo(self, ev): @@ -109,7 +111,6 @@ numskipped = len(self._skipped) numpassed = len(self._passed) sum = numfailed + numpassed - self._tw.line() self._tw.sep("=", "%d/%d passed + %d skips in %.2f seconds" % (numpassed, sum, numskipped, session_duration), bold=True) if numfailed == 0: @@ -118,6 +119,13 @@ self._tw.sep("=", "failures: %d" %(numfailed), red=True) self._tw.line() + def summary_deselected(self): + if not self._deselected: + return + self._tw.sep("=", "%d tests deselected by %r" %( + len(self._deselected), self.config.option.keyword), bold=True) + + def summary_skips(self): if not self._failed or self.config.option.showskipsummary: folded_skips = self._folded_skips() Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Wed Aug 13 22:42:28 2008 @@ -93,20 +93,6 @@ kw = {'passed': OutcomeRepr(when, '', "")} return event.CollectionReport(self.colitem, res, **kw) -class SkipRunner(RobustRun): - def __init__(self, colitem, msg): - super(SkipRunner, self).__init__(colitem) - self.msg = msg - def setup(self): pass - def execute(self): - __tracebackhide__ = True - py.test2.skip(self.msg) - def teardown(self): pass - def makereport(self, res, when, excinfo, outerr): - assert excinfo - kw = self.getkw(when, excinfo, outerr) - return event.ItemTestReport(self.colitem, **kw) - NORESULT = object() # # public entrypoints / objects @@ -121,10 +107,6 @@ return " Author: hpk Date: Wed Aug 13 23:13:01 2008 New Revision: 57234 Modified: py/branch/event/py/test2/cmdline.py py/branch/event/py/test2/config.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/testing/test_functional_dsession.py py/branch/event/py/test2/session.py py/branch/event/py/test2/terminal/remote.py py/branch/event/py/test2/testing/suptest.py py/branch/event/py/test2/testing/test_config.py Log: unify things and hack a bit to get --loopoonfailing to work uniformly with dist and inprocess sessions. Modified: py/branch/event/py/test2/cmdline.py ============================================================================== --- py/branch/event/py/test2/cmdline.py (original) +++ py/branch/event/py/test2/cmdline.py Wed Aug 13 23:13:01 2008 @@ -11,7 +11,8 @@ config = py.test2.config config.parse(args) session = config.initsession() - exitstatus = session.main() + colitems = [config.getfsnode(arg) for arg in config.args] + exitstatus = session.main(colitems) raise SystemExit(exitstatus) def warn_about_missing_assertion(): Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Wed Aug 13 23:13:01 2008 @@ -178,11 +178,10 @@ def _getsessionname(self): """ return default session name as determined from options. """ name = 'Session' - if self.option.dist: + if self.option.looponfailing or self.option.executable: + name = 'RemoteTerminalSession' + elif self.option.dist: name = 'DSession' - else: - if self.option.looponfailing or self.option.executable: - name = 'RemoteTerminalSession' return name def _reparse(self, args): Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Wed Aug 13 23:13:01 2008 @@ -75,31 +75,30 @@ print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" raise SystemExit - def main(self): - colitems = [self.config.getfsnode(arg) for arg in self.config.args] - if self.config.option.looponfailing: - self.runsession_looponfailing(colitems) - exitstatus = 0 - else: - exitstatus = self.runsession(colitems) + def main(self, colitems): + #if self.config.option.looponfailing: + # self.runsession_looponfailing(colitems) + # exitstatus = 0 + #else: + exitstatus = self.runsession(colitems) return exitstatus - def runsession_looponfailing(self, initialcolitems): - eventrecorder = looponfailing.EventRecorder(self.bus) - statrecorder = looponfailing.StatRecorder([self.config.topdir]) - colitems = initialcolitems - while 1: - self.runsession(colitems[:]) - reports = eventrecorder.getfailures() - eventrecorder.clear() - self.bus.notify(event.LooponfailingInfo(reports, [self.config.topdir])) - if reports: - colitems = [rep.colitem for rep in reports] - else: - if colitems != initialcolitems: - colitems = initialcolitems - continue # runagain - statrecorder.waitonchange(checkinterval=2.0) + #def runsession_looponfailing(self, initialcolitems): + # eventrecorder = looponfailing.EventRecorder(self.bus) + # statrecorder = looponfailing.StatRecorder([self.config.topdir]) + # colitems = initialcolitems + # while 1: + # self.runsession(colitems[:]) + # reports = eventrecorder.getfailures() + # eventrecorder.clear() + # self.bus.notify(event.LooponfailingInfo(reports, [self.config.topdir])) + # if reports: + # colitems = [rep.colitem for rep in reports] + # else: + # if colitems != initialcolitems: + # colitems = initialcolitems + # continue # runagain + # statrecorder.waitonchange(checkinterval=2.0) def runsession(self, colitems): Modified: py/branch/event/py/test2/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_functional_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_functional_dsession.py Wed Aug 13 23:13:01 2008 @@ -56,7 +56,7 @@ config = self.parseconfig('-d', p1) dsession = DSession(config) readevent = eventreader(dsession) - dsession.main() + dsession.main([config.getfsnode(p1)]) ev = readevent(event.ItemTestReport) assert ev.passed ev = readevent(event.ItemTestReport) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Wed Aug 13 23:13:01 2008 @@ -45,8 +45,8 @@ # conflicting options if option.looponfailing and option.usepdb: raise ValueError, "--looponfailing together with --pdb not supported." - if option.looponfailing and option.dist: - raise ValueError, "--looponfailing together with --dist not supported." + #if option.looponfailing and option.dist: + # raise ValueError, "--looponfailing together with --dist not supported." if option.executable and option.usepdb: raise ValueError, "--exec together with --pdb not supported." @@ -89,8 +89,7 @@ self._nomatch = True return remaining - def collect(self): - colitems = [self.config.getfsnode(arg) for arg in self.config.args] + def collect(self, colitems): keyword = self.config.option.keyword for x in self.genitems(colitems, keyword): yield x @@ -114,13 +113,13 @@ #self.reporter.deactivate() return self._failurelist - def main(self): + def main(self, colitems): """ main loop for running tests. """ self.shouldstop = False self.sessionstarts() exitstatus = outcome.EXIT_OK try: - for item in self.collect(): + for item in self.collect(colitems): if self.shouldstop: break if not self.config.option.collectonly: @@ -129,6 +128,7 @@ except KeyboardInterrupt: exitstatus = outcome.EXIT_INTERRUPTED except: + raise self.bus.notify(event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR if self._failurelist and exitstatus == 0: Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Wed Aug 13 23:13:01 2008 @@ -4,6 +4,7 @@ from py.__.test2.outcome import Failed, Passed, Skipped from py.__.test2.dsession.mypickle import PickleChannel from py.__.test2.report.terminal import TerminalReporter +from py.__.test2 import event def checkpyfilechange(rootdir, statcache={}): """ wait until project files are changed. """ @@ -71,25 +72,29 @@ assert executable is not None, executable self.executable = executable - def main(self): + def main(self, colitems): rootdir = self.config.topdir wasfailing = False - failures = [] + failures = colitems + failurereports = [] while 1: - if self.config.option.looponfailing and (failures or not wasfailing): + if self.config.option.looponfailing and (failurereports or not wasfailing): while not checkpyfilechange(rootdir): py.std.time.sleep(4.4) - wasfailing = len(failures) - failures = self.run_remote_session(failures) + wasfailing = len(failurereports) + failurereports = self.run_remote_session(failures) if not self.config.option.looponfailing: break print "#" * 60 print "# looponfailing: mode: %d failures args" % len(failures) - for ev in failures: + for ev in failurereports: name = "/".join(ev.colitem.listnames()) # XXX print "Failure at: %r" % (name,) print "# watching py files below %s" % rootdir print "# ", "^" * len(str(rootdir)) + failures = [ev.colitem for ev in failurereports] + if not failures: + failures = colitems return failures def _initslavegateway(self): @@ -124,7 +129,7 @@ break reporter.processevent(ev) reporter.deactivate() - return reporter._failed + return reporter._failed except channel.RemoteError, e: print "*" * 70 print "ERROR in slave operations" @@ -148,9 +153,12 @@ session.shouldclose = channel.isclosed print "SLAVE: starting session.main()" def sendevent(ev): - channel.send(ev) + if not isinstance(ev, event.HostGatewayReady) and \ + not isinstance(ev, event.HostDown): + + channel.send(ev) session.bus.subscribe(sendevent) - session.main() + session.main(failures) session.bus.unsubscribe(sendevent) channel.send(None) Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Wed Aug 13 23:13:01 2008 @@ -31,9 +31,12 @@ sorter = EventSorter(config, session) return sorter +def getcolitems(config): + return [config.getfsnode(arg) for arg in config.args] + def events_from_cmdline(args=None): sorter = initsorter_from_cmdline(args) - sorter.session.main() + sorter.session.main(getcolitems(sorter.session.config)) return sorter def events_from_runsource(source): @@ -43,7 +46,7 @@ def events_from_session(session): sorter = EventSorter(session.config, session) - session.main() + session.main(getcolitems(session.config)) return sorter def events_run_example(examplename, *args): Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Wed Aug 13 23:13:01 2008 @@ -221,7 +221,7 @@ config = py.test2.config._reparse([self.tmpdir, '--exec=x']) assert config._getsessionname() == 'RemoteTerminalSession' config = py.test2.config._reparse([self.tmpdir, '--dist', '--exec=x']) - assert config._getsessionname() == 'DSession' + assert config._getsessionname() == 'RemoteTerminalSession' def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" From hpk at codespeak.net Thu Aug 14 13:39:56 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 13:39:56 +0200 (CEST) Subject: [py-svn] r57245 - in py/branch/event/py/test2: . report terminal terminal/testing testing Message-ID: <20080814113956.91513169F2C@codespeak.net> Author: hpk Date: Thu Aug 14 13:39:55 2008 New Revision: 57245 Added: py/branch/event/py/test2/terminal/testing/ (props changed) py/branch/event/py/test2/terminal/testing/__init__.py py/branch/event/py/test2/terminal/testing/test_remote.py Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/terminal/remote.py py/branch/event/py/test2/testing/test_config.py py/branch/event/py/test2/testing/test_session.py Log: implement tested looponfailing mode that works both with dsessions and inprocess sessions Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Thu Aug 14 13:39:55 2008 @@ -179,7 +179,7 @@ """ return default session name as determined from options. """ name = 'Session' if self.option.looponfailing or self.option.executable: - name = 'RemoteTerminalSession' + name = 'LooponfailingSession' elif self.option.dist: name = 'DSession' return name @@ -216,7 +216,7 @@ # default import paths for sessions Session = 'py.__.test2.session' -RemoteTerminalSession = 'py.__.test2.terminal.remote' +LooponfailingSession = 'py.__.test2.terminal.remote' DSession = 'py.__.test2.dsession.dsession' # Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Thu Aug 14 13:39:55 2008 @@ -27,12 +27,12 @@ self.currentfspath = fspath self._tw.write(res) - def write_line(self, line): + def write_line(self, line, **markup): line = str(line) if self.currentfspath: self._tw.line() self.currentfspath = None - self._tw.line(line) + self._tw.line(line, **markup) def rep_InternalException(self, ev): for line in str(ev.excinfo.getrepr()).split("\n"): @@ -87,13 +87,25 @@ loc = report.outcome.longrepr.reprcrash except AttributeError: loc = str(report.outcome.longrepr)[:50] - self.write_line(loc) + self.write_line(loc, red=True) self.write_line("") self.write_line("watching files below:") for rootdir in ev.rootdirs: self.write_line(" %s" %(rootdir,)) self._tw.sep("#", "waiting for changes") + if 0: + print "#" * 60 + print "# looponfailing: mode: %d failures args" % len(failures) + for ev in failurereports: + name = "/".join(ev.colitem.listnames()) # XXX + print "Failure at: %r" % (name,) + print "# watching py files below %s" % rootdir + print "# ", "^" * len(str(rootdir)) + failures = [ev.colitem for ev in failurereports] + if not failures: + failures = colitems + # # summaries for TestrunFinish # Modified: py/branch/event/py/test2/terminal/remote.py ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/terminal/remote.py Thu Aug 14 13:39:55 2008 @@ -5,65 +5,61 @@ from py.__.test2.dsession.mypickle import PickleChannel from py.__.test2.report.terminal import TerminalReporter from py.__.test2 import event +from py.__.test2.dsession import looponfailing -def checkpyfilechange(rootdir, statcache={}): - """ wait until project files are changed. """ - def fil(p): - return p.ext in ('.py', '.c', '.h') - #fil = lambda x: x.check(fnmatch='*.py') - def rec(p): - return p.check(dotfile=0) - changed = False - for path in rootdir.visit(fil, rec): - oldstat = statcache.get(path, None) +class LooponfailingSession(Session): + def __init__(self, config): + super(LooponfailingSession, self).__init__(config=config) + self.rootdirs = [self.config.topdir] # xxx dist_rsync_roots? + self.statrecorder = looponfailing.StatRecorder(self.rootdirs) + self.remotecontrol = RemoteControl(self.config, notify=self.bus.notify) + + def main(self, initialitems): + self.loopstate = loopstate = LoopState(initialitems) + self.remotecontrol.setup() + while 1: + self.loop_once(loopstate) + if loopstate.wasfailing and not loopstate.failreports: + continue # rerun immediately + self.statrecorder.waitonchange(checkinterval=2.0) + + def loop_once(self, loopstate): + colitems = loopstate.colitems + if not colitems: + colitems = loopstate.initial + loopstate.wasfailing = len(loopstate.failreports) + events = [] + self.bus.subscribe(events.append) try: - statcache[path] = curstat = path.stat() - except py.error.ENOENT: - if oldstat: - del statcache[path] - print "# WARN: race condition on", path - else: - if oldstat: - if oldstat.mtime != curstat.mtime or \ - oldstat.size != curstat.size: - changed = True - print "# MODIFIED", path - else: - changed = True - return changed - -def getfailureitems(failures): - l = [] - for rootpath, names in failures: - root = py.path.local(rootpath) - if root.check(dir=1): - current = py.test2.collect.Directory(root).Directory(root) - elif root.check(file=1): - current = py.test2.collect.Module(root).Module(root) - # root is fspath of names[0] -> pop names[0] - # slicing works with empty lists - names = names[1:] - while names: - name = names.pop(0) - try: - current = current.join(name) - except NameError: - print "WARNING: could not find %s on %r" %(name, current) - break - else: - l.append(current) - return l - -class RemoteTerminalSession(Session): - def __init__(self, config, file=None): - super(RemoteTerminalSession, self).__init__(config=config) + self.remotecontrol.runtests(colitems) + finally: + self.bus.unsubscribe(events.append) + reports = [] + colitems = [] + for ev in events: + if isinstance(ev, event.ItemTestReport) and ev.failed: + reports.append(ev) + colitems.append(ev.colitem) + loopstate.colitems = colitems + loopstate.failreports = reports + ev = event.LooponfailingInfo(loopstate.failreports, self.rootdirs) + self.bus.notify(ev) + self.remotecontrol.setup() + +class LoopState: + def __init__(self, initial, colitems=None): + self.initial = initial + self.colitems = colitems or initial + self.failreports = [] + +class RemoteControl(object): + def __init__(self, config, notify): + self.config = config + self.notify = notify self._setexecutable() - if file is None: - file = py.std.sys.stdout - self._file = file - self.out = py.io.TerminalWriter(file) def _setexecutable(self): + # XXX --exec logic should go to DSession name = self.config.option.executable if name is None: executable = py.std.sys.executable @@ -72,74 +68,59 @@ assert executable is not None, executable self.executable = executable - def main(self, colitems): - rootdir = self.config.topdir - wasfailing = False - failures = colitems - failurereports = [] - while 1: - if self.config.option.looponfailing and (failurereports or not wasfailing): - while not checkpyfilechange(rootdir): - py.std.time.sleep(4.4) - wasfailing = len(failurereports) - failurereports = self.run_remote_session(failures) - if not self.config.option.looponfailing: - break - print "#" * 60 - print "# looponfailing: mode: %d failures args" % len(failures) - for ev in failurereports: - name = "/".join(ev.colitem.listnames()) # XXX - print "Failure at: %r" % (name,) - print "# watching py files below %s" % rootdir - print "# ", "^" * len(str(rootdir)) - failures = [ev.colitem for ev in failurereports] - if not failures: - failures = colitems - return failures - - def _initslavegateway(self): - print "* opening PopenGateway: ", self.executable - topdir = self.config.topdir - return py.execnet.PopenGateway(self.executable), topdir - - def run_remote_session(self, failures): + def trace(self, *args): + msg = " ".join([str(x) for x in args]) + print "RemoteControl:", msg + + def setup(self): + if hasattr(self, 'gateway'): + raise ValueError("already have gateway %r" % self.gateway) from py.__.test2.dsession import masterslave - gw, topdir = self._initslavegateway() - channel = gw.remote_exec(source=""" + self.trace("setting up slave session") + self.gateway = py.execnet.PopenGateway(self.executable) + channel = self.gateway.remote_exec(source=""" from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) print "SLAVE: initializing ..." - from py.__.test2.terminal.remote import slaverun_TerminalSession + from py.__.test2.terminal.remote import slave_runsession from py.__.test2.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) - slaverun_TerminalSession(channel, config) - """, stdout=self.out, stderr=self.out) + slave_runsession(channel, config) + """) # , stdout=self.out, stderr=self.out) channel = PickleChannel(channel) - reporter = TerminalReporter(self.config) + masterslave.send_and_receive_pickled_config( + channel, self.config, remote_topdir=self.config.topdir) + self.trace("set up of slave session complete") + self.channel = channel + + def ensure_teardown(self): + if hasattr(self, 'channel'): + if not self.channel.isclosed(): + self.trace("closing", self.channel) + self.channel.close() + del self.channel + if hasattr(self, 'gateway'): + self.trace("exiting", self.gateway) + self.gateway.exit() + del self.gateway + + def runtests(self, colitems): try: - print "MASTER: initiating slave terminal session ->" - masterslave.send_and_receive_pickled_config(channel, self.config, topdir) - print "sending", failures - channel.send(failures) - print "MASTER: send config, topdir=%s" % (topdir,) + self.trace("sending", colitems) + self.channel.send(colitems) try: while 1: - ev = channel.receive() + ev = self.channel.receive() if ev is None: break - reporter.processevent(ev) - reporter.deactivate() - return reporter._failed - except channel.RemoteError, e: - print "*" * 70 - print "ERROR in slave operations" - print "*" * 70 - print e - return [] + self.notify(ev) + except self.channel.RemoteError, e: + self.trace("ERROR", e) + raise finally: - gw.exit() + self.ensure_teardown() -def slaverun_TerminalSession(channel, config): +def slave_runsession(channel, config): """ we run this on the other side. """ print "SLAVE: received configuration, using topdir:", config.topdir #config.option.session = None @@ -155,7 +136,6 @@ def sendevent(ev): if not isinstance(ev, event.HostGatewayReady) and \ not isinstance(ev, event.HostDown): - channel.send(ev) session.bus.subscribe(sendevent) session.main(failures) Added: py/branch/event/py/test2/terminal/testing/__init__.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/terminal/testing/__init__.py Thu Aug 14 13:39:55 2008 @@ -0,0 +1 @@ +# Added: py/branch/event/py/test2/terminal/testing/test_remote.py ============================================================================== --- (empty file) +++ py/branch/event/py/test2/terminal/testing/test_remote.py Thu Aug 14 13:39:55 2008 @@ -0,0 +1,100 @@ +import py +from py.__.test2.testing import suptest +from py.__.test2.terminal.remote import LooponfailingSession, LoopState, RemoteControl +from py.__.test2 import event + +def getevent(l, evtype): + result = getevents(l, evtype) + if not result: + raise ValueError("event %r not found in %r" %(evtype, l)) + return result[0] + +def getevents(l, evtype): + result = [] + for ev in l: + if isinstance(ev, evtype): + result.append(ev) + return result + + +class TestRemoteControl(suptest.InlineCollection): + def test_simple(self): + item = self.getitem("def test_func(): pass\n") + events = [] + control = RemoteControl(item._config, notify=events.append) + control.setup() + control.runtests([item]) + ev = getevent(events, event.ItemTestReport) + assert ev.passed + assert ev.colitem == item + + def test_double_setup_teardown_calls(self): + item = self.getitem("def test_func(): pass\n") + events = [] + control = RemoteControl(item._config, notify=events.append) + control.setup() + py.test2.raises(ValueError, control.setup) + control.ensure_teardown() + control.ensure_teardown() + +class TestLooponFailing(suptest.InlineCollection): + def test_looponfailing_from_fail_to_ok(self): + modcol = self.getmodulecol(""" + def test_one(): + x = 0 + assert x == 1 + def test_two(): + assert 1 + """) + session = LooponfailingSession(modcol._config) + loopstate = LoopState(initial=[modcol]) + events = [] + session.bus.subscribe(events.append) + + session.remotecontrol.setup() + loopstate.colitems = [] + session.loop_once(loopstate) + ev = getevent(events, event.LooponfailingInfo) + assert ev.failreports == loopstate.failreports + evlist = getevents(events, event.ItemTestReport) + failed = [ev for ev in evlist if ev.failed] + assert len(failed) == 1 + assert failed[0].colitem.name == "test_one" + assert loopstate.colitems[0] == failed[0].colitem + + modcol.fspath.write(py.code.Source(""" + def test_one(): + x = 15 + assert x == 15 + def test_two(): + assert 1 + """)) + pyc = modcol.fspath + "c" + if pyc.check(): pyc.remove() # XXX move to statrecorder? + assert session.statrecorder.check() + events[:] = [] + session.loop_once(loopstate) + assert not loopstate.colitems + assert not loopstate.failreports + evlist = getevents(events, event.LooponfailingInfo) + assert len(evlist) == 1 + assert not evlist[0].failreports + + def test_full_loop_from_fail_to_different_fail(self): + modcol = self.getmodulecol(""" + def test_one(): + x = 0 + assert x == 1 + def test_two(): + assert 1 + """) + session = LooponfailingSession(modcol._config) + events = [] ; session.bus.subscribe(events.append) + del session.statrecorder + py.test2.raises(AttributeError, "session.main([modcol])") + assert len(session.loopstate.failreports) == 1 + linfo = getevent(events, event.LooponfailingInfo) + assert linfo.failreports == session.loopstate.failreports + assert linfo.rootdirs == session.rootdirs + + Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Thu Aug 14 13:39:55 2008 @@ -217,11 +217,11 @@ def test_implied_different_sessions(self): config = py.test2.config._reparse([self.tmpdir, '--looponfailing']) - assert config._getsessionname() == 'RemoteTerminalSession' + assert config._getsessionname() == 'LooponfailingSession' config = py.test2.config._reparse([self.tmpdir, '--exec=x']) - assert config._getsessionname() == 'RemoteTerminalSession' + assert config._getsessionname() == 'LooponfailingSession' config = py.test2.config._reparse([self.tmpdir, '--dist', '--exec=x']) - assert config._getsessionname() == 'RemoteTerminalSession' + assert config._getsessionname() == 'LooponfailingSession' def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Thu Aug 14 13:39:55 2008 @@ -223,19 +223,17 @@ assert failed[1].colitem.name == "test_other" assert failed[2].colitem.name == "test_two" -from py.__.test2.terminal.remote import RemoteTerminalSession +from py.__.test2.terminal.remote import LooponfailingSession class Test_TerminalRemoteSession: - def test_exec_leads_to_RemoteTerminalSession(self): + def test_exec_leads_to_DistSession(self): + py.test.skip("XXX fix --exec") executable = py.std.sys.executable config = py.test2.config._reparse(['--exec=' + executable]) print config - session = config.initsession() - assert isinstance(session, RemoteTerminalSession) - assert session.executable == executable + assert config._getsessionname() == "LooponfailingSession" - def test_looponfailing_leads_to_RemoteTerminalSession(self): + def test_looponfailing_leads_to_LooponfailingSession(self): config = py.test2.config._reparse(['--looponfailing']) - session = config.initsession() - assert isinstance(session, RemoteTerminalSession) - + sessionname = config._getsessionname() + assert sessionname == "LooponfailingSession" From hpk at codespeak.net Thu Aug 14 15:42:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 15:42:00 +0200 (CEST) Subject: [py-svn] r57247 - in py/branch/event/py/test2: . dsession dsession/testing looponfail looponfail/testing terminal testing Message-ID: <20080814134200.5BAAF16A01C@codespeak.net> Author: hpk Date: Thu Aug 14 15:41:58 2008 New Revision: 57247 Added: py/branch/event/py/test2/looponfail/ - copied from r57235, py/branch/event/py/test2/terminal/ py/branch/event/py/test2/looponfail/remote.py - copied, changed from r57246, py/branch/event/py/test2/terminal/remote.py py/branch/event/py/test2/looponfail/testing/ - copied from r57246, py/branch/event/py/test2/terminal/testing/ py/branch/event/py/test2/looponfail/testing/test_util.py - copied, changed from r57235, py/branch/event/py/test2/dsession/testing/test_looponfailing.py py/branch/event/py/test2/looponfail/util.py - copied unchanged from r57235, py/branch/event/py/test2/dsession/looponfailing.py Removed: py/branch/event/py/test2/dsession/looponfailing.py py/branch/event/py/test2/dsession/testing/test_looponfailing.py py/branch/event/py/test2/terminal/ Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/looponfail/testing/test_remote.py py/branch/event/py/test2/testing/test_session.py Log: refactor files related to looponfailing into one dir Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Thu Aug 14 15:41:58 2008 @@ -216,7 +216,7 @@ # default import paths for sessions Session = 'py.__.test2.session' -LooponfailingSession = 'py.__.test2.terminal.remote' +LooponfailingSession = 'py.__.test2.looponfail.remote' DSession = 'py.__.test2.dsession.dsession' # Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Thu Aug 14 15:41:58 2008 @@ -14,7 +14,6 @@ from py.__.test2.session import Session from py.__.test2.runner import OutcomeRepr from py.__.test2 import outcome -from py.__.test2.dsession import looponfailing import Queue @@ -76,32 +75,6 @@ raise SystemExit def main(self, colitems): - #if self.config.option.looponfailing: - # self.runsession_looponfailing(colitems) - # exitstatus = 0 - #else: - exitstatus = self.runsession(colitems) - return exitstatus - - #def runsession_looponfailing(self, initialcolitems): - # eventrecorder = looponfailing.EventRecorder(self.bus) - # statrecorder = looponfailing.StatRecorder([self.config.topdir]) - # colitems = initialcolitems - # while 1: - # self.runsession(colitems[:]) - # reports = eventrecorder.getfailures() - # eventrecorder.clear() - # self.bus.notify(event.LooponfailingInfo(reports, [self.config.topdir])) - # if reports: - # colitems = [rep.colitem for rep in reports] - # else: - # if colitems != initialcolitems: - # colitems = initialcolitems - # continue # runagain - # statrecorder.waitonchange(checkinterval=2.0) - - - def runsession(self, colitems): self.bus.notify(event.TestrunStart()) self.setup_hosts() exitstatus = self.loop(colitems) Deleted: /py/branch/event/py/test2/dsession/looponfailing.py ============================================================================== --- /py/branch/event/py/test2/dsession/looponfailing.py Thu Aug 14 15:41:58 2008 +++ (empty file) @@ -1,65 +0,0 @@ -import py -from py.__.test2 import event - -class StatRecorder: - def __init__(self, rootdirlist): - self.rootdirlist = rootdirlist - self.statcache = {} - self.check() # snapshot state - - def fil(self, p): - return p.ext in ('.py', '.txt', '.c', '.h') - def rec(self, p): - return p.check(dotfile=0) - - def waitonchange(self, checkinterval=1.0): - while 1: - changed = self.check() - if changed: - return - py.std.time.sleep(checkinterval) - - def check(self): - changed = False - statcache = self.statcache - newstat = {} - for rootdir in self.rootdirlist: - for path in rootdir.visit(self.fil, self.rec): - oldstat = statcache.get(path, None) - if oldstat is not None: - del statcache[path] - try: - newstat[path] = curstat = path.stat() - except py.error.ENOENT: - if oldstat: - del statcache[path] - changed = True - else: - if oldstat: - if oldstat.mtime != curstat.mtime or \ - oldstat.size != curstat.size: - changed = True - print "# MODIFIED", path - else: - changed = True - if statcache: - changed = True - self.statcache = newstat - return changed - - -class EventRecorder(object): - def __init__(self, bus): - self.events = [] - self.bus = bus - self.bus.subscribe(self.events.append) - - def getfailures(self): - return [ev for ev in self.events - if isinstance(ev, event.BaseReport) and \ - ev.failed] - def clear(self): - self.events[:] = [] - - def unsubscribe(self): - self.bus.unsubscribe(self.events.append) Deleted: /py/branch/event/py/test2/dsession/testing/test_looponfailing.py ============================================================================== --- /py/branch/event/py/test2/dsession/testing/test_looponfailing.py Thu Aug 14 15:41:58 2008 +++ (empty file) @@ -1,73 +0,0 @@ -import py -from py.__.test2.dsession.looponfailing import StatRecorder, EventRecorder -from py.__.test2 import event - -def test_filechange(): - tmp = py.test.ensuretemp("test_filechange") - hello = tmp.ensure("hello.py") - sd = StatRecorder([tmp]) - changed = sd.check() - assert not changed - - hello.write("world") - changed = sd.check() - assert changed - - tmp.ensure("new.py") - changed = sd.check() - assert changed - - tmp.join("new.py").remove() - changed = sd.check() - assert changed - - tmp.join("a", "b", "c.py").ensure() - changed = sd.check() - assert changed - - tmp.join("a", "c.txt").ensure() - changed = sd.check() - assert changed - changed = sd.check() - assert not changed - - tmp.join("a").remove() - changed = sd.check() - assert changed - -def test_waitonchange(): - tmp = py.test.ensuretemp("test_waitonchange") - sd = StatRecorder([tmp]) - - wp = py._thread.WorkerPool(1) - reply = wp.dispatch(sd.waitonchange, checkinterval=0.2) - py.std.time.sleep(0.05) - tmp.ensure("newfile.py") - reply.get(timeout=0.5) - wp.shutdown() - -def test_eventrecorder(): - bus = event.EventBus() - recorder = EventRecorder(bus) - bus.notify(event.NOP()) - assert recorder.events - assert not recorder.getfailures() - rep = event.ItemTestReport(None, failed=True) - bus.notify(rep) - failures = recorder.getfailures() - assert failures == [rep] - recorder.clear() - assert not recorder.events - assert not recorder.getfailures() - recorder.unsubscribe() - bus.notify(rep) - assert not recorder.events - assert not recorder.getfailures() - - - - - - - - Copied: py/branch/event/py/test2/looponfail/remote.py (from r57246, py/branch/event/py/test2/terminal/remote.py) ============================================================================== --- py/branch/event/py/test2/terminal/remote.py (original) +++ py/branch/event/py/test2/looponfail/remote.py Thu Aug 14 15:41:58 2008 @@ -5,13 +5,13 @@ from py.__.test2.dsession.mypickle import PickleChannel from py.__.test2.report.terminal import TerminalReporter from py.__.test2 import event -from py.__.test2.dsession import looponfailing +from py.__.test2.looponfail import util class LooponfailingSession(Session): def __init__(self, config): super(LooponfailingSession, self).__init__(config=config) self.rootdirs = [self.config.topdir] # xxx dist_rsync_roots? - self.statrecorder = looponfailing.StatRecorder(self.rootdirs) + self.statrecorder = util.StatRecorder(self.rootdirs) self.remotecontrol = RemoteControl(self.config, notify=self.bus.notify) def main(self, initialitems): @@ -82,7 +82,7 @@ from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) print "SLAVE: initializing ..." - from py.__.test2.terminal.remote import slave_runsession + from py.__.test2.looponfail.remote import slave_runsession from py.__.test2.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) slave_runsession(channel, config) Modified: py/branch/event/py/test2/looponfail/testing/test_remote.py ============================================================================== --- py/branch/event/py/test2/terminal/testing/test_remote.py (original) +++ py/branch/event/py/test2/looponfail/testing/test_remote.py Thu Aug 14 15:41:58 2008 @@ -1,6 +1,6 @@ import py from py.__.test2.testing import suptest -from py.__.test2.terminal.remote import LooponfailingSession, LoopState, RemoteControl +from py.__.test2.looponfail.remote import LooponfailingSession, LoopState, RemoteControl from py.__.test2 import event def getevent(l, evtype): Copied: py/branch/event/py/test2/looponfail/testing/test_util.py (from r57235, py/branch/event/py/test2/dsession/testing/test_looponfailing.py) ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_looponfailing.py (original) +++ py/branch/event/py/test2/looponfail/testing/test_util.py Thu Aug 14 15:41:58 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2.dsession.looponfailing import StatRecorder, EventRecorder +from py.__.test2.looponfail.util import StatRecorder, EventRecorder from py.__.test2 import event def test_filechange(): Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Thu Aug 14 15:41:58 2008 @@ -223,7 +223,7 @@ assert failed[1].colitem.name == "test_other" assert failed[2].colitem.name == "test_two" -from py.__.test2.terminal.remote import LooponfailingSession +from py.__.test2.looponfail.remote import LooponfailingSession class Test_TerminalRemoteSession: def test_exec_leads_to_DistSession(self): From hpk at codespeak.net Thu Aug 14 16:00:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 16:00:18 +0200 (CEST) Subject: [py-svn] r57248 - py/branch/event/py/test2/testing Message-ID: <20080814140018.AD46B16A021@codespeak.net> Author: hpk Date: Thu Aug 14 16:00:15 2008 New Revision: 57248 Modified: py/branch/event/py/test2/testing/acceptance_test.py Log: add (passing) looponfailing acceptance test Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Thu Aug 14 16:00:15 2008 @@ -352,7 +352,9 @@ import pexpect except ImportError: py.test.skip("need pexpect") - return pexpect.spawn + def spawn(cmd): + return pexpect.spawn(cmd, logfile=self.tmpdir.join("spawn.out").open("w")) + return spawn def test_pdb_interaction(self): spawn = self.getspawn() @@ -372,7 +374,6 @@ def test_simple_looponfailing_interaction(self): - py.test.skip("implement --looponfailing") spawn = self.getspawn() test_one = self.makepyfile(test_one=""" def test_1(): @@ -380,17 +381,21 @@ """) child = spawn("%s %s --looponfailing test_one.py" % (py.std.sys.executable, - str(pytestpath)[:-1])) + str(pytestpath))) child.timeout = EXPECTTIMEOUT child.expect("assert 1 == 0") child.expect("test_one.py:") child.expect("failures: 1") + + child.expect("waiting for changes") test_one.write(py.code.Source(""" def test_1(): assert 1 == 1 """)) - child.expect("file change detected", timeout=2.0) - child.expect("failures: no failures :)") - child.kill() + pyc = test_one + "c" + if pyc.check(): pyc.remove() # XXX + child.expect("MODIFIED.*test_one.py", timeout=4.0) + child.expect("failures: no failures", timeout=5.0) + child.kill(15) From hpk at codespeak.net Thu Aug 14 16:58:43 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 16:58:43 +0200 (CEST) Subject: [py-svn] r57251 - in py/branch/event/py/test2: looponfail looponfail/testing testing Message-ID: <20080814145843.BAF31169F7B@codespeak.net> Author: hpk Date: Thu Aug 14 16:58:43 2008 New Revision: 57251 Modified: py/branch/event/py/test2/looponfail/testing/test_remote.py py/branch/event/py/test2/looponfail/testing/test_util.py py/branch/event/py/test2/looponfail/util.py py/branch/event/py/test2/testing/acceptance_test.py Log: * remove pycfiles by default for changed .py files * enable acceptance test for looponfailing Modified: py/branch/event/py/test2/looponfail/testing/test_remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/testing/test_remote.py (original) +++ py/branch/event/py/test2/looponfail/testing/test_remote.py Thu Aug 14 16:58:43 2008 @@ -69,8 +69,6 @@ def test_two(): assert 1 """)) - pyc = modcol.fspath + "c" - if pyc.check(): pyc.remove() # XXX move to statrecorder? assert session.statrecorder.check() events[:] = [] session.loop_once(loopstate) Modified: py/branch/event/py/test2/looponfail/testing/test_util.py ============================================================================== --- py/branch/event/py/test2/looponfail/testing/test_util.py (original) +++ py/branch/event/py/test2/looponfail/testing/test_util.py Thu Aug 14 16:58:43 2008 @@ -35,6 +35,23 @@ changed = sd.check() assert changed +def test_pycremoval(): + tmp = py.test.ensuretemp("test_pycremoval") + hello = tmp.ensure("hello.py") + sd = StatRecorder([tmp]) + changed = sd.check() + assert not changed + + pycfile = hello + "c" + pycfile.ensure() + changed = sd.check() + assert not changed + + hello.write("world") + changed = sd.check() + assert not pycfile.check() + + def test_waitonchange(): tmp = py.test.ensuretemp("test_waitonchange") sd = StatRecorder([tmp]) Modified: py/branch/event/py/test2/looponfail/util.py ============================================================================== --- py/branch/event/py/test2/looponfail/util.py (original) +++ py/branch/event/py/test2/looponfail/util.py Thu Aug 14 16:58:43 2008 @@ -19,7 +19,7 @@ return py.std.time.sleep(checkinterval) - def check(self): + def check(self, removepycfiles=True): changed = False statcache = self.statcache newstat = {} @@ -40,6 +40,11 @@ oldstat.size != curstat.size: changed = True print "# MODIFIED", path + if removepycfiles and path.ext == ".py": + pycfile = path + "c" + if pycfile.check(): + pycfile.remove() + else: changed = True if statcache: Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Thu Aug 14 16:58:43 2008 @@ -379,22 +379,18 @@ def test_1(): assert 1 == 0 """) - + test_one.setmtime(test_one.mtime() - 5.0) child = spawn("%s %s --looponfailing test_one.py" % (py.std.sys.executable, str(pytestpath))) child.timeout = EXPECTTIMEOUT child.expect("assert 1 == 0") child.expect("test_one.py:") child.expect("failures: 1") - child.expect("waiting for changes") - test_one.write(py.code.Source(""" def test_1(): assert 1 == 1 """)) - pyc = test_one + "c" - if pyc.check(): pyc.remove() # XXX child.expect("MODIFIED.*test_one.py", timeout=4.0) child.expect("failures: no failures", timeout=5.0) child.kill(15) From hpk at codespeak.net Thu Aug 14 17:11:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 17:11:41 +0200 (CEST) Subject: [py-svn] r57254 - in py/branch/event/py/test2: . report Message-ID: <20080814151141.A52FC169FEE@codespeak.net> Author: hpk Date: Thu Aug 14 17:11:41 2008 New Revision: 57254 Modified: py/branch/event/py/test2/event.py py/branch/event/py/test2/report/terminal.py Log: format InternalException immediately, make it picklable Modified: py/branch/event/py/test2/event.py ============================================================================== --- py/branch/event/py/test2/event.py (original) +++ py/branch/event/py/test2/event.py Thu Aug 14 17:11:41 2008 @@ -54,7 +54,7 @@ def __init__(self, excinfo=None): if excinfo is None: excinfo = py.code.ExceptionInfo() - self.excinfo = excinfo + self.repr = excinfo.getrepr(funcargs=True, showlocals=True) # ---------------------------------------------------------------------- # Events related to collecting and executing test Items Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Thu Aug 14 17:11:41 2008 @@ -35,7 +35,7 @@ self._tw.line(line, **markup) def rep_InternalException(self, ev): - for line in str(ev.excinfo.getrepr()).split("\n"): + for line in str(ev.repr).split("\n"): self.write_line("InternalException: " + line) def rep_HostGatewayReady(self, ev): From hpk at codespeak.net Thu Aug 14 17:37:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 17:37:36 +0200 (CEST) Subject: [py-svn] r57257 - in py/branch/event/py/code: . testing Message-ID: <20080814153736.425D2169FC5@codespeak.net> Author: hpk Date: Thu Aug 14 17:37:35 2008 New Revision: 57257 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py Log: fix off-by-one error Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Thu Aug 14 17:37:35 2008 @@ -54,7 +54,7 @@ exconly = self.exconly(tryshort=True) entry = self.traceback.getcrashentry() path, lineno = entry.path, entry.lineno - reprcrash = ReprFileLocation(path, lineno, exconly) + reprcrash = ReprFileLocation(path, lineno+1, exconly) return reprcrash def getrepr(self, showlocals=False, style="long", tbfilter=True, funcargs=False): Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Thu Aug 14 17:37:35 2008 @@ -469,9 +469,9 @@ excinfo = py.test.raises(ValueError, mod.entry) repr = excinfo.getrepr() assert repr.reprcrash.path.endswith("mod.py") - assert repr.reprcrash.lineno == 2 + assert repr.reprcrash.lineno == 3 assert repr.reprcrash.message == "ValueError" - assert str(repr.reprcrash).endswith("mod.py:2: ValueError") + assert str(repr.reprcrash).endswith("mod.py:3: ValueError") def test_repr_traceback_recursion(self): mod = self.importasmod(""" From hpk at codespeak.net Thu Aug 14 17:54:40 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 17:54:40 +0200 (CEST) Subject: [py-svn] r57258 - in py/branch/event/py/test2: dsession/testing report/testing testing Message-ID: <20080814155440.6DA0C169F94@codespeak.net> Author: hpk Date: Thu Aug 14 17:54:38 2008 New Revision: 57258 Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/report/testing/test_collectonly.py py/branch/event/py/test2/report/testing/test_terminal.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/suptest.py Log: unify test machinery, add failing verbose-reporting acceptance test Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Thu Aug 14 17:54:38 2008 @@ -271,7 +271,7 @@ """) session = DSession(modcol._config) - modcol._config.option.keyword = modcol.name + modcol._config.option.keyword = "nothing" dsel = session.filteritems([modcol]) assert dsel == [modcol] items = [modcol.join(x) for x in modcol.listdir()] Modified: py/branch/event/py/test2/report/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/report/testing/test_collectonly.py Thu Aug 14 17:54:38 2008 @@ -1,39 +1,12 @@ import py from py.__.test2.report.collectonly import CollectonlyReporter from py.__.test2 import event -from py.__.test2.testing import suptest +from py.__.test2.testing.suptest import ( + InlineCollection, popvalue, assert_stringio_contains_lines) -def popvalue(stringio): - value = stringio.getvalue().rstrip() - stringio.truncate(0) - return value - -def assert_stringio_contains_lines(stringio, tomatchlines): - stringio.seek(0) - l = stringio.readlines() - l = map(str.rstrip, l) - suptest.assert_lines_contain_lines(l, tomatchlines) - -class InlineCollect: - def setup_class(cls): - tmpdir = py.test.ensuretemp(__name__) - cls.clstmpdir = tmpdir.join(cls.__name__) - - def setup_method(self, method): - self.tmpdir = self.clstmpdir.join(method.__name__) - - def getmodulecol(self, configargs, source): - self.tmpdir.ensure("__init__.py") - path = self.tmpdir.ensure(self.tmpdir.purebasename + ".py") - path.write(py.code.Source(source)) - print "wrote InlineCollect example to", path - config = py.test2.config._reparse([path] + configargs) - self.session = config.initsession() - return config.getfsnode(path) - -class TestCollectonly(InlineCollect): +class TestCollectonly(InlineCollection): def test_collectonly_basic(self): - modcol = self.getmodulecol(['--collectonly'], """ + modcol = self.getmodulecol(configargs=['--collectonly'], source=""" def test_func(): pass """) @@ -42,7 +15,7 @@ indent = rep.indent rep.processevent(event.CollectionStart(modcol)) s = popvalue(stringio) - assert s == "" + assert s == "" item = modcol.join("test_func") rep.processevent(event.ItemStart(item)) @@ -52,21 +25,21 @@ assert rep.indent == indent def test_collectonly_skipped_module(self): - modcol = self.getmodulecol(['--collectonly'], """ + modcol = self.getmodulecol(configargs=['--collectonly'], source=""" import py py.test2.skip("nomod") - """) + """, withsession=True) stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, bus=self.session.bus, out=stringio) cols = list(self.session.genitems([modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ - + !!! Skipped: 'nomod' !!! """) def test_collectonly_failed_module(self): - modcol = self.getmodulecol(['--collectonly'], """ + modcol = self.getmodulecol(configargs=['--collectonly'], source=""" raise ValueError(0) """) stringio = py.std.cStringIO.StringIO() @@ -74,7 +47,7 @@ cols = list(self.session.genitems([modcol])) assert len(cols) == 0 assert_stringio_contains_lines(stringio, """ - + !!! ValueError: 0 !!! """) Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Thu Aug 14 17:54:38 2008 @@ -3,10 +3,11 @@ from py.__.test2 import event #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report -from test_collectonly import InlineCollect, popvalue, assert_stringio_contains_lines +from py.__.test2.testing.suptest import ( + InlineCollection, popvalue, assert_stringio_contains_lines) from py.__.test2.dsession.hostmanage import Host -class TestTerminal(InlineCollect): +class TestTerminal(InlineCollection): def test_session_reporter_subscription(self): config = py.test2.config._reparse(['xxx']) session = config.initsession() @@ -18,7 +19,7 @@ #assert rep.processevent not in session.bus._subscribers def test_pass_skip_fail(self): - modcol = self.getmodulecol([], """ + modcol = self.getmodulecol(""" import py def test_ok(): pass @@ -26,7 +27,7 @@ py.test2.skip("xx") def test_func(): assert 0 - """) + """, withsession=True) stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) rep.processevent(event.TestrunStart()) @@ -43,9 +44,9 @@ ]) def test_collect_fail(self): - modcol = self.getmodulecol([], """ + modcol = self.getmodulecol(""" import xyz - """) + """, withsession=True) stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, bus=self.session.bus, file=stringio) rep.processevent(event.TestrunStart()) @@ -61,7 +62,7 @@ ]) def test_internal_exception(self): - modcol = self.getmodulecol([], "def test_one(): pass") + modcol = self.getmodulecol("def test_one(): pass") stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) excinfo = py.test2.raises(ValueError, "raise ValueError('hello')") @@ -70,7 +71,7 @@ assert s.find("InternalException:") != -1 def test_hostready_crash(self): - modcol = self.getmodulecol([], """ + modcol = self.getmodulecol(""" def test_one(): pass """) @@ -86,7 +87,7 @@ assert s.find("myerror") != -1 def test_writeline(self): - modcol = self.getmodulecol([], "def test_one(): pass") + modcol = self.getmodulecol("def test_one(): pass") stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) rep.write_fspath_result(py.path.local("xy.py"), '.') @@ -97,7 +98,7 @@ assert lines[2] == "hello world" def test_looponfailingreport(self): - modcol = self.getmodulecol([], """ + modcol = self.getmodulecol(""" def test_fail(): assert 0 def test_fail2(): @@ -114,4 +115,3 @@ "*%s*" % (modcol._config.topdir), "*waiting*", ]) - Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Thu Aug 14 17:54:38 2008 @@ -3,7 +3,7 @@ pydir = py.path.local(py.__file__).dirpath() pytestpath = pydir.join("bin", "py.test2") -EXPECTTIMEOUT=1.0 +EXPECTTIMEOUT=10.0 def setup_module(mod): mod.modtmpdir = py.test2.ensuretemp(mod.__name__) @@ -115,7 +115,7 @@ "*test_one.py ss", "*test_two.py - Skipped*", "___* skipped test summary *_", - "*conftest.py:2: *3* Skipped: 'test'", + "*conftest.py:3: *3* Skipped: 'test'", ]) assert result.ret == 0 @@ -333,11 +333,11 @@ def test_keyboard_interrupt(self): p1 = self.makepyfile(test_one=""" - import py - def test_fail(): - raise ValueError() - def test_inter(): - raise KeyboardInterrupt() + import py + def test_fail(): + raise ValueError() + def test_inter(): + raise KeyboardInterrupt() """) result = self.runpytest(p1) assert_lines_contain_lines(result.outlines, [ @@ -346,6 +346,30 @@ "*0/1 passed*", ]) + def test_verbose_reporting(self): + p1 = self.makepyfile(test_one=""" + import py + def test_fail(): + raise ValueError() + def test_pass(): + pass + class TestClass: + def test_skip(self): + py.test.skip("hello") + def test_gen(): + def check(x): + assert x == 1 + yield check, 0 + """) + result = self.runpytest(p1, '-v') + assert_lines_contain_lines(result.outlines, [ + "*test_one.py:2: test_fail*FAIL", + "*test_one.py:4: test_pass*PASS", + "*test_one.py:7: TestClass.test_skip*SKIP", + "*test_one.py:10: test_gen.check*FAIL", + ]) + assert result.ret == 1 + class TestInteractive(AcceptBase): def getspawn(self): try: Modified: py/branch/event/py/test2/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test2/testing/suptest.py Thu Aug 14 17:54:38 2008 @@ -190,10 +190,13 @@ class InlineCollection(FileCreation): """ helps to collect and run test functions inlining other test functions. """ - def getmodulecol(self, source): + def getmodulecol(self, source, configargs=(), withsession=False): self.tmpdir.ensure("__init__.py") - path = self.makepyfile(funcname=py.code.Source(source).strip()) - self.config = self.parseconfig(path) + kw = {self.tmpdir.basename: py.code.Source(source).strip()} + path = self.makepyfile(**kw) + self.config = self.parseconfig(path, *configargs) + if withsession: + self.session = self.config.initsession() return self.config.getfsnode(path) def parseconfig(self, *args): @@ -215,3 +218,13 @@ return runner(item, **runnerargs) +def popvalue(stringio): + value = stringio.getvalue().rstrip() + stringio.truncate(0) + return value + +def assert_stringio_contains_lines(stringio, tomatchlines): + stringio.seek(0) + l = stringio.readlines() + l = map(str.rstrip, l) + assert_lines_contain_lines(l, tomatchlines) From hpk at codespeak.net Thu Aug 14 18:33:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 14 Aug 2008 18:33:41 +0200 (CEST) Subject: [py-svn] r57259 - in py/branch/event/py/test2: report report/testing testing Message-ID: <20080814163341.3090916A000@codespeak.net> Author: hpk Date: Thu Aug 14 18:33:40 2008 New Revision: 57259 Modified: py/branch/event/py/test2/report/base.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_basereporter.py py/branch/event/py/test2/report/testing/test_terminal.py py/branch/event/py/test2/testing/acceptance_test.py Log: implement verbose reporting, slightly hackish, still Modified: py/branch/event/py/test2/report/base.py ============================================================================== --- py/branch/event/py/test2/report/base.py (original) +++ py/branch/event/py/test2/report/base.py Thu Aug 14 18:33:40 2008 @@ -86,17 +86,20 @@ return str(v) def getrelpath(curdir, dest): - base = curdir.common(dest) - if not base: # can be the case on windows + try: + base = curdir.common(dest) + if not base: # can be the case on windows + return dest + curdir2base = curdir.relto(base) + reldest = dest.relto(base) + if curdir2base: + n = curdir2base.count(curdir.sep) + 1 + else: + n = 0 + l = ['..'] * n + if reldest: + l.append(reldest) + target = dest.sep.join(l) + return target + except AttributeError: return dest - curdir2base = curdir.relto(base) - reldest = dest.relto(base) - if curdir2base: - n = curdir2base.count(curdir.sep) + 1 - else: - n = 0 - l = ['..'] * n - if reldest: - l.append(reldest) - target = dest.sep.join(l) - return target Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Thu Aug 14 18:33:40 2008 @@ -4,7 +4,7 @@ from py.__.test2.report.base import BaseReporter from py.__.test2.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped -from py.__.test2.report.base import getrelpath, repr_pythonversion +from py.__.test2.report.base import getrelpath, repr_pythonversion, getmodpath class TerminalReporter(BaseReporter): def __init__(self, config, file=None, bus=None): @@ -34,6 +34,12 @@ self.currentfspath = None self._tw.line(line, **markup) + def getoutcomeword(self, item): + if item.passed: return self._tw.markup("PASS", green=True) + elif item.failed: return self._tw.markup("FAIL", red=True) + elif item.skipped: return "SKIP" + else: return self._tw.markup("???", red=True) + def rep_InternalException(self, ev): for line in str(ev.repr).split("\n"): self.write_line("InternalException: " + line) @@ -50,7 +56,19 @@ def rep_ItemTestReport(self, ev): super(TerminalReporter, self).rep_ItemTestReport(ev) fspath = ev.colitem.fspath - self.write_fspath_result(fspath, ev.outcome.shortrepr) + if not self.config.option.verbose: + self.write_fspath_result(fspath, ev.outcome.shortrepr) + else: + fspath, lineno = ev.colitem.getfsloc() + try: + _, lineno = py.std.inspect.findsource(ev.colitem.obj) + except IOError: + lineno = -2 + relpath = getrelpath(self.curdir, fspath) + word = self.getoutcomeword(ev) + modpath = getmodpath(ev.colitem) + self.write_line("%s:%d: %s %s" % ( + relpath, lineno+1, modpath, word)) def rep_CollectionReport(self, ev): super(TerminalReporter, self).rep_CollectionReport(ev) Modified: py/branch/event/py/test2/report/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_basereporter.py (original) +++ py/branch/event/py/test2/report/testing/test_basereporter.py Thu Aug 14 18:33:40 2008 @@ -114,3 +114,5 @@ s = getrelpath(curdir, curdir.dirpath().join("sister")) assert s == ".." + sep + "sister" assert getrelpath(curdir, curdir.dirpath()) == ".." + + assert getrelpath(curdir, "hello") == "hello" Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Thu Aug 14 18:33:40 2008 @@ -43,6 +43,36 @@ "E assert 0", ]) + def test_pass_skip_fail_verbose(self): + modcol = self.getmodulecol(""" + import py + def test_ok(): + pass + def test_skip(): + py.test2.skip("xx") + def test_func(): + assert 0 + """, configargs=("-v",), withsession=True) + stringio = py.std.cStringIO.StringIO() + rep = TerminalReporter(modcol._config, file=stringio) + rep.processevent(event.TestrunStart()) + items = [modcol.join(x) for x in modcol.listdir()] + for item in items: + ev = basic_run_report(item) + rep.processevent(ev) + + assert_stringio_contains_lines(stringio, [ + "*test_pass_skip_fail_verbose.py:2: *test_ok*PASS", + "*test_pass_skip_fail_verbose.py:4: *test_skip*SKIP", + "*test_pass_skip_fail_verbose.py:6: *test_func*FAIL", + ]) + rep.processevent(event.TestrunFinish()) + assert_stringio_contains_lines(stringio, [ + " def test_func():", + "> assert 0", + "E assert 0", + ]) + def test_collect_fail(self): modcol = self.getmodulecol(""" import xyz Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Thu Aug 14 18:33:40 2008 @@ -363,10 +363,10 @@ """) result = self.runpytest(p1, '-v') assert_lines_contain_lines(result.outlines, [ - "*test_one.py:2: test_fail*FAIL", - "*test_one.py:4: test_pass*PASS", - "*test_one.py:7: TestClass.test_skip*SKIP", - "*test_one.py:10: test_gen.check*FAIL", + "*test_one.py:2: test_one.test_fail*FAIL", + "*test_one.py:4: test_one.test_pass*PASS", + "*test_one.py:7: test_one.TestClass().test_skip*SKIP", + "*test_one.py:10: test_one.test_gen*FAIL", ]) assert result.ret == 1 From hpk at codespeak.net Fri Aug 15 09:01:58 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 09:01:58 +0200 (CEST) Subject: [py-svn] r57262 - in py/branch/event/py/test2: . testing Message-ID: <20080815070158.98F53169E4D@codespeak.net> Author: hpk Date: Fri Aug 15 09:01:56 2008 New Revision: 57262 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/test_doctest.py py/branch/event/py/test2/testing/test_runner_functional.py Log: rename getfailurerepr to repr_failure Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Fri Aug 15 09:01:56 2008 @@ -220,7 +220,7 @@ col = config.getfsnode(fspath) return col._getitembynames(names) - def _getfailurerepr_py(self, excinfo, outerr): + def _repr_failure_py(self, excinfo, outerr): excinfo.traceback = self._prunetraceback(excinfo.traceback) repr = excinfo.getrepr(funcargs=True, showlocals=self._config.option.showlocals) @@ -229,7 +229,7 @@ repr.addsection("Captured std%s" % secname, content.rstrip()) return repr - getfailurerepr = _getfailurerepr_py + repr_failure = _repr_failure_py shortfailurerepr = "F" @@ -269,8 +269,8 @@ """ raise NotImplementedError("abstract") - def getfailurerepr(self, excinfo, outerr): - return self._getfailurerepr_py(excinfo, outerr) + def repr_failure(self, excinfo, outerr): + return self._repr_failure_py(excinfo, outerr) class FSCollector(Collector): def __init__(self, fspath, parent=None, config=None): Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Fri Aug 15 09:01:56 2008 @@ -216,8 +216,8 @@ traceback = ntraceback.filter() return traceback - def getfailurerepr(self, excinfo, outerr): - return self._getfailurerepr_py(excinfo, outerr) + def repr_failure(self, excinfo, outerr): + return self._repr_failure_py(excinfo, outerr) shortfailurerepr = "F" @@ -286,7 +286,7 @@ self.reprlocation.toterminal(tw) class DoctestFileContent(Item): - def getfailurerepr(self, excinfo, outerr): + def repr_failure(self, excinfo, outerr): if excinfo.errisinstance(py.compat.doctest.DocTestFailure): doctestfailure = excinfo.value example = doctestfailure.example @@ -308,7 +308,7 @@ return ReprFailDoctest(reprlocation, lines) #elif excinfo.errisinstance(py.compat.doctest.UnexpectedException): else: - return super(DoctestFileContent, self).getfailurerepr(excinfo, outerr) + return super(DoctestFileContent, self).repr_failure(excinfo, outerr) def execute(self): failed, tot = py.compat.doctest.testfile( Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Aug 15 09:01:56 2008 @@ -55,10 +55,10 @@ else: outcome = "failed" if when == "execute": - longrepr = self.colitem.getfailurerepr(excinfo, outerr) + longrepr = self.colitem.repr_failure(excinfo, outerr) shortrepr = self.colitem.shortfailurerepr else: - longrepr = self.colitem._getfailurerepr_py(excinfo, outerr) + longrepr = self.colitem._repr_failure_py(excinfo, outerr) shortrepr = self.colitem.shortfailurerepr.lower() kw = { outcome: OutcomeRepr(when, shortrepr, longrepr)} return kw Modified: py/branch/event/py/test2/testing/test_doctest.py ============================================================================== --- py/branch/event/py/test2/testing/test_doctest.py (original) +++ py/branch/event/py/test2/testing/test_doctest.py Fri Aug 15 09:01:56 2008 @@ -27,5 +27,5 @@ """)) testitem = py.test.collect.DoctestFile(p).join(p.basename) excinfo = py.test.raises(Failed, "testitem.execute()") - repr = testitem.getfailurerepr(excinfo, ("", "")) + repr = testitem.repr_failure(excinfo, ("", "")) assert repr.reprlocation Modified: py/branch/event/py/test2/testing/test_runner_functional.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner_functional.py (original) +++ py/branch/event/py/test2/testing/test_runner_functional.py Fri Aug 15 09:01:56 2008 @@ -93,7 +93,7 @@ self.makepyfile(conftest=""" import py class Function(py.test2.collect.Function): - def getfailurerepr(self, excinfo, outerr): + def repr_failure(self, excinfo, outerr): return "hello" """) ev = self.runitem(""" @@ -113,7 +113,7 @@ self.makepyfile(conftest=""" import py class Function(py.test2.collect.Function): - def getfailurerepr(self, excinfo): + def repr_failure(self, excinfo): assert 0 """) ev = self.runitem(""" From hpk at codespeak.net Fri Aug 15 11:07:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 11:07:32 +0200 (CEST) Subject: [py-svn] r57263 - in py/branch/event/py/code: . testing Message-ID: <20080815090732.E9E3E16856B@codespeak.net> Author: hpk Date: Fri Aug 15 11:07:31 2008 New Revision: 57263 Modified: py/branch/event/py/code/code.py py/branch/event/py/code/testing/test_code.py Log: consistently return TypeError for non-code objects Modified: py/branch/event/py/code/code.py ============================================================================== --- py/branch/event/py/code/code.py (original) +++ py/branch/event/py/code/code.py Fri Aug 15 11:07:31 2008 @@ -6,12 +6,12 @@ rawcode = getattr(rawcode, 'im_func', rawcode) rawcode = getattr(rawcode, 'func_code', rawcode) self.raw = rawcode - self.filename = rawcode.co_filename try: + self.filename = rawcode.co_filename self.firstlineno = rawcode.co_firstlineno - 1 + self.name = rawcode.co_name except AttributeError: raise TypeError("not a code object: %r" %(rawcode,)) - self.name = rawcode.co_name def __eq__(self, other): return self.raw == other.raw Modified: py/branch/event/py/code/testing/test_code.py ============================================================================== --- py/branch/event/py/code/testing/test_code.py (original) +++ py/branch/event/py/code/testing/test_code.py Fri Aug 15 11:07:31 2008 @@ -78,4 +78,8 @@ code = py.code.Code(co_code) assert str(code.path) == name assert code.fullsource is None - + +def test_code_with_class(): + class A: + pass + py.test.raises(TypeError, "py.code.Code(A)") From hpk at codespeak.net Fri Aug 15 11:14:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 11:14:02 +0200 (CEST) Subject: [py-svn] r57264 - in py/branch/event/py/test2: . report testing Message-ID: <20080815091402.A2A99168574@codespeak.net> Author: hpk Date: Fri Aug 15 11:14:02 2008 New Revision: 57264 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/report/base.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/runner.py py/branch/event/py/test2/testing/acceptance_test.py py/branch/event/py/test2/testing/test_collect.py Log: * introduce a draft Item.repr_metainfo() for representing meta information about a test * refine presentation of verbose mode * unify towards a getfslineno() method available on pyobjects Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Fri Aug 15 11:14:02 2008 @@ -61,6 +61,28 @@ col.setup() self.stack.append(col) +class ReprMetaInfo(object): + def __init__(self, fspath=None, lineno=None, modpath=None): + self.fspath = fspath + self.lineno = lineno + self.modpath = modpath + + def verboseline(self, basedir=None): + params = self.__dict__.copy() + if self.fspath: + if basedir is not None: + params['fspath'] = getrelpath(basedir, self.fspath) + if self.lineno is not None: + params['lineno'] = self.lineno + 1 + if self.fspath and self.lineno and self.modpath: + line = "%(fspath)s:%(lineno)s: %(modpath)s" + elif self.fspath and self.lineno: + line = "%(fspath)s:%(lineno)s" + else: + line = "[nometainfo]" + return line % params + + class Node(object): """ base class for Nodes in the collection tree. Collector nodes have children and @@ -74,6 +96,7 @@ - configuration/options for setup/teardown stdout/stderr capturing and execution of test items """ + ReprMetaInfo = ReprMetaInfo # XXX we keep global SetupState here because # pycollect's Generators participate # in setup/teardown procedures during collect. @@ -360,3 +383,30 @@ if self._config.option.boxed: return forked_run_report return basic_run_report + + def repr_metainfo(self): + code = py.code.Code(self.execute) + return self.ReprMetaInfo(code.path, code.firstlineno) + + def execute(self): + """ execute this test item.""" + + +def getrelpath(curdir, dest): + try: + base = curdir.common(dest) + if not base: # can be the case on windows + return dest + curdir2base = curdir.relto(base) + reldest = dest.relto(base) + if curdir2base: + n = curdir2base.count(curdir.sep) + 1 + else: + n = 0 + l = ['..'] * n + if reldest: + l.append(reldest) + target = dest.sep.join(l) + return target + except AttributeError: + return dest Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Fri Aug 15 11:14:02 2008 @@ -20,6 +20,56 @@ def _getobj(self): return getattr(self.parent.obj, self.name) + def getmodpath(self, stopatmodule=True): + """ return python path relative to the containing module. """ + chain = self.listchain() + chain.reverse() + parts = [] + for node in chain: + if isinstance(node, Instance): + continue + if stopatmodule and isinstance(node, Module): + break + parts.append(node.name) + parts.reverse() + s = ".".join(parts) + return s.replace(".[", "[") + + def getfslineno(self): + try: + return self._fslineno + except AttributeError: + pass + try: + code = py.code.Code(self.obj) + except TypeError: + # fallback to + fn = (py.std.inspect.getsourcefile(self.obj) or + py.std.inspect.getfile(self.obj)) + fspath = fn and py.path.local(fn) or None + if fspath: + try: + lines, lineno = py.std.inspect.findsource(self.obj) + except IOError: + lineno = None + else: + lineno = None + else: + fspath = code.path + lineno = code.firstlineno + self._fslineno = fspath, lineno + return fspath, lineno + + def repr_metainfo(self): + fspath, lineno = self.getfslineno() + modpath = self.getmodpath() + return self.ReprMetaInfo( + fspath=fspath, + lineno=lineno, + modpath=modpath, + ) + + class PyCollectorMixin(PyobjMixin, Collector): Class = configproperty('Class') Instance = configproperty('Instance') @@ -147,18 +197,9 @@ teardown_class = getattr(teardown_class, 'im_func', teardown_class) teardown_class(self.obj) - def _getsortvalue(self): - # try to locate the class in the source - not nice, but probably - # the most useful "solution" that we have - try: - file = (py.std.inspect.getsourcefile(self.obj) or - py.std.inspect.getfile(self.obj)) - if not file: - raise IOError - lines, lineno = py.std.inspect.findsource(self.obj) - return py.path.local(file), lineno - except IOError: - pass + def _getsortvalue(self): + return self.getfslineno() + class Instance(PyCollectorMixin, Collector): def _getobj(self): @@ -175,14 +216,7 @@ """ mixin for the code common to Function and Generator. """ def _getsortvalue(self): - return self.getfsloc() - - _fsloc = None - def getfsloc(self): - if self._fsloc is None: - code = py.code.Code(self.obj) - self._fsloc = code.path, code.firstlineno - return self._fsloc + return self.getfslineno() def setup(self): """ perform setup for this test function. """ Modified: py/branch/event/py/test2/report/base.py ============================================================================== --- py/branch/event/py/test2/report/base.py (original) +++ py/branch/event/py/test2/report/base.py Fri Aug 15 11:14:02 2008 @@ -1,4 +1,6 @@ from py.__.test2 import event +from py.__.test2.collect import getrelpath + import sys class BaseReporter(object): @@ -85,21 +87,3 @@ except (TypeError, ValueError): return str(v) -def getrelpath(curdir, dest): - try: - base = curdir.common(dest) - if not base: # can be the case on windows - return dest - curdir2base = curdir.relto(base) - reldest = dest.relto(base) - if curdir2base: - n = curdir2base.count(curdir.sep) + 1 - else: - n = 0 - l = ['..'] * n - if reldest: - l.append(reldest) - target = dest.sep.join(l) - return target - except AttributeError: - return dest Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Fri Aug 15 11:14:02 2008 @@ -59,16 +59,10 @@ if not self.config.option.verbose: self.write_fspath_result(fspath, ev.outcome.shortrepr) else: - fspath, lineno = ev.colitem.getfsloc() - try: - _, lineno = py.std.inspect.findsource(ev.colitem.obj) - except IOError: - lineno = -2 - relpath = getrelpath(self.curdir, fspath) + info = ev.colitem.repr_metainfo() + line = info.verboseline(basedir=self.curdir) word = self.getoutcomeword(ev) - modpath = getmodpath(ev.colitem) - self.write_line("%s:%d: %s %s" % ( - relpath, lineno+1, modpath, word)) + self.write_line("%s %s" %(line, word)) def rep_CollectionReport(self, ev): super(TerminalReporter, self).rep_CollectionReport(ev) Modified: py/branch/event/py/test2/runner.py ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test2/runner.py Fri Aug 15 11:14:02 2008 @@ -138,7 +138,7 @@ return report_crash(item, result) def report_crash(item, result): - path, lineno = item.getfsloc() + path, lineno = item.getfslineno() longrepr = [ ("X", "CRASHED"), ("%s:%s: CRASHED with signal %d" %(path, lineno, result.signal)), Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Fri Aug 15 11:14:02 2008 @@ -363,10 +363,10 @@ """) result = self.runpytest(p1, '-v') assert_lines_contain_lines(result.outlines, [ - "*test_one.py:2: test_one.test_fail*FAIL", - "*test_one.py:4: test_one.test_pass*PASS", - "*test_one.py:7: test_one.TestClass().test_skip*SKIP", - "*test_one.py:10: test_one.test_gen*FAIL", + "*test_one.py:2: test_fail*FAIL", + "*test_one.py:4: test_pass*PASS", + "*test_one.py:7: TestClass.test_skip*SKIP", + "*test_one.py:10: test_gen*FAIL", ]) assert result.ret == 1 Modified: py/branch/event/py/test2/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test2/testing/test_collect.py Fri Aug 15 11:14:02 2008 @@ -383,7 +383,7 @@ assert items[0].name == 'test_one' assert items[1].name == 'test_method_one' assert items[2].name == 'test_method_one' - + def test_custom_python_collection_from_conftest(self): checkfile = setupdata.setup_customconfigtest(self.tmp) for x in (self.tmp, checkfile, checkfile.dirpath()): @@ -481,7 +481,67 @@ col3 = config.getfsnode(config.topdir.dirpath()) py.test2.raises(ValueError, "col3._totrail()") - + +class TestCollectorReprs(suptest.InlineCollection): + def test_repr_metainfo_basic_item(self): + modcol = self.getmodulecol("") + Item = py.test2.collect.Item + item = Item("virtual", parent=modcol) + info = item.repr_metainfo() + code = py.code.Code(Item.execute) + assert info.fspath == code.path + assert info.lineno == code.firstlineno + assert not info.modpath + + def test_repr_metainfo_func(self): + item = self.getitem("def test_func(): pass") + info = item.repr_metainfo() + assert info.fspath == item.fspath + assert info.lineno == 0 + assert info.modpath == "test_func" + + def test_repr_metainfo_class(self): + modcol = self.getmodulecol(""" + # lineno 0 + class TestClass: + def test_hello(self): pass + """) + classcol = modcol.join("TestClass") + info = classcol.repr_metainfo() + assert info.fspath == modcol.fspath + assert info.lineno == 1 + assert info.modpath == "TestClass" + + def test_repr_metainfo_generator(self): + modcol = self.getmodulecol(""" + # lineno 0 + def test_gen(): + def check(x): + assert x + yield check, 3 + """) + gencol = modcol.join("test_gen") + info = gencol.repr_metainfo() + assert info.fspath == modcol.fspath + assert info.lineno == 1 + assert info.modpath == "test_gen" + + genitem = gencol.join(gencol.listdir()[0]) + info = genitem.repr_metainfo() + assert info.fspath == modcol.fspath + assert info.lineno == 2 + assert info.modpath == "test_gen[0]" + """ + def test_func(): + pass + def test_genfunc(): + def check(x): + pass + yield check, 3 + class TestClass: + def test_method(self): + pass + """ from py.__.test2.dsession.mypickle import ImmutablePickler class PickleTransport: From hpk at codespeak.net Fri Aug 15 11:42:48 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 11:42:48 +0200 (CEST) Subject: [py-svn] r57265 - in py/branch/event/py/test2/dsession: . testing Message-ID: <20080815094248.42A42169E82@codespeak.net> Author: hpk Date: Fri Aug 15 11:42:45 2008 New Revision: 57265 Modified: py/branch/event/py/test2/dsession/mypickle.py py/branch/event/py/test2/dsession/testing/test_mypickle.py Log: * python2.4 compatibility * eliminate potential race condition Modified: py/branch/event/py/test2/dsession/mypickle.py ============================================================================== --- py/branch/event/py/test2/dsession/mypickle.py (original) +++ py/branch/event/py/test2/dsession/mypickle.py Fri Aug 15 11:42:45 2008 @@ -99,7 +99,7 @@ """ Problems while unpickling. """ def __init__(self, formatted): self.formatted = formatted - super(UnpickleError, self).__init__(formatted) + Exception.__init__(self, formatted) def __str__(self): return self.formatted Modified: py/branch/event/py/test2/dsession/testing/test_mypickle.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_mypickle.py (original) +++ py/branch/event/py/test2/dsession/testing/test_mypickle.py Fri Aug 15 11:42:45 2008 @@ -173,13 +173,13 @@ channel = PickleChannel(channel) from py.__.test2.dsession.testing.test_mypickle import A a1 = A() - channel.receive() + channel.send(a1) channel.send(a1) """) channel = PickleChannel(channel) queue = py.std.Queue.Queue() - channel.send("start") - del channel._ipickle._unpicklememo + a = channel.receive() + channel._ipickle._unpicklememo.clear() channel.setcallback(queue.put, endmarker=-1) next = queue.get(timeout=TESTTIMEOUT) assert next == -1 From hpk at codespeak.net Fri Aug 15 12:18:40 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 12:18:40 +0200 (CEST) Subject: [py-svn] r57270 - in py/branch/event/py/test2: . dsession report report/testing testing Message-ID: <20080815101840.B616F168528@codespeak.net> Author: hpk Date: Fri Aug 15 12:18:39 2008 New Revision: 57270 Modified: py/branch/event/py/test2/dsession/hostmanage.py py/branch/event/py/test2/event.py py/branch/event/py/test2/report/base.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_terminal.py py/branch/event/py/test2/session.py py/branch/event/py/test2/testing/acceptance_test.py Log: introduce HostUp event Modified: py/branch/event/py/test2/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/hostmanage.py (original) +++ py/branch/event/py/test2/dsession/hostmanage.py Fri Aug 15 12:18:39 2008 @@ -1,5 +1,5 @@ import py -import os +import sys, os from py.__.test2.dsession.masterslave import MasterNode from py.__.test2 import event @@ -184,3 +184,11 @@ if not homedir: homedir = os.environ.get('HOMEPATH', '.') os.chdir(homedir) + +def makehostup(host=None): + if host is None: + host = Host("localhost") + platinfo = {} + for name in 'platform', 'executable', 'version_info': + platinfo["sys."+name] = getattr(sys, name) + return event.HostUp(host, platinfo) Modified: py/branch/event/py/test2/event.py ============================================================================== --- py/branch/event/py/test2/event.py (original) +++ py/branch/event/py/test2/event.py Fri Aug 15 12:18:39 2008 @@ -118,9 +118,9 @@ self.roots = roots class HostUp(BaseEvent): - def __init__(self, host, info): + def __init__(self, host, platinfo): self.host = host - self.info = info + self.platinfo = platinfo class HostDown(BaseEvent): def __init__(self, host, error=None): Modified: py/branch/event/py/test2/report/base.py ============================================================================== --- py/branch/event/py/test2/report/base.py (original) +++ py/branch/event/py/test2/report/base.py Fri Aug 15 12:18:39 2008 @@ -80,8 +80,9 @@ return ".".join(parts) return colitem.name -def repr_pythonversion(): - v = sys.version_info +def repr_pythonversion(v=None): + if v is None: + v = sys.version_info try: return "%s.%s.%s-%s-%s" % v except (TypeError, ValueError): Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Fri Aug 15 12:18:39 2008 @@ -78,7 +78,16 @@ super(TerminalReporter, self).rep_TestrunStart(ev) self._tw.sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() - self.out_hostinfo() + #self.out_hostinfo() + + def rep_HostUp(self, ev): + d = ev.platinfo.copy() + d['hostid'] = ev.host.hostid + d['version'] = repr_pythonversion(d['sys.version_info']) + self._tw.line("%(hostid)s %(sys.platform)s " + "%(sys.executable)s - Python %(version)s" % + d) + def rep_TestrunFinish(self, ev): self._tw.line("") Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Fri Aug 15 12:18:39 2008 @@ -1,11 +1,13 @@ import py +import sys from py.__.test2.report.terminal import TerminalReporter from py.__.test2 import event #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report from py.__.test2.testing.suptest import ( InlineCollection, popvalue, assert_stringio_contains_lines) -from py.__.test2.dsession.hostmanage import Host +from py.__.test2.dsession.hostmanage import Host, makehostup +from py.__.test2.report.base import repr_pythonversion class TestTerminal(InlineCollection): def test_session_reporter_subscription(self): @@ -18,6 +20,18 @@ session.sessionfinishes() #assert rep.processevent not in session.bus._subscribers + def test_hostup(self): + item = self.getitem("def test_func(): pass") + stringio = py.std.cStringIO.StringIO() + rep = TerminalReporter(item._config, file=stringio) + rep.processevent(event.TestrunStart()) + host = Host("localhost") + rep.processevent(makehostup(host)) + s = popvalue(stringio) + expect = "%s %s %s - Python %s" %(host.hostid, sys.platform, + sys.executable, repr_pythonversion(sys.version_info)) + assert s.find(expect) != -1 + def test_pass_skip_fail(self): modcol = self.getmodulecol(""" import py Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Aug 15 12:18:39 2008 @@ -15,6 +15,7 @@ Item = (py.test.collect.Item, py.test2.collect.Item) Collector = (py.test.collect.Collector, py.test2.collect.Collector) from runner import basic_collect_report +from py.__.test2.dsession.hostmanage import makehostup class Session(object): """ @@ -117,6 +118,7 @@ """ main loop for running tests. """ self.shouldstop = False self.sessionstarts() + self.bus.notify(makehostup()) exitstatus = outcome.EXIT_OK try: for item in self.collect(colitems): @@ -144,3 +146,4 @@ pdb = self.config.option.usepdb and self.runpdb or None testrep = runner(item, pdb=pdb) self.bus.notify(testrep) + Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Fri Aug 15 12:18:39 2008 @@ -178,7 +178,7 @@ verinfo = ".".join(map(str, py.std.sys.version_info[:3])) extra = assert_lines_contain_lines(result.outlines, [ "*===== test session starts ====*", - "host 0: %s %s - Python %s*" %( + "localhost* %s %s - Python %s*" %( py.std.sys.platform, py.std.sys.executable, verinfo), "*test_one.py .", "==* 1/1 passed + 0 skips in *.[0-9][0-9] seconds *", From hpk at codespeak.net Fri Aug 15 13:41:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 13:41:08 +0200 (CEST) Subject: [py-svn] r57272 - in py/branch/event/py/test2: dsession dsession/testing looponfail report report/testing testing Message-ID: <20080815114108.6CB9A169E76@codespeak.net> Author: hpk Date: Fri Aug 15 13:41:07 2008 New Revision: 57272 Modified: py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/hostmanage.py py/branch/event/py/test2/dsession/masterslave.py py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/dsession/testing/test_hostmanage.py py/branch/event/py/test2/looponfail/remote.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_terminal.py py/branch/event/py/test2/testing/acceptance_test.py Log: HostUp events come from the remote side now simplify dsession hosthandling a bit Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Fri Aug 15 13:41:07 2008 @@ -42,7 +42,6 @@ self.queue = Queue.Queue() self.host2pending = {} self.item2host = {} - self.hosts = [] self._testsfailed = False def fixoptions(self): @@ -98,7 +97,16 @@ continue loopstate.dowork = True self.bus.notify(ev) - if isinstance(ev, event.HostDown): + if isinstance(ev, event.ItemTestReport): + self.removeitem(ev.colitem) + if ev.failed: + loopstate.testsfailed = True + elif isinstance(ev, event.CollectionReport): + if ev.passed: + colitems.extend(ev.result) + elif isinstance(ev, event.HostUp): + self.addhost(ev.host) + elif isinstance(ev, event.HostDown): pending = self.removehost(ev.host) if pending: crashitem = pending.pop(0) @@ -107,13 +115,6 @@ elif isinstance(ev, event.RescheduleItems): colitems.extend(ev.items) loopstate.dowork = False # avoid busywait - elif isinstance(ev, event.ItemTestReport): - self.removeitem(ev.colitem) - if ev.failed: - loopstate.testsfailed = True - elif isinstance(ev, event.CollectionReport): - if ev.passed: - colitems.extend(ev.result) # termination conditions if ((loopstate.testsfailed and self.config.option.exitfirst) or @@ -140,6 +141,7 @@ def loop(self, colitems): try: loopstate = LoopState(colitems) + loopstate.dowork = False # first receive at least one HostUp events while 1: self.loop_once(loopstate) if loopstate.exitstatus is not None: @@ -155,17 +157,14 @@ return exitstatus def triggershutdown(self): - for host in self.hosts: + for host in self.host2pending: host.node.shutdown() def addhost(self, host): assert host not in self.host2pending - assert host not in self.hosts self.host2pending[host] = [] - self.hosts.append(host) def removehost(self, host): - self.hosts.remove(host) pending = self.host2pending.pop(host) for item in pending: del self.item2host[item] @@ -214,10 +213,8 @@ """ setup any neccessary resources ahead of the test run. """ self.hm = HostManager(self) self.hm.setup_hosts(notify=self.queue.put) - for host in self.hm.hosts: - self.addhost(host) def teardown_hosts(self): """ teardown any resources after a test run. """ - for host in self.hosts: + for host in self.host2pending: host.gw.exit() Modified: py/branch/event/py/test2/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/hostmanage.py (original) +++ py/branch/event/py/test2/dsession/hostmanage.py Fri Aug 15 13:41:07 2008 @@ -24,6 +24,12 @@ assert self.inplacelocal or self.relpath self.hostid = self._getuniqueid(self.hostname) + def __getstate__(self): + return (self.hostname, self.relpath, self.hostid) + + def __setstate__(self, repr): + self.hostname, self.relpath, self.hostid = repr + def _getuniqueid(self, hostname): l = self._hostname2list.setdefault(hostname, []) hostid = hostname + "[%d]" % len(l) Modified: py/branch/event/py/test2/dsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/dsession/masterslave.py (original) +++ py/branch/event/py/test2/dsession/masterslave.py Fri Aug 15 13:41:07 2008 @@ -86,6 +86,7 @@ assert host.inplacelocal remote_topdir = config.topdir send_and_receive_pickled_config(channel, config, remote_topdir) + channel.send(host) return channel def setup_at_slave_side(channel, config): @@ -95,6 +96,9 @@ if hasattr(os, 'nice'): nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) + from py.__.test2.dsession.hostmanage import makehostup + host = channel.receive() + channel.send(makehostup(host)) while 1: task = channel.receive() if task is None: # shutdown Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Fri Aug 15 13:41:07 2008 @@ -1,6 +1,6 @@ from py.__.test2.testing.suptest import InlineCollection from py.__.test2.dsession.dsession import DSession, LoopState -from py.__.test2.dsession.hostmanage import Host +from py.__.test2.dsession.hostmanage import Host, makehostup from py.__.test2.runner import basic_collect_report from py.__.test2 import event from py.__.test2 import outcome @@ -31,9 +31,9 @@ session = DSession(item._config) host = Host("localhost") host.node = MockNode() - assert not session.hosts + assert not session.host2pending session.addhost(host) - assert len(session.hosts) == 1 + assert len(session.host2pending) == 1 session.senditems([item]) pending = session.removehost(host) assert pending == [item] @@ -177,14 +177,27 @@ assert str(testrep.outcome.longrepr).find("CRASHED") != -1 assert str(testrep.outcome.longrepr).find(host.hostname) != -1 + def test_hostup_adds_to_available(self): + item = self.getitem("def test_func(): pass") + # setup a session with two hosts + session = DSession(item._config) + host1 = Host("localhost") + hostup = makehostup(host1) + session.queue.put(hostup) + loopstate = LoopState([item]) + loopstate.dowork = False + assert len(session.host2pending) == 0 + session.loop_once(loopstate) + assert len(session.host2pending) == 1 + def test_event_propagation(self): item = self.getitem("def test_func(): pass") session = DSession(item._config) - ev = event.HostDown(Host("localhost"), None) + ev = event.NOP() events = [] ; session.bus.subscribe(events.append) session.queue.put(ev) - py.test.raises(ValueError, "session.loop_once(LoopState([]))") + session.loop_once(LoopState([])) assert events[0] == ev def runthrough(self, item): Modified: py/branch/event/py/test2/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/dsession/testing/test_hostmanage.py Fri Aug 15 13:41:07 2008 @@ -263,6 +263,9 @@ queue = py.std.Queue.Queue() hm.setup_hosts(notify=queue.put) for host in hm.hosts: + ev = queue.get(timeout=2.0) + assert isinstance(ev, event.HostUp) + for host in hm.hosts: host.node.shutdown() for host in hm.hosts: ev = queue.get(timeout=2.0) Modified: py/branch/event/py/test2/looponfail/remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/remote.py (original) +++ py/branch/event/py/test2/looponfail/remote.py Fri Aug 15 13:41:07 2008 @@ -134,9 +134,7 @@ session.shouldclose = channel.isclosed print "SLAVE: starting session.main()" def sendevent(ev): - if not isinstance(ev, event.HostGatewayReady) and \ - not isinstance(ev, event.HostDown): - channel.send(ev) + channel.send(ev) session.bus.subscribe(sendevent) session.main(failures) session.bus.unsubscribe(sendevent) Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Fri Aug 15 13:41:07 2008 @@ -45,7 +45,17 @@ self.write_line("InternalException: " + line) def rep_HostGatewayReady(self, ev): - self.write_line("HostGatewayReady: %s" %(ev.host,)) + if self.config.option.verbose: + self.write_line("HostGatewayReady: %s" %(ev.host,)) + + def rep_HostUp(self, ev): + d = ev.platinfo.copy() + d['hostid'] = ev.host.hostid + d['version'] = repr_pythonversion(d['sys.version_info']) + self.write_line("HOSTUP: %(hostid)s %(sys.platform)s " + "%(sys.executable)s - Python %(version)s" % + d) + def rep_HostDown(self, ev): host = ev.host @@ -80,15 +90,6 @@ self._sessionstarttime = py.std.time.time() #self.out_hostinfo() - def rep_HostUp(self, ev): - d = ev.platinfo.copy() - d['hostid'] = ev.host.hostid - d['version'] = repr_pythonversion(d['sys.version_info']) - self._tw.line("%(hostid)s %(sys.platform)s " - "%(sys.executable)s - Python %(version)s" % - d) - - def rep_TestrunFinish(self, ev): self._tw.line("") if ev.exitstatus == 0 or ev.exitstatus == 1: Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Fri Aug 15 13:41:07 2008 @@ -118,7 +118,7 @@ modcol = self.getmodulecol(""" def test_one(): pass - """) + """, configargs=("-v",)) stringio = py.std.cStringIO.StringIO() host1 = Host("localhost") rep = TerminalReporter(modcol._config, file=stringio) Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Fri Aug 15 13:41:07 2008 @@ -178,7 +178,7 @@ verinfo = ".".join(map(str, py.std.sys.version_info[:3])) extra = assert_lines_contain_lines(result.outlines, [ "*===== test session starts ====*", - "localhost* %s %s - Python %s*" %( + "*localhost* %s %s - Python %s*" %( py.std.sys.platform, py.std.sys.executable, verinfo), "*test_one.py .", "==* 1/1 passed + 0 skips in *.[0-9][0-9] seconds *", @@ -287,9 +287,9 @@ ) result = self.runpytest(p1, '-d') assert_lines_contain_lines(result.outlines, [ - "HostGatewayReady*localhost*", - "HostGatewayReady*localhost*", - "HostGatewayReady*localhost*", + "HOSTUP: localhost*Python*", + "HOSTUP: localhost*Python*", + "HOSTUP: localhost*Python*", "*1/3 passed + 1 skip*", "*failures: 2*", ]) @@ -322,9 +322,9 @@ ) result = self.runpytest(p1, '-d') assert_lines_contain_lines(result.outlines, [ - "HostGatewayReady*localhost*", - "HostGatewayReady*localhost*", - "HostGatewayReady*localhost*", + "*localhost*Python*", + "*localhost*Python*", + "*localhost*Python*", "HostDown*localhost*TERMINATED*", "*1/4 passed + 1 skip*", "*failures: 3*", From hpk at codespeak.net Fri Aug 15 13:49:39 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 13:49:39 +0200 (CEST) Subject: [py-svn] r57273 - py/branch/event/py/test2 Message-ID: <20080815114939.2192F1684C2@codespeak.net> Author: hpk Date: Fri Aug 15 13:49:35 2008 New Revision: 57273 Modified: py/branch/event/py/test2/session.py Log: small removals Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Aug 15 13:49:35 2008 @@ -41,13 +41,9 @@ if option.runbrowser and not option.startserver: #print "--runbrowser implies --startserver" option.startserver = True - if self.config.getvalue("dist_boxed") and option.dist: - option.boxed = True # conflicting options if option.looponfailing and option.usepdb: raise ValueError, "--looponfailing together with --pdb not supported." - #if option.looponfailing and option.dist: - # raise ValueError, "--looponfailing together with --dist not supported." if option.executable and option.usepdb: raise ValueError, "--exec together with --pdb not supported." @@ -130,7 +126,6 @@ except KeyboardInterrupt: exitstatus = outcome.EXIT_INTERRUPTED except: - raise self.bus.notify(event.InternalException()) exitstatus = outcome.EXIT_INTERNALERROR if self._failurelist and exitstatus == 0: From hpk at codespeak.net Fri Aug 15 13:59:39 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 13:59:39 +0200 (CEST) Subject: [py-svn] r57274 - in py/branch/event/py/test2: . looponfail Message-ID: <20080815115939.97E8A169E7F@codespeak.net> Author: hpk Date: Fri Aug 15 13:59:38 2008 New Revision: 57274 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/defaultconftest.py py/branch/event/py/test2/looponfail/remote.py py/branch/event/py/test2/pycollect.py Log: add --debug option, improve docstrings Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Fri Aug 15 13:59:38 2008 @@ -10,15 +10,10 @@ The is a schematic example of a tree of collectors and test items:: Directory - Module - Class - Instance - Function - Generator - ... - Function - Generator - Function + Directory + CustomCollector # provided via conftest's + CustomItem # provided via conftest's + CustomItem # provided via conftest's Directory ... Modified: py/branch/event/py/test2/defaultconftest.py ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test2/defaultconftest.py Fri Aug 15 13:59:38 2008 @@ -77,6 +77,9 @@ Option('', '--exec', action="store", dest="executable", default=None, help="python executable to run the tests with."), + Option('', '--debug', + action="store_true", dest="debug", default=False, + help="turn on debugging information."), ) config._addoptions('EXPERIMENTAL options', Modified: py/branch/event/py/test2/looponfail/remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/remote.py (original) +++ py/branch/event/py/test2/looponfail/remote.py Fri Aug 15 13:59:38 2008 @@ -69,8 +69,9 @@ self.executable = executable def trace(self, *args): - msg = " ".join([str(x) for x in args]) - print "RemoteControl:", msg + if self.config.option.debug: + msg = " ".join([str(x) for x in args]) + print "RemoteControl:", msg def setup(self): if hasattr(self, 'gateway'): Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Fri Aug 15 13:59:38 2008 @@ -1,5 +1,20 @@ """ -Python related Collect nodes. +Python related collection nodes. Here is an example of +a tree of collectors and test items that this modules provides:: + + Module # FSCollector + Class + Instance + Function + Generator + ... + Function + Generator + Function + + DoctestFile # FSCollector + DoctestFileContent # acts as Item + """ import py from py.__.test2.collect import Collector, FSCollector, Item, configproperty From hpk at codespeak.net Fri Aug 15 14:44:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 14:44:35 +0200 (CEST) Subject: [py-svn] r57277 - in py/branch/event/py/test2: . testing Message-ID: <20080815124435.466F0169E6C@codespeak.net> Author: hpk Date: Fri Aug 15 14:44:33 2008 New Revision: 57277 Modified: py/branch/event/py/test2/pycollect.py py/branch/event/py/test2/testing/test_setup_nested.py Log: always use fresh instances for test methods Modified: py/branch/event/py/test2/pycollect.py ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test2/pycollect.py Fri Aug 15 14:44:33 2008 @@ -226,6 +226,9 @@ return [] Function = property(Function) + def newinstance(self): + self.obj = self._getobj() + return self.obj class FunctionMixin(PyobjMixin): """ mixin for the code common to Function and Generator. @@ -239,7 +242,11 @@ name = 'setup_method' else: name = 'setup_function' - obj = self.parent.obj + if isinstance(self.parent, Instance): + obj = self.parent.newinstance() + self.obj = self._getobj() + else: + obj = self.parent.obj setup_func_or_method = getattr(obj, name, None) if setup_func_or_method is not None: return setup_func_or_method(self.obj) @@ -253,7 +260,7 @@ obj = self.parent.obj teardown_func_or_meth = getattr(obj, name, None) if teardown_func_or_meth is not None: - return teardown_func_or_meth(self.obj) + teardown_func_or_meth(self.obj) def _prunetraceback(self, traceback): if not self._config.option.fulltrace: Modified: py/branch/event/py/test2/testing/test_setup_nested.py ============================================================================== --- py/branch/event/py/test2/testing/test_setup_nested.py (original) +++ py/branch/event/py/test2/testing/test_setup_nested.py Fri Aug 15 14:44:33 2008 @@ -124,3 +124,20 @@ rep = sorter.getreport("test_one") assert rep.passed +def test_method_setup_uses_fresh_instances(): + sorter = suptest.events_from_runsource(""" + class TestSelfState1: + def __init__(self): + self.hello = 42 + def test_hello(self): + self.world = 23 + def test_afterhello(self): + assert not hasattr(self, 'world') + assert self.hello == 42 + class TestSelfState2: + def test_hello(self): + self.world = 10 + def test_world(self): + assert not hasattr(self, 'world') + """) + sorter.assertoutcome(passed=4, failed=0) From hpk at codespeak.net Fri Aug 15 14:45:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 14:45:10 +0200 (CEST) Subject: [py-svn] r57278 - py/branch/event/py/test2/report/testing Message-ID: <20080815124510.AD307169E6C@codespeak.net> Author: hpk Date: Fri Aug 15 14:45:09 2008 New Revision: 57278 Modified: py/branch/event/py/test2/report/testing/test_collectonly.py Log: fix bug uncovered by fresh instances Modified: py/branch/event/py/test2/report/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/report/testing/test_collectonly.py Fri Aug 15 14:45:09 2008 @@ -41,7 +41,7 @@ def test_collectonly_failed_module(self): modcol = self.getmodulecol(configargs=['--collectonly'], source=""" raise ValueError(0) - """) + """, withsession=True) stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, bus=self.session.bus, out=stringio) cols = list(self.session.genitems([modcol])) From hpk at codespeak.net Fri Aug 15 15:40:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 15:40:19 +0200 (CEST) Subject: [py-svn] r57283 - in py/branch/event/py/io: . testing Message-ID: <20080815134019.949D2169DF5@codespeak.net> Author: hpk Date: Fri Aug 15 15:40:19 2008 New Revision: 57283 Modified: py/branch/event/py/io/terminalwriter.py py/branch/event/py/io/testing/test_terminalwriter.py Log: extend terminalwriter a bit, test attributes Modified: py/branch/event/py/io/terminalwriter.py ============================================================================== --- py/branch/event/py/io/terminalwriter.py (original) +++ py/branch/event/py/io/terminalwriter.py Fri Aug 15 15:40:19 2008 @@ -60,12 +60,13 @@ file = WriteFile(file) self._file = file self.fullwidth = get_terminal_width() + self.hasmarkup = sys.platform != "win32" and \ + hasattr(file, 'isatty') and file.isatty() def _escaped(self, text, esc): - if esc and sys.platform != "win32": - if hasattr(self._file, 'isatty') and self._file.isatty(): - text = (''.join(['\x1b[%sm' % cod for cod in esc]) + - text +'\x1b[0m') + if esc and self.hasmarkup: + text = (''.join(['\x1b[%sm' % cod for cod in esc]) + + text +'\x1b[0m') return text def markup(self, text, **kw): @@ -78,7 +79,7 @@ return self._escaped(text, tuple(esc)) def sep(self, sepchar, title=None, fullwidth=None, **kw): - if not fullwidth: + if fullwidth is None: fullwidth = self.fullwidth # the goal is to have the line be as long as possible # under the condition that len(line) <= fullwidth @@ -102,8 +103,12 @@ self.line(line, **kw) - def write(self, s): - self._file.write(str(s)) + def write(self, s, **kw): + if s: + s = str(s) + if self.hasmarkup and kw: + s = self.markup(s, **kw) + self._file.write(s) self._file.flush() def line(self, s='', **kw): Modified: py/branch/event/py/io/testing/test_terminalwriter.py ============================================================================== --- py/branch/event/py/io/testing/test_terminalwriter.py (original) +++ py/branch/event/py/io/testing/test_terminalwriter.py Fri Aug 15 15:40:19 2008 @@ -59,6 +59,23 @@ py.test.raises(ValueError, "tw.markup('x', wronkw=3)") py.test.raises(ValueError, "tw.markup('x', wronkw=0)") + def test_line_write_markup(self): + tw = self.getwriter() + tw.hasmarkup = True + tw.line("x", bold=True) + tw.write("x\n", red=True) + l = self.getlines() + assert len(l[0]) > 2, l + assert len(l[1]) > 2, l + + def test_attr_fullwidth(self): + tw = self.getwriter() + tw.sep("-", "hello", fullwidth=40) + tw.fullwidth = 40 + tw.sep("-", "hello") + l = self.getlines() + assert len(l[0]) == len(l[1]) + class TestStringIO(BaseTests): def getwriter(self): self.tw = py.io.TerminalWriter(stringio=True) @@ -78,3 +95,14 @@ io.write("".join(self.writes)) io.seek(0) return io.readlines() + +def test_attr_hasmarkup(): + tw = py.io.TerminalWriter(stringio=True) + assert not tw.hasmarkup + tw.hasmarkup = True + tw.line("hello", bold=True) + s = tw.stringio.getvalue() + assert len(s) > len("hello") + + + From hpk at codespeak.net Fri Aug 15 17:37:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 17:37:31 +0200 (CEST) Subject: [py-svn] r57290 - in py/branch/event/py/test2: . dsession looponfail looponfail/testing report report/testing Message-ID: <20080815153731.A4BA1169E8A@codespeak.net> Author: hpk Date: Fri Aug 15 17:37:29 2008 New Revision: 57290 Modified: py/branch/event/py/test2/cmdline.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/looponfail/remote.py py/branch/event/py/test2/looponfail/testing/test_remote.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_terminal.py py/branch/event/py/test2/session.py Log: fixing and refactoring looponfailing once again. Modified: py/branch/event/py/test2/cmdline.py ============================================================================== --- py/branch/event/py/test2/cmdline.py (original) +++ py/branch/event/py/test2/cmdline.py Fri Aug 15 17:37:29 2008 @@ -11,8 +11,7 @@ config = py.test2.config config.parse(args) session = config.initsession() - colitems = [config.getfsnode(arg) for arg in config.args] - exitstatus = session.main(colitems) + exitstatus = session.main() raise SystemExit(exitstatus) def warn_about_missing_assertion(): Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Fri Aug 15 17:37:29 2008 @@ -73,7 +73,8 @@ print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" raise SystemExit - def main(self, colitems): + def main(self, colitems=None): + colitems = self.getinitialitems(colitems) self.bus.notify(event.TestrunStart()) self.setup_hosts() exitstatus = self.loop(colitems) Modified: py/branch/event/py/test2/looponfail/remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/remote.py (original) +++ py/branch/event/py/test2/looponfail/remote.py Fri Aug 15 17:37:29 2008 @@ -1,3 +1,13 @@ +""" + LooponfailingSession and Helpers. + + NOTE that one really has to avoid loading and depending on + application modules within the controlling process + (the one that starts repeatedly test processes) + otherwise changes to source code can crash + the controlling process which should never happen. +""" + from __future__ import generators import py from py.__.test2.session import Session @@ -12,50 +22,32 @@ super(LooponfailingSession, self).__init__(config=config) self.rootdirs = [self.config.topdir] # xxx dist_rsync_roots? self.statrecorder = util.StatRecorder(self.rootdirs) - self.remotecontrol = RemoteControl(self.config, notify=self.bus.notify) + self.remotecontrol = RemoteControl(self.config) + self.out = py.io.TerminalWriter() - def main(self, initialitems): + def main(self, initialitems=None): self.loopstate = loopstate = LoopState(initialitems) self.remotecontrol.setup() while 1: self.loop_once(loopstate) - if loopstate.wasfailing and not loopstate.failreports: + if not loopstate.colitems and loopstate.wasfailing: continue # rerun immediately self.statrecorder.waitonchange(checkinterval=2.0) def loop_once(self, loopstate): colitems = loopstate.colitems - if not colitems: - colitems = loopstate.initial - loopstate.wasfailing = len(loopstate.failreports) - events = [] - self.bus.subscribe(events.append) - try: - self.remotecontrol.runtests(colitems) - finally: - self.bus.unsubscribe(events.append) - reports = [] - colitems = [] - for ev in events: - if isinstance(ev, event.ItemTestReport) and ev.failed: - reports.append(ev) - colitems.append(ev.colitem) - loopstate.colitems = colitems - loopstate.failreports = reports - ev = event.LooponfailingInfo(loopstate.failreports, self.rootdirs) - self.bus.notify(ev) + loopstate.wasfailing = colitems and len(colitems) + loopstate.colitems = self.remotecontrol.runsession(colitems or ()) + #ev = event.LooponfailingInfo(loopstate.failreports, self.rootdirs) self.remotecontrol.setup() class LoopState: - def __init__(self, initial, colitems=None): - self.initial = initial - self.colitems = colitems or initial - self.failreports = [] + def __init__(self, colitems=None): + self.colitems = colitems class RemoteControl(object): - def __init__(self, config, notify): + def __init__(self, config): self.config = config - self.notify = notify self._setexecutable() def _setexecutable(self): @@ -73,24 +65,27 @@ msg = " ".join([str(x) for x in args]) print "RemoteControl:", msg - def setup(self): + def setup(self, out=None): if hasattr(self, 'gateway'): raise ValueError("already have gateway %r" % self.gateway) + if out is None: + out = py.io.TerminalWriter() from py.__.test2.dsession import masterslave self.trace("setting up slave session") self.gateway = py.execnet.PopenGateway(self.executable) channel = self.gateway.remote_exec(source=""" from py.__.test2.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - print "SLAVE: initializing ..." from py.__.test2.looponfail.remote import slave_runsession from py.__.test2.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) - slave_runsession(channel, config) - """) # , stdout=self.out, stderr=self.out) + width, hasmarkup = channel.receive() + slave_runsession(channel, config, width, hasmarkup) + """, stdout=out, stderr=out) channel = PickleChannel(channel) masterslave.send_and_receive_pickled_config( channel, self.config, remote_topdir=self.config.topdir) + channel.send((out.fullwidth, out.hasmarkup)) self.trace("set up of slave session complete") self.channel = channel @@ -105,39 +100,57 @@ self.gateway.exit() del self.gateway - def runtests(self, colitems): + def runsession(self, colitems=()): try: self.trace("sending", colitems) - self.channel.send(colitems) + trails = colitems + self.channel.send(trails) try: - while 1: - ev = self.channel.receive() - if ev is None: - break - self.notify(ev) + return self.channel.receive() except self.channel.RemoteError, e: self.trace("ERROR", e) raise finally: self.ensure_teardown() -def slave_runsession(channel, config): +def slave_runsession(channel, config, width, hasmarkup): """ we run this on the other side. """ - print "SLAVE: received configuration, using topdir:", config.topdir + if config.option.debug: + def DEBUG(*args): + print " ".join(map(str, args)) + else: + def DEBUG(*args): pass + + DEBUG("SLAVE: received configuration, using topdir:", config.topdir) #config.option.session = None config.option.looponfailing = False config.option.usepdb = False config.option.executable = None - failures = channel.receive() - print "SLAVE: initsession()" + trails = channel.receive() + + DEBUG("SLAVE: initsession()") session = config.initsession() - session.reporter.deactivate() + session.reporter._tw.hasmarkup = hasmarkup + session.reporter._tw.fullwidth = width + if trails: + colitems = [py.test2.collect.Collector._fromtrail(x, config) + for x in trails] + else: + colitems = None session.shouldclose = channel.isclosed - print "SLAVE: starting session.main()" - def sendevent(ev): - channel.send(ev) - session.bus.subscribe(sendevent) - session.main(failures) - session.bus.unsubscribe(sendevent) - channel.send(None) - + #def sendevent(ev): + # channel.send(ev) + #session.bus.subscribe(sendevent) + failreports = [] + def recordfailures(ev): + if isinstance(ev, event.BaseReport): + if ev.failed: + failreports.append(ev) + session.bus.subscribe(recordfailures) + + DEBUG("SLAVE: starting session.main()") + session.main(colitems) + session.bus.unsubscribe(recordfailures) + ev = event.LooponfailingInfo(failreports, [config.topdir]) + session.bus.notify(ev) + channel.send([x.colitem._totrail() for x in failreports if x.failed]) Modified: py/branch/event/py/test2/looponfail/testing/test_remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/testing/test_remote.py (original) +++ py/branch/event/py/test2/looponfail/testing/test_remote.py Fri Aug 15 17:37:29 2008 @@ -18,24 +18,49 @@ class TestRemoteControl(suptest.InlineCollection): - def test_simple(self): + def test_nofailures(self): item = self.getitem("def test_func(): pass\n") events = [] - control = RemoteControl(item._config, notify=events.append) + control = RemoteControl(item._config) control.setup() - control.runtests([item]) - ev = getevent(events, event.ItemTestReport) - assert ev.passed - assert ev.colitem == item + failures = control.runsession() + assert not failures - def test_double_setup_teardown_calls(self): - item = self.getitem("def test_func(): pass\n") - events = [] - control = RemoteControl(item._config, notify=events.append) + def test_failures(self): + item = self.getitem("def test_func(): assert 0\n") + control = RemoteControl(item._config) + control.setup() + failures = control.runsession() + assert failures control.setup() - py.test2.raises(ValueError, control.setup) - control.ensure_teardown() - control.ensure_teardown() + item.fspath.write("def test_func(): assert 1\n") + (item.fspath + "c").remove() + failures = control.runsession(failures) + assert not failures + + def test_failure_change(self): + modcol = self.getitem(""" + def test_func(): + assert 0 + """) + control = RemoteControl(modcol._config) + control.setup() + failures = control.runsession() + assert failures + control.setup() + modcol.fspath.write(py.code.Source(""" + def test_func(): + assert 1 + def test_new(): + assert 0 + """)) + (modcol.fspath + "c").remove() + failures = control.runsession(failures) + assert not failures + control.setup() + failures = control.runsession() + assert failures + assert str(failures).find("test_new") != -1 class TestLooponFailing(suptest.InlineCollection): def test_looponfailing_from_fail_to_ok(self): @@ -47,21 +72,11 @@ assert 1 """) session = LooponfailingSession(modcol._config) - loopstate = LoopState(initial=[modcol]) - events = [] - session.bus.subscribe(events.append) - + loopstate = LoopState() session.remotecontrol.setup() - loopstate.colitems = [] session.loop_once(loopstate) - ev = getevent(events, event.LooponfailingInfo) - assert ev.failreports == loopstate.failreports - evlist = getevents(events, event.ItemTestReport) - failed = [ev for ev in evlist if ev.failed] - assert len(failed) == 1 - assert failed[0].colitem.name == "test_one" - assert loopstate.colitems[0] == failed[0].colitem - + assert len(loopstate.colitems) == 1 + modcol.fspath.write(py.code.Source(""" def test_one(): x = 15 @@ -70,29 +85,30 @@ assert 1 """)) assert session.statrecorder.check() - events[:] = [] session.loop_once(loopstate) assert not loopstate.colitems - assert not loopstate.failreports - evlist = getevents(events, event.LooponfailingInfo) - assert len(evlist) == 1 - assert not evlist[0].failreports - def test_full_loop_from_fail_to_different_fail(self): + def test_looponfailing_from_one_to_two_tests(self): modcol = self.getmodulecol(""" def test_one(): - x = 0 - assert x == 1 - def test_two(): - assert 1 + assert 0 """) session = LooponfailingSession(modcol._config) - events = [] ; session.bus.subscribe(events.append) - del session.statrecorder - py.test2.raises(AttributeError, "session.main([modcol])") - assert len(session.loopstate.failreports) == 1 - linfo = getevent(events, event.LooponfailingInfo) - assert linfo.failreports == session.loopstate.failreports - assert linfo.rootdirs == session.rootdirs - - + loopstate = LoopState() + session.remotecontrol.setup() + loopstate.colitems = [] + session.loop_once(loopstate) + assert len(loopstate.colitems) == 1 + + modcol.fspath.write(py.code.Source(""" + def test_one(): + assert 1 # passes now + def test_two(): + assert 0 # new and fails + """)) + assert session.statrecorder.check() + session.loop_once(loopstate) + assert len(loopstate.colitems) == 0 + + session.loop_once(loopstate) + assert len(loopstate.colitems) == 1 Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Fri Aug 15 17:37:29 2008 @@ -102,19 +102,18 @@ self.summary_stats() def rep_LooponfailingInfo(self, ev): - self._tw.sep("#", "LOOPONFAILING") if ev.failreports: + self._tw.sep("#", "LOOPONFAILING", red=True) for report in ev.failreports: try: loc = report.outcome.longrepr.reprcrash except AttributeError: loc = str(report.outcome.longrepr)[:50] self.write_line(loc, red=True) - self.write_line("") - self.write_line("watching files below:") - for rootdir in ev.rootdirs: - self.write_line(" %s" %(rootdir,)) + #self.write_line("watching files below:") self._tw.sep("#", "waiting for changes") + for rootdir in ev.rootdirs: + self.write_line("### Watching: %s" %(rootdir,), bold=True) if 0: print "#" * 60 Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Fri Aug 15 17:37:29 2008 @@ -156,6 +156,6 @@ assert_stringio_contains_lines(stringio, [ "*test_looponfailingreport.py:2: assert 0", "*test_looponfailingreport.py:4: ValueError*", - "*%s*" % (modcol._config.topdir), "*waiting*", + "*%s*" % (modcol._config.topdir), ]) Modified: py/branch/event/py/test2/session.py ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test2/session.py Fri Aug 15 17:37:29 2008 @@ -110,8 +110,15 @@ #self.reporter.deactivate() return self._failurelist - def main(self, colitems): + def getinitialitems(self, colitems): + if colitems is None: + colitems = [self.config.getfsnode(arg) + for arg in self.config.args] + return colitems + + def main(self, colitems=None): """ main loop for running tests. """ + colitems = self.getinitialitems(colitems) self.shouldstop = False self.sessionstarts() self.bus.notify(makehostup()) From hpk at codespeak.net Fri Aug 15 18:07:05 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 18:07:05 +0200 (CEST) Subject: [py-svn] r57291 - py/branch/event/py/test2/report Message-ID: <20080815160705.AE339169E75@codespeak.net> Author: hpk Date: Fri Aug 15 18:07:04 2008 New Revision: 57291 Modified: py/branch/event/py/test2/report/terminal.py Log: fix random newlines. Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Fri Aug 15 18:07:04 2008 @@ -27,13 +27,20 @@ self.currentfspath = fspath self._tw.write(res) - def write_line(self, line, **markup): - line = str(line) + def ensure_newline(self): if self.currentfspath: self._tw.line() self.currentfspath = None + + def write_line(self, line, **markup): + line = str(line) + self.ensure_newline() self._tw.line(line, **markup) + def write_sep(self, sep, title=None, **markup): + self.ensure_newline() + self._tw.sep(sep, title, **markup) + def getoutcomeword(self, item): if item.passed: return self._tw.markup("PASS", green=True) elif item.failed: return self._tw.markup("FAIL", red=True) @@ -86,7 +93,7 @@ def rep_TestrunStart(self, ev): super(TerminalReporter, self).rep_TestrunStart(ev) - self._tw.sep("=", "test session starts", bold=True) + self.write_sep("=", "test session starts", bold=True) self._sessionstarttime = py.std.time.time() #self.out_hostinfo() @@ -96,22 +103,20 @@ self.summary_failures() self.summary_skips() elif ev.exitstatus == 2: - self._tw.sep("!", "KEYBOARD INTERRUPT") - self._tw.line() + self.write_sep("!", "KEYBOARD INTERRUPT") self.summary_deselected() self.summary_stats() def rep_LooponfailingInfo(self, ev): if ev.failreports: - self._tw.sep("#", "LOOPONFAILING", red=True) + self.write_sep("#", "LOOPONFAILING", red=True) for report in ev.failreports: try: loc = report.outcome.longrepr.reprcrash except AttributeError: loc = str(report.outcome.longrepr)[:50] self.write_line(loc, red=True) - #self.write_line("watching files below:") - self._tw.sep("#", "waiting for changes") + self.write_sep("#", "waiting for changes") for rootdir in ev.rootdirs: self.write_line("### Watching: %s" %(rootdir,), bold=True) @@ -133,9 +138,9 @@ def summary_failures(self): if self._failed: - self._tw.sep("=", "FAILURES") + self.write_sep("=", "FAILURES") for ev in self._failed: - self._tw.sep("_") + self.write_sep("_") ev.toterminal(self._tw) def summary_stats(self): @@ -144,18 +149,17 @@ numskipped = len(self._skipped) numpassed = len(self._passed) sum = numfailed + numpassed - self._tw.sep("=", "%d/%d passed + %d skips in %.2f seconds" % + self.write_sep("=", "%d/%d passed + %d skips in %.2f seconds" % (numpassed, sum, numskipped, session_duration), bold=True) if numfailed == 0: - self._tw.sep("=", "failures: no failures :)", green=True) + self.write_sep("=", "failures: no failures :)", green=True) else: - self._tw.sep("=", "failures: %d" %(numfailed), red=True) - self._tw.line() + self.write_sep("=", "failures: %d" %(numfailed), red=True) def summary_deselected(self): if not self._deselected: return - self._tw.sep("=", "%d tests deselected by %r" %( + self.write_sep("=", "%d tests deselected by %r" %( len(self._deselected), self.config.option.keyword), bold=True) @@ -163,7 +167,7 @@ if not self._failed or self.config.option.showskipsummary: folded_skips = self._folded_skips() if folded_skips: - self._tw.sep("_", "skipped test summary") + self.write_sep("_", "skipped test summary") for num, fspath, lineno, reason in folded_skips: self._tw.line("%s:%d: [%d] %s" %(fspath, lineno, num, reason)) From hpk at codespeak.net Fri Aug 15 18:19:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 18:19:52 +0200 (CEST) Subject: [py-svn] r57292 - py/branch/event/py/test2/looponfail Message-ID: <20080815161952.B933E169E4E@codespeak.net> Author: hpk Date: Fri Aug 15 18:19:52 2008 New Revision: 57292 Modified: py/branch/event/py/test2/looponfail/remote.py Log: keyboardinterrupt bails out without traceback. Modified: py/branch/event/py/test2/looponfail/remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/remote.py (original) +++ py/branch/event/py/test2/looponfail/remote.py Fri Aug 15 18:19:52 2008 @@ -26,13 +26,17 @@ self.out = py.io.TerminalWriter() def main(self, initialitems=None): - self.loopstate = loopstate = LoopState(initialitems) - self.remotecontrol.setup() - while 1: - self.loop_once(loopstate) - if not loopstate.colitems and loopstate.wasfailing: - continue # rerun immediately - self.statrecorder.waitonchange(checkinterval=2.0) + try: + self.loopstate = loopstate = LoopState(initialitems) + self.remotecontrol.setup() + while 1: + self.loop_once(loopstate) + if not loopstate.colitems and loopstate.wasfailing: + continue # rerun immediately + self.statrecorder.waitonchange(checkinterval=2.0) + except KeyboardInterrupt: + print + pass def loop_once(self, loopstate): colitems = loopstate.colitems From hpk at codespeak.net Fri Aug 15 18:26:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 18:26:46 +0200 (CEST) Subject: [py-svn] r57293 - py/branch/event/py/test2/testing Message-ID: <20080815162646.403C1169E75@codespeak.net> Author: hpk Date: Fri Aug 15 18:26:45 2008 New Revision: 57293 Modified: py/branch/event/py/test2/testing/acceptance_test.py Log: fix flaky checks for asynchronously coming up hosts Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Fri Aug 15 18:26:45 2008 @@ -288,8 +288,8 @@ result = self.runpytest(p1, '-d') assert_lines_contain_lines(result.outlines, [ "HOSTUP: localhost*Python*", - "HOSTUP: localhost*Python*", - "HOSTUP: localhost*Python*", + #"HOSTUP: localhost*Python*", + #"HOSTUP: localhost*Python*", "*1/3 passed + 1 skip*", "*failures: 2*", ]) From hpk at codespeak.net Fri Aug 15 21:44:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 21:44:28 +0200 (CEST) Subject: [py-svn] r57294 - in py/branch/event/py/test2: . dsession dsession/testing testing Message-ID: <20080815194428.A12DD169E95@codespeak.net> Author: hpk Date: Fri Aug 15 21:44:25 2008 New Revision: 57294 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/defaultconftest.py py/branch/event/py/test2/dsession/hostmanage.py py/branch/event/py/test2/dsession/testing/test_hostmanage.py py/branch/event/py/test2/testing/test_config.py py/branch/event/py/test2/testing/test_session.py Log: implement --exec, works nicely with dsessions and looponfailing Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Fri Aug 15 21:44:25 2008 @@ -177,11 +177,12 @@ def _getsessionname(self): """ return default session name as determined from options. """ - name = 'Session' - if self.option.looponfailing or self.option.executable: + if self.option.looponfailing: name = 'LooponfailingSession' - elif self.option.dist: + elif self.option.dist or self.option.executable: name = 'DSession' + else: + name = 'Session' return name def _reparse(self, args): Modified: py/branch/event/py/test2/defaultconftest.py ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test2/defaultconftest.py Fri Aug 15 21:44:25 2008 @@ -16,7 +16,7 @@ #dist_hosts: needs to be provided by user #dist_rsync_roots: might be provided by user, if not present or None, # whole pkgdir will be rsynced -dist_remotepython = "python" +# XXX deprecated dist_remotepython = None dist_taskspernode = 15 dist_boxed = False if hasattr(py.std.os, 'nice'): Modified: py/branch/event/py/test2/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/hostmanage.py (original) +++ py/branch/event/py/test2/dsession/hostmanage.py Fri Aug 15 21:44:25 2008 @@ -7,7 +7,7 @@ """ Host location representation for distributed testing. """ _hostname2list = {} - def __init__(self, spec, addrel=""): + def __init__(self, spec, addrel="", python=None): parts = spec.split(':', 1) self.hostname = parts.pop(0) self.relpath = parts and parts.pop(0) or "" @@ -23,6 +23,7 @@ assert not parts assert self.inplacelocal or self.relpath self.hostid = self._getuniqueid(self.hostname) + self.python = python def __getstate__(self): return (self.hostname, self.relpath, self.hostid) @@ -36,7 +37,8 @@ l.append(hostid) return hostid - def initgateway(self, python="python"): + def initgateway(self): + python = self.python or "python" if self.hostname == "localhost": self.gw = py.execnet.PopenGateway(python=python) else: @@ -132,15 +134,14 @@ self.roots = roots if hosts is None: hosts = self.session.config.getvalue("dist_hosts") - hosts = [Host(x, addrel) for x in hosts] + python = self.session.config.option.executable or "python" + hosts = [Host(x, addrel, python=python) for x in hosts] self.hosts = hosts def prepare_gateways(self): - python = self.session.config.getvalue("dist_remotepython") for host in self.hosts: - host.initgateway(python=python) + host.initgateway() self.session.bus.notify(event.HostGatewayReady(host, self.roots)) - host.gw.host = host def init_rsync(self): self.prepare_gateways() Modified: py/branch/event/py/test2/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/dsession/testing/test_hostmanage.py Fri Aug 15 21:44:25 2008 @@ -84,6 +84,19 @@ finally: host.gw.exit() + def test_initgateway_python(self): + host = Host("localhost", python="python2.4") + l = [] + def p(python): + l.append(python) + raise ValueError + py.magic.patch(py.execnet, 'PopenGateway', p) + try: + py.test2.raises(ValueError, host.initgateway) + finally: + py.magic.revert(py.execnet, 'PopenGateway') + assert l[0] == host.python + def test_initgateway_ssh_and_remotepath(self): from py.__.conftest import option if not option.sshtarget: Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Fri Aug 15 21:44:25 2008 @@ -206,11 +206,6 @@ assert s.find("TestrunStart") != -1 def test_implied_lsession(self): - #optnames = 'startserver runbrowser apigen=x rest boxed'.split() - #for x in optnames: - # config = py.test2.config._reparse([self.tmpdir, '--%s' % x]) - # assert config._getsessionname() == 'LSession' - for x in 'startserver runbrowser rest'.split(): config = py.test2.config._reparse([self.tmpdir, '--dist', '--%s' % x]) assert config._getsessionname() == 'DSession' @@ -219,8 +214,11 @@ config = py.test2.config._reparse([self.tmpdir, '--looponfailing']) assert config._getsessionname() == 'LooponfailingSession' config = py.test2.config._reparse([self.tmpdir, '--exec=x']) - assert config._getsessionname() == 'LooponfailingSession' + assert config._getsessionname() == 'DSession' config = py.test2.config._reparse([self.tmpdir, '--dist', '--exec=x']) + assert config._getsessionname() == 'DSession' + config = py.test2.config._reparse([self.tmpdir, '-f', + '--dist', '--exec=x']) assert config._getsessionname() == 'LooponfailingSession' def test_sessionname_lookup_custom(self): @@ -486,3 +484,4 @@ assert newcol.fspath == topdir assert newcol2.fspath.basename == dir1.basename assert newcol2.fspath.relto(topdir) + Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Fri Aug 15 21:44:25 2008 @@ -223,17 +223,3 @@ assert failed[1].colitem.name == "test_other" assert failed[2].colitem.name == "test_two" -from py.__.test2.looponfail.remote import LooponfailingSession - -class Test_TerminalRemoteSession: - def test_exec_leads_to_DistSession(self): - py.test.skip("XXX fix --exec") - executable = py.std.sys.executable - config = py.test2.config._reparse(['--exec=' + executable]) - print config - assert config._getsessionname() == "LooponfailingSession" - - def test_looponfailing_leads_to_LooponfailingSession(self): - config = py.test2.config._reparse(['--looponfailing']) - sessionname = config._getsessionname() - assert sessionname == "LooponfailingSession" From hpk at codespeak.net Fri Aug 15 22:47:51 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 22:47:51 +0200 (CEST) Subject: [py-svn] r57295 - in py/branch/event/py/test2: . dsession dsession/testing testing Message-ID: <20080815204751.9AB62169E37@codespeak.net> Author: hpk Date: Fri Aug 15 22:47:48 2008 New Revision: 57295 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/defaultconftest.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/dsession/hostmanage.py py/branch/event/py/test2/dsession/testing/test_dsession.py py/branch/event/py/test2/dsession/testing/test_hostmanage.py py/branch/event/py/test2/testing/test_config.py Log: implement new --numprocesses option to specify the - um - number of processes to run tests on. Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Fri Aug 15 22:47:48 2008 @@ -179,7 +179,7 @@ """ return default session name as determined from options. """ if self.option.looponfailing: name = 'LooponfailingSession' - elif self.option.dist or self.option.executable: + elif self.option.numprocesses or self.option.dist or self.option.executable: name = 'DSession' else: name = 'Session' Modified: py/branch/event/py/test2/defaultconftest.py ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test2/defaultconftest.py Fri Aug 15 22:47:48 2008 @@ -77,6 +77,9 @@ Option('', '--exec', action="store", dest="executable", default=None, help="python executable to run the tests with."), + Option('-n', '--numprocesses', dest="numprocesses", default=0, + action="store", type="int", + help="number of local test processes."), Option('', '--debug', action="store_true", dest="debug", default=False, help="turn on debugging information."), Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Fri Aug 15 22:47:48 2008 @@ -57,14 +57,18 @@ raise ValueError, "--looponfailing together with --pdb not supported." if option.executable and option.usepdb: raise ValueError, "--exec together with --pdb not supported." - + if option.executable and not option.dist and not option.numprocesses: + option.numprocesses = 1 config = self.config + if config.option.numprocesses: + return try: config.getvalue('dist_hosts') except KeyError: - print "For running ad-hoc distributed tests you need to specify" - print "dist_hosts in a local conftest.py file, for example:" - print "for example:" + print "Don't know where to distribute tests to. You may want" + print "to specify either a number of local processes to start" + print "with '--numprocesses=NUM' or specify 'dist_hosts' in a local" + print "conftest.py file, for example:" print print " dist_hosts = ['localhost'] * 4 # for 3 processors" print " dist_hosts = ['you at remote.com', '...'] # for testing on ssh accounts" Modified: py/branch/event/py/test2/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/hostmanage.py (original) +++ py/branch/event/py/test2/dsession/hostmanage.py Fri Aug 15 22:47:48 2008 @@ -122,6 +122,15 @@ ) return remotepath +def gethosts(config, addrel): + if config.option.numprocesses: + hosts = ['localhost'] * config.option.numprocesses + else: + hosts = config.getvalue("dist_hosts") + python = config.option.executable or "python" + hosts = [Host(x, addrel, python=python) for x in hosts] + return hosts + class HostManager(object): def __init__(self, session, hosts=None): self.session = session @@ -133,9 +142,7 @@ self._addrel = addrel self.roots = roots if hosts is None: - hosts = self.session.config.getvalue("dist_hosts") - python = self.session.config.option.executable or "python" - hosts = [Host(x, addrel, python=python) for x in hosts] + hosts = gethosts(self.session.config, addrel) self.hosts = hosts def prepare_gateways(self): Modified: py/branch/event/py/test2/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test2/dsession/testing/test_dsession.py Fri Aug 15 22:47:48 2008 @@ -25,6 +25,14 @@ print queue.get() class TestDSession(InlineCollection): + def test_fixoptions(self): + config = self.parseconfig("--exec=xxx") + config.initsession().fixoptions() + assert config.option.numprocesses == 1 + config = self.parseconfig("--exec=xxx", '-n3') + config.initsession().fixoptions() + assert config.option.numprocesses == 3 + def test_add_remove_host(self): item = self.getitem("def test_func(): pass") rep = run(item) Modified: py/branch/event/py/test2/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test2/dsession/testing/test_hostmanage.py Fri Aug 15 22:47:48 2008 @@ -4,7 +4,7 @@ import py from basetest import DirSetup -from py.__.test2.dsession.hostmanage import HostRSync, Host, HostManager +from py.__.test2.dsession.hostmanage import HostRSync, Host, HostManager, gethosts from py.__.test2.dsession.hostmanage import sethomedir, gethomedir, getpath_relto_home from py.__.test2 import event @@ -314,3 +314,8 @@ assert py.path.local._gethomedir() == curdir +def test_gethosts(): + config = py.test2.config._reparse(['-n3']) + hosts = gethosts(config, '') + assert len(hosts) == 3 + Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Fri Aug 15 22:47:48 2008 @@ -188,14 +188,6 @@ def setup_method(self, method): self.tmpdir = self.tmproot.ensure(method.__name__, dir=1) - def test_sessionname_default(self): - config = py.test2.config._reparse([self.tmpdir]) - assert config._getsessionname() == 'Session' - - def test_sessionname_dist(self): - config = py.test2.config._reparse([self.tmpdir, '--dist']) - assert config._getsessionname() == 'DSession' - def test_session_eventlog(self): eventlog = self.tmpdir.join("test_session_eventlog") config = py.test2.config._reparse([self.tmpdir, @@ -211,6 +203,12 @@ assert config._getsessionname() == 'DSession' def test_implied_different_sessions(self): + config = py.test2.config._reparse([self.tmpdir]) + assert config._getsessionname() == 'Session' + config = py.test2.config._reparse([self.tmpdir, '--dist']) + assert config._getsessionname() == 'DSession' + config = py.test2.config._reparse([self.tmpdir, '-n3']) + assert config._getsessionname() == 'DSession' config = py.test2.config._reparse([self.tmpdir, '--looponfailing']) assert config._getsessionname() == 'LooponfailingSession' config = py.test2.config._reparse([self.tmpdir, '--exec=x']) From hpk at codespeak.net Fri Aug 15 22:48:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 22:48:02 +0200 (CEST) Subject: [py-svn] r57296 - in py/branch/event/py/test2: . report/testing testing Message-ID: <20080815204802.8989E169E37@codespeak.net> Author: hpk Date: Fri Aug 15 22:48:00 2008 New Revision: 57296 Modified: py/branch/event/py/test2/collect.py py/branch/event/py/test2/report/testing/test_collectonly.py py/branch/event/py/test2/report/testing/test_terminal.py py/branch/event/py/test2/testing/acceptance_test.py Log: a few fixes for python2.3 Modified: py/branch/event/py/test2/collect.py ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test2/collect.py Fri Aug 15 22:48:00 2008 @@ -231,12 +231,12 @@ %(chain[0].fspath, topdir)) return relpath, tuple([x.name for x in chain[1:]]) - @staticmethod def _fromtrail(trail, config): relpath, names = trail fspath = config.topdir.join(relpath) col = config.getfsnode(fspath) return col._getitembynames(names) + _fromtrail = staticmethod(_fromtrail) def _repr_failure_py(self, excinfo, outerr): excinfo.traceback = self._prunetraceback(excinfo.traceback) Modified: py/branch/event/py/test2/report/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_collectonly.py (original) +++ py/branch/event/py/test2/report/testing/test_collectonly.py Fri Aug 15 22:48:00 2008 @@ -1,8 +1,8 @@ import py from py.__.test2.report.collectonly import CollectonlyReporter from py.__.test2 import event -from py.__.test2.testing.suptest import ( - InlineCollection, popvalue, assert_stringio_contains_lines) +from py.__.test2.testing.suptest import InlineCollection, popvalue +from py.__.test2.testing.suptest import assert_stringio_contains_lines class TestCollectonly(InlineCollection): def test_collectonly_basic(self): Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Fri Aug 15 22:48:00 2008 @@ -4,8 +4,8 @@ from py.__.test2 import event #from py.__.test2.testing import suptest from py.__.test2.runner import basic_run_report -from py.__.test2.testing.suptest import ( - InlineCollection, popvalue, assert_stringio_contains_lines) +from py.__.test2.testing.suptest import InlineCollection, popvalue +from py.__.test2.testing.suptest import assert_stringio_contains_lines from py.__.test2.dsession.hostmanage import Host, makehostup from py.__.test2.report.base import repr_pythonversion Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Fri Aug 15 22:48:00 2008 @@ -15,6 +15,11 @@ self.errlines = errlines class AcceptBase(FileCreation): + def popen(self, cmdargs, stdout, stderr): + if not hasattr(py.std, 'subprocess'): + py.test.skip("no subprocess module") + return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr) + def runpytest(self, *args): pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") cmdargs = [py.std.sys.executable, pytestcmd] + list(args) @@ -22,8 +27,7 @@ p1 = py.path.local("stdout") p2 = py.path.local("stderr") print "running", cmdargs, "curdir=", py.path.local() - popen = py.std.subprocess.Popen(cmdargs, - stdout=p1.open("w"), stderr=p2.open("w")) + popen = self.popen(cmdargs, stdout=p1.open("w"), stderr=p2.open("w")) ret = popen.wait() out, err = p1.readlines(cr=0), p2.readlines(cr=0) if err: From hpk at codespeak.net Fri Aug 15 23:35:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 23:35:22 +0200 (CEST) Subject: [py-svn] r57299 - in py/branch/event/py/code: . testing Message-ID: <20080815213522.08A0E169E04@codespeak.net> Author: hpk Date: Fri Aug 15 23:35:22 2008 New Revision: 57299 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/code/testing/test_excinfo.py Log: test and fix extraline. Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Fri Aug 15 23:35:22 2008 @@ -249,7 +249,7 @@ sepok = True entry.toterminal(tw) if self.extraline: - tw.line(extraline) + tw.line(self.extraline) class ReprEntry(Repr): localssep = "_ " Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Fri Aug 15 23:35:22 2008 @@ -488,6 +488,7 @@ p = FormattedExcinfo(style="short") reprtb = p.repr_traceback(excinfo) assert reprtb.extraline == "!!! Recursion detected (same locals & position)" + assert str(reprtb) def test_tb_entry_AssertionError(self): # probably this test is a bit redundant From hpk at codespeak.net Fri Aug 15 23:47:27 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 23:47:27 +0200 (CEST) Subject: [py-svn] r57300 - in py/branch/event/py: code test2/testing Message-ID: <20080815214727.A9865169E30@codespeak.net> Author: hpk Date: Fri Aug 15 23:47:23 2008 New Revision: 57300 Modified: py/branch/event/py/code/excinfo.py py/branch/event/py/test2/testing/acceptance_test.py Log: removing locals separator for now Modified: py/branch/event/py/code/excinfo.py ============================================================================== --- py/branch/event/py/code/excinfo.py (original) +++ py/branch/event/py/code/excinfo.py Fri Aug 15 23:47:23 2008 @@ -267,7 +267,8 @@ red = line.startswith("E ") tw.line(tw.markup(bold=True, red=red, text=line)) if self.reprlocals: - tw.sep(self.localssep, "Locals") + #tw.sep(self.localssep, "Locals") + tw.line("") self.reprlocals.toterminal(tw) if self.reprfileloc: tw.line("") Modified: py/branch/event/py/test2/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test2/testing/acceptance_test.py Fri Aug 15 23:47:23 2008 @@ -250,7 +250,7 @@ """) result = self.runpytest(p1, '-l') assert_lines_contain_lines(result.outlines, [ - "_ _ * Locals *", + #"_ _ * Locals *", "x* = 3", "y* = 'xxxxxx*" ]) From hpk at codespeak.net Fri Aug 15 23:55:59 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 15 Aug 2008 23:55:59 +0200 (CEST) Subject: [py-svn] r57301 - in py/branch/event/py/test2: . dsession testing Message-ID: <20080815215559.EDD24169E38@codespeak.net> Author: hpk Date: Fri Aug 15 23:55:58 2008 New Revision: 57301 Modified: py/branch/event/py/test2/config.py py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/testing/test_config.py py/branch/event/py/test2/testing/test_session.py Log: * let DSession also generate ItemStart and CollectionStart events * unify tests for DSession and Session * --collectonly takes precedence before dsession options Modified: py/branch/event/py/test2/config.py ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test2/config.py Fri Aug 15 23:55:58 2008 @@ -177,7 +177,9 @@ def _getsessionname(self): """ return default session name as determined from options. """ - if self.option.looponfailing: + if self.option.collectonly: + name = 'Session' + elif self.option.looponfailing: name = 'LooponfailingSession' elif self.option.numprocesses or self.option.dist or self.option.executable: name = 'DSession' Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Fri Aug 15 23:55:58 2008 @@ -181,8 +181,10 @@ for next in colitems: if isinstance(next, Item): senditems.append(next) + self.bus.notify(event.ItemStart(next)) else: ev = basic_collect_report(next) + self.bus.notify(event.CollectionStart(next)) self.queue.put(ev) self.senditems(senditems) Modified: py/branch/event/py/test2/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test2/testing/test_config.py Fri Aug 15 23:55:58 2008 @@ -197,7 +197,7 @@ s = eventlog.read() assert s.find("TestrunStart") != -1 - def test_implied_lsession(self): + def test_implied_dsession(self): for x in 'startserver runbrowser rest'.split(): config = py.test2.config._reparse([self.tmpdir, '--dist', '--%s' % x]) assert config._getsessionname() == 'DSession' @@ -218,6 +218,10 @@ config = py.test2.config._reparse([self.tmpdir, '-f', '--dist', '--exec=x']) assert config._getsessionname() == 'LooponfailingSession' + config = py.test2.config._reparse([self.tmpdir, '-f', '-n3', + '--dist', '--exec=x', + '--collectonly']) + assert config._getsessionname() == 'Session' def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" Modified: py/branch/event/py/test2/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test2/testing/test_session.py Fri Aug 15 23:55:58 2008 @@ -56,30 +56,82 @@ item = dlist[0].items[0] assert item.name == "test_one" -class TestSession: +class SessionTests(suptest.InlineCollection): + def events_from_cmdline(self, *args): + paths = [p for p in args if isinstance(p, py.path.local)] + if not paths: + args = (self.tmpdir,) + args + config = self.parseconfig(*args) + self.session = config.initsession() + self.sorter = suptest.EventSorter(config, self.session) + self.session.main() + return self.sorter + + def events_from_runsource(self, source, *args): + p = self.makepyfile(test_source=source) + return self.events_from_cmdline(p, *args) + + def makepyfile(self, *args, **kw): + self.tmpdir.ensure('__init__.py') + return super(SessionTests, self).makepyfile(*args, **kw) - def test_terminal(self): - sorter = suptest.events_run_example("filetest.py") - passed, skipped, failed = sorter.countoutcomes() - assert failed == 2 - assert passed == skipped == 0 + def test_basic_testitem_events(self): + tfile = self.makepyfile(test_one=""" + def test_one(): + pass + def test_one_one(): + assert 0 + def test_other(): + raise ValueError(23) + def test_two(someargs): + pass + """) + sorter = self.events_from_cmdline(tfile) + passed, skipped, failed = sorter.listoutcomes() + assert len(skipped) == 0 + assert len(passed) == 1 + assert len(failed) == 3 + assert failed[0].colitem.name == "test_one_one" + assert failed[1].colitem.name == "test_other" + assert failed[2].colitem.name == "test_two" + itemstarted = sorter.get(event.ItemStart) + assert len(itemstarted) == 4 + colstarted = sorter.get(event.CollectionStart) + assert len(colstarted) == 1 + col = colstarted[0].collector + assert isinstance(col, py.test2.collect.Module) - def test_syntax_error_module(self): - sorter = suptest.events_run_example("syntax_error.py") + def test_nested_import_error(self): + tfile = self.makepyfile(test_one=""" + import import_fails + def test_this(): + assert import_fails.a == 1 + """, import_fails=""" + import does_not_work + a = 1 + """) + sorter = self.events_from_cmdline() l = sorter.getfailedcollections() - assert len(l) == 1 + assert len(l) == 1 out = l[0].outcome.longrepr.reprcrash.message - assert out.find(str('syntax_error.py')) != -1 - assert out.find(str('not python')) != -1 + assert out.find('does_not_work') != -1 - def test_exit_first_problem(self): - sorter = suptest.events_run_example('filetest.py', '--exitfirst') - passed, skipped, failed = sorter.countoutcomes() - assert failed == 1 - assert passed == skipped == 0 + def test_raises_output(self): + self.makepyfile(test_one=""" + import py + def test_raises_doesnt(): + py.test2.raises(ValueError, int, "3") + """) + sorter = self.events_from_cmdline() + passed, skipped, failed = sorter.listoutcomes() + assert len(failed) == 1 + out = failed[0].outcome.longrepr.reprcrash.message + if not out.find("DID NOT RAISE") != -1: + print out + py.test2.fail("incorrect raises() output") def test_generator_yields_None(self): - sorter = suptest.events_from_runsource(""" + sorter = self.events_from_runsource(""" def test_1(): yield None """) @@ -88,21 +140,77 @@ i = out.find('TypeError') assert i != -1 - def test_raises_output(self): - sorter = suptest.events_from_runsource(''' + def test_syntax_error_module(self): + sorter = self.events_from_runsource("this is really not python") + l = sorter.getfailedcollections() + assert len(l) == 1 + out = l[0].outcome.longrepr.reprcrash.message + assert out.find(str('not python')) != -1 + + def test_exit_first_problem(self): + sorter = self.events_from_runsource(""" + def test_one(): assert 0 + def test_two(): assert 0 + """, '--exitfirst') + passed, skipped, failed = sorter.countoutcomes() + assert failed == 1 + assert passed == skipped == 0 + + def test_broken_repr(self): + self.makepyfile(test_broken=""" import py - def test_raises_doesnt(): - py.test2.raises(ValueError, int, "3") - ''') + class BrokenRepr1: + foo=0 + def __repr__(self): + raise Exception("Ha Ha fooled you, I'm a broken repr().") + class BrokenRepr2: + foo=0 + def __repr__(self): + raise "Ha Ha fooled you, I'm a broken repr()." + + class TestBrokenClass: + def test_explicit_bad_repr(self): + t = BrokenRepr1() + py.test2.raises(Exception, 'repr(t)') + + def test_implicit_bad_repr1(self): + t = BrokenRepr1() + assert t.foo == 1 + + def test_implicit_bad_repr2(self): + t = BrokenRepr2() + assert t.foo == 1 + """) + sorter = self.events_from_cmdline() passed, skipped, failed = sorter.listoutcomes() - assert len(failed) == 1 + assert len(failed) == 2 out = failed[0].outcome.longrepr.reprcrash.message - if not out.find("DID NOT RAISE") != -1: - print out - py.test2.fail("incorrect raises() output") + assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' + out = failed[1].outcome.longrepr.reprcrash.message + assert out.find("[unknown exception raised in repr()]") != -1 + +class TestNewSession(SessionTests): + def test_pdb_run(self): + tfile = self.makepyfile(test_one=""" + def test_usepdb(): + assert 0 + """) + l = [] + def mypdb(*args): + l.append(args) + py.magic.patch(py.__.test2.custompdb, 'post_mortem', mypdb) + try: + sorter = self.events_from_cmdline('--pdb') + finally: + py.magic.revert(py.__.test2.custompdb, 'post_mortem') + rep = sorter.getreport("test_usepdb") + assert rep.failed + assert len(l) == 1 + tb = py.code.Traceback(l[0][0]) + assert tb[-1].name == "test_usepdb" def test_order_of_execution(self): - sorter = suptest.events_from_runsource(""" + sorter = self.events_from_runsource(""" l = [] def test_1(): l.append(1) @@ -126,50 +234,26 @@ assert passed == 7 # also test listnames() here ... - def test_nested_import_error(self): - tfile = suptest.makeuniquepyfile(""" - import import_fails - def test_this(): - assert import_fails.a == 1 - """) - tfile.dirpath('import_fails.py').write(py.code.Source(""" - import does_not_work - a = 1 - """)) - sorter = suptest.events_from_cmdline([tfile]) - l = sorter.getfailedcollections() - assert len(l) == 1 - out = l[0].outcome.longrepr.reprcrash.message - assert out.find('does_not_work') != -1 - - def test_safe_repr(self): - sorter = suptest.events_run_example("brokenrepr.py") - passed, skipped, failed = sorter.listoutcomes() - assert len(failed) == 2 - out = failed[0].outcome.longrepr.reprcrash.message - assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' - out = failed[1].outcome.longrepr.reprcrash.message - assert out.find("[unknown exception raised in repr()]") != -1 - def test_collect_only_with_various_situations(self): - p = suptest.makeuniquepyfile(""" - def test_one(): - raise ValueError() + p = self.makepyfile( + test_one=""" + def test_one(): + raise ValueError() + + class TestX: + def test_method_one(self): + pass - class TestX: - def test_method_one(self): + class TestY(TestX): pass - - class TestY(TestX): - pass - """) - p.dirpath("test_two.py").write(py.code.Source(""" - import py - py.test.skip('xxx') - """)) - p.dirpath("test_three.py").write("xxxdsadsadsadsa") - - sorter = suptest.events_from_cmdline([p.dirpath(), '--collectonly']) + """, + test_two=""" + import py + py.test.skip('xxx') + """, + test_three="xxxdsadsadsadsa" + ) + sorter = self.events_from_cmdline('--collectonly') itemstarted = sorter.get(event.ItemStart) assert len(itemstarted) == 3 @@ -183,43 +267,8 @@ assert len(colfail) == 1 assert len(colskipped) == 1 - def test_pdb_run(self): - tfile = suptest.makeuniquepyfile(""" - def test_usepdb(): - assert 0 - """) - l = [] - def mypdb(*args): - l.append(args) - py.magic.patch(py.__.test2.custompdb, 'post_mortem', mypdb) - try: - sorter = suptest.events_from_cmdline([tfile, '--pdb']) - finally: - py.magic.revert(py.__.test2.custompdb, 'post_mortem') - - rep = sorter.getreport("test_usepdb") - assert rep.failed - assert len(l) == 1 - tb = py.code.Traceback(l[0][0]) - assert tb[-1].name == "test_usepdb" - - def test_basic_testitem_events(self): - tfile = suptest.makeuniquepyfile(""" - def test_one(): - pass - def test_one_one(): - assert 0 - def test_other(): - raise ValueError(23) - def test_two(someargs): - pass - """) - sorter = suptest.events_from_cmdline([tfile]) - passed, skipped, failed = sorter.listoutcomes() - assert len(skipped) == 0 - assert len(passed) == 1 - assert len(failed) == 3 - assert failed[0].colitem.name == "test_one_one" - assert failed[1].colitem.name == "test_other" - assert failed[2].colitem.name == "test_two" - +class TestNewSessionDSession(SessionTests): + def parseconfig(self, *args): + args = ('-n1',) + args + return SessionTests.parseconfig(self, *args) + From hpk at codespeak.net Sat Aug 16 00:43:06 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 00:43:06 +0200 (CEST) Subject: [py-svn] r57303 - in py/branch/event/py/test2: dsession report report/testing Message-ID: <20080815224306.0DD91168523@codespeak.net> Author: hpk Date: Sat Aug 16 00:43:03 2008 New Revision: 57303 Modified: py/branch/event/py/test2/dsession/dsession.py py/branch/event/py/test2/report/terminal.py py/branch/event/py/test2/report/testing/test_terminal.py Log: fix ItemStart event generation for DSessions nicer --verbose output Modified: py/branch/event/py/test2/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test2/dsession/dsession.py Sat Aug 16 00:43:03 2008 @@ -181,7 +181,6 @@ for next in colitems: if isinstance(next, Item): senditems.append(next) - self.bus.notify(event.ItemStart(next)) else: ev = basic_collect_report(next) self.bus.notify(event.CollectionStart(next)) @@ -197,6 +196,7 @@ sending = tosend[:room] host.node.sendlist(sending) for item in sending: + self.bus.notify(event.ItemStart(item)) self.item2host[item] = host pending.extend(sending) tosend[:] = tosend[room:] # update inplace Modified: py/branch/event/py/test2/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test2/report/terminal.py Sat Aug 16 00:43:03 2008 @@ -27,8 +27,16 @@ self.currentfspath = fspath self._tw.write(res) + def write_ensure_prefix(self, prefix, extra=""): + if self.currentfspath != prefix: + self._tw.line() + self.currentfspath = prefix + self._tw.write(prefix) + if extra: + self._tw.write(extra) + def ensure_newline(self): - if self.currentfspath: + if self.currentfspath: self._tw.line() self.currentfspath = None @@ -62,13 +70,23 @@ self.write_line("HOSTUP: %(hostid)s %(sys.platform)s " "%(sys.executable)s - Python %(version)s" % d) - def rep_HostDown(self, ev): host = ev.host error = ev.error if error: self.write_line("HostDown %s: %s" %(host.hostid, error)) + + def rep_ItemStart(self, ev): + if self.config.option.verbose: + info = ev.item.repr_metainfo() + line = info.verboseline(basedir=self.curdir) + " " + # if on DSession we could display "sending" + self.write_ensure_prefix(line) + + def rep_RescheduleItems(self, ev): + if self.config.option.debug: + self.write_sep("!", "RESCHEDULING %s " %(ev.items,)) def rep_ItemTestReport(self, ev): super(TerminalReporter, self).rep_ItemTestReport(ev) @@ -77,9 +95,9 @@ self.write_fspath_result(fspath, ev.outcome.shortrepr) else: info = ev.colitem.repr_metainfo() - line = info.verboseline(basedir=self.curdir) + line = info.verboseline(basedir=self.curdir) + " " word = self.getoutcomeword(ev) - self.write_line("%s %s" %(line, word)) + self.write_ensure_prefix(line, word) def rep_CollectionReport(self, ev): super(TerminalReporter, self).rep_CollectionReport(ev) Modified: py/branch/event/py/test2/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test2/report/testing/test_terminal.py Sat Aug 16 00:43:03 2008 @@ -72,6 +72,9 @@ rep.processevent(event.TestrunStart()) items = [modcol.join(x) for x in modcol.listdir()] for item in items: + rep.processevent(event.ItemStart(item)) + s = stringio.getvalue().strip() + assert s.endswith(item.name) ev = basic_run_report(item) rep.processevent(ev) From hpk at codespeak.net Sat Aug 16 09:16:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 09:16:23 +0200 (CEST) Subject: [py-svn] r57304 - py/branch/event/py/test Message-ID: <20080816071623.8EE16168507@codespeak.net> Author: hpk Date: Sat Aug 16 09:16:21 2008 New Revision: 57304 Removed: py/branch/event/py/test/ Log: merging part 1, remove old py/test From hpk at codespeak.net Sat Aug 16 10:49:34 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 10:49:34 +0200 (CEST) Subject: [py-svn] r57306 - in py/branch/event/py: . bin c-extension code/testing compat compat/testing doc execnet/testing green io io/testing misc misc/testing path/local/testing rest/testing test test/dsession test/dsession/testing test/looponfail test/looponfail/testing test/report test/report/testing test/terminal test/testing test/web test2 Message-ID: <20080816084934.0E67C169E04@codespeak.net> Author: hpk Date: Sat Aug 16 10:49:26 2008 New Revision: 57306 Added: py/branch/event/py/c-extension/__init__.py py/branch/event/py/test/ - copied from r57246, py/branch/event/py/test2/ py/branch/event/py/test/cmdline.py - copied, changed from r57305, py/branch/event/py/test2/cmdline.py py/branch/event/py/test/collect.py - copied, changed from r57305, py/branch/event/py/test2/collect.py py/branch/event/py/test/config.py - copied, changed from r57305, py/branch/event/py/test2/config.py py/branch/event/py/test/defaultconftest.py - copied, changed from r57305, py/branch/event/py/test2/defaultconftest.py py/branch/event/py/test/dsession/ - copied from r57305, py/branch/event/py/test2/dsession/ py/branch/event/py/test/event.py - copied, changed from r57305, py/branch/event/py/test2/event.py py/branch/event/py/test/looponfail/ - copied from r57305, py/branch/event/py/test2/looponfail/ py/branch/event/py/test/pycollect.py - copied, changed from r57305, py/branch/event/py/test2/pycollect.py py/branch/event/py/test/report/ - copied from r57305, py/branch/event/py/test2/report/ py/branch/event/py/test/runner.py - copied, changed from r57305, py/branch/event/py/test2/runner.py py/branch/event/py/test/session.py - copied, changed from r57305, py/branch/event/py/test2/session.py py/branch/event/py/test/testing/ - copied from r57305, py/branch/event/py/test2/testing/ Removed: py/branch/event/py/bin/py.test2 py/branch/event/py/misc/_maketest2.py py/branch/event/py/test/_mypytest.py py/branch/event/py/test/terminal/ py/branch/event/py/test2/ Modified: py/branch/event/py/__init__.py py/branch/event/py/bin/_maketest2.py py/branch/event/py/c-extension/conftest.py py/branch/event/py/code/testing/test_excinfo.py py/branch/event/py/compat/conftest.py py/branch/event/py/compat/testing/test_doctest.py py/branch/event/py/compat/testing/test_doctest2.py py/branch/event/py/doc/code.txt py/branch/event/py/doc/conftest.py py/branch/event/py/doc/test_conftest.py py/branch/event/py/execnet/testing/test_gateway.py py/branch/event/py/green/conftest.py py/branch/event/py/io/forkedfunc.py py/branch/event/py/io/testing/test_forkedfunc.py py/branch/event/py/io/testing/test_terminalwriter.py py/branch/event/py/misc/conftest-socketgatewayrun.py py/branch/event/py/misc/testing/test_initpkg.py py/branch/event/py/path/local/testing/test_local.py py/branch/event/py/rest/testing/test_convert.py py/branch/event/py/rest/testing/test_directive.py py/branch/event/py/test/compat.py py/branch/event/py/test/conftesthandle.py py/branch/event/py/test/dsession/dsession.py py/branch/event/py/test/dsession/hostmanage.py py/branch/event/py/test/dsession/masterslave.py py/branch/event/py/test/dsession/testing/basetest.py py/branch/event/py/test/dsession/testing/test_dsession.py py/branch/event/py/test/dsession/testing/test_functional_dsession.py py/branch/event/py/test/dsession/testing/test_hostmanage.py py/branch/event/py/test/dsession/testing/test_masterslave.py py/branch/event/py/test/dsession/testing/test_mypickle.py py/branch/event/py/test/looponfail/remote.py py/branch/event/py/test/looponfail/testing/test_remote.py py/branch/event/py/test/looponfail/testing/test_util.py py/branch/event/py/test/looponfail/util.py py/branch/event/py/test/report/base.py py/branch/event/py/test/report/collectonly.py py/branch/event/py/test/report/rest.py py/branch/event/py/test/report/terminal.py py/branch/event/py/test/report/testing/test_basereporter.py py/branch/event/py/test/report/testing/test_collectonly.py py/branch/event/py/test/report/testing/test_rest.py py/branch/event/py/test/report/testing/test_terminal.py py/branch/event/py/test/report/testing/test_web.py py/branch/event/py/test/report/testing/test_webjs.py py/branch/event/py/test/report/web.py py/branch/event/py/test/report/webjs.py py/branch/event/py/test/testing/acceptance_test.py py/branch/event/py/test/testing/setupdata.py py/branch/event/py/test/testing/suptest.py py/branch/event/py/test/testing/test_collect.py py/branch/event/py/test/testing/test_compat.py py/branch/event/py/test/testing/test_config.py py/branch/event/py/test/testing/test_conftesthandle.py py/branch/event/py/test/testing/test_doctest.py py/branch/event/py/test/testing/test_event.py py/branch/event/py/test/testing/test_outcome.py py/branch/event/py/test/testing/test_repevent.py py/branch/event/py/test/testing/test_runner_functional.py py/branch/event/py/test/testing/test_session.py py/branch/event/py/test/web/webcheck.py Log: copying py/test2 to py/test and fixing test related issues in the py lib. Modified: py/branch/event/py/__init__.py ============================================================================== --- py/branch/event/py/__init__.py (original) +++ py/branch/event/py/__init__.py Sat Aug 16 10:49:26 2008 @@ -26,47 +26,28 @@ exportdefs = { # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), - 'test2.__doc__' : ('./test2/__init__.py', '__doc__'), - 'test.raises' : ('./test/raises.py', 'raises'), - 'test2.raises' : ('./test2/outcome.py', 'raises'), - 'test.deprecated_call' : ('./test/deprecate.py', 'deprecated_call'), - 'test2.deprecated_call' : ('./test2/outcome.py', 'deprecated_call'), - 'test.skip' : ('./test/item.py', 'skip'), - 'test2.skip' : ('./test2/outcome.py', 'skip'), - 'test.fail' : ('./test/item.py', 'fail'), - 'test2.fail' : ('./test2/outcome.py', 'fail'), - 'test.exit' : ('./test/session.py', 'exit'), - 'test2.exit' : ('./test2/outcome.py', 'exit'), + 'test.raises' : ('./test/outcome.py', 'raises'), + 'test.deprecated_call' : ('./test/outcome.py', 'deprecated_call'), + 'test.skip' : ('./test/outcome.py', 'skip'), + 'test.fail' : ('./test/outcome.py', 'fail'), + 'test.exit' : ('./test/outcome.py', 'exit'), 'test.pdb' : ('./test/custompdb.py', 'set_trace'), - 'test2.pdb' : ('./test2/custompdb.py', 'set_trace'), # configuration/initialization related test api 'test.config' : ('./test/config.py', 'config_per_process'), - 'test2.config' : ('./test2/config.py', 'config_per_process'), 'test.ensuretemp' : ('./test/config.py', 'ensuretemp'), - 'test2.ensuretemp' : ('./test2/config.py', 'ensuretemp'), 'test.cmdline.main' : ('./test/cmdline.py', 'main'), - 'test2.cmdline.main' : ('./test2/cmdline.py', 'main'), # for customization of collecting/running tests 'test.collect.Collector' : ('./test/collect.py', 'Collector'), 'test.collect.Directory' : ('./test/collect.py', 'Directory'), - 'test.collect.Module' : ('./test/collect.py', 'Module'), - 'test.collect.DoctestFile' : ('./test/collect.py', 'DoctestFile'), - 'test.collect.Class' : ('./test/collect.py', 'Class'), - 'test.collect.Instance' : ('./test/collect.py', 'Instance'), - 'test.collect.Generator' : ('./test/collect.py', 'Generator'), - 'test.collect.Item' : ('./test/item.py', 'Item'), - 'test.collect.Function' : ('./test/item.py', 'Function'), - 'test2.collect.Collector' : ('./test2/collect.py', 'Collector'), - 'test2.collect.Directory' : ('./test2/collect.py', 'Directory'), - 'test2.collect.Module' : ('./test2/pycollect.py', 'Module'), - 'test2.collect.DoctestFile' : ('./test2/pycollect.py', 'DoctestFile'), - 'test2.collect.Class' : ('./test2/pycollect.py', 'Class'), - 'test2.collect.Instance' : ('./test2/pycollect.py', 'Instance'), - 'test2.collect.Generator' : ('./test2/pycollect.py', 'Generator'), - 'test2.collect.Item' : ('./test2/collect.py', 'Item'), - 'test2.collect.Function' : ('./test2/pycollect.py', 'Function'), + 'test.collect.Module' : ('./test/pycollect.py', 'Module'), + 'test.collect.DoctestFile' : ('./test/pycollect.py', 'DoctestFile'), + 'test.collect.Class' : ('./test/pycollect.py', 'Class'), + 'test.collect.Instance' : ('./test/pycollect.py', 'Instance'), + 'test.collect.Generator' : ('./test/pycollect.py', 'Generator'), + 'test.collect.Item' : ('./test/collect.py', 'Item'), + 'test.collect.Function' : ('./test/pycollect.py', 'Function'), # thread related API (still in early design phase) '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), Modified: py/branch/event/py/bin/_maketest2.py ============================================================================== --- py/branch/event/py/bin/_maketest2.py (original) +++ py/branch/event/py/bin/_maketest2.py Sat Aug 16 10:49:26 2008 @@ -1,4 +1,4 @@ -""" create a py/test2 hierarchy for use of refactoring +""" create a py/test hierarchy for use of refactoring but still be able to use py.test as a testing tool for doing the refactorings. """ @@ -10,8 +10,8 @@ for line in initfile.readlines(): newline = line l.append(line) - newline = newline.replace("'test.", "'test2.") - newline = newline.replace("'./test/", "'./test2/") + newline = newline.replace("'test.", "'test.") + newline = newline.replace("'./test/", "'./test/") if newline != line: l.append(newline) initfile.write("".join(l)) @@ -22,17 +22,17 @@ if x.basename == "__init__.py": continue s = n = x.read() - n = n.replace("py.test.", "py.test2.") - #n = n.replace("py.test2.skip", "py.test.skip") - n = n.replace("py.test ", "py.test2 ") - n = n.replace("py.__.test.", "py.__.test2.") - n = n.replace("py.__.test ", "py.__.test2 ") + n = n.replace("py.test.", "py.test.") + #n = n.replace("py.test.skip", "py.test.skip") + n = n.replace("py.test ", "py.test ") + n = n.replace("py.__.test.", "py.__.test.") + n = n.replace("py.__.test ", "py.__.test ") if x.basename.startswith("test_"): - n = n.replace("py.test2.skip(", "py.test.skip(") + n = n.replace("py.test.skip(", "py.test.skip(") if s != n: if n.find("TestCaseUnit") != -1: - n = n.replace("py.test2.collect", "py.test.collect") + n = n.replace("py.test.collect", "py.test.collect") print "writing modified", x x.write(n) @@ -50,11 +50,11 @@ cmd("svn revert %s" % initfile) change_init(initfile) - test2dir = basedir.join("test2") - cmd("svn revert -R test2") - cmd("rm -rf test2") - cmd("svn cp test test2") - perform_replace(test2dir) + testdir = basedir.join("test") + cmd("svn revert -R test") + cmd("rm -rf test") + cmd("svn cp test test") + perform_replace(testdir) finally: olddir.chdir() Deleted: /py/branch/event/py/bin/py.test2 ============================================================================== --- /py/branch/event/py/bin/py.test2 Sat Aug 16 10:49:26 2008 +++ (empty file) @@ -1,4 +0,0 @@ -#!/usr/bin/env python - -from _findpy import py -py.test2.cmdline.main() Added: py/branch/event/py/c-extension/__init__.py ============================================================================== --- (empty file) +++ py/branch/event/py/c-extension/__init__.py Sat Aug 16 10:49:26 2008 @@ -0,0 +1 @@ +# Modified: py/branch/event/py/c-extension/conftest.py ============================================================================== --- py/branch/event/py/c-extension/conftest.py (original) +++ py/branch/event/py/c-extension/conftest.py Sat Aug 16 10:49:26 2008 @@ -9,6 +9,6 @@ # return False # return super(Directory, self).recfilter(path) - #def run(self): + #def listdir(self): # py.test.skip("c-extension testing needs platform selection") pass Modified: py/branch/event/py/code/testing/test_excinfo.py ============================================================================== --- py/branch/event/py/code/testing/test_excinfo.py (original) +++ py/branch/event/py/code/testing/test_excinfo.py Sat Aug 16 10:49:26 2008 @@ -244,7 +244,7 @@ class TestFormattedExcinfo: def setup_method(self, method): - self.tmpdir = py.test2.ensuretemp("%s_%s" %( + self.tmpdir = py.test.ensuretemp("%s_%s" %( self.__class__.__name__, method.__name__)) def importasmod(self, source): Modified: py/branch/event/py/compat/conftest.py ============================================================================== --- py/branch/event/py/compat/conftest.py (original) +++ py/branch/event/py/compat/conftest.py Sat Aug 16 10:49:26 2008 @@ -1,5 +1,5 @@ import py class Directory(py.test.collect.Directory): - def run(self): - py.test.skip("compat tests currently need to be run manually") + def listdir(self): + py.test.skip("compat tests need to be run manually") Modified: py/branch/event/py/compat/testing/test_doctest.py ============================================================================== --- py/branch/event/py/compat/testing/test_doctest.py (original) +++ py/branch/event/py/compat/testing/test_doctest.py Sat Aug 16 10:49:26 2008 @@ -1917,7 +1917,7 @@ >>> import unittest >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt') + ... 'test_doctest.txt') >>> suite.run(unittest.TestResult()) @@ -1927,7 +1927,7 @@ >>> import unittest >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', + ... 'test_doctest.txt', ... package='test') >>> suite.run(unittest.TestResult()) @@ -1974,7 +1974,7 @@ You can specify initial global variables: >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', + ... 'test_doctest.txt', ... globs={'favorite_color': 'blue'}) >>> suite.run(unittest.TestResult()) @@ -1983,7 +1983,7 @@ provide doctest options: >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', + ... 'test_doctest.txt', ... optionflags=doctest.DONT_ACCEPT_BLANKLINE, ... globs={'favorite_color': 'blue'}) >>> suite.run(unittest.TestResult()) @@ -2004,7 +2004,7 @@ Here, we installed a silly variable that the test expects: >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt', + ... 'test_doctest.txt', ... setUp=setUp, tearDown=tearDown) >>> suite.run(unittest.TestResult()) @@ -2287,7 +2287,7 @@ (1, 4) """ -def old_test2(): r""" +def old_test(): r""" >>> from doctest import Tester >>> t = Tester(globs={}, verbose=1) >>> test = r''' Modified: py/branch/event/py/compat/testing/test_doctest2.py ============================================================================== --- py/branch/event/py/compat/testing/test_doctest2.py (original) +++ py/branch/event/py/compat/testing/test_doctest2.py Sat Aug 16 10:49:26 2008 @@ -107,9 +107,9 @@ clsm = classmethod(clsm) def test_main(): - from py.__.compat.testing import test_doctest2 + from py.__.compat.testing import test_doctest EXPECTED = 19 - f, t = test_support.run_doctest(test_doctest2) + f, t = test_support.run_doctest(test_doctest) if t != EXPECTED: raise test_support.TestFailed("expected %d tests to run, not %d" % (EXPECTED, t)) Modified: py/branch/event/py/doc/code.txt ============================================================================== --- py/branch/event/py/doc/code.txt (original) +++ py/branch/event/py/doc/code.txt Sat Aug 16 10:49:26 2008 @@ -133,7 +133,7 @@ ... except: ... excinfo = py.code.ExceptionInfo() >>> excinfo.typename - 'exceptions.NameError' + 'NameError' >>> isinstance(excinfo.traceback, py.code.Traceback) True >>> excinfo.exconly() Modified: py/branch/event/py/doc/conftest.py ============================================================================== --- py/branch/event/py/doc/conftest.py (original) +++ py/branch/event/py/doc/conftest.py Sat Aug 16 10:49:26 2008 @@ -108,12 +108,12 @@ #return [] # no need to rebuild class ReSTSyntaxTest(py.test.collect.Item): - def run(self): + def execute(self): mypath = self.fspath restcheck(py.path.svnwc(mypath)) class DoctestText(py.test.collect.Item): - def run(self): + def execute(self): # XXX quite nasty... but it works (fixes win32 issues) s = self._normalize_linesep() l = [] @@ -128,9 +128,6 @@ else: l.append(line) docstring = "\n".join(l) - self.execute(mod, docstring) - - def execute(self, mod, docstring): mod.__doc__ = docstring failed, tot = py.compat.doctest.testmod(mod, verbose=1) if failed: @@ -149,7 +146,7 @@ return s class LinkCheckerMaker(py.test.collect.Collector): - def run(self): + def listdir(self): l = [] for call, tryfn, path, lineno in genlinkchecks(self.fspath): l.append(tryfn) @@ -158,7 +155,7 @@ def join(self, name): for call, tryfn, path, lineno in genlinkchecks(self.fspath): if tryfn == name: - return CheckLink(name, parent=self, args=(tryfn, path, lineno), obj=call) + return CheckLink(name, parent=self, args=(tryfn, path, lineno), callobj=call) class CheckLink(py.test.collect.Function): def setup(self): @@ -166,7 +163,7 @@ def teardown(self): pass -class ReSTChecker(py.test.collect.Module): +class ReSTChecker(py.test.collect.Module): DoctestText = DoctestText ReSTSyntaxTest = ReSTSyntaxTest @@ -177,7 +174,7 @@ pass def teardown(self): pass - def run(self): + def listdir(self): return [self.fspath.basename, 'checklinks', 'doctest'] def join(self, name): if name == self.fspath.basename: @@ -268,8 +265,8 @@ class DocDirectory(py.test.collect.Directory): ReSTChecker = ReSTChecker - def run(self): - results = super(DocDirectory, self).run() + def listdir(self): + results = super(DocDirectory, self).listdir() for x in self.fspath.listdir('*.txt', sort=True): results.append(x.basename) return results Modified: py/branch/event/py/doc/test_conftest.py ============================================================================== --- py/branch/event/py/doc/test_conftest.py (original) +++ py/branch/event/py/doc/test_conftest.py Sat Aug 16 10:49:26 2008 @@ -1,24 +1,27 @@ import py -from py.__.test import repevent +from py.__.test import event def setup_module(mod): mod.tmpdir = py.test.ensuretemp('docdoctest') def countoutcomes(session): l = [] - session.main(l.append) + session.bus.subscribe(l.append) + session.main() + session.bus.unsubscribe(l.append) passed = failed = skipped = 0 - for event in l: - if isinstance(event, repevent.ReceivedItemOutcome): - if event.outcome.passed: + for ev in l: + if isinstance(ev, event.ItemTestReport): + if ev.passed: passed += 1 - elif event.outcome.skipped: + elif ev.skipped: skipped += 1 else: failed += 1 - elif isinstance(event, repevent.FailedTryiter): - failed += 1 + elif isinstance(ev, event.CollectionReport): + if ev.failed: + failed += 1 return failed, passed, skipped def test_doctest_extra_exec(): Modified: py/branch/event/py/execnet/testing/test_gateway.py ============================================================================== --- py/branch/event/py/execnet/testing/test_gateway.py (original) +++ py/branch/event/py/execnet/testing/test_gateway.py Sat Aug 16 10:49:26 2008 @@ -76,7 +76,7 @@ def test_channel_makefile_incompatmode(self): channel = self.fac.new() - py.test2.raises(ValueError, 'channel.makefile("rw")') + py.test.raises(ValueError, 'channel.makefile("rw")') class PopenGatewayTestSetup: @@ -397,7 +397,7 @@ def test_channel_makefile_incompatmode(self): channel = self.gw.newchannel() - py.test2.raises(ValueError, 'channel.makefile("rw")') + py.test.raises(ValueError, 'channel.makefile("rw")') def test_confusion_from_os_write_stdout(self): channel = self.gw.remote_exec(""" Modified: py/branch/event/py/green/conftest.py ============================================================================== --- py/branch/event/py/green/conftest.py (original) +++ py/branch/event/py/green/conftest.py Sat Aug 16 10:49:26 2008 @@ -1,8 +1,8 @@ import py, os class Directory(py.test.collect.Directory): - def run(self): + def listdir(self): if os.name == 'nt': py.test.skip("Cannot test green layer on windows") else: - return super(Directory, self).run() + return super(Directory, self).listdir() Modified: py/branch/event/py/io/forkedfunc.py ============================================================================== --- py/branch/event/py/io/forkedfunc.py (original) +++ py/branch/event/py/io/forkedfunc.py Sat Aug 16 10:49:26 2008 @@ -23,9 +23,9 @@ self.args = args self.kwargs = kwargs self.tempdir = tempdir = py.path.local.mkdtemp() - self.RETVAL = tempdir.join('retval') - self.STDOUT = tempdir.join('stdout') - self.STDERR = tempdir.join('stderr') + self.RETVAL = tempdir.ensure('retval') + self.STDOUT = tempdir.ensure('stdout') + self.STDERR = tempdir.ensure('stderr') pid = os.fork() if pid: # in parent process Modified: py/branch/event/py/io/testing/test_forkedfunc.py ============================================================================== --- py/branch/event/py/io/testing/test_forkedfunc.py (original) +++ py/branch/event/py/io/testing/test_forkedfunc.py Sat Aug 16 10:49:26 2008 @@ -3,7 +3,7 @@ def setup_module(mod): if not hasattr(os, 'fork'): py.test.skip("forkedfunc requires os.fork") - mod.tmpdir = py.test2.ensuretemp(mod.__file__) + mod.tmpdir = py.test.ensuretemp(mod.__file__) def test_waitfinish_removes_tempdir(): ff = py.io.ForkedFunc(boxf1) Modified: py/branch/event/py/io/testing/test_terminalwriter.py ============================================================================== --- py/branch/event/py/io/testing/test_terminalwriter.py (original) +++ py/branch/event/py/io/testing/test_terminalwriter.py Sat Aug 16 10:49:26 2008 @@ -70,8 +70,8 @@ def test_attr_fullwidth(self): tw = self.getwriter() - tw.sep("-", "hello", fullwidth=40) - tw.fullwidth = 40 + tw.sep("-", "hello", fullwidth=70) + tw.fullwidth = 70 tw.sep("-", "hello") l = self.getlines() assert len(l[0]) == len(l[1]) Deleted: /py/branch/event/py/misc/_maketest2.py ============================================================================== --- /py/branch/event/py/misc/_maketest2.py Sat Aug 16 10:49:26 2008 +++ (empty file) @@ -1,53 +0,0 @@ -""" create a py/test2 hierarchy copied from py/test. - useful for refactoring py.test itself and still - use py.test itself. -""" - -from _findpy import py - -def change_init(initfile): - l = [] - for line in initfile.readlines(): - newline = line - l.append(line) - newline = newline.replace("'test.", "'test2.") - newline = newline.replace("'./test/", "'./test2/") - if newline != line: - l.append(newline) - initfile.write("".join(l)) - -def perform_replace(directory): - for x in directory.visit("*.py", - rec=lambda x: x.check(dir=1, dotfile=0)): - s = n = x.read() - n = n.replace("py.test", "py.test2") - n = n.replace("py.__.test.", "py.__.test2.") - n = n.replace("py.__.test ", "py.__.test2 ") - if s != n: - print "writing modified", x - x.write(n) - -def cmd(command): - print "* executing:", command - return py.process.cmdexec(command) - -if __name__ == '__main__': - basedir = py.path.local(py.__file__).dirpath() - #st = py.path.svnwc(basedir).status() - #assert not st.modified - olddir = basedir.chdir() - try: - initfile = basedir.join("__init__.py") - cmd("svn revert %s" % initfile) - change_init(initfile) - - test2dir = basedir.join("test2") - cmd("svn revert -R test2") - cmd("rm -rf test2") - cmd("svn cp test test2") - perform_replace(test2dir) - - finally: - olddir.chdir() - - Modified: py/branch/event/py/misc/conftest-socketgatewayrun.py ============================================================================== --- py/branch/event/py/misc/conftest-socketgatewayrun.py (original) +++ py/branch/event/py/misc/conftest-socketgatewayrun.py Sat Aug 16 10:49:26 2008 @@ -12,7 +12,7 @@ """ import py -from py.__.test.terminal.remote import RemoteTerminalSession +from py.__.test.looponfail.remote import LooponfailingSession import os @@ -27,7 +27,7 @@ # return False return True -class MySession(RemoteTerminalSession): +class MySession(LooponfailingSession): socketserveradr = ('10.9.2.62', 8888) socketserveradr = ('10.9.4.148', 8888) Modified: py/branch/event/py/misc/testing/test_initpkg.py ============================================================================== --- py/branch/event/py/misc/testing/test_initpkg.py (original) +++ py/branch/event/py/misc/testing/test_initpkg.py Sat Aug 16 10:49:26 2008 @@ -60,6 +60,9 @@ base.join('rest', 'directive.py'), base.join('test', 'testing', 'import_test'), base.join('c-extension',), + base.join('test', 'report', 'web.py'), + base.join('test', 'report', 'webjs.py'), + base.join('test', 'report', 'rest.py'), base.join('magic', 'greenlet.py'), base.join('bin'), base.join('execnet', 'script'), Modified: py/branch/event/py/path/local/testing/test_local.py ============================================================================== --- py/branch/event/py/path/local/testing/test_local.py (original) +++ py/branch/event/py/path/local/testing/test_local.py Sat Aug 16 10:49:26 2008 @@ -261,8 +261,6 @@ assert not numdir.new(ext=str(i-3)).check() def test_locked_make_numbered_dir(self): - if py.test.config.option.boxed: - py.test.skip("Fails when run as boxed tests") root = self.tmpdir for i in range(10): numdir = local.make_numbered_dir(prefix='base2.', rootdir=root, Modified: py/branch/event/py/rest/testing/test_convert.py ============================================================================== --- py/branch/event/py/rest/testing/test_convert.py (original) +++ py/branch/event/py/rest/testing/test_convert.py Sat Aug 16 10:49:26 2008 @@ -4,10 +4,10 @@ datadir = py.magic.autopath().dirpath().join("data") def setup_module(mod): - 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") + required = 'gs', 'dot', 'latex', 'epstopdf', + for exe in required: + if not py.path.local.sysfind(exe): + py.test.skip("%r not found, required: %r" %(exe, required)) def test_convert_dot(): # XXX not really clear that the result is valid pdf/eps Modified: py/branch/event/py/rest/testing/test_directive.py ============================================================================== --- py/branch/event/py/rest/testing/test_directive.py (original) +++ py/branch/event/py/rest/testing/test_directive.py Sat Aug 16 10:49:26 2008 @@ -30,8 +30,9 @@ png.remove() def _graphviz_pdf(self): - if not py.path.local.sysfind("dot") or not py.path.local.sysfind("latex"): - py.test.skip("graphviz and latex needed") + for exe in 'dot latex epstopdf'.split(): + if not py.path.local.sysfind(exe): + py.test.skip("%r needed" %(exe,)) directive.set_backend_and_register_directives("latex") txt = py.path.local(datadir.join("graphviz.txt")) Deleted: /py/branch/event/py/test2/_mypytest.py ============================================================================== --- /py/branch/event/py/test2/_mypytest.py Sat Aug 16 10:49:26 2008 +++ (empty file) @@ -1,8 +0,0 @@ -import py - -if py.test.config._initialized: - Function = py.test.collect.Function -else: - Function = py.test2.collect.Function - - Copied: py/branch/event/py/test/cmdline.py (from r57305, py/branch/event/py/test2/cmdline.py) ============================================================================== --- py/branch/event/py/test2/cmdline.py (original) +++ py/branch/event/py/test/cmdline.py Sat Aug 16 10:49:26 2008 @@ -8,7 +8,7 @@ warn_about_missing_assertion() if args is None: args = py.std.sys.argv[1:] - config = py.test2.config + config = py.test.config config.parse(args) session = config.initsession() exitstatus = session.main() Copied: py/branch/event/py/test/collect.py (from r57305, py/branch/event/py/test2/collect.py) ============================================================================== --- py/branch/event/py/test2/collect.py (original) +++ py/branch/event/py/test/collect.py Sat Aug 16 10:49:26 2008 @@ -371,7 +371,7 @@ name2items[name] = res return res -from py.__.test2.runner import basic_run_report, forked_run_report +from py.__.test.runner import basic_run_report, forked_run_report class Item(Node): """ a basic test item. """ def _getrunner(self): Modified: py/branch/event/py/test/compat.py ============================================================================== --- py/branch/event/py/test2/compat.py (original) +++ py/branch/event/py/test/compat.py Sat Aug 16 10:49:26 2008 @@ -1,6 +1,6 @@ import py -from _mypytest import Function +from py.test.collect import Function class TestCaseUnit(Function): """ compatibility Unit executor for TestCase methods @@ -27,10 +27,10 @@ def fail(self, msg=None): """ fail immediate with given message. """ - py.test2.fail(msg) + py.test.fail(msg) def assertRaises(self, excclass, func, *args, **kwargs): - py.test2.raises(excclass, func, *args, **kwargs) + py.test.raises(excclass, func, *args, **kwargs) failUnlessRaises = assertRaises # dynamically construct (redundant) methods @@ -49,7 +49,7 @@ def %(name)s(self, %(sig)s, msg=""): __tracebackhide__ = True if %(expr)s: - py.test2.fail(msg=msg + (%(sigsubst)r %% (%(sig)s))) + py.test.fail(msg=msg + (%(sigsubst)r %% (%(sig)s))) """ % locals() ) source = "".join(items) Copied: py/branch/event/py/test/config.py (from r57305, py/branch/event/py/test2/config.py) ============================================================================== --- py/branch/event/py/test2/config.py (original) +++ py/branch/event/py/test/config.py Sat Aug 16 10:49:26 2008 @@ -2,7 +2,7 @@ import py from conftesthandle import Conftest -from py.__.test2.defaultconftest import adddefaultoptions +from py.__.test.defaultconftest import adddefaultoptions optparse = py.compat.optparse @@ -145,9 +145,9 @@ def initreporter(self, bus): if self.option.collectonly: - from py.__.test2.report.collectonly import Reporter + from py.__.test.report.collectonly import Reporter else: - from py.__.test2.report.terminal import Reporter + from py.__.test.report.terminal import Reporter rep = Reporter(self, bus=bus) return rep @@ -191,13 +191,13 @@ """ this is used from tests that want to re-invoke parse(). """ #assert args # XXX should not be empty global config_per_process - oldconfig = py.test2.config + oldconfig = py.test.config try: - config_per_process = py.test2.config = Config() + config_per_process = py.test.config = Config() config_per_process.parse(args) return config_per_process finally: - config_per_process = py.test2.config = oldconfig + config_per_process = py.test.config = oldconfig def _getcapture(self, path=None): if self.option.nocapture: @@ -213,14 +213,14 @@ else: raise ValueError("unknown io capturing: " + iocapture) -# this is the one per-process instance of py.test2 configuration +# this is the one per-process instance of py.test configuration config_per_process = Config() # default import paths for sessions -Session = 'py.__.test2.session' -LooponfailingSession = 'py.__.test2.looponfail.remote' -DSession = 'py.__.test2.dsession.dsession' +Session = 'py.__.test.session' +LooponfailingSession = 'py.__.test.looponfail.remote' +DSession = 'py.__.test.dsession.dsession' # # helpers Modified: py/branch/event/py/test/conftesthandle.py ============================================================================== --- py/branch/event/py/test2/conftesthandle.py (original) +++ py/branch/event/py/test/conftesthandle.py Sat Aug 16 10:49:26 2008 @@ -3,7 +3,7 @@ class Conftest(object): """ the single place for accessing values and interacting - towards conftest modules from py.test2 objects. + towards conftest modules from py.test objects. Note that triggering Conftest instances to import conftest.py files may result in added cmdline options. Copied: py/branch/event/py/test/defaultconftest.py (from r57305, py/branch/event/py/test2/defaultconftest.py) ============================================================================== --- py/branch/event/py/test2/defaultconftest.py (original) +++ py/branch/event/py/test/defaultconftest.py Sat Aug 16 10:49:26 2008 @@ -1,12 +1,12 @@ import py -Module = py.test2.collect.Module -DoctestFile = py.test2.collect.DoctestFile -Directory = py.test2.collect.Directory -Class = py.test2.collect.Class -Generator = py.test2.collect.Generator -Function = py.test2.collect.Function -Instance = py.test2.collect.Instance +Module = py.test.collect.Module +DoctestFile = py.test.collect.DoctestFile +Directory = py.test.collect.Directory +Class = py.test.collect.Class +Generator = py.test.collect.Generator +Function = py.test.collect.Function +Instance = py.test.collect.Instance conf_iocapture = "fd" # overridable from conftest.py @@ -20,7 +20,7 @@ dist_taskspernode = 15 dist_boxed = False if hasattr(py.std.os, 'nice'): - dist_nicelevel = py.std.os.nice(0) # nice py.test2 works + dist_nicelevel = py.std.os.nice(0) # nice py.test works else: dist_nicelevel = 0 dist_rsync_ignore = [] Modified: py/branch/event/py/test/dsession/dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/dsession.py (original) +++ py/branch/event/py/test/dsession/dsession.py Sat Aug 16 10:49:26 2008 @@ -5,15 +5,15 @@ """ import py -from py.__.test2 import event -import py.__.test2.custompdb -from py.__.test2.dsession.hostmanage import HostManager -Item = (py.test.collect.Item, py.test2.collect.Item) -Collector = (py.test.collect.Collector, py.test2.collect.Collector) -from py.__.test2.runner import basic_run_report, basic_collect_report -from py.__.test2.session import Session -from py.__.test2.runner import OutcomeRepr -from py.__.test2 import outcome +from py.__.test import event +import py.__.test.custompdb +from py.__.test.dsession.hostmanage import HostManager +Item = (py.test.collect.Item, py.test.collect.Item) +Collector = (py.test.collect.Collector, py.test.collect.Collector) +from py.__.test.runner import basic_run_report, basic_collect_report +from py.__.test.session import Session +from py.__.test.runner import OutcomeRepr +from py.__.test import outcome import Queue Modified: py/branch/event/py/test/dsession/hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/hostmanage.py (original) +++ py/branch/event/py/test/dsession/hostmanage.py Sat Aug 16 10:49:26 2008 @@ -1,7 +1,7 @@ import py import sys, os -from py.__.test2.dsession.masterslave import MasterNode -from py.__.test2 import event +from py.__.test.dsession.masterslave import MasterNode +from py.__.test import event class Host(object): """ Host location representation for distributed testing. """ Modified: py/branch/event/py/test/dsession/masterslave.py ============================================================================== --- py/branch/event/py/test2/dsession/masterslave.py (original) +++ py/branch/event/py/test/dsession/masterslave.py Sat Aug 16 10:49:26 2008 @@ -2,8 +2,8 @@ Manage setup, running and local representation of remote nodes/processes. """ import py -from py.__.test2 import event -from py.__.test2.dsession.mypickle import PickleChannel +from py.__.test import event +from py.__.test.dsession.mypickle import PickleChannel class MasterNode(object): ENDMARK = -1 @@ -74,9 +74,9 @@ # setting up slave code def install_slave(host, config): channel = host.gw.remote_exec(source=""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dsession import masterslave + from py.__.test.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) masterslave.setup_at_slave_side(channel, config) """) @@ -96,7 +96,7 @@ if hasattr(os, 'nice'): nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) - from py.__.test2.dsession.hostmanage import makehostup + from py.__.test.dsession.hostmanage import makehostup host = channel.receive() channel.send(makehostup(host)) while 1: Modified: py/branch/event/py/test/dsession/testing/basetest.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/basetest.py (original) +++ py/branch/event/py/test/dsession/testing/basetest.py Sat Aug 16 10:49:26 2008 @@ -3,12 +3,12 @@ """ import py -from py.__.test2.testing.setupdata import getexamplefile +from py.__.test.testing.setupdata import getexamplefile class DirSetup(object): def setup_method(self, method): name = "%s.%s" %(self.__class__.__name__, method.func_name) - self.tmpdir = py.test2.ensuretemp(name) + self.tmpdir = py.test.ensuretemp(name) self.source = self.tmpdir.ensure("source", dir=1) self.dest = self.tmpdir.join("dest") @@ -16,7 +16,7 @@ class BasicRsessionTest(object): def setup_class(cls): path = getexamplefile("funcexamples.py") - cls.config = py.test2.config._reparse([path.dirpath()]) + cls.config = py.test.config._reparse([path.dirpath()]) cls.modulecol = cls.config.getfsnode(path) def setup_method(self, method): Modified: py/branch/event/py/test/dsession/testing/test_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_dsession.py (original) +++ py/branch/event/py/test/dsession/testing/test_dsession.py Sat Aug 16 10:49:26 2008 @@ -1,9 +1,9 @@ -from py.__.test2.testing.suptest import InlineCollection -from py.__.test2.dsession.dsession import DSession, LoopState -from py.__.test2.dsession.hostmanage import Host, makehostup -from py.__.test2.runner import basic_collect_report -from py.__.test2 import event -from py.__.test2 import outcome +from py.__.test.testing.suptest import InlineCollection +from py.__.test.dsession.dsession import DSession, LoopState +from py.__.test.dsession.hostmanage import Host, makehostup +from py.__.test.runner import basic_collect_report +from py.__.test import event +from py.__.test import outcome import py def run(item): @@ -46,7 +46,7 @@ pending = session.removehost(host) assert pending == [item] assert item not in session.item2host - py.test2.raises(Exception, "session.removehost(host)") + py.test.raises(Exception, "session.removehost(host)") def test_senditems_removeitems(self): item = self.getitem("def test_func(): pass") Modified: py/branch/event/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_functional_dsession.py (original) +++ py/branch/event/py/test/dsession/testing/test_functional_dsession.py Sat Aug 16 10:49:26 2008 @@ -3,11 +3,11 @@ """ import py -from py.__.test2 import event -from py.__.test2.dsession.dsession import DSession -from py.__.test2.dsession.hostmanage import HostManager, Host +from py.__.test import event +from py.__.test.dsession.dsession import DSession +from py.__.test.dsession.hostmanage import HostManager, Host from basetest import BasicRsessionTest, DirSetup -from py.__.test2.testing import suptest +from py.__.test.testing import suptest import os def eventreader(session): @@ -31,7 +31,7 @@ class TestAsyncFunctional(suptest.InlineCollection): def test_dist_no_disthost(self): config = self.parseconfig(self.tmpdir, '-d') - py.test2.raises(SystemExit, "config.initsession()") + py.test.raises(SystemExit, "config.initsession()") def test_session_eventlog_dist(self): self.makepyfile(conftest="dist_hosts=['localhost']\n") @@ -49,7 +49,7 @@ pass def test_x(): import py - py.test2.skip("aaa") + py.test.skip("aaa") def test_fail(): assert 0 """) @@ -70,7 +70,7 @@ def test_distribution_rsync_roots_example(self): py.test.skip("testing for root rsync needs rework") - destdir = py.test2.ensuretemp("example_dist_destdir") + destdir = py.test.ensuretemp("example_dist_destdir") subdir = "sub_example_dist" sourcedir = self.tmpdir.mkdir("source") sourcedir.ensure(subdir, "conftest.py").write(py.code.Source(""" @@ -94,7 +94,7 @@ # assert py.__file__ != '%s' """ % (tmpdir.join(subdir), py.__file__))) destdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) - config = py.test2.config._reparse([tmpdir.join(subdir)]) + config = py.test.config._reparse([tmpdir.join(subdir)]) assert config.topdir == tmpdir assert not tmpdir.join("__init__.py").check() dist = DSession(config) Modified: py/branch/event/py/test/dsession/testing/test_hostmanage.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_hostmanage.py (original) +++ py/branch/event/py/test/dsession/testing/test_hostmanage.py Sat Aug 16 10:49:26 2008 @@ -4,9 +4,9 @@ import py from basetest import DirSetup -from py.__.test2.dsession.hostmanage import HostRSync, Host, HostManager, gethosts -from py.__.test2.dsession.hostmanage import sethomedir, gethomedir, getpath_relto_home -from py.__.test2 import event +from py.__.test.dsession.hostmanage import HostRSync, Host, HostManager, gethosts +from py.__.test.dsession.hostmanage import sethomedir, gethomedir, getpath_relto_home +from py.__.test import event class TestHost(DirSetup): def _gethostinfo(self, relpath=""): @@ -53,7 +53,7 @@ def test_non_existing_hosts(self): host = Host("alskdjalsdkjasldkajlsd") - py.test2.raises((py.process.cmdexec.Error, IOError, EOFError), + py.test.raises((py.process.cmdexec.Error, IOError, EOFError), host.initgateway) def test_remote_has_homedir_as_currentdir(self): @@ -92,7 +92,7 @@ raise ValueError py.magic.patch(py.execnet, 'PopenGateway', p) try: - py.test2.raises(ValueError, host.initgateway) + py.test.raises(ValueError, host.initgateway) finally: py.magic.revert(py.execnet, 'PopenGateway') assert l[0] == host.python @@ -187,7 +187,7 @@ if dist_rsync_roots: l.append("dist_rsync_roots = %r" % dist_rsync_roots) self.source.join("conftest.py").write("\n".join(l)) - config = py.test2.config._reparse([self.source]) + config = py.test.config._reparse([self.source]) assert config.topdir == self.source session = config.initsession() hm = HostManager(session) @@ -195,7 +195,7 @@ return hm def test_hostmanager_custom_hosts(self): - session = py.test2.config._reparse([self.source]).initsession() + session = py.test.config._reparse([self.source]).initsession() hm = HostManager(session, hosts=[1,2,3]) assert hm.hosts == [1,2,3] @@ -234,7 +234,7 @@ self.source.join("conftest.py").write(py.code.Source(""" dist_rsync_roots = ['dir1/dir2'] """)) - session = py.test2.config._reparse([self.source]).initsession() + session = py.test.config._reparse([self.source]).initsession() hm = HostManager(session, hosts=[Host("localhost:" + str(self.dest))]) hm.init_rsync() @@ -250,7 +250,7 @@ self.source.join("conftest.py").write(py.code.Source(""" dist_rsync_ignore = ['dir1/dir2', 'dir5/dir6'] """)) - session = py.test2.config._reparse([self.source]).initsession() + session = py.test.config._reparse([self.source]).initsession() hm = HostManager(session, hosts=[Host("localhost:" + str(self.dest))]) hm.init_rsync() @@ -261,7 +261,7 @@ def test_hostmanage_optimise_localhost(self): hosts = [Host("localhost") for i in range(3)] - session = py.test2.config._reparse([self.source]).initsession() + session = py.test.config._reparse([self.source]).initsession() hm = HostManager(session, hosts=hosts) hm.init_rsync() for host in hosts: @@ -271,7 +271,7 @@ def test_hostmanage_setup_hosts(self): hosts = [Host("localhost") for i in range(3)] - session = py.test2.config._reparse([self.source]).initsession() + session = py.test.config._reparse([self.source]).initsession() hm = HostManager(session, hosts=hosts) queue = py.std.Queue.Queue() hm.setup_hosts(notify=queue.put) @@ -286,7 +286,7 @@ def XXXtest_ssh_rsync_samehost_twice(self): #XXX we have no easy way to have a temp directory remotely! - option = py.test2.config.option + option = py.test.config.option if option.sshtarget is None: py.test.skip("no known ssh target, use -S to set one") host1 = Host("%s" % (option.sshtarget, )) @@ -315,7 +315,7 @@ assert py.path.local._gethomedir() == curdir def test_gethosts(): - config = py.test2.config._reparse(['-n3']) + config = py.test.config._reparse(['-n3']) hosts = gethosts(config, '') assert len(hosts) == 3 Modified: py/branch/event/py/test/dsession/testing/test_masterslave.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_masterslave.py (original) +++ py/branch/event/py/test/dsession/testing/test_masterslave.py Sat Aug 16 10:49:26 2008 @@ -1,9 +1,9 @@ import py -from py.__.test2.dsession.masterslave import MasterNode -from py.__.test2.dsession.hostmanage import Host +from py.__.test.dsession.masterslave import MasterNode +from py.__.test.dsession.hostmanage import Host from basetest import BasicRsessionTest -from py.__.test2 import event +from py.__.test import event class TestMasterSlaveConnection(BasicRsessionTest): def getevent(self, eventtype=event.ItemTestReport, timeout=2.0): @@ -61,7 +61,7 @@ def test_send_on_closed_channel(self): item = self.getfunc("passed") self.node.channel.close() - py.test2.raises(IOError, "self.node.send(item)") + py.test.raises(IOError, "self.node.send(item)") #ev = self.getevent(event.InternalException) #assert ev.excinfo.errisinstance(IOError) Modified: py/branch/event/py/test/dsession/testing/test_mypickle.py ============================================================================== --- py/branch/event/py/test2/dsession/testing/test_mypickle.py (original) +++ py/branch/event/py/test/dsession/testing/test_mypickle.py Sat Aug 16 10:49:26 2008 @@ -1,6 +1,6 @@ import py -from py.__.test2.dsession.mypickle import ImmutablePickler, PickleChannel, UnpickleError +from py.__.test.dsession.mypickle import ImmutablePickler, PickleChannel, UnpickleError class A: pass @@ -75,9 +75,9 @@ def test_popen_send_instance(self): channel = self.gw.remote_exec(""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dsession.testing.test_mypickle import A + from py.__.test.dsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -94,9 +94,9 @@ def test_send_concurrent(self): channel = self.gw.remote_exec(""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dsession.testing.test_mypickle import A + from py.__.test.dsession.testing.test_mypickle import A l = [A() for i in range(10)] channel.send(l) other_l = channel.receive() @@ -124,9 +124,9 @@ def test_popen_with_callback(self): channel = self.gw.remote_exec(""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dsession.testing.test_mypickle import A + from py.__.test.dsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -145,9 +145,9 @@ def test_popen_with_callback_with_endmarker(self): channel = self.gw.remote_exec(""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dsession.testing.test_mypickle import A + from py.__.test.dsession.testing.test_mypickle import A a1 = A() a1.hello = 10 channel.send(a1) @@ -169,9 +169,9 @@ def test_popen_with_callback_with_endmarker_and_unpickling_error(self): channel = self.gw.remote_exec(""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.dsession.testing.test_mypickle import A + from py.__.test.dsession.testing.test_mypickle import A a1 = A() channel.send(a1) channel.send(a1) @@ -188,7 +188,7 @@ def test_popen_with_newchannel(self): channel = self.gw.remote_exec(""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) newchannel = channel.receive() newchannel.send(42) @@ -202,7 +202,7 @@ def test_popen_with_various_methods(self): channel = self.gw.remote_exec(""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) channel.receive() """) Copied: py/branch/event/py/test/event.py (from r57305, py/branch/event/py/test2/event.py) ============================================================================== --- py/branch/event/py/test2/event.py (original) +++ py/branch/event/py/test/event.py Sat Aug 16 10:49:26 2008 @@ -4,8 +4,7 @@ import py import time -from py.__.test2.outcome import Skipped -from py.__.test.outcome import Skipped as Skipped2 +from py.__.test.outcome import Skipped class EventBus(object): """ General Event Bus for distributing events. """ Modified: py/branch/event/py/test/looponfail/remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/remote.py (original) +++ py/branch/event/py/test/looponfail/remote.py Sat Aug 16 10:49:26 2008 @@ -10,12 +10,12 @@ from __future__ import generators import py -from py.__.test2.session import Session -from py.__.test2.outcome import Failed, Passed, Skipped -from py.__.test2.dsession.mypickle import PickleChannel -from py.__.test2.report.terminal import TerminalReporter -from py.__.test2 import event -from py.__.test2.looponfail import util +from py.__.test.session import Session +from py.__.test.outcome import Failed, Passed, Skipped +from py.__.test.dsession.mypickle import PickleChannel +from py.__.test.report.terminal import TerminalReporter +from py.__.test import event +from py.__.test.looponfail import util class LooponfailingSession(Session): def __init__(self, config): @@ -74,14 +74,14 @@ raise ValueError("already have gateway %r" % self.gateway) if out is None: out = py.io.TerminalWriter() - from py.__.test2.dsession import masterslave + from py.__.test.dsession import masterslave self.trace("setting up slave session") self.gateway = py.execnet.PopenGateway(self.executable) channel = self.gateway.remote_exec(source=""" - from py.__.test2.dsession.mypickle import PickleChannel + from py.__.test.dsession.mypickle import PickleChannel channel = PickleChannel(channel) - from py.__.test2.looponfail.remote import slave_runsession - from py.__.test2.dsession import masterslave + from py.__.test.looponfail.remote import slave_runsession + from py.__.test.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) width, hasmarkup = channel.receive() slave_runsession(channel, config, width, hasmarkup) @@ -137,7 +137,7 @@ session.reporter._tw.hasmarkup = hasmarkup session.reporter._tw.fullwidth = width if trails: - colitems = [py.test2.collect.Collector._fromtrail(x, config) + colitems = [py.test.collect.Collector._fromtrail(x, config) for x in trails] else: colitems = None Modified: py/branch/event/py/test/looponfail/testing/test_remote.py ============================================================================== --- py/branch/event/py/test2/looponfail/testing/test_remote.py (original) +++ py/branch/event/py/test/looponfail/testing/test_remote.py Sat Aug 16 10:49:26 2008 @@ -1,7 +1,7 @@ import py -from py.__.test2.testing import suptest -from py.__.test2.looponfail.remote import LooponfailingSession, LoopState, RemoteControl -from py.__.test2 import event +from py.__.test.testing import suptest +from py.__.test.looponfail.remote import LooponfailingSession, LoopState, RemoteControl +from py.__.test import event def getevent(l, evtype): result = getevents(l, evtype) Modified: py/branch/event/py/test/looponfail/testing/test_util.py ============================================================================== --- py/branch/event/py/test2/looponfail/testing/test_util.py (original) +++ py/branch/event/py/test/looponfail/testing/test_util.py Sat Aug 16 10:49:26 2008 @@ -1,6 +1,6 @@ import py -from py.__.test2.looponfail.util import StatRecorder, EventRecorder -from py.__.test2 import event +from py.__.test.looponfail.util import StatRecorder, EventRecorder +from py.__.test import event def test_filechange(): tmp = py.test.ensuretemp("test_filechange") Modified: py/branch/event/py/test/looponfail/util.py ============================================================================== --- py/branch/event/py/test2/looponfail/util.py (original) +++ py/branch/event/py/test/looponfail/util.py Sat Aug 16 10:49:26 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2 import event +from py.__.test import event class StatRecorder: def __init__(self, rootdirlist): Copied: py/branch/event/py/test/pycollect.py (from r57305, py/branch/event/py/test2/pycollect.py) ============================================================================== --- py/branch/event/py/test2/pycollect.py (original) +++ py/branch/event/py/test/pycollect.py Sat Aug 16 10:49:26 2008 @@ -17,7 +17,7 @@ """ import py -from py.__.test2.collect import Collector, FSCollector, Item, configproperty +from py.__.test.collect import Collector, FSCollector, Item, configproperty class PyobjMixin(object): def obj(): Modified: py/branch/event/py/test/report/base.py ============================================================================== --- py/branch/event/py/test2/report/base.py (original) +++ py/branch/event/py/test/report/base.py Sat Aug 16 10:49:26 2008 @@ -1,5 +1,5 @@ -from py.__.test2 import event -from py.__.test2.collect import getrelpath +from py.__.test import event +from py.__.test.collect import getrelpath import sys Modified: py/branch/event/py/test/report/collectonly.py ============================================================================== --- py/branch/event/py/test2/report/collectonly.py (original) +++ py/branch/event/py/test/report/collectonly.py Sat Aug 16 10:49:26 2008 @@ -2,8 +2,8 @@ """ --collectonly session, not to spread logic all over the place """ import py -from py.__.test2.report.base import BaseReporter -from py.__.test2.outcome import Skipped as Skipped2 +from py.__.test.report.base import BaseReporter +from py.__.test.outcome import Skipped as Skipped2 from py.__.test.outcome import Skipped class CollectonlyReporter(BaseReporter): Modified: py/branch/event/py/test/report/rest.py ============================================================================== --- py/branch/event/py/test2/report/rest.py (original) +++ py/branch/event/py/test/report/rest.py Sat Aug 16 10:49:26 2008 @@ -5,8 +5,8 @@ import py import sys from StringIO import StringIO -from py.__.test2.reporter import AbstractReporter -from py.__.test2 import event +from py.__.test.reporter import AbstractReporter +from py.__.test import event from py.__.rest.rst import * class RestReporter(AbstractReporter): @@ -71,7 +71,7 @@ def report_ItemStart(self, event): item = event.item - if isinstance(item, py.test2.collect.Module): + if isinstance(item, py.test.collect.Module): lgt = len(list(item._tryiter())) lns = item.listnames()[1:] name = "/".join(lns) Modified: py/branch/event/py/test/report/terminal.py ============================================================================== --- py/branch/event/py/test2/report/terminal.py (original) +++ py/branch/event/py/test/report/terminal.py Sat Aug 16 10:49:26 2008 @@ -1,10 +1,8 @@ import py import sys -from py.__.test2 import event -from py.__.test2.report.base import BaseReporter -from py.__.test2.outcome import Skipped as Skipped2 -from py.__.test.outcome import Skipped -from py.__.test2.report.base import getrelpath, repr_pythonversion, getmodpath +from py.__.test import event +from py.__.test.report.base import BaseReporter +from py.__.test.report.base import getrelpath, repr_pythonversion, getmodpath class TerminalReporter(BaseReporter): def __init__(self, config, file=None, bus=None): Modified: py/branch/event/py/test/report/testing/test_basereporter.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_basereporter.py (original) +++ py/branch/event/py/test/report/testing/test_basereporter.py Sat Aug 16 10:49:26 2008 @@ -1,10 +1,10 @@ import py -from py.__.test2.report.base import BaseReporter -from py.__.test2.event import EventBus -from py.__.test2 import event -from py.__.test2.runner import OutcomeRepr -from py.__.test2.report.base import getrelpath, getmodpath, repr_pythonversion -from py.__.test2.testing import setupdata +from py.__.test.report.base import BaseReporter +from py.__.test.event import EventBus +from py.__.test import event +from py.__.test.runner import OutcomeRepr +from py.__.test.report.base import getrelpath, getmodpath, repr_pythonversion +from py.__.test.testing import setupdata import sys class TestBaseReporter: @@ -75,7 +75,7 @@ def test_getmodpath_cases(): tmpdir = py.test.ensuretemp("test_getmodpath_cases") - pkgdir = tmpdir.join("pkg") + pkgdir = tmpdir.join("test_getmodpath") pkgdir.ensure("__init__.py") nopkgdir = tmpdir.ensure("nopkg", dir=1) def checkpkg(names, expected): Modified: py/branch/event/py/test/report/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_collectonly.py (original) +++ py/branch/event/py/test/report/testing/test_collectonly.py Sat Aug 16 10:49:26 2008 @@ -1,8 +1,8 @@ import py -from py.__.test2.report.collectonly import CollectonlyReporter -from py.__.test2 import event -from py.__.test2.testing.suptest import InlineCollection, popvalue -from py.__.test2.testing.suptest import assert_stringio_contains_lines +from py.__.test.report.collectonly import CollectonlyReporter +from py.__.test import event +from py.__.test.testing.suptest import InlineCollection, popvalue +from py.__.test.testing.suptest import assert_stringio_contains_lines class TestCollectonly(InlineCollection): def test_collectonly_basic(self): @@ -27,7 +27,7 @@ def test_collectonly_skipped_module(self): modcol = self.getmodulecol(configargs=['--collectonly'], source=""" import py - py.test2.skip("nomod") + py.test.skip("nomod") """, withsession=True) stringio = py.std.cStringIO.StringIO() rep = CollectonlyReporter(modcol._config, bus=self.session.bus, out=stringio) Modified: py/branch/event/py/test/report/testing/test_rest.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_rest.py (original) +++ py/branch/event/py/test/report/testing/test_rest.py Sat Aug 16 10:49:26 2008 @@ -6,13 +6,13 @@ py.test.skip("refactor ReST reporter tests") -from py.__.test2.testing.test_reporter import AbstractTestReporter,\ +from py.__.test.testing.test_reporter import AbstractTestReporter,\ DummyChannel -from py.__.test2 import event -from py.__.test2.dsession.rest import RestReporter, NoLinkWriter +from py.__.test import event +from py.__.test.dsession.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * -from py.__.test2.dsession.hostmanage import Host -from py.__.test2.outcome import SerializableOutcome +from py.__.test.dsession.hostmanage import Host +from py.__.test.outcome import SerializableOutcome class Container(object): def __init__(self, **args): @@ -26,7 +26,7 @@ class TestRestUnits(object): def setup_method(self, method): - config = py.test2.config._reparse(["some_sub"]) + config = py.test.config._reparse(["some_sub"]) config.option.verbose = False self.config = config hosts = [Host('localhost')] @@ -82,7 +82,7 @@ """ def test_report_ItemStart(self): - class FakeModule(py.test2.collect.Module): + class FakeModule(py.test.collect.Module): def __init__(self, parent): self.parent = parent self.fspath = py.path.local('.') Modified: py/branch/event/py/test/report/testing/test_terminal.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_terminal.py (original) +++ py/branch/event/py/test/report/testing/test_terminal.py Sat Aug 16 10:49:26 2008 @@ -1,17 +1,17 @@ import py import sys -from py.__.test2.report.terminal import TerminalReporter -from py.__.test2 import event -#from py.__.test2.testing import suptest -from py.__.test2.runner import basic_run_report -from py.__.test2.testing.suptest import InlineCollection, popvalue -from py.__.test2.testing.suptest import assert_stringio_contains_lines -from py.__.test2.dsession.hostmanage import Host, makehostup -from py.__.test2.report.base import repr_pythonversion +from py.__.test.report.terminal import TerminalReporter +from py.__.test import event +#from py.__.test.testing import suptest +from py.__.test.runner import basic_run_report +from py.__.test.testing.suptest import InlineCollection, popvalue +from py.__.test.testing.suptest import assert_stringio_contains_lines +from py.__.test.dsession.hostmanage import Host, makehostup +from py.__.test.report.base import repr_pythonversion class TestTerminal(InlineCollection): def test_session_reporter_subscription(self): - config = py.test2.config._reparse(['xxx']) + config = py.test.config._reparse(['xxx']) session = config.initsession() session.sessionstarts() rep = session.reporter @@ -38,7 +38,7 @@ def test_ok(): pass def test_skip(): - py.test2.skip("xx") + py.test.skip("xx") def test_func(): assert 0 """, withsession=True) @@ -63,7 +63,7 @@ def test_ok(): pass def test_skip(): - py.test2.skip("xx") + py.test.skip("xx") def test_func(): assert 0 """, configargs=("-v",), withsession=True) @@ -112,7 +112,7 @@ modcol = self.getmodulecol("def test_one(): pass") stringio = py.std.cStringIO.StringIO() rep = TerminalReporter(modcol._config, file=stringio) - excinfo = py.test2.raises(ValueError, "raise ValueError('hello')") + excinfo = py.test.raises(ValueError, "raise ValueError('hello')") rep.processevent(event.InternalException(excinfo)) s = popvalue(stringio) assert s.find("InternalException:") != -1 Modified: py/branch/event/py/test/report/testing/test_web.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_web.py (original) +++ py/branch/event/py/test/report/testing/test_web.py Sat Aug 16 10:49:26 2008 @@ -15,14 +15,14 @@ mod.commproxy.USE_MOCHIKIT = False mod.rpython2javascript = rpython2javascript mod.commproxy = mod.commproxy - from py.__.test2.dsession.web import TestHandler as _TestHandler - from py.__.test2.dsession.web import MultiQueue + from py.__.test.dsession.web import TestHandler as _TestHandler + from py.__.test.dsession.web import MultiQueue mod._TestHandler = _TestHandler mod.MultiQueue = MultiQueue def test_js_generate(): - from py.__.test2.dsession import webjs - from py.__.test2.dsession.web import FUNCTION_LIST, IMPORTED_PYPY + from py.__.test.dsession import webjs + from py.__.test.dsession.web import FUNCTION_LIST, IMPORTED_PYPY source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False) assert source @@ -36,7 +36,7 @@ assert h.parse_args('foo=bar%20baz') == {'foo': 'bar baz'} assert h.parse_args('foo%20bar=baz') == {'foo bar': 'baz'} assert h.parse_args('foo=bar%baz') == {'foo': 'bar\xbaz'} - py.test2.raises(ValueError, 'h.parse_args("foo")') + py.test.raises(ValueError, 'h.parse_args("foo")') class TestMultiQueue(object): def test_get_one_sessid(self): Modified: py/branch/event/py/test/report/testing/test_webjs.py ============================================================================== --- py/branch/event/py/test2/report/testing/test_webjs.py (original) +++ py/branch/event/py/test/report/testing/test_webjs.py Sat Aug 16 10:49:26 2008 @@ -12,8 +12,8 @@ mod.dom = dom mod.schedule_callbacks = schedule_callbacks - from py.__.test2.dsession import webjs - from py.__.test2.dsession.web import exported_methods + from py.__.test.dsession import webjs + from py.__.test.dsession.web import exported_methods mod.webjs = webjs mod.exported_methods = exported_methods mod.here = py.magic.autopath().dirpath() @@ -28,8 +28,8 @@ mod.dom = dom dom.window = dom.Window(html) dom.document = dom.window.document - from py.__.test2.dsession import webjs - from py.__.test2.dsession.web import exported_methods + from py.__.test.dsession import webjs + from py.__.test.dsession.web import exported_methods mod.webjs = webjs mod.exported_methods = exported_methods Modified: py/branch/event/py/test/report/web.py ============================================================================== --- py/branch/event/py/test2/report/web.py (original) +++ py/branch/event/py/test/report/web.py Sat Aug 16 10:49:26 2008 @@ -14,10 +14,10 @@ import socket import py -from py.__.test2.dsession.dsession import RSession -from py.__.test2 import event -from py.__.test2 import collect -from py.__.test2.dsession.webdata import json +from py.__.test.dsession.dsession import RSession +from py.__.test import event +from py.__.test import collect +from py.__.test.dsession.webdata import json DATADIR = py.path.local(__file__).dirpath("webdata") FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", @@ -59,7 +59,7 @@ return d class MultiQueue(object): - """ a tailor-made queue (internally using Queue) for py.test2.dsession.web + """ a tailor-made queue (internally using Queue) for py.test.dsession.web API-wise the main difference is that the get() method gets a sessid argument, which is used to determine what data to feed to the client @@ -290,7 +290,7 @@ self.pending_events.put(event) def report_ItemStart(self, event): - if isinstance(event.item, py.test2.collect.Module): + if isinstance(event.item, py.test.collect.Module): self.pending_events.put(event) def report_unknown(self, event): @@ -406,7 +406,7 @@ web_name = py.path.local(__file__).dirpath().join("webjs.py") if IMPORTED_PYPY and web_name.mtime() > js_name.mtime() or \ (not js_name.check()): - from py.__.test2.dsession import webjs + from py.__.test.dsession import webjs javascript_source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False) Modified: py/branch/event/py/test/report/webjs.py ============================================================================== --- py/branch/event/py/test2/report/webjs.py (original) +++ py/branch/event/py/test/report/webjs.py Sat Aug 16 10:49:26 2008 @@ -1,14 +1,14 @@ -""" javascript source for py.test2 distributed +""" javascript source for py.test distributed """ import py -from py.__.test2.dsession.web import exported_methods +from py.__.test.dsession.web import exported_methods try: from pypy.translator.js.modules import dom from pypy.translator.js.helper import __show_traceback except ImportError: - py.test2.skip("PyPy not found") + py.test.skip("PyPy not found") def create_elem(s): return dom.document.createElement(s) Copied: py/branch/event/py/test/runner.py (from r57305, py/branch/event/py/test2/runner.py) ============================================================================== --- py/branch/event/py/test2/runner.py (original) +++ py/branch/event/py/test/runner.py Sat Aug 16 10:49:26 2008 @@ -8,11 +8,10 @@ import py, os, sys -from py.__.test2 import event -from py.__.test2.outcome import Skipped, Exit -from py.__.test.outcome import Skipped as Skipped2 -from py.__.test2.dsession.mypickle import ImmutablePickler -import py.__.test2.custompdb +from py.__.test import event +from py.__.test.outcome import Skipped, Exit +from py.__.test.dsession.mypickle import ImmutablePickler +import py.__.test.custompdb class RobustRun(object): """ a robust setup/execute/teardown protocol. """ @@ -48,7 +47,7 @@ return self.makereport(res, when, excinfo, outerr) def getkw(self, when, excinfo, outerr): - if excinfo.errisinstance((Skipped,Skipped2)): + if excinfo.errisinstance(Skipped): outcome = "skipped" shortrepr = "s" longrepr = excinfo._getreprcrash() Copied: py/branch/event/py/test/session.py (from r57305, py/branch/event/py/test2/session.py) ============================================================================== --- py/branch/event/py/test2/session.py (original) +++ py/branch/event/py/test/session.py Sat Aug 16 10:49:26 2008 @@ -6,16 +6,16 @@ """ import py -from py.__.test2 import event, outcome -from py.__.test2.event import EventBus -import py.__.test2.custompdb +from py.__.test import event, outcome +from py.__.test.event import EventBus +import py.__.test.custompdb # used for genitems() -from py.__.test2.outcome import Exit -Item = (py.test.collect.Item, py.test2.collect.Item) -Collector = (py.test.collect.Collector, py.test2.collect.Collector) +from py.__.test.outcome import Exit +Item = (py.test.collect.Item, py.test.collect.Item) +Collector = (py.test.collect.Collector, py.test.collect.Collector) from runner import basic_collect_report -from py.__.test2.dsession.hostmanage import makehostup +from py.__.test.dsession.hostmanage import makehostup class Session(object): """ @@ -129,7 +129,7 @@ break if not self.config.option.collectonly: self.runtest(item) - py.test2.collect.Item._setupstate.teardown_all() + py.test.collect.Item._setupstate.teardown_all() except KeyboardInterrupt: exitstatus = outcome.EXIT_INTERRUPTED except: @@ -141,7 +141,7 @@ return exitstatus def runpdb(self, excinfo): - py.__.test2.custompdb.post_mortem(excinfo._excinfo[2]) + py.__.test.custompdb.post_mortem(excinfo._excinfo[2]) def runtest(self, item): runner = item._getrunner() Modified: py/branch/event/py/test/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test2/testing/acceptance_test.py (original) +++ py/branch/event/py/test/testing/acceptance_test.py Sat Aug 16 10:49:26 2008 @@ -2,11 +2,11 @@ from suptest import assert_lines_contain_lines, FileCreation pydir = py.path.local(py.__file__).dirpath() -pytestpath = pydir.join("bin", "py.test2") +pytestpath = pydir.join("bin", "py.test") EXPECTTIMEOUT=10.0 def setup_module(mod): - mod.modtmpdir = py.test2.ensuretemp(mod.__name__) + mod.modtmpdir = py.test.ensuretemp(mod.__name__) class Result: def __init__(self, ret, outlines, errlines): @@ -21,7 +21,7 @@ return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr) def runpytest(self, *args): - pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test2") + pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test") cmdargs = [py.std.sys.executable, pytestcmd] + list(args) cmdargs = map(str, cmdargs) p1 = py.path.local("stdout") @@ -148,7 +148,7 @@ def test_fail(): assert 0 def test_skip(): - py.test2.skip("dontshow") + py.test.skip("dontshow") """) result = self.runpytest() assert str(result.outlines).find("skip test summary") == -1 @@ -185,8 +185,8 @@ "*localhost* %s %s - Python %s*" %( py.std.sys.platform, py.std.sys.executable, verinfo), "*test_one.py .", - "==* 1/1 passed + 0 skips in *.[0-9][0-9] seconds *", - "* no failures :)*", + "=* 1/1 passed + 0 skips in *.[0-9][0-9] seconds *=", + "=* no failures :)*=", ]) def test_traceback_failure(self): Modified: py/branch/event/py/test/testing/setupdata.py ============================================================================== --- py/branch/event/py/test2/testing/setupdata.py (original) +++ py/branch/event/py/test/testing/setupdata.py Sat Aug 16 10:49:26 2008 @@ -2,17 +2,17 @@ #def setup_module(mod): # mod.datadir = setupdatadir() -# mod.tmpdir = py.test2.ensuretemp(mod.__name__) +# mod.tmpdir = py.test.ensuretemp(mod.__name__) #def setupdatadir(): -# datadir = py.test2.ensuretemp("datadir") +# datadir = py.test.ensuretemp("datadir") # for name in namecontent: # getexamplefile(name) # return datadir def getexamplefile(basename, tmpdir=None): if tmpdir is None: - tmpdir = py.test2.ensuretemp("example") + tmpdir = py.test.ensuretemp("example") tmpdir.ensure("__init__.py") path = tmpdir.join(basename) if not path.check(): @@ -22,7 +22,7 @@ def getexamplecollector(names, tmpdir=None): fn = getexamplefile(names[0], tmpdir=tmpdir) - config = py.test2.config._reparse([fn.dirpath()]) + config = py.test.config._reparse([fn.dirpath()]) col = config.getfsnode(fn) return col._getitembynames(names[1:]) @@ -68,7 +68,7 @@ def test_explicit_bad_repr(self): t = BrokenRepr1() - py.test2.raises(Exception, 'repr(t)') + py.test.raises(Exception, 'repr(t)') def test_implicit_bad_repr1(self): t = BrokenRepr1() @@ -135,7 +135,7 @@ raise AssertionError("hello world") def funcskipped(): - py.test2.skip("skipped") + py.test.skip("skipped") def funcprint(): print "samfing" @@ -148,13 +148,13 @@ asddsa def funcexplicitfail(): - py.test2.fail("3") + py.test.fail("3") def funcraisesfails(): - py.test2.raises(ValueError, lambda: 123) + py.test.raises(ValueError, lambda: 123) def funcoptioncustom(): - assert py.test2.config.getvalue("custom") + assert py.test.config.getvalue("custom") def funchang(): import time @@ -191,9 +191,9 @@ o = tmpdir.ensure('customconfigtest', dir=1) o.ensure('conftest.py').write("""if 1: import py - class MyFunction(py.test2.collect.Function): + class MyFunction(py.test.collect.Function): pass - class Directory(py.test2.collect.Directory): + class Directory(py.test.collect.Directory): def filefilter(self, fspath): return fspath.check(basestarts='check_', ext='.py') class myfuncmixin: @@ -201,10 +201,10 @@ def funcnamefilter(self, name): return name.startswith('check_') - class Module(myfuncmixin, py.test2.collect.Module): + class Module(myfuncmixin, py.test.collect.Module): def classnamefilter(self, name): return name.startswith('CustomTestClass') - class Instance(myfuncmixin, py.test2.collect.Instance): + class Instance(myfuncmixin, py.test.collect.Instance): pass """) checkfile = o.ensure('somedir', 'check_something.py') @@ -221,11 +221,11 @@ o = tmpdir.ensure('customconfigtest_nonpython', dir=1) o.ensure('conftest.py').write("""if 1: import py - class CustomItem(py.test2.collect.Item): + class CustomItem(py.test.collect.Item): def run(self): pass - class Directory(py.test2.collect.Directory): + class Directory(py.test.collect.Directory): def filefilter(self, fspath): return fspath.check(basestarts='check_', ext='.txt') def join(self, name): Modified: py/branch/event/py/test/testing/suptest.py ============================================================================== --- py/branch/event/py/test2/testing/suptest.py (original) +++ py/branch/event/py/test/testing/suptest.py Sat Aug 16 10:49:26 2008 @@ -12,7 +12,7 @@ eventappender(config): for getting all events in a list: """ import py -from py.__.test2 import event +from py.__.test import event from fnmatch import fnmatch def eventappender(session): @@ -26,7 +26,7 @@ def initsorter_from_cmdline(args=None): if args is None: args = [] - config = py.test2.config._reparse(args) + config = py.test.config._reparse(args) session = config.initsession() sorter = EventSorter(config, session) return sorter @@ -171,7 +171,7 @@ # class FileCreation(object): def setup_method(self, method): - self.tmpdir = py.test2.ensuretemp("%s_%s" % + self.tmpdir = py.test.ensuretemp("%s_%s" % (self.__class__.__name__, method.__name__)) def makepyfile(self, **kwargs): return self._makefile('.py', **kwargs) @@ -200,7 +200,7 @@ return self.config.getfsnode(path) def parseconfig(self, *args): - return py.test2.config._reparse(list(args)) + return py.test.config._reparse(list(args)) def getitems(self, source): modulecol = self.getmodulecol(source) Modified: py/branch/event/py/test/testing/test_collect.py ============================================================================== --- py/branch/event/py/test2/testing/test_collect.py (original) +++ py/branch/event/py/test/testing/test_collect.py Sat Aug 16 10:49:26 2008 @@ -1,11 +1,11 @@ from __future__ import generators import py -from py.__.test2 import event, outcome +from py.__.test import event, outcome import setupdata, suptest -from py.__.test2.conftesthandle import Conftest -from py.__.test2.collect import SetupState +from py.__.test.conftesthandle import Conftest +from py.__.test.collect import SetupState from test_config import getcolitems -from py.__.test2.pycollect import DoctestFileContent +from py.__.test.pycollect import DoctestFileContent class DummyConfig: def __init__(self): @@ -18,27 +18,27 @@ return self._conftest.rget(name, fspath) def setup_module(mod): - mod.tmpdir = py.test2.ensuretemp(mod.__name__) + mod.tmpdir = py.test.ensuretemp(mod.__name__) mod.dummyconfig = DummyConfig() def test_collect_versus_item(): path = setupdata.getexamplefile("filetest.py") - col = py.test2.collect.Module(path, config=dummyconfig) - assert not isinstance(col, py.test2.collect.Item) + col = py.test.collect.Module(path, config=dummyconfig) + assert not isinstance(col, py.test.collect.Item) item = col.join("test_one") assert not hasattr(item, "join") - assert not isinstance(item, py.test2.collect.Collector) + assert not isinstance(item, py.test.collect.Collector) def test_collector_deprecated_run_method(): path = setupdata.getexamplefile("filetest.py") - col = py.test2.collect.Module(path, config=dummyconfig) - res1 = py.test2.deprecated_call(col.run) + col = py.test.collect.Module(path, config=dummyconfig) + res1 = py.test.deprecated_call(col.run) res2 = col.listdir() assert res1 == res2 def test_module_assertion_setup(): path = setupdata.getexamplefile("filetest.py") - col = py.test2.collect.Module(path, config=dummyconfig) + col = py.test.collect.Module(path, config=dummyconfig) from py.__.magic import assertion l = [] py.magic.patch(assertion, "invoke", lambda: l.append(None)) @@ -59,13 +59,13 @@ def test_failing_import_execfile(): dest = setupdata.getexamplefile('failingimport.py') - col = py.test2.collect.Module(dest, config=dummyconfig) - py.test2.raises(ImportError, col.listdir) - py.test2.raises(ImportError, col.listdir) + col = py.test.collect.Module(dest, config=dummyconfig) + py.test.raises(ImportError, col.listdir) + py.test.raises(ImportError, col.listdir) def test_collect_listnames_and_back(): path = setupdata.getexamplefile("filetest.py") - col1 = py.test2.collect.Directory(path.dirpath().dirpath(), + col1 = py.test.collect.Directory(path.dirpath().dirpath(), config=dummyconfig) col2 = col1.join(path.dirpath().basename) col3 = col2.join(path.basename) @@ -78,18 +78,18 @@ def test_finds_tests(): fn = setupdata.getexamplefile('filetest.py') - col = py.test2.collect.Module(fn, config=dummyconfig) + col = py.test.collect.Module(fn, config=dummyconfig) l = col.listdir() assert len(l) == 2 assert l[0] == 'test_one' assert l[1] == 'TestClass' def test_found_certain_testfiles(): - tmp = py.test2.ensuretemp("found_certain_testfiles") + tmp = py.test.ensuretemp("found_certain_testfiles") tmp.ensure('test_found.py') tmp.ensure('found_test.py') - col = py.test2.collect.Directory(tmp, config=dummyconfig) + col = py.test.collect.Directory(tmp, config=dummyconfig) items = [col.join(x) for x in col.listdir()] assert len(items) == 2 @@ -97,7 +97,7 @@ assert items[0].name == 'found_test.py' def test_ignored_certain_directories(): - tmp = py.test2.ensuretemp("ignore_certain_directories") + tmp = py.test.ensuretemp("ignore_certain_directories") tmp.ensure("_darcs", 'test_notfound.py') tmp.ensure("CVS", 'test_notfound.py') tmp.ensure("{arch}", 'test_notfound.py') @@ -106,14 +106,14 @@ tmp.ensure("normal", 'test_found.py') tmp.ensure('test_found.py') - col = py.test2.collect.Directory(tmp, config=dummyconfig) + col = py.test.collect.Directory(tmp, config=dummyconfig) items = col.listdir() assert len(items) == 2 assert 'normal' in items assert 'test_found.py' in items def test_failing_import_directory(): - class MyDirectory(py.test2.collect.Directory): + class MyDirectory(py.test.collect.Directory): def filefilter(self, p): return p.check(fnmatch='testspecial*.py') filetest = setupdata.getexamplefile("testspecial_importerror.py") @@ -121,48 +121,48 @@ l = mydir.listdir() assert len(l) == 1 col = mydir.join(l[0]) - assert isinstance(col, py.test2.collect.Module) - py.test2.raises(ImportError, col.listdir) + assert isinstance(col, py.test.collect.Module) + py.test.raises(ImportError, col.listdir) def test_module_file_not_found(): fn = tmpdir.join('nada','no') - col = py.test2.collect.Module(fn, config=dummyconfig) - py.test2.raises(py.error.ENOENT, col.listdir) + col = py.test.collect.Module(fn, config=dummyconfig) + py.test.raises(py.error.ENOENT, col.listdir) def test_syntax_error_in_module(): modpath = setupdata.getexamplefile("syntax_error.py") - col = py.test2.collect.Module(modpath, config=dummyconfig) - py.test2.raises(SyntaxError, col.listdir) + col = py.test.collect.Module(modpath, config=dummyconfig) + py.test.raises(SyntaxError, col.listdir) def test_disabled_class(): p = setupdata.getexamplefile('disabled.py') - col = py.test2.collect.Module(p, config=dummyconfig) + col = py.test.collect.Module(p, config=dummyconfig) l = col.listdir() assert len(l) == 1 col = col.join(l[0]) - assert isinstance(col, py.test2.collect.Class) + assert isinstance(col, py.test.collect.Class) assert not col.listdir() def test_disabled_module(): p = setupdata.getexamplefile("disabled_module.py") - col = py.test2.collect.Module(p, config=dummyconfig) + col = py.test.collect.Module(p, config=dummyconfig) l = col.listdir() assert len(l) == 0 def test_generative_simple(): tfile = setupdata.getexamplefile('test_generative.py') - col = py.test2.collect.Module(tfile, config=dummyconfig) + col = py.test.collect.Module(tfile, config=dummyconfig) l = col.listdir() assert len(l) == 2 l = col.multijoin(l) generator = l[0] - assert isinstance(generator, py.test2.collect.Generator) + assert isinstance(generator, py.test.collect.Generator) l2 = generator.listdir() assert len(l2) == 2 l2 = generator.multijoin(l2) - assert isinstance(l2[0], py.test2.collect.Function) - assert isinstance(l2[1], py.test2.collect.Function) + assert isinstance(l2[0], py.test.collect.Function) + assert isinstance(l2[1], py.test.collect.Function) assert l2[0].name == '[0]' assert l2[1].name == '[1]' @@ -173,12 +173,12 @@ classlist = l[1].multijoin(classlist) cls = classlist[0] generator = cls.join(cls.listdir()[0]) - assert isinstance(generator, py.test2.collect.Generator) + assert isinstance(generator, py.test.collect.Generator) l2 = generator.listdir() assert len(l2) == 2 l2 = generator.multijoin(l2) - assert isinstance(l2[0], py.test2.collect.Function) - assert isinstance(l2[1], py.test2.collect.Function) + assert isinstance(l2[0], py.test.collect.Function) + assert isinstance(l2[1], py.test.collect.Function) assert l2[0].name == '[0]' assert l2[1].name == '[1]' @@ -239,7 +239,7 @@ assert not skipped and not failed def test_check_directory_ordered(): - tmpdir = py.test2.ensuretemp("test_check_directory_ordered") + tmpdir = py.test.ensuretemp("test_check_directory_ordered") fnames = [] for i in range(9, -1, -1): x = tmpdir.ensure("xdir%d" %(i, ), dir=1) @@ -250,17 +250,17 @@ fnames.sort() tmpdir.ensure('adir', dir=1) fnames.insert(10, 'adir') - col = py.test2.collect.Directory(tmpdir, config=dummyconfig) + col = py.test.collect.Directory(tmpdir, config=dummyconfig) names = col.listdir() assert names == fnames def test_check_equality_and_cmp_basic(): path = setupdata.getexamplefile("funcexamples.py") - col = py.test2.collect.Module(path, config=dummyconfig) + col = py.test.collect.Module(path, config=dummyconfig) fn1 = col.join("funcpassed") - assert isinstance(fn1, py.test2.collect.Function) + assert isinstance(fn1, py.test.collect.Function) fn2 = col.join("funcpassed") - assert isinstance(fn2, py.test2.collect.Function) + assert isinstance(fn2, py.test.collect.Function) assert fn1 == fn2 assert fn1 != col @@ -268,7 +268,7 @@ assert hash(fn1) == hash(fn2) fn3 = col.join("funcfailed") - assert isinstance(fn3, py.test2.collect.Function) + assert isinstance(fn3, py.test.collect.Function) assert not (fn1 == fn3) assert fn1 != fn3 assert cmp(fn1, fn3) == -1 @@ -285,7 +285,7 @@ class Testgenitems: def setup_class(cls): - cls.classtemp = py.test2.ensuretemp(cls.__name__) + cls.classtemp = py.test.ensuretemp(cls.__name__) def setup_method(self, method): self.tmp = self.classtemp.mkdir(method.func_name) @@ -294,7 +294,7 @@ if tmp is None: tmp = self.tmp print "using tempdir", tmp - config = py.test2.config._reparse([tmp]) + config = py.test.config._reparse([tmp]) session = config.initsession() l = suptest.eventappender(session) items = list(session.genitems(getcolitems(config))) @@ -319,12 +319,12 @@ assert i != j def test_skip_by_conftest_directory(self): - from py.__.test2 import outcome + from py.__.test import outcome self.tmp.ensure("subdir", "conftest.py").write(py.code.Source(""" import py - class Directory(py.test2.collect.Directory): + class Directory(py.test.collect.Directory): def listdir(self): - py.test2.skip("intentional") + py.test.skip("intentional") """)) items, events = self._genitems() assert len(items) == 0 @@ -338,7 +338,7 @@ # do we want to unify behaviour with # test_subdir_conftest_error? self.tmp.ensure("conftest.py").write("raise SyntaxError\n") - py.test2.raises(SyntaxError, self._genitems) + py.test.raises(SyntaxError, self._genitems) def test_subdir_conftest_error(self): self.tmp.ensure("sub", "conftest.py").write("raise SyntaxError\n") @@ -353,7 +353,7 @@ def test_skip_at_module_level(self): self.tmp.ensure("test_module.py").write(py.code.Source(""" import py - py.test2.skip('xxx') + py.test.skip('xxx') """)) items, events = self._genitems() funcs = [x for x in items if isinstance(x, event.ItemStart)] @@ -459,33 +459,33 @@ a = self.tmpdir.ensure("a", dir=1) self.tmpdir.ensure("a", "__init__.py") x = self.tmpdir.ensure("a", "trail.py") - config = py.test2.config._reparse([x]) + config = py.test.config._reparse([x]) col = config.getfsnode(x) trail = col._totrail() assert len(trail) == 2 assert trail[0] == a.relto(config.topdir) assert trail[1] == ('trail.py',) - col2 = py.test2.collect.Collector._fromtrail(trail, config) + col2 = py.test.collect.Collector._fromtrail(trail, config) assert col2.listnames() == col.listnames() def test_totrail_topdir_and_beyond(self): - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) col = config.getfsnode(config.topdir) trail = col._totrail() assert len(trail) == 2 assert trail[0] == '.' assert trail[1] == () - col2 = py.test2.collect.Collector._fromtrail(trail, config) + col2 = py.test.collect.Collector._fromtrail(trail, config) assert col2.fspath == config.topdir assert len(col2.listchain()) == 1 col3 = config.getfsnode(config.topdir.dirpath()) - py.test2.raises(ValueError, + py.test.raises(ValueError, "col3._totrail()") class TestCollectorReprs(suptest.InlineCollection): def test_repr_metainfo_basic_item(self): modcol = self.getmodulecol("") - Item = py.test2.collect.Item + Item = py.test.collect.Item item = Item("virtual", parent=modcol) info = item.repr_metainfo() code = py.code.Code(Item.execute) @@ -543,7 +543,7 @@ pass """ -from py.__.test2.dsession.mypickle import ImmutablePickler +from py.__.test.dsession.mypickle import ImmutablePickler class PickleTransport: def __init__(self): self.p1 = ImmutablePickler(uneven=0) @@ -568,7 +568,7 @@ return p2config def test_pickle_config(self): - config1 = py.test2.config._reparse([]) + config1 = py.test.config._reparse([]) p2config = self.unifyconfig(config1) assert p2config.topdir == config1.topdir config_back = self.p2_to_p1(p2config) Modified: py/branch/event/py/test/testing/test_compat.py ============================================================================== --- py/branch/event/py/test2/testing/test_compat.py (original) +++ py/branch/event/py/test/testing/test_compat.py Sat Aug 16 10:49:26 2008 @@ -1,7 +1,7 @@ from __future__ import generators import py -from py.__.test2.compat import TestCase -from py.__.test2.outcome import Failed +from py.__.test.compat import TestCase +from py.__.test.outcome import Failed class TestCompatTestCaseSetupSemantics(TestCase): globlist = [] Modified: py/branch/event/py/test/testing/test_config.py ============================================================================== --- py/branch/event/py/test2/testing/test_config.py (original) +++ py/branch/event/py/test/testing/test_config.py Sat Aug 16 10:49:26 2008 @@ -1,27 +1,27 @@ from __future__ import generators import py -from py.__.test2.config import gettopdir +from py.__.test.config import gettopdir import suptest, setupdata -from py.__.test2 import event +from py.__.test import event def getcolitems(config): return [config.getfsnode(arg) for arg in config.args] def test_tmpdir(): - d1 = py.test2.ensuretemp('hello') - d2 = py.test2.ensuretemp('hello') + d1 = py.test.ensuretemp('hello') + d2 = py.test.ensuretemp('hello') assert d1 == d2 assert d1.check(dir=1) def test_config_cmdline_options(): - o = py.test2.ensuretemp('configoptions') + o = py.test.ensuretemp('configoptions') o.ensure("conftest.py").write(py.code.Source(""" import py def _callback(option, opt_str, value, parser, *args, **kwargs): option.tdest = True - Option = py.test2.config.Option - option = py.test2.config.addoptions("testing group", + Option = py.test.config.Option + option = py.test.config.addoptions("testing group", Option('-G', '--glong', action="store", default=42, type="int", dest="gdest", help="g value."), # XXX note: special case, option without a destination @@ -31,53 +31,53 @@ """)) old = o.chdir() try: - config = py.test2.config._reparse(['-G', '17']) + config = py.test.config._reparse(['-G', '17']) finally: old.chdir() assert config.option.gdest == 17 def test_config_cmdline_options_only_lowercase(): - o = py.test2.ensuretemp('test_config_cmdline_options_only_lowercase') + o = py.test.ensuretemp('test_config_cmdline_options_only_lowercase') o.ensure("conftest.py").write(py.code.Source(""" import py - Option = py.test2.config.Option - options = py.test2.config.addoptions("testing group", + Option = py.test.config.Option + options = py.test.config.addoptions("testing group", Option('-g', '--glong', action="store", default=42, type="int", dest="gdest", help="g value."), ) """)) old = o.chdir() try: - py.test2.raises(ValueError, """ - py.test2.config._reparse(['-g', '17']) + py.test.raises(ValueError, """ + py.test.config._reparse(['-g', '17']) """) finally: old.chdir() def test_parsing_again_fails(): - dir = py.test2.ensuretemp("parsing_again_fails") - config = py.test2.config._reparse([str(dir)]) - py.test2.raises(AssertionError, "config.parse([])") + dir = py.test.ensuretemp("parsing_again_fails") + config = py.test.config._reparse([str(dir)]) + py.test.raises(AssertionError, "config.parse([])") def test_config_getvalue_honours_conftest(): - o = py.test2.ensuretemp('testconfigget') + o = py.test.ensuretemp('testconfigget') o.ensure("conftest.py").write("x=1") o.ensure("sub", "conftest.py").write("x=2 ; y = 3") - config = py.test2.config._reparse([str(o)]) + config = py.test.config._reparse([str(o)]) assert config.getvalue("x") == 1 assert config.getvalue("x", o.join('sub')) == 2 - py.test2.raises(KeyError, "config.getvalue('y')") - config = py.test2.config._reparse([str(o.join('sub'))]) + py.test.raises(KeyError, "config.getvalue('y')") + config = py.test.config._reparse([str(o.join('sub'))]) assert config.getvalue("x") == 2 assert config.getvalue("y") == 3 assert config.getvalue("x", o) == 1 - py.test2.raises(KeyError, 'config.getvalue("y", o)') + py.test.raises(KeyError, 'config.getvalue("y", o)') def test_siblingconftest_fails_maybe(): - from py.__.test2 import config + from py.__.test import config cfg = config.Config() - o = py.test2.ensuretemp('siblingconftest') + o = py.test.ensuretemp('siblingconftest') o.ensure("__init__.py") o.ensure("sister1", "__init__.py") o.ensure("sister1", "conftest.py").write(py.code.Source(""" @@ -101,17 +101,17 @@ old.chdir() def test_config_overwrite(): - o = py.test2.ensuretemp('testconfigget') + o = py.test.ensuretemp('testconfigget') o.ensure("conftest.py").write("x=1") - config = py.test2.config._reparse([str(o)]) + config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 config.option.x = 2 assert config.getvalue('x') == 2 - config = py.test2.config._reparse([str(o)]) + config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 def test_gettopdir(): - tmp = py.test2.ensuretemp("topdir") + tmp = py.test.ensuretemp("topdir") assert gettopdir([tmp]) == tmp topdir =gettopdir([tmp.join("hello"), tmp.join("world")]) assert topdir == tmp @@ -119,7 +119,7 @@ assert gettopdir([somefile]) == tmp def test_gettopdir_pypkg(): - tmp = py.test2.ensuretemp("topdir2") + tmp = py.test.ensuretemp("topdir2") a = tmp.ensure('a', dir=1) b = tmp.ensure('a', 'b', '__init__.py') c = tmp.ensure('a', 'b', 'c.py') @@ -129,12 +129,12 @@ def test_config_initafterpickle_some(): - tmp = py.test2.ensuretemp("test_config_initafterpickle_some") + tmp = py.test.ensuretemp("test_config_initafterpickle_some") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1 ; y=2") hello = tmp.ensure("test_hello.py") - config = py.test2.config._reparse([hello]) - config2 = py.test2.config._reparse([tmp.dirpath()]) + config = py.test.config._reparse([hello]) + config2 = py.test.config._reparse([tmp.dirpath()]) config2._initialized = False # we have to do that from tests config2._repr = config._makerepr() config2._initafterpickle(topdir=tmp.dirpath()) @@ -148,15 +148,15 @@ assert col.parent.parent is None def test_config_make_and__mergerepr(): - tmp = py.test2.ensuretemp("reprconfig1") + tmp = py.test.ensuretemp("reprconfig1") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1") - config = py.test2.config._reparse([tmp]) + config = py.test.config._reparse([tmp]) repr = config._makerepr() config.option.verbose = 42 repr2 = config._makerepr() - config = py.test2.config._reparse([tmp.dirpath()]) - py.test2.raises(KeyError, "config.getvalue('x')") + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(KeyError, "config.getvalue('x')") config._mergerepr(repr) assert config.getvalue('x') == 1 config._mergerepr(repr2) @@ -164,33 +164,33 @@ def test_config_rconfig(): - tmp = py.test2.ensuretemp("rconfigopt") + tmp = py.test.ensuretemp("rconfigopt") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write(py.code.Source(""" import py - Option = py.test2.config.Option - option = py.test2.config.addoptions("testing group", + Option = py.test.config.Option + option = py.test.config.addoptions("testing group", Option('-G', '--glong', action="store", default=42, type="int", dest="gdest", help="g value.")) """)) - config = py.test2.config._reparse([tmp, "-G", "11"]) + config = py.test.config._reparse([tmp, "-G", "11"]) assert config.option.gdest == 11 repr = config._makerepr() - config = py.test2.config._reparse([tmp.dirpath()]) - py.test2.raises(AttributeError, "config.option.gdest") + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(AttributeError, "config.option.gdest") config._mergerepr(repr) assert config.option.gdest == 11 class TestSessionAndOptions: def setup_class(cls): - cls.tmproot = py.test2.ensuretemp(cls.__name__) + cls.tmproot = py.test.ensuretemp(cls.__name__) def setup_method(self, method): self.tmpdir = self.tmproot.ensure(method.__name__, dir=1) def test_session_eventlog(self): eventlog = self.tmpdir.join("test_session_eventlog") - config = py.test2.config._reparse([self.tmpdir, + config = py.test.config._reparse([self.tmpdir, '--eventlog=%s' % eventlog]) session = config.initsession() session.bus.notify(event.TestrunStart()) @@ -199,52 +199,52 @@ def test_implied_dsession(self): for x in 'startserver runbrowser rest'.split(): - config = py.test2.config._reparse([self.tmpdir, '--dist', '--%s' % x]) + config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x]) assert config._getsessionname() == 'DSession' def test_implied_different_sessions(self): - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) assert config._getsessionname() == 'Session' - config = py.test2.config._reparse([self.tmpdir, '--dist']) + config = py.test.config._reparse([self.tmpdir, '--dist']) assert config._getsessionname() == 'DSession' - config = py.test2.config._reparse([self.tmpdir, '-n3']) + config = py.test.config._reparse([self.tmpdir, '-n3']) assert config._getsessionname() == 'DSession' - config = py.test2.config._reparse([self.tmpdir, '--looponfailing']) + config = py.test.config._reparse([self.tmpdir, '--looponfailing']) assert config._getsessionname() == 'LooponfailingSession' - config = py.test2.config._reparse([self.tmpdir, '--exec=x']) + config = py.test.config._reparse([self.tmpdir, '--exec=x']) assert config._getsessionname() == 'DSession' - config = py.test2.config._reparse([self.tmpdir, '--dist', '--exec=x']) + config = py.test.config._reparse([self.tmpdir, '--dist', '--exec=x']) assert config._getsessionname() == 'DSession' - config = py.test2.config._reparse([self.tmpdir, '-f', + config = py.test.config._reparse([self.tmpdir, '-f', '--dist', '--exec=x']) assert config._getsessionname() == 'LooponfailingSession' - config = py.test2.config._reparse([self.tmpdir, '-f', '-n3', + config = py.test.config._reparse([self.tmpdir, '-f', '-n3', '--dist', '--exec=x', '--collectonly']) assert config._getsessionname() == 'Session' def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" - from py.__.test2.session import Session + from py.__.test.session import Session class MySession(Session): pass """)) - config = py.test2.config._reparse(["--session=MySession", self.tmpdir]) + config = py.test.config._reparse(["--session=MySession", self.tmpdir]) session = config.initsession() assert session.__class__.__name__ == 'MySession' def test_initsession(self): - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) session = config.initsession() assert session.config is config def test_boxed_option_default(self): self.tmpdir.join("conftest.py").write("dist_hosts=[]") tmpdir = self.tmpdir.ensure("subdir", dir=1) - config = py.test2.config._reparse([tmpdir]) + config = py.test.config._reparse([tmpdir]) config.initsession() assert not config.option.boxed - config = py.test2.config._reparse(['--dist', tmpdir]) + config = py.test.config._reparse(['--dist', tmpdir]) config.initsession() assert not config.option.boxed @@ -255,16 +255,16 @@ dist_hosts = [] dist_boxed = True """)) - config = py.test2.config._reparse(['--dist', tmpdir]) + config = py.test.config._reparse(['--dist', tmpdir]) config.initsession() assert config.option.boxed - def test_boxed_option_from_conftest2(self): + def test_boxed_option_from_conftest(self): tmpdir = self.tmpdir tmpdir.join("conftest.py").write(py.code.Source(""" dist_boxed = False """)) - config = py.test2.config._reparse([tmpdir, '--box']) + config = py.test.config._reparse([tmpdir, '--box']) assert config.option.boxed config.initsession() assert config.option.boxed @@ -274,7 +274,7 @@ somepath = tmpdir.join("x", "y", "z") p = tmpdir.join("conftest.py") p.write("pathlist = ['.', %r]" % str(somepath)) - config = py.test2.config._reparse([p]) + config = py.test.config._reparse([p]) assert config.getvalue_pathlist('notexist') is None pl = config.getvalue_pathlist('pathlist') print pl @@ -287,13 +287,13 @@ assert pl == [py.path.local()] def test_config_iocapturing(self): - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) assert config.getvalue("conf_iocapture") tmpdir = self.tmpdir.ensure("sub-with-conftest", dir=1) tmpdir.join("conftest.py").write(py.code.Source(""" conf_iocapture = "no" """)) - config = py.test2.config._reparse([tmpdir]) + config = py.test.config._reparse([tmpdir]) assert config.getvalue("conf_iocapture") == "no" capture = config._getcapture() assert isinstance(capture, py.io.StdCapture) @@ -312,8 +312,8 @@ def check_conflict_option(opts): print "testing if options conflict:", " ".join(opts) path = setupdata.getexamplefile("filetest.py") - config = py.test2.config._reparse(opts + [path]) - py.test2.raises((ValueError, SystemExit), """ + config = py.test.config._reparse(opts + [path]) + py.test.raises((ValueError, SystemExit), """ config.initsession() """) py.test.skip("check on conflict options") @@ -329,7 +329,7 @@ def test_implied_options(self): def check_implied_option(opts, expr): path = setupdata.getexamplefile("filetest.py") - config = py.test2.config._reparse(opts + [path]) + config = py.test.config._reparse(opts + [path]) session = config.initsession() assert eval(expr, session.config.option.__dict__) @@ -355,28 +355,28 @@ def test_is_not_boxed_by_default(self): path = setupdata.getexamplefile("filetest.py") - config = py.test2.config._reparse([path]) + config = py.test.config._reparse([path]) assert not config.option.boxed class TestConfigColitems: def setup_class(cls): - cls.tmproot = py.test2.ensuretemp(cls.__name__) + cls.tmproot = py.test.ensuretemp(cls.__name__) def setup_method(self, method): self.tmpdir = self.tmproot.mkdir(method.__name__) def test_getcolitems_onedir(self): - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) colitems = getcolitems(config) assert len(colitems) == 1 col = colitems[0] - assert isinstance(col, py.test2.collect.Directory) + assert isinstance(col, py.test.collect.Directory) for col in col.listchain(): assert col._config is config def test_getcolitems_twodirs(self): - config = py.test2.config._reparse([self.tmpdir, self.tmpdir]) + config = py.test.config._reparse([self.tmpdir, self.tmpdir]) colitems = getcolitems(config) assert len(colitems) == 2 col1, col2 = colitems @@ -385,7 +385,7 @@ def test_getcolitems_curdir_and_subdir(self): a = self.tmpdir.ensure("a", dir=1) - config = py.test2.config._reparse([self.tmpdir, a]) + config = py.test.config._reparse([self.tmpdir, a]) colitems = getcolitems(config) assert len(colitems) == 2 col1, col2 = colitems @@ -397,9 +397,9 @@ def test__getcol_global_file(self): x = self.tmpdir.ensure("x.py") - config = py.test2.config._reparse([x]) + config = py.test.config._reparse([x]) col = config.getfsnode(x) - assert isinstance(col, py.test2.collect.Module) + assert isinstance(col, py.test.collect.Module) assert col.name == 'x.py' assert col.parent.name == self.tmpdir.basename assert col.parent.parent is None @@ -408,9 +408,9 @@ def test__getcol_global_dir(self): x = self.tmpdir.ensure("a", dir=1) - config = py.test2.config._reparse([x]) + config = py.test.config._reparse([x]) col = config.getfsnode(x) - assert isinstance(col, py.test2.collect.Directory) + assert isinstance(col, py.test.collect.Directory) print col.listchain() assert col.name == 'a' assert col.parent is None @@ -419,9 +419,9 @@ def test__getcol_pkgfile(self): x = self.tmpdir.ensure("x.py") self.tmpdir.ensure("__init__.py") - config = py.test2.config._reparse([x]) + config = py.test.config._reparse([x]) col = config.getfsnode(x) - assert isinstance(col, py.test2.collect.Module) + assert isinstance(col, py.test.collect.Module) assert col.name == 'x.py' assert col.parent.name == x.dirpath().basename assert col.parent.parent is None @@ -431,7 +431,7 @@ def test_config_picklability(self): import cPickle - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) s = cPickle.dumps(config) newconfig = cPickle.loads(s) assert not hasattr(newconfig, "topdir") @@ -444,7 +444,7 @@ def test_config_and_collector_pickling_missing_initafter(self): from cPickle import Pickler, Unpickler - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) col = config.getfsnode(config.topdir) io = py.std.cStringIO.StringIO() pickler = Pickler(io) @@ -459,7 +459,7 @@ def test_config_and_collector_pickling(self): from cPickle import Pickler, Unpickler dir1 = self.tmpdir.ensure("somedir", dir=1) - config = py.test2.config._reparse([self.tmpdir]) + config = py.test.config._reparse([self.tmpdir]) col = config.getfsnode(config.topdir) col1 = col.join(dir1.basename) assert col1.parent is col Modified: py/branch/event/py/test/testing/test_conftesthandle.py ============================================================================== --- py/branch/event/py/test2/testing/test_conftesthandle.py (original) +++ py/branch/event/py/test/testing/test_conftesthandle.py Sat Aug 16 10:49:26 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2.conftesthandle import Conftest +from py.__.test.conftesthandle import Conftest class TestConftestValueAccessGlobal: def setup_class(cls): @@ -7,7 +7,7 @@ # and thus no further import scope) it should still all work # because "global" conftests are imported with a # mangled module name (related to their actual path) - cls.basedir = d = py.test2.ensuretemp(cls.__name__) + cls.basedir = d = py.test.ensuretemp(cls.__name__) d.ensure("adir/conftest.py").write("a=1 ; Directory = 3") d.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") @@ -30,18 +30,18 @@ def test_default_Module_setting_is_visible_always(self): for path in self.basedir.parts(): conftest = Conftest(path) - #assert conftest.lget("Module") == py.test2.collect.Module - assert conftest.rget("Module") == py.test2.collect.Module + #assert conftest.lget("Module") == py.test.collect.Module + assert conftest.rget("Module") == py.test.collect.Module def test_default_has_lower_prio(self): conftest = Conftest(self.basedir.join("adir")) assert conftest.rget('Directory') == 3 - #assert conftest.lget('Directory') == py.test2.collect.Directory + #assert conftest.lget('Directory') == py.test.collect.Directory def test_value_access_not_existing(self): conftest = Conftest(self.basedir) - py.test2.raises(KeyError, "conftest.rget('a')") - #py.test2.raises(KeyError, "conftest.lget('a')") + py.test.raises(KeyError, "conftest.rget('a')") + #py.test.raises(KeyError, "conftest.lget('a')") def test_value_access_by_path(self): conftest = Conftest(self.basedir) @@ -50,7 +50,7 @@ assert conftest.rget("a", self.basedir.join('adir', 'b')) == 1.5 #assert conftest.lget("a", self.basedir.join('adir', 'b')) == 1 #assert conftest.lget("b", self.basedir.join('adir', 'b')) == 2 - #assert py.test2.raises(KeyError, + #assert py.test.raises(KeyError, # 'conftest.lget("b", self.basedir.join("a"))' #) Modified: py/branch/event/py/test/testing/test_doctest.py ============================================================================== --- py/branch/event/py/test2/testing/test_doctest.py (original) +++ py/branch/event/py/test/testing/test_doctest.py Sat Aug 16 10:49:26 2008 @@ -1,31 +1,34 @@ import py -from py.__.test2.outcome import Failed +from py.__.test.outcome import Failed +from py.__.test.testing.suptest import InlineCollection def setup_module(mod): mod.tmp = py.test.ensuretemp(__name__) -def test_simple_docteststring(): - p = tmp.join("test_simple_docteststring") - p.write(py.code.Source(""" - >>> i = 0 - >>> i + 1 - 1 - """)) - testitem = py.test.collect.DoctestFile(p).join(p.basename) - res = testitem.execute() - assert res is None - +class TestDoctests(InlineCollection): + def test_simple_docteststring(self): + txtfile = self.maketxtfile(test_doc=""" + >>> i = 0 + >>> i + 1 + 1 + """) + config = self.parseconfig(txtfile) + col = config.getfsnode(txtfile) + testitem = col.join(txtfile.basename) + res = testitem.execute() + assert res is None + -def test_doctest_unexpected_exception(): - py.test.skip("implement nice doctest repr for unexpected exceptions") - p = tmp.join("test_doctest_unexpected_exception") - p.write(py.code.Source(""" - >>> i = 0 - >>> x - 2 - """)) - testitem = py.test.collect.DoctestFile(p).join(p.basename) - excinfo = py.test.raises(Failed, "testitem.execute()") - repr = testitem.repr_failure(excinfo, ("", "")) - assert repr.reprlocation + def test_doctest_unexpected_exception(self): + py.test.skip("implement nice doctest repr for unexpected exceptions") + p = tmp.join("test_doctest_unexpected_exception") + p.write(py.code.Source(""" + >>> i = 0 + >>> x + 2 + """)) + testitem = py.test.collect.DoctestFile(p).join(p.basename) + excinfo = py.test.raises(Failed, "testitem.execute()") + repr = testitem.repr_failure(excinfo, ("", "")) + assert repr.reprlocation Modified: py/branch/event/py/test/testing/test_event.py ============================================================================== --- py/branch/event/py/test2/testing/test_event.py (original) +++ py/branch/event/py/test/testing/test_event.py Sat Aug 16 10:49:26 2008 @@ -1,6 +1,6 @@ import py -from py.__.test2.event import EventBus -from py.__.test2 import event +from py.__.test.event import EventBus +from py.__.test import event import suptest from py.__.code.testing.test_excinfo import TWMock Modified: py/branch/event/py/test/testing/test_outcome.py ============================================================================== --- py/branch/event/py/test2/testing/test_outcome.py (original) +++ py/branch/event/py/test/testing/test_outcome.py Sat Aug 16 10:49:26 2008 @@ -5,16 +5,16 @@ class TestRaises: def test_raises(self): - py.test2.raises(ValueError, "int('qwe')") + py.test.raises(ValueError, "int('qwe')") def test_raises_exec(self): - py.test2.raises(ValueError, "a,x = []") + py.test.raises(ValueError, "a,x = []") def test_raises_syntax_error(self): - py.test2.raises(SyntaxError, "qwe qwe qwe") + py.test.raises(SyntaxError, "qwe qwe qwe") def test_raises_function(self): - py.test2.raises(ValueError, int, 'hello') + py.test.raises(ValueError, int, 'hello') # # ============ test py.test.deprecated_call() ============== @@ -32,15 +32,15 @@ filename="hello", lineno=3) def test_deprecated_call_raises(): - excinfo = py.test2.raises(AssertionError, - "py.test2.deprecated_call(dep, 3)") + excinfo = py.test.raises(AssertionError, + "py.test.deprecated_call(dep, 3)") assert str(excinfo).find("did not produce") != -1 def test_deprecated_call(): - py.test2.deprecated_call(dep, 0) + py.test.deprecated_call(dep, 0) def test_deprecated_call_ret(): - ret = py.test2.deprecated_call(dep, 0) + ret = py.test.deprecated_call(dep, 0) assert ret == 42 def test_deprecated_call_preserves(): @@ -52,10 +52,10 @@ assert f == py.std.warnings.filters def test_deprecated_explicit_call_raises(): - py.test2.raises(AssertionError, - "py.test2.deprecated_call(dep_explicit, 3)") + py.test.raises(AssertionError, + "py.test.deprecated_call(dep_explicit, 3)") def test_deprecated_explicit_call(): - py.test2.deprecated_call(dep_explicit, 0) - py.test2.deprecated_call(dep_explicit, 0) + py.test.deprecated_call(dep_explicit, 0) + py.test.deprecated_call(dep_explicit, 0) Modified: py/branch/event/py/test/testing/test_repevent.py ============================================================================== --- py/branch/event/py/test2/testing/test_repevent.py (original) +++ py/branch/event/py/test/testing/test_repevent.py Sat Aug 16 10:49:26 2008 @@ -1,5 +1,5 @@ -from py.__.test2 import event +from py.__.test import event import setupdata, suptest from py.__.code.testing.test_excinfo import TWMock Modified: py/branch/event/py/test/testing/test_runner_functional.py ============================================================================== --- py/branch/event/py/test2/testing/test_runner_functional.py (original) +++ py/branch/event/py/test/testing/test_runner_functional.py Sat Aug 16 10:49:26 2008 @@ -1,7 +1,7 @@ import py -from py.__.test2.testing.suptest import InlineCollection -from py.__.test2.runner import basic_run_report, forked_run_report, basic_collect_report -from py.__.test2.runner import RobustRun +from py.__.test.testing.suptest import InlineCollection +from py.__.test.runner import basic_run_report, forked_run_report, basic_collect_report +from py.__.test.runner import RobustRun from py.__.code.excinfo import ReprExceptionInfo class BaseTests(InlineCollection): @@ -31,7 +31,7 @@ ev = self.runitem(""" import py def test_func(): - py.test2.skip("hello") + py.test.skip("hello") """) assert not ev.failed assert not ev.passed @@ -47,7 +47,7 @@ ev = self.runitem(""" import py def setup_function(func): - py.test2.skip("hello") + py.test.skip("hello") def test_func(): pass """) @@ -92,7 +92,7 @@ def test_custom_failure_repr(self): self.makepyfile(conftest=""" import py - class Function(py.test2.collect.Function): + class Function(py.test.collect.Function): def repr_failure(self, excinfo, outerr): return "hello" """) @@ -112,7 +112,7 @@ def test_failure_in_setup_function_ignores_custom_failure_repr(self): self.makepyfile(conftest=""" import py - class Function(py.test2.collect.Function): + class Function(py.test.collect.Function): def repr_failure(self, excinfo): assert 0 """) @@ -160,10 +160,10 @@ assert ev.outcome.when == "execute" def test_exit_propagates(self): - from py.__.test2.outcome import Exit + from py.__.test.outcome import Exit try: self.runitem(""" - from py.__.test2.outcome import Exit + from py.__.test.outcome import Exit def test_func(): raise Exit() """) @@ -177,7 +177,7 @@ return basic_run_report def test_keyboardinterrupt_propagates(self): - from py.__.test2.outcome import Exit + from py.__.test.outcome import Exit try: self.runitem(""" def test_func(): @@ -243,7 +243,7 @@ def test_skip_at_module_scope(self): col = self.getmodulecol(""" import py - py.test2.skip("hello") + py.test.skip("hello") def test_func(): pass """) Modified: py/branch/event/py/test/testing/test_session.py ============================================================================== --- py/branch/event/py/test2/testing/test_session.py (original) +++ py/branch/event/py/test/testing/test_session.py Sat Aug 16 10:49:26 2008 @@ -1,5 +1,5 @@ import py -from py.__.test2 import event +from py.__.test import event import suptest, setupdata def setup_module(mod): @@ -30,7 +30,7 @@ """)) conftest = o.join('conftest.py').write(py.code.Source(""" import py - class Class(py.test2.collect.Class): + class Class(py.test.collect.Class): def _keywords(self): return ['xxx', self.name] """)) @@ -99,7 +99,7 @@ colstarted = sorter.get(event.CollectionStart) assert len(colstarted) == 1 col = colstarted[0].collector - assert isinstance(col, py.test2.collect.Module) + assert isinstance(col, py.test.collect.Module) def test_nested_import_error(self): tfile = self.makepyfile(test_one=""" @@ -120,7 +120,7 @@ self.makepyfile(test_one=""" import py def test_raises_doesnt(): - py.test2.raises(ValueError, int, "3") + py.test.raises(ValueError, int, "3") """) sorter = self.events_from_cmdline() passed, skipped, failed = sorter.listoutcomes() @@ -128,7 +128,7 @@ out = failed[0].outcome.longrepr.reprcrash.message if not out.find("DID NOT RAISE") != -1: print out - py.test2.fail("incorrect raises() output") + py.test.fail("incorrect raises() output") def test_generator_yields_None(self): sorter = self.events_from_runsource(""" @@ -171,7 +171,7 @@ class TestBrokenClass: def test_explicit_bad_repr(self): t = BrokenRepr1() - py.test2.raises(Exception, 'repr(t)') + py.test.raises(Exception, 'repr(t)') def test_implicit_bad_repr1(self): t = BrokenRepr1() @@ -198,11 +198,11 @@ l = [] def mypdb(*args): l.append(args) - py.magic.patch(py.__.test2.custompdb, 'post_mortem', mypdb) + py.magic.patch(py.__.test.custompdb, 'post_mortem', mypdb) try: sorter = self.events_from_cmdline('--pdb') finally: - py.magic.revert(py.__.test2.custompdb, 'post_mortem') + py.magic.revert(py.__.test.custompdb, 'post_mortem') rep = sorter.getreport("test_usepdb") assert rep.failed assert len(l) == 1 Modified: py/branch/event/py/test/web/webcheck.py ============================================================================== --- py/branch/event/py/test2/web/webcheck.py (original) +++ py/branch/event/py/test/web/webcheck.py Sat Aug 16 10:49:26 2008 @@ -6,7 +6,7 @@ def check_html(string): """check an HTML string for wellformedness and validity""" - tempdir = py.test2.ensuretemp('check_html') + tempdir = py.test.ensuretemp('check_html') filename = 'temp%s.html' % (hash(string), ) tempfile = tempdir.join(filename) tempfile.write(string) @@ -22,7 +22,7 @@ valid = match.group(1) is None text = match.group(2).strip() if not valid: - temp = py.test2.ensuretemp('/w3_results_%s.html' % hash(html), dir=0) + temp = py.test.ensuretemp('/w3_results_%s.html' % hash(html), dir=0) temp.write(html) raise HTMLError( "The html is not valid. See the report file at '%s'" % temp) From hpk at codespeak.net Sat Aug 16 12:36:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 12:36:37 +0200 (CEST) Subject: [py-svn] r57309 - in py/branch/event/py/test: . testing Message-ID: <20080816103637.0A3A316856F@codespeak.net> Author: hpk Date: Sat Aug 16 12:36:35 2008 New Revision: 57309 Modified: py/branch/event/py/test/pycollect.py py/branch/event/py/test/testing/test_collect.py Log: refine function equality, normalize its __init__ Modified: py/branch/event/py/test/pycollect.py ============================================================================== --- py/branch/event/py/test/pycollect.py (original) +++ py/branch/event/py/test/pycollect.py Sat Aug 16 12:36:35 2008 @@ -294,7 +294,7 @@ if not callable(call): raise TypeError("%r yielded non callable test %r" %(self.obj, call,)) name = "[%d]" % i - d[name] = self.Function(name, self, args, callobj=call) + d[name] = self.Function(name, self, args=args, callobj=call) return d def getcallargs(self, obj): @@ -312,8 +312,8 @@ """ a Function Item is responsible for setting up and executing a Python callable test object. """ - def __init__(self, name, parent, args=(), callobj=_dummy): - super(Function, self).__init__(name, parent) + def __init__(self, name, parent=None, config=None, args=(), callobj=_dummy): + super(Function, self).__init__(name, parent, config=config) self._args = args if callobj is not _dummy: self._obj = callobj @@ -322,6 +322,21 @@ """ execute the given test function. """ self.obj(*self._args) + def __eq__(self, other): + try: + return (self.name == other.name and + self._args == other._args and + self.parent == other.parent and + self.obj == other.obj) + except AttributeError: + pass + return False + def __ne__(self, other): + return not self == other + + def __repr__(self): + return ""%(self.name, id(self)) + class DoctestFile(FSCollector): def listdir(self): return [self.fspath.basename] Modified: py/branch/event/py/test/testing/test_collect.py ============================================================================== --- py/branch/event/py/test/testing/test_collect.py (original) +++ py/branch/event/py/test/testing/test_collect.py Sat Aug 16 12:36:35 2008 @@ -283,6 +283,29 @@ assert [1,2,3] != fn assert col != fn + +def test_function_equality(): + config = py.test.config._reparse([tmpdir]) + f1 = py.test.collect.Function(name="name", config=config, + args=(1,), callobj=isinstance) + f2 = py.test.collect.Function(name="name", config=config, + args=(1,), callobj=callable) + assert not f1 == f2 + assert f1 != f2 + f3 = py.test.collect.Function(name="name", config=config, + args=(1,2), callobj=callable) + assert not f3 == f2 + assert f3 != f2 + + assert not f3 == f1 + assert f3 != f1 + + f1_b = py.test.collect.Function(name="name", config=config, + args=(1,), callobj=isinstance) + assert f1 == f1_b + assert not f1 != f1_b + + class Testgenitems: def setup_class(cls): cls.classtemp = py.test.ensuretemp(cls.__name__) From hpk at codespeak.net Sat Aug 16 13:15:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 13:15:02 +0200 (CEST) Subject: [py-svn] r57310 - py/branch/event/py/test Message-ID: <20080816111502.672C4168505@codespeak.net> Author: hpk Date: Sat Aug 16 13:15:00 2008 New Revision: 57310 Modified: py/branch/event/py/test/collect.py Log: small improvement to default repr_metainfo Modified: py/branch/event/py/test/collect.py ============================================================================== --- py/branch/event/py/test/collect.py (original) +++ py/branch/event/py/test/collect.py Sat Aug 16 13:15:00 2008 @@ -69,8 +69,11 @@ params['fspath'] = getrelpath(basedir, self.fspath) if self.lineno is not None: params['lineno'] = self.lineno + 1 + if self.fspath and self.lineno and self.modpath: line = "%(fspath)s:%(lineno)s: %(modpath)s" + elif self.fspath and self.modpath: + line = "%(fspath)s: %(modpath)s" elif self.fspath and self.lineno: line = "%(fspath)s:%(lineno)s" else: From hpk at codespeak.net Sat Aug 16 13:58:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 13:58:36 +0200 (CEST) Subject: [py-svn] r57311 - in py/branch/event/py/test: . dsession report report/testing testing Message-ID: <20080816115836.63A22169DB6@codespeak.net> Author: hpk Date: Sat Aug 16 13:58:33 2008 New Revision: 57311 Modified: py/branch/event/py/test/dsession/dsession.py py/branch/event/py/test/event.py py/branch/event/py/test/report/terminal.py py/branch/event/py/test/report/testing/test_collectonly.py py/branch/event/py/test/testing/acceptance_test.py Log: extend ItemStart event: print info in --verbose on where items get distributed to. Modified: py/branch/event/py/test/dsession/dsession.py ============================================================================== --- py/branch/event/py/test/dsession/dsession.py (original) +++ py/branch/event/py/test/dsession/dsession.py Sat Aug 16 13:58:33 2008 @@ -196,8 +196,11 @@ sending = tosend[:room] host.node.sendlist(sending) for item in sending: - self.bus.notify(event.ItemStart(item)) + #assert item not in self.item2host, ( + # "sending same item %r to multiple hosts " + # "not implemented" %(item,)) self.item2host[item] = host + self.bus.notify(event.ItemStart(item, host)) pending.extend(sending) tosend[:] = tosend[room:] # update inplace if not tosend: Modified: py/branch/event/py/test/event.py ============================================================================== --- py/branch/event/py/test/event.py (original) +++ py/branch/event/py/test/event.py Sat Aug 16 13:58:33 2008 @@ -60,8 +60,9 @@ # ---------------------------------------------------------------------- class ItemStart(BaseEvent): - def __init__(self, item): + def __init__(self, item, host=None): self.item = item + self.host = host self.time = timestamp() class Deselected(BaseEvent): @@ -126,12 +127,6 @@ self.host = host self.error = error -class SendItem(BaseEvent): - def __init__(self, host, item): - self.item = item - self.host = host - - # # events related to rsyncing # Modified: py/branch/event/py/test/report/terminal.py ============================================================================== --- py/branch/event/py/test/report/terminal.py (original) +++ py/branch/event/py/test/report/terminal.py Sat Aug 16 13:58:33 2008 @@ -32,6 +32,7 @@ self._tw.write(prefix) if extra: self._tw.write(extra) + self.currentfspath = -2 def ensure_newline(self): if self.currentfspath: @@ -79,8 +80,10 @@ if self.config.option.verbose: info = ev.item.repr_metainfo() line = info.verboseline(basedir=self.curdir) + " " - # if on DSession we could display "sending" - self.write_ensure_prefix(line) + extra = "" + if ev.host: + extra = "-> " + ev.host.hostid + self.write_ensure_prefix(line, extra) def rep_RescheduleItems(self, ev): if self.config.option.debug: Modified: py/branch/event/py/test/report/testing/test_collectonly.py ============================================================================== --- py/branch/event/py/test/report/testing/test_collectonly.py (original) +++ py/branch/event/py/test/report/testing/test_collectonly.py Sat Aug 16 13:58:33 2008 @@ -20,7 +20,7 @@ item = modcol.join("test_func") rep.processevent(event.ItemStart(item)) s = popvalue(stringio) - assert s == " " + assert s.find("Function 'test_func'") != -1 rep.processevent(event.CollectionReport(modcol, [], passed="")) assert rep.indent == indent Modified: py/branch/event/py/test/testing/acceptance_test.py ============================================================================== --- py/branch/event/py/test/testing/acceptance_test.py (original) +++ py/branch/event/py/test/testing/acceptance_test.py Sat Aug 16 13:58:33 2008 @@ -70,10 +70,10 @@ assert result.ret == 0 extra = assert_lines_contain_lines(result.outlines, py.code.Source(""" - + - + """).strip()) def test_nested_import_error(self): From hpk at codespeak.net Sat Aug 16 15:46:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 15:46:35 +0200 (CEST) Subject: [py-svn] r57313 - py/branch/event/py/test Message-ID: <20080816134635.5D423169E2F@codespeak.net> Author: hpk Date: Sat Aug 16 15:46:33 2008 New Revision: 57313 Modified: py/branch/event/py/test/collect.py Log: better default reported metainfo Modified: py/branch/event/py/test/collect.py ============================================================================== --- py/branch/event/py/test/collect.py (original) +++ py/branch/event/py/test/collect.py Sat Aug 16 15:46:33 2008 @@ -383,8 +383,11 @@ return basic_run_report def repr_metainfo(self): - code = py.code.Code(self.execute) - return self.ReprMetaInfo(code.path, code.firstlineno) + try: + return self.ReprMetaInfo(self.fspath, modpath=self.__class__.__name__) + except AttributeError: + code = py.code.Code(self.execute) + return self.ReprMetaInfo(code.path, code.firstlineno) def execute(self): """ execute this test item.""" From hpk at codespeak.net Sat Aug 16 15:49:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 15:49:13 +0200 (CEST) Subject: [py-svn] r57314 - py/branch/event/py/test/testing Message-ID: <20080816134913.4C0A1169E2C@codespeak.net> Author: hpk Date: Sat Aug 16 15:49:12 2008 New Revision: 57314 Modified: py/branch/event/py/test/testing/test_collect.py Log: forgot to checkin test for repr_metainfo Modified: py/branch/event/py/test/testing/test_collect.py ============================================================================== --- py/branch/event/py/test/testing/test_collect.py (original) +++ py/branch/event/py/test/testing/test_collect.py Sat Aug 16 15:49:12 2008 @@ -511,10 +511,9 @@ Item = py.test.collect.Item item = Item("virtual", parent=modcol) info = item.repr_metainfo() - code = py.code.Code(Item.execute) - assert info.fspath == code.path - assert info.lineno == code.firstlineno - assert not info.modpath + assert info.fspath == modcol.fspath + assert not info.lineno + assert info.modpath == "Item" def test_repr_metainfo_func(self): item = self.getitem("def test_func(): pass") From hpk at codespeak.net Sat Aug 16 15:51:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 15:51:21 +0200 (CEST) Subject: [py-svn] r57315 - in py/branch/event/py/test: . dsession/testing testing Message-ID: <20080816135121.4A09E169E2C@codespeak.net> Author: hpk Date: Sat Aug 16 15:51:20 2008 New Revision: 57315 Modified: py/branch/event/py/test/config.py py/branch/event/py/test/dsession/testing/test_functional_dsession.py py/branch/event/py/test/testing/test_config.py Log: make sure that configs in distributed settings are seen as the global py.test.config singleton. Modified: py/branch/event/py/test/config.py ============================================================================== --- py/branch/event/py/test/config.py (original) +++ py/branch/event/py/test/config.py Sat Aug 16 15:51:20 2008 @@ -76,10 +76,16 @@ return l, self.option def _mergerepr(self, repr): + # before any conftests are loaded we + # need to set the per-process singleton + # (also seens py.test.config) to have consistent + # option handling + global config_per_process + config_per_process = self args, cmdlineopts = repr self.args = [self.topdir.join(x) for x in args] - self._conftest.setinitial(self.args) self.option = cmdlineopts + self._conftest.setinitial(self.args) def getfsnode(self, path): path = py.path.local(path) @@ -128,7 +134,8 @@ self._parser.add_option_group(optgroup) for opt in specs: if hasattr(opt, 'default') and opt.dest: - setattr(self.option, opt.dest, opt.default) + if not hasattr(self.option, opt.dest): + setattr(self.option, opt.dest, opt.default) return self.option def getvalue(self, name, path=None): Modified: py/branch/event/py/test/dsession/testing/test_functional_dsession.py ============================================================================== --- py/branch/event/py/test/dsession/testing/test_functional_dsession.py (original) +++ py/branch/event/py/test/dsession/testing/test_functional_dsession.py Sat Aug 16 15:51:20 2008 @@ -42,6 +42,33 @@ s = eventlog.read() assert s.find("TestrunStart") != -1 + def test_conftest_options(self): + self.makepyfile(conftest=""" + print "importing conftest" + import py + Option = py.test.config.Option + option = py.test.config.addoptions("someopt", + Option('', '--forcegen', action="store_true", dest="forcegen", default=False)) + """) + self.makepyfile(__init__="#") + p1 = self.makepyfile(test_one=""" + def test_1(): + import py, conftest + print "test_1: py.test.config.option.forcegen", py.test.config.option.forcegen + print "test_1: conftest", conftest + print "test_1: conftest.option.forcegen", conftest.option.forcegen + assert conftest.option.forcegen + """) + print p1 + config = self.parseconfig('-n1', p1, '--forcegen') + dsession = DSession(config) + readevent = eventreader(dsession) + dsession.main() + ev = readevent(event.ItemTestReport) + if not ev.passed: + print ev.outcome.longrepr + assert 0 + def test_dist_some_tests(self): self.makepyfile(conftest="dist_hosts=['localhost']\n") p1 = self.makepyfile(test_one=""" Modified: py/branch/event/py/test/testing/test_config.py ============================================================================== --- py/branch/event/py/test/testing/test_config.py (original) +++ py/branch/event/py/test/testing/test_config.py Sat Aug 16 15:51:20 2008 @@ -179,7 +179,11 @@ config = py.test.config._reparse([tmp.dirpath()]) py.test.raises(AttributeError, "config.option.gdest") config._mergerepr(repr) + option = config.addoptions("testing group", + config.Option('-G', '--glong', action="store", default=42, + type="int", dest="gdest", help="g value.")) assert config.option.gdest == 11 + assert option.gdest == 11 class TestSessionAndOptions: def setup_class(cls): From hpk at codespeak.net Sat Aug 16 16:42:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 16:42:24 +0200 (CEST) Subject: [py-svn] r57316 - py/branch/event/py/doc Message-ID: <20080816144224.7FE07169E28@codespeak.net> Author: hpk Date: Sat Aug 16 16:42:20 2008 New Revision: 57316 Modified: py/branch/event/py/doc/conftest.py py/branch/event/py/doc/future.txt py/branch/event/py/doc/path.txt py/branch/event/py/doc/why_py.txt Log: hacking conftest.py a bit and fixing a few links/urls after --checkremote Modified: py/branch/event/py/doc/conftest.py ============================================================================== --- py/branch/event/py/doc/conftest.py (original) +++ py/branch/event/py/doc/conftest.py Sat Aug 16 16:42:20 2008 @@ -8,11 +8,13 @@ mypath = py.magic.autopath().dirpath() +TIMEOUT_URLOPEN = 5.0 + Option = py.test.config.Option option = py.test.config.addoptions("documentation check options", Option('-R', '--checkremote', - action="store_true", dest="checkremote", default=False, - help="perform tests involving remote accesses (links, svn)" + action="store_true", dest="checkremote", default=False, + help="urlopen() remote links found in ReST text files.", ), Option('', '--forcegen', action="store_true", dest="forcegen", default=False, @@ -114,17 +116,26 @@ class DoctestText(py.test.collect.Item): def execute(self): - # XXX quite nasty... but it works (fixes win32 issues) s = self._normalize_linesep() l = [] prefix = '.. >>> ' mod = py.std.types.ModuleType(self.fspath.purebasename) + skipchunk = False for line in deindent(s).split('\n'): stripped = line.strip() + if skipchunk and line.startswith(skipchunk): + print "skipping", line + continue + skipchunk = False if stripped.startswith(prefix): - exec py.code.Source(stripped[len(prefix):]).compile() in \ - mod.__dict__ - line = "" + try: + exec py.code.Source(stripped[len(prefix):]).compile() in \ + mod.__dict__ + except ValueError, e: + if e.args and e.args[0] == "skipchunk": + skipchunk = " " * (len(line) - len(line.lstrip())) + else: + raise else: l.append(line) docstring = "\n".join(l) @@ -135,6 +146,7 @@ self.fspath, failed, tot)) def _normalize_linesep(self): + # XXX quite nasty... but it works (fixes win32 issues) s = self.fspath.read() linesep = '\n' if '\r' in s: @@ -149,15 +161,21 @@ def listdir(self): l = [] for call, tryfn, path, lineno in genlinkchecks(self.fspath): - l.append(tryfn) + l.append("%s:%d" %(tryfn, lineno)) return l def join(self, name): + i = name.rfind(':') + assert i != -1 + jname, jlineno = name[:i], int(name[i+1:]) for call, tryfn, path, lineno in genlinkchecks(self.fspath): - if tryfn == name: + if tryfn == jname and lineno == jlineno: return CheckLink(name, parent=self, args=(tryfn, path, lineno), callobj=call) class CheckLink(py.test.collect.Function): + def repr_metainfo(self): + return self.ReprMetaInfo(fspath=self.fspath, lineno=self._args[2], + modpath="checklink: %s" % (self._args[0],)) def setup(self): pass def teardown(self): @@ -212,9 +230,14 @@ yield localrefcheck, tryfn, path, lineno def urlcheck(tryfn, path, lineno): - try: - print "trying remote", tryfn - py.std.urllib2.urlopen(tryfn) + old = py.std.socket.getdefaulttimeout() + py.std.socket.setdefaulttimeout(TIMEOUT_URLOPEN) + try: + try: + print "trying remote", tryfn + py.std.urllib2.urlopen(tryfn) + finally: + py.std.socket.setdefaulttimeout(old) except (py.std.urllib2.URLError, py.std.urllib2.HTTPError), e: if e.code in (401, 403): # authorization required, forbidden py.test.skip("%s: %s" %(tryfn, str(e))) Modified: py/branch/event/py/doc/future.txt ============================================================================== --- py/branch/event/py/doc/future.txt (original) +++ py/branch/event/py/doc/future.txt Sat Aug 16 16:42:20 2008 @@ -74,8 +74,6 @@ a certain test broke, or when its performance decreased considerably. -.. _`CPython's distutils`: http://www.python.org/dev/doc/devel/lib/module-distutils.html - .. _`restructured text`: http://docutils.sourceforge.net/docs/user/rst/quickref.html .. _`python standard library`: http://www.python.org/doc/2.3.4/lib/lib.html .. _`xpython EuroPython 2004 talk`: http://codespeak.net/svn/user/hpk/talks/xpython-talk.txt Modified: py/branch/event/py/doc/path.txt ============================================================================== --- py/branch/event/py/doc/path.txt (original) +++ py/branch/event/py/doc/path.txt Sat Aug 16 16:42:20 2008 @@ -52,7 +52,7 @@ Some example usage of :api:`py.path.svnurl`:: .. >>> import py - .. >>> if not getattr(py.test.config.option, 'checkremote', 0): py.test.skip("use --checkremote to enable svn remote doctests") + .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') >>> url = py.path.svnurl('http://codespeak.net/svn/py') >>> info = url.info() >>> info.kind @@ -64,6 +64,7 @@ Example usage of :api:`py.path.svnwc`:: + .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') >>> temp = py.test.ensuretemp('py.path_documentation') >>> wc = py.path.svnwc(temp.join('svnwc')) >>> wc.checkout('http://codespeak.net/svn/py/dist/py/path/local') @@ -112,6 +113,7 @@ >>> for fpath in dirpath.visit('*.txt'): ... if 'bar' in fpath.read(): ... results.append(fpath.basename) + >>> results.sort() >>> results ['textfile1.txt', 'textfile2.txt', 'textfile2.txt'] @@ -176,6 +178,7 @@ As an example of 'uncommon' methods, we'll show how to read and write properties in an :api:`py.path.svnwc` instance:: + .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') >>> wc.propget('foo') '' >>> wc.propset('foo', 'bar') @@ -193,6 +196,7 @@ Some uncommon functionality can also be provided as extensions, such as SVN authentication:: + .. >>> if not py.test.config.option.checkremote: raise ValueError('skipchunk') >>> auth = py.path.SvnAuth('anonymous', 'user', cache_auth=False, ... interactive=False) >>> wc.auth = auth Modified: py/branch/event/py/doc/why_py.txt ============================================================================== --- py/branch/event/py/doc/why_py.txt (original) +++ py/branch/event/py/doc/why_py.txt Sat Aug 16 16:42:20 2008 @@ -114,13 +114,7 @@ Another focus are well tested so called *path* implementations that allow you to seemlessly work with different backends, currently a local filesystem, subversion working copies and -subversion remote URLs. The `jorendorff path.py`_ implementation -goes somewhat in the same direction but doesn't try to -systematically access local and remote file systems as well as -other hierarchic namespaces. The latter is the focus of the -``py.path`` API. - -.. _`jorendorff path.py`: http://www.jorendorff.com/articles/python/path/ +subversion remote URLs. How does py development work? ============================= From hpk at codespeak.net Sat Aug 16 16:53:07 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 16:53:07 +0200 (CEST) Subject: [py-svn] r57317 - py/branch/event/py/code/testing Message-ID: <20080816145307.DD10D169E28@codespeak.net> Author: hpk Date: Sat Aug 16 16:53:05 2008 New Revision: 57317 Modified: py/branch/event/py/code/testing/test_source.py Log: port 56618 and 56619 from trunk Modified: py/branch/event/py/code/testing/test_source.py ============================================================================== --- py/branch/event/py/code/testing/test_source.py (original) +++ py/branch/event/py/code/testing/test_source.py Sat Aug 16 16:53:05 2008 @@ -300,14 +300,17 @@ lines = deindent(source.splitlines()) assert lines == ['', 'def f():', ' def g():', ' pass', ' '] -def test_write_read(): - py.test.skip("Failing") +def test_source_of_class_at_eof_without_newline(): + py.test.skip("CPython's inspect.getsource is buggy") + # this test fails because the implicit inspect.getsource(A) below + # does not return the "x = 1" last line. tmpdir = py.test.ensuretemp("source_write_read") source = py.code.Source(''' - class A(object): - def method(self): - x = 1 + class A(object): + def method(self): + x = 1 ''') - tmpdir.ensure("a.py").write(source) + path = tmpdir.join("a.py") + path.write(source) s2 = py.code.Source(tmpdir.join("a.py").pyimport().A) - assert source == s2 + assert str(source).strip() == str(s2).strip() From hpk at codespeak.net Sat Aug 16 17:02:56 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:02:56 +0200 (CEST) Subject: [py-svn] r57318 - py/branch/event/py/doc Message-ID: <20080816150256.C63CE168405@codespeak.net> Author: hpk Date: Sat Aug 16 17:02:55 2008 New Revision: 57318 Added: py/branch/event/py/doc/TODO.txt - copied unchanged from r57317, py/trunk/py/doc/TODO.txt Log: get trunk's TODO file into branch. From hpk at codespeak.net Sat Aug 16 17:08:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:08:26 +0200 (CEST) Subject: [py-svn] r57319 - py/branch/event/py/misc Message-ID: <20080816150826.4389A169E24@codespeak.net> Author: hpk Date: Sat Aug 16 17:08:24 2008 New Revision: 57319 Modified: py/branch/event/py/misc/_dist.py Log: merge 56743 and 56749 from trunk(ghum's fixes to PATH handling on windows) Modified: py/branch/event/py/misc/_dist.py ============================================================================== --- py/branch/event/py/misc/_dist.py (original) +++ py/branch/event/py/misc/_dist.py Sat Aug 16 17:08:24 2008 @@ -97,12 +97,48 @@ # Add py/bin to PATH environment variable bindir = os.path.join(sysconfig.get_python_lib(), "py", "bin", "win32") + + # check for the user path + ureg = _winreg.ConnectRegistry(None, _winreg.HKEY_CURRENT_USER) + ukey = r"Environment" + + # not every user has his own path on windows + try: + upath = get_registry_value(ureg, ukey, "PATH") + except WindowsError: + upath="" + # if bindir allready in userpath -> do nothing + if bindir in upath: + return + reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" path = get_registry_value(reg, key, "Path") + # if bindir allready in systempath -> do nothing + if bindir in path: + return path += ";" + bindir print "Setting PATH to:", path - set_registry_value(reg, key, "Path", path) + + pathset=False + try: + set_registry_value(reg, key, "PATH", path) + pathset=True + except WindowsError: + print "cannot set systempath, falling back to userpath" + pass + + if not pathset: + try: + if len(upath)>0: #if no user path present + upath += ";" + upath+=bindir + set_registry_value(ureg, ukey, "Path", upath) + pathset=True + except WindowsError: + print "cannot set userpath, please add %s to your path" % (bindir,) + return + #print "Current PATH is:", get_registry_value(reg, key, "Path") # Propagate changes throughout the system From hpk at codespeak.net Sat Aug 16 17:10:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:10:18 +0200 (CEST) Subject: [py-svn] r57320 - in py/trunk/py: code doc io misc test Message-ID: <20080816151018.709F2169E24@codespeak.net> Author: hpk Date: Sat Aug 16 17:10:17 2008 New Revision: 57320 Removed: py/trunk/py/code/ py/trunk/py/doc/ py/trunk/py/io/ py/trunk/py/misc/ py/trunk/py/test/ Log: remove test, misc, doc, io, and code directories that are to come from the event branch with the next commit. From hpk at codespeak.net Sat Aug 16 17:27:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:27:03 +0200 (CEST) Subject: [py-svn] r57321 - in py/trunk/py: . bin c-extension code code/testing compat doc green io io/testing misc misc/testing test test/dsession test/dsession/testing test/report test/report/testing test/testing Message-ID: <20080816152703.68FF3169E23@codespeak.net> Author: hpk Date: Sat Aug 16 17:26:59 2008 New Revision: 57321 Added: py/trunk/py/c-extension/__init__.py py/trunk/py/code/ - copied from r57305, py/branch/event/py/code/ py/trunk/py/code/testing/test_excinfo.py - copied unchanged from r57306, py/branch/event/py/code/testing/test_excinfo.py py/trunk/py/code/testing/test_source.py - copied unchanged from r57317, py/branch/event/py/code/testing/test_source.py py/trunk/py/doc/ - copied from r57305, py/branch/event/py/doc/ py/trunk/py/doc/code.txt - copied unchanged from r57306, py/branch/event/py/doc/code.txt py/trunk/py/doc/conftest.py - copied unchanged from r57316, py/branch/event/py/doc/conftest.py py/trunk/py/doc/future.txt - copied unchanged from r57316, py/branch/event/py/doc/future.txt py/trunk/py/doc/path.txt - copied unchanged from r57316, py/branch/event/py/doc/path.txt py/trunk/py/doc/test_conftest.py - copied unchanged from r57306, py/branch/event/py/doc/test_conftest.py py/trunk/py/doc/why_py.txt - copied unchanged from r57316, py/branch/event/py/doc/why_py.txt py/trunk/py/io/ - copied from r57305, py/branch/event/py/io/ py/trunk/py/io/forkedfunc.py - copied unchanged from r57306, py/branch/event/py/io/forkedfunc.py py/trunk/py/io/testing/test_forkedfunc.py - copied unchanged from r57306, py/branch/event/py/io/testing/test_forkedfunc.py py/trunk/py/io/testing/test_terminalwriter.py - copied unchanged from r57306, py/branch/event/py/io/testing/test_terminalwriter.py py/trunk/py/misc/ - copied from r57305, py/branch/event/py/misc/ py/trunk/py/misc/_dist.py - copied unchanged from r57319, py/branch/event/py/misc/_dist.py py/trunk/py/misc/conftest-socketgatewayrun.py - copied unchanged from r57306, py/branch/event/py/misc/conftest-socketgatewayrun.py py/trunk/py/misc/testing/test_initpkg.py - copied unchanged from r57306, py/branch/event/py/misc/testing/test_initpkg.py py/trunk/py/test/ - copied from r57306, py/branch/event/py/test/ py/trunk/py/test/collect.py - copied unchanged from r57313, py/branch/event/py/test/collect.py py/trunk/py/test/config.py - copied unchanged from r57315, py/branch/event/py/test/config.py py/trunk/py/test/dsession/dsession.py - copied unchanged from r57311, py/branch/event/py/test/dsession/dsession.py py/trunk/py/test/dsession/testing/test_functional_dsession.py - copied unchanged from r57315, py/branch/event/py/test/dsession/testing/test_functional_dsession.py py/trunk/py/test/event.py - copied unchanged from r57311, py/branch/event/py/test/event.py py/trunk/py/test/pycollect.py - copied unchanged from r57309, py/branch/event/py/test/pycollect.py py/trunk/py/test/report/terminal.py - copied unchanged from r57311, py/branch/event/py/test/report/terminal.py py/trunk/py/test/report/testing/test_collectonly.py - copied unchanged from r57311, py/branch/event/py/test/report/testing/test_collectonly.py py/trunk/py/test/testing/acceptance_test.py - copied unchanged from r57311, py/branch/event/py/test/testing/acceptance_test.py py/trunk/py/test/testing/test_collect.py - copied unchanged from r57314, py/branch/event/py/test/testing/test_collect.py py/trunk/py/test/testing/test_config.py - copied unchanged from r57315, py/branch/event/py/test/testing/test_config.py Removed: py/trunk/py/misc/_maketest2.py Modified: py/trunk/py/__init__.py py/trunk/py/bin/_findpy.py py/trunk/py/compat/conftest.py py/trunk/py/green/conftest.py Log: merging the event branch: * moving in test, misc, code, io directories and py/__init__.py * py/bin/_find.py does not print to stderr anymore * a few fixes to conftest files in other dirs some more fixes and adjustments pending Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Sat Aug 16 17:26:59 2008 @@ -26,11 +26,11 @@ exportdefs = { # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), - 'test.raises' : ('./test/raises.py', 'raises'), - 'test.deprecated_call' : ('./test/deprecate.py', 'deprecated_call'), - 'test.skip' : ('./test/item.py', 'skip'), - 'test.fail' : ('./test/item.py', 'fail'), - 'test.exit' : ('./test/session.py', 'exit'), + 'test.raises' : ('./test/outcome.py', 'raises'), + 'test.deprecated_call' : ('./test/outcome.py', 'deprecated_call'), + 'test.skip' : ('./test/outcome.py', 'skip'), + 'test.fail' : ('./test/outcome.py', 'fail'), + 'test.exit' : ('./test/outcome.py', 'exit'), 'test.pdb' : ('./test/custompdb.py', 'set_trace'), # configuration/initialization related test api @@ -41,13 +41,13 @@ # for customization of collecting/running tests 'test.collect.Collector' : ('./test/collect.py', 'Collector'), 'test.collect.Directory' : ('./test/collect.py', 'Directory'), - 'test.collect.Module' : ('./test/collect.py', 'Module'), - 'test.collect.DoctestFile' : ('./test/collect.py', 'DoctestFile'), - 'test.collect.Class' : ('./test/collect.py', 'Class'), - 'test.collect.Instance' : ('./test/collect.py', 'Instance'), - 'test.collect.Generator' : ('./test/collect.py', 'Generator'), - 'test.collect.Item' : ('./test/item.py', 'Item'), - 'test.collect.Function' : ('./test/item.py', 'Function'), + 'test.collect.Module' : ('./test/pycollect.py', 'Module'), + 'test.collect.DoctestFile' : ('./test/pycollect.py', 'DoctestFile'), + 'test.collect.Class' : ('./test/pycollect.py', 'Class'), + 'test.collect.Instance' : ('./test/pycollect.py', 'Instance'), + 'test.collect.Generator' : ('./test/pycollect.py', 'Generator'), + 'test.collect.Item' : ('./test/collect.py', 'Item'), + 'test.collect.Function' : ('./test/pycollect.py', 'Function'), # thread related API (still in early design phase) '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), @@ -93,6 +93,7 @@ 'builtin.sorted' : ('./builtin/sorted.py', 'sorted'), 'builtin.BaseException' : ('./builtin/exception.py', 'BaseException'), 'builtin.GeneratorExit' : ('./builtin/exception.py', 'GeneratorExit'), + 'builtin.sysex' : ('./builtin/exception.py', 'sysex'), 'builtin.set' : ('./builtin/set.py', 'set'), 'builtin.frozenset' : ('./builtin/set.py', 'frozenset'), @@ -111,6 +112,8 @@ 'io.FDCapture' : ('./io/fdcapture.py', 'FDCapture'), 'io.StdCapture' : ('./io/stdcapture.py', 'StdCapture'), 'io.StdCaptureFD' : ('./io/stdcapture.py', 'StdCaptureFD'), + 'io.TerminalWriter' : ('./io/terminalwriter.py', 'TerminalWriter'), + 'io.ForkedFunc' : ('./io/forkedfunc.py', 'ForkedFunc'), # error module, defining all errno's as Classes 'error' : ('./misc/error.py', 'error'), Modified: py/trunk/py/bin/_findpy.py ============================================================================== --- py/trunk/py/bin/_findpy.py (original) +++ py/trunk/py/bin/_findpy.py Sat Aug 16 17:26:59 2008 @@ -19,7 +19,7 @@ # if p == current: # return True if current != sys.path[0]: # if we are already first, then ok - print >>sys.stderr, "inserting into sys.path:", current + #print >>sys.stderr, "inserting into sys.path:", current sys.path.insert(0, current) return True current = opd(current) Added: py/trunk/py/c-extension/__init__.py ============================================================================== --- (empty file) +++ py/trunk/py/c-extension/__init__.py Sat Aug 16 17:26:59 2008 @@ -0,0 +1 @@ +# Modified: py/trunk/py/compat/conftest.py ============================================================================== --- py/trunk/py/compat/conftest.py (original) +++ py/trunk/py/compat/conftest.py Sat Aug 16 17:26:59 2008 @@ -1,5 +1,5 @@ import py class Directory(py.test.collect.Directory): - def run(self): + def listdir(self): py.test.skip("compat tests currently need to be run manually") Modified: py/trunk/py/green/conftest.py ============================================================================== --- py/trunk/py/green/conftest.py (original) +++ py/trunk/py/green/conftest.py Sat Aug 16 17:26:59 2008 @@ -1,8 +1,8 @@ import py, os class Directory(py.test.collect.Directory): - def run(self): + def listdir(self): if os.name == 'nt': py.test.skip("Cannot test green layer on windows") else: - return super(Directory, self).run() + return super(Directory, self).listdir() Deleted: /py/branch/event/py/misc/_maketest2.py ============================================================================== --- /py/branch/event/py/misc/_maketest2.py Sat Aug 16 17:26:59 2008 +++ (empty file) @@ -1,53 +0,0 @@ -""" create a py/test2 hierarchy copied from py/test. - useful for refactoring py.test itself and still - use py.test itself. -""" - -from _findpy import py - -def change_init(initfile): - l = [] - for line in initfile.readlines(): - newline = line - l.append(line) - newline = newline.replace("'test.", "'test2.") - newline = newline.replace("'./test/", "'./test2/") - if newline != line: - l.append(newline) - initfile.write("".join(l)) - -def perform_replace(directory): - for x in directory.visit("*.py", - rec=lambda x: x.check(dir=1, dotfile=0)): - s = n = x.read() - n = n.replace("py.test", "py.test2") - n = n.replace("py.__.test.", "py.__.test2.") - n = n.replace("py.__.test ", "py.__.test2 ") - if s != n: - print "writing modified", x - x.write(n) - -def cmd(command): - print "* executing:", command - return py.process.cmdexec(command) - -if __name__ == '__main__': - basedir = py.path.local(py.__file__).dirpath() - #st = py.path.svnwc(basedir).status() - #assert not st.modified - olddir = basedir.chdir() - try: - initfile = basedir.join("__init__.py") - cmd("svn revert %s" % initfile) - change_init(initfile) - - test2dir = basedir.join("test2") - cmd("svn revert -R test2") - cmd("rm -rf test2") - cmd("svn cp test test2") - perform_replace(test2dir) - - finally: - olddir.chdir() - - From hpk at codespeak.net Sat Aug 16 17:29:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:29:36 +0200 (CEST) Subject: [py-svn] r57322 - py/trunk/py/rest/testing Message-ID: <20080816152936.782ED169E23@codespeak.net> Author: hpk Date: Sat Aug 16 17:29:35 2008 New Revision: 57322 Modified: py/trunk/py/rest/testing/test_convert.py py/trunk/py/rest/testing/test_directive.py Log: port 57306 from branch: check for actually needed binaries Modified: py/trunk/py/rest/testing/test_convert.py ============================================================================== --- py/trunk/py/rest/testing/test_convert.py (original) +++ py/trunk/py/rest/testing/test_convert.py Sat Aug 16 17:29:35 2008 @@ -4,10 +4,10 @@ datadir = py.magic.autopath().dirpath().join("data") def setup_module(mod): - 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") + required = 'gs', 'dot', 'latex', 'epstopdf', + for exe in required: + if not py.path.local.sysfind(exe): + py.test.skip("%r not found, required: %r" %(exe, required)) def test_convert_dot(): # XXX not really clear that the result is valid pdf/eps Modified: py/trunk/py/rest/testing/test_directive.py ============================================================================== --- py/trunk/py/rest/testing/test_directive.py (original) +++ py/trunk/py/rest/testing/test_directive.py Sat Aug 16 17:29:35 2008 @@ -30,8 +30,9 @@ png.remove() def _graphviz_pdf(self): - if not py.path.local.sysfind("dot") or not py.path.local.sysfind("latex"): - py.test.skip("graphviz and latex needed") + for exe in 'dot latex epstopdf'.split(): + if not py.path.local.sysfind(exe): + py.test.skip("%r needed" %(exe,)) directive.set_backend_and_register_directives("latex") txt = py.path.local(datadir.join("graphviz.txt")) From hpk at codespeak.net Sat Aug 16 17:38:54 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:38:54 +0200 (CEST) Subject: [py-svn] r57323 - py/trunk/py/path/local Message-ID: <20080816153854.030B3168563@codespeak.net> Author: hpk Date: Sat Aug 16 17:38:54 2008 New Revision: 57323 Modified: py/trunk/py/path/local/local.py Log: port 57174 from branch: ignore errors when removing dirs in teardowns Modified: py/trunk/py/path/local/local.py ============================================================================== --- py/trunk/py/path/local/local.py (original) +++ py/trunk/py/path/local/local.py Sat Aug 16 17:38:54 2008 @@ -657,7 +657,9 @@ pass # assume that it means that there is no 'lf' try: path.remove(rec=1) - except py.error.Error: + except KeyboardInterrupt: + raise + except: # this might be py.error.Error, WindowsError ... pass # make link... From hpk at codespeak.net Sat Aug 16 17:47:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:47:28 +0200 (CEST) Subject: [py-svn] r57324 - py/trunk/py Message-ID: <20080816154728.3A4EA169E24@codespeak.net> Author: hpk Date: Sat Aug 16 17:47:26 2008 New Revision: 57324 Modified: py/trunk/py/__init__.py Log: remove superflous helper attribute Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Sat Aug 16 17:47:26 2008 @@ -93,7 +93,6 @@ 'builtin.sorted' : ('./builtin/sorted.py', 'sorted'), 'builtin.BaseException' : ('./builtin/exception.py', 'BaseException'), 'builtin.GeneratorExit' : ('./builtin/exception.py', 'GeneratorExit'), - 'builtin.sysex' : ('./builtin/exception.py', 'sysex'), 'builtin.set' : ('./builtin/set.py', 'set'), 'builtin.frozenset' : ('./builtin/set.py', 'frozenset'), From hpk at codespeak.net Sat Aug 16 17:47:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 17:47:45 +0200 (CEST) Subject: [py-svn] r57325 - py/trunk/py/apigen/testing Message-ID: <20080816154745.2B933169E24@codespeak.net> Author: hpk Date: Sat Aug 16 17:47:44 2008 New Revision: 57325 Modified: py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_linker.py Log: for noew skip apigen tests that fail Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Sat Aug 16 17:47:44 2008 @@ -11,6 +11,8 @@ from py.__.apigen.conftest import option from py.__.path.svn.testing.svntestbase import make_test_repo +py.test.skip("apigen needs work on py.test to work again") + def run_string_sequence_test(data, seq): currpos = -1 for s in seq: Modified: py/trunk/py/apigen/testing/test_linker.py ============================================================================== --- py/trunk/py/apigen/testing/test_linker.py (original) +++ py/trunk/py/apigen/testing/test_linker.py Sat Aug 16 17:47:44 2008 @@ -48,6 +48,7 @@ assert bar.read() == 'baz' def test_with_anchor(self): + py.test.skip("get_lazyhref needs fixing?") linker = TempLinker() temphref = linker.get_lazyhref('py.path.local', 'LocalPath.join') linker.set_link('py.path.local', 'py/path/local.html') From hpk at codespeak.net Sat Aug 16 19:49:27 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 19:49:27 +0200 (CEST) Subject: [py-svn] r57330 - py/branch/event/py/misc Message-ID: <20080816174927.EE9481683FD@codespeak.net> Author: hpk Date: Sat Aug 16 19:49:24 2008 New Revision: 57330 Modified: py/branch/event/py/misc/terminal_helper.py Log: deprecate this module, import some names from the new location Modified: py/branch/event/py/misc/terminal_helper.py ============================================================================== --- py/branch/event/py/misc/terminal_helper.py (original) +++ py/branch/event/py/misc/terminal_helper.py Sat Aug 16 19:49:24 2008 @@ -1,132 +1,14 @@ """ -Helper functions for writing to terminals and files. +(DEPRECATED) use py.io.TerminalWriter """ - import sys, os import py -def get_terminal_width(): - try: - import termios,fcntl,struct - call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8) - height,width = struct.unpack( "hhhh", call ) [:2] - terminal_width = width - except (SystemExit, KeyboardInterrupt), e: - raise - except: - # FALLBACK - terminal_width = int(os.environ.get('COLUMNS', 80))-1 - return terminal_width - -terminal_width = get_terminal_width() - -def ansi_print(text, esc, file=None, newline=True, flush=False): - if file is None: - file = sys.stderr - text = text.rstrip() - if esc and sys.platform != "win32" and file.isatty(): - if not isinstance(esc, tuple): - esc = (esc,) - text = (''.join(['\x1b[%sm' % cod for cod in esc]) + - text + - '\x1b[0m') # ANSI color code "reset" - if newline: - text += '\n' - file.write(text) - if flush: - file.flush() - -class Out(object): - tty = False - def __init__(self, file): - self.file = py.io.dupfile(file) - self.fullwidth = get_terminal_width() - - def sep(self, sepchar, title=None, fullwidth=None): - if not fullwidth: - fullwidth = self.fullwidth - # the goal is to have the line be as long as possible - # under the condition that len(line) <= fullwidth - if title is not None: - # we want 2 + 2*len(fill) + len(title) <= fullwidth - # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth - # 2*len(sepchar)*N <= fullwidth - len(title) - 2 - # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) - N = (fullwidth - len(title) - 2) // (2*len(sepchar)) - fill = sepchar * N - line = "%s %s %s" % (fill, title, fill) - else: - # we want len(sepchar)*N <= fullwidth - # i.e. N <= fullwidth // len(sepchar) - line = sepchar * (fullwidth // len(sepchar)) - # in some situations there is room for an extra sepchar at the right, - # in particular if we consider that with a sepchar like "_ " the - # trailing space is not important at the end of the line - if len(line) + len(sepchar.rstrip()) <= fullwidth: - line += sepchar.rstrip() - self.line(line) - -class TerminalOut(Out): - tty = True - def __init__(self, file): - super(TerminalOut, self).__init__(file) - - def sep(self, sepchar, title=None): - super(TerminalOut, self).sep(sepchar, title, - self.terminal_width) - - def write(self, s): - self.file.write(str(s)) - self.file.flush() - - def line(self, s=''): - if s: - self.file.write(s + '\n') - else: - self.file.write('\n') - self.file.flush() - - def xxxrewrite(self, s=''): - #self.write('\x1b[u%s' % s) - this escape sequence does - # strange things, or nothing at all, on various terminals. - # XXX what is it supposed to do in the first place?? - self.write(s) - -class FileOut(Out): - def write(self, s): - self.file.write(str(s)) - self.file.flush() - - def line(self, s=''): - if s: - self.file.write(str(s) + '\n') - else: - self.file.write('\n') - self.file.flush() - - def xxxrewrite(self, s=''): - self.write(s) - -def getout(file): - # XXX investigate further into terminal output, this is not enough - # - if file is None: - file = py.std.sys.stdout - elif hasattr(file, 'send'): - file = WriteFile(file.send) - elif callable(file): - file = WriteFile(file) - if hasattr(file, 'isatty') and file.isatty(): - return TerminalOut(file) - else: - return FileOut(file) - -class WriteFile(object): - def __init__(self, writemethod): - self.write = writemethod - def flush(self): - return +py.std.warnings.warn("py.__.misc.terminal_helper is deprecated, use py.io.TerminalWriter", + DeprecationWarning, stacklevel=2) + +from py.__.io.terminalwriter import get_terminal_width, terminal_width, ansi_print From hpk at codespeak.net Sat Aug 16 19:51:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 19:51:21 +0200 (CEST) Subject: [py-svn] r57331 - py/trunk/py/misc Message-ID: <20080816175121.BB95F169DFC@codespeak.net> Author: hpk Date: Sat Aug 16 19:51:21 2008 New Revision: 57331 Modified: py/trunk/py/misc/terminal_helper.py Log: port 57330 (which accidentally went to the branch) Modified: py/trunk/py/misc/terminal_helper.py ============================================================================== --- py/trunk/py/misc/terminal_helper.py (original) +++ py/trunk/py/misc/terminal_helper.py Sat Aug 16 19:51:21 2008 @@ -1,132 +1,14 @@ """ -Helper functions for writing to terminals and files. +(DEPRECATED) use py.io.TerminalWriter """ - import sys, os import py -def get_terminal_width(): - try: - import termios,fcntl,struct - call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8) - height,width = struct.unpack( "hhhh", call ) [:2] - terminal_width = width - except (SystemExit, KeyboardInterrupt), e: - raise - except: - # FALLBACK - terminal_width = int(os.environ.get('COLUMNS', 80))-1 - return terminal_width - -terminal_width = get_terminal_width() - -def ansi_print(text, esc, file=None, newline=True, flush=False): - if file is None: - file = sys.stderr - text = text.rstrip() - if esc and sys.platform != "win32" and file.isatty(): - if not isinstance(esc, tuple): - esc = (esc,) - text = (''.join(['\x1b[%sm' % cod for cod in esc]) + - text + - '\x1b[0m') # ANSI color code "reset" - if newline: - text += '\n' - file.write(text) - if flush: - file.flush() - -class Out(object): - tty = False - def __init__(self, file): - self.file = py.io.dupfile(file) - self.fullwidth = get_terminal_width() - - def sep(self, sepchar, title=None, fullwidth=None): - if not fullwidth: - fullwidth = self.fullwidth - # the goal is to have the line be as long as possible - # under the condition that len(line) <= fullwidth - if title is not None: - # we want 2 + 2*len(fill) + len(title) <= fullwidth - # i.e. 2 + 2*len(sepchar)*N + len(title) <= fullwidth - # 2*len(sepchar)*N <= fullwidth - len(title) - 2 - # N <= (fullwidth - len(title) - 2) // (2*len(sepchar)) - N = (fullwidth - len(title) - 2) // (2*len(sepchar)) - fill = sepchar * N - line = "%s %s %s" % (fill, title, fill) - else: - # we want len(sepchar)*N <= fullwidth - # i.e. N <= fullwidth // len(sepchar) - line = sepchar * (fullwidth // len(sepchar)) - # in some situations there is room for an extra sepchar at the right, - # in particular if we consider that with a sepchar like "_ " the - # trailing space is not important at the end of the line - if len(line) + len(sepchar.rstrip()) <= fullwidth: - line += sepchar.rstrip() - self.line(line) - -class TerminalOut(Out): - tty = True - def __init__(self, file): - super(TerminalOut, self).__init__(file) - - def sep(self, sepchar, title=None): - super(TerminalOut, self).sep(sepchar, title, - self.terminal_width) - - def write(self, s): - self.file.write(str(s)) - self.file.flush() - - def line(self, s=''): - if s: - self.file.write(s + '\n') - else: - self.file.write('\n') - self.file.flush() - - def xxxrewrite(self, s=''): - #self.write('\x1b[u%s' % s) - this escape sequence does - # strange things, or nothing at all, on various terminals. - # XXX what is it supposed to do in the first place?? - self.write(s) - -class FileOut(Out): - def write(self, s): - self.file.write(str(s)) - self.file.flush() - - def line(self, s=''): - if s: - self.file.write(str(s) + '\n') - else: - self.file.write('\n') - self.file.flush() - - def xxxrewrite(self, s=''): - self.write(s) - -def getout(file): - # XXX investigate further into terminal output, this is not enough - # - if file is None: - file = py.std.sys.stdout - elif hasattr(file, 'send'): - file = WriteFile(file.send) - elif callable(file): - file = WriteFile(file) - if hasattr(file, 'isatty') and file.isatty(): - return TerminalOut(file) - else: - return FileOut(file) - -class WriteFile(object): - def __init__(self, writemethod): - self.write = writemethod - def flush(self): - return +py.std.warnings.warn("py.__.misc.terminal_helper is deprecated, use py.io.TerminalWriter", + DeprecationWarning, stacklevel=2) + +from py.__.io.terminalwriter import get_terminal_width, terminal_width, ansi_print From hpk at codespeak.net Sat Aug 16 19:53:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 19:53:19 +0200 (CEST) Subject: [py-svn] r57332 - py/branch/event Message-ID: <20080816175319.E798C169E77@codespeak.net> Author: hpk Date: Sat Aug 16 19:53:19 2008 New Revision: 57332 Removed: py/branch/event/ Log: remove event branch From hpk at codespeak.net Sat Aug 16 20:21:14 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 16 Aug 2008 20:21:14 +0200 (CEST) Subject: [py-svn] r57333 - py/branch/guido-svnwc-xml-status/py/path/svn Message-ID: <20080816182114.9963A169E80@codespeak.net> Author: hpk Date: Sat Aug 16 20:21:13 2008 New Revision: 57333 Modified: py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Log: python2.3 compatibility Modified: py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/wccommand.py Sat Aug 16 20:21:13 2008 @@ -538,7 +538,6 @@ # seem to be a more solid approach :( _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)') - @staticmethod def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): """ return a new WCStatus object from data 's' """ @@ -632,10 +631,9 @@ rootstatus.update_rev = update_rev continue return rootstatus - + fromstring = staticmethod(fromstring) class XMLWCStatus(WCStatus): - @staticmethod def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): """ parse 'data' (XML string as outputted by svn st) into a status obj """ @@ -732,6 +730,7 @@ rootstatus.locked.append(wcpath) return rootstatus + fromstring = staticmethod(fromstring) class InfoSvnWCCommand: def __init__(self, output): From hpk at codespeak.net Sun Aug 17 10:24:59 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 10:24:59 +0200 (CEST) Subject: [py-svn] r57340 - in py/release/0.9.x: . py Message-ID: <20080817082459.7C59C16856F@codespeak.net> Author: hpk Date: Sun Aug 17 10:24:57 2008 New Revision: 57340 Added: py/release/0.9.x/LICENSE (contents, props changed) Modified: py/release/0.9.x/py/LICENSE Log: update LICENSE file a bit, add a link at top-level Added: py/release/0.9.x/LICENSE ============================================================================== --- (empty file) +++ py/release/0.9.x/LICENSE Sun Aug 17 10:24:57 2008 @@ -0,0 +1 @@ +link py/LICENSE \ No newline at end of file Modified: py/release/0.9.x/py/LICENSE ============================================================================== --- py/release/0.9.x/py/LICENSE (original) +++ py/release/0.9.x/py/LICENSE Sun Aug 17 10:24:57 2008 @@ -1,25 +1,28 @@ -py lib Copyright holders, 2003-2005 +py lib Copyright holders, 2003-2008 ======================================= Except when otherwise stated (look for LICENSE files or information at the beginning of each file) the files in the 'py' directory are -copyrighted by one or more of the following people and organizations: +copyrighted by one or more of the following people and organizations: - Holger Krekel - merlinux GmbH, Germany - Armin Rigo - Carl Friedrich Bolz - Maciek Fijalkowski - Guido Wesdorp - Jan Balster + Holger Krekel, holger at merlinux eu + Guido Wesdorp, johnny at johnnydebris net + Carl Friedrich Bolz, cfbolz at gmx de + Armin Rigo, arigo at tunes org + Maciek Fijalkowski, fijal at genesilico.pl + merlinux GmbH, Germany, office at merlinux eu Contributors include:: - Ian Bicking - Grig Gheorghiu - Bob Ippolito - Christian Tismer - Samuele Pedroni + Briand Dorsey + Samuele Pedroni + Harald Armin Massa + Ralf Schmitt + Ian Bicking + Jan Balster + Grig Gheorghiu + Bob Ippolito + Christian Tismer Except when otherwise stated (look for LICENSE files or information at the beginning of each file) all files in the 'py' directory are From hpk at codespeak.net Sun Aug 17 12:51:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 12:51:21 +0200 (CEST) Subject: [py-svn] r57341 - py/build Message-ID: <20080817105121.B88AC16853A@codespeak.net> Author: hpk Date: Sun Aug 17 12:51:18 2008 New Revision: 57341 Added: py/build/ py/build/gensetup.py py/build/winpath.py Log: add a separate directory for building and release scripts Added: py/build/gensetup.py ============================================================================== --- (empty file) +++ py/build/gensetup.py Sun Aug 17 12:51:18 2008 @@ -0,0 +1,227 @@ +import sys + +sys.path.insert(0, sys.argv[1]) +import py + +toolpath = py.magic.autopath() + +def error(msg): + print >>sys.stderr, msg + raise SystemExit, 1 + +def reformat(text): + return " ".join(text.split()) + +class CollectFiles: + def __init__(self, wcbasedir, wcpaths): + self.packages = [] + self.data_files = [] + self._datadict = [] + for p in wcpaths: + if p.check(file=1): + if p.basename.startswith("py.") and p.dirpath().basename == 'bin': + self.scripts.append(p.relto(wcbasedir)) + self.adddatafile(p) + elif p.ext == '.py': + self.addpythonfile(p) + else: + self.adddatafile(p) + #else: + # if not p.listdir(): + # self.adddatafile(p.ensure('dummy')) + + def adddatafile(self, p): + target = self._pkgtarget.join(p.dirpath().relto(self._pkgdir)) + l = self._datadict.setdefault(str(target), []) + l.append(p.relto(self._rootdir)) + + def addpythonfile(self, wc): + p = wc.localpath + parts = p.parts() + for above in p.parts(reverse=True)[1:]: + if self._pkgdir.relto(above): + dottedname = p.dirpath().relto(self._rootdir).replace(p.sep, '.') + if dottedname not in self.packages: + self.packages.append(dottedname) + break + if not above.join('__init__.py').check(): + self.adddatafile(p) + #print "warning, added data file", p + break + +class SetupWriter(object): + EXCLUDES = ("MANIFEST.in",) + + def __init__(self, wcbasedir, pkg): + self.wcbasedir = wcbasedir + self.basedir = wcbasedir.localpath + assert self.basedir.check() + self.pkg = pkg + self.meta = pkg.__pkg__ + self.lines = [] + self.wcinfo = self.wcbasedir.info() + self.wcstatus = self.wcbasedir.status(rec=True) + + def append(self, string): + lines = string.split("\n") + while lines: + if not lines[0].strip(): + lines.pop(0) + continue + break + line = lines[0] + indent = len(line) - len(line.lstrip()) + for line in lines: + if line.strip(): + assert not line[:indent].strip(), line + line = line[indent:] + print line + self.lines.append(line) + + def write_winfuncs(self): + self.append(''' + ''') + + + def setup_header(self): + tooltime = "%s %s" %(py.std.time.asctime(), py.std.time.tzname[0]) + toolname = toolpath.basename + pkgname = self.pkg.__name__ + url = self.wcinfo.url + rev = self.wcinfo.rev + self.append(''' + """ + setup file for %(pkgname)r package based on: + + %(url)s, revision=%(rev)s + + [auto-generated by %(toolname)s, %(tooltime)s] + """ + import os, sys + from distutils.core import setup, Extension + ''' % locals()) + + def setup_trailer(self): + self.append(''' + def main(): + do_setup() + if 'install' in sys.argv[1:]: + on_win32_add_to_PATH() + if __name__ == '__main__': + main() + ''') + + def setup_function(self): + params = self.__dict__.copy() + params.update(self.meta.__dict__) + #params['url'] = self.wcinfo.url + #params['rev'] = self.wcinfo.rev + params['long_description'] = reformat(params['long_description']) + params['scripts'] = self.getscripts() + print py.std.pprint.pprint(params) + self.append(''' + def do_setup(): + setup( + name=%(name)r, + description=%(description)r, + version=%(version)r, + url=%(url)r, + license=%(license)r, + platforms=%(platforms)r, + author=%(author)r, + author_email=%(author_email)r, + ''' % params) + indent = " " * 8 + self.append_pprint(indent, scripts=self.getscripts()) + self.append_pprint(indent, long_description=params['long_description']) + self.append_pprint(indent, packages=self.getpackages()) + self.append_pprint(indent, data_files=self.getdatafiles()) + #self.append_pprint(indent, package_dir={'py': 'py'}) + #self.append_pprint(indent, packages=self.getpackages()) + self.lines.append(indent[4:] + ")\n") + + + def append_pprint(self, indent, **kw): + for name, value in kw.items(): + stringio = py.std.StringIO.StringIO() + value = py.std.pprint.pprint(value, stream=stringio) + stringio.seek(0) + lines = stringio.readlines() + line = lines.pop(0).rstrip() + self.lines.append(indent + "%s=%s" %(name, line)) + indent = indent + " " * (len(name)+1) + for line in lines: + self.lines.append(indent + line.rstrip()) + self.lines[-1] = self.lines[-1] + "," + + def getscripts(self): + bindir = self.wcbasedir.join('py', 'bin') + scripts = [] + for p in self.wcstatus.allpath(): + if p.dirpath() == bindir and p.basename.startswith('py.'): + scripts.append(p.relto(self.wcbasedir)) + return scripts + + def getpackages(self): + packages = [] + for p in self.wcstatus.allpath(): + if p.check(dir=1) and p.join('__init__.py').check(): + modpath = p.relto(self.wcbasedir).replace(p.sep, '.') + packages.append(modpath) + return packages + + def getdatafiles(self): + datafiles = [] + for p in self.wcstatus.allpath(): + if p.check(file=1) and not p.ext == ".py": + datafiles.append(p.relto(self.wcbasedir)) + return datafiles + + def setup_win32(self): + import winpath + self.append(py.std.inspect.getsource(winpath)) + + def write_setup(self): + self.setup_header() + self.setup_function() + self.setup_win32() + self.setup_trailer() + targetfile = self.basedir.join("setup.py") + targetfile.write("\n".join(self.lines)) + print "wrote", targetfile + + def write_manifest(self): + lines = [] + status = self.wcstatus + for p in status.allpath(): + if p.check(dir=1): + continue + toadd = p.relto(wcbasedir) + if toadd in self.EXCLUDES: + lines.append("exclude %s" %(toadd)) + elif toadd: + lines.append("include %s" %(toadd)) + targetfile = self.basedir.join("MANIFEST.in") + targetfile.write("\n".join(lines)) + print "wrote", targetfile + + def write_all(self): + self.write_manifest() + self.write_setup() + +if __name__ == "__main__": + basedir = py.path.local(sys.argv[1]) + if not basedir.check(): + error("basedir not found: %s" %(basedir,)) + pydir = basedir.join('py') + if not pydir.check(): + error("no 'py' directory found in: %s" %(pydir,)) + actualpydir = py.path.local(py.__file__).dirpath() + if pydir != actualpydir: + error("package dir conflict, %s != %s" %(pydir, actualpydir)) + wcbasedir = py.path.svnwc(basedir) + if not wcbasedir.check(versioned=True): + error("not a svn working copy:%s" % basedir) + + writer = SetupWriter(wcbasedir, py) + writer.write_all() Added: py/build/winpath.py ============================================================================== --- (empty file) +++ py/build/winpath.py Sun Aug 17 12:51:18 2008 @@ -0,0 +1,60 @@ + +# +# some helpers to add the py lib scripts to the +# WIN32 cmdline environment +# +import os, sys +try: + import _winreg +except ImportError: + winextensions = 0 +else: + winextensions = 1 + +def on_win32_add_to_PATH(): + if sys.platform != 'win32' or not winextensions: + return + # Add py/bin to PATH environment variable + bindir = os.path.join(sysconfig.get_python_lib(), "py", "bin", "win32") + reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" + path = get_registry_value(reg, key, "Path") + if bindir in path: + return + path += ";" + bindir + print "Setting PATH to:", path + set_registry_value(reg, key, "Path", path) + #print "Current PATH is:", get_registry_value(reg, key, "Path") + + # Propagate changes to current command prompt + os.system("set PATH=%s" % path) + try_propagate_system() + +def try_propagate_system(): + try: + import win32gui, win32con + except ImportError: + return + # Propagate changes throughout the system + win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, + win32con.WM_SETTINGCHANGE, 0, "Environment", + win32con.SMTO_ABORTIFHUNG, 5000) + + + +def get_registry_value(reg, key, value_name): + k = _winreg.OpenKey(reg, key) + value = _winreg.QueryValueEx(k, value_name)[0] + _winreg.CloseKey(k) + return value + +def set_registry_value(reg, key, value_name, value): + k = _winreg.OpenKey(reg, key, 0, _winreg.KEY_WRITE) + value_type = _winreg.REG_SZ + # if we handle the Path value, then set its type to REG_EXPAND_SZ + # so that things like %SystemRoot% get automatically expanded by the + # command prompt + if value_name == "Path": + value_type = _winreg.REG_EXPAND_SZ + _winreg.SetValueEx(k, value_name, 0, value_type, value) + _winreg.CloseKey(k) From hpk at codespeak.net Sun Aug 17 13:36:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 13:36:32 +0200 (CEST) Subject: [py-svn] r57344 - py/build Message-ID: <20080817113632.15727168527@codespeak.net> Author: hpk Date: Sun Aug 17 13:36:31 2008 New Revision: 57344 Modified: py/build/gensetup.py Log: this now generates a setup.py that installs the py lib including an extension Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 13:36:31 2008 @@ -12,43 +12,6 @@ def reformat(text): return " ".join(text.split()) -class CollectFiles: - def __init__(self, wcbasedir, wcpaths): - self.packages = [] - self.data_files = [] - self._datadict = [] - for p in wcpaths: - if p.check(file=1): - if p.basename.startswith("py.") and p.dirpath().basename == 'bin': - self.scripts.append(p.relto(wcbasedir)) - self.adddatafile(p) - elif p.ext == '.py': - self.addpythonfile(p) - else: - self.adddatafile(p) - #else: - # if not p.listdir(): - # self.adddatafile(p.ensure('dummy')) - - def adddatafile(self, p): - target = self._pkgtarget.join(p.dirpath().relto(self._pkgdir)) - l = self._datadict.setdefault(str(target), []) - l.append(p.relto(self._rootdir)) - - def addpythonfile(self, wc): - p = wc.localpath - parts = p.parts() - for above in p.parts(reverse=True)[1:]: - if self._pkgdir.relto(above): - dottedname = p.dirpath().relto(self._rootdir).replace(p.sep, '.') - if dottedname not in self.packages: - self.packages.append(dottedname) - break - if not above.join('__init__.py').check(): - self.adddatafile(p) - #print "warning, added data file", p - break - class SetupWriter(object): EXCLUDES = ("MANIFEST.in",) @@ -61,6 +24,9 @@ self.lines = [] self.wcinfo = self.wcbasedir.info() self.wcstatus = self.wcbasedir.status(rec=True) + self.allpaths = [x for x in self.wcstatus.allpath() + if x not in self.wcstatus.unknown and + x not in self.wcstatus.external] def append(self, string): lines = string.split("\n") @@ -130,12 +96,15 @@ platforms=%(platforms)r, author=%(author)r, author_email=%(author_email)r, + ext_modules = [Extension("py.c-extension.greenlet.greenlet", + ["py/c-extension/greenlet/greenlet.c"]),], ''' % params) indent = " " * 8 self.append_pprint(indent, scripts=self.getscripts()) self.append_pprint(indent, long_description=params['long_description']) self.append_pprint(indent, packages=self.getpackages()) - self.append_pprint(indent, data_files=self.getdatafiles()) + #self.append_pprint(indent, data_files=self.getdatafiles()) + self.append_pprint(indent, package_data=self.getpackagedata()) #self.append_pprint(indent, package_dir={'py': 'py'}) #self.append_pprint(indent, packages=self.getpackages()) self.lines.append(indent[4:] + ")\n") @@ -157,24 +126,34 @@ def getscripts(self): bindir = self.wcbasedir.join('py', 'bin') scripts = [] - for p in self.wcstatus.allpath(): + for p in self.allpaths: if p.dirpath() == bindir and p.basename.startswith('py.'): scripts.append(p.relto(self.wcbasedir)) return scripts def getpackages(self): packages = [] - for p in self.wcstatus.allpath(): + for p in self.allpaths: if p.check(dir=1) and p.join('__init__.py').check(): modpath = p.relto(self.wcbasedir).replace(p.sep, '.') packages.append(modpath) return packages + def getpackagedata(self): + datafiles = [] + pkgbase = self.wcbasedir.join(self.pkg.__name__) + for p in self.allpaths: + if p.check(file=1) and not p.ext == ".py": + if p.dirpath() != self.wcbasedir: + datafiles.append(p.relto(pkgbase)) + return {'py': datafiles} + def getdatafiles(self): datafiles = [] - for p in self.wcstatus.allpath(): + for p in self.allpaths: if p.check(file=1) and not p.ext == ".py": - datafiles.append(p.relto(self.wcbasedir)) + if p.dirpath() != self.wcbasedir: + datafiles.append(p.relto(self.wcbasedir)) return datafiles def setup_win32(self): @@ -193,15 +172,13 @@ def write_manifest(self): lines = [] status = self.wcstatus - for p in status.allpath(): + for p in self.allpaths: if p.check(dir=1): continue toadd = p.relto(wcbasedir) - if toadd in self.EXCLUDES: - lines.append("exclude %s" %(toadd)) - elif toadd: - lines.append("include %s" %(toadd)) - targetfile = self.basedir.join("MANIFEST.in") + if toadd and toadd not in self.EXCLUDES: + lines.append("%s" %(toadd)) + targetfile = self.basedir.join("MANIFEST") targetfile.write("\n".join(lines)) print "wrote", targetfile From hpk at codespeak.net Sun Aug 17 13:53:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 13:53:22 +0200 (CEST) Subject: [py-svn] r57345 - py/release/0.9.x Message-ID: <20080817115322.31A32169E74@codespeak.net> Author: hpk Date: Sun Aug 17 13:53:19 2008 New Revision: 57345 Added: py/release/0.9.x/MANIFEST py/release/0.9.x/README.txt Modified: py/release/0.9.x/setup.py Log: * new autogenerated setup.py and MANIFEST * adding new README Added: py/release/0.9.x/MANIFEST ============================================================================== --- (empty file) +++ py/release/0.9.x/MANIFEST Sun Aug 17 13:53:19 2008 @@ -0,0 +1,423 @@ +LICENSE +MANIFEST +README.txt +py/LICENSE +py/__init__.py +py/apigen/__init__.py +py/apigen/api.js +py/apigen/apigen.js +py/apigen/apigen.py +py/apigen/conftest.py +py/apigen/html.py +py/apigen/htmlgen.py +py/apigen/layout.py +py/apigen/linker.py +py/apigen/project.py +py/apigen/rest/__init__.py +py/apigen/rest/genrest.py +py/apigen/rest/htmlhandlers.py +py/apigen/rest/testing/__init__.py +py/apigen/rest/testing/somemodule.py +py/apigen/rest/testing/someothermodule.py +py/apigen/rest/testing/test_htmlhandlers.py +py/apigen/rest/testing/test_rest.py +py/apigen/source/__init__.py +py/apigen/source/browser.py +py/apigen/source/color.py +py/apigen/source/html.py +py/apigen/source/index.cgi +py/apigen/source/path.py +py/apigen/source/server.py +py/apigen/source/testing/__init__.py +py/apigen/source/testing/test_browser.py +py/apigen/source/testing/test_color.py +py/apigen/source/testing/test_html.py +py/apigen/style.css +py/apigen/testing/__init__.py +py/apigen/testing/test_apigen_example.py +py/apigen/testing/test_apigen_functional.py +py/apigen/testing/test_htmlgen.py +py/apigen/testing/test_linker.py +py/apigen/todo-apigen.txt +py/apigen/todo.txt +py/apigen/tracer/__init__.py +py/apigen/tracer/description.py +py/apigen/tracer/docstorage.py +py/apigen/tracer/magic.py +py/apigen/tracer/model.py +py/apigen/tracer/permastore.py +py/apigen/tracer/testing/__init__.py +py/apigen/tracer/testing/package/__init__.py +py/apigen/tracer/testing/package/submodule/__init__.py +py/apigen/tracer/testing/package/submodule/pak/__init__.py +py/apigen/tracer/testing/package/submodule/pak/mod.py +py/apigen/tracer/testing/package/submodule/pak/somenamespace.py +py/apigen/tracer/testing/runtest.py +py/apigen/tracer/testing/test_desc.py +py/apigen/tracer/testing/test_docgen.py +py/apigen/tracer/testing/test_model.py +py/apigen/tracer/testing/test_package.py +py/apigen/tracer/tracer.py +py/bin/_docgen.py +py/bin/_findpy.py +py/bin/_makepyrelease.py +py/bin/_update_website.py +py/bin/py.cleanup +py/bin/py.countloc +py/bin/py.lookup +py/bin/py.rest +py/bin/py.test +py/bin/pytest.cmd +py/bin/win32/py.cleanup.cmd +py/bin/win32/py.countloc.cmd +py/bin/win32/py.lookup.cmd +py/bin/win32/py.rest.cmd +py/bin/win32/py.test.cmd +py/builtin/__init__.py +py/builtin/enumerate.py +py/builtin/exception.py +py/builtin/reversed.py +py/builtin/set.py +py/builtin/sorted.py +py/builtin/testing/__init__.py +py/builtin/testing/test_enumerate.py +py/builtin/testing/test_exception.py +py/builtin/testing/test_reversed.py +py/builtin/testing/test_set.py +py/builtin/testing/test_sorted.py +py/c-extension/conftest.py +py/c-extension/greenlet/README.txt +py/c-extension/greenlet/dummy_greenlet.py +py/c-extension/greenlet/greenlet.c +py/c-extension/greenlet/greenlet.h +py/c-extension/greenlet/setup.py +py/c-extension/greenlet/slp_platformselect.h +py/c-extension/greenlet/switch_amd64_unix.h +py/c-extension/greenlet/switch_ppc_macosx.h +py/c-extension/greenlet/switch_ppc_unix.h +py/c-extension/greenlet/switch_s390_unix.h +py/c-extension/greenlet/switch_sparc_sun_gcc.h +py/c-extension/greenlet/switch_x86_msvc.h +py/c-extension/greenlet/switch_x86_unix.h +py/c-extension/greenlet/test_generator.py +py/c-extension/greenlet/test_generator_nested.py +py/c-extension/greenlet/test_greenlet.py +py/c-extension/greenlet/test_remote.py +py/c-extension/greenlet/test_throw.py +py/code/__init__.py +py/code/code.py +py/code/excinfo.py +py/code/frame.py +py/code/safe_repr.py +py/code/source.py +py/code/testing/__init__.py +py/code/testing/test_code.py +py/code/testing/test_cpython_features.py +py/code/testing/test_excinfo.py +py/code/testing/test_frame.py +py/code/testing/test_safe_repr.py +py/code/testing/test_source.py +py/code/traceback2.py +py/compat/LICENSE +py/compat/__init__.py +py/compat/conftest.py +py/compat/doctest.py +py/compat/optparse.py +py/compat/subprocess.py +py/compat/testing/__init__.py +py/compat/testing/_findpy.py +py/compat/testing/test_doctest.py +py/compat/testing/test_doctest.txt +py/compat/testing/test_doctest2.py +py/compat/testing/test_doctest2.txt +py/compat/testing/test_optparse.py +py/compat/testing/test_subprocess.py +py/compat/testing/test_textwrap.py +py/compat/textwrap.py +py/conftest.py +py/doc/TODO.txt +py/doc/__init__.py +py/doc/apigen.txt +py/doc/bin.txt +py/doc/changes-0.9.1.txt +py/doc/code.txt +py/doc/coding-style.txt +py/doc/confrest.py +py/doc/conftest.py +py/doc/contact.txt +py/doc/download.txt +py/doc/example/genhtml.py +py/doc/example/genhtmlcss.py +py/doc/example/genxml.py +py/doc/example/pytest/failure_demo.py +py/doc/example/pytest/test_failures.py +py/doc/example/pytest/test_setup_flow_example.py +py/doc/execnet.txt +py/doc/future.txt +py/doc/future/code_template.txt +py/doc/future/planning.txt +py/doc/future/pylib_pypy.txt +py/doc/future/rsession_todo.txt +py/doc/greenlet.txt +py/doc/impl-test.txt +py/doc/index.txt +py/doc/io.txt +py/doc/links.txt +py/doc/log.txt +py/doc/misc.txt +py/doc/path.txt +py/doc/release-0.9.0.txt +py/doc/release-0.9.1.txt +py/doc/style.css +py/doc/talk/execnet-overview.txt +py/doc/talk/make.py +py/doc/talk/makeref.py +py/doc/talk/pytest-overview.txt +py/doc/talk/ui/default/blank.gif +py/doc/talk/ui/default/framing.css +py/doc/talk/ui/default/iepngfix.htc +py/doc/talk/ui/default/opera.css +py/doc/talk/ui/default/outline.css +py/doc/talk/ui/default/pretty.css +py/doc/talk/ui/default/print.css +py/doc/talk/ui/default/py-web.png +py/doc/talk/ui/default/s5-core.css +py/doc/talk/ui/default/slides.css +py/doc/talk/ui/default/slides.js +py/doc/talk/ui/merlinux-klein.png +py/doc/talk/ui/py-web.png +py/doc/talk/ui/py.css +py/doc/test.txt +py/doc/test_conftest.py +py/doc/why_py.txt +py/doc/xml.txt +py/env.cmd +py/env.py +py/execnet/NOTES +py/execnet/__init__.py +py/execnet/channel.py +py/execnet/gateway.py +py/execnet/inputoutput.py +py/execnet/message.py +py/execnet/register.py +py/execnet/rsync.py +py/execnet/rsync_remote.py +py/execnet/script/loop_socketserver.py +py/execnet/script/quitserver.py +py/execnet/script/shell.py +py/execnet/script/socketserver.py +py/execnet/script/socketserverservice.py +py/execnet/script/xx.py +py/execnet/testing/__init__.py +py/execnet/testing/test_gateway.py +py/execnet/testing/test_pickle.py +py/execnet/testing/test_rsync.py +py/initpkg.py +py/io/__init__.py +py/io/dupfile.py +py/io/fdcapture.py +py/io/stdcapture.py +py/io/test/__init__.py +py/io/test/test_dupfile.py +py/io/test/test_fdcapture.py +py/io/test/test_stdcapture.py +py/log/__init__.py +py/log/consumer.py +py/log/logger.py +py/log/producer.py +py/log/testing/__init__.py +py/log/testing/test_log.py +py/log/testing/test_logger.py +py/magic/__init__.py +py/magic/assertion.py +py/magic/autopath.py +py/magic/exprinfo.py +py/magic/greenlet.py +py/magic/invoke.py +py/magic/patch.py +py/magic/testing/__init__.py +py/magic/testing/test_assertion.py +py/magic/testing/test_autopath.py +py/magic/testing/test_exprinfo.py +py/magic/testing/test_invoke.py +py/magic/testing/test_patch.py +py/magic/testing/test_viewtype.py +py/magic/viewtype.py +py/misc/__init__.py +py/misc/_dist.py +py/misc/buildcmodule.py +py/misc/cache.py +py/misc/cmdline/__init__.py +py/misc/cmdline/countloc.py +py/misc/conftest-socketgatewayrun.py +py/misc/difftime.py +py/misc/dynpkg.py +py/misc/error.py +py/misc/findmissingdocstrings.py +py/misc/killproc.py +py/misc/rest.py +py/misc/std.py +py/misc/svnlook.py +py/misc/terminal_helper.py +py/misc/testing/__init__.py +py/misc/testing/data/svnlookrepo.dump +py/misc/testing/test_api.py +py/misc/testing/test_cache.py +py/misc/testing/test_error.py +py/misc/testing/test_initpkg.py +py/misc/testing/test_oskill.py +py/misc/testing/test_std.py +py/misc/testing/test_svnlook.py +py/misc/testing/test_terminal.py +py/misc/testing/test_update_website.py +py/path/__init__.py +py/path/common.py +py/path/gateway/TODO.txt +py/path/gateway/__init__.py +py/path/gateway/channeltest.py +py/path/gateway/channeltest2.py +py/path/gateway/remotepath.py +py/path/local/__init__.py +py/path/local/common.py +py/path/local/local.py +py/path/local/posix.py +py/path/local/testing/__init__.py +py/path/local/testing/test_local.py +py/path/local/testing/test_posix.py +py/path/local/testing/test_win.py +py/path/local/win.py +py/path/svn/__init__.py +py/path/svn/cache.py +py/path/svn/quoting.txt +py/path/svn/svncommon.py +py/path/svn/testing/__init__.py +py/path/svn/testing/repotest.dump +py/path/svn/testing/svntestbase.py +py/path/svn/testing/test_auth.py +py/path/svn/testing/test_test_repo.py +py/path/svn/testing/test_urlcommand.py +py/path/svn/testing/test_wccommand.py +py/path/svn/urlcommand.py +py/path/svn/wccommand.py +py/path/testing/__init__.py +py/path/testing/common.py +py/path/testing/fscommon.py +py/path/testing/test_api.py +py/process/__init__.py +py/process/cmdexec.py +py/process/testing/__init__.py +py/process/testing/test_cmdexec.py +py/rest/__init__.py +py/rest/convert.py +py/rest/directive.py +py/rest/latex.py +py/rest/rest.sty.template +py/rest/rst.py +py/rest/testing/__init__.py +py/rest/testing/data/example.rst2pdfconfig +py/rest/testing/data/example1.dot +py/rest/testing/data/formula.txt +py/rest/testing/data/formula1.txt +py/rest/testing/data/graphviz.txt +py/rest/testing/data/part1.txt +py/rest/testing/data/part2.txt +py/rest/testing/data/tocdepth.rst2pdfconfig +py/rest/testing/test_convert.py +py/rest/testing/test_directive.py +py/rest/testing/test_htmlrest.py +py/rest/testing/test_rst.py +py/rest/testing/test_rst2pdf.py +py/rest/testing/test_transform.py +py/rest/transform.py +py/test/__init__.py +py/test/cmdline.py +py/test/collect.py +py/test/config.py +py/test/conftesthandle.py +py/test/defaultconftest.py +py/test/deprecate.py +py/test/doctest.py +py/test/item.py +py/test/outcome.py +py/test/raises.py +py/test/representation.py +py/test/rsession/__init__.py +py/test/rsession/box.py +py/test/rsession/executor.py +py/test/rsession/hostmanage.py +py/test/rsession/local.py +py/test/rsession/master.py +py/test/rsession/outcome.py +py/test/rsession/repevent.py +py/test/rsession/reporter.py +py/test/rsession/rest.py +py/test/rsession/rsession.py +py/test/rsession/slave.py +py/test/rsession/testing/__init__.py +py/test/rsession/testing/basetest.py +py/test/rsession/testing/example1.py +py/test/rsession/testing/example2.py +py/test/rsession/testing/test_boxing.py +py/test/rsession/testing/test_executor.py +py/test/rsession/testing/test_hostmanage.py +py/test/rsession/testing/test_lsession.py +py/test/rsession/testing/test_master.py +py/test/rsession/testing/test_outcome.py +py/test/rsession/testing/test_repevent.py +py/test/rsession/testing/test_reporter.py +py/test/rsession/testing/test_rest.py +py/test/rsession/testing/test_rsession.py +py/test/rsession/testing/test_slave.py +py/test/rsession/testing/test_web.py +py/test/rsession/testing/test_webjs.py +py/test/rsession/web.py +py/test/rsession/webdata/__init__.py +py/test/rsession/webdata/index.html +py/test/rsession/webdata/json.py +py/test/rsession/webdata/source.js +py/test/rsession/webjs.py +py/test/session.py +py/test/terminal/__init__.py +py/test/terminal/out.py +py/test/terminal/remote.py +py/test/terminal/terminal.py +py/test/testing/__init__.py +py/test/testing/import_test/package/__init__.py +py/test/testing/import_test/package/absolute_import_shared_lib.py +py/test/testing/import_test/package/module_that_imports_shared_lib.py +py/test/testing/import_test/package/shared_lib.py +py/test/testing/import_test/package/test_import.py +py/test/testing/setupdata.py +py/test/testing/test_collect.py +py/test/testing/test_config.py +py/test/testing/test_conftesthandle.py +py/test/testing/test_deprecated.py +py/test/testing/test_doctest.py +py/test/testing/test_raises.py +py/test/testing/test_remote.py +py/test/testing/test_repr.py +py/test/testing/test_session.py +py/test/testing/test_setup_nested.py +py/test/web/__init__.py +py/test/web/exception.py +py/test/web/post_multipart.py +py/test/web/webcheck.py +py/thread/__init__.py +py/thread/io.py +py/thread/pool.py +py/thread/testing/__init__.py +py/thread/testing/test_io.py +py/thread/testing/test_pool.py +py/tool/__init__.py +py/tool/testing/__init__.py +py/tool/testing/test_utestconvert.py +py/tool/utestconvert.py +py/xmlobj/__init__.py +py/xmlobj/html.py +py/xmlobj/misc.py +py/xmlobj/testing/__init__.py +py/xmlobj/testing/test_html.py +py/xmlobj/testing/test_xml.py +py/xmlobj/visit.py +py/xmlobj/xml.py +setup.py \ No newline at end of file Added: py/release/0.9.x/README.txt ============================================================================== --- (empty file) +++ py/release/0.9.x/README.txt Sun Aug 17 13:53:19 2008 @@ -0,0 +1,19 @@ +The py lib is a development support library featuring +the following tools and modules: + +* py.test: popular python testing tool +* py.execnet: ad-hoc distributed execution +* py.magic.greenlet: micro-threads +* py.path: uniform local and svn path objects + +It includes code and contributions from several people, +listed in the LICENSE file. + +For questions, please see py/doc/index.txt, refer to the website +http://pylib.org or http://pytest.org +come to the #pylib IRC freenode channel or subscribe to the +http://codespeak.net/mailman/listinfo/py-dev mailing list. + +have fun, + +holger krekel, holger at merlinux eu Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 13:53:19 2008 @@ -1,3 +1,249 @@ -import py -from py.__.misc._dist import setup -setup(py) +""" + setup file for 'py' package based on: + + svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57343 + + [auto-generated by gensetup.py, Sun Aug 17 13:53:30 2008 CET] +""" +import os, sys +from distutils.core import setup, Extension + +def main(): + setup(cmdclass=cmdclass, + name='py', + description='py lib: agile development and test support library', + version='0.9.2-pre-alpha', + url='http://codespeak.net/py', + license='MIT license', + platforms=['unix', 'linux', 'cygwin', 'win32'], + author='holger krekel, Carl Friedrich Bolz, Guido Wesdorp, Maciej Fijalkowski, Armin Rigo & others', + author_email='py-dev at codespeak.net', + ext_modules = [Extension("py.c-extension.greenlet.greenlet", + ["py/c-extension/greenlet/greenlet.c"]),], + + scripts=['py/bin/py.cleanup', + 'py/bin/py.countloc', + 'py/bin/py.lookup', + 'py/bin/py.rest', + 'py/bin/py.test'], + long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', + packages=['py', + 'py.apigen', + 'py.apigen.rest', + 'py.apigen.rest.testing', + 'py.apigen.source', + 'py.apigen.source.testing', + 'py.apigen.testing', + 'py.apigen.tracer', + 'py.apigen.tracer.testing', + 'py.apigen.tracer.testing.package', + 'py.apigen.tracer.testing.package.submodule', + 'py.apigen.tracer.testing.package.submodule.pak', + 'py.builtin', + 'py.builtin.testing', + 'py.code', + 'py.code.testing', + 'py.compat', + 'py.compat.testing', + 'py.doc', + 'py.execnet', + 'py.execnet.testing', + 'py.io', + 'py.io.test', + 'py.log', + 'py.log.testing', + 'py.magic', + 'py.magic.testing', + 'py.misc', + 'py.misc.cmdline', + 'py.misc.testing', + 'py.path', + 'py.path.gateway', + 'py.path.local', + 'py.path.local.testing', + 'py.path.svn', + 'py.path.svn.testing', + 'py.path.testing', + 'py.process', + 'py.process.testing', + 'py.rest', + 'py.rest.testing', + 'py.test', + 'py.test.rsession', + 'py.test.rsession.testing', + 'py.test.rsession.webdata', + 'py.test.terminal', + 'py.test.testing', + 'py.test.testing.import_test.package', + 'py.test.web', + 'py.thread', + 'py.thread.testing', + 'py.tool', + 'py.tool.testing', + 'py.xmlobj', + 'py.xmlobj.testing'], + package_data={'py': ['LICENSE', + 'apigen/api.js', + 'apigen/apigen.js', + 'apigen/source/index.cgi', + 'apigen/style.css', + 'apigen/todo-apigen.txt', + 'apigen/todo.txt', + 'bin/py.cleanup', + 'bin/py.countloc', + 'bin/py.lookup', + 'bin/py.rest', + 'bin/py.test', + 'bin/pytest.cmd', + 'bin/win32/py.cleanup.cmd', + 'bin/win32/py.countloc.cmd', + 'bin/win32/py.lookup.cmd', + 'bin/win32/py.rest.cmd', + 'bin/win32/py.test.cmd', + 'c-extension/greenlet/README.txt', + 'c-extension/greenlet/greenlet.c', + 'c-extension/greenlet/greenlet.h', + 'c-extension/greenlet/slp_platformselect.h', + 'c-extension/greenlet/switch_amd64_unix.h', + 'c-extension/greenlet/switch_ppc_macosx.h', + 'c-extension/greenlet/switch_ppc_unix.h', + 'c-extension/greenlet/switch_s390_unix.h', + 'c-extension/greenlet/switch_sparc_sun_gcc.h', + 'c-extension/greenlet/switch_x86_msvc.h', + 'c-extension/greenlet/switch_x86_unix.h', + 'compat/LICENSE', + 'compat/testing/test_doctest.txt', + 'compat/testing/test_doctest2.txt', + 'doc/TODO.txt', + 'doc/apigen.txt', + 'doc/bin.txt', + 'doc/changes-0.9.1.txt', + 'doc/code.txt', + 'doc/coding-style.txt', + 'doc/contact.txt', + 'doc/download.txt', + 'doc/execnet.txt', + 'doc/future.txt', + 'doc/future/code_template.txt', + 'doc/future/planning.txt', + 'doc/future/pylib_pypy.txt', + 'doc/future/rsession_todo.txt', + 'doc/greenlet.txt', + 'doc/impl-test.txt', + 'doc/index.txt', + 'doc/io.txt', + 'doc/links.txt', + 'doc/log.txt', + 'doc/misc.txt', + 'doc/path.txt', + 'doc/release-0.9.0.txt', + 'doc/release-0.9.1.txt', + 'doc/style.css', + 'doc/talk/execnet-overview.txt', + 'doc/talk/pytest-overview.txt', + 'doc/talk/ui/default/blank.gif', + 'doc/talk/ui/default/framing.css', + 'doc/talk/ui/default/iepngfix.htc', + 'doc/talk/ui/default/opera.css', + 'doc/talk/ui/default/outline.css', + 'doc/talk/ui/default/pretty.css', + 'doc/talk/ui/default/print.css', + 'doc/talk/ui/default/py-web.png', + 'doc/talk/ui/default/s5-core.css', + 'doc/talk/ui/default/slides.css', + 'doc/talk/ui/default/slides.js', + 'doc/talk/ui/merlinux-klein.png', + 'doc/talk/ui/py-web.png', + 'doc/talk/ui/py.css', + 'doc/test.txt', + 'doc/why_py.txt', + 'doc/xml.txt', + 'env.cmd', + 'execnet/NOTES', + 'misc/testing/data/svnlookrepo.dump', + 'path/gateway/TODO.txt', + 'path/svn/quoting.txt', + 'path/svn/testing/repotest.dump', + 'rest/rest.sty.template', + 'rest/testing/data/example.rst2pdfconfig', + 'rest/testing/data/example1.dot', + 'rest/testing/data/formula.txt', + 'rest/testing/data/formula1.txt', + 'rest/testing/data/graphviz.txt', + 'rest/testing/data/part1.txt', + 'rest/testing/data/part2.txt', + 'rest/testing/data/tocdepth.rst2pdfconfig', + 'test/rsession/webdata/index.html', + 'test/rsession/webdata/source.js']}, + ) + +# +# some helpers to add the py lib scripts to the +# WIN32 cmdline environment +# +import os, sys +try: + import _winreg +except ImportError: + winextensions = 0 +else: + winextensions = 1 + +def on_win32_add_to_PATH(): + if sys.platform != 'win32' or not winextensions: + return + # Add py/bin to PATH environment variable + bindir = os.path.join(sysconfig.get_python_lib(), "py", "bin", "win32") + reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" + path = get_registry_value(reg, key, "Path") + if bindir in path: + return + path += ";" + bindir + print "Setting PATH to:", path + set_registry_value(reg, key, "Path", path) + #print "Current PATH is:", get_registry_value(reg, key, "Path") + + # Propagate changes to current command prompt + os.system("set PATH=%s" % path) + try_propagate_system() + +def try_propagate_system(): + try: + import win32gui, win32con + except ImportError: + return + # Propagate changes throughout the system + win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, + win32con.WM_SETTINGCHANGE, 0, "Environment", + win32con.SMTO_ABORTIFHUNG, 5000) + + + +def get_registry_value(reg, key, value_name): + k = _winreg.OpenKey(reg, key) + value = _winreg.QueryValueEx(k, value_name)[0] + _winreg.CloseKey(k) + return value + +def set_registry_value(reg, key, value_name, value): + k = _winreg.OpenKey(reg, key, 0, _winreg.KEY_WRITE) + value_type = _winreg.REG_SZ + # if we handle the Path value, then set its type to REG_EXPAND_SZ + # so that things like %SystemRoot% get automatically expanded by the + # command prompt + if value_name == "Path": + value_type = _winreg.REG_EXPAND_SZ + _winreg.SetValueEx(k, value_name, 0, value_type, value) + _winreg.CloseKey(k) + +from distutils.command.install import install +class my_install(install): + def finalize_other(self): + install.finalize_other(self) + on_win32_add_to_PATH() +cmdclass = {'install': my_install} + +if __name__ == '__main__': + main() + \ No newline at end of file From hpk at codespeak.net Sun Aug 17 13:58:57 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 13:58:57 +0200 (CEST) Subject: [py-svn] r57346 - in py: extradoc/talk/2006-march extradoc/talk/2006-march/ui release/0.9.x/py/doc/talk trunk/py/doc/talk Message-ID: <20080817115857.AC61A1684F6@codespeak.net> Author: hpk Date: Sun Aug 17 13:58:56 2008 New Revision: 57346 Added: py/extradoc/talk/2006-march/ py/extradoc/talk/2006-march/execnet-overview.txt - copied unchanged from r57343, py/release/0.9.x/py/doc/talk/execnet-overview.txt py/extradoc/talk/2006-march/make.py - copied unchanged from r57343, py/release/0.9.x/py/doc/talk/make.py py/extradoc/talk/2006-march/makeref.py - copied unchanged from r57343, py/release/0.9.x/py/doc/talk/makeref.py py/extradoc/talk/2006-march/pytest-overview.txt - copied unchanged from r57343, py/release/0.9.x/py/doc/talk/pytest-overview.txt py/extradoc/talk/2006-march/ui/ - copied from r57343, py/release/0.9.x/py/doc/talk/ui/ Removed: py/release/0.9.x/py/doc/talk/ py/trunk/py/doc/talk/ Log: shift talk from py/doc to extradoc/talk dir From hpk at codespeak.net Sun Aug 17 14:08:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 14:08:24 +0200 (CEST) Subject: [py-svn] r57347 - py/release/0.9.x Message-ID: <20080817120824.DBB0A16852F@codespeak.net> Author: hpk Date: Sun Aug 17 14:08:23 2008 New Revision: 57347 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/setup.py Log: fix a bug, regen Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 14:08:23 2008 @@ -169,24 +169,6 @@ py/doc/release-0.9.0.txt py/doc/release-0.9.1.txt py/doc/style.css -py/doc/talk/execnet-overview.txt -py/doc/talk/make.py -py/doc/talk/makeref.py -py/doc/talk/pytest-overview.txt -py/doc/talk/ui/default/blank.gif -py/doc/talk/ui/default/framing.css -py/doc/talk/ui/default/iepngfix.htc -py/doc/talk/ui/default/opera.css -py/doc/talk/ui/default/outline.css -py/doc/talk/ui/default/pretty.css -py/doc/talk/ui/default/print.css -py/doc/talk/ui/default/py-web.png -py/doc/talk/ui/default/s5-core.css -py/doc/talk/ui/default/slides.css -py/doc/talk/ui/default/slides.js -py/doc/talk/ui/merlinux-klein.png -py/doc/talk/ui/py-web.png -py/doc/talk/ui/py.css py/doc/test.txt py/doc/test_conftest.py py/doc/why_py.txt Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 14:08:23 2008 @@ -3,10 +3,11 @@ svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57343 - [auto-generated by gensetup.py, Sun Aug 17 13:53:30 2008 CET] + [auto-generated by gensetup.py, Sun Aug 17 14:05:21 2008 CET] """ import os, sys from distutils.core import setup, Extension +from distutils import sysconfig def main(): setup(cmdclass=cmdclass, @@ -139,22 +140,6 @@ 'doc/release-0.9.0.txt', 'doc/release-0.9.1.txt', 'doc/style.css', - 'doc/talk/execnet-overview.txt', - 'doc/talk/pytest-overview.txt', - 'doc/talk/ui/default/blank.gif', - 'doc/talk/ui/default/framing.css', - 'doc/talk/ui/default/iepngfix.htc', - 'doc/talk/ui/default/opera.css', - 'doc/talk/ui/default/outline.css', - 'doc/talk/ui/default/pretty.css', - 'doc/talk/ui/default/print.css', - 'doc/talk/ui/default/py-web.png', - 'doc/talk/ui/default/s5-core.css', - 'doc/talk/ui/default/slides.css', - 'doc/talk/ui/default/slides.js', - 'doc/talk/ui/merlinux-klein.png', - 'doc/talk/ui/py-web.png', - 'doc/talk/ui/py.css', 'doc/test.txt', 'doc/why_py.txt', 'doc/xml.txt', @@ -246,4 +231,4 @@ if __name__ == '__main__': main() - \ No newline at end of file + From hpk at codespeak.net Sun Aug 17 14:48:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 14:48:19 +0200 (CEST) Subject: [py-svn] r57348 - py/release/0.9.x Message-ID: <20080817124819.9FA471684DA@codespeak.net> Author: hpk Date: Sun Aug 17 14:48:14 2008 New Revision: 57348 Modified: py/release/0.9.x/setup.py Log: a different way to handle the script problem on windows Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 14:48:14 2008 @@ -177,6 +177,7 @@ def on_win32_add_to_PATH(): if sys.platform != 'win32' or not winextensions: return + assert 0 # Add py/bin to PATH environment variable bindir = os.path.join(sysconfig.get_python_lib(), "py", "bin", "win32") reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) @@ -184,7 +185,7 @@ path = get_registry_value(reg, key, "Path") if bindir in path: return - path += ";" + bindir + path = bindir + ";" + path print "Setting PATH to:", path set_registry_value(reg, key, "Path", path) #print "Current PATH is:", get_registry_value(reg, key, "Path") @@ -222,12 +223,30 @@ _winreg.SetValueEx(k, value_name, 0, value_type, value) _winreg.CloseKey(k) -from distutils.command.install import install -class my_install(install): - def finalize_other(self): - install.finalize_other(self) - on_win32_add_to_PATH() -cmdclass = {'install': my_install} +from distutils.command.install_scripts import install_scripts +class my_install_scripts(install_scripts): + def run(self): + install_scripts.run(self) + print self.outfiles + for fn in self.outfiles: + basename = os.path.basename(fn) + if "." in basename: + #print "tackling", fn + newbasename = basename.replace(".", "_") + newfn = os.path.join(os.path.dirname(fn), newbasename) + if os.path.exists(newfn): + os.remove(newfn) + os.rename(fn, newfn) + newname = fn + ".cmd" + if os.path.exists(newname): + os.remove(newname) + f = open(newname, 'w') + f.write("@echo off\n") + f.write('python "~dp0\%s" %%*\n' % basename) + f.close() + + +cmdclass = {'install_scripts': my_install_scripts} if __name__ == '__main__': main() From hpk at codespeak.net Sun Aug 17 14:58:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 14:58:11 +0200 (CEST) Subject: [py-svn] r57349 - py/build Message-ID: <20080817125811.56A2C169E3C@codespeak.net> Author: hpk Date: Sun Aug 17 14:58:09 2008 New Revision: 57349 Modified: py/build/gensetup.py py/build/winpath.py Log: new way to deal with windows binary, include _findpy Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 14:58:09 2008 @@ -51,7 +51,9 @@ def setup_header(self): tooltime = "%s %s" %(py.std.time.asctime(), py.std.time.tzname[0]) - toolname = toolpath.basename + toolname = toolpath.basename + toolrevision = py.path.svnwc(toolpath).info().rev + pkgname = self.pkg.__name__ url = self.wcinfo.url rev = self.wcinfo.rev @@ -61,18 +63,15 @@ %(url)s, revision=%(rev)s - [auto-generated by %(toolname)s, %(tooltime)s] + autogenerated by %(toolname)s, %(toolrevision)s, %(tooltime)s """ import os, sys from distutils.core import setup, Extension + from distutils import sysconfig ''' % locals()) def setup_trailer(self): self.append(''' - def main(): - do_setup() - if 'install' in sys.argv[1:]: - on_win32_add_to_PATH() if __name__ == '__main__': main() ''') @@ -83,11 +82,10 @@ #params['url'] = self.wcinfo.url #params['rev'] = self.wcinfo.rev params['long_description'] = reformat(params['long_description']) - params['scripts'] = self.getscripts() print py.std.pprint.pprint(params) self.append(''' - def do_setup(): - setup( + def main(): + setup(cmdclass=cmdclass, name=%(name)r, description=%(description)r, version=%(version)r, @@ -127,8 +125,9 @@ bindir = self.wcbasedir.join('py', 'bin') scripts = [] for p in self.allpaths: - if p.dirpath() == bindir and p.basename.startswith('py.'): - scripts.append(p.relto(self.wcbasedir)) + if p.dirpath() == bindir: + if p.basename.startswith('py.') or p.basename == "_findpy": + scripts.append(p.relto(self.wcbasedir)) return scripts def getpackages(self): @@ -156,6 +155,18 @@ datafiles.append(p.relto(self.wcbasedir)) return datafiles + def xxxsetup_win32(self): + import winpath + self.append(py.std.inspect.getsource(winpath)) + self.append(""" + from distutils.command.install import install + class my_install(install): + def finalize_other(self): + install.finalize_other(self) + on_win32_add_to_PATH() + cmdclass = {'install': my_install} + """) + def setup_win32(self): import winpath self.append(py.std.inspect.getsource(winpath)) Modified: py/build/winpath.py ============================================================================== --- py/build/winpath.py (original) +++ py/build/winpath.py Sun Aug 17 14:58:09 2008 @@ -1,60 +1,26 @@ -# -# some helpers to add the py lib scripts to the -# WIN32 cmdline environment -# -import os, sys -try: - import _winreg -except ImportError: - winextensions = 0 -else: - winextensions = 1 +# on windows we need to hack up the to-be-installed scripts -def on_win32_add_to_PATH(): - if sys.platform != 'win32' or not winextensions: - return - # Add py/bin to PATH environment variable - bindir = os.path.join(sysconfig.get_python_lib(), "py", "bin", "win32") - reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) - key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" - path = get_registry_value(reg, key, "Path") - if bindir in path: - return - path += ";" + bindir - print "Setting PATH to:", path - set_registry_value(reg, key, "Path", path) - #print "Current PATH is:", get_registry_value(reg, key, "Path") - - # Propagate changes to current command prompt - os.system("set PATH=%s" % path) - try_propagate_system() - -def try_propagate_system(): - try: - import win32gui, win32con - except ImportError: - return - # Propagate changes throughout the system - win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, - win32con.WM_SETTINGCHANGE, 0, "Environment", - win32con.SMTO_ABORTIFHUNG, 5000) - - - -def get_registry_value(reg, key, value_name): - k = _winreg.OpenKey(reg, key) - value = _winreg.QueryValueEx(k, value_name)[0] - _winreg.CloseKey(k) - return value - -def set_registry_value(reg, key, value_name, value): - k = _winreg.OpenKey(reg, key, 0, _winreg.KEY_WRITE) - value_type = _winreg.REG_SZ - # if we handle the Path value, then set its type to REG_EXPAND_SZ - # so that things like %SystemRoot% get automatically expanded by the - # command prompt - if value_name == "Path": - value_type = _winreg.REG_EXPAND_SZ - _winreg.SetValueEx(k, value_name, 0, value_type, value) - _winreg.CloseKey(k) +from distutils.command.install_scripts import install_scripts +class my_install_scripts(install_scripts): + def run(self): + install_scripts.run(self) + print self.outfiles + for fn in self.outfiles: + basename = os.path.basename(fn) + if "." in basename: + #print "tackling", fn + newbasename = basename.replace(".", "_") + newfn = os.path.join(os.path.dirname(fn), newbasename) + if os.path.exists(newfn): + os.remove(newfn) + os.rename(fn, newfn) + newname = fn + ".cmd" + if os.path.exists(newname): + os.remove(newname) + f = open(newname, 'w') + f.write("@echo off\n") + f.write('python "~dp0\%s" %%*\n' % basename) + f.close() + +cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Sun Aug 17 14:59:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 14:59:46 +0200 (CEST) Subject: [py-svn] r57350 - py/build Message-ID: <20080817125946.026E3169E3C@codespeak.net> Author: hpk Date: Sun Aug 17 14:59:46 2008 New Revision: 57350 Modified: py/build/gensetup.py Log: actually include the _findpy module Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 14:59:46 2008 @@ -126,7 +126,7 @@ scripts = [] for p in self.allpaths: if p.dirpath() == bindir: - if p.basename.startswith('py.') or p.basename == "_findpy": + if p.basename.startswith('py.') or p.basename == "_findpy.py": scripts.append(p.relto(self.wcbasedir)) return scripts From hpk at codespeak.net Sun Aug 17 15:00:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 15:00:24 +0200 (CEST) Subject: [py-svn] r57351 - in py/release/0.9.x: . py/bin/win32 Message-ID: <20080817130024.A7759169E3C@codespeak.net> Author: hpk Date: Sun Aug 17 15:00:23 2008 New Revision: 57351 Removed: py/release/0.9.x/py/bin/win32/ Modified: py/release/0.9.x/setup.py Log: new approach for getting executable win32 scripts Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 15:00:23 2008 @@ -1,9 +1,9 @@ """ setup file for 'py' package based on: - svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57343 + svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57348 - [auto-generated by gensetup.py, Sun Aug 17 14:05:21 2008 CET] + autogenerated by gensetup.py, 57350, Sun Aug 17 15:00:37 2008 CET """ import os, sys from distutils.core import setup, Extension @@ -22,7 +22,8 @@ ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], - scripts=['py/bin/py.cleanup', + scripts=['py/bin/_findpy.py', + 'py/bin/py.cleanup', 'py/bin/py.countloc', 'py/bin/py.lookup', 'py/bin/py.rest', @@ -96,11 +97,6 @@ 'bin/py.rest', 'bin/py.test', 'bin/pytest.cmd', - 'bin/win32/py.cleanup.cmd', - 'bin/win32/py.countloc.cmd', - 'bin/win32/py.lookup.cmd', - 'bin/win32/py.rest.cmd', - 'bin/win32/py.test.cmd', 'c-extension/greenlet/README.txt', 'c-extension/greenlet/greenlet.c', 'c-extension/greenlet/greenlet.h', @@ -162,66 +158,7 @@ 'test/rsession/webdata/source.js']}, ) -# -# some helpers to add the py lib scripts to the -# WIN32 cmdline environment -# -import os, sys -try: - import _winreg -except ImportError: - winextensions = 0 -else: - winextensions = 1 - -def on_win32_add_to_PATH(): - if sys.platform != 'win32' or not winextensions: - return - assert 0 - # Add py/bin to PATH environment variable - bindir = os.path.join(sysconfig.get_python_lib(), "py", "bin", "win32") - reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) - key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" - path = get_registry_value(reg, key, "Path") - if bindir in path: - return - path = bindir + ";" + path - print "Setting PATH to:", path - set_registry_value(reg, key, "Path", path) - #print "Current PATH is:", get_registry_value(reg, key, "Path") - - # Propagate changes to current command prompt - os.system("set PATH=%s" % path) - try_propagate_system() - -def try_propagate_system(): - try: - import win32gui, win32con - except ImportError: - return - # Propagate changes throughout the system - win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, - win32con.WM_SETTINGCHANGE, 0, "Environment", - win32con.SMTO_ABORTIFHUNG, 5000) - - - -def get_registry_value(reg, key, value_name): - k = _winreg.OpenKey(reg, key) - value = _winreg.QueryValueEx(k, value_name)[0] - _winreg.CloseKey(k) - return value - -def set_registry_value(reg, key, value_name, value): - k = _winreg.OpenKey(reg, key, 0, _winreg.KEY_WRITE) - value_type = _winreg.REG_SZ - # if we handle the Path value, then set its type to REG_EXPAND_SZ - # so that things like %SystemRoot% get automatically expanded by the - # command prompt - if value_name == "Path": - value_type = _winreg.REG_EXPAND_SZ - _winreg.SetValueEx(k, value_name, 0, value_type, value) - _winreg.CloseKey(k) +# on windows we need to hack up the to-be-installed scripts from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): @@ -245,9 +182,8 @@ f.write('python "~dp0\%s" %%*\n' % basename) f.close() - cmdclass = {'install_scripts': my_install_scripts} - + if __name__ == '__main__': main() - + \ No newline at end of file From hpk at codespeak.net Sun Aug 17 15:02:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 15:02:52 +0200 (CEST) Subject: [py-svn] r57352 - py/release/0.9.x Message-ID: <20080817130252.91F6C169E3C@codespeak.net> Author: hpk Date: Sun Aug 17 15:02:50 2008 New Revision: 57352 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/setup.py Log: fixing win32 ... Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 15:02:50 2008 @@ -68,11 +68,6 @@ py/bin/py.rest py/bin/py.test py/bin/pytest.cmd -py/bin/win32/py.cleanup.cmd -py/bin/win32/py.countloc.cmd -py/bin/win32/py.lookup.cmd -py/bin/win32/py.rest.cmd -py/bin/win32/py.test.cmd py/builtin/__init__.py py/builtin/enumerate.py py/builtin/exception.py Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 15:02:50 2008 @@ -3,7 +3,7 @@ svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57348 - autogenerated by gensetup.py, 57350, Sun Aug 17 15:00:37 2008 CET + autogenerated by gensetup.py, 57350, Sun Aug 17 15:03:13 2008 CET """ import os, sys from distutils.core import setup, Extension @@ -164,7 +164,7 @@ class my_install_scripts(install_scripts): def run(self): install_scripts.run(self) - print self.outfiles + #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) if "." in basename: @@ -179,7 +179,7 @@ os.remove(newname) f = open(newname, 'w') f.write("@echo off\n") - f.write('python "~dp0\%s" %%*\n' % basename) + f.write('python "~dp0\%s" %%*\n' % newbasename) f.close() cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Sun Aug 17 15:07:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 15:07:04 +0200 (CEST) Subject: [py-svn] r57353 - py/release/0.9.x Message-ID: <20080817130704.82F0C169DB4@codespeak.net> Author: hpk Date: Sun Aug 17 15:07:03 2008 New Revision: 57353 Modified: py/release/0.9.x/setup.py Log: fixing ... Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 15:07:03 2008 @@ -3,7 +3,7 @@ svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57348 - autogenerated by gensetup.py, 57350, Sun Aug 17 15:03:13 2008 CET + autogenerated by gensetup.py, 57350, Sun Aug 17 15:07:38 2008 CET """ import os, sys from distutils.core import setup, Extension @@ -179,7 +179,7 @@ os.remove(newname) f = open(newname, 'w') f.write("@echo off\n") - f.write('python "~dp0\%s" %%*\n' % newbasename) + f.write('python "%%~dp0\%s" %%*\n' % newbasename) f.close() cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Sun Aug 17 17:02:59 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 17:02:59 +0200 (CEST) Subject: [py-svn] r57357 - py/build Message-ID: <20080817150259.24798168539@codespeak.net> Author: hpk Date: Sun Aug 17 17:02:57 2008 New Revision: 57357 Modified: py/build/winpath.py Log: fix helper script Modified: py/build/winpath.py ============================================================================== --- py/build/winpath.py (original) +++ py/build/winpath.py Sun Aug 17 17:02:57 2008 @@ -5,7 +5,7 @@ class my_install_scripts(install_scripts): def run(self): install_scripts.run(self) - print self.outfiles + #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) if "." in basename: @@ -20,7 +20,7 @@ os.remove(newname) f = open(newname, 'w') f.write("@echo off\n") - f.write('python "~dp0\%s" %%*\n' % basename) + f.write('python "%%~dp0\%s" %%*\n' % newbasename) f.close() cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Sun Aug 17 17:24:34 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 17:24:34 +0200 (CEST) Subject: [py-svn] r57358 - in py: build release/0.9.x/py/bin trunk/py/bin trunk/py/bin/win32 Message-ID: <20080817152434.BA96E169EB0@codespeak.net> Author: hpk Date: Sun Aug 17 17:24:29 2008 New Revision: 57358 Added: py/build/_docgen.py - copied unchanged from r57348, py/release/0.9.x/py/bin/_docgen.py py/build/_makepyrelease.py - copied unchanged from r57348, py/release/0.9.x/py/bin/_makepyrelease.py py/build/_update_website.py - copied unchanged from r57348, py/release/0.9.x/py/bin/_update_website.py Removed: py/release/0.9.x/py/bin/_docgen.py py/release/0.9.x/py/bin/_makepyrelease.py py/release/0.9.x/py/bin/_update_website.py py/release/0.9.x/py/bin/pytest.cmd py/trunk/py/bin/_docgen.py py/trunk/py/bin/_makepyrelease.py py/trunk/py/bin/_update_website.py py/trunk/py/bin/pytest.cmd py/trunk/py/bin/win32/ Modified: py/build/gensetup.py Log: move more scripts to build directory Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 17:24:29 2008 @@ -15,9 +15,10 @@ class SetupWriter(object): EXCLUDES = ("MANIFEST.in",) - def __init__(self, wcbasedir, pkg): + def __init__(self, wcbasedir, pkg, setuptools=False): self.wcbasedir = wcbasedir self.basedir = wcbasedir.localpath + self.setuptools = setuptools assert self.basedir.check() self.pkg = pkg self.meta = pkg.__pkg__ @@ -66,9 +67,11 @@ autogenerated by %(toolname)s, %(toolrevision)s, %(tooltime)s """ import os, sys - from distutils.core import setup, Extension - from distutils import sysconfig ''' % locals()) + if self.setuptools: + self.lines.append("from setuptools import setup, Extension") + else: + self.lines.append("from distutils.core import setup, Extension") def setup_trailer(self): self.append(''' @@ -211,5 +214,5 @@ if not wcbasedir.check(versioned=True): error("not a svn working copy:%s" % basedir) - writer = SetupWriter(wcbasedir, py) + writer = SetupWriter(wcbasedir, py, setuptools=True) writer.write_all() Deleted: /py/release/0.9.x/py/bin/_docgen.py ============================================================================== --- /py/release/0.9.x/py/bin/_docgen.py Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -""" quick tool to get documentation + apigen docs generated - - given a certain targetpath, apigen docs will be placed in 'apigen', - - user can choose to only build either docs or apigen docs: in this case, - the navigation bar will be adjusted -""" - -from _findpy import py -import py -pypath = py.__pkg__.getpath() - -def run_tests(path, envvars='', args=''): - pytestpath = pypath.join('bin/py.test') - cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % - (pypath.dirpath(), envvars, pytestpath, args, path)) - print cmd - py.process.cmdexec(cmd) - -def build_apigen_docs(targetpath, testargs=''): - run_tests(pypath, - 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( - targetpath,), - '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) - -def build_docs(targetpath, testargs): - docpath = pypath.join('doc') - run_tests(docpath, '', - testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,)) - docpath.copy(targetpath) - -def build_nav(targetpath, docs=True, api=True): - pass - -def build(targetpath, docs=True, api=True, testargs=''): - targetpath.ensure(dir=True) - if docs: - print 'building docs' - build_docs(targetpath, testargs) - if api: - print 'building api' - build_apigen_docs(targetpath, testargs) - -if __name__ == '__main__': - import sys - if len(sys.argv) == 1: - print 'usage: %s [options]' - print - print ' targetdir: a path to a directory (created if it doesn\'t' - print ' exist) where the docs are put' - print ' options: options passed to py.test when running the tests' - sys.exit(1) - targetpath = py.path.local(sys.argv[1]) - args = ' '.join(sys.argv[2:]) - build(targetpath, True, True, args) - Deleted: /py/release/0.9.x/py/bin/_makepyrelease.py ============================================================================== --- /py/release/0.9.x/py/bin/_makepyrelease.py Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,184 +0,0 @@ -#!/usr/bin/env python - -from _findpy import py -import sys - -pydir = py.path.local(py.__file__).dirpath() -rootdir = pydir.dirpath() - -def gen_manifest(): - pywc = py.path.svnwc(pydir) - status = pywc.status(rec=True) - #assert not status.modified - #assert not status.deleted - #assert not status.added - versioned = dict([(x.localpath,1) for x in status.allpath()]) - - l = [] - for x in rootdir.visit(None, lambda x: x.basename != '.svn'): - if x.check(file=1): - names = [y.basename for y in x.parts()] - if '.svn' in names: - l.append(x) - elif x in versioned: - l.append(x) - l.append(rootdir / "setup.py") - l = [x.relto(rootdir) for x in l] - l.append("") - s = "\n".join(l) - return s - -def trace(arg): - lines = str(arg).split('\n') - prefix = "[trace] " - prefix = "* " - indent = len(prefix) - ispace = " " * indent - lines = [ispace + line for line in lines] - if lines: - lines[0] = prefix + lines[0][indent:] - for line in lines: - print >>py.std.sys.stdout, line - -def make_distfiles(tmpdir): - """ return distdir with tar.gz and zipfile. """ - manifest = tmpdir.join('MANIFEST') - trace("generating %s" %(manifest,)) - content = gen_manifest() - manifest.write(content) - trace("wrote %d files into manifest file" %len(content.split('\n'))) - - distdir = tmpdir.ensure('dist', dir=1) - oldir = rootdir.chdir() - try: - from py.__.misc._dist import setup - trace("invoking sdist, generating into %s" % (distdir,)) - setup(py, script_name="setup.py", - script_args=('-q', 'sdist', '--no-prune', - '-m', str(manifest), - '--formats=gztar,zip', - '-d', str(distdir))) - setup(py, script_name="setup.py", - script_args=('-q', 'bdist_wininst', - #'-m', str(manifest), - '-d', str(distdir))) - finally: - oldir.chdir() - return distdir - - -def pytest(unpacked): - trace("py-testing %s" % unpacked) - old = unpacked.chdir() - try: - import os - os.system("python py/bin/py.test py") - finally: - old.chdir() - -def unpackremotetar(tmpdir, strurl): - import tarfile, urllib - f = urllib.urlopen(strurl) - basename = strurl.split('/')[-1] - target = tmpdir.join(basename) - trace("downloading %r to %s" %(strurl, target,)) - target.write(f.read()) - - trace("extracting to %s" %(target,)) - old = tmpdir.chdir() - try: - py.process.cmdexec("tar zxf %s" %(target,)) - finally: - old.chdir() - prefix = '.tar.gz' - assert basename.endswith(prefix) - stripped = basename[:-len(prefix)] - unpacked = tmpdir.join(stripped) - assert unpacked - return unpacked - -def checksvnworks(unpacked): - pywc = py.path.svnwc(unpacked.join('py')) - trace("checking versioning works: %s" %(pywc,)) - status = pywc.status(rec=True) - assert not status.modified - assert not status.deleted - assert not status.unknown - -def pytest_remote(address, url): - gw = py.execnet.SshGateway(address) - basename = url[url.rfind('/')+1:] - purebasename = basename[:-len('.tar.gz')] - - def mytrace(x, l=[]): - l.append(x) - if x.endswith('\n'): - trace("".join(l)) - l[:] = [] - - channel = gw.remote_exec(stdout=mytrace, stderr=sys.stderr, source=""" - url = %(url)r - basename = %(basename)r - purebasename = %(purebasename)r - import os, urllib - f = urllib.urlopen(url) - print "reading from", url - s = f.read() - f.close() - f = open(basename, 'w') - f.write(s) - f.close() - if os.path.exists(purebasename): - import shutil - shutil.rmtree(purebasename) - os.system("tar zxf %%s" %% (basename,)) - print "unpacked", purebasename - os.chdir(purebasename) - print "testing at %(address)s ..." - #os.system("python py/bin/py.test py") - import commands - status, output = commands.getstatusoutput("python py/bin/py.test py") - #print output - print "status:", status - - """ % locals()) - channel.waitclose(200.0) - -if __name__ == '__main__': - py.magic.invoke(assertion=True) - version = py.std.sys.argv[1] - assert py.__pkg__.version == version, ( - "py package has version %s\nlocation: %s" % - (py.__pkg__.version, pydir)) - - tmpdir = py.path.local.get_temproot().join('makepyrelease-%s' % version) - if tmpdir.check(): - trace("removing %s" %(tmpdir,)) - tmpdir.remove() - tmpdir.mkdir() - trace("using tmpdir %s" %(tmpdir,)) - - distdir = make_distfiles(tmpdir) - targz = distdir.join('py-%s.tar.gz' % version) - zip = distdir.join('py-%s.zip' % version) - files = distdir.listdir() - for fn in files: - assert fn.check(file=1) - - remotedir = 'codespeak.net://www/codespeak.net/htdocs/download/py/' - source = distdir # " ".join([str(x) for x in files]) - trace("rsyncing %(source)s to %(remotedir)s" % locals()) - py.process.cmdexec("rsync -avz %(source)s/ %(remotedir)s" % locals()) - - ddir = tmpdir.ensure('download', dir=1) - URL = py.__pkg__.download_url # 'http://codespeak.net/download/py/' - unpacked = unpackremotetar(ddir, URL) - assert unpacked == ddir.join("py-%s" % (version,)) - - #checksvnworks(unpacked) - #pytest(unpacked) - - pytest_remote('test at codespeak.net', py.__pkg__.download_url) - - - Deleted: /py/release/0.9.x/py/bin/_update_website.py ============================================================================== --- /py/release/0.9.x/py/bin/_update_website.py Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,89 +0,0 @@ -#!/usr/bin/env python - -""" run py.test with the --apigen option and rsync the results to a host - - rsyncs the whole package (with all the ReST docs converted to HTML) as well - as the apigen docs to a given remote host and path -""" -from _findpy import py -import py -import sys - -def rsync(pkgpath, apidocspath, gateway, remotepath): - """ copy the code and docs to the remote host """ - # copy to a temp dir first, even though both paths (normally) share the - # same parent dir, that may contain other stuff that we don't want to - # copy... - tempdir = py.test.ensuretemp('update_website_rsync_temp') - pkgpath.copy(tempdir.ensure(pkgpath.basename, dir=True)) - apidocspath.copy(tempdir.ensure(apidocspath.basename, dir=True)) - - rs = py.execnet.RSync(tempdir) - rs.add_target(gateway, remotepath, delete=True) - rs.send() - -def run_tests(pkgpath, apigenpath, args='', captureouterr=False): - """ run the unit tests and build the docs """ - pypath = py.__pkg__.getpath() - pytestpath = pypath.join('bin/py.test') - # XXX this would need a Windows specific version if we want to allow - # running this script on that platform, but currently --apigen doesn't - # work there anyway... - apigenscript = pkgpath.join('apigen/apigen.py') # XXX be more general here? - if not apigenscript.check(file=True): - apigenscript = pypath.join('apigen/apigen.py') - cmd = ('APIGENPATH="%s" PYTHONPATH="%s:%s" python ' - '"%s" %s --apigen="%s" "%s"' % (apigenpath, pypath.dirpath(), - pkgpath.dirpath(), pytestpath, - args, apigenscript, - pkgpath)) - if captureouterr: - cmd += ' > /dev/null 2>&1' - try: - output = py.process.cmdexec(cmd) - except py.error.Error, e: - return e.err or str(e) - return None - -def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=False): - print 'running tests' - errors = run_tests(pkgpath, apidocspath, args) - if errors: - print >>sys.stderr, \ - 'Errors while running the unit tests: %s' % (errors,) - if not ignorefail: - print >>sys.stderr, ('if you want to continue the update ' - 'regardless of failures, use --ignorefail') - sys.exit(1) - - print 'rsyncing' - gateway = py.execnet.SshGateway(rhost) - errors = rsync(pkgpath, apidocspath, gateway, rpath) - if errors: - print >>sys.stderr, 'Errors while rsyncing: %s' - sys.exit(1) - -if __name__ == '__main__': - args = sys.argv[1:] - if '--help' in args or '-h' in args: - print 'usage: %s [options]' - print - print 'run the py lib tests and update the py lib website' - print 'options:' - print ' --ignorefail: ignore errors in the unit tests and always' - print ' try to rsync' - print ' --help: show this message' - print - print 'any additional arguments are passed on as-is to the py.test' - print 'child process' - sys.exit() - ignorefail = False - if '--ignorefail' in args: - args.remove('--ignorefail') - ignorefail = True - args = ' '.join(sys.argv[1:]) - pkgpath = py.__pkg__.getpath() - apidocspath = pkgpath.dirpath().join('apigen') - main(pkgpath, apidocspath, 'codespeak.net', - '/home/guido/rsynctests', args, ignorefail) - Deleted: /py/release/0.9.x/py/bin/pytest.cmd ============================================================================== --- /py/release/0.9.x/py/bin/pytest.cmd Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,3 +0,0 @@ - at echo off -python "%~dp0\py.test" %* - Deleted: /py/trunk/py/bin/_docgen.py ============================================================================== --- /py/trunk/py/bin/_docgen.py Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -""" quick tool to get documentation + apigen docs generated - - given a certain targetpath, apigen docs will be placed in 'apigen', - - user can choose to only build either docs or apigen docs: in this case, - the navigation bar will be adjusted -""" - -from _findpy import py -import py -pypath = py.__pkg__.getpath() - -def run_tests(path, envvars='', args=''): - pytestpath = pypath.join('bin/py.test') - cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % - (pypath.dirpath(), envvars, pytestpath, args, path)) - print cmd - py.process.cmdexec(cmd) - -def build_apigen_docs(targetpath, testargs=''): - run_tests(pypath, - 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( - targetpath,), - '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) - -def build_docs(targetpath, testargs): - docpath = pypath.join('doc') - run_tests(docpath, '', - testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,)) - docpath.copy(targetpath) - -def build_nav(targetpath, docs=True, api=True): - pass - -def build(targetpath, docs=True, api=True, testargs=''): - targetpath.ensure(dir=True) - if docs: - print 'building docs' - build_docs(targetpath, testargs) - if api: - print 'building api' - build_apigen_docs(targetpath, testargs) - -if __name__ == '__main__': - import sys - if len(sys.argv) == 1: - print 'usage: %s [options]' - print - print ' targetdir: a path to a directory (created if it doesn\'t' - print ' exist) where the docs are put' - print ' options: options passed to py.test when running the tests' - sys.exit(1) - targetpath = py.path.local(sys.argv[1]) - args = ' '.join(sys.argv[2:]) - build(targetpath, True, True, args) - Deleted: /py/trunk/py/bin/_makepyrelease.py ============================================================================== --- /py/trunk/py/bin/_makepyrelease.py Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,184 +0,0 @@ -#!/usr/bin/env python - -from _findpy import py -import sys - -pydir = py.path.local(py.__file__).dirpath() -rootdir = pydir.dirpath() - -def gen_manifest(): - pywc = py.path.svnwc(pydir) - status = pywc.status(rec=True) - #assert not status.modified - #assert not status.deleted - #assert not status.added - versioned = dict([(x.localpath,1) for x in status.allpath()]) - - l = [] - for x in rootdir.visit(None, lambda x: x.basename != '.svn'): - if x.check(file=1): - names = [y.basename for y in x.parts()] - if '.svn' in names: - l.append(x) - elif x in versioned: - l.append(x) - l.append(rootdir / "setup.py") - l = [x.relto(rootdir) for x in l] - l.append("") - s = "\n".join(l) - return s - -def trace(arg): - lines = str(arg).split('\n') - prefix = "[trace] " - prefix = "* " - indent = len(prefix) - ispace = " " * indent - lines = [ispace + line for line in lines] - if lines: - lines[0] = prefix + lines[0][indent:] - for line in lines: - print >>py.std.sys.stdout, line - -def make_distfiles(tmpdir): - """ return distdir with tar.gz and zipfile. """ - manifest = tmpdir.join('MANIFEST') - trace("generating %s" %(manifest,)) - content = gen_manifest() - manifest.write(content) - trace("wrote %d files into manifest file" %len(content.split('\n'))) - - distdir = tmpdir.ensure('dist', dir=1) - oldir = rootdir.chdir() - try: - from py.__.misc._dist import setup - trace("invoking sdist, generating into %s" % (distdir,)) - setup(py, script_name="setup.py", - script_args=('-q', 'sdist', '--no-prune', - '-m', str(manifest), - '--formats=gztar,zip', - '-d', str(distdir))) - setup(py, script_name="setup.py", - script_args=('-q', 'bdist_wininst', - #'-m', str(manifest), - '-d', str(distdir))) - finally: - oldir.chdir() - return distdir - - -def pytest(unpacked): - trace("py-testing %s" % unpacked) - old = unpacked.chdir() - try: - import os - os.system("python py/bin/py.test py") - finally: - old.chdir() - -def unpackremotetar(tmpdir, strurl): - import tarfile, urllib - f = urllib.urlopen(strurl) - basename = strurl.split('/')[-1] - target = tmpdir.join(basename) - trace("downloading %r to %s" %(strurl, target,)) - target.write(f.read()) - - trace("extracting to %s" %(target,)) - old = tmpdir.chdir() - try: - py.process.cmdexec("tar zxf %s" %(target,)) - finally: - old.chdir() - prefix = '.tar.gz' - assert basename.endswith(prefix) - stripped = basename[:-len(prefix)] - unpacked = tmpdir.join(stripped) - assert unpacked - return unpacked - -def checksvnworks(unpacked): - pywc = py.path.svnwc(unpacked.join('py')) - trace("checking versioning works: %s" %(pywc,)) - status = pywc.status(rec=True) - assert not status.modified - assert not status.deleted - assert not status.unknown - -def pytest_remote(address, url): - gw = py.execnet.SshGateway(address) - basename = url[url.rfind('/')+1:] - purebasename = basename[:-len('.tar.gz')] - - def mytrace(x, l=[]): - l.append(x) - if x.endswith('\n'): - trace("".join(l)) - l[:] = [] - - channel = gw.remote_exec(stdout=mytrace, stderr=sys.stderr, source=""" - url = %(url)r - basename = %(basename)r - purebasename = %(purebasename)r - import os, urllib - f = urllib.urlopen(url) - print "reading from", url - s = f.read() - f.close() - f = open(basename, 'w') - f.write(s) - f.close() - if os.path.exists(purebasename): - import shutil - shutil.rmtree(purebasename) - os.system("tar zxf %%s" %% (basename,)) - print "unpacked", purebasename - os.chdir(purebasename) - print "testing at %(address)s ..." - #os.system("python py/bin/py.test py") - import commands - status, output = commands.getstatusoutput("python py/bin/py.test py") - #print output - print "status:", status - - """ % locals()) - channel.waitclose(200.0) - -if __name__ == '__main__': - py.magic.invoke(assertion=True) - version = py.std.sys.argv[1] - assert py.__pkg__.version == version, ( - "py package has version %s\nlocation: %s" % - (py.__pkg__.version, pydir)) - - tmpdir = py.path.local.get_temproot().join('makepyrelease-%s' % version) - if tmpdir.check(): - trace("removing %s" %(tmpdir,)) - tmpdir.remove() - tmpdir.mkdir() - trace("using tmpdir %s" %(tmpdir,)) - - distdir = make_distfiles(tmpdir) - targz = distdir.join('py-%s.tar.gz' % version) - zip = distdir.join('py-%s.zip' % version) - files = distdir.listdir() - for fn in files: - assert fn.check(file=1) - - remotedir = 'codespeak.net://www/codespeak.net/htdocs/download/py/' - source = distdir # " ".join([str(x) for x in files]) - trace("rsyncing %(source)s to %(remotedir)s" % locals()) - py.process.cmdexec("rsync -avz %(source)s/ %(remotedir)s" % locals()) - - ddir = tmpdir.ensure('download', dir=1) - URL = py.__pkg__.download_url # 'http://codespeak.net/download/py/' - unpacked = unpackremotetar(ddir, URL) - assert unpacked == ddir.join("py-%s" % (version,)) - - #checksvnworks(unpacked) - #pytest(unpacked) - - pytest_remote('test at codespeak.net', py.__pkg__.download_url) - - - Deleted: /py/trunk/py/bin/_update_website.py ============================================================================== --- /py/trunk/py/bin/_update_website.py Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,89 +0,0 @@ -#!/usr/bin/env python - -""" run py.test with the --apigen option and rsync the results to a host - - rsyncs the whole package (with all the ReST docs converted to HTML) as well - as the apigen docs to a given remote host and path -""" -from _findpy import py -import py -import sys - -def rsync(pkgpath, apidocspath, gateway, remotepath): - """ copy the code and docs to the remote host """ - # copy to a temp dir first, even though both paths (normally) share the - # same parent dir, that may contain other stuff that we don't want to - # copy... - tempdir = py.test.ensuretemp('update_website_rsync_temp') - pkgpath.copy(tempdir.ensure(pkgpath.basename, dir=True)) - apidocspath.copy(tempdir.ensure(apidocspath.basename, dir=True)) - - rs = py.execnet.RSync(tempdir) - rs.add_target(gateway, remotepath, delete=True) - rs.send() - -def run_tests(pkgpath, apigenpath, args='', captureouterr=False): - """ run the unit tests and build the docs """ - pypath = py.__pkg__.getpath() - pytestpath = pypath.join('bin/py.test') - # XXX this would need a Windows specific version if we want to allow - # running this script on that platform, but currently --apigen doesn't - # work there anyway... - apigenscript = pkgpath.join('apigen/apigen.py') # XXX be more general here? - if not apigenscript.check(file=True): - apigenscript = pypath.join('apigen/apigen.py') - cmd = ('APIGENPATH="%s" PYTHONPATH="%s:%s" python ' - '"%s" %s --apigen="%s" "%s"' % (apigenpath, pypath.dirpath(), - pkgpath.dirpath(), pytestpath, - args, apigenscript, - pkgpath)) - if captureouterr: - cmd += ' > /dev/null 2>&1' - try: - output = py.process.cmdexec(cmd) - except py.error.Error, e: - return e.err or str(e) - return None - -def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=False): - print 'running tests' - errors = run_tests(pkgpath, apidocspath, args) - if errors: - print >>sys.stderr, \ - 'Errors while running the unit tests: %s' % (errors,) - if not ignorefail: - print >>sys.stderr, ('if you want to continue the update ' - 'regardless of failures, use --ignorefail') - sys.exit(1) - - print 'rsyncing' - gateway = py.execnet.SshGateway(rhost) - errors = rsync(pkgpath, apidocspath, gateway, rpath) - if errors: - print >>sys.stderr, 'Errors while rsyncing: %s' - sys.exit(1) - -if __name__ == '__main__': - args = sys.argv[1:] - if '--help' in args or '-h' in args: - print 'usage: %s [options]' - print - print 'run the py lib tests and update the py lib website' - print 'options:' - print ' --ignorefail: ignore errors in the unit tests and always' - print ' try to rsync' - print ' --help: show this message' - print - print 'any additional arguments are passed on as-is to the py.test' - print 'child process' - sys.exit() - ignorefail = False - if '--ignorefail' in args: - args.remove('--ignorefail') - ignorefail = True - args = ' '.join(sys.argv[1:]) - pkgpath = py.__pkg__.getpath() - apidocspath = pkgpath.dirpath().join('apigen') - main(pkgpath, apidocspath, 'codespeak.net', - '/home/guido/rsynctests', args, ignorefail) - Deleted: /py/trunk/py/bin/pytest.cmd ============================================================================== --- /py/trunk/py/bin/pytest.cmd Sun Aug 17 17:24:29 2008 +++ (empty file) @@ -1,3 +0,0 @@ - at echo off -python "%~dp0\py.test" %* - From hpk at codespeak.net Sun Aug 17 17:27:39 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 17:27:39 +0200 (CEST) Subject: [py-svn] r57359 - py/release/0.9.x Message-ID: <20080817152739.8EE99169EBC@codespeak.net> Author: hpk Date: Sun Aug 17 17:27:37 2008 New Revision: 57359 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/setup.py Log: see how things work with setuptools Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 17:27:37 2008 @@ -58,16 +58,12 @@ py/apigen/tracer/testing/test_model.py py/apigen/tracer/testing/test_package.py py/apigen/tracer/tracer.py -py/bin/_docgen.py py/bin/_findpy.py -py/bin/_makepyrelease.py -py/bin/_update_website.py py/bin/py.cleanup py/bin/py.countloc py/bin/py.lookup py/bin/py.rest py/bin/py.test -py/bin/pytest.cmd py/builtin/__init__.py py/builtin/enumerate.py py/builtin/exception.py Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 17:27:37 2008 @@ -3,12 +3,11 @@ svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57348 - autogenerated by gensetup.py, 57350, Sun Aug 17 15:07:38 2008 CET + autogenerated by gensetup.py, 57358, Sun Aug 17 17:26:59 2008 CET """ import os, sys -from distutils.core import setup, Extension -from distutils import sysconfig +from setuptools import setup, Extension def main(): setup(cmdclass=cmdclass, name='py', @@ -96,7 +95,6 @@ 'bin/py.lookup', 'bin/py.rest', 'bin/py.test', - 'bin/pytest.cmd', 'c-extension/greenlet/README.txt', 'c-extension/greenlet/greenlet.c', 'c-extension/greenlet/greenlet.h', From hpk at codespeak.net Sun Aug 17 19:08:40 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 19:08:40 +0200 (CEST) Subject: [py-svn] r57361 - py/release/0.9.x Message-ID: <20080817170840.05276169EDA@codespeak.net> Author: hpk Date: Sun Aug 17 19:08:39 2008 New Revision: 57361 Modified: py/release/0.9.x/ (props changed) py/release/0.9.x/setup.py Log: another try Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 19:08:39 2008 @@ -1,9 +1,9 @@ """ setup file for 'py' package based on: - svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57348 + svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57358 - autogenerated by gensetup.py, 57358, Sun Aug 17 17:26:59 2008 CET + autogenerated by gensetup.py, 57358, Sun Aug 17 17:52:51 2008 CET """ import os, sys @@ -154,6 +154,7 @@ 'rest/testing/data/tocdepth.rst2pdfconfig', 'test/rsession/webdata/index.html', 'test/rsession/webdata/source.js']}, + zip_safe=False, ) # on windows we need to hack up the to-be-installed scripts @@ -165,7 +166,7 @@ #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) - if "." in basename: + if "." in basename and basename != "_findpy.py": #print "tackling", fn newbasename = basename.replace(".", "_") newfn = os.path.join(os.path.dirname(fn), newbasename) From hpk at codespeak.net Sun Aug 17 19:18:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 19:18:20 +0200 (CEST) Subject: [py-svn] r57362 - py/release/0.9.x Message-ID: <20080817171820.64BF4169E9F@codespeak.net> Author: hpk Date: Sun Aug 17 19:18:19 2008 New Revision: 57362 Added: py/release/0.9.x/ez_setup.py Modified: py/release/0.9.x/setup.py Log: make it easy to use pure distutils if one wants. Added: py/release/0.9.x/ez_setup.py ============================================================================== --- (empty file) +++ py/release/0.9.x/ez_setup.py Sun Aug 17 19:18:19 2008 @@ -0,0 +1,272 @@ +#!python +"""Bootstrap setuptools installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import sys +DEFAULT_VERSION = "0.6c8" +DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] + +md5_data = { + 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', + 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', + 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', + 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', + 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', + 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', + 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', + 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', + 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', + 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', + 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', + 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', + 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', + 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', + 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', + 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', + 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', + 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', + 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', + 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', + 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', + 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', + 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', + 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', + 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', + 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', + 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', + 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', + 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', + 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', +} + +import sys, os + +def _validate_md5(egg_name, data): + if egg_name in md5_data: + from md5 import md5 + digest = md5(data).hexdigest() + if digest != md5_data[egg_name]: + print >>sys.stderr, ( + "md5 validation of %s failed! (Possible download problem?)" + % egg_name + ) + sys.exit(2) + return data + + +def use_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + download_delay=15 +): + """Automatically find/download setuptools and make it available on sys.path + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end with + a '/'). `to_dir` is the directory where setuptools will be downloaded, if + it is not already available. If `download_delay` is specified, it should + be the number of seconds that will be paused before initiating a download, + should one be required. If an older version of setuptools is installed, + this routine will print a message to ``sys.stderr`` and raise SystemExit in + an attempt to abort the calling script. + """ + was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules + def do_download(): + egg = download_setuptools(version, download_base, to_dir, download_delay) + sys.path.insert(0, egg) + import setuptools; setuptools.bootstrap_install_from = egg + try: + import pkg_resources + except ImportError: + return do_download() + try: + pkg_resources.require("setuptools>="+version); return + except pkg_resources.VersionConflict, e: + if was_imported: + print >>sys.stderr, ( + "The required version of setuptools (>=%s) is not available, and\n" + "can't be installed while this script is running. Please install\n" + " a more recent version first, using 'easy_install -U setuptools'." + "\n\n(Currently using %r)" + ) % (version, e.args[0]) + sys.exit(2) + else: + del pkg_resources, sys.modules['pkg_resources'] # reload ok + return do_download() + except pkg_resources.DistributionNotFound: + return do_download() + +def download_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + delay = 15 +): + """Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download attempt. + """ + import urllib2, shutil + egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) + url = download_base + egg_name + saveto = os.path.join(to_dir, egg_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + from distutils import log + if delay: + log.warn(""" +--------------------------------------------------------------------------- +This script requires setuptools version %s to run (even to display +help). I will attempt to download it for you (from +%s), but +you may need to enable firewall access for this script first. +I will start the download in %d seconds. + +(Note: if this machine does not have network access, please obtain the file + + %s + +and place it in this directory before rerunning this script.) +---------------------------------------------------------------------------""", + version, download_base, delay, url + ); from time import sleep; sleep(delay) + log.warn("Downloading %s", url) + src = urllib2.urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = _validate_md5(egg_name, src.read()) + dst = open(saveto,"wb"); dst.write(data) + finally: + if src: src.close() + if dst: dst.close() + return os.path.realpath(saveto) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + try: + import setuptools + except ImportError: + egg = None + try: + egg = download_setuptools(version, delay=0) + sys.path.insert(0,egg) + from setuptools.command.easy_install import main + return main(list(argv)+[egg]) # we're done here + finally: + if egg and os.path.exists(egg): + os.unlink(egg) + else: + if setuptools.__version__ == '0.0.1': + print >>sys.stderr, ( + "You have an obsolete version of setuptools installed. Please\n" + "remove it from your system entirely before rerunning this script." + ) + sys.exit(2) + + req = "setuptools>="+version + import pkg_resources + try: + pkg_resources.require(req) + except pkg_resources.VersionConflict: + try: + from setuptools.command.easy_install import main + except ImportError: + from easy_install import main + main(list(argv)+[download_setuptools(delay=0)]) + sys.exit(0) # try to force an exit + else: + if argv: + from setuptools.command.easy_install import main + main(argv) + else: + print "Setuptools version",version,"or greater has been installed." + print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' + +def update_md5(filenames): + """Update our built-in md5 registry""" + + import re + from md5 import md5 + + for name in filenames: + base = os.path.basename(name) + f = open(name,'rb') + md5_data[base] = md5(f.read()).hexdigest() + f.close() + + data = [" %r: %r,\n" % it for it in md5_data.items()] + data.sort() + repl = "".join(data) + + import inspect + srcfile = inspect.getsourcefile(sys.modules[__name__]) + f = open(srcfile, 'rb'); src = f.read(); f.close() + + match = re.search("\nmd5_data = {\n([^}]+)}", src) + if not match: + print >>sys.stderr, "Internal error!" + sys.exit(2) + + src = src[:match.start(1)] + repl + src[match.end(1):] + f = open(srcfile,'w') + f.write(src) + f.close() + + +if __name__=='__main__': + if len(sys.argv)>2 and sys.argv[1]=='--md5update': + update_md5(sys.argv[2:]) + else: + main(sys.argv[1:]) + + + + + Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 19:18:19 2008 @@ -1,13 +1,19 @@ """ setup file for 'py' package based on: - svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57358 + svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57361 - autogenerated by gensetup.py, 57358, Sun Aug 17 17:52:51 2008 CET + autogenerated by gensetup.py, 57358, Sun Aug 17 19:17:41 2008 CET """ import os, sys -from setuptools import setup, Extension +if 1: # set to zero if you want plain distutils + import ez_setup + ez_setup.use_setuptools() + from setuptools import setup, Extension +else: + from distutils.core import setup, Extension + def main(): setup(cmdclass=cmdclass, name='py', From hpk at codespeak.net Sun Aug 17 19:22:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 19:22:26 +0200 (CEST) Subject: [py-svn] r57363 - py/build Message-ID: <20080817172226.A2FD5169EDE@codespeak.net> Author: hpk Date: Sun Aug 17 19:22:25 2008 New Revision: 57363 Added: py/build/docgen.py - copied unchanged from r57362, py/build/_docgen.py py/build/makepyrelease.py - copied unchanged from r57362, py/build/_makepyrelease.py Removed: py/build/_docgen.py py/build/_makepyrelease.py Modified: py/build/ (props changed) py/build/gensetup.py (contents, props changed) py/build/winpath.py (contents, props changed) Log: shfiting things around, make alternative to setuptools easy Deleted: /py/build/_docgen.py ============================================================================== --- /py/build/_docgen.py Sun Aug 17 19:22:25 2008 +++ (empty file) @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -""" quick tool to get documentation + apigen docs generated - - given a certain targetpath, apigen docs will be placed in 'apigen', - - user can choose to only build either docs or apigen docs: in this case, - the navigation bar will be adjusted -""" - -from _findpy import py -import py -pypath = py.__pkg__.getpath() - -def run_tests(path, envvars='', args=''): - pytestpath = pypath.join('bin/py.test') - cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % - (pypath.dirpath(), envvars, pytestpath, args, path)) - print cmd - py.process.cmdexec(cmd) - -def build_apigen_docs(targetpath, testargs=''): - run_tests(pypath, - 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( - targetpath,), - '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) - -def build_docs(targetpath, testargs): - docpath = pypath.join('doc') - run_tests(docpath, '', - testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,)) - docpath.copy(targetpath) - -def build_nav(targetpath, docs=True, api=True): - pass - -def build(targetpath, docs=True, api=True, testargs=''): - targetpath.ensure(dir=True) - if docs: - print 'building docs' - build_docs(targetpath, testargs) - if api: - print 'building api' - build_apigen_docs(targetpath, testargs) - -if __name__ == '__main__': - import sys - if len(sys.argv) == 1: - print 'usage: %s [options]' - print - print ' targetdir: a path to a directory (created if it doesn\'t' - print ' exist) where the docs are put' - print ' options: options passed to py.test when running the tests' - sys.exit(1) - targetpath = py.path.local(sys.argv[1]) - args = ' '.join(sys.argv[2:]) - build(targetpath, True, True, args) - Deleted: /py/build/_makepyrelease.py ============================================================================== --- /py/build/_makepyrelease.py Sun Aug 17 19:22:25 2008 +++ (empty file) @@ -1,184 +0,0 @@ -#!/usr/bin/env python - -from _findpy import py -import sys - -pydir = py.path.local(py.__file__).dirpath() -rootdir = pydir.dirpath() - -def gen_manifest(): - pywc = py.path.svnwc(pydir) - status = pywc.status(rec=True) - #assert not status.modified - #assert not status.deleted - #assert not status.added - versioned = dict([(x.localpath,1) for x in status.allpath()]) - - l = [] - for x in rootdir.visit(None, lambda x: x.basename != '.svn'): - if x.check(file=1): - names = [y.basename for y in x.parts()] - if '.svn' in names: - l.append(x) - elif x in versioned: - l.append(x) - l.append(rootdir / "setup.py") - l = [x.relto(rootdir) for x in l] - l.append("") - s = "\n".join(l) - return s - -def trace(arg): - lines = str(arg).split('\n') - prefix = "[trace] " - prefix = "* " - indent = len(prefix) - ispace = " " * indent - lines = [ispace + line for line in lines] - if lines: - lines[0] = prefix + lines[0][indent:] - for line in lines: - print >>py.std.sys.stdout, line - -def make_distfiles(tmpdir): - """ return distdir with tar.gz and zipfile. """ - manifest = tmpdir.join('MANIFEST') - trace("generating %s" %(manifest,)) - content = gen_manifest() - manifest.write(content) - trace("wrote %d files into manifest file" %len(content.split('\n'))) - - distdir = tmpdir.ensure('dist', dir=1) - oldir = rootdir.chdir() - try: - from py.__.misc._dist import setup - trace("invoking sdist, generating into %s" % (distdir,)) - setup(py, script_name="setup.py", - script_args=('-q', 'sdist', '--no-prune', - '-m', str(manifest), - '--formats=gztar,zip', - '-d', str(distdir))) - setup(py, script_name="setup.py", - script_args=('-q', 'bdist_wininst', - #'-m', str(manifest), - '-d', str(distdir))) - finally: - oldir.chdir() - return distdir - - -def pytest(unpacked): - trace("py-testing %s" % unpacked) - old = unpacked.chdir() - try: - import os - os.system("python py/bin/py.test py") - finally: - old.chdir() - -def unpackremotetar(tmpdir, strurl): - import tarfile, urllib - f = urllib.urlopen(strurl) - basename = strurl.split('/')[-1] - target = tmpdir.join(basename) - trace("downloading %r to %s" %(strurl, target,)) - target.write(f.read()) - - trace("extracting to %s" %(target,)) - old = tmpdir.chdir() - try: - py.process.cmdexec("tar zxf %s" %(target,)) - finally: - old.chdir() - prefix = '.tar.gz' - assert basename.endswith(prefix) - stripped = basename[:-len(prefix)] - unpacked = tmpdir.join(stripped) - assert unpacked - return unpacked - -def checksvnworks(unpacked): - pywc = py.path.svnwc(unpacked.join('py')) - trace("checking versioning works: %s" %(pywc,)) - status = pywc.status(rec=True) - assert not status.modified - assert not status.deleted - assert not status.unknown - -def pytest_remote(address, url): - gw = py.execnet.SshGateway(address) - basename = url[url.rfind('/')+1:] - purebasename = basename[:-len('.tar.gz')] - - def mytrace(x, l=[]): - l.append(x) - if x.endswith('\n'): - trace("".join(l)) - l[:] = [] - - channel = gw.remote_exec(stdout=mytrace, stderr=sys.stderr, source=""" - url = %(url)r - basename = %(basename)r - purebasename = %(purebasename)r - import os, urllib - f = urllib.urlopen(url) - print "reading from", url - s = f.read() - f.close() - f = open(basename, 'w') - f.write(s) - f.close() - if os.path.exists(purebasename): - import shutil - shutil.rmtree(purebasename) - os.system("tar zxf %%s" %% (basename,)) - print "unpacked", purebasename - os.chdir(purebasename) - print "testing at %(address)s ..." - #os.system("python py/bin/py.test py") - import commands - status, output = commands.getstatusoutput("python py/bin/py.test py") - #print output - print "status:", status - - """ % locals()) - channel.waitclose(200.0) - -if __name__ == '__main__': - py.magic.invoke(assertion=True) - version = py.std.sys.argv[1] - assert py.__pkg__.version == version, ( - "py package has version %s\nlocation: %s" % - (py.__pkg__.version, pydir)) - - tmpdir = py.path.local.get_temproot().join('makepyrelease-%s' % version) - if tmpdir.check(): - trace("removing %s" %(tmpdir,)) - tmpdir.remove() - tmpdir.mkdir() - trace("using tmpdir %s" %(tmpdir,)) - - distdir = make_distfiles(tmpdir) - targz = distdir.join('py-%s.tar.gz' % version) - zip = distdir.join('py-%s.zip' % version) - files = distdir.listdir() - for fn in files: - assert fn.check(file=1) - - remotedir = 'codespeak.net://www/codespeak.net/htdocs/download/py/' - source = distdir # " ".join([str(x) for x in files]) - trace("rsyncing %(source)s to %(remotedir)s" % locals()) - py.process.cmdexec("rsync -avz %(source)s/ %(remotedir)s" % locals()) - - ddir = tmpdir.ensure('download', dir=1) - URL = py.__pkg__.download_url # 'http://codespeak.net/download/py/' - unpacked = unpackremotetar(ddir, URL) - assert unpacked == ddir.join("py-%s" % (version,)) - - #checksvnworks(unpacked) - #pytest(unpacked) - - pytest_remote('test at codespeak.net', py.__pkg__.download_url) - - - Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 19:22:25 2008 @@ -68,10 +68,15 @@ """ import os, sys ''' % locals()) - if self.setuptools: - self.lines.append("from setuptools import setup, Extension") - else: - self.lines.append("from distutils.core import setup, Extension") + + self.append(""" + if 1: # set to zero if you want plain distutils + import ez_setup + ez_setup.use_setuptools() + from setuptools import setup, Extension + else: + from distutils.core import setup, Extension + """) def setup_trailer(self): self.append(''' @@ -108,6 +113,8 @@ self.append_pprint(indent, package_data=self.getpackagedata()) #self.append_pprint(indent, package_dir={'py': 'py'}) #self.append_pprint(indent, packages=self.getpackages()) + if self.setuptools: + self.append_pprint(indent, zip_safe=False) self.lines.append(indent[4:] + ")\n") Modified: py/build/winpath.py ============================================================================== --- py/build/winpath.py (original) +++ py/build/winpath.py Sun Aug 17 19:22:25 2008 @@ -8,7 +8,7 @@ #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) - if "." in basename: + if "." in basename and basename != "_findpy.py": #print "tackling", fn newbasename = basename.replace(".", "_") newfn = os.path.join(os.path.dirname(fn), newbasename) From hpk at codespeak.net Sun Aug 17 21:10:48 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 21:10:48 +0200 (CEST) Subject: [py-svn] r57365 - in py/release/0.9.x: . py py/misc/testing Message-ID: <20080817191048.9B791168523@codespeak.net> Author: hpk Date: Sun Aug 17 21:10:45 2008 New Revision: 57365 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/py/misc/testing/test_initpkg.py py/release/0.9.x/setup.py Log: avoid hacking scripts on non-windows bump version remove superflous test Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 21:10:45 2008 @@ -1,6 +1,7 @@ LICENSE MANIFEST README.txt +ez_setup.py py/LICENSE py/__init__.py py/apigen/__init__.py Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Sun Aug 17 21:10:45 2008 @@ -1,25 +1,25 @@ # -*- coding: utf-8 -*- """ - the py lib is a development support library featuring - py.test, ad-hoc distributed execution, micro-threads - and svn abstractions. +the py lib is a development support library featuring +py.test, ad-hoc distributed execution, micro-threads +and svn abstractions. """ from initpkg import initpkg -version = "0.9.2-pre-alpha" +version = "0.9.2-alpha-1" initpkg(__name__, description = "py lib: agile development and test support library", revision = int('$LastChangedRevision$'.split(':')[1][:-1]), lastchangedate = '$LastChangedDate$', version = version, - url = "http://codespeak.net/py", - download_url = "XXX", # "http://codespeak.net/download/py/py-%s.tar.gz" %(version,), + url = "http://pylib.org", + download_url = "http://pypi.python.org/pypi?:action=display&name=py", license = "MIT license", - platforms = ['unix', 'linux', 'cygwin', 'win32'], - author = "holger krekel, Carl Friedrich Bolz, Guido Wesdorp, Maciej Fijalkowski, Armin Rigo & others", - author_email = "py-dev at codespeak.net", + platforms = ['unix', 'linux', 'osx', 'cygwin', 'win32'], + author = "holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others", + author_email = "holger at merlinux.eu, py-dev at codespeak.net", long_description = globals()['__doc__'], # EXPORTED API Modified: py/release/0.9.x/py/misc/testing/test_initpkg.py ============================================================================== --- py/release/0.9.x/py/misc/testing/test_initpkg.py (original) +++ py/release/0.9.x/py/misc/testing/test_initpkg.py Sun Aug 17 21:10:45 2008 @@ -252,8 +252,3 @@ # help(std.path) # #assert False -def test_url_of_version(): - py.test.skip("FAILING! - provide a proper URL or upload pylib tgz") - from urllib import URLopener - URLopener().open(py.__pkg__.download_url) - Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 21:10:45 2008 @@ -1,9 +1,9 @@ """ setup file for 'py' package based on: - svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57361 + svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57364 - autogenerated by gensetup.py, 57358, Sun Aug 17 19:17:41 2008 CET + autogenerated by gensetup.py, 57364, Sun Aug 17 21:09:50 2008 CET """ import os, sys @@ -18,12 +18,12 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-pre-alpha', - url='http://codespeak.net/py', + version='0.9.2-alpha-1', + url='http://pylib.org', license='MIT license', - platforms=['unix', 'linux', 'cygwin', 'win32'], - author='holger krekel, Carl Friedrich Bolz, Guido Wesdorp, Maciej Fijalkowski, Armin Rigo & others', - author_email='py-dev at codespeak.net', + platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], + author='holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others', + author_email='holger at merlinux.eu, py-dev at codespeak.net', ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], @@ -164,7 +164,6 @@ ) # on windows we need to hack up the to-be-installed scripts - from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): def run(self): @@ -186,9 +185,11 @@ f.write("@echo off\n") f.write('python "%%~dp0\%s" %%*\n' % newbasename) f.close() +if sys.platform == "win32": + cmdclass = {'install_scripts': my_install_scripts} +else: + cmdclass = {} -cmdclass = {'install_scripts': my_install_scripts} - if __name__ == '__main__': main() \ No newline at end of file From hpk at codespeak.net Sun Aug 17 21:17:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 21:17:08 +0200 (CEST) Subject: [py-svn] r57366 - py/build Message-ID: <20080817191708.2B324169EBA@codespeak.net> Author: hpk Date: Sun Aug 17 21:17:06 2008 New Revision: 57366 Removed: py/build/winpath.py Modified: py/build/gensetup.py Log: integrate winpath hacks into gensetup.py directly Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 21:17:06 2008 @@ -178,9 +178,35 @@ """) def setup_win32(self): - import winpath - self.append(py.std.inspect.getsource(winpath)) - + self.append(r''' + # on windows we need to hack up the to-be-installed scripts + from distutils.command.install_scripts import install_scripts + class my_install_scripts(install_scripts): + def run(self): + install_scripts.run(self) + #print self.outfiles + for fn in self.outfiles: + basename = os.path.basename(fn) + if "." in basename and basename != "_findpy.py": + #print "tackling", fn + newbasename = basename.replace(".", "_") + newfn = os.path.join(os.path.dirname(fn), newbasename) + if os.path.exists(newfn): + os.remove(newfn) + os.rename(fn, newfn) + newname = fn + ".cmd" + if os.path.exists(newname): + os.remove(newname) + f = open(newname, 'w') + f.write("@echo off\n") + f.write('python "%%~dp0\%s" %%*\n' % newbasename) + f.close() + if sys.platform == "win32": + cmdclass = {'install_scripts': my_install_scripts} + else: + cmdclass = {} + ''') + def write_setup(self): self.setup_header() self.setup_function() Deleted: /py/build/winpath.py ============================================================================== --- /py/build/winpath.py Sun Aug 17 21:17:06 2008 +++ (empty file) @@ -1,26 +0,0 @@ - -# on windows we need to hack up the to-be-installed scripts - -from distutils.command.install_scripts import install_scripts -class my_install_scripts(install_scripts): - def run(self): - install_scripts.run(self) - #print self.outfiles - for fn in self.outfiles: - basename = os.path.basename(fn) - if "." in basename and basename != "_findpy.py": - #print "tackling", fn - newbasename = basename.replace(".", "_") - newfn = os.path.join(os.path.dirname(fn), newbasename) - if os.path.exists(newfn): - os.remove(newfn) - os.rename(fn, newfn) - newname = fn + ".cmd" - if os.path.exists(newname): - os.remove(newname) - f = open(newname, 'w') - f.write("@echo off\n") - f.write('python "%%~dp0\%s" %%*\n' % newbasename) - f.close() - -cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Sun Aug 17 21:32:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 21:32:09 +0200 (CEST) Subject: [py-svn] r57367 - py/release/0.9.x/py/rest/testing Message-ID: <20080817193209.1C75C169EA3@codespeak.net> Author: hpk Date: Sun Aug 17 21:32:08 2008 New Revision: 57367 Modified: py/release/0.9.x/py/rest/testing/test_convert.py py/release/0.9.x/py/rest/testing/test_directive.py Log: port 57322 from trunk Modified: py/release/0.9.x/py/rest/testing/test_convert.py ============================================================================== --- py/release/0.9.x/py/rest/testing/test_convert.py (original) +++ py/release/0.9.x/py/rest/testing/test_convert.py Sun Aug 17 21:32:08 2008 @@ -4,10 +4,10 @@ datadir = py.magic.autopath().dirpath().join("data") def setup_module(mod): - 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") + required = 'gs', 'dot', 'latex', 'epstopdf', + for exe in required: + if not py.path.local.sysfind(exe): + py.test.skip("%r not found, required: %r" %(exe, required)) def test_convert_dot(): # XXX not really clear that the result is valid pdf/eps Modified: py/release/0.9.x/py/rest/testing/test_directive.py ============================================================================== --- py/release/0.9.x/py/rest/testing/test_directive.py (original) +++ py/release/0.9.x/py/rest/testing/test_directive.py Sun Aug 17 21:32:08 2008 @@ -30,8 +30,9 @@ png.remove() def _graphviz_pdf(self): - if not py.path.local.sysfind("dot") or not py.path.local.sysfind("latex"): - py.test.skip("graphviz and latex needed") + for exe in 'dot latex epstopdf'.split(): + if not py.path.local.sysfind(exe): + py.test.skip("%r needed" %(exe,)) directive.set_backend_and_register_directives("latex") txt = py.path.local(datadir.join("graphviz.txt")) From hpk at codespeak.net Sun Aug 17 21:33:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 21:33:31 +0200 (CEST) Subject: [py-svn] r57368 - in py/release/0.9.x/py: c-extension misc misc/testing Message-ID: <20080817193331.4A7F9169EB4@codespeak.net> Author: hpk Date: Sun Aug 17 21:33:29 2008 New Revision: 57368 Removed: py/release/0.9.x/py/c-extension/conftest.py py/release/0.9.x/py/misc/testing/test_update_website.py Modified: py/release/0.9.x/py/misc/buildcmodule.py Log: don't re-build greenlets if they are there remove test that belongs to build Deleted: /py/release/0.9.x/py/c-extension/conftest.py ============================================================================== --- /py/release/0.9.x/py/c-extension/conftest.py Sun Aug 17 21:33:29 2008 +++ (empty file) @@ -1,14 +0,0 @@ -import py - -class Directory(py.test.collect.Directory): - # XXX see in which situations/platforms we want - # run tests here - #def recfilter(self, path): - # if py.std.sys.platform == 'linux2': - # if path.basename == 'greenlet': - # return False - # return super(Directory, self).recfilter(path) - - #def run(self): - # py.test.skip("c-extension testing needs platform selection") - pass Modified: py/release/0.9.x/py/misc/buildcmodule.py ============================================================================== --- py/release/0.9.x/py/misc/buildcmodule.py (original) +++ py/release/0.9.x/py/misc/buildcmodule.py Sun Aug 17 21:33:29 2008 @@ -32,7 +32,7 @@ lib = dirpath.join(modname+ext) # XXX argl! we need better "build"-locations alltogether! - if lib.check(): + if lib.check() and lib.stat().mtime < cfile.stat().mtime: try: lib.remove() except EnvironmentError: Deleted: /py/release/0.9.x/py/misc/testing/test_update_website.py ============================================================================== --- /py/release/0.9.x/py/misc/testing/test_update_website.py Sun Aug 17 21:33:29 2008 +++ (empty file) @@ -1,73 +0,0 @@ -import py -import sys - -here = py.magic.autopath().dirpath() -update_website = here.join('../../bin/_update_website.py').pyimport() - -def test_rsync(): - temp = py.test.ensuretemp('update_website_rsync') - pkgpath = temp.join('pkg') - apipath = temp.join('apigen') - pkgpath.ensure('foo/bar.txt', file=True).write('baz') - pkgpath.ensure('spam/eggs.txt', file=True).write('spam') - apipath.ensure('api/foo.html', file=True).write('') - apipath.ensure('source/spam.html', file=True).write('') - - rsyncpath = temp.join('rsync') - assert not rsyncpath.check() - gateway = py.execnet.PopenGateway() - update_website.rsync(pkgpath, apipath, gateway, rsyncpath.strpath) - assert rsyncpath.check(dir=True) - assert rsyncpath.join('pkg').check(dir=True) - assert rsyncpath.join('pkg/spam/eggs.txt').read() == 'spam' - assert rsyncpath.join('apigen').check(dir=True) - assert rsyncpath.join('apigen/api/foo.html').read() == '' - -def setup_pkg(testname): - temp = py.test.ensuretemp(testname) - pkgpath = temp.ensure('pkg', dir=True) - pyfile = pkgpath.ensure('mod.py').write(py.code.Source(""" - def foo(x): - return x + 1 - """)) - testfile = pkgpath.ensure('test/test_mod.py').write(py.code.Source(""" - from pkg.sub import foo - def test_foo(): - assert foo(1) == 2 - """)) - initfile = pkgpath.ensure('__init__.py').write(py.code.Source(""" - import py - from py.__.initpkg import initpkg - initpkg(__name__, exportdefs={ - 'sub.foo': ('./mod.py', 'foo'), - }) - """)) - initfile = pkgpath.ensure('apigen/apigen.py').write(py.code.Source(""" - from py.__.apigen.apigen import build, \ - get_documentable_items_pkgdir as get_documentable_items - """)) - return pkgpath - -def test_run_tests(): - if py.std.sys.platform == "win32": - py.test.skip("update_website is not supposed to be run from win32") - pkgpath = setup_pkg('update_website_run_tests') - errors = update_website.run_tests(pkgpath, - pkgpath.dirpath().join('apigen'), - captureouterr=True) - print errors - assert not errors - assert pkgpath.join('../apigen').check(dir=True) - assert pkgpath.join('../apigen/api/sub.foo.html').check(file=True) - -def test_run_tests_failure(): - if py.std.sys.platform == "win32": - py.test.skip("update_website is not supposed to be run from win32") - pkgpath = setup_pkg('update_website_run_tests_failure') - assert not pkgpath.join('../apigen').check(dir=True) - pkgpath.ensure('../apigen', file=True) - errors = update_website.run_tests(pkgpath, - pkgpath.dirpath().join('apigen'), - captureouterr=True) - assert errors # some error message - From hpk at codespeak.net Sun Aug 17 21:55:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 21:55:31 +0200 (CEST) Subject: [py-svn] r57369 - py/build Message-ID: <20080817195531.D12D716852F@codespeak.net> Author: hpk Date: Sun Aug 17 21:55:28 2008 New Revision: 57369 Added: py/build/test_update_website.py - copied, changed from r57325, py/trunk/py/misc/testing/test_update_website.py Log: adding and tweaking the test_update_website.py script here Copied: py/build/test_update_website.py (from r57325, py/trunk/py/misc/testing/test_update_website.py) ============================================================================== --- py/trunk/py/misc/testing/test_update_website.py (original) +++ py/build/test_update_website.py Sun Aug 17 21:55:28 2008 @@ -1,8 +1,8 @@ import py import sys -here = py.magic.autopath().dirpath() -update_website = here.join('../../bin/_update_website.py').pyimport() +mydir = py.magic.autopath().dirpath() +update_website = mydir.join('_update_website.py').pyimport() def test_rsync(): temp = py.test.ensuretemp('update_website_rsync') From hpk at codespeak.net Sun Aug 17 22:07:52 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:07:52 +0200 (CEST) Subject: [py-svn] r57370 - py/build Message-ID: <20080817200752.B7204169ED9@codespeak.net> Author: hpk Date: Sun Aug 17 22:07:50 2008 New Revision: 57370 Added: py/build/_fixpythonpath.py Modified: py/build/_update_website.py py/build/docgen.py py/build/gensetup.py py/build/makepyrelease.py Log: various updates to the build helpers, trying to work against setuptools/eggs hiding PYTHONPATHs Added: py/build/_fixpythonpath.py ============================================================================== --- (empty file) +++ py/build/_fixpythonpath.py Sun Aug 17 22:07:50 2008 @@ -0,0 +1,11 @@ +import os, sys + +l = [] +for p in os.environ['PYTHONPATH'].split(':'): + if os.path.exists(os.path.join(p, 'py')): + l.append(p) + +index = 0 +while not sys.path[index]: + index += 1 +sys.path[index:index] = l Modified: py/build/_update_website.py ============================================================================== --- py/build/_update_website.py (original) +++ py/build/_update_website.py Sun Aug 17 22:07:50 2008 @@ -5,7 +5,7 @@ rsyncs the whole package (with all the ReST docs converted to HTML) as well as the apigen docs to a given remote host and path """ -from _findpy import py +import _fixpythonpath import py import sys Modified: py/build/docgen.py ============================================================================== --- py/build/docgen.py (original) +++ py/build/docgen.py Sun Aug 17 22:07:50 2008 @@ -8,9 +8,11 @@ the navigation bar will be adjusted """ -from _findpy import py +import _fixpythonpath + import py pypath = py.__pkg__.getpath() +print "using pypath", pypath def run_tests(path, envvars='', args=''): pytestpath = pypath.join('bin/py.test') @@ -42,6 +44,7 @@ if api: print 'building api' build_apigen_docs(targetpath, testargs) + if __name__ == '__main__': import sys Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 22:07:50 2008 @@ -106,8 +106,9 @@ ["py/c-extension/greenlet/greenlet.c"]),], ''' % params) indent = " " * 8 - self.append_pprint(indent, scripts=self.getscripts()) self.append_pprint(indent, long_description=params['long_description']) + self.append_pprint(indent, classifiers=self.meta.classifiers) + self.append_pprint(indent, scripts=self.getscripts()) self.append_pprint(indent, packages=self.getpackages()) #self.append_pprint(indent, data_files=self.getdatafiles()) self.append_pprint(indent, package_data=self.getpackagedata()) Modified: py/build/makepyrelease.py ============================================================================== --- py/build/makepyrelease.py (original) +++ py/build/makepyrelease.py Sun Aug 17 22:07:50 2008 @@ -1,33 +1,10 @@ #!/usr/bin/env python -from _findpy import py import sys pydir = py.path.local(py.__file__).dirpath() rootdir = pydir.dirpath() -def gen_manifest(): - pywc = py.path.svnwc(pydir) - status = pywc.status(rec=True) - #assert not status.modified - #assert not status.deleted - #assert not status.added - versioned = dict([(x.localpath,1) for x in status.allpath()]) - - l = [] - for x in rootdir.visit(None, lambda x: x.basename != '.svn'): - if x.check(file=1): - names = [y.basename for y in x.parts()] - if '.svn' in names: - l.append(x) - elif x in versioned: - l.append(x) - l.append(rootdir / "setup.py") - l = [x.relto(rootdir) for x in l] - l.append("") - s = "\n".join(l) - return s - def trace(arg): lines = str(arg).split('\n') prefix = "[trace] " @@ -40,7 +17,12 @@ for line in lines: print >>py.std.sys.stdout, line -def make_distfiles(tmpdir): +class PyRelease: + def __init__(self, wcpath, pkg): + assert self.wcpath.join(pkg.__name__)__name__ + + +def make_sdist(tmpdir): """ return distdir with tar.gz and zipfile. """ manifest = tmpdir.join('MANIFEST') trace("generating %s" %(manifest,)) From hpk at codespeak.net Sun Aug 17 22:09:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:09:03 +0200 (CEST) Subject: [py-svn] r57371 - py/build Message-ID: <20080817200903.5D106169ED9@codespeak.net> Author: hpk Date: Sun Aug 17 22:09:01 2008 New Revision: 57371 Modified: py/build/_fixpythonpath.py Log: pythonpath may not be set Modified: py/build/_fixpythonpath.py ============================================================================== --- py/build/_fixpythonpath.py (original) +++ py/build/_fixpythonpath.py Sun Aug 17 22:09:01 2008 @@ -1,7 +1,7 @@ import os, sys l = [] -for p in os.environ['PYTHONPATH'].split(':'): +for p in os.environ.get('PYTHONPATH', '').split(':'): if os.path.exists(os.path.join(p, 'py')): l.append(p) From hpk at codespeak.net Sun Aug 17 22:10:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:10:36 +0200 (CEST) Subject: [py-svn] r57372 - in py/release/0.9.x: . py Message-ID: <20080817201036.C06C1169ED9@codespeak.net> Author: hpk Date: Sun Aug 17 22:10:35 2008 New Revision: 57372 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: trove identifier Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 22:10:35 2008 @@ -77,7 +77,6 @@ py/builtin/testing/test_reversed.py py/builtin/testing/test_set.py py/builtin/testing/test_sorted.py -py/c-extension/conftest.py py/c-extension/greenlet/README.txt py/c-extension/greenlet/dummy_greenlet.py py/c-extension/greenlet/greenlet.c @@ -243,7 +242,6 @@ py/misc/testing/test_std.py py/misc/testing/test_svnlook.py py/misc/testing/test_terminal.py -py/misc/testing/test_update_website.py py/path/__init__.py py/path/common.py py/path/gateway/TODO.txt Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Sun Aug 17 22:10:35 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-1" +version = "0.9.2-alpha-2" initpkg(__name__, description = "py lib: agile development and test support library", @@ -21,6 +21,20 @@ author = "holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others", author_email = "holger at merlinux.eu, py-dev at codespeak.net", long_description = globals()['__doc__'], + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Operating System :: MacOS :: MacOS X", + "Topic :: Software Development :: Testing", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Distributed Computing", + "Topic :: Utilities", + "Programming Language :: Python", + ], + # EXPORTED API exportdefs = { Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 22:10:35 2008 @@ -3,7 +3,7 @@ svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57364 - autogenerated by gensetup.py, 57364, Sun Aug 17 21:09:50 2008 CET + autogenerated by gensetup.py, 57366, Sun Aug 17 21:50:48 2008 CET """ import os, sys @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-1', + version='0.9.2-alpha-2', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -27,6 +27,17 @@ ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], + classifiers=['Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: POSIX', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: MacOS :: MacOS X', + 'Topic :: Software Development :: Testing', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Distributed Computing', + 'Topic :: Utilities', + 'Programming Language :: Python'], scripts=['py/bin/_findpy.py', 'py/bin/py.cleanup', 'py/bin/py.countloc', From hpk at codespeak.net Sun Aug 17 22:23:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:23:18 +0200 (CEST) Subject: [py-svn] r57373 - py/release/0.9.x/py/bin Message-ID: <20080817202318.18E36168522@codespeak.net> Author: hpk Date: Sun Aug 17 22:23:17 2008 New Revision: 57373 Removed: py/release/0.9.x/py/bin/_findpy.py Log: remove it here Deleted: /py/release/0.9.x/py/bin/_findpy.py ============================================================================== --- /py/release/0.9.x/py/bin/_findpy.py Sun Aug 17 22:23:17 2008 +++ (empty file) @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# -# find and import a version of 'py' -# -import sys -import os -from os.path import dirname as opd, exists, join, basename, abspath - -def searchpy(current): - while 1: - last = current - initpy = join(current, '__init__.py') - if not exists(initpy): - pydir = join(current, 'py') - # recognize py-package and ensure it is importable - if exists(pydir) and exists(join(pydir, '__init__.py')): - #for p in sys.path: - # if p == current: - # return True - if current != sys.path[0]: # if we are already first, then ok - print >>sys.stderr, "inserting into sys.path:", current - sys.path.insert(0, current) - return True - current = opd(current) - if last == current: - return False - -if not searchpy(abspath(os.curdir)): - if not searchpy(opd(abspath(sys.argv[0]))): - if not searchpy(opd(__file__)): - pass # let's hope it is just on sys.path - -import py - -if __name__ == '__main__': - print "py lib is at", py.__file__ From hpk at codespeak.net Sun Aug 17 22:23:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:23:44 +0200 (CEST) Subject: [py-svn] r57374 - py/release/0.9.x/py/bin Message-ID: <20080817202344.A6608168522@codespeak.net> Author: hpk Date: Sun Aug 17 22:23:42 2008 New Revision: 57374 Added: py/release/0.9.x/py/bin/_findpy.py (contents, props changed) Log: and add as a link from root Added: py/release/0.9.x/py/bin/_findpy.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/_findpy.py Sun Aug 17 22:23:42 2008 @@ -0,0 +1 @@ +link ../../_findpy.py \ No newline at end of file From hpk at codespeak.net Sun Aug 17 22:26:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:26:17 +0200 (CEST) Subject: [py-svn] r57375 - in py/release/0.9.x: . py/compat/testing Message-ID: <20080817202617.11C6616852F@codespeak.net> Author: hpk Date: Sun Aug 17 22:26:14 2008 New Revision: 57375 Added: py/release/0.9.x/_findpy.py - copied unchanged from r57364, py/release/0.9.x/py/bin/_findpy.py Removed: py/release/0.9.x/py/compat/testing/_findpy.py Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/setup.py Log: findpy refinements Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 22:26:14 2008 @@ -1,6 +1,7 @@ LICENSE MANIFEST README.txt +_findpy.py ez_setup.py py/LICENSE py/__init__.py Deleted: /py/release/0.9.x/py/compat/testing/_findpy.py ============================================================================== --- /py/release/0.9.x/py/compat/testing/_findpy.py Sun Aug 17 22:26:14 2008 +++ (empty file) @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# -# find and import a version of 'py' -# -import sys -import os -from os.path import dirname as opd, exists, join, basename, abspath - -def searchpy(current): - while 1: - last = current - initpy = join(current, '__init__.py') - if not exists(initpy): - pydir = join(current, 'py') - # recognize py-package and ensure it is importable - if exists(pydir) and exists(join(pydir, '__init__.py')): - #for p in sys.path: - # if p == current: - # return True - if current != sys.path[0]: # if we are already first, then ok - print >>sys.stderr, "inserting into sys.path:", current - sys.path.insert(0, current) - return True - current = opd(current) - if last == current: - return False - -if not searchpy(abspath(os.curdir)): - if not searchpy(opd(abspath(sys.argv[0]))): - if not searchpy(opd(__file__)): - pass # let's hope it is just on sys.path - -import py - -if __name__ == '__main__': - print "py lib is at", py.__file__ Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 22:26:14 2008 @@ -3,7 +3,7 @@ svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57364 - autogenerated by gensetup.py, 57366, Sun Aug 17 21:50:48 2008 CET + autogenerated by gensetup.py, 57370, Sun Aug 17 22:26:17 2008 CET """ import os, sys @@ -27,6 +27,8 @@ ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], + py_modules=['_findpy'], + long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', classifiers=['Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', @@ -38,13 +40,11 @@ 'Topic :: System :: Distributed Computing', 'Topic :: Utilities', 'Programming Language :: Python'], - scripts=['py/bin/_findpy.py', - 'py/bin/py.cleanup', + scripts=['py/bin/py.cleanup', 'py/bin/py.countloc', 'py/bin/py.lookup', 'py/bin/py.rest', 'py/bin/py.test'], - long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', packages=['py', 'py.apigen', 'py.apigen.rest', @@ -182,7 +182,7 @@ #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) - if "." in basename and basename != "_findpy.py": + if "." in basename: #print "tackling", fn newbasename = basename.replace(".", "_") newfn = os.path.join(os.path.dirname(fn), newbasename) From hpk at codespeak.net Sun Aug 17 22:37:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:37:17 +0200 (CEST) Subject: [py-svn] r57376 - py/build Message-ID: <20080817203717.3B441169DB4@codespeak.net> Author: hpk Date: Sun Aug 17 22:37:15 2008 New Revision: 57376 Modified: py/build/gensetup.py Log: have _findpy be installed at root level Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Sun Aug 17 22:37:15 2008 @@ -106,6 +106,7 @@ ["py/c-extension/greenlet/greenlet.c"]),], ''' % params) indent = " " * 8 + self.append_pprint(indent, py_modules=['_findpy',]), self.append_pprint(indent, long_description=params['long_description']) self.append_pprint(indent, classifiers=self.meta.classifiers) self.append_pprint(indent, scripts=self.getscripts()) @@ -137,7 +138,7 @@ scripts = [] for p in self.allpaths: if p.dirpath() == bindir: - if p.basename.startswith('py.') or p.basename == "_findpy.py": + if p.basename.startswith('py.'): scripts.append(p.relto(self.wcbasedir)) return scripts @@ -188,7 +189,7 @@ #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) - if "." in basename and basename != "_findpy.py": + if "." in basename: #print "tackling", fn newbasename = basename.replace(".", "_") newfn = os.path.join(os.path.dirname(fn), newbasename) From hpk at codespeak.net Sun Aug 17 22:38:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 22:38:09 +0200 (CEST) Subject: [py-svn] r57377 - py/release/0.9.x Message-ID: <20080817203809.7639B169DB4@codespeak.net> Author: hpk Date: Sun Aug 17 22:38:08 2008 New Revision: 57377 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/setup.py Log: newgen Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 22:38:08 2008 @@ -117,7 +117,6 @@ py/compat/optparse.py py/compat/subprocess.py py/compat/testing/__init__.py -py/compat/testing/_findpy.py py/compat/testing/test_doctest.py py/compat/testing/test_doctest.txt py/compat/testing/test_doctest2.py Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 22:38:08 2008 @@ -3,7 +3,7 @@ svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57364 - autogenerated by gensetup.py, 57370, Sun Aug 17 22:26:17 2008 CET + autogenerated by gensetup.py, 57370, Sun Aug 17 22:27:58 2008 CET """ import os, sys From hpk at codespeak.net Sun Aug 17 23:45:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 23:45:00 +0200 (CEST) Subject: [py-svn] r57378 - py/release/0.9.x/py/execnet/script Message-ID: <20080817214500.709D316850F@codespeak.net> Author: hpk Date: Sun Aug 17 23:44:57 2008 New Revision: 57378 Added: py/release/0.9.x/py/execnet/script/__init__.py Log: mark this for installation Added: py/release/0.9.x/py/execnet/script/__init__.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/execnet/script/__init__.py Sun Aug 17 23:44:57 2008 @@ -0,0 +1 @@ +# From hpk at codespeak.net Sun Aug 17 23:47:12 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 17 Aug 2008 23:47:12 +0200 (CEST) Subject: [py-svn] r57379 - py/release/0.9.x Message-ID: <20080817214712.5D358169EE7@codespeak.net> Author: hpk Date: Sun Aug 17 23:47:10 2008 New Revision: 57379 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/setup.py Log: include script dir Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Sun Aug 17 23:47:10 2008 @@ -175,6 +175,7 @@ py/execnet/register.py py/execnet/rsync.py py/execnet/rsync_remote.py +py/execnet/script/__init__.py py/execnet/script/loop_socketserver.py py/execnet/script/quitserver.py py/execnet/script/shell.py Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Sun Aug 17 23:47:10 2008 @@ -1,9 +1,9 @@ """ setup file for 'py' package based on: - svn+ssh://codespeak.net/svn/py/release/0.9.x, revision=57364 + https://codespeak.net/svn/py/release/0.9.x, revision=57364 - autogenerated by gensetup.py, 57370, Sun Aug 17 22:27:58 2008 CET + autogenerated by gensetup.py, 57376, Sun Aug 17 23:46:03 2008 CET """ import os, sys @@ -65,6 +65,7 @@ 'py.compat.testing', 'py.doc', 'py.execnet', + 'py.execnet.script', 'py.execnet.testing', 'py.io', 'py.io.test', From hpk at codespeak.net Mon Aug 18 10:40:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 10:40:45 +0200 (CEST) Subject: [py-svn] r57389 - py/build Message-ID: <20080818084045.102AA169EC3@codespeak.net> Author: hpk Date: Mon Aug 18 10:40:45 2008 New Revision: 57389 Added: py/build/makebdistegg.py Log: a script to remote-control a windows machine and have it make two binary eggs for python2.4 and python2.5 automatically Added: py/build/makebdistegg.py ============================================================================== --- (empty file) +++ py/build/makebdistegg.py Mon Aug 18 10:40:45 2008 @@ -0,0 +1,106 @@ +import py +import os +import sys + +def trace(*args): + print >>sys.stderr, " ".join(map(str, args)) + +def remote_chdirtemp(): + #remote_tmpdir = winexec(""" + # import os, tempfile + # tmpdir = tempfile.mkdtemp() + # os.chdir(tmpdir) + # channel.send(tmpdir) + #""").receive() + remote_tmpdir = winexec(r""" + import os, tempfile, shutil + base = r"C:\tmp\makepyrelease" + path = tempfile.mkdtemp(dir=base) + #if os.path.exists(path): + # shutil.rmtree(path, ignore_errors=True) + #os.mkdir(path) + os.chdir(path) + channel.send(path) + """).receive() + trace("using remote tempdir", remote_tmpdir) + +def sendfile(path): + channel = winexec(""" + fn = channel.receive() + content = channel.receive() + open(fn, 'w').write(content) + """) + trace("sending", path) + channel.send(path.basename) + channel.send(path.read()) + +def remote_checkout(url): + channel = winexec(""" + import os + url = channel.receive() + errno = os.system("svn co " + url) + assert not errno + """) + channel.send(url) + trace("waiting for remote to checkout", url) + channel.waitclose() + +def remote_cd(path): + trace("changing remote curdir to", path) + winexec("import os ; os.chdir(%r)" % path).waitclose() + +def remote_makebdist_egg(python="python25"): + channel = winexec(r""" + import os + errno = os.system(r"C:\%s\python setup.py bdist_egg >log") + channel.send(open('log').read()) + assert not errno + """ % python) + log = channel.receive() + logpath = py.path.local("bdist_egg_%s.log" % python) + logpath.write(log) + trace("received result in", logpath) + +def remote_getdist(): + channel = winexec(r""" + import py + for p in py.path.local("dist").listdir("*.egg"): + channel.send(p.basename) + channel.send(p.read()) + channel.send(None) + """) + while 1: + basename = channel.receive() + if basename is None: + break + print "receiving", basename + content = channel.receive() + py.path.local(basename).write(content) + print "complete" + +def winexec(source): + return gw.remote_exec(source, stdout=sys.stdout, stderr=sys.stderr) + +def main(): + #errno = os.system("python setup.py sdist") + #assert not errno + #l = py.path.local("dist").listdir("*.gz") + #assert len(l) == 1 + #sdist = l[0] + + trace("gateway", gw) + wc = py.path.svnwc() + + remote_tmpdir = remote_chdirtemp() + remote_checkout(wc.info().url) + remote_cd(wc.basename) + for python in "python24", "python25": + remote_makebdist_egg(python) + remote_getdist() + +if __name__ == '__main__': + gw = py.execnet.SocketGateway("10.9.2.62", 8888) + try: + main() + finally: + gw.exit() From hpk at codespeak.net Mon Aug 18 10:45:54 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 10:45:54 +0200 (CEST) Subject: [py-svn] r57390 - py/release/0.9.x/py/bin Message-ID: <20080818084554.4E57A169EFC@codespeak.net> Author: hpk Date: Mon Aug 18 10:45:51 2008 New Revision: 57390 Removed: py/release/0.9.x/py/bin/_findpy.py Log: remove link, doesn't work on win32 as expected Deleted: /py/release/0.9.x/py/bin/_findpy.py ============================================================================== --- /py/release/0.9.x/py/bin/_findpy.py Mon Aug 18 10:45:51 2008 +++ (empty file) @@ -1 +0,0 @@ -link ../../_findpy.py \ No newline at end of file From hpk at codespeak.net Mon Aug 18 10:46:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 10:46:24 +0200 (CEST) Subject: [py-svn] r57391 - py/release/0.9.x/py/bin Message-ID: <20080818084624.3AC24169EFC@codespeak.net> Author: hpk Date: Mon Aug 18 10:46:23 2008 New Revision: 57391 Added: py/release/0.9.x/py/bin/_findpy.py - copied unchanged from r57387, py/release/0.9.x/_findpy.py Log: rather copy the orig file From hpk at codespeak.net Mon Aug 18 11:05:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 11:05:18 +0200 (CEST) Subject: [py-svn] r57393 - in py/release/0.9.x: . py Message-ID: <20080818090518.70884169EB7@codespeak.net> Author: hpk Date: Mon Aug 18 11:05:16 2008 New Revision: 57393 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: auto-commit for building new eggs Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Mon Aug 18 11:05:16 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-2" +version = "0.9.2-alpha-3" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 11:05:16 2008 @@ -1,9 +1,9 @@ """ setup file for 'py' package based on: - https://codespeak.net/svn/py/release/0.9.x, revision=57364 + https://codespeak.net/svn/py/release/0.9.x, revision=57387 - autogenerated by gensetup.py, 57376, Sun Aug 17 23:46:03 2008 CET + autogenerated by gensetup.py, 57376, Mon Aug 18 11:06:03 2008 CET """ import os, sys From hpk at codespeak.net Mon Aug 18 11:46:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 11:46:21 +0200 (CEST) Subject: [py-svn] r57394 - py/release/0.9.x Message-ID: <20080818094621.F30E0169F0F@codespeak.net> Author: hpk Date: Mon Aug 18 11:46:18 2008 New Revision: 57394 Modified: py/release/0.9.x/setup.py Log: auto-commit for building new eggs Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 11:46:18 2008 @@ -3,7 +3,7 @@ https://codespeak.net/svn/py/release/0.9.x, revision=57387 - autogenerated by gensetup.py, 57376, Mon Aug 18 11:06:03 2008 CET + autogenerated by gensetup.py, 57376, Mon Aug 18 11:47:06 2008 CET """ import os, sys @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-2', + version='0.9.2-alpha-3', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], From hpk at codespeak.net Mon Aug 18 11:59:18 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 11:59:18 +0200 (CEST) Subject: [py-svn] r57395 - in py/release/0.9.x: . py Message-ID: <20080818095918.E051C169F34@codespeak.net> Author: hpk Date: Mon Aug 18 11:59:16 2008 New Revision: 57395 Modified: py/release/0.9.x/README.txt (contents, props changed) py/release/0.9.x/py/LICENSE Log: adding Id keyword to readme, changes to license file Modified: py/release/0.9.x/README.txt ============================================================================== --- py/release/0.9.x/README.txt (original) +++ py/release/0.9.x/README.txt Mon Aug 18 11:59:16 2008 @@ -1,4 +1,4 @@ -The py lib is a development support library featuring +The py lib is a Python development support library featuring the following tools and modules: * py.test: popular python testing tool @@ -10,10 +10,11 @@ listed in the LICENSE file. For questions, please see py/doc/index.txt, refer to the website -http://pylib.org or http://pytest.org -come to the #pylib IRC freenode channel or subscribe to the -http://codespeak.net/mailman/listinfo/py-dev mailing list. +http://pylib.org or come to the #pylib IRC freenode channel or subscribe to +http://codespeak.net/mailman/listinfo/py-dev . have fun, holger krekel, holger at merlinux eu + +$Id$ Modified: py/release/0.9.x/py/LICENSE ============================================================================== --- py/release/0.9.x/py/LICENSE (original) +++ py/release/0.9.x/py/LICENSE Mon Aug 18 11:59:16 2008 @@ -10,11 +10,11 @@ Carl Friedrich Bolz, cfbolz at gmx de Armin Rigo, arigo at tunes org Maciek Fijalkowski, fijal at genesilico.pl + Briand Dorsey, briandorsey at gmail com merlinux GmbH, Germany, office at merlinux eu Contributors include:: - Briand Dorsey Samuele Pedroni Harald Armin Massa Ralf Schmitt From hpk at codespeak.net Mon Aug 18 12:05:47 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 12:05:47 +0200 (CEST) Subject: [py-svn] r57397 - py/release/0.9.x/py/test Message-ID: <20080818100547.0EABD169F42@codespeak.net> Author: hpk Date: Mon Aug 18 12:05:46 2008 New Revision: 57397 Modified: py/release/0.9.x/py/test/representation.py Log: make the parseable location of errors be as brief as possible. Modified: py/release/0.9.x/py/test/representation.py ============================================================================== --- py/release/0.9.x/py/test/representation.py (original) +++ py/release/0.9.x/py/test/representation.py Mon Aug 18 12:05:46 2008 @@ -123,8 +123,12 @@ # filename and lineno output for each entry, # using an output format that most editors unterstand loc = "%s:%d:" %(entry.path, entry.lineno+1) + try: + msg = excinfo.type.__name__ + except AttributeError: + msg = excinfo.typename # can be longer if entry == last: - loc += " %r" % excinfo.exconly() + loc += " %s" % msg self.out.line(loc) self.repr_locals(entry.locals) From hpk at codespeak.net Mon Aug 18 12:52:06 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 12:52:06 +0200 (CEST) Subject: [py-svn] r57401 - in py/release/0.9.x/py/rest/testing: . data Message-ID: <20080818105206.E1A75169F2C@codespeak.net> Author: hpk Date: Mon Aug 18 12:52:02 2008 New Revision: 57401 Added: py/release/0.9.x/py/rest/testing/setup.py Modified: py/release/0.9.x/py/rest/testing/data/ (props changed) py/release/0.9.x/py/rest/testing/test_convert.py py/release/0.9.x/py/rest/testing/test_directive.py py/release/0.9.x/py/rest/testing/test_htmlrest.py py/release/0.9.x/py/rest/testing/test_rst2pdf.py Log: refactoring tests to not create files inline Added: py/release/0.9.x/py/rest/testing/setup.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/rest/testing/setup.py Mon Aug 18 12:52:02 2008 @@ -0,0 +1,10 @@ +import py + +pydir = py.path.local(py.__file__).dirpath() +mydatadir = py.magic.autopath().dirpath().join("data") + +def getdata(): + rel = mydatadir.relto(pydir) + tmpdir = py.test.ensuretemp(rel.replace(pydir.sep, '_')) + mydatadir.copy(tmpdir) + return tmpdir Modified: py/release/0.9.x/py/rest/testing/test_convert.py ============================================================================== --- py/release/0.9.x/py/rest/testing/test_convert.py (original) +++ py/release/0.9.x/py/rest/testing/test_convert.py Mon Aug 18 12:52:02 2008 @@ -1,13 +1,14 @@ import py from py.__.rest.convert import convert_dot, latexformula2png - -datadir = py.magic.autopath().dirpath().join("data") +from py.__.rest.testing.setup import getdata def setup_module(mod): required = 'gs', 'dot', 'latex', 'epstopdf', for exe in required: if not py.path.local.sysfind(exe): py.test.skip("%r not found, required: %r" %(exe, required)) + mod.datadir = getdata() + def test_convert_dot(): # XXX not really clear that the result is valid pdf/eps Modified: py/release/0.9.x/py/rest/testing/test_directive.py ============================================================================== --- py/release/0.9.x/py/rest/testing/test_directive.py (original) +++ py/release/0.9.x/py/rest/testing/test_directive.py Mon Aug 18 12:52:02 2008 @@ -7,9 +7,11 @@ 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 -datadir = py.magic.autopath().dirpath().join("data") -testdir = py.test.ensuretemp("rest") +def setup_module(mod): + mod.datadir = getdata() + mod.testdir = py.test.ensuretemp("rest") class TestGraphviz(object): def _graphviz_html(self): Modified: py/release/0.9.x/py/rest/testing/test_htmlrest.py ============================================================================== --- py/release/0.9.x/py/rest/testing/test_htmlrest.py (original) +++ py/release/0.9.x/py/rest/testing/test_htmlrest.py Mon Aug 18 12:52:02 2008 @@ -2,6 +2,7 @@ import py from py.__.misc import rest +from py.__.rest.testing.setup import getdata def setup_module(mod): if not py.path.local.sysfind("gs") or \ @@ -13,12 +14,12 @@ except ImportError: py.test.skip("docutils not present") -data = py.magic.autopath().dirpath().join("data") + mod.datadir = getdata() def test_process_simple(): # fallback test: only checks that no exception is raised def rec(p): return p.check(dotfile=0) - for x in data.visit("*.txt", rec=rec): + for x in datadir.visit("*.txt", rec=rec): yield rest.process, x Modified: py/release/0.9.x/py/rest/testing/test_rst2pdf.py ============================================================================== --- py/release/0.9.x/py/rest/testing/test_rst2pdf.py (original) +++ py/release/0.9.x/py/rest/testing/test_rst2pdf.py Mon Aug 18 12:52:02 2008 @@ -2,6 +2,8 @@ import py from py.__.rest.latex import process_configfile, process_rest_file +from py.__.rest.testing.setup import getdata + try: import docutils except ImportError: @@ -13,22 +15,20 @@ not py.path.local.sysfind("dot") or \ not py.path.local.sysfind("latex"): py.test.skip("ghostscript, graphviz and latex needed") - mod.data = py.magic.autopath().dirpath().join("data") + mod.datadir = getdata() class TestRst2Pdf(object): def _process_rest_file(self): - data = py.magic.autopath().dirpath().join("data") - part2 = data.join("part1.txt") + part2 = datadir.join("part1.txt") pdf = part2.new(ext="pdf") process_rest_file(part2) assert pdf.check() pdf.remove() def _process_configfile(self): - data = py.magic.autopath().dirpath().join("data") - config = data.join("example.rst2pdfconfig") + config = datadir.join("example.rst2pdfconfig") pdf = config.new(ext="pdf") - tex = data.join('example.tex') + tex = datadir.join('example.tex') process_configfile(config, debug=True) assert pdf.check() assert tex.check() @@ -45,9 +45,9 @@ def rec(p): return p.check(dotfile=0) - for x in data.visit("*.rst2pdfconfig", rec=rec): + for x in datadir.visit("*.rst2pdfconfig", rec=rec): process_configfile(x) - for x in data.visit("*.txt", rec=rec): + for x in datadir.visit("*.txt", rec=rec): process_rest_file(x) def test_rst2pdf(self): From hpk at codespeak.net Mon Aug 18 13:16:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 13:16:21 +0200 (CEST) Subject: [py-svn] r57403 - in py/release/0.9.x: . py/doc Message-ID: <20080818111621.C976D169F44@codespeak.net> Author: hpk Date: Mon Aug 18 13:16:21 2008 New Revision: 57403 Added: py/release/0.9.x/CHANGELOG - copied, changed from r57387, py/release/0.9.x/py/doc/changes-0.9.1.txt Removed: py/release/0.9.x/py/doc/changes-0.9.1.txt Modified: py/release/0.9.x/py/doc/bin.txt py/release/0.9.x/setup.py Log: adding changelog to toplevel directory Copied: py/release/0.9.x/CHANGELOG (from r57387, py/release/0.9.x/py/doc/changes-0.9.1.txt) ============================================================================== --- py/release/0.9.x/py/doc/changes-0.9.1.txt (original) +++ py/release/0.9.x/CHANGELOG Mon Aug 18 13:16:21 2008 @@ -1,3 +1,25 @@ +Changes between 0.9.1 and 0.9.2 +=============================== + +* much refined installation, metadata, created new setup.py, + now based on setuptools/ez_setup (by flipping a bit you + can have good old distutils). + +* improved the way of making py.* scripts available in + windows environments, they are now added to the + Scripts directory as ".cmd" files. + +* py.test's traceback is better parseable from editors + (follows the filenames:LINENO: MSG convention) + +* improving stability for py.execnet on windows + +* fix to make "py.test --runbrowser" work if + Javascript is generated + +* removed randomly added py.test.broken and + py.test.notimplemented helpers. + Changes between 0.9.0 and 0.9.1 =============================== Modified: py/release/0.9.x/py/doc/bin.txt ============================================================================== --- py/release/0.9.x/py/doc/bin.txt (original) +++ py/release/0.9.x/py/doc/bin.txt Mon Aug 18 13:16:21 2008 @@ -52,7 +52,9 @@ Usage: ``py.rest [PATHS] [options]`` Loot recursively for .txt files starting from ``PATHS`` and convert them to -html using docutils (or to pdf files, if the --pdf option is used). +html using docutils or to pdf files, if the ``--pdf`` option is used. For +conversion to PDF you will need several command line tools, on Ubuntu Linux +this is **texlive** and **texlive-extra-utils**. ``py.rest`` has some extra features over rst2html (which is shipped with docutils). Most of these are still experimental, the one which is most likely Deleted: /py/release/0.9.x/py/doc/changes-0.9.1.txt ============================================================================== --- /py/release/0.9.x/py/doc/changes-0.9.1.txt Mon Aug 18 13:16:21 2008 +++ (empty file) @@ -1,53 +0,0 @@ -Changes between 0.9.0 and 0.9.1 -=============================== - -This is a fairly complete list of changes between 0.9 and 0.9.1, which can -serve as a reference for developers. - -* allowing + signs in py.path.svn urls [39106] -* fixed support for Failed exceptions without excinfo in py.test [39340] -* added support for killing processes for Windows (as well as platforms that - support os.kill) in py.misc.killproc [39655] -* added setup/teardown for generative tests to py.test [40702] -* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739] -* fixed problem with calling .remove() on wcpaths of non-versioned files in - py.path [44248] -* fixed some import and inheritance issues in py.test [41480, 44648, 44655] -* fail to run greenlet tests when pypy is available, but without stackless - [45294] -* small fixes in rsession tests [45295] -* fixed issue with 2.5 type representations in py.test [45483, 45484] -* made that internal reporting issues displaying is done atomically in py.test - [45518] -* made that non-existing files are igored by the py.lookup script [45519] -* improved exception name creation in py.test [45535] -* made that less threads are used in execnet [merge in 45539] -* removed lock required for atomical reporting issue displaying in py.test - [45545] -* removed globals from execnet [45541, 45547] -* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit - get called in 2.5 (py.execnet) [45548] -* fixed bug in joining threads in py.execnet's servemain [45549] -* refactored py.test.rsession tests to not rely on exact output format anymore - [45646] -* using repr() on test outcome [45647] -* added 'Reason' classes for py.test.skip() [45648, 45649] -* killed some unnecessary sanity check in py.test.collect [45655] -* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only - usable by Administrators [45901] -* added support for locking and non-recursive commits to py.path.svnwc [45994] -* locking files in py.execnet to prevent CPython from segfaulting [46010] -* added export() method to py.path.svnurl -* fixed -d -x in py.test [47277] -* fixed argument concatenation problem in py.path.svnwc [49423] -* restore py.test behaviour that it exits with code 1 when there are failures - [49974] -* don't fail on html files that don't have an accompanying .txt file [50606] -* fixed 'utestconvert.py < input' [50645] -* small fix for code indentation in py.code.source [50755] -* fix _docgen.py documentation building [51285] -* improved checks for source representation of code blocks in py.test [51292] -* added support for passing authentication to py.path.svn* objects [52000, - 52001] -* removed sorted() call for py.apigen tests in favour of [].sort() to support - Python 2.3 [52481] Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 13:16:21 2008 @@ -3,7 +3,7 @@ https://codespeak.net/svn/py/release/0.9.x, revision=57387 - autogenerated by gensetup.py, 57376, Mon Aug 18 11:47:06 2008 CET + autogenerated by gensetup.py """ import os, sys From hpk at codespeak.net Mon Aug 18 13:27:19 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 13:27:19 +0200 (CEST) Subject: [py-svn] r57404 - in py/release/0.9.x/py: . test test/testing Message-ID: <20080818112719.3CC2E169F51@codespeak.net> Author: hpk Date: Mon Aug 18 13:27:17 2008 New Revision: 57404 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/py/test/item.py py/release/0.9.x/py/test/testing/test_session.py Log: actually remove broken/notimplemented Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Mon Aug 18 13:27:17 2008 @@ -45,8 +45,6 @@ 'test.skip' : ('./test/item.py', 'skip'), 'test.fail' : ('./test/item.py', 'fail'), 'test.exit' : ('./test/session.py', 'exit'), - 'test.broken' : ('./test/item.py', 'Broken'), - 'test.notimplemented' : ('./test/item.py', '_NotImplemented'), # configuration/initialization related test api 'test.config' : ('./test/config.py', 'config_per_process'), Modified: py/release/0.9.x/py/test/item.py ============================================================================== --- py/release/0.9.x/py/test/item.py (original) +++ py/release/0.9.x/py/test/item.py Mon Aug 18 13:27:17 2008 @@ -78,14 +78,6 @@ def __repr__(self): return self.msg -class Broken(BaseReason): - def __repr__(self): - return "Broken: %s" % (self.msg,) - -class _NotImplemented(BaseReason): - def __repr__(self): - return "Not implemented: %s" % (self.msg,) - # whatever comes here.... def skip(msg=BaseReason()): Modified: py/release/0.9.x/py/test/testing/test_session.py ============================================================================== --- py/release/0.9.x/py/test/testing/test_session.py (original) +++ py/release/0.9.x/py/test/testing/test_session.py Mon Aug 18 13:27:17 2008 @@ -314,24 +314,3 @@ expected_output = '\nE ' + line_to_report + '\n' print 'Looking for:', expected_output assert expected_output in out - - -def test_skip_reasons(): - tmp = py.test.ensuretemp("check_skip_reasons") - tmp.ensure("test_one.py").write(py.code.Source(""" - import py - def test_1(): - py.test.skip(py.test.broken('stuff')) - - def test_2(): - py.test.skip(py.test.notimplemented('stuff')) - """)) - tmp.ensure("__init__.py") - config = py.test.config._reparse([tmp]) - session = config.initsession() - session.main() - skips = session.getitemoutcomepairs(Skipped) - assert len(skips) == 2 - assert repr(skips[0][1]) == 'Broken: stuff' - assert repr(skips[1][1]) == 'Not implemented: stuff' - From hpk at codespeak.net Mon Aug 18 13:32:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 13:32:41 +0200 (CEST) Subject: [py-svn] r57406 - py/release/0.9.x Message-ID: <20080818113241.163F1169F52@codespeak.net> Author: hpk Date: Mon Aug 18 13:32:39 2008 New Revision: 57406 Modified: py/release/0.9.x/CHANGELOG (contents, props changed) Log: revisit changelog Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Mon Aug 18 13:32:39 2008 @@ -1,24 +1,26 @@ +$Id$ + Changes between 0.9.1 and 0.9.2 =============================== -* much refined installation, metadata, created new setup.py, +* refined installation and metadata, created new setup.py, now based on setuptools/ez_setup (by flipping a bit you - can have good old distutils). + can use distutils). * improved the way of making py.* scripts available in windows environments, they are now added to the Scripts directory as ".cmd" files. +* improving stability for py.execnet on windows + * py.test's traceback is better parseable from editors (follows the filenames:LINENO: MSG convention) -* improving stability for py.execnet on windows - -* fix to make "py.test --runbrowser" work if - Javascript is generated +* fix to javascript-generation, "py.test --runbrowser" + should work more reliably now -* removed randomly added py.test.broken and - py.test.notimplemented helpers. +* removed previously accidentally added + py.test.broken and py.test.notimplemented helpers. Changes between 0.9.0 and 0.9.1 =============================== From hpk at codespeak.net Mon Aug 18 13:35:59 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 13:35:59 +0200 (CEST) Subject: [py-svn] r57407 - in py/release/0.9.x: . py Message-ID: <20080818113559.9847C169F1A@codespeak.net> Author: hpk Date: Mon Aug 18 13:35:57 2008 New Revision: 57407 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: new setup/manifest/version Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Mon Aug 18 13:35:57 2008 @@ -1,3 +1,4 @@ +CHANGELOG LICENSE MANIFEST README.txt @@ -130,7 +131,6 @@ py/doc/__init__.py py/doc/apigen.txt py/doc/bin.txt -py/doc/changes-0.9.1.txt py/doc/code.txt py/doc/coding-style.txt py/doc/confrest.py @@ -295,6 +295,7 @@ py/rest/testing/data/part1.txt py/rest/testing/data/part2.txt py/rest/testing/data/tocdepth.rst2pdfconfig +py/rest/testing/setup.py py/rest/testing/test_convert.py py/rest/testing/test_directive.py py/rest/testing/test_htmlrest.py Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Mon Aug 18 13:35:57 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-3" +version = "0.9.2-alpha-4" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 13:35:57 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-3', + version='0.9.2-alpha-4', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -130,7 +130,6 @@ 'doc/TODO.txt', 'doc/apigen.txt', 'doc/bin.txt', - 'doc/changes-0.9.1.txt', 'doc/code.txt', 'doc/coding-style.txt', 'doc/contact.txt', From hpk at codespeak.net Mon Aug 18 13:50:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 13:50:10 +0200 (CEST) Subject: [py-svn] r57410 - in py/release/0.9.x: . py Message-ID: <20080818115010.E08AD168026@codespeak.net> Author: hpk Date: Mon Aug 18 13:50:09 2008 New Revision: 57410 Modified: py/release/0.9.x/CHANGELOG py/release/0.9.x/py/__init__.py py/release/0.9.x/py/initpkg.py py/release/0.9.x/setup.py Log: another version, added py.__version__ attribute Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Mon Aug 18 13:50:09 2008 @@ -11,7 +11,7 @@ windows environments, they are now added to the Scripts directory as ".cmd" files. -* improving stability for py.execnet on windows +* improving stability of py.execnet on windows * py.test's traceback is better parseable from editors (follows the filenames:LINENO: MSG convention) @@ -22,6 +22,8 @@ * removed previously accidentally added py.test.broken and py.test.notimplemented helpers. +* there now is a py.__version__ attribute + Changes between 0.9.0 and 0.9.1 =============================== Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Mon Aug 18 13:50:09 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-4" +version = "0.9.2-alpha-5" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/py/initpkg.py ============================================================================== --- py/release/0.9.x/py/initpkg.py (original) +++ py/release/0.9.x/py/initpkg.py Mon Aug 18 13:50:09 2008 @@ -32,7 +32,7 @@ # --------------------------------------------------- class Package(object): - def __init__(self, name, exportdefs): + def __init__(self, name, exportdefs, metainfo): pkgmodule = sys.modules[name] assert pkgmodule.__name__ == name self.name = name @@ -54,6 +54,13 @@ # inhibit further direct filesystem imports through the package module del pkgmodule.__path__ + # set metainfo + for name, value in metainfo.items(): + setattr(self, name, value) + version = metainfo.get('version', None) + if version: + pkgmodule.__version__ = version + def _resolve(self, extpyish): """ resolve a combined filesystem/python extpy-ish path. """ fspath, modpath = extpyish @@ -229,9 +236,7 @@ def initpkg(pkgname, exportdefs, **kw): #print "initializing package", pkgname # bootstrap Package object - pkg = Package(pkgname, exportdefs) - for name, value in kw.items(): - setattr(pkg, name, value) + pkg = Package(pkgname, exportdefs, kw) seen = { pkgname : pkg.module } deferred_imports = [] Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 13:50:09 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-4', + version='0.9.2-alpha-5', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], From hpk at codespeak.net Mon Aug 18 14:42:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 14:42:02 +0200 (CEST) Subject: [py-svn] r57418 - py/release/0.9.x/py/apigen/tracer/testing Message-ID: <20080818124202.612BB169F44@codespeak.net> Author: hpk Date: Mon Aug 18 14:41:59 2008 New Revision: 57418 Modified: py/release/0.9.x/py/apigen/tracer/testing/test_package.py Log: skipping this test on windows for now. Modified: py/release/0.9.x/py/apigen/tracer/testing/test_package.py ============================================================================== --- py/release/0.9.x/py/apigen/tracer/testing/test_package.py (original) +++ py/release/0.9.x/py/apigen/tracer/testing/test_package.py Mon Aug 18 14:41:59 2008 @@ -8,6 +8,7 @@ import sys import py + def setup_module(mod): sys.path.insert(0, str(py.path.local(__file__).dirpath().join("package"))) import submodule @@ -24,6 +25,8 @@ def test_init(self): ds = self.ds print py.builtin.sorted(ds.descs.keys()) + if sys.platform == "win32": + py.test.skip("not sure why, but this fails with 4 == 6") assert len(ds.descs) == 6 assert py.builtin.sorted(ds.descs.keys()) == [ 'notpak.notmod.notclass', 'notpak.notmod.notclass.__init__', From hpk at codespeak.net Mon Aug 18 15:26:04 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 15:26:04 +0200 (CEST) Subject: [py-svn] r57424 - in py/release/0.9.x: . py py/doc Message-ID: <20080818132604.2FCB6169F49@codespeak.net> Author: hpk Date: Mon Aug 18 15:26:03 2008 New Revision: 57424 Added: py/release/0.9.x/py/doc/release-0.9.2.txt Modified: py/release/0.9.x/CHANGELOG py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: draft 0.9.2 announcement Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Mon Aug 18 15:26:03 2008 @@ -11,7 +11,7 @@ windows environments, they are now added to the Scripts directory as ".cmd" files. -* improving stability of py.execnet on windows +* improved py.execnet bootstrapping on windows * py.test's traceback is better parseable from editors (follows the filenames:LINENO: MSG convention) Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Mon Aug 18 15:26:03 2008 @@ -159,6 +159,7 @@ py/doc/path.txt py/doc/release-0.9.0.txt py/doc/release-0.9.1.txt +py/doc/release-0.9.2.txt py/doc/style.css py/doc/test.txt py/doc/test_conftest.py Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Mon Aug 18 15:26:03 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-5" +version = "0.9.2-alpha-6" initpkg(__name__, description = "py lib: agile development and test support library", Added: py/release/0.9.x/py/doc/release-0.9.2.txt ============================================================================== --- (empty file) +++ py/release/0.9.x/py/doc/release-0.9.2.txt Mon Aug 18 15:26:03 2008 @@ -0,0 +1,24 @@ +py lib 0.9.2: bugfix release +============================= + +Welcome to the 0.9.2 py lib and py.test release - +mainly fixing Windows issues, providing better +packaging and integration with setuptools. + +Summary of main feature of the py lib: + +* py.test: cross-project testing tool with many advanced features +* py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes +* py.magic.greenlet: micro-threads on standard CPython ("stackless-light") +* py.path: path abstractions over local and subversion files +* rich documentation of py's exported API +* tested against Linux, Win32, OSX, works on python 2.3-2.6 + +See here for more information: + +Download/Install: http://codespeak.net/py/0.9.2/download.html +Documentation/API: http://codespeak.net/py/0.9.2/index.html + +best and have fun, + +holger krekel Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 15:26:03 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-5', + version='0.9.2-alpha-6', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -150,6 +150,7 @@ 'doc/path.txt', 'doc/release-0.9.0.txt', 'doc/release-0.9.1.txt', + 'doc/release-0.9.2.txt', 'doc/style.css', 'doc/test.txt', 'doc/why_py.txt', From hpk at codespeak.net Mon Aug 18 17:08:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 17:08:41 +0200 (CEST) Subject: [py-svn] r57429 - in py/trunk: . py py/apigen/tracer/testing py/c-extension py/compat/testing py/doc py/execnet/script py/misc py/misc/testing py/rest/testing py/rest/testing/data Message-ID: <20080818150841.3EE89169F2F@codespeak.net> Author: hpk Date: Mon Aug 18 17:08:39 2008 New Revision: 57429 Added: py/trunk/CHANGELOG - copied unchanged from r57428, py/release/0.9.x/CHANGELOG py/trunk/LICENSE - copied unchanged from r57428, py/release/0.9.x/LICENSE py/trunk/MANIFEST - copied unchanged from r57428, py/release/0.9.x/MANIFEST py/trunk/README.txt - copied unchanged from r57428, py/release/0.9.x/README.txt py/trunk/_findpy.py - copied unchanged from r57428, py/release/0.9.x/_findpy.py py/trunk/ez_setup.py - copied unchanged from r57428, py/release/0.9.x/ez_setup.py py/trunk/py/doc/release-0.9.2.txt - copied unchanged from r57428, py/release/0.9.x/py/doc/release-0.9.2.txt py/trunk/py/execnet/script/__init__.py - copied unchanged from r57428, py/release/0.9.x/py/execnet/script/__init__.py py/trunk/py/rest/testing/setup.py - copied unchanged from r57428, py/release/0.9.x/py/rest/testing/setup.py Removed: py/trunk/py/c-extension/conftest.py py/trunk/py/compat/testing/_findpy.py py/trunk/py/misc/testing/test_update_website.py Modified: py/trunk/ (props changed) py/trunk/py/LICENSE py/trunk/py/__init__.py py/trunk/py/apigen/tracer/testing/test_package.py py/trunk/py/doc/bin.txt py/trunk/py/initpkg.py py/trunk/py/misc/buildcmodule.py py/trunk/py/misc/testing/test_initpkg.py py/trunk/py/rest/testing/data/ (props changed) py/trunk/py/rest/testing/test_convert.py py/trunk/py/rest/testing/test_directive.py py/trunk/py/rest/testing/test_htmlrest.py py/trunk/py/rest/testing/test_rst2pdf.py py/trunk/setup.py Log: merging/porting the release branch changes and cleanups to trunk. svn merge -r 56716:HEAD ../release/0.9.x/ to trunk Modified: py/trunk/py/LICENSE ============================================================================== --- py/trunk/py/LICENSE (original) +++ py/trunk/py/LICENSE Mon Aug 18 17:08:39 2008 @@ -1,25 +1,28 @@ -py lib Copyright holders, 2003-2005 +py lib Copyright holders, 2003-2008 ======================================= Except when otherwise stated (look for LICENSE files or information at the beginning of each file) the files in the 'py' directory are -copyrighted by one or more of the following people and organizations: +copyrighted by one or more of the following people and organizations: - Holger Krekel - merlinux GmbH, Germany - Armin Rigo - Carl Friedrich Bolz - Maciek Fijalkowski - Guido Wesdorp - Jan Balster + Holger Krekel, holger at merlinux eu + Guido Wesdorp, johnny at johnnydebris net + Carl Friedrich Bolz, cfbolz at gmx de + Armin Rigo, arigo at tunes org + Maciek Fijalkowski, fijal at genesilico.pl + Briand Dorsey, briandorsey at gmail com + merlinux GmbH, Germany, office at merlinux eu Contributors include:: - Ian Bicking - Grig Gheorghiu - Bob Ippolito - Christian Tismer - Samuele Pedroni + Samuele Pedroni + Harald Armin Massa + Ralf Schmitt + Ian Bicking + Jan Balster + Grig Gheorghiu + Bob Ippolito + Christian Tismer Except when otherwise stated (look for LICENSE files or information at the beginning of each file) all files in the 'py' directory are Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Mon Aug 18 17:08:39 2008 @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- """ - the py lib is a development support library featuring - py.test, ad-hoc distributed execution, micro-threads - and svn abstractions. +the py lib is a development support library featuring +py.test, ad-hoc distributed execution, micro-threads +and svn abstractions. """ from initpkg import initpkg @@ -14,13 +14,27 @@ revision = int('$LastChangedRevision$'.split(':')[1][:-1]), lastchangedate = '$LastChangedDate$', version = version, - url = "http://codespeak.net/py", - download_url = "XXX", # "http://codespeak.net/download/py/py-%s.tar.gz" %(version,), + url = "http://pylib.org", + download_url = "http://pypi.python.org/pypi?:action=display&name=py", license = "MIT license", - platforms = ['unix', 'linux', 'cygwin', 'win32'], - author = "holger krekel, Carl Friedrich Bolz, Guido Wesdorp, Maciej Fijalkowski, Armin Rigo & others", - author_email = "py-dev at codespeak.net", + platforms = ['unix', 'linux', 'osx', 'cygwin', 'win32'], + author = "holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others", + author_email = "holger at merlinux.eu, py-dev at codespeak.net", long_description = globals()['__doc__'], + classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX", + "Operating System :: Microsoft :: Windows", + "Operating System :: MacOS :: MacOS X", + "Topic :: Software Development :: Testing", + "Topic :: Software Development :: Libraries", + "Topic :: System :: Distributed Computing", + "Topic :: Utilities", + "Programming Language :: Python", + ], + # EXPORTED API exportdefs = { Modified: py/trunk/py/apigen/tracer/testing/test_package.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_package.py (original) +++ py/trunk/py/apigen/tracer/testing/test_package.py Mon Aug 18 17:08:39 2008 @@ -8,6 +8,7 @@ import sys import py + def setup_module(mod): sys.path.insert(0, str(py.path.local(__file__).dirpath().join("package"))) import submodule @@ -24,6 +25,8 @@ def test_init(self): ds = self.ds print py.builtin.sorted(ds.descs.keys()) + if sys.platform == "win32": + py.test.skip("not sure why, but this fails with 4 == 6") assert len(ds.descs) == 6 assert py.builtin.sorted(ds.descs.keys()) == [ 'notpak.notmod.notclass', 'notpak.notmod.notclass.__init__', Deleted: /py/trunk/py/c-extension/conftest.py ============================================================================== --- /py/trunk/py/c-extension/conftest.py Mon Aug 18 17:08:39 2008 +++ (empty file) @@ -1,14 +0,0 @@ -import py - -class Directory(py.test.collect.Directory): - # XXX see in which situations/platforms we want - # run tests here - #def recfilter(self, path): - # if py.std.sys.platform == 'linux2': - # if path.basename == 'greenlet': - # return False - # return super(Directory, self).recfilter(path) - - #def run(self): - # py.test.skip("c-extension testing needs platform selection") - pass Deleted: /py/trunk/py/compat/testing/_findpy.py ============================================================================== --- /py/trunk/py/compat/testing/_findpy.py Mon Aug 18 17:08:39 2008 +++ (empty file) @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# -# find and import a version of 'py' -# -import sys -import os -from os.path import dirname as opd, exists, join, basename, abspath - -def searchpy(current): - while 1: - last = current - initpy = join(current, '__init__.py') - if not exists(initpy): - pydir = join(current, 'py') - # recognize py-package and ensure it is importable - if exists(pydir) and exists(join(pydir, '__init__.py')): - #for p in sys.path: - # if p == current: - # return True - if current != sys.path[0]: # if we are already first, then ok - print >>sys.stderr, "inserting into sys.path:", current - sys.path.insert(0, current) - return True - current = opd(current) - if last == current: - return False - -if not searchpy(abspath(os.curdir)): - if not searchpy(opd(abspath(sys.argv[0]))): - if not searchpy(opd(__file__)): - pass # let's hope it is just on sys.path - -import py - -if __name__ == '__main__': - print "py lib is at", py.__file__ Modified: py/trunk/py/doc/bin.txt ============================================================================== --- py/trunk/py/doc/bin.txt (original) +++ py/trunk/py/doc/bin.txt Mon Aug 18 17:08:39 2008 @@ -52,7 +52,9 @@ Usage: ``py.rest [PATHS] [options]`` Loot recursively for .txt files starting from ``PATHS`` and convert them to -html using docutils (or to pdf files, if the --pdf option is used). +html using docutils or to pdf files, if the ``--pdf`` option is used. For +conversion to PDF you will need several command line tools, on Ubuntu Linux +this is **texlive** and **texlive-extra-utils**. ``py.rest`` has some extra features over rst2html (which is shipped with docutils). Most of these are still experimental, the one which is most likely Modified: py/trunk/py/initpkg.py ============================================================================== --- py/trunk/py/initpkg.py (original) +++ py/trunk/py/initpkg.py Mon Aug 18 17:08:39 2008 @@ -32,7 +32,7 @@ # --------------------------------------------------- class Package(object): - def __init__(self, name, exportdefs): + def __init__(self, name, exportdefs, metainfo): pkgmodule = sys.modules[name] assert pkgmodule.__name__ == name self.name = name @@ -54,6 +54,13 @@ # inhibit further direct filesystem imports through the package module del pkgmodule.__path__ + # set metainfo + for name, value in metainfo.items(): + setattr(self, name, value) + version = metainfo.get('version', None) + if version: + pkgmodule.__version__ = version + def _resolve(self, extpyish): """ resolve a combined filesystem/python extpy-ish path. """ fspath, modpath = extpyish @@ -229,9 +236,7 @@ def initpkg(pkgname, exportdefs, **kw): #print "initializing package", pkgname # bootstrap Package object - pkg = Package(pkgname, exportdefs) - for name, value in kw.items(): - setattr(pkg, name, value) + pkg = Package(pkgname, exportdefs, kw) seen = { pkgname : pkg.module } deferred_imports = [] Modified: py/trunk/py/misc/buildcmodule.py ============================================================================== --- py/trunk/py/misc/buildcmodule.py (original) +++ py/trunk/py/misc/buildcmodule.py Mon Aug 18 17:08:39 2008 @@ -32,7 +32,7 @@ lib = dirpath.join(modname+ext) # XXX argl! we need better "build"-locations alltogether! - if lib.check(): + if lib.check() and lib.stat().mtime < cfile.stat().mtime: try: lib.remove() except EnvironmentError: Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Mon Aug 18 17:08:39 2008 @@ -255,13 +255,3 @@ # help(std.path) # #assert False -def test_url_of_version(): - #py.test.skip("FAILING! - provide a proper URL or upload pylib tgz") - from urllib import URLopener - url = py.__pkg__.download_url - if url.lower() == "xxx": - assert py.__pkg__.version.find("alpha") != -1 - else: - URLopener().open(url) - - Deleted: /py/trunk/py/misc/testing/test_update_website.py ============================================================================== --- /py/trunk/py/misc/testing/test_update_website.py Mon Aug 18 17:08:39 2008 +++ (empty file) @@ -1,75 +0,0 @@ -import py -import sys - -here = py.magic.autopath().dirpath() -update_website = here.join('../../bin/_update_website.py').pyimport() - -def test_rsync(): - temp = py.test.ensuretemp('update_website_rsync') - pkgpath = temp.join('pkg') - apipath = temp.join('apigen') - pkgpath.ensure('foo/bar.txt', file=True).write('baz') - pkgpath.ensure('spam/eggs.txt', file=True).write('spam') - apipath.ensure('api/foo.html', file=True).write('') - apipath.ensure('source/spam.html', file=True).write('') - - rsyncpath = temp.join('rsync') - assert not rsyncpath.check() - gateway = py.execnet.PopenGateway() - update_website.rsync(pkgpath, apipath, gateway, rsyncpath.strpath) - assert rsyncpath.check(dir=True) - assert rsyncpath.join('pkg').check(dir=True) - assert rsyncpath.join('pkg/spam/eggs.txt').read() == 'spam' - assert rsyncpath.join('apigen').check(dir=True) - assert rsyncpath.join('apigen/api/foo.html').read() == '' - -def setup_pkg(testname): - temp = py.test.ensuretemp(testname) - pkgpath = temp.ensure('pkg', dir=True) - pyfile = pkgpath.ensure('mod.py').write(py.code.Source(""" - def foo(x): - return x + 1 - """)) - testfile = pkgpath.ensure('test/test_mod.py').write(py.code.Source(""" - from pkg.sub import foo - def test_foo(): - assert foo(1) == 2 - """)) - initfile = pkgpath.ensure('__init__.py').write(py.code.Source(""" - import py - from py.__.initpkg import initpkg - initpkg(__name__, exportdefs={ - 'sub.foo': ('./mod.py', 'foo'), - }) - """)) - initfile = pkgpath.ensure('apigen/apigen.py').write(py.code.Source(""" - from py.__.apigen.apigen import build, \ - get_documentable_items_pkgdir as get_documentable_items - """)) - return pkgpath - -def test_run_tests(): - if py.std.sys.platform == "win32": - py.test.skip("update_website is not supposed to be run from win32") - pkgpath = setup_pkg('update_website_run_tests') - errors = update_website.run_tests(pkgpath, - pkgpath.dirpath().join('apigen'), - captureouterr=True) - print errors - assert not errors - py.test.skip("Apigen turned off") - assert pkgpath.join('../apigen').check(dir=True) - assert pkgpath.join('../apigen/api/sub.foo.html').check(file=True) - -def test_run_tests_failure(): - if py.std.sys.platform == "win32": - py.test.skip("update_website is not supposed to be run from win32") - pkgpath = setup_pkg('update_website_run_tests_failure') - assert not pkgpath.join('../apigen').check(dir=True) - py.test.skip("Apigen turned off") - pkgpath.ensure('../apigen', file=True) - errors = update_website.run_tests(pkgpath, - pkgpath.dirpath().join('apigen'), - captureouterr=True) - assert errors # some error message - Modified: py/trunk/py/rest/testing/test_convert.py ============================================================================== --- py/trunk/py/rest/testing/test_convert.py (original) +++ py/trunk/py/rest/testing/test_convert.py Mon Aug 18 17:08:39 2008 @@ -1,13 +1,13 @@ import py from py.__.rest.convert import convert_dot, latexformula2png - -datadir = py.magic.autopath().dirpath().join("data") +from py.__.rest.testing.setup import getdata def setup_module(mod): required = 'gs', 'dot', 'latex', 'epstopdf', for exe in required: if not py.path.local.sysfind(exe): py.test.skip("%r not found, required: %r" %(exe, required)) + mod.datadir = getdata() def test_convert_dot(): # XXX not really clear that the result is valid pdf/eps Modified: py/trunk/py/rest/testing/test_directive.py ============================================================================== --- py/trunk/py/rest/testing/test_directive.py (original) +++ py/trunk/py/rest/testing/test_directive.py Mon Aug 18 17:08:39 2008 @@ -7,9 +7,11 @@ 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 -datadir = py.magic.autopath().dirpath().join("data") -testdir = py.test.ensuretemp("rest") +def setup_module(mod): + mod.datadir = getdata() + mod.testdir = py.test.ensuretemp("rest") class TestGraphviz(object): def _graphviz_html(self): Modified: py/trunk/py/rest/testing/test_htmlrest.py ============================================================================== --- py/trunk/py/rest/testing/test_htmlrest.py (original) +++ py/trunk/py/rest/testing/test_htmlrest.py Mon Aug 18 17:08:39 2008 @@ -2,6 +2,7 @@ import py from py.__.misc import rest +from py.__.rest.testing.setup import getdata def setup_module(mod): if not py.path.local.sysfind("gs") or \ @@ -13,12 +14,12 @@ except ImportError: py.test.skip("docutils not present") -data = py.magic.autopath().dirpath().join("data") + mod.datadir = getdata() def test_process_simple(): # fallback test: only checks that no exception is raised def rec(p): return p.check(dotfile=0) - for x in data.visit("*.txt", rec=rec): + for x in datadir.visit("*.txt", rec=rec): yield rest.process, x Modified: py/trunk/py/rest/testing/test_rst2pdf.py ============================================================================== --- py/trunk/py/rest/testing/test_rst2pdf.py (original) +++ py/trunk/py/rest/testing/test_rst2pdf.py Mon Aug 18 17:08:39 2008 @@ -2,6 +2,8 @@ import py from py.__.rest.latex import process_configfile, process_rest_file +from py.__.rest.testing.setup import getdata + try: import docutils except ImportError: @@ -13,22 +15,20 @@ not py.path.local.sysfind("dot") or \ not py.path.local.sysfind("latex"): py.test.skip("ghostscript, graphviz and latex needed") - mod.data = py.magic.autopath().dirpath().join("data") + mod.datadir = getdata() class TestRst2Pdf(object): def _process_rest_file(self): - data = py.magic.autopath().dirpath().join("data") - part2 = data.join("part1.txt") + part2 = datadir.join("part1.txt") pdf = part2.new(ext="pdf") process_rest_file(part2) assert pdf.check() pdf.remove() def _process_configfile(self): - data = py.magic.autopath().dirpath().join("data") - config = data.join("example.rst2pdfconfig") + config = datadir.join("example.rst2pdfconfig") pdf = config.new(ext="pdf") - tex = data.join('example.tex') + tex = datadir.join('example.tex') process_configfile(config, debug=True) assert pdf.check() assert tex.check() @@ -45,9 +45,9 @@ def rec(p): return p.check(dotfile=0) - for x in data.visit("*.rst2pdfconfig", rec=rec): + for x in datadir.visit("*.rst2pdfconfig", rec=rec): process_configfile(x) - for x in data.visit("*.txt", rec=rec): + for x in datadir.visit("*.txt", rec=rec): process_rest_file(x) def test_rst2pdf(self): Modified: py/trunk/setup.py ============================================================================== --- py/trunk/setup.py (original) +++ py/trunk/setup.py Mon Aug 18 17:08:39 2008 @@ -1,3 +1,207 @@ -import py -from py.__.misc._dist import setup -setup(py) +""" + setup file for 'py' package based on: + + https://codespeak.net/svn/py/release/0.9.x, revision=57387 + + autogenerated by gensetup.py +""" +import os, sys + +if 1: # set to zero if you want plain distutils + import ez_setup + ez_setup.use_setuptools() + from setuptools import setup, Extension +else: + from distutils.core import setup, Extension + +def main(): + setup(cmdclass=cmdclass, + name='py', + description='py lib: agile development and test support library', + version='0.9.2-alpha-6', + url='http://pylib.org', + license='MIT license', + platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], + author='holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others', + author_email='holger at merlinux.eu, py-dev at codespeak.net', + ext_modules = [Extension("py.c-extension.greenlet.greenlet", + ["py/c-extension/greenlet/greenlet.c"]),], + + py_modules=['_findpy'], + long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', + classifiers=['Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Operating System :: POSIX', + 'Operating System :: Microsoft :: Windows', + 'Operating System :: MacOS :: MacOS X', + 'Topic :: Software Development :: Testing', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Distributed Computing', + 'Topic :: Utilities', + 'Programming Language :: Python'], + scripts=['py/bin/py.cleanup', + 'py/bin/py.countloc', + 'py/bin/py.lookup', + 'py/bin/py.rest', + 'py/bin/py.test'], + packages=['py', + 'py.apigen', + 'py.apigen.rest', + 'py.apigen.rest.testing', + 'py.apigen.source', + 'py.apigen.source.testing', + 'py.apigen.testing', + 'py.apigen.tracer', + 'py.apigen.tracer.testing', + 'py.apigen.tracer.testing.package', + 'py.apigen.tracer.testing.package.submodule', + 'py.apigen.tracer.testing.package.submodule.pak', + 'py.builtin', + 'py.builtin.testing', + 'py.code', + 'py.code.testing', + 'py.compat', + 'py.compat.testing', + 'py.doc', + 'py.execnet', + 'py.execnet.script', + 'py.execnet.testing', + 'py.io', + 'py.io.test', + 'py.log', + 'py.log.testing', + 'py.magic', + 'py.magic.testing', + 'py.misc', + 'py.misc.cmdline', + 'py.misc.testing', + 'py.path', + 'py.path.gateway', + 'py.path.local', + 'py.path.local.testing', + 'py.path.svn', + 'py.path.svn.testing', + 'py.path.testing', + 'py.process', + 'py.process.testing', + 'py.rest', + 'py.rest.testing', + 'py.test', + 'py.test.rsession', + 'py.test.rsession.testing', + 'py.test.rsession.webdata', + 'py.test.terminal', + 'py.test.testing', + 'py.test.testing.import_test.package', + 'py.test.web', + 'py.thread', + 'py.thread.testing', + 'py.tool', + 'py.tool.testing', + 'py.xmlobj', + 'py.xmlobj.testing'], + package_data={'py': ['LICENSE', + 'apigen/api.js', + 'apigen/apigen.js', + 'apigen/source/index.cgi', + 'apigen/style.css', + 'apigen/todo-apigen.txt', + 'apigen/todo.txt', + 'bin/py.cleanup', + 'bin/py.countloc', + 'bin/py.lookup', + 'bin/py.rest', + 'bin/py.test', + 'c-extension/greenlet/README.txt', + 'c-extension/greenlet/greenlet.c', + 'c-extension/greenlet/greenlet.h', + 'c-extension/greenlet/slp_platformselect.h', + 'c-extension/greenlet/switch_amd64_unix.h', + 'c-extension/greenlet/switch_ppc_macosx.h', + 'c-extension/greenlet/switch_ppc_unix.h', + 'c-extension/greenlet/switch_s390_unix.h', + 'c-extension/greenlet/switch_sparc_sun_gcc.h', + 'c-extension/greenlet/switch_x86_msvc.h', + 'c-extension/greenlet/switch_x86_unix.h', + 'compat/LICENSE', + 'compat/testing/test_doctest.txt', + 'compat/testing/test_doctest2.txt', + 'doc/TODO.txt', + 'doc/apigen.txt', + 'doc/bin.txt', + 'doc/code.txt', + 'doc/coding-style.txt', + 'doc/contact.txt', + 'doc/download.txt', + 'doc/execnet.txt', + 'doc/future.txt', + 'doc/future/code_template.txt', + 'doc/future/planning.txt', + 'doc/future/pylib_pypy.txt', + 'doc/future/rsession_todo.txt', + 'doc/greenlet.txt', + 'doc/impl-test.txt', + 'doc/index.txt', + 'doc/io.txt', + 'doc/links.txt', + 'doc/log.txt', + 'doc/misc.txt', + 'doc/path.txt', + 'doc/release-0.9.0.txt', + 'doc/release-0.9.1.txt', + 'doc/release-0.9.2.txt', + 'doc/style.css', + 'doc/test.txt', + 'doc/why_py.txt', + 'doc/xml.txt', + 'env.cmd', + 'execnet/NOTES', + 'misc/testing/data/svnlookrepo.dump', + 'path/gateway/TODO.txt', + 'path/svn/quoting.txt', + 'path/svn/testing/repotest.dump', + 'rest/rest.sty.template', + 'rest/testing/data/example.rst2pdfconfig', + 'rest/testing/data/example1.dot', + 'rest/testing/data/formula.txt', + 'rest/testing/data/formula1.txt', + 'rest/testing/data/graphviz.txt', + 'rest/testing/data/part1.txt', + 'rest/testing/data/part2.txt', + 'rest/testing/data/tocdepth.rst2pdfconfig', + 'test/rsession/webdata/index.html', + 'test/rsession/webdata/source.js']}, + zip_safe=False, + ) + +# on windows we need to hack up the to-be-installed scripts +from distutils.command.install_scripts import install_scripts +class my_install_scripts(install_scripts): + def run(self): + install_scripts.run(self) + #print self.outfiles + for fn in self.outfiles: + basename = os.path.basename(fn) + if "." in basename: + #print "tackling", fn + newbasename = basename.replace(".", "_") + newfn = os.path.join(os.path.dirname(fn), newbasename) + if os.path.exists(newfn): + os.remove(newfn) + os.rename(fn, newfn) + newname = fn + ".cmd" + if os.path.exists(newname): + os.remove(newname) + f = open(newname, 'w') + f.write("@echo off\n") + f.write('python "%%~dp0\%s" %%*\n' % newbasename) + f.close() +if sys.platform == "win32": + cmdclass = {'install_scripts': my_install_scripts} +else: + cmdclass = {} + +if __name__ == '__main__': + main() + \ No newline at end of file From hpk at codespeak.net Mon Aug 18 17:50:57 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 17:50:57 +0200 (CEST) Subject: [py-svn] r57430 - py/release/0.9.x/py/path/svn Message-ID: <20080818155057.BE450169F0F@codespeak.net> Author: hpk Date: Mon Aug 18 17:50:54 2008 New Revision: 57430 Modified: py/release/0.9.x/py/path/svn/urlcommand.py Log: port from trunk 56512 (svn 1.5 chris lamb) Modified: py/release/0.9.x/py/path/svn/urlcommand.py ============================================================================== --- py/release/0.9.x/py/path/svn/urlcommand.py (original) +++ py/release/0.9.x/py/path/svn/urlcommand.py Mon Aug 18 17:50:54 2008 @@ -239,7 +239,9 @@ for lsline in lines: if lsline: info = InfoSvnCommand(lsline) - nameinfo_seq.append((info._name, info)) + if info._name != '.': # svn 1.5 produces '.' dirs, + nameinfo_seq.append((info._name, info)) + return nameinfo_seq auth = self.auth and self.auth.makecmdoptions() or None if self.rev is not None: From hpk at codespeak.net Mon Aug 18 17:51:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 17:51:46 +0200 (CEST) Subject: [py-svn] r57431 - py/release/0.9.x/py/apigen/tracer Message-ID: <20080818155146.A449A168503@codespeak.net> Author: hpk Date: Mon Aug 18 17:51:44 2008 New Revision: 57431 Modified: py/release/0.9.x/py/apigen/tracer/docstorage.py Log: applying chris' fix for generating docs for platforms without greenlets Modified: py/release/0.9.x/py/apigen/tracer/docstorage.py ============================================================================== --- py/release/0.9.x/py/apigen/tracer/docstorage.py (original) +++ py/release/0.9.x/py/apigen/tracer/docstorage.py Mon Aug 18 17:51:44 2008 @@ -21,8 +21,21 @@ for key, value in defs.iteritems(): chain = key.split('.') base = module - for elem in chain: - base = getattr(base, elem) + # XXX generalize this: + # a bit of special casing for greenlets which are + # not available on all the platforms that python/py + # lib runs + try: + for elem in chain: + base = getattr(base, elem) + except RuntimeError, exc: + if elem == "greenlet": + print exc.__class__.__name__, exc + print "Greenlets not supported on this platform. Skipping apigen doc for this module" + continue + else: + raise + if value[1] == '*': d.update(get_star_import_tree(base, key)) else: From hpk at codespeak.net Mon Aug 18 18:01:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 18:01:13 +0200 (CEST) Subject: [py-svn] r57432 - in py/release/0.9.x/py: . misc Message-ID: <20080818160113.F3256169EB0@codespeak.net> Author: hpk Date: Mon Aug 18 18:01:11 2008 New Revision: 57432 Modified: py/release/0.9.x/py/LICENSE py/release/0.9.x/py/misc/buildcmodule.py Log: have a flag (disabled by default and in the release branch) for dynamic re-compilation of c-modules. Modified: py/release/0.9.x/py/LICENSE ============================================================================== --- py/release/0.9.x/py/LICENSE (original) +++ py/release/0.9.x/py/LICENSE Mon Aug 18 18:01:11 2008 @@ -16,6 +16,7 @@ Contributors include:: Samuele Pedroni + Chris Lamb Harald Armin Massa Ralf Schmitt Ian Bicking Modified: py/release/0.9.x/py/misc/buildcmodule.py ============================================================================== --- py/release/0.9.x/py/misc/buildcmodule.py (original) +++ py/release/0.9.x/py/misc/buildcmodule.py Mon Aug 18 18:01:11 2008 @@ -3,6 +3,9 @@ """ import py +# set to true for automatic re-compilation of extensions +AUTOREGEN = False + # XXX we should distutils in a subprocess, because it messes up the # environment and who knows what else. Currently we just save # and restore os.environ. @@ -32,7 +35,7 @@ lib = dirpath.join(modname+ext) # XXX argl! we need better "build"-locations alltogether! - if lib.check() and lib.stat().mtime < cfile.stat().mtime: + if lib.check() and AUTOREGEN and lib.stat().mtime < cfile.stat().mtime: try: lib.remove() except EnvironmentError: From hpk at codespeak.net Mon Aug 18 18:04:03 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 18:04:03 +0200 (CEST) Subject: [py-svn] r57433 - in py: build release/0.9.x/py/bin Message-ID: <20080818160403.D744C169EE9@codespeak.net> Author: hpk Date: Mon Aug 18 18:04:02 2008 New Revision: 57433 Added: py/release/0.9.x/py/bin/_docgen.py - copied, changed from r57370, py/build/docgen.py Removed: py/build/docgen.py Log: moving _docgen script back to py/bin Deleted: /py/build/docgen.py ============================================================================== --- /py/build/docgen.py Mon Aug 18 18:04:02 2008 +++ (empty file) @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -""" quick tool to get documentation + apigen docs generated - - given a certain targetpath, apigen docs will be placed in 'apigen', - - user can choose to only build either docs or apigen docs: in this case, - the navigation bar will be adjusted -""" - -import _fixpythonpath - -import py -pypath = py.__pkg__.getpath() -print "using pypath", pypath - -def run_tests(path, envvars='', args=''): - pytestpath = pypath.join('bin/py.test') - cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % - (pypath.dirpath(), envvars, pytestpath, args, path)) - print cmd - py.process.cmdexec(cmd) - -def build_apigen_docs(targetpath, testargs=''): - run_tests(pypath, - 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( - targetpath,), - '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) - -def build_docs(targetpath, testargs): - docpath = pypath.join('doc') - run_tests(docpath, '', - testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,)) - docpath.copy(targetpath) - -def build_nav(targetpath, docs=True, api=True): - pass - -def build(targetpath, docs=True, api=True, testargs=''): - targetpath.ensure(dir=True) - if docs: - print 'building docs' - build_docs(targetpath, testargs) - if api: - print 'building api' - build_apigen_docs(targetpath, testargs) - - -if __name__ == '__main__': - import sys - if len(sys.argv) == 1: - print 'usage: %s [options]' - print - print ' targetdir: a path to a directory (created if it doesn\'t' - print ' exist) where the docs are put' - print ' options: options passed to py.test when running the tests' - sys.exit(1) - targetpath = py.path.local(sys.argv[1]) - args = ' '.join(sys.argv[2:]) - build(targetpath, True, True, args) - Copied: py/release/0.9.x/py/bin/_docgen.py (from r57370, py/build/docgen.py) ============================================================================== --- py/build/docgen.py (original) +++ py/release/0.9.x/py/bin/_docgen.py Mon Aug 18 18:04:02 2008 @@ -8,9 +8,7 @@ the navigation bar will be adjusted """ -import _fixpythonpath - -import py +from _findpy import py pypath = py.__pkg__.getpath() print "using pypath", pypath From hpk at codespeak.net Mon Aug 18 18:07:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 18:07:00 +0200 (CEST) Subject: [py-svn] r57434 - py/release/0.9.x/py/bin Message-ID: <20080818160700.E2851169F49@codespeak.net> Author: hpk Date: Mon Aug 18 18:06:59 2008 New Revision: 57434 Modified: py/release/0.9.x/py/bin/_docgen.py Log: making _docgen show more output during operations Modified: py/release/0.9.x/py/bin/_docgen.py ============================================================================== --- py/release/0.9.x/py/bin/_docgen.py (original) +++ py/release/0.9.x/py/bin/_docgen.py Mon Aug 18 18:06:59 2008 @@ -11,13 +11,15 @@ from _findpy import py pypath = py.__pkg__.getpath() print "using pypath", pypath +import os def run_tests(path, envvars='', args=''): pytestpath = pypath.join('bin/py.test') cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % (pypath.dirpath(), envvars, pytestpath, args, path)) print cmd - py.process.cmdexec(cmd) + errno = os.system(cmd) + assert not errno def build_apigen_docs(targetpath, testargs=''): run_tests(pypath, From hpk at codespeak.net Mon Aug 18 18:16:16 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 18:16:16 +0200 (CEST) Subject: [py-svn] r57436 - py/build Message-ID: <20080818161616.11696169F53@codespeak.net> Author: hpk Date: Mon Aug 18 18:16:15 2008 New Revision: 57436 Added: py/build/update_website.py - copied unchanged from r57370, py/build/_update_website.py Removed: py/build/_update_website.py Modified: py/build/gensetup.py py/build/makebdistegg.py Log: fixes to build scripts, rename update_website script Deleted: /py/build/_update_website.py ============================================================================== --- /py/build/_update_website.py Mon Aug 18 18:16:15 2008 +++ (empty file) @@ -1,89 +0,0 @@ -#!/usr/bin/env python - -""" run py.test with the --apigen option and rsync the results to a host - - rsyncs the whole package (with all the ReST docs converted to HTML) as well - as the apigen docs to a given remote host and path -""" -import _fixpythonpath -import py -import sys - -def rsync(pkgpath, apidocspath, gateway, remotepath): - """ copy the code and docs to the remote host """ - # copy to a temp dir first, even though both paths (normally) share the - # same parent dir, that may contain other stuff that we don't want to - # copy... - tempdir = py.test.ensuretemp('update_website_rsync_temp') - pkgpath.copy(tempdir.ensure(pkgpath.basename, dir=True)) - apidocspath.copy(tempdir.ensure(apidocspath.basename, dir=True)) - - rs = py.execnet.RSync(tempdir) - rs.add_target(gateway, remotepath, delete=True) - rs.send() - -def run_tests(pkgpath, apigenpath, args='', captureouterr=False): - """ run the unit tests and build the docs """ - pypath = py.__pkg__.getpath() - pytestpath = pypath.join('bin/py.test') - # XXX this would need a Windows specific version if we want to allow - # running this script on that platform, but currently --apigen doesn't - # work there anyway... - apigenscript = pkgpath.join('apigen/apigen.py') # XXX be more general here? - if not apigenscript.check(file=True): - apigenscript = pypath.join('apigen/apigen.py') - cmd = ('APIGENPATH="%s" PYTHONPATH="%s:%s" python ' - '"%s" %s --apigen="%s" "%s"' % (apigenpath, pypath.dirpath(), - pkgpath.dirpath(), pytestpath, - args, apigenscript, - pkgpath)) - if captureouterr: - cmd += ' > /dev/null 2>&1' - try: - output = py.process.cmdexec(cmd) - except py.error.Error, e: - return e.err or str(e) - return None - -def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=False): - print 'running tests' - errors = run_tests(pkgpath, apidocspath, args) - if errors: - print >>sys.stderr, \ - 'Errors while running the unit tests: %s' % (errors,) - if not ignorefail: - print >>sys.stderr, ('if you want to continue the update ' - 'regardless of failures, use --ignorefail') - sys.exit(1) - - print 'rsyncing' - gateway = py.execnet.SshGateway(rhost) - errors = rsync(pkgpath, apidocspath, gateway, rpath) - if errors: - print >>sys.stderr, 'Errors while rsyncing: %s' - sys.exit(1) - -if __name__ == '__main__': - args = sys.argv[1:] - if '--help' in args or '-h' in args: - print 'usage: %s [options]' - print - print 'run the py lib tests and update the py lib website' - print 'options:' - print ' --ignorefail: ignore errors in the unit tests and always' - print ' try to rsync' - print ' --help: show this message' - print - print 'any additional arguments are passed on as-is to the py.test' - print 'child process' - sys.exit() - ignorefail = False - if '--ignorefail' in args: - args.remove('--ignorefail') - ignorefail = True - args = ' '.join(sys.argv[1:]) - pkgpath = py.__pkg__.getpath() - apidocspath = pkgpath.dirpath().join('apigen') - main(pkgpath, apidocspath, 'codespeak.net', - '/home/guido/rsynctests', args, ignorefail) - Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Mon Aug 18 18:16:15 2008 @@ -42,7 +42,6 @@ if line.strip(): assert not line[:indent].strip(), line line = line[indent:] - print line self.lines.append(line) def write_winfuncs(self): @@ -51,9 +50,9 @@ def setup_header(self): - tooltime = "%s %s" %(py.std.time.asctime(), py.std.time.tzname[0]) + #tooltime = "%s %s" %(py.std.time.asctime(), py.std.time.tzname[0]) toolname = toolpath.basename - toolrevision = py.path.svnwc(toolpath).info().rev + #toolrevision = py.path.svnwc(toolpath).info().rev pkgname = self.pkg.__name__ url = self.wcinfo.url @@ -64,7 +63,7 @@ %(url)s, revision=%(rev)s - autogenerated by %(toolname)s, %(toolrevision)s, %(tooltime)s + autogenerated by %(toolname)s """ import os, sys ''' % locals()) @@ -90,7 +89,7 @@ #params['url'] = self.wcinfo.url #params['rev'] = self.wcinfo.rev params['long_description'] = reformat(params['long_description']) - print py.std.pprint.pprint(params) + #print py.std.pprint.pprint(params) self.append(''' def main(): setup(cmdclass=cmdclass, @@ -119,7 +118,6 @@ self.append_pprint(indent, zip_safe=False) self.lines.append(indent[4:] + ")\n") - def append_pprint(self, indent, **kw): for name, value in kw.items(): stringio = py.std.StringIO.StringIO() @@ -154,7 +152,8 @@ datafiles = [] pkgbase = self.wcbasedir.join(self.pkg.__name__) for p in self.allpaths: - if p.check(file=1) and not p.ext == ".py": + if p.check(file=1) and (not p.dirpath("__init__.py").check() + or p.ext != ".py"): if p.dirpath() != self.wcbasedir: datafiles.append(p.relto(pkgbase)) return {'py': datafiles} @@ -224,7 +223,7 @@ for p in self.allpaths: if p.check(dir=1): continue - toadd = p.relto(wcbasedir) + toadd = p.relto(self.wcbasedir) if toadd and toadd not in self.EXCLUDES: lines.append("%s" %(toadd)) targetfile = self.basedir.join("MANIFEST") @@ -235,7 +234,7 @@ self.write_manifest() self.write_setup() -if __name__ == "__main__": +def parseargs(): basedir = py.path.local(sys.argv[1]) if not basedir.check(): error("basedir not found: %s" %(basedir,)) @@ -248,6 +247,14 @@ wcbasedir = py.path.svnwc(basedir) if not wcbasedir.check(versioned=True): error("not a svn working copy:%s" % basedir) - + return basedir + +def main(basedir=None): + if basedir is None: + basedir = parseargs() + wcbasedir = py.path.svnwc(basedir) writer = SetupWriter(wcbasedir, py, setuptools=True) writer.write_all() + +if __name__ == "__main__": + main() Modified: py/build/makebdistegg.py ============================================================================== --- py/build/makebdistegg.py (original) +++ py/build/makebdistegg.py Mon Aug 18 18:16:15 2008 @@ -2,6 +2,10 @@ import os import sys +mydir = py.magic.autopath().dirpath() +gensetuppath = mydir.join("gensetup.py") +assert gensetuppath.check() + def trace(*args): print >>sys.stderr, " ".join(map(str, args)) @@ -81,6 +85,10 @@ def winexec(source): return gw.remote_exec(source, stdout=sys.stdout, stderr=sys.stderr) +def sysexec(cmd): + print "executing", cmd + os.system(cmd) + def main(): #errno = os.system("python setup.py sdist") #assert not errno @@ -88,8 +96,12 @@ #assert len(l) == 1 #sdist = l[0] - trace("gateway", gw) wc = py.path.svnwc() + #trace("regenerating setup") + #sysexec("python %s %s" %(gensetuppath, py.path.local())) + #wc.commit("auto-commit for building new eggs") + + trace("gateway", gw) remote_tmpdir = remote_chdirtemp() remote_checkout(wc.info().url) From hpk at codespeak.net Mon Aug 18 18:16:54 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 18:16:54 +0200 (CEST) Subject: [py-svn] r57437 - in py/release/0.9.x: . py Message-ID: <20080818161654.5D8FB168065@codespeak.net> Author: hpk Date: Mon Aug 18 18:16:53 2008 New Revision: 57437 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: regen setup and MANIFEST after fixes to gensetup Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Mon Aug 18 18:16:53 2008 @@ -61,6 +61,7 @@ py/apigen/tracer/testing/test_model.py py/apigen/tracer/testing/test_package.py py/apigen/tracer/tracer.py +py/bin/_docgen.py py/bin/_findpy.py py/bin/py.cleanup py/bin/py.countloc Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Mon Aug 18 18:16:53 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-6" +version = "0.9.2-alpha-7" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 18:16:53 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-6', + version='0.9.2-alpha-7', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -108,14 +108,18 @@ 'apigen/style.css', 'apigen/todo-apigen.txt', 'apigen/todo.txt', + 'bin/_docgen.py', + 'bin/_findpy.py', 'bin/py.cleanup', 'bin/py.countloc', 'bin/py.lookup', 'bin/py.rest', 'bin/py.test', 'c-extension/greenlet/README.txt', + 'c-extension/greenlet/dummy_greenlet.py', 'c-extension/greenlet/greenlet.c', 'c-extension/greenlet/greenlet.h', + 'c-extension/greenlet/setup.py', 'c-extension/greenlet/slp_platformselect.h', 'c-extension/greenlet/switch_amd64_unix.h', 'c-extension/greenlet/switch_ppc_macosx.h', @@ -124,6 +128,11 @@ 'c-extension/greenlet/switch_sparc_sun_gcc.h', 'c-extension/greenlet/switch_x86_msvc.h', 'c-extension/greenlet/switch_x86_unix.h', + 'c-extension/greenlet/test_generator.py', + 'c-extension/greenlet/test_generator_nested.py', + 'c-extension/greenlet/test_greenlet.py', + 'c-extension/greenlet/test_remote.py', + 'c-extension/greenlet/test_throw.py', 'compat/LICENSE', 'compat/testing/test_doctest.txt', 'compat/testing/test_doctest2.txt', @@ -134,6 +143,12 @@ 'doc/coding-style.txt', 'doc/contact.txt', 'doc/download.txt', + 'doc/example/genhtml.py', + 'doc/example/genhtmlcss.py', + 'doc/example/genxml.py', + 'doc/example/pytest/failure_demo.py', + 'doc/example/pytest/test_failures.py', + 'doc/example/pytest/test_setup_flow_example.py', 'doc/execnet.txt', 'doc/future.txt', 'doc/future/code_template.txt', From hpk at codespeak.net Mon Aug 18 18:30:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 18:30:37 +0200 (CEST) Subject: [py-svn] r57438 - py/release/0.9.x/py/misc/testing Message-ID: <20080818163037.13F61169F5E@codespeak.net> Author: hpk Date: Mon Aug 18 18:30:35 2008 New Revision: 57438 Modified: py/release/0.9.x/py/misc/testing/test_oskill.py Log: check for subprocess module availability Modified: py/release/0.9.x/py/misc/testing/test_oskill.py ============================================================================== --- py/release/0.9.x/py/misc/testing/test_oskill.py (original) +++ py/release/0.9.x/py/misc/testing/test_oskill.py Mon Aug 18 18:30:35 2008 @@ -8,6 +8,10 @@ py.test.skip("you\'re using an older version of windows, which " "doesn\'t support 'taskkill' - py.misc.killproc is not " "available") + try: + import subprocess + except ImportError: + py.test.skip("no subprocess module") tmp = py.test.ensuretemp("test_win_killsubprocess") t = tmp.join("t.py") t.write("import time ; time.sleep(100)") From hpk at codespeak.net Mon Aug 18 18:57:15 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 18:57:15 +0200 (CEST) Subject: [py-svn] r57439 - py/trunk/py/bin Message-ID: <20080818165715.240C1169F71@codespeak.net> Author: hpk Date: Mon Aug 18 18:57:13 2008 New Revision: 57439 Modified: py/trunk/py/bin/py.lookup Log: account for changed location Modified: py/trunk/py/bin/py.lookup ============================================================================== --- py/trunk/py/bin/py.lookup (original) +++ py/trunk/py/bin/py.lookup Mon Aug 18 18:57:13 2008 @@ -10,7 +10,7 @@ import sys, os sys.path.insert(0, os.path.dirname(__file__)) from _findpy import py -from py.__.misc.terminal_helper import ansi_print, terminal_width +from py.__.io.terminalwriter import ansi_print, terminal_width import re curdir = py.path.local() From hpk at codespeak.net Mon Aug 18 19:33:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 19:33:32 +0200 (CEST) Subject: [py-svn] r57440 - in py/trunk/py: code/testing execnet path test test/testing Message-ID: <20080818173332.98F73169EEF@codespeak.net> Author: hpk Date: Mon Aug 18 19:33:31 2008 New Revision: 57440 Modified: py/trunk/py/code/testing/test_safe_repr.py py/trunk/py/execnet/rsync.py py/trunk/py/path/common.py py/trunk/py/test/outcome.py py/trunk/py/test/testing/test_session.py Log: various fixes for python2.6 Modified: py/trunk/py/code/testing/test_safe_repr.py ============================================================================== --- py/trunk/py/code/testing/test_safe_repr.py (original) +++ py/trunk/py/code/testing/test_safe_repr.py Mon Aug 18 19:33:31 2008 @@ -24,7 +24,12 @@ assert 'Exception' in safe_repr._repr(BrokenRepr(BrokenReprException("really broken"))) def test_string_exception(): - assert 'unknown' in safe_repr._repr(BrokenRepr("string")) + if py.std.sys.version_info < (2,6): + assert 'unknown' in safe_repr._repr(BrokenRepr("string")) + else: + assert 'TypeError' in safe_repr._repr(BrokenRepr("string")) + + def test_big_repr(): assert len(safe_repr._repr(range(1000))) <= \ Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Mon Aug 18 19:33:31 2008 @@ -1,6 +1,9 @@ -import py, os, stat, md5 +import py, os, stat from Queue import Queue - +try: + from hashlib import md5 +except ImportError: + from md5 import md5 class RSync(object): """ This class allows to send a directory structure (recursively) Modified: py/trunk/py/path/common.py ============================================================================== --- py/trunk/py/path/common.py (original) +++ py/trunk/py/path/common.py Mon Aug 18 19:33:31 2008 @@ -431,7 +431,7 @@ old_import_hook = None -def custom_import_hook(name, glob=None, loc=None, fromlist=None): +def custom_import_hook(name, glob=None, loc=None, fromlist=None, extra=None, level=None): __tracebackhide__ = False __file__ = glob and glob.get('__file__') if isinstance(__file__, PathStr): @@ -457,5 +457,9 @@ return modules[0] # outermost package # fall-back __tracebackhide__ = True - return old_import_hook(name, glob, loc, fromlist) + try: + return old_import_hook(name, glob, loc, fromlist, level) + except TypeError: + return old_import_hook(name, glob, loc, fromlist) + Modified: py/trunk/py/test/outcome.py ============================================================================== --- py/trunk/py/test/outcome.py (original) +++ py/trunk/py/test/outcome.py Mon Aug 18 19:33:31 2008 @@ -99,18 +99,26 @@ """ assert that calling func(*args, **kwargs) triggers a DeprecationWarning. """ + warningmodule = py.std.warnings l = [] - oldwarn = py.std.warnings.warn_explicit + oldwarn_explicit = getattr(warningmodule, 'warn_explicit') def warn_explicit(*args, **kwargs): l.append(args) + oldwarn_explicit(*args, **kwargs) + oldwarn = getattr(warningmodule, 'warn') + def warn(*args, **kwargs): + l.append(args) oldwarn(*args, **kwargs) - py.magic.patch(py.std.warnings, 'warn_explicit', warn_explicit) + warningmodule.warn_explicit = warn_explicit + warningmodule.warn = warn try: ret = func(*args, **kwargs) finally: - py.magic.revert(py.std.warnings, 'warn_explicit') + warningmodule.warn_explicit = warn_explicit + warningmodule.warn = warn if not l: + print warningmodule raise AssertionError("%r did not produce DeprecationWarning" %(func,)) return ret Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Mon Aug 18 19:33:31 2008 @@ -187,7 +187,8 @@ out = failed[0].outcome.longrepr.reprcrash.message assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' out = failed[1].outcome.longrepr.reprcrash.message - assert out.find("[unknown exception raised in repr()]") != -1 + assert (out.find("[unknown exception raised in repr()]") != -1 or + out.find("TypeError") != -1) class TestNewSession(SessionTests): def test_pdb_run(self): From hpk at codespeak.net Mon Aug 18 19:48:17 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 19:48:17 +0200 (CEST) Subject: [py-svn] r57441 - in py/release/0.9.x: . py py/test/rsession/testing Message-ID: <20080818174817.4402A169EE5@codespeak.net> Author: hpk Date: Mon Aug 18 19:48:15 2008 New Revision: 57441 Modified: py/release/0.9.x/CHANGELOG py/release/0.9.x/py/__init__.py py/release/0.9.x/py/test/rsession/testing/test_reporter.py py/release/0.9.x/setup.py Log: fixes for python 2.3 Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Mon Aug 18 19:48:15 2008 @@ -13,6 +13,8 @@ * improved py.execnet bootstrapping on windows +* fix for py.path.svn* to work with svn 1.5 + * py.test's traceback is better parseable from editors (follows the filenames:LINENO: MSG convention) Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Mon Aug 18 19:48:15 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-7" +version = "0.9.2-alpha-8" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/release/0.9.x/py/test/rsession/testing/test_reporter.py (original) +++ py/release/0.9.x/py/test/rsession/testing/test_reporter.py Mon Aug 18 19:48:15 2008 @@ -120,6 +120,8 @@ return out def test_failed_to_load(self): + if py.std.sys.version_info < (2,4): + py.test.skip(">= python 2.4 required") tmpdir = py.test.ensuretemp("failedtoload") tmpdir.ensure("__init__.py") tmpdir.ensure("test_three.py").write(py.code.Source(""" @@ -211,6 +213,8 @@ assert val.find(expected) != -1 def test_full_module(self): + if py.std.sys.version_info < (2,4): + py.test.skip(">= python 2.4 required") val = self._test_full_module() assert val.find("FAILED TO LOAD MODULE: repmod/test_three.py\n"\ "\nSkipped ('reason') repmod/test_two.py") != -1 Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Mon Aug 18 19:48:15 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-7', + version='0.9.2-alpha-8', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], From hpk at codespeak.net Mon Aug 18 20:03:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 20:03:00 +0200 (CEST) Subject: [py-svn] r57442 - py/trunk/py/bin Message-ID: <20080818180300.1C9AF169F51@codespeak.net> Author: hpk Date: Mon Aug 18 20:02:56 2008 New Revision: 57442 Modified: py/trunk/py/bin/py.lookup Log: robustify. this can also happen to get exectued while being in another version of the py lib. Modified: py/trunk/py/bin/py.lookup ============================================================================== --- py/trunk/py/bin/py.lookup (original) +++ py/trunk/py/bin/py.lookup Mon Aug 18 20:02:56 2008 @@ -10,7 +10,10 @@ import sys, os sys.path.insert(0, os.path.dirname(__file__)) from _findpy import py -from py.__.io.terminalwriter import ansi_print, terminal_width +try: + from py.__.io.terminalwriter import ansi_print, terminal_width +except ImportError: + from py.__.misc.terminal_helper import ansi_print, terminal_width import re curdir = py.path.local() From hpk at codespeak.net Mon Aug 18 20:09:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 20:09:21 +0200 (CEST) Subject: [py-svn] r57443 - in py/release/0.9.x/py: code/testing execnet path test test/testing Message-ID: <20080818180921.268BA169F86@codespeak.net> Author: hpk Date: Mon Aug 18 20:09:19 2008 New Revision: 57443 Modified: py/release/0.9.x/py/code/testing/test_safe_repr.py py/release/0.9.x/py/execnet/rsync.py py/release/0.9.x/py/path/common.py py/release/0.9.x/py/test/deprecate.py py/release/0.9.x/py/test/testing/test_session.py Log: porting python2.6 fixes from trunk rev 57440 Modified: py/release/0.9.x/py/code/testing/test_safe_repr.py ============================================================================== --- py/release/0.9.x/py/code/testing/test_safe_repr.py (original) +++ py/release/0.9.x/py/code/testing/test_safe_repr.py Mon Aug 18 20:09:19 2008 @@ -24,7 +24,12 @@ assert 'Exception' in safe_repr._repr(BrokenRepr(BrokenReprException("really broken"))) def test_string_exception(): - assert 'unknown' in safe_repr._repr(BrokenRepr("string")) + if py.std.sys.version_info < (2,6): + assert 'unknown' in safe_repr._repr(BrokenRepr("string")) + else: + assert 'TypeError' in safe_repr._repr(BrokenRepr("string")) + + def test_big_repr(): assert len(safe_repr._repr(range(1000))) <= \ Modified: py/release/0.9.x/py/execnet/rsync.py ============================================================================== --- py/release/0.9.x/py/execnet/rsync.py (original) +++ py/release/0.9.x/py/execnet/rsync.py Mon Aug 18 20:09:19 2008 @@ -1,6 +1,9 @@ -import py, os, stat, md5 +import py, os, stat from Queue import Queue - +try: + from hashlib import md5 +except ImportError: + from md5 import md5 class RSync(object): """ This class allows to send a directory structure (recursively) Modified: py/release/0.9.x/py/path/common.py ============================================================================== --- py/release/0.9.x/py/path/common.py (original) +++ py/release/0.9.x/py/path/common.py Mon Aug 18 20:09:19 2008 @@ -431,7 +431,7 @@ old_import_hook = None -def custom_import_hook(name, glob=None, loc=None, fromlist=None): +def custom_import_hook(name, glob=None, loc=None, fromlist=None, extra=None, level=None): __tracebackhide__ = False __file__ = glob and glob.get('__file__') if isinstance(__file__, PathStr): @@ -457,5 +457,9 @@ return modules[0] # outermost package # fall-back __tracebackhide__ = True - return old_import_hook(name, glob, loc, fromlist) + try: + return old_import_hook(name, glob, loc, fromlist, level) + except TypeError: + return old_import_hook(name, glob, loc, fromlist) + Modified: py/release/0.9.x/py/test/deprecate.py ============================================================================== --- py/release/0.9.x/py/test/deprecate.py (original) +++ py/release/0.9.x/py/test/deprecate.py Mon Aug 18 20:09:19 2008 @@ -4,15 +4,26 @@ """ assert that calling func(*args, **kwargs) triggers a DeprecationWarning. """ + warningmodule = py.std.warnings l = [] - oldwarn = py.std.warnings.warn_explicit + oldwarn_explicit = getattr(warningmodule, 'warn_explicit') def warn_explicit(*args, **kwargs): l.append(args) + oldwarn_explicit(*args, **kwargs) + oldwarn = getattr(warningmodule, 'warn') + def warn(*args, **kwargs): + l.append(args) oldwarn(*args, **kwargs) - py.magic.patch(py.std.warnings, 'warn_explicit', warn_explicit) + warningmodule.warn_explicit = warn_explicit + warningmodule.warn = warn try: - _ = func(*args, **kwargs) + ret = func(*args, **kwargs) finally: - py.magic.revert(py.std.warnings, 'warn_explicit') - assert l + warningmodule.warn_explicit = warn_explicit + warningmodule.warn = warn + if not l: + print warningmodule + raise AssertionError("%r did not produce DeprecationWarning" %(func,)) + return ret + Modified: py/release/0.9.x/py/test/testing/test_session.py ============================================================================== --- py/release/0.9.x/py/test/testing/test_session.py (original) +++ py/release/0.9.x/py/test/testing/test_session.py Mon Aug 18 20:09:19 2008 @@ -289,7 +289,8 @@ l = session.getitemoutcomepairs(Failed) assert len(l) == 2 assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' - assert out.find("[unknown exception raised in repr()]") != -1 + assert (out.find("[unknown exception raised in repr()]") != -1 or + out.find("TypeError") != -1) def test_E_on_correct_line(self): o = tmpdir.ensure('E_on_correct_line', dir=1) From hpk at codespeak.net Mon Aug 18 21:03:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 18 Aug 2008 21:03:26 +0200 (CEST) Subject: [py-svn] r57445 - py/trunk/py/bin Message-ID: <20080818190326.D5BAA16A003@codespeak.net> Author: hpk Date: Mon Aug 18 21:03:25 2008 New Revision: 57445 Modified: py/trunk/py/bin/_findpy.py Log: retain the behaviour of printing if we insert to sys.path Modified: py/trunk/py/bin/_findpy.py ============================================================================== --- py/trunk/py/bin/_findpy.py (original) +++ py/trunk/py/bin/_findpy.py Mon Aug 18 21:03:25 2008 @@ -19,7 +19,7 @@ # if p == current: # return True if current != sys.path[0]: # if we are already first, then ok - #print >>sys.stderr, "inserting into sys.path:", current + print >>sys.stderr, "inserting into sys.path:", current sys.path.insert(0, current) return True current = opd(current) From guido at codespeak.net Mon Aug 18 21:09:02 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 18 Aug 2008 21:09:02 +0200 (CEST) Subject: [py-svn] r57446 - py/branch/guido-svnwc-xml-status/py/path/svn/testing Message-ID: <20080818190902.BE304169F2B@codespeak.net> Author: guido Date: Mon Aug 18 21:09:01 2008 New Revision: 57446 Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py Log: Made that a couple of slow tests get skipped - use --runslowtests to run them. Modified: py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py (original) +++ py/branch/guido-svnwc-xml-status/py/path/svn/testing/test_wccommand.py Mon Aug 18 21:09:01 2008 @@ -4,6 +4,7 @@ from py.__.path.svn.wccommand import InfoSvnWCCommand from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon +from py.__.conftest import option if py.path.local.sysfind('svn') is None: py.test.skip("cannot test py.path.svn, 'svn' binary not found") @@ -157,6 +158,9 @@ self.root.revert(rec=1) def test_status_conflict(self): + if not option.runslowtests: + py.test.skip('skipping slow unit tests - use --runslowtests ' + 'to override') wc = self.root wccopy = py.path.svnwc( py.test.ensuretemp('test_status_conflict_wccopy')) @@ -174,6 +178,9 @@ assert [x.basename for x in s.conflict] == ['conflictsamplefile'] def test_status_external(self): + if not option.runslowtests: + py.test.skip('skipping slow unit tests - use --runslowtests ' + 'to override') otherrepo, otherwc = getrepowc('externalrepo', 'externalwc') d = self.root.ensure('sampledir', dir=1) try: From guido at codespeak.net Mon Aug 18 22:37:41 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 18 Aug 2008 22:37:41 +0200 (CEST) Subject: [py-svn] r57449 - in py/trunk/py/path/svn: . testing Message-ID: <20080818203741.E5BCD16A009@codespeak.net> Author: guido Date: Mon Aug 18 22:37:39 2008 New Revision: 57449 Modified: py/trunk/py/path/svn/testing/svntestbase.py py/trunk/py/path/svn/testing/test_wccommand.py py/trunk/py/path/svn/wccommand.py Log: Merging https://codespeak.net/svn/py/branch/guido-svnwc-xml-status with trunk (revisions 56843:57448). Modified: py/trunk/py/path/svn/testing/svntestbase.py ============================================================================== --- py/trunk/py/path/svn/testing/svntestbase.py (original) +++ py/trunk/py/path/svn/testing/svntestbase.py Mon Aug 18 22:37:39 2008 @@ -73,7 +73,7 @@ def setup_method(self, meth): bn = meth.func_name - for x in 'test_remove', 'test_move': + for x in 'test_remove', 'test_move', 'test_status_deleted': if bn.startswith(x): self._savedrepowc = save_repowc() Modified: py/trunk/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/trunk/py/path/svn/testing/test_wccommand.py (original) +++ py/trunk/py/path/svn/testing/test_wccommand.py Mon Aug 18 22:37:39 2008 @@ -4,6 +4,7 @@ from py.__.path.svn.wccommand import InfoSvnWCCommand from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon +from py.__.conftest import option if py.path.local.sysfind('svn') is None: py.test.skip("cannot test py.path.svn, 'svn' binary not found") @@ -141,6 +142,78 @@ finally: self.root.revert(rec=1) + def test_status_ignored(self): + try: + d = self.root.join('sampledir') + p = py.path.local(d).join('ignoredfile') + p.ensure(file=True) + s = d.status() + assert [x.basename for x in s.unknown] == ['ignoredfile'] + assert [x.basename for x in s.ignored] == [] + d.propset('svn:ignore', 'ignoredfile') + s = d.status() + assert [x.basename for x in s.unknown] == [] + assert [x.basename for x in s.ignored] == ['ignoredfile'] + finally: + self.root.revert(rec=1) + + def test_status_conflict(self): + if not option.runslowtests: + py.test.skip('skipping slow unit tests - use --runslowtests ' + 'to override') + wc = self.root + wccopy = py.path.svnwc( + py.test.ensuretemp('test_status_conflict_wccopy')) + wccopy.checkout(wc.url) + p = wc.ensure('conflictsamplefile', file=1) + p.write('foo') + wc.commit('added conflictsamplefile') + wccopy.update() + assert wccopy.join('conflictsamplefile').check() + p.write('bar') + wc.commit('wrote some data') + wccopy.join('conflictsamplefile').write('baz') + wccopy.update() + s = wccopy.status() + assert [x.basename for x in s.conflict] == ['conflictsamplefile'] + + def test_status_external(self): + if not option.runslowtests: + py.test.skip('skipping slow unit tests - use --runslowtests ' + 'to override') + otherrepo, otherwc = getrepowc('externalrepo', 'externalwc') + d = self.root.ensure('sampledir', dir=1) + try: + d.remove() + d.add() + d.update() + d.propset('svn:externals', 'otherwc %s' % (otherwc.url,)) + d.update() + s = d.status() + assert [x.basename for x in s.external] == ['otherwc'] + assert 'otherwc' not in [x.basename for x in s.unchanged] + s = d.status(rec=1) + assert [x.basename for x in s.external] == ['otherwc'] + assert 'otherwc' in [x.basename for x in s.unchanged] + finally: + self.root.revert(rec=1) + + def test_status_deleted(self): + d = self.root.ensure('sampledir', dir=1) + d.remove() + d.add() + self.root.commit() + d.ensure('deletefile', dir=0) + d.commit() + s = d.status() + assert 'deletefile' in [x.basename for x in s.unchanged] + assert not s.deleted + p = d.join('deletefile') + p.remove() + s = d.status() + assert 'deletefile' not in s.unchanged + assert [x.basename for x in s.deleted] == ['deletefile'] + def test_diff(self): p = self.root / 'anotherfile' out = p.diff(rev=2) Modified: py/trunk/py/path/svn/wccommand.py ============================================================================== --- py/trunk/py/path/svn/wccommand.py (original) +++ py/trunk/py/path/svn/wccommand.py Mon Aug 18 22:37:39 2008 @@ -9,6 +9,8 @@ """ import os, sys, time, re, calendar +from xml.dom import minidom +from xml.parsers.expat import ExpatError import py from py.__.path import common from py.__.path.svn import cache @@ -255,9 +257,17 @@ else: updates = '' - cmd = 'status -v %s %s %s' % (updates, rec, externals) - out = self._authsvn(cmd) - rootstatus = WCStatus(self).fromstring(out, self) + try: + cmd = 'status -v --xml --no-ignore %s %s %s' % ( + updates, rec, externals) + out = self._authsvn(cmd) + except py.process.cmdexec.Error: + cmd = 'status -v --no-ignore %s %s %s' % ( + updates, rec, externals) + out = self._authsvn(cmd) + rootstatus = WCStatus(self).fromstring(out, self) + else: + rootstatus = XMLWCStatus(self).fromstring(out, self) return rootstatus def diff(self, rev=None): @@ -528,7 +538,6 @@ # seem to be a more solid approach :( _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(.+?)\s{2,}(.*)') - @staticmethod def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): """ return a new WCStatus object from data 's' """ @@ -573,6 +582,13 @@ if line.lower().find('against revision:')!=-1: update_rev = int(rest.split(':')[1].strip()) continue + if line.lower().find('status on external') > -1: + # XXX not sure what to do here... perhaps we want to + # store some state instead of just continuing, as right + # now it makes the top-level external get added twice + # (once as external, once as 'normal' unchanged item) + # because of the way SVN presents external items + continue # keep trying raise ValueError, "could not parse line %r" % line else: @@ -615,6 +631,106 @@ rootstatus.update_rev = update_rev continue return rootstatus + fromstring = staticmethod(fromstring) + +class XMLWCStatus(WCStatus): + def fromstring(data, rootwcpath, rev=None, modrev=None, author=None): + """ parse 'data' (XML string as outputted by svn st) into a status obj + """ + # XXX for externals, the path is shown twice: once + # with external information, and once with full info as if + # the item was a normal non-external... the current way of + # dealing with this issue is by ignoring it - this does make + # externals appear as external items as well as 'normal', + # unchanged ones in the status object so this is far from ideal + rootstatus = WCStatus(rootwcpath, rev, modrev, author) + update_rev = None + try: + doc = minidom.parseString(data) + except ExpatError, e: + raise ValueError(str(e)) + urevels = doc.getElementsByTagName('against') + if urevels: + rootstatus.update_rev = urevels[-1].getAttribute('revision') + for entryel in doc.getElementsByTagName('entry'): + path = entryel.getAttribute('path') + statusel = entryel.getElementsByTagName('wc-status')[0] + itemstatus = statusel.getAttribute('item') + + if itemstatus == 'unversioned': + wcpath = rootwcpath.join(path, abs=1) + rootstatus.unknown.append(wcpath) + continue + elif itemstatus == 'external': + wcpath = rootwcpath.__class__( + rootwcpath.localpath.join(path, abs=1), + auth=rootwcpath.auth) + rootstatus.external.append(wcpath) + continue + elif itemstatus == 'ignored': + wcpath = rootwcpath.join(path, abs=1) + rootstatus.ignored.append(wcpath) + continue + + rev = statusel.getAttribute('revision') + if itemstatus == 'added' or itemstatus == 'none': + rev = '0' + modrev = '?' + author = '?' + date = '' + else: + print entryel.toxml() + commitel = entryel.getElementsByTagName('commit')[0] + if commitel: + modrev = commitel.getAttribute('revision') + author = '' + for c in commitel.getElementsByTagName('author')[0]\ + .childNodes: + author += c.nodeValue + date = '' + for c in commitel.getElementsByTagName('date')[0]\ + .childNodes: + date += c.nodeValue + + wcpath = rootwcpath.join(path, abs=1) + + assert itemstatus != 'modified' or wcpath.check(file=1), ( + 'did\'t expect a directory with changed content here') + + itemattrname = { + 'normal': 'unchanged', + 'unversioned': 'unknown', + 'conflicted': 'conflict', + 'none': 'added', + }.get(itemstatus, itemstatus) + + attr = getattr(rootstatus, itemattrname) + attr.append(wcpath) + + propsstatus = statusel.getAttribute('props') + if propsstatus not in ('none', 'normal'): + rootstatus.prop_modified.append(wcpath) + + if wcpath == rootwcpath: + rootstatus.rev = rev + rootstatus.modrev = modrev + rootstatus.author = author + rootstatus.date = date + + # handle repos-status element (remote info) + rstatusels = entryel.getElementsByTagName('repos-status') + if rstatusels: + rstatusel = rstatusels[0] + ritemstatus = rstatusel.getAttribute('item') + if ritemstatus in ('added', 'modified'): + rootstatus.update_available.append(wcpath) + + lockels = entryel.getElementsByTagName('lock') + if len(lockels): + rootstatus.locked.append(wcpath) + + return rootstatus + fromstring = staticmethod(fromstring) class InfoSvnWCCommand: def __init__(self, output): From hpk at codespeak.net Tue Aug 19 07:58:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 07:58:46 +0200 (CEST) Subject: [py-svn] r57455 - py/trunk/py/test/testing Message-ID: <20080819055846.9F62B169F51@codespeak.net> Author: hpk Date: Tue Aug 19 07:58:44 2008 New Revision: 57455 Modified: py/trunk/py/test/testing/acceptance_test.py Log: fixing for changed output Modified: py/trunk/py/test/testing/acceptance_test.py ============================================================================== --- py/trunk/py/test/testing/acceptance_test.py (original) +++ py/trunk/py/test/testing/acceptance_test.py Tue Aug 19 07:58:44 2008 @@ -66,7 +66,8 @@ pass """) result = self.runpytest("--collectonly", p) - assert not "".join(result.errlines) + err = "".join(result.errlines) + assert err.strip().startswith("inserting into sys.path") assert result.ret == 0 extra = assert_lines_contain_lines(result.outlines, py.code.Source(""" From hpk at codespeak.net Tue Aug 19 13:44:42 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 13:44:42 +0200 (CEST) Subject: [py-svn] r57461 - py/build Message-ID: <20080819114442.91F71169F50@codespeak.net> Author: hpk Date: Tue Aug 19 13:44:41 2008 New Revision: 57461 Added: py/build/winpath.py Log: adding a new winpath helper to prune PATH on windows Added: py/build/winpath.py ============================================================================== --- (empty file) +++ py/build/winpath.py Tue Aug 19 13:44:41 2008 @@ -0,0 +1,68 @@ + +# +# some helpers to add the py lib scripts to the +# WIN32 cmdline environment +# +import os, sys + +class Win32PathHandling: + _winreg = None + def __init__(self): + if sys.platform == 'win32': + try: + import _winreg + except ImportError: + print sys.stderr, "huh could not import _winreg on windows, ignoring" + else: + self._winreg = _winreg + + def remove_pylib_path(self): + reg = self._winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" + path = self.get_registry_value(reg, key, "Path") + newpath = self.prunepath(path) + if newpath != path: + print "Found old PATH:", path + print "Setting pruned PATH:", newpath + set_registry_value(reg, key, "Path", newpath) + # Propagate changes to current command prompt + os.system("set PATH=%s" % path) + self.try_propagate_system() + + def prunepath(self, path): + basename = os.path.basename + dirname = os.path.dirname + l = [] + for p in path.split(';'): + if basename(p) == "bin" and dirname(p) == "py": + if os.path.exists(os.path.join(p, 'py.test')): + continue # prune this path + l.append(p) + return ";".join(l) + + def try_propagate_system(self): + try: + import win32gui, win32con + except ImportError: + return + # Propagate changes throughout the system + win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, + win32con.WM_SETTINGCHANGE, 0, "Environment", + win32con.SMTO_ABORTIFHUNG, 5000) + + def get_registry_value(self, reg, key, value_name): + k = self._winreg.OpenKey(reg, key) + value = self._winreg.QueryValueEx(k, value_name)[0] + self._winreg.CloseKey(k) + return value + + def set_registry_value(self, reg, key, value_name, value): + k = self._winreg.OpenKey(reg, key, 0, self._winreg.KEY_WRITE) + value_type = self._winreg.REG_SZ + # if we handle the Path value, then set its type to REG_EXPAND_SZ + # so that things like %SystemRoot% get automatically expanded by the + # command prompt + if value_name == "Path": + value_type = self._winreg.REG_EXPAND_SZ + self._winreg.SetValueEx(k, value_name, 0, value_type, value) + self._winreg.CloseKey(k) From hpk at codespeak.net Tue Aug 19 13:54:38 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 13:54:38 +0200 (CEST) Subject: [py-svn] r57462 - py/build Message-ID: <20080819115438.E0A3D169FFA@codespeak.net> Author: hpk Date: Tue Aug 19 13:54:36 2008 New Revision: 57462 Modified: py/build/winpath.py Log: a few fixes, seems to work on win Modified: py/build/winpath.py ============================================================================== --- py/build/winpath.py (original) +++ py/build/winpath.py Tue Aug 19 13:54:36 2008 @@ -17,14 +17,15 @@ self._winreg = _winreg def remove_pylib_path(self): - reg = self._winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) + reg = self._winreg.ConnectRegistry( + None, self._winreg.HKEY_LOCAL_MACHINE) key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" path = self.get_registry_value(reg, key, "Path") newpath = self.prunepath(path) if newpath != path: - print "Found old PATH:", path - print "Setting pruned PATH:", newpath - set_registry_value(reg, key, "Path", newpath) + print "PATH contains old py/bin/win32 scripts:", path + print "pruning and setting a new PATH:", newpath + self.set_registry_value(reg, key, "Path", newpath) # Propagate changes to current command prompt os.system("set PATH=%s" % path) self.try_propagate_system() @@ -34,9 +35,9 @@ dirname = os.path.dirname l = [] for p in path.split(';'): - if basename(p) == "bin" and dirname(p) == "py": - if os.path.exists(os.path.join(p, 'py.test')): - continue # prune this path + if basename(p) == "win32" and basename(dirname(p)) == "bin" \ + and basename(dirname(dirname(p))) == "py": + continue # prune this path l.append(p) return ";".join(l) @@ -66,3 +67,7 @@ value_type = self._winreg.REG_EXPAND_SZ self._winreg.SetValueEx(k, value_name, 0, value_type, value) self._winreg.CloseKey(k) + +if __name__ == "__main__": + w32path = Win32PathHandling() + w32path.remove_pylib_path() From hpk at codespeak.net Tue Aug 19 14:10:38 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 14:10:38 +0200 (CEST) Subject: [py-svn] r57463 - py/release/0.9.x/py/path Message-ID: <20080819121038.5D1EC16A01B@codespeak.net> Author: hpk Date: Tue Aug 19 14:10:36 2008 New Revision: 57463 Added: py/release/0.9.x/py/path/ - copied from r57462, py/trunk/py/path/ Log: let's just use trunk's py.path copy which includes Guido's changes of using status --xml. From hpk at codespeak.net Tue Aug 19 14:23:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 14:23:31 +0200 (CEST) Subject: [py-svn] r57464 - py/trunk/py/path/svn Message-ID: <20080819122331.3C5F616A028@codespeak.net> Author: hpk Date: Tue Aug 19 14:23:30 2008 New Revision: 57464 Modified: py/trunk/py/path/svn/wccommand.py Log: removing print statement (there is something to be said about doctests :) Modified: py/trunk/py/path/svn/wccommand.py ============================================================================== --- py/trunk/py/path/svn/wccommand.py (original) +++ py/trunk/py/path/svn/wccommand.py Tue Aug 19 14:23:30 2008 @@ -679,7 +679,7 @@ author = '?' date = '' else: - print entryel.toxml() + #print entryel.toxml() commitel = entryel.getElementsByTagName('commit')[0] if commitel: modrev = commitel.getAttribute('revision') From hpk at codespeak.net Tue Aug 19 14:24:14 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 14:24:14 +0200 (CEST) Subject: [py-svn] r57465 - py/release/0.9.x/py/path/svn Message-ID: <20080819122414.8399D16A026@codespeak.net> Author: hpk Date: Tue Aug 19 14:24:12 2008 New Revision: 57465 Modified: py/release/0.9.x/py/path/svn/wccommand.py Log: commenting print here as well. Modified: py/release/0.9.x/py/path/svn/wccommand.py ============================================================================== --- py/release/0.9.x/py/path/svn/wccommand.py (original) +++ py/release/0.9.x/py/path/svn/wccommand.py Tue Aug 19 14:24:12 2008 @@ -679,7 +679,7 @@ author = '?' date = '' else: - print entryel.toxml() + #print entryel.toxml() commitel = entryel.getElementsByTagName('commit')[0] if commitel: modrev = commitel.getAttribute('revision') From hpk at codespeak.net Tue Aug 19 14:28:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 14:28:32 +0200 (CEST) Subject: [py-svn] r57466 - py/release/0.9.x Message-ID: <20080819122832.C8BA516A03B@codespeak.net> Author: hpk Date: Tue Aug 19 14:28:31 2008 New Revision: 57466 Modified: py/release/0.9.x/CHANGELOG py/release/0.9.x/setup.py Log: new setup, new path, new version Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Tue Aug 19 14:28:31 2008 @@ -5,18 +5,25 @@ * refined installation and metadata, created new setup.py, now based on setuptools/ez_setup (by flipping a bit you - can use distutils). + can use distutils). (thanks to Ralf Schmitt for his support) * improved the way of making py.* scripts available in windows environments, they are now added to the Scripts directory as ".cmd" files. * improved py.execnet bootstrapping on windows + (thanks Matthew Edwards, Baptiste Lepille) + +* py.path.svnwc.status() now is more complete and + uses xml output from the 'svn' command if available + (Guido Wesdorp) * fix for py.path.svn* to work with svn 1.5 + (Chris Lamb) * py.test's traceback is better parseable from editors (follows the filenames:LINENO: MSG convention) + (thanks to Osmo Salomaa) * fix to javascript-generation, "py.test --runbrowser" should work more reliably now Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Tue Aug 19 14:28:31 2008 @@ -190,6 +190,70 @@ zip_safe=False, ) +class Win32PathHandling: + """ a helper to remove things added to system PATHs in previous installations. """ + _winreg = None + def __init__(self): + if sys.platform == 'win32': + try: + import _winreg + except ImportError: + print sys.stderr, "huh could not import _winreg on windows, ignoring" + else: + self._winreg = _winreg + + def remove_pylib_path(self): + reg = self._winreg.ConnectRegistry( + None, self._winreg.HKEY_LOCAL_MACHINE) + key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" + path = self.get_registry_value(reg, key, "Path") + newpath = self.prunepath(path) + if newpath != path: + print "PATH contains old py/bin/win32 scripts:", path + print "pruning and setting a new PATH:", newpath + self.set_registry_value(reg, key, "Path", newpath) + # Propagate changes to current command prompt + os.system("set PATH=%s" % path) + self.try_propagate_system() + + def prunepath(self, path): + basename = os.path.basename + dirname = os.path.dirname + l = [] + for p in path.split(';'): + if basename(p) == "win32" and basename(dirname(p)) == "bin" \ + and basename(dirname(dirname(p))) == "py": + continue # prune this path + l.append(p) + return ";".join(l) + + def try_propagate_system(self): + try: + import win32gui, win32con + except ImportError: + return + # Propagate changes throughout the system + win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, + win32con.WM_SETTINGCHANGE, 0, "Environment", + win32con.SMTO_ABORTIFHUNG, 5000) + + def get_registry_value(self, reg, key, value_name): + k = self._winreg.OpenKey(reg, key) + value = self._winreg.QueryValueEx(k, value_name)[0] + self._winreg.CloseKey(k) + return value + + def set_registry_value(self, reg, key, value_name, value): + k = self._winreg.OpenKey(reg, key, 0, self._winreg.KEY_WRITE) + value_type = self._winreg.REG_SZ + # if we handle the Path value, then set its type to REG_EXPAND_SZ + # so that things like %SystemRoot% get automatically expanded by the + # command prompt + if value_name == "Path": + value_type = self._winreg.REG_EXPAND_SZ + self._winreg.SetValueEx(k, value_name, 0, value_type, value) + self._winreg.CloseKey(k) + # on windows we need to hack up the to-be-installed scripts from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): @@ -212,6 +276,9 @@ f.write("@echo off\n") f.write('python "%%~dp0\%s" %%*\n' % newbasename) f.close() + w32path = Win32PathHandling() + w32path.remove_pylib_path() + if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} else: From hpk at codespeak.net Tue Aug 19 19:34:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 19:34:21 +0200 (CEST) Subject: [py-svn] r57468 - py/trunk Message-ID: <20080819173421.63BDA16854E@codespeak.net> Author: hpk Date: Tue Aug 19 19:34:19 2008 New Revision: 57468 Modified: py/trunk/MANIFEST py/trunk/setup.py Log: regen setup for trunk Modified: py/trunk/MANIFEST ============================================================================== --- py/trunk/MANIFEST (original) +++ py/trunk/MANIFEST Tue Aug 19 19:34:19 2008 @@ -67,6 +67,7 @@ py/bin/py.lookup py/bin/py.rest py/bin/py.test +py/bin/py.which py/builtin/__init__.py py/builtin/enumerate.py py/builtin/exception.py @@ -79,6 +80,7 @@ py/builtin/testing/test_reversed.py py/builtin/testing/test_set.py py/builtin/testing/test_sorted.py +py/c-extension/__init__.py py/c-extension/greenlet/README.txt py/c-extension/greenlet/dummy_greenlet.py py/c-extension/greenlet/greenlet.c @@ -86,6 +88,7 @@ py/c-extension/greenlet/setup.py py/c-extension/greenlet/slp_platformselect.h py/c-extension/greenlet/switch_amd64_unix.h +py/c-extension/greenlet/switch_mips_unix.h py/c-extension/greenlet/switch_ppc_macosx.h py/c-extension/greenlet/switch_ppc_unix.h py/c-extension/greenlet/switch_s390_unix.h @@ -105,7 +108,6 @@ py/code/source.py py/code/testing/__init__.py py/code/testing/test_code.py -py/code/testing/test_cpython_features.py py/code/testing/test_excinfo.py py/code/testing/test_frame.py py/code/testing/test_safe_repr.py @@ -130,6 +132,7 @@ py/doc/TODO.txt py/doc/__init__.py py/doc/apigen.txt +py/doc/apigen_refactorings.txt py/doc/bin.txt py/doc/code.txt py/doc/coding-style.txt @@ -147,6 +150,7 @@ py/doc/future.txt py/doc/future/code_template.txt py/doc/future/planning.txt +py/doc/future/planning2.txt py/doc/future/pylib_pypy.txt py/doc/future/rsession_todo.txt py/doc/greenlet.txt @@ -158,7 +162,6 @@ py/doc/misc.txt py/doc/path.txt py/doc/release-0.9.0.txt -py/doc/release-0.9.1.txt py/doc/release-0.9.2.txt py/doc/style.css py/doc/test.txt @@ -187,15 +190,36 @@ py/execnet/testing/test_gateway.py py/execnet/testing/test_pickle.py py/execnet/testing/test_rsync.py +py/green/__init__.py +py/green/conftest.py +py/green/greenexecnet.py +py/green/greensock2.py +py/green/msgstruct.py +py/green/pipe/__init__.py +py/green/pipe/common.py +py/green/pipe/fd.py +py/green/pipe/gsocket.py +py/green/pipe/mp.py +py/green/pipelayer.py +py/green/server/__init__.py +py/green/server/httpserver.py +py/green/test/__init__.py +py/green/test/test_greenexecnet.py +py/green/test/test_greensock2.py +py/green/test/test_pipelayer.py py/initpkg.py py/io/__init__.py py/io/dupfile.py py/io/fdcapture.py +py/io/forkedfunc.py py/io/stdcapture.py -py/io/test/__init__.py -py/io/test/test_dupfile.py -py/io/test/test_fdcapture.py -py/io/test/test_stdcapture.py +py/io/terminalwriter.py +py/io/testing/__init__.py +py/io/testing/test_dupfile.py +py/io/testing/test_fdcapture.py +py/io/testing/test_forkedfunc.py +py/io/testing/test_stdcapture.py +py/io/testing/test_terminalwriter.py py/log/__init__.py py/log/consumer.py py/log/logger.py @@ -307,70 +331,70 @@ py/test/__init__.py py/test/cmdline.py py/test/collect.py +py/test/compat.py py/test/config.py py/test/conftesthandle.py +py/test/custompdb.py py/test/defaultconftest.py -py/test/deprecate.py -py/test/doctest.py -py/test/item.py +py/test/dsession/__init__.py +py/test/dsession/dsession.py +py/test/dsession/hostmanage.py +py/test/dsession/masterslave.py +py/test/dsession/mypickle.py +py/test/dsession/testing/__init__.py +py/test/dsession/testing/basetest.py +py/test/dsession/testing/test_dsession.py +py/test/dsession/testing/test_functional_dsession.py +py/test/dsession/testing/test_hostmanage.py +py/test/dsession/testing/test_masterslave.py +py/test/dsession/testing/test_mypickle.py +py/test/event.py +py/test/looponfail/__init__.py +py/test/looponfail/remote.py +py/test/looponfail/testing/__init__.py +py/test/looponfail/testing/test_remote.py +py/test/looponfail/testing/test_util.py +py/test/looponfail/util.py py/test/outcome.py -py/test/raises.py -py/test/representation.py -py/test/rsession/__init__.py -py/test/rsession/box.py -py/test/rsession/executor.py -py/test/rsession/hostmanage.py -py/test/rsession/local.py -py/test/rsession/master.py -py/test/rsession/outcome.py -py/test/rsession/repevent.py -py/test/rsession/reporter.py -py/test/rsession/rest.py -py/test/rsession/rsession.py -py/test/rsession/slave.py -py/test/rsession/testing/__init__.py -py/test/rsession/testing/basetest.py -py/test/rsession/testing/example1.py -py/test/rsession/testing/example2.py -py/test/rsession/testing/test_boxing.py -py/test/rsession/testing/test_executor.py -py/test/rsession/testing/test_hostmanage.py -py/test/rsession/testing/test_lsession.py -py/test/rsession/testing/test_master.py -py/test/rsession/testing/test_outcome.py -py/test/rsession/testing/test_repevent.py -py/test/rsession/testing/test_reporter.py -py/test/rsession/testing/test_rest.py -py/test/rsession/testing/test_rsession.py -py/test/rsession/testing/test_slave.py -py/test/rsession/testing/test_web.py -py/test/rsession/testing/test_webjs.py -py/test/rsession/web.py -py/test/rsession/webdata/__init__.py -py/test/rsession/webdata/index.html -py/test/rsession/webdata/json.py -py/test/rsession/webdata/source.js -py/test/rsession/webjs.py +py/test/pycollect.py +py/test/report/__init__.py +py/test/report/base.py +py/test/report/collectonly.py +py/test/report/rest.py +py/test/report/terminal.py +py/test/report/testing/__init__.py +py/test/report/testing/test_basereporter.py +py/test/report/testing/test_collectonly.py +py/test/report/testing/test_rest.py +py/test/report/testing/test_terminal.py +py/test/report/testing/test_web.py +py/test/report/testing/test_webjs.py +py/test/report/web.py +py/test/report/webdata/__init__.py +py/test/report/webdata/index.html +py/test/report/webdata/json.py +py/test/report/webdata/source.js +py/test/report/webjs.py +py/test/runner.py py/test/session.py -py/test/terminal/__init__.py -py/test/terminal/out.py -py/test/terminal/remote.py -py/test/terminal/terminal.py py/test/testing/__init__.py +py/test/testing/acceptance_test.py py/test/testing/import_test/package/__init__.py py/test/testing/import_test/package/absolute_import_shared_lib.py py/test/testing/import_test/package/module_that_imports_shared_lib.py py/test/testing/import_test/package/shared_lib.py py/test/testing/import_test/package/test_import.py py/test/testing/setupdata.py +py/test/testing/suptest.py py/test/testing/test_collect.py +py/test/testing/test_compat.py py/test/testing/test_config.py py/test/testing/test_conftesthandle.py -py/test/testing/test_deprecated.py py/test/testing/test_doctest.py -py/test/testing/test_raises.py -py/test/testing/test_remote.py -py/test/testing/test_repr.py +py/test/testing/test_event.py +py/test/testing/test_outcome.py +py/test/testing/test_repevent.py +py/test/testing/test_runner_functional.py py/test/testing/test_session.py py/test/testing/test_setup_nested.py py/test/web/__init__.py Modified: py/trunk/setup.py ============================================================================== --- py/trunk/setup.py (original) +++ py/trunk/setup.py Tue Aug 19 19:34:19 2008 @@ -1,7 +1,7 @@ """ setup file for 'py' package based on: - https://codespeak.net/svn/py/release/0.9.x, revision=57387 + https://codespeak.net/svn/py/trunk, revision=57462 autogenerated by gensetup.py """ @@ -17,8 +17,8 @@ def main(): setup(cmdclass=cmdclass, name='py', - description='py lib: agile development and test support library', - version='0.9.2-alpha-6', + description='pylib and py.test: agile development and test support library', + version='1.0.0a1', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -44,7 +44,8 @@ 'py/bin/py.countloc', 'py/bin/py.lookup', 'py/bin/py.rest', - 'py/bin/py.test'], + 'py/bin/py.test', + 'py/bin/py.which'], packages=['py', 'py.apigen', 'py.apigen.rest', @@ -59,6 +60,7 @@ 'py.apigen.tracer.testing.package.submodule.pak', 'py.builtin', 'py.builtin.testing', + 'py.c-extension', 'py.code', 'py.code.testing', 'py.compat', @@ -67,8 +69,12 @@ 'py.execnet', 'py.execnet.script', 'py.execnet.testing', + 'py.green', + 'py.green.pipe', + 'py.green.server', + 'py.green.test', 'py.io', - 'py.io.test', + 'py.io.testing', 'py.log', 'py.log.testing', 'py.magic', @@ -88,10 +94,13 @@ 'py.rest', 'py.rest.testing', 'py.test', - 'py.test.rsession', - 'py.test.rsession.testing', - 'py.test.rsession.webdata', - 'py.test.terminal', + 'py.test.dsession', + 'py.test.dsession.testing', + 'py.test.looponfail', + 'py.test.looponfail.testing', + 'py.test.report', + 'py.test.report.testing', + 'py.test.report.webdata', 'py.test.testing', 'py.test.testing.import_test.package', 'py.test.web', @@ -108,36 +117,54 @@ 'apigen/style.css', 'apigen/todo-apigen.txt', 'apigen/todo.txt', + 'bin/_findpy.py', 'bin/py.cleanup', 'bin/py.countloc', 'bin/py.lookup', 'bin/py.rest', 'bin/py.test', + 'bin/py.which', 'c-extension/greenlet/README.txt', + 'c-extension/greenlet/dummy_greenlet.py', 'c-extension/greenlet/greenlet.c', 'c-extension/greenlet/greenlet.h', + 'c-extension/greenlet/setup.py', 'c-extension/greenlet/slp_platformselect.h', 'c-extension/greenlet/switch_amd64_unix.h', + 'c-extension/greenlet/switch_mips_unix.h', 'c-extension/greenlet/switch_ppc_macosx.h', 'c-extension/greenlet/switch_ppc_unix.h', 'c-extension/greenlet/switch_s390_unix.h', 'c-extension/greenlet/switch_sparc_sun_gcc.h', 'c-extension/greenlet/switch_x86_msvc.h', 'c-extension/greenlet/switch_x86_unix.h', + 'c-extension/greenlet/test_generator.py', + 'c-extension/greenlet/test_generator_nested.py', + 'c-extension/greenlet/test_greenlet.py', + 'c-extension/greenlet/test_remote.py', + 'c-extension/greenlet/test_throw.py', 'compat/LICENSE', 'compat/testing/test_doctest.txt', 'compat/testing/test_doctest2.txt', 'doc/TODO.txt', 'doc/apigen.txt', + 'doc/apigen_refactorings.txt', 'doc/bin.txt', 'doc/code.txt', 'doc/coding-style.txt', 'doc/contact.txt', 'doc/download.txt', + 'doc/example/genhtml.py', + 'doc/example/genhtmlcss.py', + 'doc/example/genxml.py', + 'doc/example/pytest/failure_demo.py', + 'doc/example/pytest/test_failures.py', + 'doc/example/pytest/test_setup_flow_example.py', 'doc/execnet.txt', 'doc/future.txt', 'doc/future/code_template.txt', 'doc/future/planning.txt', + 'doc/future/planning2.txt', 'doc/future/pylib_pypy.txt', 'doc/future/rsession_todo.txt', 'doc/greenlet.txt', @@ -149,7 +176,6 @@ 'doc/misc.txt', 'doc/path.txt', 'doc/release-0.9.0.txt', - 'doc/release-0.9.1.txt', 'doc/release-0.9.2.txt', 'doc/style.css', 'doc/test.txt', @@ -170,11 +196,75 @@ 'rest/testing/data/part1.txt', 'rest/testing/data/part2.txt', 'rest/testing/data/tocdepth.rst2pdfconfig', - 'test/rsession/webdata/index.html', - 'test/rsession/webdata/source.js']}, + 'test/report/webdata/index.html', + 'test/report/webdata/source.js']}, zip_safe=False, ) +class Win32PathHandling: + """ a helper to remove things added to system PATHs in previous installations. """ + _winreg = None + def __init__(self): + if sys.platform == 'win32': + try: + import _winreg + except ImportError: + print sys.stderr, "huh could not import _winreg on windows, ignoring" + else: + self._winreg = _winreg + + def remove_pylib_path(self): + reg = self._winreg.ConnectRegistry( + None, self._winreg.HKEY_LOCAL_MACHINE) + key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" + path = self.get_registry_value(reg, key, "Path") + newpath = self.prunepath(path) + if newpath != path: + print "PATH contains old py/bin/win32 scripts:", path + print "pruning and setting a new PATH:", newpath + self.set_registry_value(reg, key, "Path", newpath) + # Propagate changes to current command prompt + os.system("set PATH=%s" % path) + self.try_propagate_system() + + def prunepath(self, path): + basename = os.path.basename + dirname = os.path.dirname + l = [] + for p in path.split(';'): + if basename(p) == "win32" and basename(dirname(p)) == "bin" \ + and basename(dirname(dirname(p))) == "py": + continue # prune this path + l.append(p) + return ";".join(l) + + def try_propagate_system(self): + try: + import win32gui, win32con + except ImportError: + return + # Propagate changes throughout the system + win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, + win32con.WM_SETTINGCHANGE, 0, "Environment", + win32con.SMTO_ABORTIFHUNG, 5000) + + def get_registry_value(self, reg, key, value_name): + k = self._winreg.OpenKey(reg, key) + value = self._winreg.QueryValueEx(k, value_name)[0] + self._winreg.CloseKey(k) + return value + + def set_registry_value(self, reg, key, value_name, value): + k = self._winreg.OpenKey(reg, key, 0, self._winreg.KEY_WRITE) + value_type = self._winreg.REG_SZ + # if we handle the Path value, then set its type to REG_EXPAND_SZ + # so that things like %SystemRoot% get automatically expanded by the + # command prompt + if value_name == "Path": + value_type = self._winreg.REG_EXPAND_SZ + self._winreg.SetValueEx(k, value_name, 0, value_type, value) + self._winreg.CloseKey(k) + # on windows we need to hack up the to-be-installed scripts from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): @@ -195,8 +285,11 @@ os.remove(newname) f = open(newname, 'w') f.write("@echo off\n") - f.write('python "%%~dp0\%s" %%*\n' % newbasename) + f.write('python "%%~dp0\py_command_trampolin" %s %%*\n' % basename) f.close() + w32path = Win32PathHandling() + w32path.remove_pylib_path() + if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} else: From guido at codespeak.net Tue Aug 19 21:50:09 2008 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 19 Aug 2008 21:50:09 +0200 (CEST) Subject: [py-svn] r57474 - in py/trunk/py/path/svn: . testing Message-ID: <20080819195009.7F92416A02B@codespeak.net> Author: guido Date: Tue Aug 19 21:50:06 2008 New Revision: 57474 Modified: py/trunk/py/path/svn/testing/test_wccommand.py py/trunk/py/path/svn/wccommand.py Log: Fixed bug reported by Martijn Faassen - when the XML output of 'svn st --xml' didn't contain author information, the code raised an exception. Modified: py/trunk/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/trunk/py/path/svn/testing/test_wccommand.py (original) +++ py/trunk/py/path/svn/testing/test_wccommand.py Tue Aug 19 21:50:06 2008 @@ -1,7 +1,7 @@ import py import sys from py.__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc -from py.__.path.svn.wccommand import InfoSvnWCCommand +from py.__.path.svn.wccommand import InfoSvnWCCommand, XMLWCStatus from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon from py.__.conftest import option @@ -214,6 +214,19 @@ assert 'deletefile' not in s.unchanged assert [x.basename for x in s.deleted] == ['deletefile'] + def test_status_noauthor(self): + # testing for XML without author - this used to raise an exception + xml = '''\ + + + + 2008-08-19T16:50:53.400198Z + + + + ''' + XMLWCStatus.fromstring(xml, self.root) + def test_diff(self): p = self.root / 'anotherfile' out = p.diff(rev=2) Modified: py/trunk/py/path/svn/wccommand.py ============================================================================== --- py/trunk/py/path/svn/wccommand.py (original) +++ py/trunk/py/path/svn/wccommand.py Tue Aug 19 21:50:06 2008 @@ -684,9 +684,10 @@ if commitel: modrev = commitel.getAttribute('revision') author = '' - for c in commitel.getElementsByTagName('author')[0]\ - .childNodes: - author += c.nodeValue + author_els = commitel.getElementsByTagName('author') + if author_els: + for c in author_els[0].childNodes: + author += c.nodeValue date = '' for c in commitel.getElementsByTagName('date')[0]\ .childNodes: From hpk at codespeak.net Tue Aug 19 22:06:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 22:06:46 +0200 (CEST) Subject: [py-svn] r57475 - in py/release/0.9.x/py: . bin cmdline cmdline/testing Message-ID: <20080819200646.173BF169FCF@codespeak.net> Author: hpk Date: Tue Aug 19 22:06:43 2008 New Revision: 57475 Added: py/release/0.9.x/py/cmdline/ py/release/0.9.x/py/cmdline/__init__.py py/release/0.9.x/py/cmdline/pycleanup.py - copied, changed from r57469, py/release/0.9.x/py/bin/py.cleanup py/release/0.9.x/py/cmdline/pycountloc.py - copied, changed from r57469, py/release/0.9.x/py/bin/py.countloc py/release/0.9.x/py/cmdline/pylookup.py - copied, changed from r57469, py/release/0.9.x/py/bin/py.lookup py/release/0.9.x/py/cmdline/pyrest.py - copied, changed from r57469, py/release/0.9.x/py/bin/py.rest py/release/0.9.x/py/cmdline/pytest.py - copied, changed from r57469, py/release/0.9.x/py/bin/py.test py/release/0.9.x/py/cmdline/testing/ Removed: py/release/0.9.x/py/bin/py.cleanup py/release/0.9.x/py/bin/py.countloc py/release/0.9.x/py/bin/py.lookup py/release/0.9.x/py/bin/py.rest py/release/0.9.x/py/bin/py.test Modified: py/release/0.9.x/py/__init__.py Log: going for a systematic approach to py lib command line scripts that works for unix and windows and has some tests. Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Tue Aug 19 22:06:43 2008 @@ -38,6 +38,13 @@ # EXPORTED API exportdefs = { + # py lib cmdline tools + 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), + 'cmdline.pyrest' : ('./cmdline/pyrest.py', 'main',), + 'cmdline.pylookup' : ('./cmdline/pylookup.py', 'main',), + 'cmdline.pycountloc' : ('./cmdline/pycountloc.py', 'main',), + 'cmdline.pycleanup' : ('./cmdline/pycleanup.py', 'main',), + # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), 'test.raises' : ('./test/raises.py', 'raises'), Deleted: /py/release/0.9.x/py/bin/py.cleanup ============================================================================== --- /py/release/0.9.x/py/bin/py.cleanup Tue Aug 19 22:06:43 2008 +++ (empty file) @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -"""\ -py.cleanup [PATH] - -Delete pyc file recursively, starting from PATH (which defaults to the current -working directory). Don't follow links and don't recurse into directories with -a ".". -""" -from _findpy import py -import py - -parser = py.compat.optparse.OptionParser(usage=__doc__) - -if __name__ == '__main__': - (options, args) = parser.parse_args() - - if not args: - args = ["."] - for arg in args: - path = py.path.local(arg) - print "cleaning path", path - for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)): - x.remove() Deleted: /py/release/0.9.x/py/bin/py.countloc ============================================================================== --- /py/release/0.9.x/py/bin/py.countloc Tue Aug 19 22:06:43 2008 +++ (empty file) @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -# hands on script to compute the non-empty Lines of Code -# for tests and non-test code - -"""\ -py.countloc [PATHS] - -Count (non-empty) lines of python code and number of python files recursively -starting from a list of paths given on the command line (starting from the -current working directory). Distinguish between test files and normal ones and -report them separately. -""" -from _findpy import py -import py -from py.compat import optparse -from py.__.misc.cmdline.countloc import countloc - -parser = optparse.OptionParser(usage=__doc__) - -if __name__ == '__main__': - (options, args) = parser.parse_args() - countloc(args) Deleted: /py/release/0.9.x/py/bin/py.lookup ============================================================================== --- /py/release/0.9.x/py/bin/py.lookup Tue Aug 19 22:06:43 2008 +++ (empty file) @@ -1,76 +0,0 @@ -#!/usr/bin/env python - -"""\ -py.lookup SEARCH_STRING [options] - -Looks recursively at Python files for a SEARCH_STRING, starting from the -present working directory. Prints the line, with the filename and line-number -prepended.""" - -import sys, os -sys.path.insert(0, os.path.dirname(__file__)) -from _findpy import py -from py.__.misc.terminal_helper import ansi_print, terminal_width -import re - -curdir = py.path.local() -def rec(p): - return p.check(dotfile=0) - -optparse = py.compat.optparse - -parser = optparse.OptionParser(usage=__doc__) -parser.add_option("-i", "--ignore-case", action="store_true", dest="ignorecase", - help="ignore case distinctions") -parser.add_option("-C", "--context", action="store", type="int", dest="context", - default=0, help="How many lines of output to show") - -def find_indexes(search_line, string): - indexes = [] - before = 0 - while 1: - i = search_line.find(string, before) - if i == -1: - break - indexes.append(i) - before = i + len(string) - return indexes - - -if __name__ == '__main__': - (options, args) = parser.parse_args() - string = args[0] - if options.ignorecase: - string = string.lower() - for x in curdir.visit('*.py', rec): - try: - s = x.read() - except py.error.ENOENT: - pass # whatever, probably broken link (ie emacs lock) - searchs = s - if options.ignorecase: - searchs = s.lower() - if s.find(string) != -1: - lines = s.splitlines() - if options.ignorecase: - searchlines = s.lower().splitlines() - else: - searchlines = lines - for i, (line, searchline) in enumerate(zip(lines, searchlines)): - indexes = find_indexes(searchline, string) - if not indexes: - continue - if not options.context: - sys.stdout.write("%s:%d: " %(x.relto(curdir), i+1)) - last_index = 0 - for index in indexes: - sys.stdout.write(line[last_index: index]) - ansi_print(line[index: index+len(string)], - file=sys.stdout, esc=31, newline=False) - last_index = index + len(string) - sys.stdout.write(line[last_index:] + "\n") - else: - context = (options.context)/2 - for count in range(max(0, i-context), min(len(lines) - 1, i+context+1)): - print "%s:%d: %s" %(x.relto(curdir), count+1, lines[count].rstrip()) - print "-" * terminal_width Deleted: /py/release/0.9.x/py/bin/py.rest ============================================================================== --- /py/release/0.9.x/py/bin/py.rest Tue Aug 19 22:06:43 2008 +++ (empty file) @@ -1,79 +0,0 @@ -#!/usr/bin/env python -""" -invoke - - py.rest filename1.txt directory - -to generate html files from ReST. - -It is also possible to generate pdf files using the --topdf option. - -http://docutils.sourceforge.net/docs/user/rst/quickref.html - -""" - -import os, sys -from _findpy import py -from py.__.misc import rest -from py.__.rest import directive -from py.__.rest.latex import process_rest_file, process_configfile - - -if hasattr(sys.stdout, 'fileno') and os.isatty(sys.stdout.fileno()): - def log(msg): - print msg -else: - def log(msg): - pass - -optparse = py.compat.optparse - -parser = optparse.OptionParser(usage=__doc__) -parser.add_option("--topdf", action="store_true", dest="topdf", default=False, - help="generate pdf files") -parser.add_option("--stylesheet", dest="stylesheet", default=None, - help="use specified latex style sheet") -parser.add_option("--debug", action="store_true", dest="debug", - default=False, - help="print debug output and don't delete files") - - -if __name__=='__main__': - (options, args) = parser.parse_args() - - if len(args) == 0: - filenames = [py.path.svnwc()] - else: - filenames = [py.path.svnwc(x) for x in args] - - if options.topdf: - directive.set_backend_and_register_directives("latex") - - for p in filenames: - if not p.check(): - log("path %s not found, ignoring" % p) - continue - def fil(p): - return p.check(fnmatch='*.txt', versioned=True) - def rec(p): - return p.check(dotfile=0) - if p.check(dir=1): - for x in p.visit(fil, rec): - rest.process(x) - elif p.check(file=1): - if p.ext == ".rst2pdfconfig": - directive.set_backend_and_register_directives("latex") - process_configfile(p, options.debug) - else: - if options.topdf: - cfg = p.new(ext=".rst2pdfconfig") - if cfg.check(): - print "using config file %s" % (cfg, ) - process_configfile(cfg, options.debug) - else: - process_rest_file(p.localpath, - options.stylesheet, - options.debug) - else: - rest.process(p) - Deleted: /py/release/0.9.x/py/bin/py.test ============================================================================== --- /py/release/0.9.x/py/bin/py.test Tue Aug 19 22:06:43 2008 +++ (empty file) @@ -1,4 +0,0 @@ -#!/usr/bin/env python - -from _findpy import py -py.test.cmdline.main() Added: py/release/0.9.x/py/cmdline/__init__.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/cmdline/__init__.py Tue Aug 19 22:06:43 2008 @@ -0,0 +1 @@ +# Copied: py/release/0.9.x/py/cmdline/pycleanup.py (from r57469, py/release/0.9.x/py/bin/py.cleanup) ============================================================================== --- py/release/0.9.x/py/bin/py.cleanup (original) +++ py/release/0.9.x/py/cmdline/pycleanup.py Tue Aug 19 22:06:43 2008 @@ -7,14 +7,11 @@ working directory). Don't follow links and don't recurse into directories with a ".". """ -from _findpy import py import py -parser = py.compat.optparse.OptionParser(usage=__doc__) - -if __name__ == '__main__': +def main(): + parser = py.compat.optparse.OptionParser(usage=__doc__) (options, args) = parser.parse_args() - if not args: args = ["."] for arg in args: Copied: py/release/0.9.x/py/cmdline/pycountloc.py (from r57469, py/release/0.9.x/py/bin/py.countloc) ============================================================================== --- py/release/0.9.x/py/bin/py.countloc (original) +++ py/release/0.9.x/py/cmdline/pycountloc.py Tue Aug 19 22:06:43 2008 @@ -16,8 +16,7 @@ from py.compat import optparse from py.__.misc.cmdline.countloc import countloc -parser = optparse.OptionParser(usage=__doc__) - -if __name__ == '__main__': +def main(): + parser = optparse.OptionParser(usage=__doc__) (options, args) = parser.parse_args() countloc(args) Copied: py/release/0.9.x/py/cmdline/pylookup.py (from r57469, py/release/0.9.x/py/bin/py.lookup) ============================================================================== --- py/release/0.9.x/py/bin/py.lookup (original) +++ py/release/0.9.x/py/cmdline/pylookup.py Tue Aug 19 22:06:43 2008 @@ -8,8 +8,7 @@ prepended.""" import sys, os -sys.path.insert(0, os.path.dirname(__file__)) -from _findpy import py +import py from py.__.misc.terminal_helper import ansi_print, terminal_width import re @@ -36,8 +35,7 @@ before = i + len(string) return indexes - -if __name__ == '__main__': +def main(): (options, args) = parser.parse_args() string = args[0] if options.ignorecase: Copied: py/release/0.9.x/py/cmdline/pyrest.py (from r57469, py/release/0.9.x/py/bin/py.rest) ============================================================================== --- py/release/0.9.x/py/bin/py.rest (original) +++ py/release/0.9.x/py/cmdline/pyrest.py Tue Aug 19 22:06:43 2008 @@ -38,7 +38,7 @@ help="print debug output and don't delete files") -if __name__=='__main__': +def main(): (options, args) = parser.parse_args() if len(args) == 0: Copied: py/release/0.9.x/py/cmdline/pytest.py (from r57469, py/release/0.9.x/py/bin/py.test) ============================================================================== --- py/release/0.9.x/py/bin/py.test (original) +++ py/release/0.9.x/py/cmdline/pytest.py Tue Aug 19 22:06:43 2008 @@ -1,4 +1,5 @@ #!/usr/bin/env python +import py -from _findpy import py -py.test.cmdline.main() +def main(): + py.test.cmdline.main() From hpk at codespeak.net Tue Aug 19 22:28:41 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 22:28:41 +0200 (CEST) Subject: [py-svn] r57476 - in py/release/0.9.x/py: bin cmdline cmdline/testing Message-ID: <20080819202841.4E91D16A038@codespeak.net> Author: hpk Date: Tue Aug 19 22:28:39 2008 New Revision: 57476 Added: py/release/0.9.x/py/bin/_genscripts.py py/release/0.9.x/py/bin/py.cleanup (contents, props changed) py/release/0.9.x/py/bin/py.cleanup.cmd py/release/0.9.x/py/bin/py.countloc (contents, props changed) py/release/0.9.x/py/bin/py.countloc.cmd py/release/0.9.x/py/bin/py.lookup (contents, props changed) py/release/0.9.x/py/bin/py.lookup.cmd py/release/0.9.x/py/bin/py.rest (contents, props changed) py/release/0.9.x/py/bin/py.rest.cmd py/release/0.9.x/py/bin/py.test (contents, props changed) py/release/0.9.x/py/bin/py.test.cmd py/release/0.9.x/py/cmdline/testing/test_generic.py Modified: py/release/0.9.x/py/cmdline/ (props changed) py/release/0.9.x/py/cmdline/__init__.py (props changed) py/release/0.9.x/py/cmdline/pycleanup.py (props changed) py/release/0.9.x/py/cmdline/pycountloc.py (props changed) py/release/0.9.x/py/cmdline/pylookup.py (props changed) py/release/0.9.x/py/cmdline/pytest.py (props changed) py/release/0.9.x/py/cmdline/testing/ (props changed) Log: adding script for generating entry points for both unix and windows. and generic tests for checking consistency between what is in py/bin/ and what py.cmdline.* provides. Added: py/release/0.9.x/py/bin/_genscripts.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/_genscripts.py Tue Aug 19 22:28:39 2008 @@ -0,0 +1,34 @@ +from _findpy import py + +mydir = py.magic.autopath().dirpath() + +def getbasename(name): + assert name[:2] == "py" + return "py." + name[2:] + +def genscript_unix(name): + basename = getbasename(name) + path = mydir.join(basename) + path.write(py.code.Source(""" + #!/usr/bin/env python + from _findpy import py + py.cmdline.%s() + """ % name).strip()) + path.chmod(0755) + +def genscript_windows(name): + basename = getbasename(name) + basename += ".cmd" + path = mydir.join(basename) + path.write(py.code.Source(""" + @echo off + python -c "from _findpy import py ; py.cmdline.%s()" + """ % (name)).strip()) + +if __name__ == "__main__": + for name in dir(py.cmdline): + if name[0] != "_": + genscript_unix(name) + genscript_windows(name) + + Added: py/release/0.9.x/py/bin/py.cleanup ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.cleanup Tue Aug 19 22:28:39 2008 @@ -0,0 +1,3 @@ +#!/usr/bin/env python +from _findpy import py +py.cmdline.pycleanup() \ No newline at end of file Added: py/release/0.9.x/py/bin/py.cleanup.cmd ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.cleanup.cmd Tue Aug 19 22:28:39 2008 @@ -0,0 +1,2 @@ + at echo off +python -c "from _findpy import py ; py.cmdline.pycleanup()" \ No newline at end of file Added: py/release/0.9.x/py/bin/py.countloc ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.countloc Tue Aug 19 22:28:39 2008 @@ -0,0 +1,3 @@ +#!/usr/bin/env python +from _findpy import py +py.cmdline.pycountloc() \ No newline at end of file Added: py/release/0.9.x/py/bin/py.countloc.cmd ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.countloc.cmd Tue Aug 19 22:28:39 2008 @@ -0,0 +1,2 @@ + at echo off +python -c "from _findpy import py ; py.cmdline.pycountloc()" \ No newline at end of file Added: py/release/0.9.x/py/bin/py.lookup ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.lookup Tue Aug 19 22:28:39 2008 @@ -0,0 +1,3 @@ +#!/usr/bin/env python +from _findpy import py +py.cmdline.pylookup() \ No newline at end of file Added: py/release/0.9.x/py/bin/py.lookup.cmd ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.lookup.cmd Tue Aug 19 22:28:39 2008 @@ -0,0 +1,2 @@ + at echo off +python -c "from _findpy import py ; py.cmdline.pylookup()" \ No newline at end of file Added: py/release/0.9.x/py/bin/py.rest ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.rest Tue Aug 19 22:28:39 2008 @@ -0,0 +1,3 @@ +#!/usr/bin/env python +from _findpy import py +py.cmdline.pyrest() \ No newline at end of file Added: py/release/0.9.x/py/bin/py.rest.cmd ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.rest.cmd Tue Aug 19 22:28:39 2008 @@ -0,0 +1,2 @@ + at echo off +python -c "from _findpy import py ; py.cmdline.pyrest()" \ No newline at end of file Added: py/release/0.9.x/py/bin/py.test ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.test Tue Aug 19 22:28:39 2008 @@ -0,0 +1,3 @@ +#!/usr/bin/env python +from _findpy import py +py.cmdline.pytest() \ No newline at end of file Added: py/release/0.9.x/py/bin/py.test.cmd ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/py.test.cmd Tue Aug 19 22:28:39 2008 @@ -0,0 +1,2 @@ + at echo off +python -c "from _findpy import py ; py.cmdline.pytest()" \ No newline at end of file Added: py/release/0.9.x/py/cmdline/testing/test_generic.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/cmdline/testing/test_generic.py Tue Aug 19 22:28:39 2008 @@ -0,0 +1,42 @@ +import py +import sys + +binpath = py.path.local(py.__file__).dirpath("bin") + +def setup_module(mod): + mod.tmpdir = py.test.ensuretemp(__name__) + mod.iswin32 = sys.platform == "win32" + +def checkmain(name): + main = getattr(py.cmdline, name) + assert callable(main) + assert name[:2] == "py" + scriptname = "py." + name[2:] + assert binpath.join(scriptname).check() + assert binpath.join(scriptname + ".cmd").check() + +def checkprocess(script): + assert script.check() + old = tmpdir.ensure(script.basename, dir=1).chdir() + try: + cmd = "%s" %(script, ) + if script.basename.startswith("py.lookup"): + cmd += " hello" + print "executing", script + py.process.cmdexec(cmd) + finally: + old.chdir() + +def test_cmdline_namespace(): + for name in dir(py.cmdline): + if name[0] != "_": + yield checkmain, name + +def test_script_invocation(): + for script in binpath.listdir("py.*"): + if script.ext == ".cmd": + if iswin32: + yield checkprocess, script + else: + yield checkprocess, script + From hpk at codespeak.net Tue Aug 19 22:39:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 22:39:37 +0200 (CEST) Subject: [py-svn] r57477 - in py/release/0.9.x/py/path/svn: . testing Message-ID: <20080819203937.7C10F169FA9@codespeak.net> Author: hpk Date: Tue Aug 19 22:39:36 2008 New Revision: 57477 Modified: py/release/0.9.x/py/path/svn/testing/test_wccommand.py py/release/0.9.x/py/path/svn/wccommand.py Log: porting author/svn-xml fix 57474 from trunk Modified: py/release/0.9.x/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/release/0.9.x/py/path/svn/testing/test_wccommand.py (original) +++ py/release/0.9.x/py/path/svn/testing/test_wccommand.py Tue Aug 19 22:39:36 2008 @@ -1,7 +1,7 @@ import py import sys from py.__.path.svn.testing.svntestbase import CommonSvnTests, getrepowc -from py.__.path.svn.wccommand import InfoSvnWCCommand +from py.__.path.svn.wccommand import InfoSvnWCCommand, XMLWCStatus from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon from py.__.conftest import option @@ -214,6 +214,19 @@ assert 'deletefile' not in s.unchanged assert [x.basename for x in s.deleted] == ['deletefile'] + def test_status_noauthor(self): + # testing for XML without author - this used to raise an exception + xml = '''\ + + + + 2008-08-19T16:50:53.400198Z + + + + ''' + XMLWCStatus.fromstring(xml, self.root) + def test_diff(self): p = self.root / 'anotherfile' out = p.diff(rev=2) Modified: py/release/0.9.x/py/path/svn/wccommand.py ============================================================================== --- py/release/0.9.x/py/path/svn/wccommand.py (original) +++ py/release/0.9.x/py/path/svn/wccommand.py Tue Aug 19 22:39:36 2008 @@ -684,9 +684,10 @@ if commitel: modrev = commitel.getAttribute('revision') author = '' - for c in commitel.getElementsByTagName('author')[0]\ - .childNodes: - author += c.nodeValue + author_els = commitel.getElementsByTagName('author') + if author_els: + for c in author_els[0].childNodes: + author += c.nodeValue date = '' for c in commitel.getElementsByTagName('date')[0]\ .childNodes: From hpk at codespeak.net Tue Aug 19 23:01:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 23:01:26 +0200 (CEST) Subject: [py-svn] r57479 - in py/release/0.9.x/py: bin bin/win cmdline/testing Message-ID: <20080819210126.BC7EA169FD5@codespeak.net> Author: hpk Date: Tue Aug 19 23:01:24 2008 New Revision: 57479 Added: py/release/0.9.x/py/bin/win/ py/release/0.9.x/py/bin/win/py.cleanup.cmd - copied unchanged from r57476, py/release/0.9.x/py/bin/py.cleanup.cmd py/release/0.9.x/py/bin/win/py.countloc.cmd - copied unchanged from r57476, py/release/0.9.x/py/bin/py.countloc.cmd py/release/0.9.x/py/bin/win/py.lookup.cmd - copied unchanged from r57476, py/release/0.9.x/py/bin/py.lookup.cmd py/release/0.9.x/py/bin/win/py.rest.cmd - copied unchanged from r57476, py/release/0.9.x/py/bin/py.rest.cmd py/release/0.9.x/py/bin/win/py.test.cmd - copied unchanged from r57476, py/release/0.9.x/py/bin/py.test.cmd Removed: py/release/0.9.x/py/bin/py.cleanup.cmd py/release/0.9.x/py/bin/py.countloc.cmd py/release/0.9.x/py/bin/py.lookup.cmd py/release/0.9.x/py/bin/py.rest.cmd py/release/0.9.x/py/bin/py.test.cmd Modified: py/release/0.9.x/py/bin/_genscripts.py py/release/0.9.x/py/cmdline/testing/test_generic.py Log: separating windows and unix scripts Modified: py/release/0.9.x/py/bin/_genscripts.py ============================================================================== --- py/release/0.9.x/py/bin/_genscripts.py (original) +++ py/release/0.9.x/py/bin/_genscripts.py Tue Aug 19 23:01:24 2008 @@ -19,7 +19,7 @@ def genscript_windows(name): basename = getbasename(name) basename += ".cmd" - path = mydir.join(basename) + path = mydir.join("win").join(basename) path.write(py.code.Source(""" @echo off python -c "from _findpy import py ; py.cmdline.%s()" Deleted: /py/release/0.9.x/py/bin/py.cleanup.cmd ============================================================================== --- /py/release/0.9.x/py/bin/py.cleanup.cmd Tue Aug 19 23:01:24 2008 +++ (empty file) @@ -1,2 +0,0 @@ - at echo off -python -c "from _findpy import py ; py.cmdline.pycleanup()" \ No newline at end of file Deleted: /py/release/0.9.x/py/bin/py.countloc.cmd ============================================================================== --- /py/release/0.9.x/py/bin/py.countloc.cmd Tue Aug 19 23:01:24 2008 +++ (empty file) @@ -1,2 +0,0 @@ - at echo off -python -c "from _findpy import py ; py.cmdline.pycountloc()" \ No newline at end of file Deleted: /py/release/0.9.x/py/bin/py.lookup.cmd ============================================================================== --- /py/release/0.9.x/py/bin/py.lookup.cmd Tue Aug 19 23:01:24 2008 +++ (empty file) @@ -1,2 +0,0 @@ - at echo off -python -c "from _findpy import py ; py.cmdline.pylookup()" \ No newline at end of file Deleted: /py/release/0.9.x/py/bin/py.rest.cmd ============================================================================== --- /py/release/0.9.x/py/bin/py.rest.cmd Tue Aug 19 23:01:24 2008 +++ (empty file) @@ -1,2 +0,0 @@ - at echo off -python -c "from _findpy import py ; py.cmdline.pyrest()" \ No newline at end of file Deleted: /py/release/0.9.x/py/bin/py.test.cmd ============================================================================== --- /py/release/0.9.x/py/bin/py.test.cmd Tue Aug 19 23:01:24 2008 +++ (empty file) @@ -1,2 +0,0 @@ - at echo off -python -c "from _findpy import py ; py.cmdline.pytest()" \ No newline at end of file Modified: py/release/0.9.x/py/cmdline/testing/test_generic.py ============================================================================== --- py/release/0.9.x/py/cmdline/testing/test_generic.py (original) +++ py/release/0.9.x/py/cmdline/testing/test_generic.py Tue Aug 19 23:01:24 2008 @@ -2,6 +2,7 @@ import sys binpath = py.path.local(py.__file__).dirpath("bin") +binwinpath = binpath.join("win") def setup_module(mod): mod.tmpdir = py.test.ensuretemp(__name__) @@ -13,7 +14,7 @@ assert name[:2] == "py" scriptname = "py." + name[2:] assert binpath.join(scriptname).check() - assert binpath.join(scriptname + ".cmd").check() + assert binwinpath.join(scriptname + ".cmd").check() def checkprocess(script): assert script.check() @@ -33,10 +34,11 @@ yield checkmain, name def test_script_invocation(): - for script in binpath.listdir("py.*"): - if script.ext == ".cmd": - if iswin32: - yield checkprocess, script - else: + if iswin32: + for script in binwinpath.listdir("py.*"): + assert script.ext == ".cmd" + yield checkprocess, script + else: + for script in binpath.listdir("py.*"): yield checkprocess, script From hpk at codespeak.net Tue Aug 19 23:42:15 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 23:42:15 +0200 (CEST) Subject: [py-svn] r57480 - py/trunk/py/path/gateway Message-ID: <20080819214215.39FF716A020@codespeak.net> Author: hpk Date: Tue Aug 19 23:42:13 2008 New Revision: 57480 Modified: py/trunk/py/path/gateway/channeltest.py py/trunk/py/path/gateway/channeltest2.py Log: fix changed namespace Modified: py/trunk/py/path/gateway/channeltest.py ============================================================================== --- py/trunk/py/path/gateway/channeltest.py (original) +++ py/trunk/py/path/gateway/channeltest.py Tue Aug 19 23:42:13 2008 @@ -53,7 +53,7 @@ if __name__ == '__main__': import py gw = py.execnet.PopenGateway() - channel = gw.channelfactory.new() + channel = gw._channelfactory.new() srv = PathServer(channel) c = gw.remote_exec(""" import remotepath Modified: py/trunk/py/path/gateway/channeltest2.py ============================================================================== --- py/trunk/py/path/gateway/channeltest2.py (original) +++ py/trunk/py/path/gateway/channeltest2.py Tue Aug 19 23:42:13 2008 @@ -14,7 +14,7 @@ #gw = py.execnet.SshGateway('codespeak.net') gw = py.execnet.PopenGateway() c = gw.remote_exec(SRC) -subchannel = gw.channelfactory.new() +subchannel = gw._channelfactory.new() c.send(subchannel) p = RemotePath(subchannel, c.receive()) From hpk at codespeak.net Tue Aug 19 23:47:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 19 Aug 2008 23:47:11 +0200 (CEST) Subject: [py-svn] r57481 - py/trunk/py/path/gateway Message-ID: <20080819214711.30536169FA3@codespeak.net> Author: hpk Date: Tue Aug 19 23:47:09 2008 New Revision: 57481 Modified: py/trunk/py/path/gateway/channeltest.py py/trunk/py/path/gateway/channeltest2.py py/trunk/py/path/gateway/remotepath.py Log: fix a few things, seems to work again. Modified: py/trunk/py/path/gateway/channeltest.py ============================================================================== --- py/trunk/py/path/gateway/channeltest.py (original) +++ py/trunk/py/path/gateway/channeltest.py Tue Aug 19 23:47:09 2008 @@ -25,7 +25,7 @@ def command_GET(self, id, spec): path = self.C2P[id] - self.channel.send(path.get(spec)) + self.channel.send(path._getbyspec(spec)) def command_READ(self, id): path = self.C2P[id] Modified: py/trunk/py/path/gateway/channeltest2.py ============================================================================== --- py/trunk/py/path/gateway/channeltest2.py (original) +++ py/trunk/py/path/gateway/channeltest2.py Tue Aug 19 23:47:09 2008 @@ -13,7 +13,8 @@ #gw = py.execnet.SshGateway('codespeak.net') gw = py.execnet.PopenGateway() -c = gw.remote_exec(SRC) +gw.remote_init_threads(5) +c = gw.remote_exec(SRC, stdout=py.std.sys.stdout, stderr=py.std.sys.stderr) subchannel = gw._channelfactory.new() c.send(subchannel) Modified: py/trunk/py/path/gateway/remotepath.py ============================================================================== --- py/trunk/py/path/gateway/remotepath.py (original) +++ py/trunk/py/path/gateway/remotepath.py Tue Aug 19 23:47:09 2008 @@ -33,7 +33,7 @@ self._channel.send(('JOIN', self._id, id) + args) return RemotePath(self._channel, id) - def get(self, spec): + def _getbyspec(self, spec): parts = spec.split(',') ask = [x for x in parts if x not in self._specs] if ask: From hpk at codespeak.net Wed Aug 20 00:22:47 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 00:22:47 +0200 (CEST) Subject: [py-svn] r57483 - in py/release/0.9.x: . py Message-ID: <20080819222247.53DFE169FEE@codespeak.net> Author: hpk Date: Wed Aug 20 00:22:45 2008 New Revision: 57483 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: another try at a clean install of scripts Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Wed Aug 20 00:22:45 2008 @@ -63,11 +63,17 @@ py/apigen/tracer/tracer.py py/bin/_docgen.py py/bin/_findpy.py +py/bin/_genscripts.py py/bin/py.cleanup py/bin/py.countloc py/bin/py.lookup py/bin/py.rest py/bin/py.test +py/bin/win/py.cleanup.cmd +py/bin/win/py.countloc.cmd +py/bin/win/py.lookup.cmd +py/bin/win/py.rest.cmd +py/bin/win/py.test.cmd py/builtin/__init__.py py/builtin/enumerate.py py/builtin/exception.py @@ -98,6 +104,13 @@ py/c-extension/greenlet/test_greenlet.py py/c-extension/greenlet/test_remote.py py/c-extension/greenlet/test_throw.py +py/cmdline/__init__.py +py/cmdline/pycleanup.py +py/cmdline/pycountloc.py +py/cmdline/pylookup.py +py/cmdline/pyrest.py +py/cmdline/pytest.py +py/cmdline/testing/test_generic.py py/code/__init__.py py/code/code.py py/code/excinfo.py Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 00:22:45 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2-alpha-8" +version = "0.9.2a9" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 00:22:45 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2-alpha-8', + version='0.9.2a9', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -26,6 +26,7 @@ author_email='holger at merlinux.eu, py-dev at codespeak.net', ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], + scripts = getscripts(), py_modules=['_findpy'], long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', @@ -40,11 +41,6 @@ 'Topic :: System :: Distributed Computing', 'Topic :: Utilities', 'Programming Language :: Python'], - scripts=['py/bin/py.cleanup', - 'py/bin/py.countloc', - 'py/bin/py.lookup', - 'py/bin/py.rest', - 'py/bin/py.test'], packages=['py', 'py.apigen', 'py.apigen.rest', @@ -59,6 +55,7 @@ 'py.apigen.tracer.testing.package.submodule.pak', 'py.builtin', 'py.builtin.testing', + 'py.cmdline', 'py.code', 'py.code.testing', 'py.compat', @@ -110,11 +107,17 @@ 'apigen/todo.txt', 'bin/_docgen.py', 'bin/_findpy.py', + 'bin/_genscripts.py', 'bin/py.cleanup', 'bin/py.countloc', 'bin/py.lookup', 'bin/py.rest', 'bin/py.test', + 'bin/win/py.cleanup.cmd', + 'bin/win/py.countloc.cmd', + 'bin/win/py.lookup.cmd', + 'bin/win/py.rest.cmd', + 'bin/win/py.test.cmd', 'c-extension/greenlet/README.txt', 'c-extension/greenlet/dummy_greenlet.py', 'c-extension/greenlet/greenlet.c', @@ -133,6 +136,7 @@ 'c-extension/greenlet/test_greenlet.py', 'c-extension/greenlet/test_remote.py', 'c-extension/greenlet/test_throw.py', + 'cmdline/testing/test_generic.py', 'compat/LICENSE', 'compat/testing/test_doctest.txt', 'compat/testing/test_doctest2.txt', @@ -190,71 +194,19 @@ zip_safe=False, ) -class Win32PathHandling: - """ a helper to remove things added to system PATHs in previous installations. """ - _winreg = None - def __init__(self): - if sys.platform == 'win32': - try: - import _winreg - except ImportError: - print sys.stderr, "huh could not import _winreg on windows, ignoring" - else: - self._winreg = _winreg - - def remove_pylib_path(self): - reg = self._winreg.ConnectRegistry( - None, self._winreg.HKEY_LOCAL_MACHINE) - key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" - path = self.get_registry_value(reg, key, "Path") - newpath = self.prunepath(path) - if newpath != path: - print "PATH contains old py/bin/win32 scripts:", path - print "pruning and setting a new PATH:", newpath - self.set_registry_value(reg, key, "Path", newpath) - # Propagate changes to current command prompt - os.system("set PATH=%s" % path) - self.try_propagate_system() - - def prunepath(self, path): - basename = os.path.basename - dirname = os.path.dirname - l = [] - for p in path.split(';'): - if basename(p) == "win32" and basename(dirname(p)) == "bin" \ - and basename(dirname(dirname(p))) == "py": - continue # prune this path - l.append(p) - return ";".join(l) - - def try_propagate_system(self): - try: - import win32gui, win32con - except ImportError: - return - # Propagate changes throughout the system - win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, - win32con.WM_SETTINGCHANGE, 0, "Environment", - win32con.SMTO_ABORTIFHUNG, 5000) - - def get_registry_value(self, reg, key, value_name): - k = self._winreg.OpenKey(reg, key) - value = self._winreg.QueryValueEx(k, value_name)[0] - self._winreg.CloseKey(k) - return value - - def set_registry_value(self, reg, key, value_name, value): - k = self._winreg.OpenKey(reg, key, 0, self._winreg.KEY_WRITE) - value_type = self._winreg.REG_SZ - # if we handle the Path value, then set its type to REG_EXPAND_SZ - # so that things like %SystemRoot% get automatically expanded by the - # command prompt - if value_name == "Path": - value_type = self._winreg.REG_EXPAND_SZ - self._winreg.SetValueEx(k, value_name, 0, value_type, value) - self._winreg.CloseKey(k) - -# on windows we need to hack up the to-be-installed scripts +def getscripts(): + if sys.platform == "win32": + base = "py/bin/win32/" + ext = ".cmd" + else: + base = "py/bin/" + ext = "" + l = [] + for name in ['py.rest', 'py.countloc', 'py.lookup', 'py.test', 'py.cleanup']: + l.append(base + name + ext) + +# if this is an sdist install we might have the unix +# scripts and need to replace them with windows ones. from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): def run(self): @@ -262,23 +214,20 @@ #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) - if "." in basename: - #print "tackling", fn - newbasename = basename.replace(".", "_") - newfn = os.path.join(os.path.dirname(fn), newbasename) - if os.path.exists(newfn): - os.remove(newfn) - os.rename(fn, newfn) + if basename.startswith("py.") and not basename.endswith(".cmd"): + os.remove(fn) + # also remove paths from intermediate alpha versions + p = os.path.join(os.path.dirname(fn), basename.replace(".", "_")) + if os.path.exists(p): + p.remove() newname = fn + ".cmd" if os.path.exists(newname): os.remove(newname) f = open(newname, 'w') f.write("@echo off\n") - f.write('python "%%~dp0\%s" %%*\n' % newbasename) + name = "py" + basename[3:] + f.write('python -c "from _findpy import py; py.cmdline.%s"' % name) f.close() - w32path = Win32PathHandling() - w32path.remove_pylib_path() - if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} else: From hpk at codespeak.net Wed Aug 20 00:37:54 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 00:37:54 +0200 (CEST) Subject: [py-svn] r57484 - in py/release/0.9.x/py: bin bin/win bin/win32 cmdline/testing Message-ID: <20080819223754.D7C7C169FDD@codespeak.net> Author: hpk Date: Wed Aug 20 00:37:54 2008 New Revision: 57484 Added: py/release/0.9.x/py/bin/win32/ - copied from r57479, py/release/0.9.x/py/bin/win/ Removed: py/release/0.9.x/py/bin/win/ Modified: py/release/0.9.x/py/bin/_genscripts.py py/release/0.9.x/py/cmdline/testing/test_generic.py Log: using the same name for windows scripts as in 0.9.1 Modified: py/release/0.9.x/py/bin/_genscripts.py ============================================================================== --- py/release/0.9.x/py/bin/_genscripts.py (original) +++ py/release/0.9.x/py/bin/_genscripts.py Wed Aug 20 00:37:54 2008 @@ -19,7 +19,7 @@ def genscript_windows(name): basename = getbasename(name) basename += ".cmd" - path = mydir.join("win").join(basename) + path = mydir.join("win32").join(basename) path.write(py.code.Source(""" @echo off python -c "from _findpy import py ; py.cmdline.%s()" Modified: py/release/0.9.x/py/cmdline/testing/test_generic.py ============================================================================== --- py/release/0.9.x/py/cmdline/testing/test_generic.py (original) +++ py/release/0.9.x/py/cmdline/testing/test_generic.py Wed Aug 20 00:37:54 2008 @@ -2,7 +2,7 @@ import sys binpath = py.path.local(py.__file__).dirpath("bin") -binwinpath = binpath.join("win") +binwinpath = binpath.join("win32") def setup_module(mod): mod.tmpdir = py.test.ensuretemp(__name__) From hpk at codespeak.net Wed Aug 20 00:39:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 00:39:46 +0200 (CEST) Subject: [py-svn] r57485 - in py/release/0.9.x: . py Message-ID: <20080819223946.0FBB7169FEB@codespeak.net> Author: hpk Date: Wed Aug 20 00:39:45 2008 New Revision: 57485 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: few fixes, another try Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Wed Aug 20 00:39:45 2008 @@ -69,11 +69,11 @@ py/bin/py.lookup py/bin/py.rest py/bin/py.test -py/bin/win/py.cleanup.cmd -py/bin/win/py.countloc.cmd -py/bin/win/py.lookup.cmd -py/bin/win/py.rest.cmd -py/bin/win/py.test.cmd +py/bin/win32/py.cleanup.cmd +py/bin/win32/py.countloc.cmd +py/bin/win32/py.lookup.cmd +py/bin/win32/py.rest.cmd +py/bin/win32/py.test.cmd py/builtin/__init__.py py/builtin/enumerate.py py/builtin/exception.py Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 00:39:45 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2a9" +version = "0.9.2a10" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 00:39:45 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2a9', + version='0.9.2a10', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -113,11 +113,11 @@ 'bin/py.lookup', 'bin/py.rest', 'bin/py.test', - 'bin/win/py.cleanup.cmd', - 'bin/win/py.countloc.cmd', - 'bin/win/py.lookup.cmd', - 'bin/win/py.rest.cmd', - 'bin/win/py.test.cmd', + 'bin/win32/py.cleanup.cmd', + 'bin/win32/py.countloc.cmd', + 'bin/win32/py.lookup.cmd', + 'bin/win32/py.rest.cmd', + 'bin/win32/py.test.cmd', 'c-extension/greenlet/README.txt', 'c-extension/greenlet/dummy_greenlet.py', 'c-extension/greenlet/greenlet.c', @@ -204,6 +204,7 @@ l = [] for name in ['py.rest', 'py.countloc', 'py.lookup', 'py.test', 'py.cleanup']: l.append(base + name + ext) + return l # if this is an sdist install we might have the unix # scripts and need to replace them with windows ones. @@ -226,7 +227,7 @@ f = open(newname, 'w') f.write("@echo off\n") name = "py" + basename[3:] - f.write('python -c "from _findpy import py; py.cmdline.%s"' % name) + f.write('python -c "from _findpy import py; py.cmdline.%s()"' % name) f.close() if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Wed Aug 20 00:54:34 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 00:54:34 +0200 (CEST) Subject: [py-svn] r57486 - py/build Message-ID: <20080819225434.6C566169F94@codespeak.net> Author: hpk Date: Wed Aug 20 00:54:31 2008 New Revision: 57486 Added: py/build/winpathclean.py - copied, changed from r57462, py/build/winpath.py Removed: py/build/winpath.py Modified: py/build/gensetup.py Log: a new cleanup script, and the current state of setup.py generation Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Wed Aug 20 00:54:31 2008 @@ -4,6 +4,7 @@ import py toolpath = py.magic.autopath() +binpath = py.path.local(py.__file__).dirpath('bin') def error(msg): print >>sys.stderr, msg @@ -27,7 +28,8 @@ self.wcstatus = self.wcbasedir.status(rec=True) self.allpaths = [x for x in self.wcstatus.allpath() if x not in self.wcstatus.unknown and - x not in self.wcstatus.external] + x not in self.wcstatus.external and + x not in self.wcstatus.ignored] def append(self, string): lines = string.split("\n") @@ -103,12 +105,12 @@ author_email=%(author_email)r, ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], + scripts = getscripts(), ''' % params) indent = " " * 8 self.append_pprint(indent, py_modules=['_findpy',]), self.append_pprint(indent, long_description=params['long_description']) self.append_pprint(indent, classifiers=self.meta.classifiers) - self.append_pprint(indent, scripts=self.getscripts()) self.append_pprint(indent, packages=self.getpackages()) #self.append_pprint(indent, data_files=self.getdatafiles()) self.append_pprint(indent, package_data=self.getpackagedata()) @@ -118,6 +120,21 @@ self.append_pprint(indent, zip_safe=False) self.lines.append(indent[4:] + ")\n") + def setup_scripts(self): + self.append(""" + def getscripts(): + if sys.platform == "win32": + base = "py/bin/win32/" + ext = ".cmd" + else: + base = "py/bin/" + ext = "" + l = [] + for name in %r: + l.append(base + name + ext) + return l + """ % ([script.basename for script in binpath.listdir("py.*")])) + def append_pprint(self, indent, **kw): for name, value in kw.items(): stringio = py.std.StringIO.StringIO() @@ -131,7 +148,7 @@ self.lines.append(indent + line.rstrip()) self.lines[-1] = self.lines[-1] + "," - def getscripts(self): + def xxxgetscripts(self): bindir = self.wcbasedir.join('py', 'bin') scripts = [] for p in self.allpaths: @@ -180,7 +197,8 @@ def setup_win32(self): self.append(r''' - # on windows we need to hack up the to-be-installed scripts + # if this is an sdist install we might have the unix + # scripts and need to replace them with windows ones. from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): def run(self): @@ -188,19 +206,19 @@ #print self.outfiles for fn in self.outfiles: basename = os.path.basename(fn) - if "." in basename: - #print "tackling", fn - newbasename = basename.replace(".", "_") - newfn = os.path.join(os.path.dirname(fn), newbasename) - if os.path.exists(newfn): - os.remove(newfn) - os.rename(fn, newfn) + if basename.startswith("py.") and not basename.endswith(".cmd"): + os.remove(fn) + # also remove paths from intermediate alpha versions + p = os.path.join(os.path.dirname(fn), basename.replace(".", "_")) + if os.path.exists(p): + p.remove() newname = fn + ".cmd" if os.path.exists(newname): os.remove(newname) f = open(newname, 'w') f.write("@echo off\n") - f.write('python "%%~dp0\%s" %%*\n' % newbasename) + name = "py" + basename[3:] + f.write('python -c "from _findpy import py; py.cmdline.%s()"' % name) f.close() if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} @@ -211,6 +229,7 @@ def write_setup(self): self.setup_header() self.setup_function() + self.setup_scripts() self.setup_win32() self.setup_trailer() targetfile = self.basedir.join("setup.py") Deleted: /py/build/winpath.py ============================================================================== --- /py/build/winpath.py Wed Aug 20 00:54:31 2008 +++ (empty file) @@ -1,73 +0,0 @@ - -# -# some helpers to add the py lib scripts to the -# WIN32 cmdline environment -# -import os, sys - -class Win32PathHandling: - _winreg = None - def __init__(self): - if sys.platform == 'win32': - try: - import _winreg - except ImportError: - print sys.stderr, "huh could not import _winreg on windows, ignoring" - else: - self._winreg = _winreg - - def remove_pylib_path(self): - reg = self._winreg.ConnectRegistry( - None, self._winreg.HKEY_LOCAL_MACHINE) - key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" - path = self.get_registry_value(reg, key, "Path") - newpath = self.prunepath(path) - if newpath != path: - print "PATH contains old py/bin/win32 scripts:", path - print "pruning and setting a new PATH:", newpath - self.set_registry_value(reg, key, "Path", newpath) - # Propagate changes to current command prompt - os.system("set PATH=%s" % path) - self.try_propagate_system() - - def prunepath(self, path): - basename = os.path.basename - dirname = os.path.dirname - l = [] - for p in path.split(';'): - if basename(p) == "win32" and basename(dirname(p)) == "bin" \ - and basename(dirname(dirname(p))) == "py": - continue # prune this path - l.append(p) - return ";".join(l) - - def try_propagate_system(self): - try: - import win32gui, win32con - except ImportError: - return - # Propagate changes throughout the system - win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, - win32con.WM_SETTINGCHANGE, 0, "Environment", - win32con.SMTO_ABORTIFHUNG, 5000) - - def get_registry_value(self, reg, key, value_name): - k = self._winreg.OpenKey(reg, key) - value = self._winreg.QueryValueEx(k, value_name)[0] - self._winreg.CloseKey(k) - return value - - def set_registry_value(self, reg, key, value_name, value): - k = self._winreg.OpenKey(reg, key, 0, self._winreg.KEY_WRITE) - value_type = self._winreg.REG_SZ - # if we handle the Path value, then set its type to REG_EXPAND_SZ - # so that things like %SystemRoot% get automatically expanded by the - # command prompt - if value_name == "Path": - value_type = self._winreg.REG_EXPAND_SZ - self._winreg.SetValueEx(k, value_name, 0, value_type, value) - self._winreg.CloseKey(k) - -if __name__ == "__main__": - w32path = Win32PathHandling() - w32path.remove_pylib_path() Copied: py/build/winpathclean.py (from r57462, py/build/winpath.py) ============================================================================== --- py/build/winpath.py (original) +++ py/build/winpathclean.py Wed Aug 20 00:54:31 2008 @@ -1,11 +1,12 @@ # -# some helpers to add the py lib scripts to the -# WIN32 cmdline environment # import os, sys +import py +from distutils import sysconfig class Win32PathHandling: + """ a helper to remove things added to system PATHs in previous installations. """ _winreg = None def __init__(self): if sys.platform == 'win32': @@ -71,3 +72,19 @@ if __name__ == "__main__": w32path = Win32PathHandling() w32path.remove_pylib_path() + sitepackages = py.path.local(sysconfig.get_python_lib()) + scripts = sitepackages.dirpath().dirpath().join("scripts") + if not scripts.check(): + print "could not find script dir in", scripts + raise SystemExit, 1 + for name in ("py.test", "py.lookup", "py.cleanup", "py.rest", + "py.which", "py.countloc", '_update_website.py', + '_makepyrelease.py', 'pytest.cmd',): + p = scripts.join(name) + if p.check(): + print "removing", p + p.remove() + p = scripts.join(name.replace("_")) + if p.check(): + print "removing", p + p.remove() From hpk at codespeak.net Wed Aug 20 00:56:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 00:56:28 +0200 (CEST) Subject: [py-svn] r57487 - py/build Message-ID: <20080819225628.B8487169F98@codespeak.net> Author: hpk Date: Wed Aug 20 00:56:27 2008 New Revision: 57487 Modified: py/build/winpathclean.py Log: fixing bug Modified: py/build/winpathclean.py ============================================================================== --- py/build/winpathclean.py (original) +++ py/build/winpathclean.py Wed Aug 20 00:56:27 2008 @@ -84,7 +84,7 @@ if p.check(): print "removing", p p.remove() - p = scripts.join(name.replace("_")) + p = scripts.join(name.replace(".", "_")) if p.check(): print "removing", p p.remove() From hpk at codespeak.net Wed Aug 20 00:58:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 00:58:30 +0200 (CEST) Subject: [py-svn] r57488 - py/build Message-ID: <20080819225830.0CE3C169F98@codespeak.net> Author: hpk Date: Wed Aug 20 00:58:29 2008 New Revision: 57488 Modified: py/build/gensetup.py Log: fix another bug Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Wed Aug 20 00:58:29 2008 @@ -211,7 +211,7 @@ # also remove paths from intermediate alpha versions p = os.path.join(os.path.dirname(fn), basename.replace(".", "_")) if os.path.exists(p): - p.remove() + os.remove(p) newname = fn + ".cmd" if os.path.exists(newname): os.remove(newname) From hpk at codespeak.net Wed Aug 20 01:00:38 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 01:00:38 +0200 (CEST) Subject: [py-svn] r57489 - in py/release/0.9.x: . py Message-ID: <20080819230038.A3E7A16A020@codespeak.net> Author: hpk Date: Wed Aug 20 01:00:35 2008 New Revision: 57489 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: aiming for "final" regen today. Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 01:00:35 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2a10" +version = "0.9.2a11" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 01:00:35 2008 @@ -1,7 +1,7 @@ """ setup file for 'py' package based on: - https://codespeak.net/svn/py/release/0.9.x, revision=57387 + https://codespeak.net/svn/py/release/0.9.x, revision=57488 autogenerated by gensetup.py """ @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2a10', + version='0.9.2a11', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -220,7 +220,7 @@ # also remove paths from intermediate alpha versions p = os.path.join(os.path.dirname(fn), basename.replace(".", "_")) if os.path.exists(p): - p.remove() + os.remove(p) newname = fn + ".cmd" if os.path.exists(newname): os.remove(newname) From hpk at codespeak.net Wed Aug 20 01:11:08 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 01:11:08 +0200 (CEST) Subject: [py-svn] r57490 - in py/release/0.9.x: . py Message-ID: <20080819231108.40E85169F6A@codespeak.net> Author: hpk Date: Wed Aug 20 01:11:07 2008 New Revision: 57490 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: generating a beta. Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 01:11:07 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2a11" +version = "0.9.2b1" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 01:11:07 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2a11', + version='0.9.2b1', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], From hpk at codespeak.net Wed Aug 20 14:15:40 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 14:15:40 +0200 (CEST) Subject: [py-svn] r57499 - in py/release/0.9.x/py/bin: . win32 Message-ID: <20080820121540.E0C4E16A08C@codespeak.net> Author: hpk Date: Wed Aug 20 14:15:39 2008 New Revision: 57499 Modified: py/release/0.9.x/py/bin/_genscripts.py py/release/0.9.x/py/bin/win32/py.cleanup.cmd py/release/0.9.x/py/bin/win32/py.countloc.cmd py/release/0.9.x/py/bin/win32/py.lookup.cmd py/release/0.9.x/py/bin/win32/py.rest.cmd py/release/0.9.x/py/bin/win32/py.test.cmd Log: these win32 scripts are not supposed to be installed via setup.py but be used directly (maybe doesn'?T make sense, but i am keeping them for now) Modified: py/release/0.9.x/py/bin/_genscripts.py ============================================================================== --- py/release/0.9.x/py/bin/_genscripts.py (original) +++ py/release/0.9.x/py/bin/_genscripts.py Wed Aug 20 14:15:39 2008 @@ -18,12 +18,12 @@ def genscript_windows(name): basename = getbasename(name) - basename += ".cmd" - path = mydir.join("win32").join(basename) + winbasename = basename + ".cmd" + path = mydir.join("win32").join(winbasename) path.write(py.code.Source(""" @echo off - python -c "from _findpy import py ; py.cmdline.%s()" - """ % (name)).strip()) + python %%dp0\..\%s %%* + """ % (basename)).strip()) if __name__ == "__main__": for name in dir(py.cmdline): Modified: py/release/0.9.x/py/bin/win32/py.cleanup.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.cleanup.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.cleanup.cmd Wed Aug 20 14:15:39 2008 @@ -1,2 +1,2 @@ @echo off -python -c "from _findpy import py ; py.cmdline.pycleanup()" \ No newline at end of file +python %dp0\..\py.cleanup %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.countloc.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.countloc.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.countloc.cmd Wed Aug 20 14:15:39 2008 @@ -1,2 +1,2 @@ @echo off -python -c "from _findpy import py ; py.cmdline.pycountloc()" \ No newline at end of file +python %dp0\..\py.countloc %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.lookup.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.lookup.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.lookup.cmd Wed Aug 20 14:15:39 2008 @@ -1,2 +1,2 @@ @echo off -python -c "from _findpy import py ; py.cmdline.pylookup()" \ No newline at end of file +python %dp0\..\py.lookup %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.rest.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.rest.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.rest.cmd Wed Aug 20 14:15:39 2008 @@ -1,2 +1,2 @@ @echo off -python -c "from _findpy import py ; py.cmdline.pyrest()" \ No newline at end of file +python %dp0\..\py.rest %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.test.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.test.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.test.cmd Wed Aug 20 14:15:39 2008 @@ -1,2 +1,2 @@ @echo off -python -c "from _findpy import py ; py.cmdline.pytest()" \ No newline at end of file +python %dp0\..\py.test %* \ No newline at end of file From hpk at codespeak.net Wed Aug 20 14:28:10 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 14:28:10 +0200 (CEST) Subject: [py-svn] r57500 - in py/release/0.9.x: . py Message-ID: <20080820122810.09E25169FCB@codespeak.net> Author: hpk Date: Wed Aug 20 14:28:10 2008 New Revision: 57500 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: actually changing version and committing setup.py for the hopefully final approach to get things to work. Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 14:28:10 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2b1" +version = "0.9.2b2" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 14:28:10 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2b1', + version='0.9.2b2', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -26,9 +26,13 @@ author_email='holger at merlinux.eu, py-dev at codespeak.net', ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], - scripts = getscripts(), py_modules=['_findpy'], + scripts=['py/bin/py.cleanup', + 'py/bin/py.countloc', + 'py/bin/py.lookup', + 'py/bin/py.rest', + 'py/bin/py.test'], long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', classifiers=['Development Status :: 3 - Alpha', 'Intended Audience :: Developers', @@ -194,20 +198,8 @@ zip_safe=False, ) -def getscripts(): - if sys.platform == "win32": - base = "py/bin/win32/" - ext = ".cmd" - else: - base = "py/bin/" - ext = "" - l = [] - for name in ['py.rest', 'py.countloc', 'py.lookup', 'py.test', 'py.cleanup']: - l.append(base + name + ext) - return l - -# if this is an sdist install we might have the unix -# scripts and need to replace them with windows ones. +# scripts for windows: turn "py.SCRIPT" into "py_SCRIPT" and create +# "py.SCRIPT.cmd" files invoking "py_SCRIPT" from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): def run(self): @@ -216,18 +208,17 @@ for fn in self.outfiles: basename = os.path.basename(fn) if basename.startswith("py.") and not basename.endswith(".cmd"): - os.remove(fn) - # also remove paths from intermediate alpha versions - p = os.path.join(os.path.dirname(fn), basename.replace(".", "_")) - if os.path.exists(p): - os.remove(p) - newname = fn + ".cmd" - if os.path.exists(newname): - os.remove(newname) - f = open(newname, 'w') + newbasename = basename.replace(".", "_") + newfn = os.path.join(os.path.dirname(fn), newbasename) + if os.path.exists(newfn): + os.remove(newfn) + os.rename(fn, newfn) + fncmd = fn + ".cmd" + if os.path.exists(fncmd): + os.remove(fncmd) + f = open(fncmd, 'w') f.write("@echo off\n") - name = "py" + basename[3:] - f.write('python -c "from _findpy import py; py.cmdline.%s()"' % name) + f.write('python %%dp0\%s %%*' %(newbasename)) f.close() if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Wed Aug 20 14:39:05 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 14:39:05 +0200 (CEST) Subject: [py-svn] r57501 - in py/release/0.9.x: . py Message-ID: <20080820123905.833E216A0AA@codespeak.net> Author: hpk Date: Wed Aug 20 14:39:03 2008 New Revision: 57501 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: another trysigh. Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 14:39:03 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2b2" +version = "0.9.2b3" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 14:39:03 2008 @@ -18,7 +18,7 @@ setup(cmdclass=cmdclass, name='py', description='py lib: agile development and test support library', - version='0.9.2b2', + version='0.9.2b3', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -218,7 +218,7 @@ os.remove(fncmd) f = open(fncmd, 'w') f.write("@echo off\n") - f.write('python %%dp0\%s %%*' %(newbasename)) + f.write('python "%%~dp0\%s" %%*' %(newbasename)) f.close() if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} From hpk at codespeak.net Wed Aug 20 14:39:14 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 14:39:14 +0200 (CEST) Subject: [py-svn] r57502 - py/build Message-ID: <20080820123914.7151416A0AA@codespeak.net> Author: hpk Date: Wed Aug 20 14:39:13 2008 New Revision: 57502 Modified: py/build/gensetup.py py/build/makebdistegg.py py/build/winpathclean.py Log: snapshot of build scripts. Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Wed Aug 20 14:39:13 2008 @@ -105,10 +105,10 @@ author_email=%(author_email)r, ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], - scripts = getscripts(), ''' % params) indent = " " * 8 self.append_pprint(indent, py_modules=['_findpy',]), + self.append_pprint(indent, scripts=self.getscripts()) self.append_pprint(indent, long_description=params['long_description']) self.append_pprint(indent, classifiers=self.meta.classifiers) self.append_pprint(indent, packages=self.getpackages()) @@ -121,6 +121,8 @@ self.lines.append(indent[4:] + ")\n") def setup_scripts(self): + # XXX this was used for a different approach + not used self.append(""" def getscripts(): if sys.platform == "win32": @@ -148,7 +150,7 @@ self.lines.append(indent + line.rstrip()) self.lines[-1] = self.lines[-1] + "," - def xxxgetscripts(self): + def getscripts(self): bindir = self.wcbasedir.join('py', 'bin') scripts = [] for p in self.allpaths: @@ -183,7 +185,7 @@ datafiles.append(p.relto(self.wcbasedir)) return datafiles - def xxxsetup_win32(self): + def setup_win32(self): import winpath self.append(py.std.inspect.getsource(winpath)) self.append(""" @@ -197,8 +199,8 @@ def setup_win32(self): self.append(r''' - # if this is an sdist install we might have the unix - # scripts and need to replace them with windows ones. + # scripts for windows: turn "py.SCRIPT" into "py_SCRIPT" and create + # "py.SCRIPT.cmd" files invoking "py_SCRIPT" from distutils.command.install_scripts import install_scripts class my_install_scripts(install_scripts): def run(self): @@ -207,18 +209,17 @@ for fn in self.outfiles: basename = os.path.basename(fn) if basename.startswith("py.") and not basename.endswith(".cmd"): - os.remove(fn) - # also remove paths from intermediate alpha versions - p = os.path.join(os.path.dirname(fn), basename.replace(".", "_")) - if os.path.exists(p): - os.remove(p) - newname = fn + ".cmd" - if os.path.exists(newname): - os.remove(newname) - f = open(newname, 'w') + newbasename = basename.replace(".", "_") + newfn = os.path.join(os.path.dirname(fn), newbasename) + if os.path.exists(newfn): + os.remove(newfn) + os.rename(fn, newfn) + fncmd = fn + ".cmd" + if os.path.exists(fncmd): + os.remove(fncmd) + f = open(fncmd, 'w') f.write("@echo off\n") - name = "py" + basename[3:] - f.write('python -c "from _findpy import py; py.cmdline.%s()"' % name) + f.write('python "%%~dp0\%s" %%*' %(newbasename)) f.close() if sys.platform == "win32": cmdclass = {'install_scripts': my_install_scripts} @@ -229,7 +230,7 @@ def write_setup(self): self.setup_header() self.setup_function() - self.setup_scripts() + #self.setup_scripts() self.setup_win32() self.setup_trailer() targetfile = self.basedir.join("setup.py") Modified: py/build/makebdistegg.py ============================================================================== --- py/build/makebdistegg.py (original) +++ py/build/makebdistegg.py Wed Aug 20 14:39:13 2008 @@ -63,7 +63,7 @@ log = channel.receive() logpath = py.path.local("bdist_egg_%s.log" % python) logpath.write(log) - trace("received result in", logpath) + trace("received log file in", logpath) def remote_getdist(): channel = winexec(r""" @@ -79,7 +79,7 @@ break print "receiving", basename content = channel.receive() - py.path.local(basename).write(content) + py.path.local("dist").ensure(basename).write(content) print "complete" def winexec(source): Modified: py/build/winpathclean.py ============================================================================== --- py/build/winpathclean.py (original) +++ py/build/winpathclean.py Wed Aug 20 14:39:13 2008 @@ -69,9 +69,7 @@ self._winreg.SetValueEx(k, value_name, 0, value_type, value) self._winreg.CloseKey(k) -if __name__ == "__main__": - w32path = Win32PathHandling() - w32path.remove_pylib_path() +def remove_scripts(): sitepackages = py.path.local(sysconfig.get_python_lib()) scripts = sitepackages.dirpath().dirpath().join("scripts") if not scripts.check(): @@ -79,12 +77,17 @@ raise SystemExit, 1 for name in ("py.test", "py.lookup", "py.cleanup", "py.rest", "py.which", "py.countloc", '_update_website.py', - '_makepyrelease.py', 'pytest.cmd',): + '_makepyrelease.py', '_findpy.py', 'pytest.cmd',): p = scripts.join(name) if p.check(): print "removing", p p.remove() - p = scripts.join(name.replace(".", "_")) - if p.check(): - print "removing", p - p.remove() + #p = scripts.join(name.replace(".", "_")) + #if p.check(): + # print "removing", p + # p.remove() + +if __name__ == "__main__": + w32path = Win32PathHandling() + w32path.remove_pylib_path() + remove_scripts() From hpk at codespeak.net Wed Aug 20 15:10:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 15:10:00 +0200 (CEST) Subject: [py-svn] r57504 - in py/release/0.9.x: . py py/bin py/bin/win32 Message-ID: <20080820131000.765BB16A0BC@codespeak.net> Author: hpk Date: Wed Aug 20 15:09:58 2008 New Revision: 57504 Removed: py/release/0.9.x/_findpy.py Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/py/bin/_genscripts.py py/release/0.9.x/py/bin/py.cleanup py/release/0.9.x/py/bin/py.countloc py/release/0.9.x/py/bin/py.lookup py/release/0.9.x/py/bin/py.rest py/release/0.9.x/py/bin/py.test py/release/0.9.x/py/bin/win32/py.cleanup.cmd py/release/0.9.x/py/bin/win32/py.countloc.cmd py/release/0.9.x/py/bin/win32/py.lookup.cmd py/release/0.9.x/py/bin/win32/py.rest.cmd py/release/0.9.x/py/bin/win32/py.test.cmd py/release/0.9.x/setup.py Log: going for setuptools only now. Deleted: /py/release/0.9.x/_findpy.py ============================================================================== --- /py/release/0.9.x/_findpy.py Wed Aug 20 15:09:58 2008 +++ (empty file) @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# -# find and import a version of 'py' -# -import sys -import os -from os.path import dirname as opd, exists, join, basename, abspath - -def searchpy(current): - while 1: - last = current - initpy = join(current, '__init__.py') - if not exists(initpy): - pydir = join(current, 'py') - # recognize py-package and ensure it is importable - if exists(pydir) and exists(join(pydir, '__init__.py')): - #for p in sys.path: - # if p == current: - # return True - if current != sys.path[0]: # if we are already first, then ok - print >>sys.stderr, "inserting into sys.path:", current - sys.path.insert(0, current) - return True - current = opd(current) - if last == current: - return False - -if not searchpy(abspath(os.curdir)): - if not searchpy(opd(abspath(sys.argv[0]))): - if not searchpy(opd(__file__)): - pass # let's hope it is just on sys.path - -import py - -if __name__ == '__main__': - print "py lib is at", py.__file__ Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 15:09:58 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2b3" +version = "0.9.2b4" initpkg(__name__, description = "py lib: agile development and test support library", @@ -22,7 +22,7 @@ author_email = "holger at merlinux.eu, py-dev at codespeak.net", long_description = globals()['__doc__'], classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", Modified: py/release/0.9.x/py/bin/_genscripts.py ============================================================================== --- py/release/0.9.x/py/bin/_genscripts.py (original) +++ py/release/0.9.x/py/bin/_genscripts.py Wed Aug 20 15:09:58 2008 @@ -11,7 +11,7 @@ path = mydir.join(basename) path.write(py.code.Source(""" #!/usr/bin/env python - from _findpy import py + from _findpy import py py.cmdline.%s() """ % name).strip()) path.chmod(0755) @@ -22,7 +22,7 @@ path = mydir.join("win32").join(winbasename) path.write(py.code.Source(""" @echo off - python %%dp0\..\%s %%* + python "%%~dp0\..\%s" %%* """ % (basename)).strip()) if __name__ == "__main__": Modified: py/release/0.9.x/py/bin/py.cleanup ============================================================================== --- py/release/0.9.x/py/bin/py.cleanup (original) +++ py/release/0.9.x/py/bin/py.cleanup Wed Aug 20 15:09:58 2008 @@ -1,3 +1,3 @@ #!/usr/bin/env python -from _findpy import py +from _findpy import py py.cmdline.pycleanup() \ No newline at end of file Modified: py/release/0.9.x/py/bin/py.countloc ============================================================================== --- py/release/0.9.x/py/bin/py.countloc (original) +++ py/release/0.9.x/py/bin/py.countloc Wed Aug 20 15:09:58 2008 @@ -1,3 +1,3 @@ #!/usr/bin/env python -from _findpy import py +from _findpy import py py.cmdline.pycountloc() \ No newline at end of file Modified: py/release/0.9.x/py/bin/py.lookup ============================================================================== --- py/release/0.9.x/py/bin/py.lookup (original) +++ py/release/0.9.x/py/bin/py.lookup Wed Aug 20 15:09:58 2008 @@ -1,3 +1,3 @@ #!/usr/bin/env python -from _findpy import py +from _findpy import py py.cmdline.pylookup() \ No newline at end of file Modified: py/release/0.9.x/py/bin/py.rest ============================================================================== --- py/release/0.9.x/py/bin/py.rest (original) +++ py/release/0.9.x/py/bin/py.rest Wed Aug 20 15:09:58 2008 @@ -1,3 +1,3 @@ #!/usr/bin/env python -from _findpy import py +from _findpy import py py.cmdline.pyrest() \ No newline at end of file Modified: py/release/0.9.x/py/bin/py.test ============================================================================== --- py/release/0.9.x/py/bin/py.test (original) +++ py/release/0.9.x/py/bin/py.test Wed Aug 20 15:09:58 2008 @@ -1,3 +1,3 @@ #!/usr/bin/env python -from _findpy import py +from _findpy import py py.cmdline.pytest() \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.cleanup.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.cleanup.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.cleanup.cmd Wed Aug 20 15:09:58 2008 @@ -1,2 +1,2 @@ @echo off -python %dp0\..\py.cleanup %* \ No newline at end of file +python "%~dp0\..\py.cleanup" %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.countloc.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.countloc.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.countloc.cmd Wed Aug 20 15:09:58 2008 @@ -1,2 +1,2 @@ @echo off -python %dp0\..\py.countloc %* \ No newline at end of file +python "%~dp0\..\py.countloc" %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.lookup.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.lookup.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.lookup.cmd Wed Aug 20 15:09:58 2008 @@ -1,2 +1,2 @@ @echo off -python %dp0\..\py.lookup %* \ No newline at end of file +python "%~dp0\..\py.lookup" %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.rest.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.rest.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.rest.cmd Wed Aug 20 15:09:58 2008 @@ -1,2 +1,2 @@ @echo off -python %dp0\..\py.rest %* \ No newline at end of file +python "%~dp0\..\py.rest" %* \ No newline at end of file Modified: py/release/0.9.x/py/bin/win32/py.test.cmd ============================================================================== --- py/release/0.9.x/py/bin/win32/py.test.cmd (original) +++ py/release/0.9.x/py/bin/win32/py.test.cmd Wed Aug 20 15:09:58 2008 @@ -1,2 +1,2 @@ @echo off -python %dp0\..\py.test %* \ No newline at end of file +python "%~dp0\..\py.test" %* \ No newline at end of file Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 15:09:58 2008 @@ -7,18 +7,15 @@ """ import os, sys -if 1: # set to zero if you want plain distutils - import ez_setup - ez_setup.use_setuptools() - from setuptools import setup, Extension -else: - from distutils.core import setup, Extension +import ez_setup +ez_setup.use_setuptools() +from setuptools import setup, Extension def main(): - setup(cmdclass=cmdclass, + setup( name='py', description='py lib: agile development and test support library', - version='0.9.2b3', + version='0.9.2b4', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -27,14 +24,13 @@ ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], - py_modules=['_findpy'], - scripts=['py/bin/py.cleanup', - 'py/bin/py.countloc', - 'py/bin/py.lookup', - 'py/bin/py.rest', - 'py/bin/py.test'], + entry_points={'console_scripts': ['py.cleanup = py.cmdline.pycleanup:main', + 'py.countloc = py.cmdline.pycountloc:main', + 'py.lookup = py.cmdline.pylookup:main', + 'py.rest = py.cmdline.pyrest:main', + 'py.test = py.cmdline.pytest:main']}, long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', - classifiers=['Development Status :: 3 - Alpha', + classifiers=['Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: POSIX', @@ -198,33 +194,6 @@ zip_safe=False, ) -# scripts for windows: turn "py.SCRIPT" into "py_SCRIPT" and create -# "py.SCRIPT.cmd" files invoking "py_SCRIPT" -from distutils.command.install_scripts import install_scripts -class my_install_scripts(install_scripts): - def run(self): - install_scripts.run(self) - #print self.outfiles - for fn in self.outfiles: - basename = os.path.basename(fn) - if basename.startswith("py.") and not basename.endswith(".cmd"): - newbasename = basename.replace(".", "_") - newfn = os.path.join(os.path.dirname(fn), newbasename) - if os.path.exists(newfn): - os.remove(newfn) - os.rename(fn, newfn) - fncmd = fn + ".cmd" - if os.path.exists(fncmd): - os.remove(fncmd) - f = open(fncmd, 'w') - f.write("@echo off\n") - f.write('python "%%~dp0\%s" %%*' %(newbasename)) - f.close() -if sys.platform == "win32": - cmdclass = {'install_scripts': my_install_scripts} -else: - cmdclass = {} - if __name__ == '__main__': main() \ No newline at end of file From hpk at codespeak.net Wed Aug 20 15:12:56 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 15:12:56 +0200 (CEST) Subject: [py-svn] r57505 - py/build Message-ID: <20080820131256.7184316A0BC@codespeak.net> Author: hpk Date: Wed Aug 20 15:12:55 2008 New Revision: 57505 Modified: py/build/gensetup.py py/build/winpathclean.py Log: snapshot of build tools Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Wed Aug 20 15:12:55 2008 @@ -71,12 +71,9 @@ ''' % locals()) self.append(""" - if 1: # set to zero if you want plain distutils - import ez_setup - ez_setup.use_setuptools() - from setuptools import setup, Extension - else: - from distutils.core import setup, Extension + import ez_setup + ez_setup.use_setuptools() + from setuptools import setup, Extension """) def setup_trailer(self): @@ -94,7 +91,7 @@ #print py.std.pprint.pprint(params) self.append(''' def main(): - setup(cmdclass=cmdclass, + setup( name=%(name)r, description=%(description)r, version=%(version)r, @@ -107,8 +104,9 @@ ["py/c-extension/greenlet/greenlet.c"]),], ''' % params) indent = " " * 8 - self.append_pprint(indent, py_modules=['_findpy',]), - self.append_pprint(indent, scripts=self.getscripts()) + #self.append_pprint(indent, py_modules=['_findpy',]), + #self.append_pprint(indent, scripts=self.getscripts()) + self.append_pprint(indent, entry_points={'console_scripts':self.getconsolescripts()}) self.append_pprint(indent, long_description=params['long_description']) self.append_pprint(indent, classifiers=self.meta.classifiers) self.append_pprint(indent, packages=self.getpackages()) @@ -149,6 +147,17 @@ for line in lines: self.lines.append(indent + line.rstrip()) self.lines[-1] = self.lines[-1] + "," + + def getconsolescripts(self): + bindir = self.wcbasedir.join('py', 'bin') + scripts = [] + for p in self.allpaths: + if p.dirpath() == bindir: + if p.basename.startswith('py.'): + shortname = "py" + p.basename[3:] + scripts.append("%s = py.cmdline.%s:main" % + (p.basename, shortname)) + return scripts def getscripts(self): bindir = self.wcbasedir.join('py', 'bin') @@ -231,7 +240,7 @@ self.setup_header() self.setup_function() #self.setup_scripts() - self.setup_win32() + #self.setup_win32() self.setup_trailer() targetfile = self.basedir.join("setup.py") targetfile.write("\n".join(self.lines)) Modified: py/build/winpathclean.py ============================================================================== --- py/build/winpathclean.py (original) +++ py/build/winpathclean.py Wed Aug 20 15:12:55 2008 @@ -82,10 +82,14 @@ if p.check(): print "removing", p p.remove() - #p = scripts.join(name.replace(".", "_")) - #if p.check(): - # print "removing", p - # p.remove() + p = scripts.join(name.replace(".", "_")) + if p.check(): + print "removing", p + p.remove() + p = scripts.join(name + ".cmd") + if p.check(): + print "removing", p + p.remove() if __name__ == "__main__": w32path = Win32PathHandling() From hpk at codespeak.net Wed Aug 20 15:18:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 15:18:28 +0200 (CEST) Subject: [py-svn] r57506 - in py/release/0.9.x: . py Message-ID: <20080820131828.9956F16A046@codespeak.net> Author: hpk Date: Wed Aug 20 15:18:27 2008 New Revision: 57506 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: why think if you do try-and-error? Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Wed Aug 20 15:18:27 2008 @@ -2,7 +2,6 @@ LICENSE MANIFEST README.txt -_findpy.py ez_setup.py py/LICENSE py/__init__.py Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 15:18:27 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2b4" +version = "0.9.2b5" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 15:18:27 2008 @@ -15,7 +15,7 @@ setup( name='py', description='py lib: agile development and test support library', - version='0.9.2b4', + version='0.9.2b5', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -24,11 +24,11 @@ ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], - entry_points={'console_scripts': ['py.cleanup = py.cmdline.pycleanup:main', - 'py.countloc = py.cmdline.pycountloc:main', - 'py.lookup = py.cmdline.pylookup:main', - 'py.rest = py.cmdline.pyrest:main', - 'py.test = py.cmdline.pytest:main']}, + entry_points={'console_scripts': ['py.cleanup = py.cmdline:pycleanup', + 'py.countloc = py.cmdline:pycountloc', + 'py.lookup = py.cmdline:pylookup', + 'py.rest = py.cmdline:pyrest', + 'py.test = py.cmdline:pytest']}, long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', classifiers=['Development Status :: 4 - Beta', 'Intended Audience :: Developers', From hpk at codespeak.net Wed Aug 20 15:59:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 15:59:23 +0200 (CEST) Subject: [py-svn] r57508 - in py/release/0.9.x/py/path: . local/testing Message-ID: <20080820135923.31B4A16A0D0@codespeak.net> Author: hpk Date: Wed Aug 20 15:59:21 2008 New Revision: 57508 Modified: py/release/0.9.x/py/path/common.py py/release/0.9.x/py/path/local/testing/test_local.py py/release/0.9.x/py/path/local/testing/test_win.py Log: * fix relto method to operate with normcased names for windows. * skip test for long filenames under windows, add url of martijns post. Modified: py/release/0.9.x/py/path/common.py ============================================================================== --- py/release/0.9.x/py/path/common.py (original) +++ py/release/0.9.x/py/path/common.py Wed Aug 20 15:59:21 2008 @@ -142,6 +142,9 @@ #assert strrelpath[-1] == self.sep #assert strrelpath[-2] != self.sep strself = str(self) + if sys.platform == "win32": + strself = os.path.normcase(strself) + strrelpath = os.path.normcase(strrelpath) if strself.startswith(strrelpath): return strself[len(strrelpath):] return "" Modified: py/release/0.9.x/py/path/local/testing/test_local.py ============================================================================== --- py/release/0.9.x/py/path/local/testing/test_local.py (original) +++ py/release/0.9.x/py/path/local/testing/test_local.py Wed Aug 20 15:59:21 2008 @@ -1,4 +1,5 @@ import py +import sys from py.path import local from py.__.path.common import checker from py.__.path.testing.fscommon import CommonFSTests, setuptestfs @@ -178,6 +179,10 @@ assert not hasattr(l3, 'commit') def test_long_filenames(self): + if sys.platform == "win32": + py.test.skip("win32: work around needed for path length limit") + # see http://codespeak.net/pipermail/py-dev/2008q2/000922.html + tmpdir = self.tmpdir # testing paths > 260 chars (which is Windows' limitation, but # depending on how the paths are used), but > 4096 (which is the Modified: py/release/0.9.x/py/path/local/testing/test_win.py ============================================================================== --- py/release/0.9.x/py/path/local/testing/test_win.py (original) +++ py/release/0.9.x/py/path/local/testing/test_win.py Wed Aug 20 15:59:21 2008 @@ -29,6 +29,11 @@ t2 = self.root.join("A_path") assert t1 == t1 assert t1 == t2 + + def test_relto_with_mixed_case(self): + t1 = self.root.join("a_path", "file") + t2 = self.root.join("A_path") + assert t1.relto(t2) == "file" def test_allow_unix_style_paths(self): t1 = self.root.join('a_path') From hpk at codespeak.net Wed Aug 20 16:08:40 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 16:08:40 +0200 (CEST) Subject: [py-svn] r57509 - in py/release/0.9.x/py/path: . local/testing Message-ID: <20080820140840.CA18216A0B5@codespeak.net> Author: hpk Date: Wed Aug 20 16:08:38 2008 New Revision: 57509 Modified: py/release/0.9.x/py/path/common.py py/release/0.9.x/py/path/local/testing/test_win.py Log: try to maintain cases of original filename Modified: py/release/0.9.x/py/path/common.py ============================================================================== --- py/release/0.9.x/py/path/common.py (original) +++ py/release/0.9.x/py/path/common.py Wed Aug 20 16:08:38 2008 @@ -143,9 +143,10 @@ #assert strrelpath[-2] != self.sep strself = str(self) if sys.platform == "win32": - strself = os.path.normcase(strself) - strrelpath = os.path.normcase(strrelpath) - if strself.startswith(strrelpath): + if os.path.normcase(strself).startswith( + os.path.normcase(strrelpath)): + return strself[len(strrelpath):] + elif strself.startswith(strrelpath): return strself[len(strrelpath):] return "" Modified: py/release/0.9.x/py/path/local/testing/test_win.py ============================================================================== --- py/release/0.9.x/py/path/local/testing/test_win.py (original) +++ py/release/0.9.x/py/path/local/testing/test_win.py Wed Aug 20 16:08:38 2008 @@ -31,9 +31,9 @@ assert t1 == t2 def test_relto_with_mixed_case(self): - t1 = self.root.join("a_path", "file") + t1 = self.root.join("a_path", "fiLe") t2 = self.root.join("A_path") - assert t1.relto(t2) == "file" + assert t1.relto(t2) == "fiLe" def test_allow_unix_style_paths(self): t1 = self.root.join('a_path') From hpk at codespeak.net Wed Aug 20 16:10:23 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 16:10:23 +0200 (CEST) Subject: [py-svn] r57510 - py/release/0.9.x/py/cmdline Message-ID: <20080820141023.38DB316A0D3@codespeak.net> Author: hpk Date: Wed Aug 20 16:10:21 2008 New Revision: 57510 Modified: py/release/0.9.x/py/cmdline/pyrest.py Log: don't use _findpy anymore here. Modified: py/release/0.9.x/py/cmdline/pyrest.py ============================================================================== --- py/release/0.9.x/py/cmdline/pyrest.py (original) +++ py/release/0.9.x/py/cmdline/pyrest.py Wed Aug 20 16:10:21 2008 @@ -13,7 +13,6 @@ """ import os, sys -from _findpy import py from py.__.misc import rest from py.__.rest import directive from py.__.rest.latex import process_rest_file, process_configfile From hpk at codespeak.net Wed Aug 20 16:18:32 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 16:18:32 +0200 (CEST) Subject: [py-svn] r57511 - py/release/0.9.x/py/cmdline Message-ID: <20080820141832.8005E16A0E8@codespeak.net> Author: hpk Date: Wed Aug 20 16:18:30 2008 New Revision: 57511 Modified: py/release/0.9.x/py/cmdline/pyrest.py Log: if docutils is not present, fail a bit more cleanly. Modified: py/release/0.9.x/py/cmdline/pyrest.py ============================================================================== --- py/release/0.9.x/py/cmdline/pyrest.py (original) +++ py/release/0.9.x/py/cmdline/pyrest.py Wed Aug 20 16:18:30 2008 @@ -13,10 +13,7 @@ """ import os, sys -from py.__.misc import rest -from py.__.rest import directive -from py.__.rest.latex import process_rest_file, process_configfile - +import py if hasattr(sys.stdout, 'fileno') and os.isatty(sys.stdout.fileno()): def log(msg): @@ -38,6 +35,14 @@ def main(): + try: + from py.__.misc import rest + from py.__.rest import directive + from py.__.rest.latex import process_rest_file, process_configfile + except ImportError, e: + print str(e) + sys.exit(1) + (options, args) = parser.parse_args() if len(args) == 0: From hpk at codespeak.net Wed Aug 20 16:19:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 16:19:35 +0200 (CEST) Subject: [py-svn] r57512 - py/release/0.9.x/py/cmdline/testing Message-ID: <20080820141935.706F116A0E9@codespeak.net> Author: hpk Date: Wed Aug 20 16:19:34 2008 New Revision: 57512 Added: py/release/0.9.x/py/cmdline/testing/__init__.py Log: add missing __init__ file. Added: py/release/0.9.x/py/cmdline/testing/__init__.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/cmdline/testing/__init__.py Wed Aug 20 16:19:34 2008 @@ -0,0 +1 @@ +# From hpk at codespeak.net Wed Aug 20 16:31:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 16:31:21 +0200 (CEST) Subject: [py-svn] r57515 - py/release/0.9.x/py/cmdline/testing Message-ID: <20080820143121.031FF16A0CE@codespeak.net> Author: hpk Date: Wed Aug 20 16:31:21 2008 New Revision: 57515 Modified: py/release/0.9.x/py/cmdline/testing/test_generic.py Log: fix test for missing docutils Modified: py/release/0.9.x/py/cmdline/testing/test_generic.py ============================================================================== --- py/release/0.9.x/py/cmdline/testing/test_generic.py (original) +++ py/release/0.9.x/py/cmdline/testing/test_generic.py Wed Aug 20 16:31:21 2008 @@ -20,11 +20,22 @@ assert script.check() old = tmpdir.ensure(script.basename, dir=1).chdir() try: - cmd = "%s" %(script, ) + if iswin32: + cmd = script.basename + else: + cmd = "%s" %(script, ) + if script.basename.startswith("py.lookup"): cmd += " hello" print "executing", script - py.process.cmdexec(cmd) + try: + py.process.cmdexec(cmd) + except py.process.cmdexec.Error, e: + if cmd.find("py.rest") != -1 and \ + e.out.find("module named") != -1: + return + raise + finally: old.chdir() From hpk at codespeak.net Wed Aug 20 17:02:28 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 17:02:28 +0200 (CEST) Subject: [py-svn] r57517 - in py/release/0.9.x: . py/execnet Message-ID: <20080820150228.B355616A0EF@codespeak.net> Author: hpk Date: Wed Aug 20 17:02:27 2008 New Revision: 57517 Modified: py/release/0.9.x/CHANGELOG py/release/0.9.x/py/execnet/inputoutput.py py/release/0.9.x/py/execnet/register.py Log: do binary mode settings again in the PopenIO __init__. fixes execnet for my windows installation. but i am afraid there still are issues. Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Wed Aug 20 17:02:27 2008 @@ -11,9 +11,6 @@ windows environments, they are now added to the Scripts directory as ".cmd" files. -* improved py.execnet bootstrapping on windows - (thanks Matthew Edwards, Baptiste Lepille) - * py.path.svnwc.status() now is more complete and uses xml output from the 'svn' command if available (Guido Wesdorp) Modified: py/release/0.9.x/py/execnet/inputoutput.py ============================================================================== --- py/release/0.9.x/py/execnet/inputoutput.py (original) +++ py/release/0.9.x/py/execnet/inputoutput.py Wed Aug 20 17:02:27 2008 @@ -59,10 +59,6 @@ class Popen2IO: server_stmt = """ import os, sys, StringIO -if sys.platform == "win32": - import msvcrt - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) - msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) io = Popen2IO(sys.stdout, sys.stdin) sys.stdout = sys.stderr = StringIO.StringIO() #try: @@ -75,6 +71,11 @@ def __init__(self, infile, outfile): self.outfile, self.infile = infile, outfile + if sys.platform == "win32": + import msvcrt + msvcrt.setmode(infile.fileno(), os.O_BINARY) + msvcrt.setmode(outfile.fileno(), os.O_BINARY) + self.readable = self.writeable = True self.lock = thread.allocate_lock() Modified: py/release/0.9.x/py/execnet/register.py ============================================================================== --- py/release/0.9.x/py/execnet/register.py (original) +++ py/release/0.9.x/py/execnet/register.py Wed Aug 20 17:02:27 2008 @@ -2,8 +2,12 @@ import os, inspect, socket import sys from py.magic import autopath ; mypath = autopath() - import py +if sys.platform == "win32": + win32 = True + import msvcrt +else: + win32 = False # the list of modules that must be send to the other side # for bootstrapping gateways @@ -51,13 +55,10 @@ class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): infile, outfile = os.popen2(cmd) - if sys.platform == 'win32': - import msvcrt - msvcrt.setmode(infile.fileno(), os.O_BINARY) - msvcrt.setmode(outfile.fileno(), os.O_BINARY) io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) + class PopenGateway(PopenCmdGateway): """ This Gateway provides interaction with a newly started python subprocess. From hpk at codespeak.net Wed Aug 20 17:13:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 17:13:37 +0200 (CEST) Subject: [py-svn] r57518 - in py/release/0.9.x: . py Message-ID: <20080820151337.AAE7516841E@codespeak.net> Author: hpk Date: Wed Aug 20 17:13:36 2008 New Revision: 57518 Modified: py/release/0.9.x/CHANGELOG py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: version bump, regen Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Wed Aug 20 17:13:36 2008 @@ -18,6 +18,9 @@ * fix for py.path.svn* to work with svn 1.5 (Chris Lamb) +* fix path.relto(otherpath) method on windows to + use normcase for checking if a path is relative. + * py.test's traceback is better parseable from editors (follows the filenames:LINENO: MSG convention) (thanks to Osmo Salomaa) Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Wed Aug 20 17:13:36 2008 @@ -109,6 +109,7 @@ py/cmdline/pylookup.py py/cmdline/pyrest.py py/cmdline/pytest.py +py/cmdline/testing/__init__.py py/cmdline/testing/test_generic.py py/code/__init__.py py/code/code.py Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 17:13:36 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2b5" +version = "0.9.2b6" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 17:13:36 2008 @@ -1,7 +1,7 @@ """ setup file for 'py' package based on: - https://codespeak.net/svn/py/release/0.9.x, revision=57488 + https://codespeak.net/svn/py/release/0.9.x, revision=57517 autogenerated by gensetup.py """ @@ -15,7 +15,7 @@ setup( name='py', description='py lib: agile development and test support library', - version='0.9.2b5', + version='0.9.2b6', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -56,6 +56,7 @@ 'py.builtin', 'py.builtin.testing', 'py.cmdline', + 'py.cmdline.testing', 'py.code', 'py.code.testing', 'py.compat', @@ -136,7 +137,6 @@ 'c-extension/greenlet/test_greenlet.py', 'c-extension/greenlet/test_remote.py', 'c-extension/greenlet/test_throw.py', - 'cmdline/testing/test_generic.py', 'compat/LICENSE', 'compat/testing/test_doctest.txt', 'compat/testing/test_doctest2.txt', From hpk at codespeak.net Wed Aug 20 18:27:42 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 18:27:42 +0200 (CEST) Subject: [py-svn] r57522 - py/release/0.9.x/py/doc Message-ID: <20080820162742.DDE5516A07F@codespeak.net> Author: hpk Date: Wed Aug 20 18:27:39 2008 New Revision: 57522 Modified: py/release/0.9.x/py/doc/download.txt py/release/0.9.x/py/doc/index.txt py/release/0.9.x/py/doc/release-0.9.2.txt Log: 0.9.2 updates to the entry-level docs Modified: py/release/0.9.x/py/doc/download.txt ============================================================================== --- py/release/0.9.x/py/doc/download.txt (original) +++ py/release/0.9.x/py/doc/download.txt Wed Aug 20 18:27:39 2008 @@ -1,104 +1,65 @@ -Download and Installation of the py lib -=============================================== -.. contents:: -.. sectnum:: -Downloading a tar/zip file and installing it +"easy_install py" =================================================== -The latest public release: +With a working `setuptools installation`_ you can install from the command line:: - `download py-0.9.1.tar.gz`_ - `download py-0.9.1.zip`_ + easy_install py -.. _`download py-0.9.1.tar.gz`: http://codespeak.net/download/py/py-0.9.1.tar.gz -.. _`download py-0.9.1.zip`: http://codespeak.net/download/py/py-0.9.1.zip +to get the latest release of the py lib. On non-Windows systems you will +need a working C-compiler in order to successfully complete this step. +The py lib and its tools are expected to work well on Linux, Windows +and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. -The py lib can be `globally installed via setup.py`_ -or `used locally`_. +.. _`setuptools installation`: http://pypi.python.org/pypi/setuptools -WARNING: win32 there is no pre-packaged c-extension -module (greenlet) yet and thus greenlets will not work -out of the box. - -Getting (and updating) via subversion --------------------------------------------- - -Use Subversion to checkout the latest 0.9.x stable release: - - svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x - -to obtain the complete code and documentation source. - -If you experience problems with the subversion checkout e.g. -because you have a http-proxy in between that doesn't proxy -DAV requests you can try to use "codespeak.net:8080" instead -of just "codespeak.net". Alternatively, you may tweak -your local subversion installation. - -If you want to follow stable snapshots -then you may use the equivalent of this invocation: +Downloading a tar/zip archive and installing that +=================================================== - svn co http://codespeak.net/svn/py/dist py-dist +You may also download one of these archives: + `download py-0.9.2b6.tar.gz`_ -.. _`globally installed via setup.py`: + `download py-0.9.2b6.zip`_ -Installation via setup.py ------------------------------- +.. _`download py-0.9.2b6.tar.gz`: http://pypi.python.org/packages/source/p/py/py-0.9.2b6.tar.gz +.. _`download py-0.9.2b6.zip`: http://pypi.python.org/packages/source/p/py/py-0.9.2b6.zip -Go to your unpacked/checked out directory -and issue: +and unpack it to a directory, where you then type:: python setup.py install +You will need a working C-compiler in order to comlete this step successfully. -.. _`used locally`: -Local Installation/Usage ------------------------------- +Installing from subversion / develop mode +============================================ -You need to put the checkout-directory into your ``PYTHONPATH`` -and you want to have the ``py-dist/py/bin/py.test`` script in -your (unixish) system path, which lets you execute test files -and directories. +To follow development or help with fixing things +for the next release, checkout the complete code +and documentation source:: -There is a convenient way for Bash/Shell based systems -to setup the ``PYTHONPATH`` as well as the shell ``PATH``, insert:: - - eval `python ~/path/to/py-dist/py/env.py` - -into your ``.bash_profile``. Of course, you need to -specify your own checkout-directory. - - -.. _`svn-external scenario`: - -The py lib as an svn external -------------------------------------------------------- - -Add the py lib as an external to your project `DIRECTORY` -which contains your svn-controlled root package:: + svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x - svn propedit 'svn:externals' DIRECTORY +You should then be able to issue:: -which will open an editor where you can add -the following line: + python setup.py develop - py http://codespeak.net/svn/py/dist +and work with the local version. -This will make your projcet automatically use the -most recent stable snapshot of the py lib. +If this doesn't work for you then you can also +source (linux) or execute (windows) some scripts +that set environment variables for using your checkout. +You can execute: -Alternatively you may use this url for -integrating the development version: + python ~/path/to/checkout/py/env.py - http://codespeak.net/svn/py/trunk +or on windows: -or the next one for following the e.g. the 0.9 release branch + \\path\\to\\checkout\\py\\env.cmd - http://codespeak.net/svn/py/release/0.9.x +to get settings for PYTHONPATH and PATH. py subversion directory structure Modified: py/release/0.9.x/py/doc/index.txt ============================================================================== --- py/release/0.9.x/py/doc/index.txt (original) +++ py/release/0.9.x/py/doc/index.txt Wed Aug 20 18:27:39 2008 @@ -1,13 +1,15 @@ py lib documentation ================================================= - The py lib aims at supporting a decent development process - addressing deployment, versioning, testing and documentation - perspectives. + The py lib is a development support library featuring + py.test, ad-hoc distributed execution, micro-threads + (greenlets) and uniform local path and svn abstractions. + Works on Linux, Windows and OSX, Python versions + 2.3, 2.4, 2.5 and 2.6. `Download and Installation`_ -`0.9.1 release announcement`_ +`0.9.2 release announcement`_ Main tools and API ---------------------- @@ -59,4 +61,4 @@ .. _`Why What how py?`: why_py.html .. _`future`: future.html .. _`miscellaneous features`: misc.html -.. _`0.9.1 release announcement`: release-0.9.1.html +.. _`0.9.2 release announcement`: release-0.9.2.html Modified: py/release/0.9.x/py/doc/release-0.9.2.txt ============================================================================== --- py/release/0.9.x/py/doc/release-0.9.2.txt (original) +++ py/release/0.9.x/py/doc/release-0.9.2.txt Wed Aug 20 18:27:39 2008 @@ -5,7 +5,7 @@ mainly fixing Windows issues, providing better packaging and integration with setuptools. -Summary of main feature of the py lib: +Here is a quick summary of what it provides: * py.test: cross-project testing tool with many advanced features * py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes @@ -17,6 +17,7 @@ See here for more information: Download/Install: http://codespeak.net/py/0.9.2/download.html + Documentation/API: http://codespeak.net/py/0.9.2/index.html best and have fun, From hpk at codespeak.net Wed Aug 20 18:34:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 18:34:30 +0200 (CEST) Subject: [py-svn] r57523 - py/release/0.9.x/py/doc Message-ID: <20080820163430.6B2C116A098@codespeak.net> Author: hpk Date: Wed Aug 20 18:34:29 2008 New Revision: 57523 Modified: py/release/0.9.x/py/doc/download.txt Log: prominently point to winpathclean.py script. Modified: py/release/0.9.x/py/doc/download.txt ============================================================================== --- py/release/0.9.x/py/doc/download.txt (original) +++ py/release/0.9.x/py/doc/download.txt Wed Aug 20 18:34:29 2008 @@ -12,6 +12,13 @@ The py lib and its tools are expected to work well on Linux, Windows and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. +**IMPORTANT NOTE**: if you are using Windows and have previous +installations of the py lib on your system, please download +and execute http://codespeak.net/svn/py/build/winpathclean.py +This will check that no previous files are getting in the way. +(Unfortunately we don't know about a way to execute this +code automatically during the above install). + .. _`setuptools installation`: http://pypi.python.org/pypi/setuptools Downloading a tar/zip archive and installing that From hpk at codespeak.net Wed Aug 20 18:39:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 18:39:26 +0200 (CEST) Subject: [py-svn] r57524 - in py/release/0.9.x: . py Message-ID: <20080820163926.BE23316A0C6@codespeak.net> Author: hpk Date: Wed Aug 20 18:39:18 2008 New Revision: 57524 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: bump version Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Aug 20 18:39:18 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "0.9.2b6" +version = "0.9.2b7" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Wed Aug 20 18:39:18 2008 @@ -15,7 +15,7 @@ setup( name='py', description='py lib: agile development and test support library', - version='0.9.2b6', + version='0.9.2b7', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], From hpk at codespeak.net Wed Aug 20 18:48:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 Aug 2008 18:48:21 +0200 (CEST) Subject: [py-svn] r57525 - py/release/0.9.x/py/doc Message-ID: <20080820164821.9B51116A0E1@codespeak.net> Author: hpk Date: Wed Aug 20 18:48:19 2008 New Revision: 57525 Modified: py/release/0.9.x/py/doc/download.txt Log: fix links Modified: py/release/0.9.x/py/doc/download.txt ============================================================================== --- py/release/0.9.x/py/doc/download.txt (original) +++ py/release/0.9.x/py/doc/download.txt Wed Aug 20 18:48:19 2008 @@ -26,12 +26,12 @@ You may also download one of these archives: - `download py-0.9.2b6.tar.gz`_ + `download py-0.9.2b7.tar.gz`_ - `download py-0.9.2b6.zip`_ + `download py-0.9.2b7.zip`_ -.. _`download py-0.9.2b6.tar.gz`: http://pypi.python.org/packages/source/p/py/py-0.9.2b6.tar.gz -.. _`download py-0.9.2b6.zip`: http://pypi.python.org/packages/source/p/py/py-0.9.2b6.zip +.. _`download py-0.9.2b7.tar.gz`: http://pypi.python.org/packages/source/p/py/py-0.9.2b7.tar.gz +.. _`download py-0.9.2b7.zip`: http://pypi.python.org/packages/source/p/py/py-0.9.2b7.zip and unpack it to a directory, where you then type:: From hpk at codespeak.net Thu Aug 21 09:09:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 09:09:09 +0200 (CEST) Subject: [py-svn] r57528 - py/release/0.9.x/py/doc Message-ID: <20080821070909.21AF316A120@codespeak.net> Author: hpk Date: Thu Aug 21 09:09:07 2008 New Revision: 57528 Modified: py/release/0.9.x/py/doc/impl-test.txt Log: review/refinements to extension doc. I'd really like to settle and write things up for 1.0 than trying to add more detail/examples here. Modified: py/release/0.9.x/py/doc/impl-test.txt ============================================================================== --- py/release/0.9.x/py/doc/impl-test.txt (original) +++ py/release/0.9.x/py/doc/impl-test.txt Thu Aug 21 09:09:07 2008 @@ -5,8 +5,6 @@ .. contents:: .. sectnum:: - - .. _`basicpicture`: @@ -71,6 +69,12 @@ looking for ``test_*.py`` and ``*_test.py`` files. Those files are imported under their `package name`_. +The Module collector looks for test functions +and test classes and methods. Test functions and methods +are prefixed ``test`` by default. Test classes must +start with a capitalized ``Test`` prefix. + + .. _`collector API`: test items are collectors as well @@ -84,48 +88,28 @@ .. _`package name`: -constructing the package name for modules ------------------------------------------ +constructing the package name for test modules +------------------------------------------------- Test modules are imported under their fully qualified -name. Given a module ``path`` the fully qualified package -name is constructed as follows: +name. Given a filesystem ``fspath`` it is constructed as follows: -* determine the last "upward" directory from ``path`` that - contains an ``__init__.py`` file. Going upwards - means repeatedly calling the ``dirpath()`` method - on a path object (which returns the parent directory - as a path object). +* walk the directories up to the last one that contains + an ``__init__.py`` file. -* insert this base directory into the sys.path list - as its first element +* perform ``sys.path.insert(0, basedir)``. -* import the root package +* import the root package as ``root`` -* determine the fully qualified name for the module located - at ``path`` ... +* determine the fully qualified name for ``fspath`` by either: - * if the imported root package has a __package__ object - then call ``__package__.getimportname(path)`` + * calling ``root.__pkg__.getimportname(fspath)`` if the + ``__pkg__`` exists.` or * otherwise use the relative path of the module path to the base dir and turn slashes into dots and strike the trailing ``.py``. -The Module collector will eventually trigger -``__import__(mod_fqdnname, ...)`` to finally get to -the live module object. - -Side note: this whole logic is performed by local path -object's ``pyimport()`` method. - -Module Collector ------------------ - -The default Module collector looks for test functions -and test classes and methods. Test functions and methods -are prefixed ``test`` by default. Test classes must -start with a capitalized ``Test`` prefix. Customizing the testing process @@ -246,32 +230,27 @@ If you have a module where you want to take responsibility for collecting your own test Items and possibly even for executing a test then you can provide `generative tests`_ that yield -callables and possibly arguments as a tuple. This should -serve some immediate purposes like paramtrized tests. +callables and possibly arguments as a tuple. This is especially +useful for calling application test machinery with different +parameter sets but counting each of the calls as a separate +tests. .. _`generative tests`: test.html#generative-tests -The other extension possibility goes deeper into the machinery -and allows you to specify a custom test ``Item`` class which +The other extension possibility is about +specifying a custom test ``Item`` class which is responsible for setting up and executing an underlying -test. [XXX not working: You can integrate your custom ``py.test.collect.Item`` subclass -by binding an ``Item`` name to a test class.] Or you can -extend the collection process for a whole directory tree -by putting Items in a ``conftest.py`` configuration file. -The collection process constantly looks at according names -in the *chain of conftest.py* modules to determine collectors -and items at ``Directory``, ``Module``, ``Class``, ``Function`` -or ``Generator`` level. Note that, right now, except for ``Function`` -items all classes are pure collectors, i.e. will return a list -of names (possibly empty). - -XXX implement doctests as alternatives to ``Function`` items. +test. Or you can extend the collection process for a whole +directory tree by putting Items in a ``conftest.py`` configuration file. +The collection process dynamically consults the *chain of conftest.py* +modules to determine collectors and items at ``Directory``, ``Module``, +``Class``, ``Function`` or ``Generator`` level respectively. Customizing execution of Functions ---------------------------------- -- Function test items allow total control of executing their - contained test method. ``function.run()`` will get called by the +- ``py.test.collect.Function`` test items control execution + of a test function. ``function.run()`` will get called by the session in order to actually run a test. The method is responsible for performing proper setup/teardown ("Test Fixtures") for a Function test. @@ -280,5 +259,4 @@ the default ``Function.run()`` to actually execute a python function with the given (usually empty set of) arguments. - .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev From hpk at codespeak.net Thu Aug 21 09:48:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 09:48:46 +0200 (CEST) Subject: [py-svn] r57529 - in py/trunk: . py Message-ID: <20080821074846.208CF16A15E@codespeak.net> Author: hpk Date: Thu Aug 21 09:48:44 2008 New Revision: 57529 Added: py/trunk/setup.cfg Modified: py/trunk/py/__init__.py Log: preparing experiments with "dev" tags and pypi. Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Thu Aug 21 09:48:44 2008 @@ -7,7 +7,7 @@ """ from initpkg import initpkg -version = "1.0-pre-alpha" +version = "1.0.0a1" initpkg(__name__, description = "pylib and py.test: agile development and test support library", Added: py/trunk/setup.cfg ============================================================================== --- (empty file) +++ py/trunk/setup.cfg Thu Aug 21 09:48:44 2008 @@ -0,0 +1,3 @@ +[egg_info] +tag_build = .dev +tag_svn_revision = 1 From hpk at codespeak.net Thu Aug 21 11:13:40 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 11:13:40 +0200 (CEST) Subject: [py-svn] r57531 - py/release/0.9.x/py/path/local Message-ID: <20080821091340.CFD3C16A18B@codespeak.net> Author: hpk Date: Thu Aug 21 11:13:39 2008 New Revision: 57531 Modified: py/release/0.9.x/py/path/local/local.py Log: remove old commented code Modified: py/release/0.9.x/py/path/local/local.py ============================================================================== --- py/release/0.9.x/py/path/local/local.py (original) +++ py/release/0.9.x/py/path/local/local.py Thu Aug 21 11:13:39 2008 @@ -390,10 +390,6 @@ #print "trying to import", self pkgpath = None if modname is None: - #try: - # return self._module - #except AttributeError: - # pass pkgpath = self.pypkgpath() if pkgpath is not None: if ensuresyspath: From hpk at codespeak.net Thu Aug 21 11:14:00 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 11:14:00 +0200 (CEST) Subject: [py-svn] r57532 - in py/release/0.9.x: . py Message-ID: <20080821091400.5959416A18D@codespeak.net> Author: hpk Date: Thu Aug 21 11:13:58 2008 New Revision: 57532 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/setup.py Log: regen setup, with a nicer long_description Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Thu Aug 21 11:13:58 2008 @@ -1,13 +1,19 @@ # -*- coding: utf-8 -*- """ -the py lib is a development support library featuring -py.test, ad-hoc distributed execution, micro-threads -and svn abstractions. +the py lib is a development support library featuring: + +**py.test**: cross-project testing tool with many advanced features +**py.execnet**: ad-hoc code distribution to SSH, Socket and local sub processes +**py.magic.greenlet**: micro-threads on standard CPython ("stackless-light") and PyPy +**py.path**: path abstractions over local and subversion files + +it provides rich documentation (http://pylib.org) and should work well +Linux, Win32, OSX, Python versions 2.3-2.6. """ from initpkg import initpkg -version = "0.9.2b7" +version = "0.9.2b8" initpkg(__name__, description = "py lib: agile development and test support library", @@ -15,7 +21,7 @@ lastchangedate = '$LastChangedDate$', version = version, url = "http://pylib.org", - download_url = "http://pypi.python.org/pypi?:action=display&name=py", + download_url = "http://codespeak.net/py/0.9.2/download.html", license = "MIT license", platforms = ['unix', 'linux', 'osx', 'cygwin', 'win32'], author = "holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others", Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Thu Aug 21 11:13:58 2008 @@ -11,12 +11,27 @@ ez_setup.use_setuptools() from setuptools import setup, Extension +long_description = """ + +the py lib is a development support library featuring: + +**py.test**: cross-project testing tool with many advanced features +**py.execnet**: ad-hoc code distribution to SSH, Socket and local sub processes +**py.magic.greenlet**: micro-threads on standard CPython ("stackless-light") and PyPy +**py.path**: path abstractions over local and subversion files + +it provides rich documentation (http://pylib.org) and should work well +Linux, Win32, OSX, Python versions 2.3-2.6. + +""" def main(): setup( name='py', description='py lib: agile development and test support library', - version='0.9.2b7', + long_description = long_description, + version='0.9.2b8', url='http://pylib.org', + download_url='http://codespeak.net/py/0.9.2/download.html', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others', @@ -29,7 +44,6 @@ 'py.lookup = py.cmdline:pylookup', 'py.rest = py.cmdline:pyrest', 'py.test = py.cmdline:pytest']}, - long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', classifiers=['Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', From hpk at codespeak.net Thu Aug 21 11:17:55 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 11:17:55 +0200 (CEST) Subject: [py-svn] r57533 - py/release/0.9.x Message-ID: <20080821091755.52FF916A09D@codespeak.net> Author: hpk Date: Thu Aug 21 11:17:53 2008 New Revision: 57533 Modified: py/release/0.9.x/CHANGELOG Log: no real support for distutils curerently Modified: py/release/0.9.x/CHANGELOG ============================================================================== --- py/release/0.9.x/CHANGELOG (original) +++ py/release/0.9.x/CHANGELOG Thu Aug 21 11:17:53 2008 @@ -4,8 +4,8 @@ =============================== * refined installation and metadata, created new setup.py, - now based on setuptools/ez_setup (by flipping a bit you - can use distutils). (thanks to Ralf Schmitt for his support) + now based on setuptools/ez_setup (thanks to Ralf Schmitt + for his support). * improved the way of making py.* scripts available in windows environments, they are now added to the From hpk at codespeak.net Thu Aug 21 11:40:24 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 11:40:24 +0200 (CEST) Subject: [py-svn] r57534 - in py/release/0.9.x: . py py/doc Message-ID: <20080821094024.4784316A120@codespeak.net> Author: hpk Date: Thu Aug 21 11:40:19 2008 New Revision: 57534 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/py/doc/index.txt py/release/0.9.x/setup.py Log: actually put some doc-links into the long-description for instant navigation on pypi. Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Thu Aug 21 11:40:19 2008 @@ -1,19 +1,28 @@ # -*- coding: utf-8 -*- """ -the py lib is a development support library featuring: +The py lib is a development support library featuring these tools and APIs: -**py.test**: cross-project testing tool with many advanced features -**py.execnet**: ad-hoc code distribution to SSH, Socket and local sub processes -**py.magic.greenlet**: micro-threads on standard CPython ("stackless-light") and PyPy -**py.path**: path abstractions over local and subversion files - -it provides rich documentation (http://pylib.org) and should work well -Linux, Win32, OSX, Python versions 2.3-2.6. +- `py.test`_: cross-project testing tool with many advanced features +- `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes +- `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy +- `py.path`_: path abstractions over local and subversion files +- `py.code`_: dynamic code compile and traceback printing support + +The py lib and its tools should work well on Linux, Win32, +OSX, Python versions 2.3-2.6. For questions please go to +http://pylib.org/contact.html + +.. _`py.test`: http://pylib.org/test.html +.. _`py.execnet`: http://pylib.org/execnet.html +.. _`py.magic.greenlet`: http://pylib.org/greenlet.html +.. _`py.path`: http://pylib.org/path.html +.. _`py.code`: http://pylib.org/code.html + """ from initpkg import initpkg -version = "0.9.2b8" +version = "0.9.2b9" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/py/doc/index.txt ============================================================================== --- py/release/0.9.x/py/doc/index.txt (original) +++ py/release/0.9.x/py/doc/index.txt Thu Aug 21 11:40:19 2008 @@ -22,6 +22,8 @@ `py.path`_: local and subversion Path and Filesystem access +`py.code`_: High-level access/manipulation of Python code and traceback objects. + `py lib scripts`_ describe the scripts contained in the ``py/bin`` directory. `apigen`_: a new way to generate rich Python API documentation @@ -29,8 +31,6 @@ support functionality --------------------------------- -`py.code`_: High-level access/manipulation of Python code and traceback objects. - `py.xml`_ for generating in-memory xml/html object trees `py.io`_: Helper Classes for Capturing of Input/Output Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Thu Aug 21 11:40:19 2008 @@ -13,15 +13,24 @@ long_description = """ -the py lib is a development support library featuring: +The py lib is a development support library featuring these tools and APIs: -**py.test**: cross-project testing tool with many advanced features -**py.execnet**: ad-hoc code distribution to SSH, Socket and local sub processes -**py.magic.greenlet**: micro-threads on standard CPython ("stackless-light") and PyPy -**py.path**: path abstractions over local and subversion files +- `py.test`_: cross-project testing tool with many advanced features +- `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes +- `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy +- `py.path`_: path abstractions over local and subversion files +- `py.code`_: dynamic code compile and traceback printing support + +The py lib and its tools should work well on Linux, Win32, +OSX, Python versions 2.3-2.6. For questions please go to +http://pylib.org/contact.html + +.. _`py.test`: http://pylib.org/test.html +.. _`py.execnet`: http://pylib.org/execnet.html +.. _`py.magic.greenlet`: http://pylib.org/greenlet.html +.. _`py.path`: http://pylib.org/path.html +.. _`py.code`: http://pylib.org/code.html -it provides rich documentation (http://pylib.org) and should work well -Linux, Win32, OSX, Python versions 2.3-2.6. """ def main(): @@ -29,7 +38,7 @@ name='py', description='py lib: agile development and test support library', long_description = long_description, - version='0.9.2b8', + version='0.9.2b9', url='http://pylib.org', download_url='http://codespeak.net/py/0.9.2/download.html', license='MIT license', From hpk at codespeak.net Thu Aug 21 11:48:47 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 11:48:47 +0200 (CEST) Subject: [py-svn] r57535 - in py/trunk/py/code: . testing Message-ID: <20080821094847.6EB0D16A16D@codespeak.net> Author: hpk Date: Thu Aug 21 11:48:46 2008 New Revision: 57535 Modified: py/trunk/py/code/excinfo.py py/trunk/py/code/safe_repr.py py/trunk/py/code/testing/test_safe_repr.py Log: extending hacks for a safe representation of objects to also work for broken __repr__s on newstyle classes. Modified: py/trunk/py/code/excinfo.py ============================================================================== --- py/trunk/py/code/excinfo.py (original) +++ py/trunk/py/code/excinfo.py Thu Aug 21 11:48:46 2008 @@ -153,7 +153,7 @@ else: # This formatting could all be handled by the _repr() function, which is # only repr.Repr in disguise, so is very configurable. - str_repr = safe_repr._repr(value) + str_repr = self._saferepr(value) #if len(str_repr) < 70 or not isinstance(value, # (list, tuple, dict)): lines.append("%-10s = %s" %(name, str_repr)) Modified: py/trunk/py/code/safe_repr.py ============================================================================== --- py/trunk/py/code/safe_repr.py (original) +++ py/trunk/py/code/safe_repr.py Thu Aug 21 11:48:46 2008 @@ -30,10 +30,17 @@ # Do we need a commandline switch for this? self.maxstring = 240 # 3 * 80 chars self.maxother = 160 # 2 * 80 chars + + def repr(self, x): + return self._callhelper(repr.Repr.repr, self, x) + def repr_instance(self, x, level): + return self._callhelper(__builtin__.repr, x) + + def _callhelper(self, call, x, *args): try: # Try the vanilla repr and make sure that the result is a string - s = str(__builtin__.repr(x)) + s = call(x, *args) except (KeyboardInterrupt, MemoryError, SystemExit): raise except Exception ,e: @@ -60,4 +67,5 @@ s = s[:i] + '...' + s[len(s)-j:] return s + _repr = SafeRepr().repr Modified: py/trunk/py/code/testing/test_safe_repr.py ============================================================================== --- py/trunk/py/code/testing/test_safe_repr.py (original) +++ py/trunk/py/code/testing/test_safe_repr.py Thu Aug 21 11:48:46 2008 @@ -35,5 +35,14 @@ assert len(safe_repr._repr(range(1000))) <= \ len('[' + safe_repr.SafeRepr().maxlist * "1000" + ']') +def test_repr_on_newstyle(): + class Function(object): + def __repr__(self): + return "<%s>" %(self.name) + try: + s = safe_repr._repr(Function()) + except Exception, e: + py.test.fail("saferepr failed for newstyle class") + From hpk at codespeak.net Thu Aug 21 12:00:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 12:00:36 +0200 (CEST) Subject: [py-svn] r57536 - py/trunk/py/path Message-ID: <20080821100036.C718316A189@codespeak.net> Author: hpk Date: Thu Aug 21 12:00:33 2008 New Revision: 57536 Added: py/trunk/py/path/ - copied from r57535, py/release/0.9.x/py/path/ Log: get the py.path fixes of 0.9.x release branch back to trunk From hpk at codespeak.net Thu Aug 21 12:05:11 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 12:05:11 +0200 (CEST) Subject: [py-svn] r57538 - py/trunk/py/path/gateway Message-ID: <20080821100511.1FD4316A193@codespeak.net> Author: hpk Date: Thu Aug 21 12:05:04 2008 New Revision: 57538 Modified: py/trunk/py/path/gateway/channeltest.py py/trunk/py/path/gateway/channeltest2.py py/trunk/py/path/gateway/remotepath.py Log: re-apply few fixes to make experimental gateway path basically work again. Modified: py/trunk/py/path/gateway/channeltest.py ============================================================================== --- py/trunk/py/path/gateway/channeltest.py (original) +++ py/trunk/py/path/gateway/channeltest.py Thu Aug 21 12:05:04 2008 @@ -25,7 +25,7 @@ def command_GET(self, id, spec): path = self.C2P[id] - self.channel.send(path.get(spec)) + self.channel.send(path._getbyspec(spec)) def command_READ(self, id): path = self.C2P[id] @@ -53,7 +53,7 @@ if __name__ == '__main__': import py gw = py.execnet.PopenGateway() - channel = gw.channelfactory.new() + channel = gw._channelfactory.new() srv = PathServer(channel) c = gw.remote_exec(""" import remotepath Modified: py/trunk/py/path/gateway/channeltest2.py ============================================================================== --- py/trunk/py/path/gateway/channeltest2.py (original) +++ py/trunk/py/path/gateway/channeltest2.py Thu Aug 21 12:05:04 2008 @@ -13,8 +13,9 @@ #gw = py.execnet.SshGateway('codespeak.net') gw = py.execnet.PopenGateway() -c = gw.remote_exec(SRC) -subchannel = gw.channelfactory.new() +gw.remote_init_threads(5) +c = gw.remote_exec(SRC, stdout=py.std.sys.stdout, stderr=py.std.sys.stderr) +subchannel = gw._channelfactory.new() c.send(subchannel) p = RemotePath(subchannel, c.receive()) Modified: py/trunk/py/path/gateway/remotepath.py ============================================================================== --- py/trunk/py/path/gateway/remotepath.py (original) +++ py/trunk/py/path/gateway/remotepath.py Thu Aug 21 12:05:04 2008 @@ -33,7 +33,7 @@ self._channel.send(('JOIN', self._id, id) + args) return RemotePath(self._channel, id) - def get(self, spec): + def _getbyspec(self, spec): parts = spec.split(',') ask = [x for x in parts if x not in self._specs] if ask: From hpk at codespeak.net Thu Aug 21 12:11:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 12:11:31 +0200 (CEST) Subject: [py-svn] r57539 - py/release/0.9.x/py/doc Message-ID: <20080821101131.155F316A169@codespeak.net> Author: hpk Date: Thu Aug 21 12:11:30 2008 New Revision: 57539 Modified: py/release/0.9.x/py/doc/download.txt Log: making download page version independent and routing all downloads through pypi Modified: py/release/0.9.x/py/doc/download.txt ============================================================================== --- py/release/0.9.x/py/doc/download.txt (original) +++ py/release/0.9.x/py/doc/download.txt Thu Aug 21 12:11:30 2008 @@ -24,21 +24,20 @@ Downloading a tar/zip archive and installing that =================================================== -You may also download one of these archives: +Go to the project pages and download a tar or zip file: - `download py-0.9.2b7.tar.gz`_ - - `download py-0.9.2b7.zip`_ - -.. _`download py-0.9.2b7.tar.gz`: http://pypi.python.org/packages/source/p/py/py-0.9.2b7.tar.gz -.. _`download py-0.9.2b7.zip`: http://pypi.python.org/packages/source/p/py/py-0.9.2b7.zip + http://pypi.python.org/pypi/py/ and unpack it to a directory, where you then type:: python setup.py install -You will need a working C-compiler in order to comlete this step successfully. +If you don't have a working C-compiler you can do:: + + python setup.py install_lib +You will then not be able to use greenlets but otherwise +should be fine. Installing from subversion / develop mode ============================================ From hpk at codespeak.net Thu Aug 21 12:19:02 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 12:19:02 +0200 (CEST) Subject: [py-svn] r57540 - in py/trunk: . py py/apigen/tracer py/bin py/bin/win32 py/cmdline py/cmdline/testing py/doc py/execnet py/misc py/misc/testing py/path/svn/testing Message-ID: <20080821101902.8AFBD16A198@codespeak.net> Author: hpk Date: Thu Aug 21 12:18:58 2008 New Revision: 57540 Added: py/trunk/py/bin/_docgen.py - copied unchanged from r57536, py/release/0.9.x/py/bin/_docgen.py py/trunk/py/bin/_genscripts.py - copied unchanged from r57536, py/release/0.9.x/py/bin/_genscripts.py py/trunk/py/bin/py.cleanup - copied unchanged from r57536, py/release/0.9.x/py/bin/py.cleanup py/trunk/py/bin/py.countloc - copied unchanged from r57536, py/release/0.9.x/py/bin/py.countloc py/trunk/py/bin/py.lookup - copied unchanged from r57536, py/release/0.9.x/py/bin/py.lookup py/trunk/py/bin/py.rest - copied unchanged from r57536, py/release/0.9.x/py/bin/py.rest py/trunk/py/bin/py.test - copied unchanged from r57536, py/release/0.9.x/py/bin/py.test py/trunk/py/bin/win32/ - copied from r57536, py/release/0.9.x/py/bin/win32/ py/trunk/py/bin/win32/py.cleanup.cmd - copied unchanged from r57536, py/release/0.9.x/py/bin/win32/py.cleanup.cmd py/trunk/py/bin/win32/py.countloc.cmd - copied unchanged from r57536, py/release/0.9.x/py/bin/win32/py.countloc.cmd py/trunk/py/bin/win32/py.lookup.cmd - copied unchanged from r57536, py/release/0.9.x/py/bin/win32/py.lookup.cmd py/trunk/py/bin/win32/py.rest.cmd - copied unchanged from r57536, py/release/0.9.x/py/bin/win32/py.rest.cmd py/trunk/py/bin/win32/py.test.cmd - copied unchanged from r57536, py/release/0.9.x/py/bin/win32/py.test.cmd py/trunk/py/cmdline/ (props changed) - copied from r57536, py/release/0.9.x/py/cmdline/ py/trunk/py/cmdline/__init__.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/__init__.py py/trunk/py/cmdline/pycleanup.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/pycleanup.py py/trunk/py/cmdline/pycountloc.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/pycountloc.py py/trunk/py/cmdline/pylookup.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/pylookup.py py/trunk/py/cmdline/pyrest.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/pyrest.py py/trunk/py/cmdline/pytest.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/pytest.py py/trunk/py/cmdline/testing/ (props changed) - copied from r57536, py/release/0.9.x/py/cmdline/testing/ py/trunk/py/cmdline/testing/__init__.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/testing/__init__.py py/trunk/py/cmdline/testing/test_generic.py - copied unchanged from r57536, py/release/0.9.x/py/cmdline/testing/test_generic.py py/trunk/py/path/svn/testing/test_auth.py - copied unchanged from r57536, py/release/0.9.x/py/path/svn/testing/test_auth.py Removed: py/trunk/_findpy.py Modified: py/trunk/CHANGELOG py/trunk/MANIFEST py/trunk/py/LICENSE py/trunk/py/__init__.py py/trunk/py/apigen/tracer/docstorage.py py/trunk/py/doc/download.txt py/trunk/py/doc/impl-test.txt py/trunk/py/doc/index.txt py/trunk/py/doc/release-0.9.2.txt py/trunk/py/execnet/inputoutput.py py/trunk/py/execnet/register.py py/trunk/py/misc/buildcmodule.py py/trunk/py/misc/testing/test_oskill.py py/trunk/setup.py Log: merge changes from release branch back [svn merge -r 57430:HEAD ../release/0.9.x/ .] * cmdline script organisation * execnet windows fixes * documentation updates * test skips also regen setup.py Modified: py/trunk/CHANGELOG ============================================================================== --- py/trunk/CHANGELOG (original) +++ py/trunk/CHANGELOG Thu Aug 21 12:18:58 2008 @@ -4,17 +4,26 @@ =============================== * refined installation and metadata, created new setup.py, - now based on setuptools/ez_setup (by flipping a bit you - can use distutils). + now based on setuptools/ez_setup (thanks to Ralf Schmitt + for his support). * improved the way of making py.* scripts available in windows environments, they are now added to the Scripts directory as ".cmd" files. -* improved py.execnet bootstrapping on windows +* py.path.svnwc.status() now is more complete and + uses xml output from the 'svn' command if available + (Guido Wesdorp) + +* fix for py.path.svn* to work with svn 1.5 + (Chris Lamb) + +* fix path.relto(otherpath) method on windows to + use normcase for checking if a path is relative. * py.test's traceback is better parseable from editors (follows the filenames:LINENO: MSG convention) + (thanks to Osmo Salomaa) * fix to javascript-generation, "py.test --runbrowser" should work more reliably now Modified: py/trunk/MANIFEST ============================================================================== --- py/trunk/MANIFEST (original) +++ py/trunk/MANIFEST Thu Aug 21 12:18:58 2008 @@ -61,13 +61,20 @@ py/apigen/tracer/testing/test_model.py py/apigen/tracer/testing/test_package.py py/apigen/tracer/tracer.py +py/bin/_docgen.py py/bin/_findpy.py +py/bin/_genscripts.py py/bin/py.cleanup py/bin/py.countloc py/bin/py.lookup py/bin/py.rest py/bin/py.test py/bin/py.which +py/bin/win32/py.cleanup.cmd +py/bin/win32/py.countloc.cmd +py/bin/win32/py.lookup.cmd +py/bin/win32/py.rest.cmd +py/bin/win32/py.test.cmd py/builtin/__init__.py py/builtin/enumerate.py py/builtin/exception.py @@ -100,6 +107,14 @@ py/c-extension/greenlet/test_greenlet.py py/c-extension/greenlet/test_remote.py py/c-extension/greenlet/test_throw.py +py/cmdline/__init__.py +py/cmdline/pycleanup.py +py/cmdline/pycountloc.py +py/cmdline/pylookup.py +py/cmdline/pyrest.py +py/cmdline/pytest.py +py/cmdline/testing/__init__.py +py/cmdline/testing/test_generic.py py/code/__init__.py py/code/code.py py/code/excinfo.py @@ -419,4 +434,5 @@ py/xmlobj/testing/test_xml.py py/xmlobj/visit.py py/xmlobj/xml.py +setup.cfg setup.py \ No newline at end of file Deleted: /py/trunk/_findpy.py ============================================================================== --- /py/trunk/_findpy.py Thu Aug 21 12:18:58 2008 +++ (empty file) @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -# -# find and import a version of 'py' -# -import sys -import os -from os.path import dirname as opd, exists, join, basename, abspath - -def searchpy(current): - while 1: - last = current - initpy = join(current, '__init__.py') - if not exists(initpy): - pydir = join(current, 'py') - # recognize py-package and ensure it is importable - if exists(pydir) and exists(join(pydir, '__init__.py')): - #for p in sys.path: - # if p == current: - # return True - if current != sys.path[0]: # if we are already first, then ok - print >>sys.stderr, "inserting into sys.path:", current - sys.path.insert(0, current) - return True - current = opd(current) - if last == current: - return False - -if not searchpy(abspath(os.curdir)): - if not searchpy(opd(abspath(sys.argv[0]))): - if not searchpy(opd(__file__)): - pass # let's hope it is just on sys.path - -import py - -if __name__ == '__main__': - print "py lib is at", py.__file__ Modified: py/trunk/py/LICENSE ============================================================================== --- py/trunk/py/LICENSE (original) +++ py/trunk/py/LICENSE Thu Aug 21 12:18:58 2008 @@ -16,6 +16,7 @@ Contributors include:: Samuele Pedroni + Chris Lamb Harald Armin Massa Ralf Schmitt Ian Bicking Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Thu Aug 21 12:18:58 2008 @@ -1,9 +1,24 @@ # -*- coding: utf-8 -*- """ -the py lib is a development support library featuring -py.test, ad-hoc distributed execution, micro-threads -and svn abstractions. +The py lib is a development support library featuring these tools and APIs: + +- `py.test`_: cross-project testing tool with many advanced features +- `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes +- `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy +- `py.path`_: path abstractions over local and subversion files +- `py.code`_: dynamic code compile and traceback printing support + +The py lib and its tools should work well on Linux, Win32, +OSX, Python versions 2.3-2.6. For questions please go to +http://pylib.org/contact.html + +.. _`py.test`: http://pylib.org/test.html +.. _`py.execnet`: http://pylib.org/execnet.html +.. _`py.magic.greenlet`: http://pylib.org/greenlet.html +.. _`py.path`: http://pylib.org/path.html +.. _`py.code`: http://pylib.org/code.html + """ from initpkg import initpkg @@ -15,14 +30,14 @@ lastchangedate = '$LastChangedDate$', version = version, url = "http://pylib.org", - download_url = "http://pypi.python.org/pypi?:action=display&name=py", + download_url = "http://codespeak.net/py/0.9.2/download.html", license = "MIT license", platforms = ['unix', 'linux', 'osx', 'cygwin', 'win32'], author = "holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others", author_email = "holger at merlinux.eu, py-dev at codespeak.net", long_description = globals()['__doc__'], classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: POSIX", @@ -38,6 +53,13 @@ # EXPORTED API exportdefs = { + # py lib cmdline tools + 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), + 'cmdline.pyrest' : ('./cmdline/pyrest.py', 'main',), + 'cmdline.pylookup' : ('./cmdline/pylookup.py', 'main',), + 'cmdline.pycountloc' : ('./cmdline/pycountloc.py', 'main',), + 'cmdline.pycleanup' : ('./cmdline/pycleanup.py', 'main',), + # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), 'test.raises' : ('./test/outcome.py', 'raises'), Modified: py/trunk/py/apigen/tracer/docstorage.py ============================================================================== --- py/trunk/py/apigen/tracer/docstorage.py (original) +++ py/trunk/py/apigen/tracer/docstorage.py Thu Aug 21 12:18:58 2008 @@ -21,8 +21,21 @@ for key, value in defs.iteritems(): chain = key.split('.') base = module - for elem in chain: - base = getattr(base, elem) + # XXX generalize this: + # a bit of special casing for greenlets which are + # not available on all the platforms that python/py + # lib runs + try: + for elem in chain: + base = getattr(base, elem) + except RuntimeError, exc: + if elem == "greenlet": + print exc.__class__.__name__, exc + print "Greenlets not supported on this platform. Skipping apigen doc for this module" + continue + else: + raise + if value[1] == '*': d.update(get_star_import_tree(base, key)) else: Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Thu Aug 21 12:18:58 2008 @@ -1,104 +1,71 @@ -Download and Installation of the py lib -=============================================== -.. contents:: -.. sectnum:: -Downloading a tar/zip file and installing it +"easy_install py" =================================================== -The latest public release: +With a working `setuptools installation`_ you can install from the command line:: - `download py-0.9.0.tar.gz`_ - `download py-0.9.0.zip`_ + easy_install py -.. _`download py-0.9.0.tar.gz`: http://codespeak.net/download/py/py-0.9.0.tar.gz -.. _`download py-0.9.0.zip`: http://codespeak.net/download/py/py-0.9.0.zip +to get the latest release of the py lib. On non-Windows systems you will +need a working C-compiler in order to successfully complete this step. +The py lib and its tools are expected to work well on Linux, Windows +and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. -The py lib can be `globally installed via setup.py`_ -or `used locally`_. +**IMPORTANT NOTE**: if you are using Windows and have previous +installations of the py lib on your system, please download +and execute http://codespeak.net/svn/py/build/winpathclean.py +This will check that no previous files are getting in the way. +(Unfortunately we don't know about a way to execute this +code automatically during the above install). -WARNING: win32 there is no pre-packaged c-extension -module (greenlet) yet and thus greenlets will not work -out of the box. - -Getting (and updating) via subversion --------------------------------------------- - -Use Subversion to checkout the latest 0.9.x stable release: - - svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x - -to obtain the complete code and documentation source. - -If you experience problems with the subversion checkout e.g. -because you have a http-proxy in between that doesn't proxy -DAV requests you can try to use "codespeak.net:8080" instead -of just "codespeak.net". Alternatively, you may tweak -your local subversion installation. - -If you want to follow stable snapshots -then you may use the equivalent of this invocation: - - svn co http://codespeak.net/svn/py/dist py-dist +.. _`setuptools installation`: http://pypi.python.org/pypi/setuptools +Downloading a tar/zip archive and installing that +=================================================== -.. _`globally installed via setup.py`: +Go to the project pages and download a tar or zip file: -Installation via setup.py ------------------------------- + http://pypi.python.org/pypi/py/ -Go to your unpacked/checked out directory -and issue: +and unpack it to a directory, where you then type:: python setup.py install +If you don't have a working C-compiler you can do:: -.. _`used locally`: - -Local Installation/Usage ------------------------------- - -You need to put the checkout-directory into your ``PYTHONPATH`` -and you want to have the ``py-dist/py/bin/py.test`` script in -your (unixish) system path, which lets you execute test files -and directories. - -There is a convenient way for Bash/Shell based systems -to setup the ``PYTHONPATH`` as well as the shell ``PATH``, insert:: + python setup.py install_lib - eval `python ~/path/to/py-dist/py/env.py` +You will then not be able to use greenlets but otherwise +should be fine. -into your ``.bash_profile``. Of course, you need to -specify your own checkout-directory. +Installing from subversion / develop mode +============================================ +To follow development or help with fixing things +for the next release, checkout the complete code +and documentation source:: -.. _`svn-external scenario`: - -The py lib as an svn external -------------------------------------------------------- - -Add the py lib as an external to your project `DIRECTORY` -which contains your svn-controlled root package:: + svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x - svn propedit 'svn:externals' DIRECTORY +You should then be able to issue:: -which will open an editor where you can add -the following line: + python setup.py develop - py http://codespeak.net/svn/py/dist +and work with the local version. -This will make your projcet automatically use the -most recent stable snapshot of the py lib. +If this doesn't work for you then you can also +source (linux) or execute (windows) some scripts +that set environment variables for using your checkout. +You can execute: -Alternatively you may use this url for -integrating the development version: + python ~/path/to/checkout/py/env.py - http://codespeak.net/svn/py/trunk +or on windows: -or the next one for following the e.g. the 0.9 release branch + \\path\\to\\checkout\\py\\env.cmd - http://codespeak.net/svn/py/release/0.9.x +to get settings for PYTHONPATH and PATH. py subversion directory structure Modified: py/trunk/py/doc/impl-test.txt ============================================================================== --- py/trunk/py/doc/impl-test.txt (original) +++ py/trunk/py/doc/impl-test.txt Thu Aug 21 12:18:58 2008 @@ -5,8 +5,6 @@ .. contents:: .. sectnum:: - - .. _`basicpicture`: @@ -71,6 +69,12 @@ looking for ``test_*.py`` and ``*_test.py`` files. Those files are imported under their `package name`_. +The Module collector looks for test functions +and test classes and methods. Test functions and methods +are prefixed ``test`` by default. Test classes must +start with a capitalized ``Test`` prefix. + + .. _`collector API`: test items are collectors as well @@ -84,48 +88,28 @@ .. _`package name`: -constructing the package name for modules ------------------------------------------ +constructing the package name for test modules +------------------------------------------------- Test modules are imported under their fully qualified -name. Given a module ``path`` the fully qualified package -name is constructed as follows: +name. Given a filesystem ``fspath`` it is constructed as follows: -* determine the last "upward" directory from ``path`` that - contains an ``__init__.py`` file. Going upwards - means repeatedly calling the ``dirpath()`` method - on a path object (which returns the parent directory - as a path object). +* walk the directories up to the last one that contains + an ``__init__.py`` file. -* insert this base directory into the sys.path list - as its first element +* perform ``sys.path.insert(0, basedir)``. -* import the root package +* import the root package as ``root`` -* determine the fully qualified name for the module located - at ``path`` ... +* determine the fully qualified name for ``fspath`` by either: - * if the imported root package has a __package__ object - then call ``__package__.getimportname(path)`` + * calling ``root.__pkg__.getimportname(fspath)`` if the + ``__pkg__`` exists.` or * otherwise use the relative path of the module path to the base dir and turn slashes into dots and strike the trailing ``.py``. -The Module collector will eventually trigger -``__import__(mod_fqdnname, ...)`` to finally get to -the live module object. - -Side note: this whole logic is performed by local path -object's ``pyimport()`` method. - -Module Collector ------------------ - -The default Module collector looks for test functions -and test classes and methods. Test functions and methods -are prefixed ``test`` by default. Test classes must -start with a capitalized ``Test`` prefix. Customizing the testing process @@ -256,32 +240,27 @@ If you have a module where you want to take responsibility for collecting your own test Items and possibly even for executing a test then you can provide `generative tests`_ that yield -callables and possibly arguments as a tuple. This should -serve some immediate purposes like paramtrized tests. +callables and possibly arguments as a tuple. This is especially +useful for calling application test machinery with different +parameter sets but counting each of the calls as a separate +tests. .. _`generative tests`: test.html#generative-tests -The other extension possibility goes deeper into the machinery -and allows you to specify a custom test ``Item`` class which +The other extension possibility is about +specifying a custom test ``Item`` class which is responsible for setting up and executing an underlying -test. [XXX not working: You can integrate your custom ``py.test.collect.Item`` subclass -by binding an ``Item`` name to a test class.] Or you can -extend the collection process for a whole directory tree -by putting Items in a ``conftest.py`` configuration file. -The collection process constantly looks at according names -in the *chain of conftest.py* modules to determine collectors -and items at ``Directory``, ``Module``, ``Class``, ``Function`` -or ``Generator`` level. Note that, right now, except for ``Function`` -items all classes are pure collectors, i.e. will return a list -of names (possibly empty). - -XXX implement doctests as alternatives to ``Function`` items. +test. Or you can extend the collection process for a whole +directory tree by putting Items in a ``conftest.py`` configuration file. +The collection process dynamically consults the *chain of conftest.py* +modules to determine collectors and items at ``Directory``, ``Module``, +``Class``, ``Function`` or ``Generator`` level respectively. Customizing execution of Functions ---------------------------------- -- Function test items allow total control of executing their - contained test method. ``function.run()`` will get called by the +- ``py.test.collect.Function`` test items control execution + of a test function. ``function.run()`` will get called by the session in order to actually run a test. The method is responsible for performing proper setup/teardown ("Test Fixtures") for a Function test. @@ -290,5 +269,4 @@ the default ``Function.run()`` to actually execute a python function with the given (usually empty set of) arguments. - .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Thu Aug 21 12:18:58 2008 @@ -1,13 +1,15 @@ py lib documentation ================================================= - The py lib aims at supporting a decent development process - addressing deployment, versioning, testing and documentation - perspectives. + The py lib is a development support library featuring + py.test, ad-hoc distributed execution, micro-threads + (greenlets) and uniform local path and svn abstractions. + Works on Linux, Windows and OSX, Python versions + 2.3, 2.4, 2.5 and 2.6. `Download and Installation`_ -`0.9.0 release announcement`_ +`0.9.2 release announcement`_ Main tools and API ---------------------- @@ -20,6 +22,8 @@ `py.path`_: local and subversion Path and Filesystem access +`py.code`_: High-level access/manipulation of Python code and traceback objects. + `py lib scripts`_ describe the scripts contained in the ``py/bin`` directory. `apigen`_: a new way to generate rich Python API documentation @@ -27,8 +31,6 @@ support functionality --------------------------------- -`py.code`_: High-level access/manipulation of Python code and traceback objects. - `py.xml`_ for generating in-memory xml/html object trees `py.io`_: Helper Classes for Capturing of Input/Output @@ -59,4 +61,4 @@ .. _`Why What how py?`: why_py.html .. _`future`: future.html .. _`miscellaneous features`: misc.html -.. _`0.9.0 release announcement`: release-0.9.0.html +.. _`0.9.2 release announcement`: release-0.9.2.html Modified: py/trunk/py/doc/release-0.9.2.txt ============================================================================== --- py/trunk/py/doc/release-0.9.2.txt (original) +++ py/trunk/py/doc/release-0.9.2.txt Thu Aug 21 12:18:58 2008 @@ -5,7 +5,7 @@ mainly fixing Windows issues, providing better packaging and integration with setuptools. -Summary of main feature of the py lib: +Here is a quick summary of what it provides: * py.test: cross-project testing tool with many advanced features * py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes @@ -17,6 +17,7 @@ See here for more information: Download/Install: http://codespeak.net/py/0.9.2/download.html + Documentation/API: http://codespeak.net/py/0.9.2/index.html best and have fun, Modified: py/trunk/py/execnet/inputoutput.py ============================================================================== --- py/trunk/py/execnet/inputoutput.py (original) +++ py/trunk/py/execnet/inputoutput.py Thu Aug 21 12:18:58 2008 @@ -59,10 +59,6 @@ class Popen2IO: server_stmt = """ import os, sys, StringIO -if sys.platform == "win32": - import msvcrt - msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) - msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) io = Popen2IO(sys.stdout, sys.stdin) sys.stdout = sys.stderr = StringIO.StringIO() #try: @@ -75,6 +71,11 @@ def __init__(self, infile, outfile): self.outfile, self.infile = infile, outfile + if sys.platform == "win32": + import msvcrt + msvcrt.setmode(infile.fileno(), os.O_BINARY) + msvcrt.setmode(outfile.fileno(), os.O_BINARY) + self.readable = self.writeable = True self.lock = thread.allocate_lock() Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Thu Aug 21 12:18:58 2008 @@ -2,8 +2,12 @@ import os, inspect, socket import sys from py.magic import autopath ; mypath = autopath() - import py +if sys.platform == "win32": + win32 = True + import msvcrt +else: + win32 = False # the list of modules that must be send to the other side # for bootstrapping gateways @@ -51,13 +55,10 @@ class PopenCmdGateway(InstallableGateway): def __init__(self, cmd): infile, outfile = os.popen2(cmd) - if sys.platform == 'win32': - import msvcrt - msvcrt.setmode(infile.fileno(), os.O_BINARY) - msvcrt.setmode(outfile.fileno(), os.O_BINARY) io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) + class PopenGateway(PopenCmdGateway): """ This Gateway provides interaction with a newly started python subprocess. Modified: py/trunk/py/misc/buildcmodule.py ============================================================================== --- py/trunk/py/misc/buildcmodule.py (original) +++ py/trunk/py/misc/buildcmodule.py Thu Aug 21 12:18:58 2008 @@ -3,6 +3,9 @@ """ import py +# set to true for automatic re-compilation of extensions +AUTOREGEN = True + # XXX we should distutils in a subprocess, because it messes up the # environment and who knows what else. Currently we just save # and restore os.environ. @@ -32,7 +35,7 @@ lib = dirpath.join(modname+ext) # XXX argl! we need better "build"-locations alltogether! - if lib.check() and lib.stat().mtime < cfile.stat().mtime: + if lib.check() and AUTOREGEN and lib.stat().mtime < cfile.stat().mtime: try: lib.remove() except EnvironmentError: Modified: py/trunk/py/misc/testing/test_oskill.py ============================================================================== --- py/trunk/py/misc/testing/test_oskill.py (original) +++ py/trunk/py/misc/testing/test_oskill.py Thu Aug 21 12:18:58 2008 @@ -8,6 +8,10 @@ py.test.skip("you\'re using an older version of windows, which " "doesn\'t support 'taskkill' - py.misc.killproc is not " "available") + try: + import subprocess + except ImportError: + py.test.skip("no subprocess module") tmp = py.test.ensuretemp("test_win_killsubprocess") t = tmp.join("t.py") t.write("import time ; time.sleep(100)") Modified: py/trunk/setup.py ============================================================================== --- py/trunk/setup.py (original) +++ py/trunk/setup.py Thu Aug 21 12:18:58 2008 @@ -1,25 +1,46 @@ """ setup file for 'py' package based on: - https://codespeak.net/svn/py/trunk, revision=57462 + https://codespeak.net/svn/py/trunk, revision=57536 autogenerated by gensetup.py """ import os, sys -if 1: # set to zero if you want plain distutils - import ez_setup - ez_setup.use_setuptools() - from setuptools import setup, Extension -else: - from distutils.core import setup, Extension +import ez_setup +ez_setup.use_setuptools() +from setuptools import setup, Extension +long_description = """ + +The py lib is a development support library featuring these tools and APIs: + +- `py.test`_: cross-project testing tool with many advanced features +- `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes +- `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy +- `py.path`_: path abstractions over local and subversion files +- `py.code`_: dynamic code compile and traceback printing support + +The py lib and its tools should work well on Linux, Win32, +OSX, Python versions 2.3-2.6. For questions please go to +http://pylib.org/contact.html + +.. _`py.test`: http://pylib.org/test.html +.. _`py.execnet`: http://pylib.org/execnet.html +.. _`py.magic.greenlet`: http://pylib.org/greenlet.html +.. _`py.path`: http://pylib.org/path.html +.. _`py.code`: http://pylib.org/code.html + + +""" def main(): - setup(cmdclass=cmdclass, + setup( name='py', description='pylib and py.test: agile development and test support library', + long_description = long_description, version='1.0.0a1', url='http://pylib.org', + download_url='http://codespeak.net/py/0.9.2/download.html', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='holger krekel, Guido Wesdorp, Carl Friedrich Bolz, Armin Rigo, Maciej Fijalkowski & others', @@ -27,9 +48,13 @@ ext_modules = [Extension("py.c-extension.greenlet.greenlet", ["py/c-extension/greenlet/greenlet.c"]),], - py_modules=['_findpy'], - long_description='the py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads and svn abstractions.', - classifiers=['Development Status :: 3 - Alpha', + entry_points={'console_scripts': ['py.cleanup = py.cmdline:pycleanup', + 'py.countloc = py.cmdline:pycountloc', + 'py.lookup = py.cmdline:pylookup', + 'py.rest = py.cmdline:pyrest', + 'py.test = py.cmdline:pytest', + 'py.which = py.cmdline:pywhich']}, + classifiers=['Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: POSIX', @@ -40,12 +65,6 @@ 'Topic :: System :: Distributed Computing', 'Topic :: Utilities', 'Programming Language :: Python'], - scripts=['py/bin/py.cleanup', - 'py/bin/py.countloc', - 'py/bin/py.lookup', - 'py/bin/py.rest', - 'py/bin/py.test', - 'py/bin/py.which'], packages=['py', 'py.apigen', 'py.apigen.rest', @@ -61,6 +80,8 @@ 'py.builtin', 'py.builtin.testing', 'py.c-extension', + 'py.cmdline', + 'py.cmdline.testing', 'py.code', 'py.code.testing', 'py.compat', @@ -117,13 +138,20 @@ 'apigen/style.css', 'apigen/todo-apigen.txt', 'apigen/todo.txt', + 'bin/_docgen.py', 'bin/_findpy.py', + 'bin/_genscripts.py', 'bin/py.cleanup', 'bin/py.countloc', 'bin/py.lookup', 'bin/py.rest', 'bin/py.test', 'bin/py.which', + 'bin/win32/py.cleanup.cmd', + 'bin/win32/py.countloc.cmd', + 'bin/win32/py.lookup.cmd', + 'bin/win32/py.rest.cmd', + 'bin/win32/py.test.cmd', 'c-extension/greenlet/README.txt', 'c-extension/greenlet/dummy_greenlet.py', 'c-extension/greenlet/greenlet.c', @@ -201,100 +229,6 @@ zip_safe=False, ) -class Win32PathHandling: - """ a helper to remove things added to system PATHs in previous installations. """ - _winreg = None - def __init__(self): - if sys.platform == 'win32': - try: - import _winreg - except ImportError: - print sys.stderr, "huh could not import _winreg on windows, ignoring" - else: - self._winreg = _winreg - - def remove_pylib_path(self): - reg = self._winreg.ConnectRegistry( - None, self._winreg.HKEY_LOCAL_MACHINE) - key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" - path = self.get_registry_value(reg, key, "Path") - newpath = self.prunepath(path) - if newpath != path: - print "PATH contains old py/bin/win32 scripts:", path - print "pruning and setting a new PATH:", newpath - self.set_registry_value(reg, key, "Path", newpath) - # Propagate changes to current command prompt - os.system("set PATH=%s" % path) - self.try_propagate_system() - - def prunepath(self, path): - basename = os.path.basename - dirname = os.path.dirname - l = [] - for p in path.split(';'): - if basename(p) == "win32" and basename(dirname(p)) == "bin" \ - and basename(dirname(dirname(p))) == "py": - continue # prune this path - l.append(p) - return ";".join(l) - - def try_propagate_system(self): - try: - import win32gui, win32con - except ImportError: - return - # Propagate changes throughout the system - win32gui.SendMessageTimeout(win32con.HWND_BROADCAST, - win32con.WM_SETTINGCHANGE, 0, "Environment", - win32con.SMTO_ABORTIFHUNG, 5000) - - def get_registry_value(self, reg, key, value_name): - k = self._winreg.OpenKey(reg, key) - value = self._winreg.QueryValueEx(k, value_name)[0] - self._winreg.CloseKey(k) - return value - - def set_registry_value(self, reg, key, value_name, value): - k = self._winreg.OpenKey(reg, key, 0, self._winreg.KEY_WRITE) - value_type = self._winreg.REG_SZ - # if we handle the Path value, then set its type to REG_EXPAND_SZ - # so that things like %SystemRoot% get automatically expanded by the - # command prompt - if value_name == "Path": - value_type = self._winreg.REG_EXPAND_SZ - self._winreg.SetValueEx(k, value_name, 0, value_type, value) - self._winreg.CloseKey(k) - -# on windows we need to hack up the to-be-installed scripts -from distutils.command.install_scripts import install_scripts -class my_install_scripts(install_scripts): - def run(self): - install_scripts.run(self) - #print self.outfiles - for fn in self.outfiles: - basename = os.path.basename(fn) - if "." in basename: - #print "tackling", fn - newbasename = basename.replace(".", "_") - newfn = os.path.join(os.path.dirname(fn), newbasename) - if os.path.exists(newfn): - os.remove(newfn) - os.rename(fn, newfn) - newname = fn + ".cmd" - if os.path.exists(newname): - os.remove(newname) - f = open(newname, 'w') - f.write("@echo off\n") - f.write('python "%%~dp0\py_command_trampolin" %s %%*\n' % basename) - f.close() - w32path = Win32PathHandling() - w32path.remove_pylib_path() - -if sys.platform == "win32": - cmdclass = {'install_scripts': my_install_scripts} -else: - cmdclass = {} - if __name__ == '__main__': main() \ No newline at end of file From hpk at codespeak.net Thu Aug 21 12:53:05 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 12:53:05 +0200 (CEST) Subject: [py-svn] r57541 - py/dist Message-ID: <20080821105305.9548316A1A4@codespeak.net> Author: hpk Date: Thu Aug 21 12:53:02 2008 New Revision: 57541 Added: py/dist/ - copied from r57540, py/release/0.9.x/ Log: copying the latest 0.9.x release branch to dist. From hpk at codespeak.net Thu Aug 21 13:28:47 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 13:28:47 +0200 (CEST) Subject: [py-svn] r57542 - py/build Message-ID: <20080821112847.734F816A176@codespeak.net> Author: hpk Date: Thu Aug 21 13:28:43 2008 New Revision: 57542 Modified: py/build/_fixpythonpath.py py/build/gensetup.py py/build/update_website.py Log: snapshotting build helpers Modified: py/build/_fixpythonpath.py ============================================================================== --- py/build/_fixpythonpath.py (original) +++ py/build/_fixpythonpath.py Thu Aug 21 13:28:43 2008 @@ -5,6 +5,7 @@ if os.path.exists(os.path.join(p, 'py')): l.append(p) +l.insert(0, '.') index = 0 while not sys.path[index]: index += 1 Modified: py/build/gensetup.py ============================================================================== --- py/build/gensetup.py (original) +++ py/build/gensetup.py Thu Aug 21 13:28:43 2008 @@ -29,7 +29,8 @@ self.allpaths = [x for x in self.wcstatus.allpath() if x not in self.wcstatus.unknown and x not in self.wcstatus.external and - x not in self.wcstatus.ignored] + x not in self.wcstatus.ignored and + not self.isexcluded(x)] def append(self, string): lines = string.split("\n") @@ -38,6 +39,9 @@ lines.pop(0) continue break + if not lines: + self.lines.append("") + return line = lines[0] indent = len(line) - len(line.lstrip()) for line in lines: @@ -87,15 +91,21 @@ params.update(self.meta.__dict__) #params['url'] = self.wcinfo.url #params['rev'] = self.wcinfo.rev - params['long_description'] = reformat(params['long_description']) + #params['long_description'] = reformat(params['long_description']) #print py.std.pprint.pprint(params) + self.append('long_description = """') + for line in params['long_description'].split('\n'): + self.append(line) + self.append('"""') self.append(''' def main(): setup( name=%(name)r, description=%(description)r, + long_description = long_description, version=%(version)r, url=%(url)r, + download_url=%(download_url)r, license=%(license)r, platforms=%(platforms)r, author=%(author)r, @@ -107,7 +117,6 @@ #self.append_pprint(indent, py_modules=['_findpy',]), #self.append_pprint(indent, scripts=self.getscripts()) self.append_pprint(indent, entry_points={'console_scripts':self.getconsolescripts()}) - self.append_pprint(indent, long_description=params['long_description']) self.append_pprint(indent, classifiers=self.meta.classifiers) self.append_pprint(indent, packages=self.getpackages()) #self.append_pprint(indent, data_files=self.getdatafiles()) @@ -135,7 +144,7 @@ return l """ % ([script.basename for script in binpath.listdir("py.*")])) - def append_pprint(self, indent, **kw): + def append_pprint(self, indent, append=",", **kw): for name, value in kw.items(): stringio = py.std.StringIO.StringIO() value = py.std.pprint.pprint(value, stream=stringio) @@ -146,7 +155,7 @@ indent = indent + " " * (len(name)+1) for line in lines: self.lines.append(indent + line.rstrip()) - self.lines[-1] = self.lines[-1] + "," + self.lines[-1] = self.lines[-1] + append def getconsolescripts(self): bindir = self.wcbasedir.join('py', 'bin') @@ -155,7 +164,7 @@ if p.dirpath() == bindir: if p.basename.startswith('py.'): shortname = "py" + p.basename[3:] - scripts.append("%s = py.cmdline.%s:main" % + scripts.append("%s = py.cmdline:%s" % (p.basename, shortname)) return scripts @@ -246,6 +255,12 @@ targetfile.write("\n".join(self.lines)) print "wrote", targetfile + def isexcluded(self, wcpath): + return False + rel = wcpath.relto(self.wcbasedir) + if rel.find("testing") != -1: + return True + def write_manifest(self): lines = [] status = self.wcstatus Modified: py/build/update_website.py ============================================================================== --- py/build/update_website.py (original) +++ py/build/update_website.py Thu Aug 21 13:28:43 2008 @@ -7,6 +7,7 @@ """ import _fixpythonpath import py +print "using py lib", py.__file__ import sys def rsync(pkgpath, apidocspath, gateway, remotepath): @@ -45,7 +46,7 @@ return e.err or str(e) return None -def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=False): +def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=True): print 'running tests' errors = run_tests(pkgpath, apidocspath, args) if errors: @@ -77,7 +78,7 @@ print 'any additional arguments are passed on as-is to the py.test' print 'child process' sys.exit() - ignorefail = False + ignorefail = True if '--ignorefail' in args: args.remove('--ignorefail') ignorefail = True @@ -85,5 +86,5 @@ pkgpath = py.__pkg__.getpath() apidocspath = pkgpath.dirpath().join('apigen') main(pkgpath, apidocspath, 'codespeak.net', - '/home/guido/rsynctests', args, ignorefail) + 'rsynctests', args, ignorefail) From hpk at codespeak.net Thu Aug 21 14:00:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 14:00:09 +0200 (CEST) Subject: [py-svn] r57544 - py/trunk/py/execnet Message-ID: <20080821120009.D3AFB16A178@codespeak.net> Author: hpk Date: Thu Aug 21 14:00:08 2008 New Revision: 57544 Modified: py/trunk/py/execnet/gateway.py Log: still notify callbacks if we can't send to the other side anymore. Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Thu Aug 21 14:00:08 2008 @@ -136,8 +136,14 @@ self._traceex(exc_info()) break finally: + # XXX we need to signal fatal error states to + # channels/callbacks, particularly ones + # where the other side just died. self._stopexec() - self._stopsend() + try: + self._stopsend() + except IOError: + self._trace('IOError on _stopsend()') self._channelfactory._finished_receiving() self._trace('leaving %r' % threading.currentThread()) From hpk at codespeak.net Thu Aug 21 14:04:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 14:04:46 +0200 (CEST) Subject: [py-svn] r57546 - in py/trunk/py/execnet: . testing Message-ID: <20080821120446.A53F916A058@codespeak.net> Author: hpk Date: Thu Aug 21 14:04:43 2008 New Revision: 57546 Modified: py/trunk/py/execnet/channel.py py/trunk/py/execnet/testing/test_gateway.py Log: * channels now also provide makefile(mode) with mode = 'r' for providing file-like read/readline/close methods. * added and refined crash and finalization tests Modified: py/trunk/py/execnet/channel.py ============================================================================== --- py/trunk/py/execnet/channel.py (original) +++ py/trunk/py/execnet/channel.py Thu Aug 21 14:04:43 2008 @@ -103,13 +103,16 @@ return self._closed def makefile(self, mode='w', proxyclose=False): - """ return a file-like object. Only supported mode right - now is 'w' for binary writes. If you want to have - a subsequent file.close() mean to close the channel - as well, then pass proxyclose=True. + """ return a file-like object. + mode: 'w' for binary writes, 'r' for binary reads + proxyclose: set to true if you want to have a + subsequent file.close() automatically close the channel. """ - assert mode == 'w', "mode %r not availabe" %(mode,) - return ChannelFile(channel=self, proxyclose=proxyclose) + if mode == "w": + return ChannelFileWrite(channel=self, proxyclose=proxyclose) + elif mode == "r": + return ChannelFileRead(channel=self, proxyclose=proxyclose) + raise ValueError("mode %r not availabe" %(mode,)) def close(self, error=None): """ close down this channel on both sides. """ @@ -299,18 +302,11 @@ for id in self._callbacks.keys(): self._close_callback(id) - -class ChannelFile: +class ChannelFile(object): def __init__(self, channel, proxyclose=True): self.channel = channel self._proxyclose = proxyclose - def write(self, out): - self.channel.send(out) - - def flush(self): - pass - def close(self): if self._proxyclose: self.channel.close() @@ -319,3 +315,38 @@ state = self.channel.isclosed() and 'closed' or 'open' return '' %(self.channel.id, state) +class ChannelFileWrite(ChannelFile): + def write(self, out): + self.channel.send(out) + + def flush(self): + pass + +class ChannelFileRead(ChannelFile): + def __init__(self, channel, proxyclose=True): + super(ChannelFileRead, self).__init__(channel, proxyclose) + self._buffer = "" + + def read(self, n): + while len(self._buffer) < n: + try: + self._buffer += self.channel.receive() + except EOFError: + self.close() + break + ret = self._buffer[:n] + self._buffer = self._buffer[n:] + return ret + + def readline(self): + i = self._buffer.find("\n") + if i != -1: + return self.read(i+1) + line = self.read(len(self._buffer)+1) + while line and line[-1] != "\n": + c = self.read(1) + if not c: + break + line += c + return line + Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Thu Aug 21 14:04:43 2008 @@ -74,6 +74,11 @@ channel = self.fac.new() py.test.raises(IOError, channel.waitclose, timeout=0.01) + def test_channel_makefile_incompatmode(self): + channel = self.fac.new() + py.test.raises(ValueError, 'channel.makefile("rw")') + + class PopenGatewayTestSetup: def setup_class(cls): cls.gw = py.execnet.PopenGateway() @@ -291,6 +296,19 @@ assert isinstance(l[2], channel.__class__) assert l[3] == 999 + def test_channel_endmarker_callback_error(self): + from Queue import Queue + q = Queue() + channel = self.gw.remote_exec(source=''' + raise ValueError() + ''') + channel.setcallback(q.put, endmarker=999) + val = q.get(TESTTIMEOUT) + assert val == 999 + err = channel._getremoteerror() + assert err + assert str(err).find("ValueError") != -1 + def test_remote_redirect_stdout(self): out = py.std.StringIO.StringIO() handle = self.gw._remote_redirect(stdout=out) @@ -315,7 +333,7 @@ s = subl[0] assert s.strip() == str(i) - def test_channel_file(self): + def test_channel_file_write(self): channel = self.gw.remote_exec(""" f = channel.makefile() print >>f, "hello world" @@ -344,6 +362,43 @@ assert first.strip() == 'hello world' py.test.raises(EOFError, channel.receive) + def test_channel_file_read(self): + channel = self.gw.remote_exec(""" + f = channel.makefile(mode='r') + s = f.read(2) + channel.send(s) + s = f.read(5) + channel.send(s) + """) + channel.send("xyabcde") + s1 = channel.receive() + s2 = channel.receive() + assert s1 == "xy" + assert s2 == "abcde" + + def test_channel_file_read_empty(self): + channel = self.gw.remote_exec("pass") + f = channel.makefile(mode="r") + s = f.read(3) + assert s == "" + s = f.read(5) + assert s == "" + + def test_channel_file_readline_remote(self): + channel = self.gw.remote_exec(""" + channel.send('123\\n45') + """) + channel.waitclose(TESTTIMEOUT) + f = channel.makefile(mode="r") + s = f.readline() + assert s == "123\n" + s = f.readline() + assert s == "45" + + def test_channel_makefile_incompatmode(self): + channel = self.gw.newchannel() + py.test.raises(ValueError, 'channel.makefile("rw")') + def test_confusion_from_os_write_stdout(self): channel = self.gw.remote_exec(""" import os @@ -383,7 +438,26 @@ """) text = c1.receive() assert text.find("execution disallowed") != -1 - + + +def test_channel_endmarker_remote_killterm(): + gw = py.execnet.PopenGateway() + try: + from Queue import Queue + q = Queue() + channel = gw.remote_exec(source=''' + import os + os.kill(os.getpid(), 15) + ''') + channel.setcallback(q.put, endmarker=999) + val = q.get(TESTTIMEOUT) + assert val == 999 + err = channel._getremoteerror() + finally: + gw.exit() + py.test.skip("provide information on causes/signals " + "of dying remote gateways") + #class TestBlockingIssues: # def test_join_blocked_execution_gateway(self): # gateway = py.execnet.PopenGateway() @@ -437,29 +511,44 @@ ret = channel.receive() assert ret == 42 - def disabled_test_remote_is_killed_while_sending(self): + def test_waitclose_on_remote_killed(self): + py.test.skip("fix needed: dying remote process does not cause waitclose() to fail") + if not hasattr(py.std.os, 'kill'): + py.test.skip("no os.kill") gw = py.execnet.PopenGateway() channel = gw.remote_exec(""" import os import time - channel.send(os.getppid()) channel.send(os.getpid()) while 1: - channel.send('#'*1000) - time.sleep(10) + channel.send("#" * 100) """) - parent = channel.receive() - remote = channel.receive() - assert parent == os.getpid() - time.sleep(0.5) - os.kill(remote, signal.SIGKILL) - time.sleep(1) - channel.waitclose(TESTTIMEOUT) + remotepid = channel.receive() + os.kill(remotepid, 9) + py.test.raises(channel.RemoteError, "channel.waitclose(TESTTIMEOUT)") + py.test.raises(EOFError, channel.send, None) py.test.raises(EOFError, channel.receive) - #channel.waitclose(TESTTIMEOUT) - #channel.send('#') - - + +def test_endmarker_delivery_on_remote_killterm(): + if not hasattr(py.std.os, 'kill'): + py.test.skip("no os.kill()") + gw = py.execnet.PopenGateway() + try: + from Queue import Queue + q = Queue() + channel = gw.remote_exec(source=''' + import os + os.kill(os.getpid(), 15) + ''') + channel.setcallback(q.put, endmarker=999) + val = q.get(TESTTIMEOUT) + assert val == 999 + err = channel._getremoteerror() + finally: + gw.exit() + py.test.skip("provide information on causes/signals " + "of dying remote gateways") + class SocketGatewaySetup: def setup_class(cls): @@ -516,4 +605,7 @@ py.test.raises(IOError, gw.remote_init_threads, 3) gw.exit() - + +def test_nodebug(): + from py.__.execnet import gateway + assert not gateway.debug From hpk at codespeak.net Thu Aug 21 14:12:22 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 14:12:22 +0200 (CEST) Subject: [py-svn] r57548 - in py/trunk: . py py/cmdline py/io py/io/testing py/process py/process/testing py/test Message-ID: <20080821121222.AB14516A18C@codespeak.net> Author: hpk Date: Thu Aug 21 14:12:20 2008 New Revision: 57548 Added: py/trunk/py/process/forkedfunc.py - copied unchanged from r57543, py/trunk/py/io/forkedfunc.py py/trunk/py/process/testing/test_forkedfunc.py - copied, changed from r57543, py/trunk/py/io/testing/test_forkedfunc.py Removed: py/trunk/py/io/forkedfunc.py py/trunk/py/io/testing/test_forkedfunc.py Modified: py/trunk/CHANGELOG py/trunk/py/__init__.py py/trunk/py/cmdline/pylookup.py py/trunk/py/test/runner.py Log: * introduce py.process.ForkedFunc object (previously lived at py.io and before that py.test) * avoid deprecated py/misc/terminal_helper.py * starting changelog for 1.0 Modified: py/trunk/CHANGELOG ============================================================================== --- py/trunk/CHANGELOG (original) +++ py/trunk/CHANGELOG Thu Aug 21 14:12:20 2008 @@ -1,5 +1,15 @@ $Id$ +Changes between 0.9.2 and 1.0 (UNRELEASED) +============================================= + +* revised internal py.test architecture +* new py.process.ForkedFunc object allowing to + fork execution of a function to a sub process + and getting a result back. + +XXX lots of things missing here XXX + Changes between 0.9.1 and 0.9.2 =============================== Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Thu Aug 21 14:12:20 2008 @@ -95,6 +95,7 @@ 'process.__doc__' : ('./process/__init__.py', '__doc__'), 'process.cmdexec' : ('./process/cmdexec.py', 'cmdexec'), + 'process.ForkedFunc' : ('./process/forkedfunc.py', 'ForkedFunc'), # path implementation 'path.__doc__' : ('./path/__init__.py', '__doc__'), @@ -148,7 +149,6 @@ 'io.StdCapture' : ('./io/stdcapture.py', 'StdCapture'), 'io.StdCaptureFD' : ('./io/stdcapture.py', 'StdCaptureFD'), 'io.TerminalWriter' : ('./io/terminalwriter.py', 'TerminalWriter'), - 'io.ForkedFunc' : ('./io/forkedfunc.py', 'ForkedFunc'), # error module, defining all errno's as Classes 'error' : ('./misc/error.py', 'error'), Modified: py/trunk/py/cmdline/pylookup.py ============================================================================== --- py/trunk/py/cmdline/pylookup.py (original) +++ py/trunk/py/cmdline/pylookup.py Thu Aug 21 14:12:20 2008 @@ -9,7 +9,7 @@ import sys, os import py -from py.__.misc.terminal_helper import ansi_print, terminal_width +from py.__.io.terminalwriter import re curdir = py.path.local() Deleted: /py/trunk/py/io/forkedfunc.py ============================================================================== --- /py/trunk/py/io/forkedfunc.py Thu Aug 21 14:12:20 2008 +++ (empty file) @@ -1,108 +0,0 @@ - -""" - ForkedFunc provides a way to run a function in a forked process - and get at its return value, stdout and stderr output as well - as signals and exitstatusus. - - XXX see if tempdir handling is sane -""" - -import py -import os -import sys -import marshal - -class ForkedFunc(object): - EXITSTATUS_EXCEPTION = 3 - def __init__(self, fun, args=None, kwargs=None, nice_level=0): - if args is None: - args = [] - if kwargs is None: - kwargs = {} - self.fun = fun - self.args = args - self.kwargs = kwargs - self.tempdir = tempdir = py.path.local.mkdtemp() - self.RETVAL = tempdir.ensure('retval') - self.STDOUT = tempdir.ensure('stdout') - self.STDERR = tempdir.ensure('stderr') - - pid = os.fork() - if pid: # in parent process - self.pid = pid - else: # in child process - self._child(nice_level) - - def _child(self, nice_level): - # right now we need to call a function, but first we need to - # map all IO that might happen - # make sure sys.stdout points to file descriptor one - sys.stdout = stdout = self.STDOUT.open('w') - sys.stdout.flush() - fdstdout = stdout.fileno() - if fdstdout != 1: - os.dup2(fdstdout, 1) - sys.stderr = stderr = self.STDERR.open('w') - fdstderr = stderr.fileno() - if fdstderr != 2: - os.dup2(fdstderr, 2) - retvalf = self.RETVAL.open("w") - EXITSTATUS = 0 - try: - if nice_level: - os.nice(nice_level) - try: - retval = self.fun(*self.args, **self.kwargs) - retvalf.write(marshal.dumps(retval)) - except: - excinfo = py.code.ExceptionInfo() - stderr.write(excinfo.exconly()) - EXITSTATUS = self.EXITSTATUS_EXCEPTION - finally: - stdout.close() - stderr.close() - retvalf.close() - os.close(1) - os.close(2) - os._exit(EXITSTATUS) - - def waitfinish(self, waiter=os.waitpid): - pid, systemstatus = waiter(self.pid, 0) - if systemstatus: - if os.WIFSIGNALED(systemstatus): - exitstatus = os.WTERMSIG(systemstatus) + 128 - else: - exitstatus = os.WEXITSTATUS(systemstatus) - #raise ExecutionFailed(status, systemstatus, cmd, - # ''.join(out), ''.join(err)) - else: - exitstatus = 0 - signal = systemstatus & 0x7f - if not exitstatus and not signal: - retval = self.RETVAL.open() - try: - retval_data = retval.read() - finally: - retval.close() - retval = marshal.loads(retval_data) - else: - retval = None - stdout = self.STDOUT.read() - stderr = self.STDERR.read() - self._removetemp() - return Result(exitstatus, signal, retval, stdout, stderr) - - def _removetemp(self): - if self.tempdir.check(): - self.tempdir.remove() - - def __del__(self): - self._removetemp() - -class Result(object): - def __init__(self, exitstatus, signal, retval, stdout, stderr): - self.exitstatus = exitstatus - self.signal = signal - self.retval = retval - self.out = stdout - self.err = stderr Deleted: /py/trunk/py/io/testing/test_forkedfunc.py ============================================================================== --- /py/trunk/py/io/testing/test_forkedfunc.py Thu Aug 21 14:12:20 2008 +++ (empty file) @@ -1,138 +0,0 @@ -import py, sys, os - -def setup_module(mod): - if not hasattr(os, 'fork'): - py.test.skip("forkedfunc requires os.fork") - mod.tmpdir = py.test.ensuretemp(mod.__file__) - -def test_waitfinish_removes_tempdir(): - ff = py.io.ForkedFunc(boxf1) - assert ff.tempdir.check() - ff.waitfinish() - assert not ff.tempdir.check() - -def test_tempdir_gets_gc_collected(): - ff = py.io.ForkedFunc(boxf1) - assert ff.tempdir.check() - ff.__del__() - assert not ff.tempdir.check() - os.waitpid(ff.pid, 0) - -def test_basic_forkedfunc(): - result = py.io.ForkedFunc(boxf1).waitfinish() - assert result.out == "some out\n" - assert result.err == "some err\n" - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 1 - -def test_exitstatus(): - def func(): - os._exit(4) - result = py.io.ForkedFunc(func).waitfinish() - assert result.exitstatus == 4 - assert result.signal == 0 - assert not result.out - assert not result.err - -def test_execption_in_func(): - def fun(): - raise ValueError(42) - ff = py.io.ForkedFunc(fun) - result = ff.waitfinish() - assert result.exitstatus == ff.EXITSTATUS_EXCEPTION - assert result.err.find("ValueError: 42") != -1 - assert result.signal == 0 - assert not result.retval - -def test_forkedfunc_on_fds(): - result = py.io.ForkedFunc(boxf2).waitfinish() - assert result.out == "someout" - assert result.err == "someerr" - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 2 - -def test_forkedfunc_signal(): - result = py.io.ForkedFunc(boxseg).waitfinish() - assert result.retval is None - if py.std.sys.version_info < (2,4): - py.test.skip("signal detection does not work with python prior 2.4") - assert result.signal == 11 - -def test_forkedfunc_huge_data(): - result = py.io.ForkedFunc(boxhuge).waitfinish() - assert result.out - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 3 - -def test_box_seq(): - # we run many boxes with huge data, just one after another - for i in xrange(50): - result = py.io.ForkedFunc(boxhuge).waitfinish() - assert result.out - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 3 - -def test_box_in_a_box(): - def boxfun(): - result = py.io.ForkedFunc(boxf2).waitfinish() - print result.out - print >>sys.stderr, result.err - return result.retval - - result = py.io.ForkedFunc(boxfun).waitfinish() - assert result.out == "someout\n" - assert result.err == "someerr\n" - assert result.exitstatus == 0 - assert result.signal == 0 - assert result.retval == 2 - -def test_kill_func_forked(): - class A: - pass - info = A() - import time - - def box_fun(): - time.sleep(10) # we don't want to last forever here - - ff = py.io.ForkedFunc(box_fun) - os.kill(ff.pid, 15) - result = ff.waitfinish() - if py.std.sys.version_info < (2,4): - py.test.skip("signal detection does not work with python prior 2.4") - assert result.signal == 15 - - - -# ====================================================================== -# examples -# ====================================================================== -# - -def boxf1(): - print "some out" - print >>sys.stderr, "some err" - return 1 - -def boxf2(): - os.write(1, "someout") - os.write(2, "someerr") - return 2 - -def boxseg(): - os.kill(os.getpid(), 11) - -def boxhuge(): - os.write(1, " " * 10000) - os.write(2, " " * 10000) - os.write(1, " " * 10000) - - os.write(1, " " * 10000) - os.write(2, " " * 10000) - os.write(2, " " * 10000) - os.write(1, " " * 10000) - return 3 Copied: py/trunk/py/process/testing/test_forkedfunc.py (from r57543, py/trunk/py/io/testing/test_forkedfunc.py) ============================================================================== --- py/trunk/py/io/testing/test_forkedfunc.py (original) +++ py/trunk/py/process/testing/test_forkedfunc.py Thu Aug 21 14:12:20 2008 @@ -6,20 +6,20 @@ mod.tmpdir = py.test.ensuretemp(mod.__file__) def test_waitfinish_removes_tempdir(): - ff = py.io.ForkedFunc(boxf1) + ff = py.process.ForkedFunc(boxf1) assert ff.tempdir.check() ff.waitfinish() assert not ff.tempdir.check() def test_tempdir_gets_gc_collected(): - ff = py.io.ForkedFunc(boxf1) + ff = py.process.ForkedFunc(boxf1) assert ff.tempdir.check() ff.__del__() assert not ff.tempdir.check() os.waitpid(ff.pid, 0) def test_basic_forkedfunc(): - result = py.io.ForkedFunc(boxf1).waitfinish() + result = py.process.ForkedFunc(boxf1).waitfinish() assert result.out == "some out\n" assert result.err == "some err\n" assert result.exitstatus == 0 @@ -29,7 +29,7 @@ def test_exitstatus(): def func(): os._exit(4) - result = py.io.ForkedFunc(func).waitfinish() + result = py.process.ForkedFunc(func).waitfinish() assert result.exitstatus == 4 assert result.signal == 0 assert not result.out @@ -38,7 +38,7 @@ def test_execption_in_func(): def fun(): raise ValueError(42) - ff = py.io.ForkedFunc(fun) + ff = py.process.ForkedFunc(fun) result = ff.waitfinish() assert result.exitstatus == ff.EXITSTATUS_EXCEPTION assert result.err.find("ValueError: 42") != -1 @@ -46,7 +46,7 @@ assert not result.retval def test_forkedfunc_on_fds(): - result = py.io.ForkedFunc(boxf2).waitfinish() + result = py.process.ForkedFunc(boxf2).waitfinish() assert result.out == "someout" assert result.err == "someerr" assert result.exitstatus == 0 @@ -54,14 +54,14 @@ assert result.retval == 2 def test_forkedfunc_signal(): - result = py.io.ForkedFunc(boxseg).waitfinish() + result = py.process.ForkedFunc(boxseg).waitfinish() assert result.retval is None if py.std.sys.version_info < (2,4): py.test.skip("signal detection does not work with python prior 2.4") assert result.signal == 11 def test_forkedfunc_huge_data(): - result = py.io.ForkedFunc(boxhuge).waitfinish() + result = py.process.ForkedFunc(boxhuge).waitfinish() assert result.out assert result.exitstatus == 0 assert result.signal == 0 @@ -70,7 +70,7 @@ def test_box_seq(): # we run many boxes with huge data, just one after another for i in xrange(50): - result = py.io.ForkedFunc(boxhuge).waitfinish() + result = py.process.ForkedFunc(boxhuge).waitfinish() assert result.out assert result.exitstatus == 0 assert result.signal == 0 @@ -78,12 +78,12 @@ def test_box_in_a_box(): def boxfun(): - result = py.io.ForkedFunc(boxf2).waitfinish() + result = py.process.ForkedFunc(boxf2).waitfinish() print result.out print >>sys.stderr, result.err return result.retval - result = py.io.ForkedFunc(boxfun).waitfinish() + result = py.process.ForkedFunc(boxfun).waitfinish() assert result.out == "someout\n" assert result.err == "someerr\n" assert result.exitstatus == 0 @@ -99,7 +99,7 @@ def box_fun(): time.sleep(10) # we don't want to last forever here - ff = py.io.ForkedFunc(box_fun) + ff = py.process.ForkedFunc(box_fun) os.kill(ff.pid, 15) result = ff.waitfinish() if py.std.sys.version_info < (2,4): Modified: py/trunk/py/test/runner.py ============================================================================== --- py/trunk/py/test/runner.py (original) +++ py/trunk/py/test/runner.py Thu Aug 21 14:12:20 2008 @@ -127,7 +127,7 @@ os._exit(EXITSTATUS_TESTEXIT) return ipickle.dumps(testrep) - ff = py.io.ForkedFunc(runforked) + ff = py.process.ForkedFunc(runforked) result = ff.waitfinish() if result.retval is not None: return ipickle.loads(result.retval) From hpk at codespeak.net Thu Aug 21 15:12:31 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 15:12:31 +0200 (CEST) Subject: [py-svn] r57550 - in py/trunk: . py/cmdline Message-ID: <20080821131231.022E616A0DB@codespeak.net> Author: hpk Date: Thu Aug 21 15:12:31 2008 New Revision: 57550 Removed: py/trunk/setup.cfg Modified: py/trunk/py/cmdline/pylookup.py Log: * fixing syntax error * removing setup.cfg Modified: py/trunk/py/cmdline/pylookup.py ============================================================================== --- py/trunk/py/cmdline/pylookup.py (original) +++ py/trunk/py/cmdline/pylookup.py Thu Aug 21 15:12:31 2008 @@ -9,7 +9,7 @@ import sys, os import py -from py.__.io.terminalwriter +from py.__.io.terminalwriter import ansi_print, terminal_width import re curdir = py.path.local() Deleted: /py/trunk/setup.cfg ============================================================================== --- /py/trunk/setup.cfg Thu Aug 21 15:12:31 2008 +++ (empty file) @@ -1,3 +0,0 @@ -[egg_info] -tag_build = .dev -tag_svn_revision = 1 From hpk at codespeak.net Thu Aug 21 15:25:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 15:25:29 +0200 (CEST) Subject: [py-svn] r57551 - in py/trunk/py: . bin bin/win32 cmdline cmdline/testing Message-ID: <20080821132529.E4D3816A1A8@codespeak.net> Author: hpk Date: Thu Aug 21 15:25:29 2008 New Revision: 57551 Added: py/trunk/py/bin/win32/py.which.cmd py/trunk/py/cmdline/pywhich.py - copied, changed from r57550, py/trunk/py/bin/py.which Modified: py/trunk/py/__init__.py py/trunk/py/bin/py.which py/trunk/py/cmdline/testing/test_generic.py Log: normalizing py.which cmdline script Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Thu Aug 21 15:25:29 2008 @@ -59,6 +59,7 @@ 'cmdline.pylookup' : ('./cmdline/pylookup.py', 'main',), 'cmdline.pycountloc' : ('./cmdline/pycountloc.py', 'main',), 'cmdline.pycleanup' : ('./cmdline/pycleanup.py', 'main',), + 'cmdline.pywhich' : ('./cmdline/pywhich.py', 'main',), # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), Modified: py/trunk/py/bin/py.which ============================================================================== --- py/trunk/py/bin/py.which (original) +++ py/trunk/py/bin/py.which Thu Aug 21 15:25:29 2008 @@ -1,23 +1,3 @@ -#!/usr/bin/env python - -"""\ -py.which [name] - -print the location of the given python module or package name -""" - -import sys - -if __name__ == '__main__': - name = sys.argv[1] - try: - mod = __import__(name) - except ImportError: - print >>sys.stderr, "could not import:", name - else: - try: - location = mod.__file__ - except AttributeError: - print >>sys.stderr, "module (has no __file__):", mod - else: - print location +#!/usr/bin/env python +from _findpy import py +py.cmdline.pywhich() \ No newline at end of file Added: py/trunk/py/bin/win32/py.which.cmd ============================================================================== --- (empty file) +++ py/trunk/py/bin/win32/py.which.cmd Thu Aug 21 15:25:29 2008 @@ -0,0 +1,2 @@ + at echo off +python "%~dp0\..\py.which" %* \ No newline at end of file Copied: py/trunk/py/cmdline/pywhich.py (from r57550, py/trunk/py/bin/py.which) ============================================================================== --- py/trunk/py/bin/py.which (original) +++ py/trunk/py/cmdline/pywhich.py Thu Aug 21 15:25:29 2008 @@ -8,7 +8,7 @@ import sys -if __name__ == '__main__': +def main(): name = sys.argv[1] try: mod = __import__(name) Modified: py/trunk/py/cmdline/testing/test_generic.py ============================================================================== --- py/trunk/py/cmdline/testing/test_generic.py (original) +++ py/trunk/py/cmdline/testing/test_generic.py Thu Aug 21 15:25:29 2008 @@ -25,8 +25,9 @@ else: cmd = "%s" %(script, ) - if script.basename.startswith("py.lookup"): - cmd += " hello" + if script.basename.startswith("py.lookup") or \ + script.basename.startswith("py.which"): + cmd += " sys" print "executing", script try: py.process.cmdexec(cmd) From hpk at codespeak.net Thu Aug 21 15:26:34 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 15:26:34 +0200 (CEST) Subject: [py-svn] r57552 - py/trunk Message-ID: <20080821132634.4FF5A16A1A8@codespeak.net> Author: hpk Date: Thu Aug 21 15:26:31 2008 New Revision: 57552 Modified: py/trunk/MANIFEST py/trunk/setup.py Log: regen setup.py Modified: py/trunk/MANIFEST ============================================================================== --- py/trunk/MANIFEST (original) +++ py/trunk/MANIFEST Thu Aug 21 15:26:31 2008 @@ -2,7 +2,6 @@ LICENSE MANIFEST README.txt -_findpy.py ez_setup.py py/LICENSE py/__init__.py @@ -75,6 +74,7 @@ py/bin/win32/py.lookup.cmd py/bin/win32/py.rest.cmd py/bin/win32/py.test.cmd +py/bin/win32/py.which.cmd py/builtin/__init__.py py/builtin/enumerate.py py/builtin/exception.py @@ -113,6 +113,7 @@ py/cmdline/pylookup.py py/cmdline/pyrest.py py/cmdline/pytest.py +py/cmdline/pywhich.py py/cmdline/testing/__init__.py py/cmdline/testing/test_generic.py py/code/__init__.py @@ -226,13 +227,11 @@ py/io/__init__.py py/io/dupfile.py py/io/fdcapture.py -py/io/forkedfunc.py py/io/stdcapture.py py/io/terminalwriter.py py/io/testing/__init__.py py/io/testing/test_dupfile.py py/io/testing/test_fdcapture.py -py/io/testing/test_forkedfunc.py py/io/testing/test_stdcapture.py py/io/testing/test_terminalwriter.py py/log/__init__.py @@ -318,8 +317,10 @@ py/path/testing/test_api.py py/process/__init__.py py/process/cmdexec.py +py/process/forkedfunc.py py/process/testing/__init__.py py/process/testing/test_cmdexec.py +py/process/testing/test_forkedfunc.py py/rest/__init__.py py/rest/convert.py py/rest/directive.py @@ -434,5 +435,4 @@ py/xmlobj/testing/test_xml.py py/xmlobj/visit.py py/xmlobj/xml.py -setup.cfg setup.py \ No newline at end of file Modified: py/trunk/setup.py ============================================================================== --- py/trunk/setup.py (original) +++ py/trunk/setup.py Thu Aug 21 15:26:31 2008 @@ -1,7 +1,7 @@ """ setup file for 'py' package based on: - https://codespeak.net/svn/py/trunk, revision=57536 + https://codespeak.net/svn/py/trunk, revision=57550 autogenerated by gensetup.py """ @@ -152,6 +152,7 @@ 'bin/win32/py.lookup.cmd', 'bin/win32/py.rest.cmd', 'bin/win32/py.test.cmd', + 'bin/win32/py.which.cmd', 'c-extension/greenlet/README.txt', 'c-extension/greenlet/dummy_greenlet.py', 'c-extension/greenlet/greenlet.c', From hpk at codespeak.net Thu Aug 21 15:43:47 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 15:43:47 +0200 (CEST) Subject: [py-svn] r57554 - in py/trunk/py/magic: . testing Message-ID: <20080821134347.C9AC016842B@codespeak.net> Author: hpk Date: Thu Aug 21 15:43:45 2008 New Revision: 57554 Modified: py/trunk/py/magic/assertion.py py/trunk/py/magic/testing/test_assertion.py Log: fix case where argument to AssertionError has broken __repr__ Modified: py/trunk/py/magic/assertion.py ============================================================================== --- py/trunk/py/magic/assertion.py (original) +++ py/trunk/py/magic/assertion.py Thu Aug 21 15:43:45 2008 @@ -8,7 +8,14 @@ def __init__(self, *args): BuiltinAssertionError.__init__(self, *args) if args: - self.msg = str(args[0]) + try: + self.msg = str(args[0]) + except (KeyboardInterrupt, SystemExit): + raise + except: + self.msg = "<[broken __repr__] %s at %0xd>" %( + args[0].__class__, id(args[0])) + else: f = sys._getframe(1) try: Modified: py/trunk/py/magic/testing/test_assertion.py ============================================================================== --- py/trunk/py/magic/testing/test_assertion.py (original) +++ py/trunk/py/magic/testing/test_assertion.py Thu Aug 21 15:43:45 2008 @@ -96,3 +96,11 @@ except AssertionError, e: assert e.msg.find('assert [1, 2, 3] !=') != -1 + +def test_assert_with_brokenrepr_arg(): + class BrokenRepr: + def __repr__(self): 0 / 0 + e = AssertionError(BrokenRepr()) + if e.msg.find("broken __repr__") == -1: + py.test.fail("broken __repr__ not handle correctly") + From hpk at codespeak.net Thu Aug 21 15:53:21 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 15:53:21 +0200 (CEST) Subject: [py-svn] r57556 - py/trunk/py/test Message-ID: <20080821135321.53FBF16A164@codespeak.net> Author: hpk Date: Thu Aug 21 15:53:19 2008 New Revision: 57556 Modified: py/trunk/py/test/pycollect.py Log: remove special Function __repr__ Modified: py/trunk/py/test/pycollect.py ============================================================================== --- py/trunk/py/test/pycollect.py (original) +++ py/trunk/py/test/pycollect.py Thu Aug 21 15:53:19 2008 @@ -334,9 +334,6 @@ def __ne__(self, other): return not self == other - def __repr__(self): - return ""%(self.name, id(self)) - class DoctestFile(FSCollector): def listdir(self): return [self.fspath.basename] From hpk at codespeak.net Thu Aug 21 16:25:26 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 16:25:26 +0200 (CEST) Subject: [py-svn] r57557 - in py/trunk/py/cmdline: . testing Message-ID: <20080821142526.3A54016A176@codespeak.net> Author: hpk Date: Thu Aug 21 16:25:24 2008 New Revision: 57557 Added: py/trunk/py/cmdline/testing/test_cmdline.py Modified: py/trunk/py/cmdline/pylookup.py Log: add lookup in filenames and test machinery. Modified: py/trunk/py/cmdline/pylookup.py ============================================================================== --- py/trunk/py/cmdline/pylookup.py (original) +++ py/trunk/py/cmdline/pylookup.py Thu Aug 21 16:25:24 2008 @@ -41,6 +41,13 @@ if options.ignorecase: string = string.lower() for x in curdir.visit('*.py', rec): + # match filename directly + s = x.relto(curdir) + if options.ignorecase: + s = s.lower() + if s.find(string) != -1: + print >>sys.stdout, "%s: filename matches %r" %(x, string) + try: s = x.read() except py.error.ENOENT: Added: py/trunk/py/cmdline/testing/test_cmdline.py ============================================================================== --- (empty file) +++ py/trunk/py/cmdline/testing/test_cmdline.py Thu Aug 21 16:25:24 2008 @@ -0,0 +1,18 @@ +from py.__.test.testing import suptest +from py.__.test.testing.acceptance_test import AcceptBase + +class TestPyLookup(AcceptBase): + def test_basic(self): + p = self.makepyfile(hello="def x(): pass") + result = self.run("py.lookup", "pass") + suptest.assert_lines_contain_lines(result.outlines, + ['%s:*def x(): pass' %(p.basename)] + ) + + def test_search_in_filename(self): + p = self.makepyfile(hello="def x(): pass") + result = self.run("py.lookup", "hello") + suptest.assert_lines_contain_lines(result.outlines, + ['*%s:*' %(p.basename)] + ) + From hpk at codespeak.net Thu Aug 21 16:26:30 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 16:26:30 +0200 (CEST) Subject: [py-svn] r57558 - py/trunk/py/test/testing Message-ID: <20080821142630.2950616A185@codespeak.net> Author: hpk Date: Thu Aug 21 16:26:27 2008 New Revision: 57558 Modified: py/trunk/py/test/testing/acceptance_test.py Log: should have come with 57557 - extend "acceptance" test machinery. Modified: py/trunk/py/test/testing/acceptance_test.py ============================================================================== --- py/trunk/py/test/testing/acceptance_test.py (original) +++ py/trunk/py/test/testing/acceptance_test.py Thu Aug 21 16:26:27 2008 @@ -15,14 +15,12 @@ self.errlines = errlines class AcceptBase(FileCreation): - def popen(self, cmdargs, stdout, stderr): + def popen(self, cmdargs, stdout, stderr, **kw): if not hasattr(py.std, 'subprocess'): py.test.skip("no subprocess module") - return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr) + return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) - def runpytest(self, *args): - pytestcmd = py.path.local(py.__file__).dirpath("bin", "py.test") - cmdargs = [py.std.sys.executable, pytestcmd] + list(args) + def run(self, *cmdargs): cmdargs = map(str, cmdargs) p1 = py.path.local("stdout") p2 = py.path.local("stderr") @@ -35,6 +33,18 @@ print >>py.std.sys.stderr, line return Result(ret, out, err) + def runpybin(self, scriptname, *args): + bindir = py.path.local(py.__file__).dirpath("bin") + if py.std.sys.platform == "win32": + script = bindir.join("win32", scriptname + ".cmd") + else: + script = bindir.join(scriptname) + assert script.check() + return self.run(script, *args) + + def runpytest(self, *args): + return self.runpybin("py.test", *args) + def setup_method(self, method): super(AcceptBase, self).setup_method(method) self.old = self.tmpdir.chdir() From hpk at codespeak.net Thu Aug 21 19:25:51 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 19:25:51 +0200 (CEST) Subject: [py-svn] r57564 - in py/trunk/py/test: . testing Message-ID: <20080821172551.3FEF816A127@codespeak.net> Author: hpk Date: Thu Aug 21 19:25:48 2008 New Revision: 57564 Modified: py/trunk/py/test/config.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/testing/test_config.py Log: adding an option for setting a tracedirectory so that components can write log files, depending on what they get from config.gettracedir() Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Thu Aug 21 19:25:48 2008 @@ -220,6 +220,12 @@ else: raise ValueError("unknown io capturing: " + iocapture) + + def gettracedir(self): + """ return a tracedirectory or None, depending on --tracedir. """ + if self.option.tracedir is not None: + return py.path.local(self.option.tracedir) + # this is the one per-process instance of py.test configuration config_per_process = Config() Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Thu Aug 21 19:25:48 2008 @@ -55,6 +55,9 @@ Option('', '--eventlog', action="store", dest="eventlog", default=None, help="write reporting events to given file."), + Option('', '--tracedir', + action="store", dest="tracedir", default=None, + help="write tracing information to the given directory."), Option('', '--tb', action="store", dest="tbstyle", default='long', type="choice", choices=['long', 'short', 'no'], Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Thu Aug 21 19:25:48 2008 @@ -201,6 +201,14 @@ s = eventlog.read() assert s.find("TestrunStart") != -1 + def test_tracedir(self): + tracedir = self.tmpdir.mkdir("tracedir") + config = py.test.config._reparse([self.tmpdir, + '--tracedir=%s' % tracedir]) + assert config.gettracedir() == tracedir + config = py.test.config._reparse([self.tmpdir]) + assert config.gettracedir() is None + def test_implied_dsession(self): for x in 'startserver runbrowser rest'.split(): config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x]) From hpk at codespeak.net Thu Aug 21 19:39:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 19:39:35 +0200 (CEST) Subject: [py-svn] r57565 - in py/trunk/py/test: . testing Message-ID: <20080821173935.606DB16A0DA@codespeak.net> Author: hpk Date: Thu Aug 21 19:39:34 2008 New Revision: 57565 Modified: py/trunk/py/test/config.py py/trunk/py/test/testing/test_config.py Log: also introduce config.maketrace(name, flush=False) which returns either a nulltracer or opens a log in the tracedir and returns an object that you can call with args to print into the file. Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Thu Aug 21 19:39:34 2008 @@ -226,6 +226,33 @@ if self.option.tracedir is not None: return py.path.local(self.option.tracedir) + def maketrace(self, name, flush=False): + """ return a tracedirectory or None, depending on --tracedir. """ + tracedir = self.gettracedir() + if tracedir is None: + return NullTracer() + return Tracer(tracedir.join(name), flush=flush) + +class Tracer(object): + file = None + def __init__(self, path, flush=False): + self.file = path.open(mode='w') + self.flush = flush + + def __call__(self, *args): + print >>self.file, " ".join(map(str, args)) + if self.flush: + self.file.flush() + + def close(self): + self.file.close() + +class NullTracer: + def __call__(self, *args): + pass + def close(self): + pass + # this is the one per-process instance of py.test configuration config_per_process = Config() Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Thu Aug 21 19:39:34 2008 @@ -201,13 +201,28 @@ s = eventlog.read() assert s.find("TestrunStart") != -1 - def test_tracedir(self): + def test_tracedir_tracer(self): tracedir = self.tmpdir.mkdir("tracedir") config = py.test.config._reparse([self.tmpdir, '--tracedir=%s' % tracedir]) assert config.gettracedir() == tracedir + + trace = config.maketrace("trace1.log", flush=True) + trace("hello", "world") + class A: pass + trace(A()) + p = tracedir.join("trace1.log") + lines = p.readlines(cr=0) + assert lines[0] == "hello world" + assert lines[1].find("A") != -1 + trace.close() + + def test_trace_null(self): config = py.test.config._reparse([self.tmpdir]) assert config.gettracedir() is None + trace = config.maketrace("hello", flush=True) + trace("hello", "world") + trace.close() def test_implied_dsession(self): for x in 'startserver runbrowser rest'.split(): From hpk at codespeak.net Thu Aug 21 22:31:37 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 22:31:37 +0200 (CEST) Subject: [py-svn] r57568 - py/build Message-ID: <20080821203137.74C9A16A182@codespeak.net> Author: hpk Date: Thu Aug 21 22:31:34 2008 New Revision: 57568 Added: py/build/builddocs.py Log: committing build-doc script from guido Added: py/build/builddocs.py ============================================================================== --- (empty file) +++ py/build/builddocs.py Thu Aug 21 22:31:34 2008 @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +""" build the 'py' documentation and api docs in a directory 'html' + + this script runs py.test from the nearest found py lib, and creates the + documents (provided that those doc tests are ran) and apigen docs along + the way + + the directory with documents gets placed in the current working directory, + command line arguments get passed to py.test +""" + +import py +import sys +import os + +pypath = py.__.__path__[0] +here = py.magic.autopath().dirpath() +print "pypath", pypath +path = here.join('html') +args = sys.argv[1:] +if not args: + args = [str(pypath)] + +os.system('DOCPATH="%s" APIGENPATH="%s/apigen" %s/bin/py.test ' + '--apigen=%s/apigen/apigen.py %s' % ( + path, path, pypath, pypath, ' '.join(args))) From hpk at codespeak.net Thu Aug 21 22:34:34 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 21 Aug 2008 22:34:34 +0200 (CEST) Subject: [py-svn] r57569 - py/build Message-ID: <20080821203434.2846516A1BD@codespeak.net> Author: hpk Date: Thu Aug 21 22:34:32 2008 New Revision: 57569 Modified: py/build/builddocs.py Log: a few tweaks. printing of source and target. defaulting html dir to curdir + 'html' Modified: py/build/builddocs.py ============================================================================== --- py/build/builddocs.py (original) +++ py/build/builddocs.py Thu Aug 21 22:34:32 2008 @@ -10,18 +10,21 @@ command line arguments get passed to py.test """ -import py import sys +sys.path.insert(0, '.') + +import py import os -pypath = py.__.__path__[0] -here = py.magic.autopath().dirpath() -print "pypath", pypath -path = here.join('html') +pypath = py.path.local(py.__file__).dirpath() +htmldir = py.path.local('html') args = sys.argv[1:] if not args: args = [str(pypath)] +print "pypath", pypath +print "generating docs into", htmldir + os.system('DOCPATH="%s" APIGENPATH="%s/apigen" %s/bin/py.test ' '--apigen=%s/apigen/apigen.py %s' % ( - path, path, pypath, pypath, ' '.join(args))) + htmldir, htmldir, pypath, pypath, ' '.join(args))) From hpk at codespeak.net Fri Aug 22 11:02:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 11:02:13 +0200 (CEST) Subject: [py-svn] r57573 - in py: build release/0.9.x/py/bin Message-ID: <20080822090213.42A3416A1EB@codespeak.net> Author: hpk Date: Fri Aug 22 11:02:11 2008 New Revision: 57573 Added: py/release/0.9.x/py/bin/gendoc.py Removed: py/build/builddocs.py py/release/0.9.x/py/bin/_docgen.py Log: unifying towards one quite nice tool for generating py lib docs. Deleted: /py/build/builddocs.py ============================================================================== --- /py/build/builddocs.py Fri Aug 22 11:02:11 2008 +++ (empty file) @@ -1,30 +0,0 @@ -#!/usr/bin/env python - -""" build the 'py' documentation and api docs in a directory 'html' - - this script runs py.test from the nearest found py lib, and creates the - documents (provided that those doc tests are ran) and apigen docs along - the way - - the directory with documents gets placed in the current working directory, - command line arguments get passed to py.test -""" - -import sys -sys.path.insert(0, '.') - -import py -import os - -pypath = py.path.local(py.__file__).dirpath() -htmldir = py.path.local('html') -args = sys.argv[1:] -if not args: - args = [str(pypath)] - -print "pypath", pypath -print "generating docs into", htmldir - -os.system('DOCPATH="%s" APIGENPATH="%s/apigen" %s/bin/py.test ' - '--apigen=%s/apigen/apigen.py %s' % ( - htmldir, htmldir, pypath, pypath, ' '.join(args))) Deleted: /py/release/0.9.x/py/bin/_docgen.py ============================================================================== --- /py/release/0.9.x/py/bin/_docgen.py Fri Aug 22 11:02:11 2008 +++ (empty file) @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -""" quick tool to get documentation + apigen docs generated - - given a certain targetpath, apigen docs will be placed in 'apigen', - - user can choose to only build either docs or apigen docs: in this case, - the navigation bar will be adjusted -""" - -from _findpy import py -pypath = py.__pkg__.getpath() -print "using pypath", pypath -import os - -def run_tests(path, envvars='', args=''): - pytestpath = pypath.join('bin/py.test') - cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % - (pypath.dirpath(), envvars, pytestpath, args, path)) - print cmd - errno = os.system(cmd) - assert not errno - -def build_apigen_docs(targetpath, testargs=''): - run_tests(pypath, - 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( - targetpath,), - '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) - -def build_docs(targetpath, testargs): - docpath = pypath.join('doc') - run_tests(docpath, '', - testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,)) - docpath.copy(targetpath) - -def build_nav(targetpath, docs=True, api=True): - pass - -def build(targetpath, docs=True, api=True, testargs=''): - targetpath.ensure(dir=True) - if docs: - print 'building docs' - build_docs(targetpath, testargs) - if api: - print 'building api' - build_apigen_docs(targetpath, testargs) - - -if __name__ == '__main__': - import sys - if len(sys.argv) == 1: - print 'usage: %s [options]' - print - print ' targetdir: a path to a directory (created if it doesn\'t' - print ' exist) where the docs are put' - print ' options: options passed to py.test when running the tests' - sys.exit(1) - targetpath = py.path.local(sys.argv[1]) - args = ' '.join(sys.argv[2:]) - build(targetpath, True, True, args) - Added: py/release/0.9.x/py/bin/gendoc.py ============================================================================== --- (empty file) +++ py/release/0.9.x/py/bin/gendoc.py Fri Aug 22 11:02:11 2008 @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +""" +build the 'py' documentation and api docs in a specified +directory, defaulting to 'html'. You need to be a directory +where your "py" package is. + +This script generates API documentation and static +documentation. The API documentation is generated by using +the "apigen" facility of the py lib which currently only works +on windows. +""" + +import sys +sys.path.insert(0, '.') + +import py +import os + +def sysexec(cmd): + print "executing", cmd + os.system(cmd) + +if __name__ == '__main__': + pypath = py.path.local(py.__file__).dirpath() + args = sys.argv[1:] + if not args: + htmldir = py.path.local('html') + else: + htmldir = py.path.local(sys.argv.pop(0)) + + print "generating docs into", htmldir + print "pypath", pypath + pytest = pypath.join("bin/py.test") + assert pytest.check() + + print + print "*" * 30, "apigen", "*"*30 + apigendir = htmldir.join("apigen") + env = 'DOCPATH="%s" APIGENPATH="%s"' %(htmldir, apigendir) + if apigendir.check(): + print apigendir, "exists, not re-generating - remove to trigger regeneration" + else: + sysexec('%(env)s %(pytest)s --apigen=%(pypath)s/apigen/apigen.py py' % locals()) + print + print "*" * 30, "static generation", "*" * 30 + sysexec('%(env)s %(pytest)s --forcegen %(pypath)s/doc' % locals()) From hpk at codespeak.net Fri Aug 22 11:03:46 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 11:03:46 +0200 (CEST) Subject: [py-svn] r57574 - in py/release/0.9.x: . py py/doc Message-ID: <20080822090346.5184D16A1F3@codespeak.net> Author: hpk Date: Fri Aug 22 11:03:43 2008 New Revision: 57574 Modified: py/release/0.9.x/MANIFEST py/release/0.9.x/py/__init__.py py/release/0.9.x/py/doc/download.txt py/release/0.9.x/py/doc/index.txt py/release/0.9.x/setup.py Log: improved download doc, bumped version, regen setup.py Modified: py/release/0.9.x/MANIFEST ============================================================================== --- py/release/0.9.x/MANIFEST (original) +++ py/release/0.9.x/MANIFEST Fri Aug 22 11:03:43 2008 @@ -60,9 +60,9 @@ py/apigen/tracer/testing/test_model.py py/apigen/tracer/testing/test_package.py py/apigen/tracer/tracer.py -py/bin/_docgen.py py/bin/_findpy.py py/bin/_genscripts.py +py/bin/gendoc.py py/bin/py.cleanup py/bin/py.countloc py/bin/py.lookup Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Fri Aug 22 11:03:43 2008 @@ -22,7 +22,7 @@ """ from initpkg import initpkg -version = "0.9.2b9" +version = "0.9.2b10" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/py/doc/download.txt ============================================================================== --- py/release/0.9.x/py/doc/download.txt (original) +++ py/release/0.9.x/py/doc/download.txt Fri Aug 22 11:03:43 2008 @@ -24,7 +24,7 @@ Downloading a tar/zip archive and installing that =================================================== -Go to the project pages and download a tar or zip file: +Go to the python package index (pypi) and download a tar or zip file: http://pypi.python.org/pypi/py/ @@ -48,33 +48,46 @@ svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x -You should then be able to issue:: +You can then issue:: python setup.py develop -and work with the local version. +in order to work with your checkout version. -If this doesn't work for you then you can also -source (linux) or execute (windows) some scripts -that set environment variables for using your checkout. -You can execute: +other interesting svn checkout points:: - python ~/path/to/checkout/py/env.py + http://codespeak.net/ + svn/py/release # release tags and branches + svn/py/dist # latest stable (may or may not be a release) + svn/py/trunk # head development / merge point -or on windows: - \\path\\to\\checkout\\py\\env.cmd +Working with multiple py lib versions / svn externals +======================================================= -to get settings for PYTHONPATH and PATH. +If you happen to have multiple versions of the py lib +around or you ship the py lib as an svn-external to +then you might want to use py lib scripts more directly. +For example if you have a project layout like this:: + mypkg/ + subpkg1/ + tests/ + tests/ + py/ # as svn-external, could be specific tag/version -py subversion directory structure -================================= +then you want to make sure that the actual local py lib is used +and not another system-wide version. For this you need to add +``py/bin`` or ``py\bin\win32`` respectively to your system's PATH settings. -The directory release layout of the repository is -going to follow this scheme:: +You can do this by executing (on windows) a script to set the environment:: + + c:\\path\to\checkout\py\env.cmd + +or on linux/osx you can add something like this to your shell +initialization:: + + eval `python ~/path/to/checkout/py/env.py` + +to get good settings for PYTHONPATH and PATH. - http://codespeak.net/ - svn/py/dist # latest stable (may or may not be a release) - svn/py/release # release tags and branches - svn/py/trunk # head development / merge point Modified: py/release/0.9.x/py/doc/index.txt ============================================================================== --- py/release/0.9.x/py/doc/index.txt (original) +++ py/release/0.9.x/py/doc/index.txt Fri Aug 22 11:03:43 2008 @@ -4,7 +4,7 @@ The py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads (greenlets) and uniform local path and svn abstractions. - Works on Linux, Windows and OSX, Python versions + It works on Linux, Windows and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. `Download and Installation`_ Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Fri Aug 22 11:03:43 2008 @@ -38,7 +38,7 @@ name='py', description='py lib: agile development and test support library', long_description = long_description, - version='0.9.2b9', + version='0.9.2b10', url='http://pylib.org', download_url='http://codespeak.net/py/0.9.2/download.html', license='MIT license', @@ -129,9 +129,9 @@ 'apigen/style.css', 'apigen/todo-apigen.txt', 'apigen/todo.txt', - 'bin/_docgen.py', 'bin/_findpy.py', 'bin/_genscripts.py', + 'bin/gendoc.py', 'bin/py.cleanup', 'bin/py.countloc', 'bin/py.lookup', From hpk at codespeak.net Fri Aug 22 11:10:50 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 11:10:50 +0200 (CEST) Subject: [py-svn] r57575 - py/release/0.9.x/py/bin Message-ID: <20080822091050.6EB75169E81@codespeak.net> Author: hpk Date: Fri Aug 22 11:10:49 2008 New Revision: 57575 Modified: py/release/0.9.x/py/bin/gendoc.py Log: perform some checks at the beginning Modified: py/release/0.9.x/py/bin/gendoc.py ============================================================================== --- py/release/0.9.x/py/bin/gendoc.py (original) +++ py/release/0.9.x/py/bin/gendoc.py Fri Aug 22 11:10:49 2008 @@ -22,7 +22,11 @@ os.system(cmd) if __name__ == '__main__': + pydir = py.path.local().join("py") + assert pydir.check(dir=1), "py directory not found" pypath = py.path.local(py.__file__).dirpath() + assert pydir == pypath, "directory %s and %s differ" %(pydir, pypath) + args = sys.argv[1:] if not args: htmldir = py.path.local('html') From hpk at codespeak.net Fri Aug 22 11:59:29 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 11:59:29 +0200 (CEST) Subject: [py-svn] r57577 - py/release/0.9.x/py/doc Message-ID: <20080822095929.5B17316A1EE@codespeak.net> Author: hpk Date: Fri Aug 22 11:59:25 2008 New Revision: 57577 Modified: py/release/0.9.x/py/doc/contact.txt Log: update and make contact page a bit nicer. Modified: py/release/0.9.x/py/doc/contact.txt ============================================================================== --- py/release/0.9.x/py/doc/contact.txt (original) +++ py/release/0.9.x/py/doc/contact.txt Fri Aug 22 11:59:25 2008 @@ -1,55 +1,20 @@ py lib contact and communication =================================== -.. contents:: -.. sectnum:: -IRC Channel #pylib on irc.freenode.net --------------------------------------------- +- **#pylib on irc.freenode.net**: you are welcome + to lurk or ask questions in this IRC channel, it also tracks py lib commits. -The #pylib channel on freenode displays all commits to the py lib -and you are welcome to lurk or to ask questions there! +- `py-dev developers list`_ development mailing list. Good for reporting bugs, feature questions, discussing issues. Usually sees between 1 and 10 posts per week. -`py-dev`_ developers mailing list ------------------------------------ +- `py-svn general commit mailing list`_ to follow all development commits. -If you see bugs and/or can provide patches, please -subscribe to the `py-dev developers list`_. -As of Febrary 2007 it has medium to low traffic. +- `development bug/feature tracker`_ this roundup instance serves to file bugs and track issues. +- `merlinux.eu`_ offers tutorials and commercial support for + py.test and the py lib in general. -`py-svn`_ commit mailing list ------------------------------------ - -If you'd like to see ongoing development commits, -please subscribe to: - - `py-svn general commit mailing list`_ - -This list (as of February 2007) has medium to high traffic. - - -`development bug/feature tracker`_ ---------------------------------------------- - -This (somewhat old) roundup instance still serves -to file bugs and track issues. However, we also -keep a list of "TODOs" in various directories. - - -Coding and communication ------------------------- - -We are practicing what could be called documentation, -vision, discussion and automated-test driven development. -In the `future`_ book we try to layout visions and ideas for -the near coding feature to give a means for preliminary -feedback before code hits the ground. - -With our `coding style`_ we are mostly following -cpython guidance with some additional restrictions -some of which projects like twisted_ or zope3_ have -adopted in similar ways. +.. _`merlinux.eu`: http://merlinux.eu .. _`zope3`: http://zope3.zwiki.org/ .. _twisted: http://www.twistedmatrix.org @@ -61,13 +26,13 @@ get an account on codespeak --------------------------- -codespeak_ is employing a liberal committing scheme. If you know +codespeak_ is where the subversion repository is hosted. If you know someone who is active on codespeak already or you are otherwise known in -the community then you will most probably just get access. But even if -you are new to the python developer community you may still get one if -you want to improve things and can be expected to honour the -style of coding and communication. +the community (see also: FOAF_) you will get access. But even if +you are new to the python developer community please come to the IRC +or the mailing list and ask questions, get involved. +.. _FOAF: http://en.wikipedia.org/wiki/FOAF .. _`coding style`: coding-style.html .. _us: http://codespeak.net/mailman/listinfo/py-dev .. _codespeak: http://codespeak.net/ From hpk at codespeak.net Fri Aug 22 12:13:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 12:13:35 +0200 (CEST) Subject: [py-svn] r57578 - py/release/0.9.x/py/cmdline Message-ID: <20080822101335.4B27316A1FC@codespeak.net> Author: hpk Date: Fri Aug 22 12:13:33 2008 New Revision: 57578 Modified: py/release/0.9.x/py/cmdline/pycountloc.py Log: no _findpy Modified: py/release/0.9.x/py/cmdline/pycountloc.py ============================================================================== --- py/release/0.9.x/py/cmdline/pycountloc.py (original) +++ py/release/0.9.x/py/cmdline/pycountloc.py Fri Aug 22 12:13:33 2008 @@ -11,7 +11,6 @@ current working directory). Distinguish between test files and normal ones and report them separately. """ -from _findpy import py import py from py.compat import optparse from py.__.misc.cmdline.countloc import countloc From hpk at codespeak.net Fri Aug 22 12:37:36 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 12:37:36 +0200 (CEST) Subject: [py-svn] r57579 - in py/release/0.9.x: . py py/doc Message-ID: <20080822103736.5C36316A131@codespeak.net> Author: hpk Date: Fri Aug 22 12:37:35 2008 New Revision: 57579 Modified: py/release/0.9.x/py/__init__.py py/release/0.9.x/py/doc/release-0.9.2.txt py/release/0.9.x/setup.py Log: update release announcement, tag as 0.9.2 Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Fri Aug 22 12:37:35 2008 @@ -22,7 +22,7 @@ """ from initpkg import initpkg -version = "0.9.2b10" +version = "0.9.2" initpkg(__name__, description = "py lib: agile development and test support library", Modified: py/release/0.9.x/py/doc/release-0.9.2.txt ============================================================================== --- py/release/0.9.x/py/doc/release-0.9.2.txt (original) +++ py/release/0.9.x/py/doc/release-0.9.2.txt Fri Aug 22 12:37:35 2008 @@ -3,9 +3,9 @@ Welcome to the 0.9.2 py lib and py.test release - mainly fixing Windows issues, providing better -packaging and integration with setuptools. +packaging and integration with setuptools. -Here is a quick summary of what it provides: +Here is a quick summary of what the py lib provides: * py.test: cross-project testing tool with many advanced features * py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes @@ -16,6 +16,8 @@ See here for more information: +Pypi pages: http://pypi.python.org/pypi/py/ + Download/Install: http://codespeak.net/py/0.9.2/download.html Documentation/API: http://codespeak.net/py/0.9.2/index.html Modified: py/release/0.9.x/setup.py ============================================================================== --- py/release/0.9.x/setup.py (original) +++ py/release/0.9.x/setup.py Fri Aug 22 12:37:35 2008 @@ -1,7 +1,7 @@ """ setup file for 'py' package based on: - https://codespeak.net/svn/py/release/0.9.x, revision=57517 + https://codespeak.net/svn/py/release/0.9.x, revision=57578 autogenerated by gensetup.py """ @@ -38,7 +38,7 @@ name='py', description='py lib: agile development and test support library', long_description = long_description, - version='0.9.2b10', + version='0.9.2', url='http://pylib.org', download_url='http://codespeak.net/py/0.9.2/download.html', license='MIT license', From hpk at codespeak.net Fri Aug 22 12:39:43 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 12:39:43 +0200 (CEST) Subject: [py-svn] r57580 - py/release/0.9.x/py Message-ID: <20080822103943.BEE8F16A1BF@codespeak.net> Author: hpk Date: Fri Aug 22 12:39:43 2008 New Revision: 57580 Modified: py/release/0.9.x/py/LICENSE Log: mentioning martijn Modified: py/release/0.9.x/py/LICENSE ============================================================================== --- py/release/0.9.x/py/LICENSE (original) +++ py/release/0.9.x/py/LICENSE Fri Aug 22 12:39:43 2008 @@ -19,6 +19,7 @@ Chris Lamb Harald Armin Massa Ralf Schmitt + Martijn Faassen Ian Bicking Jan Balster Grig Gheorghiu From hpk at codespeak.net Fri Aug 22 12:53:35 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 12:53:35 +0200 (CEST) Subject: [py-svn] r57581 - py/release/0.9.2 Message-ID: <20080822105335.5627B16A0BF@codespeak.net> Author: hpk Date: Fri Aug 22 12:53:34 2008 New Revision: 57581 Added: py/release/0.9.2/ - copied from r57580, py/release/0.9.x/ Log: 0.9.2 release tag From hpk at codespeak.net Fri Aug 22 13:17:13 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 13:17:13 +0200 (CEST) Subject: [py-svn] r57582 - py/dist Message-ID: <20080822111713.8947416A200@codespeak.net> Author: hpk Date: Fri Aug 22 13:17:10 2008 New Revision: 57582 Added: py/dist/ - copied from r57581, py/release/0.9.2/ Log: updating dist to reflect 0.9.2 for now (trunk is too incompatible and not yet feature complete enough to work as dist) From hpk at codespeak.net Fri Aug 22 15:41:20 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 15:41:20 +0200 (CEST) Subject: [py-svn] r57587 - in py/trunk: . py py/bin py/cmdline py/doc Message-ID: <20080822134120.7B42B16A1F3@codespeak.net> Author: hpk Date: Fri Aug 22 15:41:17 2008 New Revision: 57587 Added: py/trunk/py/bin/gendoc.py - copied unchanged from r57586, py/release/0.9.x/py/bin/gendoc.py Removed: py/trunk/py/bin/_docgen.py Modified: py/trunk/MANIFEST py/trunk/py/LICENSE py/trunk/py/cmdline/pycountloc.py py/trunk/py/doc/contact.txt py/trunk/py/doc/download.txt py/trunk/py/doc/index.txt py/trunk/py/doc/release-0.9.2.txt py/trunk/setup.py Log: merging doc and tool changes from release branch svn merge -r 57540:HEAD release/0.9.x Modified: py/trunk/MANIFEST ============================================================================== --- py/trunk/MANIFEST (original) +++ py/trunk/MANIFEST Fri Aug 22 15:41:17 2008 @@ -63,6 +63,7 @@ py/bin/_docgen.py py/bin/_findpy.py py/bin/_genscripts.py +py/bin/gendoc.py py/bin/py.cleanup py/bin/py.countloc py/bin/py.lookup @@ -115,6 +116,7 @@ py/cmdline/pytest.py py/cmdline/pywhich.py py/cmdline/testing/__init__.py +py/cmdline/testing/test_cmdline.py py/cmdline/testing/test_generic.py py/code/__init__.py py/code/code.py Modified: py/trunk/py/LICENSE ============================================================================== --- py/trunk/py/LICENSE (original) +++ py/trunk/py/LICENSE Fri Aug 22 15:41:17 2008 @@ -19,6 +19,7 @@ Chris Lamb Harald Armin Massa Ralf Schmitt + Martijn Faassen Ian Bicking Jan Balster Grig Gheorghiu Deleted: /py/trunk/py/bin/_docgen.py ============================================================================== --- /py/trunk/py/bin/_docgen.py Fri Aug 22 15:41:17 2008 +++ (empty file) @@ -1,61 +0,0 @@ -#!/usr/bin/env python - -""" quick tool to get documentation + apigen docs generated - - given a certain targetpath, apigen docs will be placed in 'apigen', - - user can choose to only build either docs or apigen docs: in this case, - the navigation bar will be adjusted -""" - -from _findpy import py -pypath = py.__pkg__.getpath() -print "using pypath", pypath -import os - -def run_tests(path, envvars='', args=''): - pytestpath = pypath.join('bin/py.test') - cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % - (pypath.dirpath(), envvars, pytestpath, args, path)) - print cmd - errno = os.system(cmd) - assert not errno - -def build_apigen_docs(targetpath, testargs=''): - run_tests(pypath, - 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( - targetpath,), - '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) - -def build_docs(targetpath, testargs): - docpath = pypath.join('doc') - run_tests(docpath, '', - testargs + ' --forcegen --apigen="%s/apigen/apigen.py"' % (pypath,)) - docpath.copy(targetpath) - -def build_nav(targetpath, docs=True, api=True): - pass - -def build(targetpath, docs=True, api=True, testargs=''): - targetpath.ensure(dir=True) - if docs: - print 'building docs' - build_docs(targetpath, testargs) - if api: - print 'building api' - build_apigen_docs(targetpath, testargs) - - -if __name__ == '__main__': - import sys - if len(sys.argv) == 1: - print 'usage: %s [options]' - print - print ' targetdir: a path to a directory (created if it doesn\'t' - print ' exist) where the docs are put' - print ' options: options passed to py.test when running the tests' - sys.exit(1) - targetpath = py.path.local(sys.argv[1]) - args = ' '.join(sys.argv[2:]) - build(targetpath, True, True, args) - Modified: py/trunk/py/cmdline/pycountloc.py ============================================================================== --- py/trunk/py/cmdline/pycountloc.py (original) +++ py/trunk/py/cmdline/pycountloc.py Fri Aug 22 15:41:17 2008 @@ -11,7 +11,6 @@ current working directory). Distinguish between test files and normal ones and report them separately. """ -from _findpy import py import py from py.compat import optparse from py.__.misc.cmdline.countloc import countloc Modified: py/trunk/py/doc/contact.txt ============================================================================== --- py/trunk/py/doc/contact.txt (original) +++ py/trunk/py/doc/contact.txt Fri Aug 22 15:41:17 2008 @@ -1,55 +1,20 @@ py lib contact and communication =================================== -.. contents:: -.. sectnum:: -IRC Channel #pylib on irc.freenode.net --------------------------------------------- +- **#pylib on irc.freenode.net**: you are welcome + to lurk or ask questions in this IRC channel, it also tracks py lib commits. -The #pylib channel on freenode displays all commits to the py lib -and you are welcome to lurk or to ask questions there! +- `py-dev developers list`_ development mailing list. Good for reporting bugs, feature questions, discussing issues. Usually sees between 1 and 10 posts per week. -`py-dev`_ developers mailing list ------------------------------------ +- `py-svn general commit mailing list`_ to follow all development commits. -If you see bugs and/or can provide patches, please -subscribe to the `py-dev developers list`_. -As of Febrary 2007 it has medium to low traffic. +- `development bug/feature tracker`_ this roundup instance serves to file bugs and track issues. +- `merlinux.eu`_ offers tutorials and commercial support for + py.test and the py lib in general. -`py-svn`_ commit mailing list ------------------------------------ - -If you'd like to see ongoing development commits, -please subscribe to: - - `py-svn general commit mailing list`_ - -This list (as of February 2007) has medium to high traffic. - - -`development bug/feature tracker`_ ---------------------------------------------- - -This (somewhat old) roundup instance still serves -to file bugs and track issues. However, we also -keep a list of "TODOs" in various directories. - - -Coding and communication ------------------------- - -We are practicing what could be called documentation, -vision, discussion and automated-test driven development. -In the `future`_ book we try to layout visions and ideas for -the near coding feature to give a means for preliminary -feedback before code hits the ground. - -With our `coding style`_ we are mostly following -cpython guidance with some additional restrictions -some of which projects like twisted_ or zope3_ have -adopted in similar ways. +.. _`merlinux.eu`: http://merlinux.eu .. _`zope3`: http://zope3.zwiki.org/ .. _twisted: http://www.twistedmatrix.org @@ -61,13 +26,13 @@ get an account on codespeak --------------------------- -codespeak_ is employing a liberal committing scheme. If you know +codespeak_ is where the subversion repository is hosted. If you know someone who is active on codespeak already or you are otherwise known in -the community then you will most probably just get access. But even if -you are new to the python developer community you may still get one if -you want to improve things and can be expected to honour the -style of coding and communication. +the community (see also: FOAF_) you will get access. But even if +you are new to the python developer community please come to the IRC +or the mailing list and ask questions, get involved. +.. _FOAF: http://en.wikipedia.org/wiki/FOAF .. _`coding style`: coding-style.html .. _us: http://codespeak.net/mailman/listinfo/py-dev .. _codespeak: http://codespeak.net/ Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Fri Aug 22 15:41:17 2008 @@ -24,7 +24,7 @@ Downloading a tar/zip archive and installing that =================================================== -Go to the project pages and download a tar or zip file: +Go to the python package index (pypi) and download a tar or zip file: http://pypi.python.org/pypi/py/ @@ -48,33 +48,46 @@ svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x -You should then be able to issue:: +You can then issue:: python setup.py develop -and work with the local version. +in order to work with your checkout version. -If this doesn't work for you then you can also -source (linux) or execute (windows) some scripts -that set environment variables for using your checkout. -You can execute: +other interesting svn checkout points:: - python ~/path/to/checkout/py/env.py + http://codespeak.net/ + svn/py/release # release tags and branches + svn/py/dist # latest stable (may or may not be a release) + svn/py/trunk # head development / merge point -or on windows: - \\path\\to\\checkout\\py\\env.cmd +Working with multiple py lib versions / svn externals +======================================================= -to get settings for PYTHONPATH and PATH. +If you happen to have multiple versions of the py lib +around or you ship the py lib as an svn-external to +then you might want to use py lib scripts more directly. +For example if you have a project layout like this:: + mypkg/ + subpkg1/ + tests/ + tests/ + py/ # as svn-external, could be specific tag/version -py subversion directory structure -================================= +then you want to make sure that the actual local py lib is used +and not another system-wide version. For this you need to add +``py/bin`` or ``py\bin\win32`` respectively to your system's PATH settings. -The directory release layout of the repository is -going to follow this scheme:: +You can do this by executing (on windows) a script to set the environment:: + + c:\\path\to\checkout\py\env.cmd + +or on linux/osx you can add something like this to your shell +initialization:: + + eval `python ~/path/to/checkout/py/env.py` + +to get good settings for PYTHONPATH and PATH. - http://codespeak.net/ - svn/py/dist # latest stable (may or may not be a release) - svn/py/release # release tags and branches - svn/py/trunk # head development / merge point Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Fri Aug 22 15:41:17 2008 @@ -4,7 +4,7 @@ The py lib is a development support library featuring py.test, ad-hoc distributed execution, micro-threads (greenlets) and uniform local path and svn abstractions. - Works on Linux, Windows and OSX, Python versions + It works on Linux, Windows and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. `Download and Installation`_ Modified: py/trunk/py/doc/release-0.9.2.txt ============================================================================== --- py/trunk/py/doc/release-0.9.2.txt (original) +++ py/trunk/py/doc/release-0.9.2.txt Fri Aug 22 15:41:17 2008 @@ -3,9 +3,9 @@ Welcome to the 0.9.2 py lib and py.test release - mainly fixing Windows issues, providing better -packaging and integration with setuptools. +packaging and integration with setuptools. -Here is a quick summary of what it provides: +Here is a quick summary of what the py lib provides: * py.test: cross-project testing tool with many advanced features * py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes @@ -16,6 +16,8 @@ See here for more information: +Pypi pages: http://pypi.python.org/pypi/py/ + Download/Install: http://codespeak.net/py/0.9.2/download.html Documentation/API: http://codespeak.net/py/0.9.2/index.html Modified: py/trunk/setup.py ============================================================================== --- py/trunk/setup.py (original) +++ py/trunk/setup.py Fri Aug 22 15:41:17 2008 @@ -138,9 +138,9 @@ 'apigen/style.css', 'apigen/todo-apigen.txt', 'apigen/todo.txt', - 'bin/_docgen.py', 'bin/_findpy.py', 'bin/_genscripts.py', + 'bin/gendoc.py', 'bin/py.cleanup', 'bin/py.countloc', 'bin/py.lookup', From hpk at codespeak.net Fri Aug 22 19:07:09 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 19:07:09 +0200 (CEST) Subject: [py-svn] r57591 - in py/trunk/py/test: . testing Message-ID: <20080822170709.4FF4016A234@codespeak.net> Author: hpk Date: Fri Aug 22 19:07:04 2008 New Revision: 57591 Modified: py/trunk/py/test/config.py py/trunk/py/test/testing/test_config.py Log: flush true by default Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Fri Aug 22 19:07:04 2008 @@ -226,16 +226,17 @@ if self.option.tracedir is not None: return py.path.local(self.option.tracedir) - def maketrace(self, name, flush=False): + def maketrace(self, name, flush=True): """ return a tracedirectory or None, depending on --tracedir. """ tracedir = self.gettracedir() if tracedir is None: return NullTracer() + tracedir.ensure(dir=1) return Tracer(tracedir.join(name), flush=flush) class Tracer(object): file = None - def __init__(self, path, flush=False): + def __init__(self, path, flush=True): self.file = path.open(mode='w') self.flush = flush Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Fri Aug 22 19:07:04 2008 @@ -202,12 +202,12 @@ assert s.find("TestrunStart") != -1 def test_tracedir_tracer(self): - tracedir = self.tmpdir.mkdir("tracedir") + tracedir = self.tmpdir.join("tracedir") config = py.test.config._reparse([self.tmpdir, '--tracedir=%s' % tracedir]) assert config.gettracedir() == tracedir - trace = config.maketrace("trace1.log", flush=True) + trace = config.maketrace("trace1.log") # flush=True by default trace("hello", "world") class A: pass trace(A()) From hpk at codespeak.net Fri Aug 22 23:14:45 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 23:14:45 +0200 (CEST) Subject: [py-svn] r57594 - in py/trunk/py/test: . dsession Message-ID: <20080822211445.8670016A160@codespeak.net> Author: hpk Date: Fri Aug 22 23:14:43 2008 New Revision: 57594 Modified: py/trunk/py/test/collect.py py/trunk/py/test/config.py py/trunk/py/test/dsession/dsession.py py/trunk/py/test/dsession/hostmanage.py py/trunk/py/test/dsession/masterslave.py py/trunk/py/test/dsession/mypickle.py Log: * adding tracing to dsession and master/slave communication (enable with --tracedir) * factor slave loop into a class * add comment to pickling Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Fri Aug 22 23:14:43 2008 @@ -114,12 +114,18 @@ return (self.name, self.parent) def __setstate__(self, (name, parent)): newnode = parent.join(name) - assert newnode is not None, (self, name, parent) + if newnode is None: + raise AssertionError(self, name, parent, parent.__dict__) self.__dict__.update(newnode.__dict__) #self.__init__(name=name, parent=parent) def __repr__(self): - return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None)) + if self._config.option.debug: + return "<%s %r %0x>" %(self.__class__.__name__, + getattr(self, 'name', None), id(self)) + else: + return "<%s %r>" %(self.__class__.__name__, + getattr(self, 'name', None)) # methods for ordering nodes Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Fri Aug 22 23:14:43 2008 @@ -241,7 +241,8 @@ self.flush = flush def __call__(self, *args): - print >>self.file, " ".join(map(str, args)) + time = round(py.std.time.time(), 3) + print >>self.file, time, " ".join(map(str, args)) if self.flush: self.file.flush() Modified: py/trunk/py/test/dsession/dsession.py ============================================================================== --- py/trunk/py/test/dsession/dsession.py (original) +++ py/trunk/py/test/dsession/dsession.py Fri Aug 22 23:14:43 2008 @@ -15,7 +15,6 @@ from py.__.test.runner import OutcomeRepr from py.__.test import outcome - import Queue class LoopState(object): @@ -34,7 +33,7 @@ Session drives the collection and running of tests and generates test events for reporters. """ - MAXITEMSPERHOST = 10 + MAXITEMSPERHOST = 15 def __init__(self, config): super(DSession, self).__init__(config=config) @@ -43,6 +42,7 @@ self.host2pending = {} self.item2host = {} self._testsfailed = False + self.trace = config.maketrace("dsession.log") def fixoptions(self): """ check, fix and determine conflicting options. """ @@ -173,6 +173,7 @@ pending = self.host2pending.pop(host) for item in pending: del self.item2host[item] + self.trace("removehost %s, pending=%r" %(host.hostid, pending)) return pending def triggertesting(self, colitems): @@ -195,6 +196,7 @@ if room > 0: sending = tosend[:room] host.node.sendlist(sending) + self.trace("sent to host %s: %r" %(host.hostid, sending)) for item in sending: #assert item not in self.item2host, ( # "sending same item %r to multiple hosts " @@ -210,8 +212,11 @@ self.queue.put(event.RescheduleItems(tosend)) def removeitem(self, item): + if item not in self.item2host: + raise AssertionError(item, self.item2host) host = self.item2host.pop(item) self.host2pending[host].remove(item) + self.trace("removed %r, host=%r" %(item,host.hostid)) def handle_crashitem(self, item, host): longrepr = "%r CRASHED THE HOST %r" %(item, host) @@ -228,3 +233,15 @@ """ teardown any resources after a test run. """ for host in self.host2pending: host.gw.exit() + + +# debugging function +def dump_picklestate(item): + l = [] + while 1: + state = item.__getstate__() + l.append(state) + item = state[-1] + if len(state) != 2: + break + return l Modified: py/trunk/py/test/dsession/hostmanage.py ============================================================================== --- py/trunk/py/test/dsession/hostmanage.py (original) +++ py/trunk/py/test/dsession/hostmanage.py Fri Aug 22 23:14:43 2008 @@ -33,7 +33,7 @@ def _getuniqueid(self, hostname): l = self._hostname2list.setdefault(hostname, []) - hostid = hostname + "[%d]" % len(l) + hostid = hostname + "-%d" % len(l) l.append(hostid) return hostid Modified: py/trunk/py/test/dsession/masterslave.py ============================================================================== --- py/trunk/py/test/dsession/masterslave.py (original) +++ py/trunk/py/test/dsession/masterslave.py Fri Aug 22 23:14:43 2008 @@ -78,7 +78,8 @@ channel = PickleChannel(channel) from py.__.test.dsession import masterslave config = masterslave.receive_and_send_pickled_config(channel) - masterslave.setup_at_slave_side(channel, config) + slavenode = masterslave.SlaveNode(channel, config) + slavenode.run() """) channel = PickleChannel(channel) remote_topdir = host.gw_remotepath @@ -89,28 +90,53 @@ channel.send(host) return channel -def setup_at_slave_side(channel, config): - # our current dir is the topdir - # XXX what about neccessary PYTHONPATHs? - import os - if hasattr(os, 'nice'): - nice_level = config.getvalue('dist_nicelevel') - os.nice(nice_level) - from py.__.test.dsession.hostmanage import makehostup - host = channel.receive() - channel.send(makehostup(host)) - while 1: - task = channel.receive() - if task is None: # shutdown - channel.send(None) - break - if isinstance(task, list): - for item in task: - runtest(channel, item) +class SlaveNode(object): + def __init__(self, channel, config): + self.channel = channel + self.config = config + import os + if hasattr(os, 'nice'): + nice_level = config.getvalue('dist_nicelevel') + os.nice(nice_level) + + def __repr__(self): + host = getattr(self, 'host', '') + return "<%s host=%s>" %(self.__class__.__name__, host.hostid) + + def run(self): + from py.__.test.dsession.hostmanage import makehostup + channel = self.channel + self.host = host = channel.receive() + channel.send(makehostup(host)) + self.trace = self.config.maketrace(host.hostid) + self.trace("initialized") + + try: + while 1: + task = channel.receive() + self.trace("received", task) + + if task is None: # shutdown + channel.send(None) + self.trace("shutting down, send None to", channel) + break + if isinstance(task, list): + for item in task: + self.runtest(item) + else: + self.runtest(task) + except KeyboardInterrupt: + raise + except: + rep = event.InternalException() + self.trace("sending back internal exception report, breaking loop") + channel.send(rep) + raise else: - runtest(channel, task) + self.trace("normal shutdown") -def runtest(channel, item): - runner = item._getrunner() - testrep = runner(item) - channel.send(testrep) + def runtest(self, item): + runner = item._getrunner() + testrep = runner(item) + self.channel.send(testrep) + self.trace("sent back testreport", testrep) Modified: py/trunk/py/test/dsession/mypickle.py ============================================================================== --- py/trunk/py/test/dsession/mypickle.py (original) +++ py/trunk/py/test/dsession/mypickle.py Fri Aug 22 23:14:43 2008 @@ -23,6 +23,9 @@ """ Pickler with a custom memoize() to take care of unique ID creation. See the usage in ImmutablePickler + XXX we could probably extend Pickler + and Unpickler classes to directly + update the other'S memos. """ def __init__(self, file, protocol, uneven): Pickler.__init__(self, file, protocol) @@ -82,9 +85,8 @@ return res def _updatepicklememo(self): - self._picklememo.update(dict( - [(id(obj), (int(x), obj)) - for x, obj in self._unpicklememo.items()])) + for x, obj in self._unpicklememo.items(): + self._picklememo[id(obj)] = (int(x), obj) def _updateunpicklememo(self): for key,obj in self._picklememo.values(): From hpk at codespeak.net Fri Aug 22 23:15:57 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 22 Aug 2008 23:15:57 +0200 (CEST) Subject: [py-svn] r57595 - py/trunk/py/test/testing Message-ID: <20080822211557.F36C216A160@codespeak.net> Author: hpk Date: Fri Aug 22 23:15:56 2008 New Revision: 57595 Modified: py/trunk/py/test/testing/test_config.py Log: i've heart somewhere that running tests before checking in is a good idea (aka bah!) Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Fri Aug 22 23:15:56 2008 @@ -213,7 +213,7 @@ trace(A()) p = tracedir.join("trace1.log") lines = p.readlines(cr=0) - assert lines[0] == "hello world" + assert lines[0].endswith("hello world") assert lines[1].find("A") != -1 trace.close() From fijal at codespeak.net Sun Aug 24 12:58:45 2008 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sun, 24 Aug 2008 12:58:45 +0200 (CEST) Subject: [py-svn] r57609 - py/trunk/py/test Message-ID: <20080824105845.1FF46169F63@codespeak.net> Author: fijal Date: Sun Aug 24 12:58:42 2008 New Revision: 57609 Modified: py/trunk/py/test/event.py Log: Keep comments in the same style accross the file Modified: py/trunk/py/test/event.py ============================================================================== --- py/trunk/py/test/event.py (original) +++ py/trunk/py/test/event.py Sun Aug 24 12:58:42 2008 @@ -127,9 +127,9 @@ self.host = host self.error = error -# -# events related to rsyncing -# +# --------------------------------------------------------------------- +# Events related to rsyncing +# --------------------------------------------------------------------- class HostRSyncing(BaseEvent): def __init__(self, host, root, remotepath, synced): From hpk at codespeak.net Fri Aug 29 20:55:44 2008 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 29 Aug 2008 20:55:44 +0200 (CEST) Subject: [py-svn] r57689 - in py: dist/py/doc release/0.9.x/py/doc trunk/py/doc Message-ID: <20080829185544.C3D4C169F91@codespeak.net> Author: hpk Date: Fri Aug 29 20:55:42 2008 New Revision: 57689 Modified: py/dist/py/doc/download.txt py/release/0.9.x/py/doc/download.txt py/trunk/py/doc/download.txt Log: mentioning "-U" switch. Modified: py/dist/py/doc/download.txt ============================================================================== --- py/dist/py/doc/download.txt (original) +++ py/dist/py/doc/download.txt Fri Aug 29 20:55:42 2008 @@ -5,12 +5,18 @@ With a working `setuptools installation`_ you can install from the command line:: - easy_install py + easy_install -U py -to get the latest release of the py lib. On non-Windows systems you will -need a working C-compiler in order to successfully complete this step. -The py lib and its tools are expected to work well on Linux, Windows -and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. +to get the latest release of the py lib. The ``-U`` switch +will trigger an upgrade if you already have an older version installed. +The py lib and its tools are expected to work well on Linux, +Windows and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. + +Note that we provide binary eggs for Windows machines - on +non-Windows systems you need a working C-compiler in order to +install the full py lib. If you don't have a compiler available +you can still install the py lib but without greenlets - look +below for the ``install_lib`` target. **IMPORTANT NOTE**: if you are using Windows and have previous installations of the py lib on your system, please download @@ -37,7 +43,7 @@ python setup.py install_lib You will then not be able to use greenlets but otherwise -should be fine. +``py.test`` and all tools and APIs are fine to use. Installing from subversion / develop mode ============================================ Modified: py/release/0.9.x/py/doc/download.txt ============================================================================== --- py/release/0.9.x/py/doc/download.txt (original) +++ py/release/0.9.x/py/doc/download.txt Fri Aug 29 20:55:42 2008 @@ -5,12 +5,18 @@ With a working `setuptools installation`_ you can install from the command line:: - easy_install py + easy_install -U py -to get the latest release of the py lib. On non-Windows systems you will -need a working C-compiler in order to successfully complete this step. -The py lib and its tools are expected to work well on Linux, Windows -and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. +to get the latest release of the py lib. The ``-U`` switch +will trigger an upgrade if you already have an older version installed. +The py lib and its tools are expected to work well on Linux, +Windows and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. + +Note that we provide binary eggs for Windows machines - on +non-Windows systems you need a working C-compiler in order to +install the full py lib. If you don't have a compiler available +you can still install the py lib but without greenlets - look +below for the ``install_lib`` target. **IMPORTANT NOTE**: if you are using Windows and have previous installations of the py lib on your system, please download @@ -37,7 +43,7 @@ python setup.py install_lib You will then not be able to use greenlets but otherwise -should be fine. +``py.test`` and all tools and APIs are fine to use. Installing from subversion / develop mode ============================================ Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Fri Aug 29 20:55:42 2008 @@ -5,12 +5,18 @@ With a working `setuptools installation`_ you can install from the command line:: - easy_install py + easy_install -U py -to get the latest release of the py lib. On non-Windows systems you will -need a working C-compiler in order to successfully complete this step. -The py lib and its tools are expected to work well on Linux, Windows -and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. +to get the latest release of the py lib. The ``-U`` switch +will trigger an upgrade if you already have an older version installed. +The py lib and its tools are expected to work well on Linux, +Windows and OSX, Python versions 2.3, 2.4, 2.5 and 2.6. + +Note that we provide binary eggs for Windows machines - on +non-Windows systems you need a working C-compiler in order to +install the full py lib. If you don't have a compiler available +you can still install the py lib but without greenlets - look +below for the ``install_lib`` target. **IMPORTANT NOTE**: if you are using Windows and have previous installations of the py lib on your system, please download @@ -37,7 +43,7 @@ python setup.py install_lib You will then not be able to use greenlets but otherwise -should be fine. +``py.test`` and all tools and APIs are fine to use. Installing from subversion / develop mode ============================================