From fijal at codespeak.net Wed Jan 3 17:06:21 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 3 Jan 2007 17:06:21 +0100 (CET) Subject: [py-svn] r36123 - in py/dist/py/test/rsession: . webdata Message-ID: <20070103160621.5837210068@code0.codespeak.net> Author: fijal Date: Wed Jan 3 17:06:18 2007 New Revision: 36123 Modified: py/dist/py/test/rsession/webdata/source.js py/dist/py/test/rsession/webjs.py Log: Adhere to new API. Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/dist/py/test/rsession/webjs.py ============================================================================== --- py/dist/py/test/rsession/webjs.py (original) +++ py/dist/py/test/rsession/webjs.py Wed Jan 3 17:06:18 2007 @@ -12,13 +12,13 @@ py.test.skip("PyPy not found") def create_elem(s): - return dom.get_document().createElement(s) + return dom.document.createElement(s) def get_elem(el): - return dom.get_document().getElementById(el) + return dom.document.getElementById(el) def create_text_elem(txt): - return dom.get_document().createTextNode(txt) + return dom.document.createTextNode(txt) tracebacks = {} skips = {} @@ -49,7 +49,7 @@ exported_methods.show_all_statuses(glob.sessid, comeback) def show_info(data="aa"): - info = dom.get_document().getElementById("info") + info = dom.document.getElementById("info") info.style.visibility = "visible" while len(info.childNodes): info.removeChild(info.childNodes[0]) @@ -59,7 +59,7 @@ # XXX: Need guido def hide_info(): - info = dom.get_document().getElementById("info") + info = dom.document.getElementById("info") info.style.visibility = "hidden" def make_module_box(msg): @@ -88,7 +88,7 @@ def add_received_item_outcome(msg, module_part): if msg['hostkey']: - host_elem = dom.get_document().getElementById(msg['hostkey']) + host_elem = dom.document.getElementById(msg['hostkey']) glob.host_pending[msg['hostkey']].pop() count = len(glob.host_pending[msg['hostkey']]) host_elem.childNodes[0].nodeValue = '%s[%s]' % ( @@ -135,23 +135,23 @@ def process(msg): if len(msg) == 0: return False - elem = dom.get_document().getElementById("testmain") + elem = dom.document.getElementById("testmain") #elem.innerHTML += '%s
' % msg['event'] - main_t = dom.get_document().getElementById("main_table") + main_t = dom.document.getElementById("main_table") if msg['type'] == 'ItemStart': # we start a new directory or what if msg['itemtype'] == 'Module': tr = make_module_box(msg) main_t.appendChild(tr) elif msg['type'] == 'SendItem': - host_elem = dom.get_document().getElementById(msg['hostkey']) + host_elem = dom.document.getElementById(msg['hostkey']) glob.host_pending[msg['hostkey']].insert(0, msg['fullitemname']) count = len(glob.host_pending[msg['hostkey']]) host_elem.childNodes[0].nodeValue = '%s[%s]' % ( glob.host_dict[msg['hostkey']], count) elif msg['type'] == 'HostReady': - host_elem = dom.get_document().getElementById(msg['hostkey']) + host_elem = dom.document.getElementById(msg['hostkey']) host_elem.style.background = \ "#00ff00" host_elem.childNodes[0].nodeValue = '%s[0]' % ( @@ -165,8 +165,8 @@ add_received_item_outcome(msg, module_part) elif msg['type'] == 'TestFinished': text = "FINISHED %s run, %s failures, %s skipped" % (msg['run'], msg['fails'], msg['skips']) - dom.get_document().title = "Py.test %s" % text - dom.get_document().getElementById("Tests").childNodes[0].nodeValue = \ + dom.document.title = "Py.test %s" % text + dom.document.getElementById("Tests").childNodes[0].nodeValue = \ "Tests [%s]" % text elif msg['type'] == 'FailedTryiter': module_part = get_elem(msg['fullitemname']) @@ -191,7 +191,7 @@ tr.appendChild(td) module_part.appendChild(tr) if glob.data_empty: - mbox = dom.get_document().getElementById('messagebox') + mbox = dom.document.getElementById('messagebox') mbox.parentNode.scrollIntoView() return True @@ -206,7 +206,7 @@ txt = create_text_elem(item_name + "\n" + data) pre.appendChild(txt) msgbox.appendChild(pre) - dom.get_document().location = "#message" + dom.document.location = "#message" glob.data_empty = False def show_traceback(item_name="aa"): @@ -227,7 +227,7 @@ show_host(glob.host) def show_host(host_name="aa"): - elem = dom.get_document().getElementById("jobs") + elem = dom.document.getElementById("jobs") while len(elem.childNodes): elem.removeChild(elem.childNodes[0]) for item in glob.host_pending[host_name]: @@ -241,14 +241,14 @@ dom.setTimeout(reshow_host, 100) def hide_host(): - elem = dom.get_document().getElementById("jobs") + elem = dom.document.getElementById("jobs") while len(elem.childNodes): elem.removeChild(elem.childNodes[0]) elem.style.visibility = "hidden" glob.host = "" def host_init(host_dict): - tbody = dom.get_document().getElementById("hostsbody") + tbody = dom.document.getElementById("hostsbody") for host in host_dict.keys(): tr = create_elem('tr') tbody.appendChild(tr) From fijal at codespeak.net Wed Jan 3 19:27:30 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 3 Jan 2007 19:27:30 +0100 (CET) Subject: [py-svn] r36129 - py/dist/py/path/svn/testing Message-ID: <20070103182730.7D32310082@code0.codespeak.net> Author: fijal Date: Wed Jan 3 19:27:28 2007 New Revision: 36129 Modified: py/dist/py/path/svn/testing/test_urlcommand.py Log: Fix a test Modified: py/dist/py/path/svn/testing/test_urlcommand.py ============================================================================== --- py/dist/py/path/svn/testing/test_urlcommand.py (original) +++ py/dist/py/path/svn/testing/test_urlcommand.py Wed Jan 3 19:27:28 2007 @@ -68,7 +68,7 @@ assert info.last_author == 'hpk' assert info.created_rev == 2256 assert info.kind == 'file' - assert time.gmtime(info.mtime)[:6] == (now.year, 11, 24, 17, 55, 0) + assert time.gmtime(info.mtime)[:6] == (2006, 11, 24, 17, 55, 0) assert info.size == 165 assert info.time == info.mtime * 1000000 From fijal at codespeak.net Wed Jan 3 20:17:44 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 3 Jan 2007 20:17:44 +0100 (CET) Subject: [py-svn] r36131 - in py/dist/py/test: . testing Message-ID: <20070103191744.BEC8110072@code0.codespeak.net> Author: fijal Date: Wed Jan 3 20:17:40 2007 New Revision: 36131 Modified: py/dist/py/test/collect.py py/dist/py/test/testing/test_collect.py Log: Add __hash__ for items and test for it Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Wed Jan 3 20:17:40 2007 @@ -102,6 +102,9 @@ except AttributeError: return False + def __hash__(self): + return hash((self.name, self.parent)) + def __ne__(self, other): return not self == other Modified: py/dist/py/test/testing/test_collect.py ============================================================================== --- py/dist/py/test/testing/test_collect.py (original) +++ py/dist/py/test/testing/test_collect.py Wed Jan 3 20:17:40 2007 @@ -410,3 +410,30 @@ l = list(col.tryiter(reporterror=errors.append)) assert len(errors) == 0 +def test_check_collect_hashes(): + tmp = py.test.ensuretemp("check_collect_hashes") + tmp.ensure("test_one.py").write(py.code.Source(""" + def test_1(): + pass + + def test_2(): + pass + """)) + tmp.ensure("test_two.py").write(py.code.Source(""" + def test_1(): + pass + + def test_2(): + pass + """)) + tmp.ensure("__init__.py") + col = py.test.collect.Directory(tmp) + errors = [] + l = list(col.tryiter(reporterror=errors.append)) + #assert len(errors) == 0 + assert len(l) == 4 + for numi, i in enumerate(l): + for numj, j in enumerate(l): + if numj != numi: + assert hash(i) != hash(j) + assert i != j From fijal at codespeak.net Wed Jan 3 20:20:42 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 3 Jan 2007 20:20:42 +0100 (CET) Subject: [py-svn] r36132 - py/dist/py/test/testing Message-ID: <20070103192042.7B7D110072@code0.codespeak.net> Author: fijal Date: Wed Jan 3 20:20:40 2007 New Revision: 36132 Modified: py/dist/py/test/testing/test_collect.py Log: Remove unnecessary code Modified: py/dist/py/test/testing/test_collect.py ============================================================================== --- py/dist/py/test/testing/test_collect.py (original) +++ py/dist/py/test/testing/test_collect.py Wed Jan 3 20:20:40 2007 @@ -428,9 +428,7 @@ """)) tmp.ensure("__init__.py") col = py.test.collect.Directory(tmp) - errors = [] - l = list(col.tryiter(reporterror=errors.append)) - #assert len(errors) == 0 + l = list(col.tryiter()) assert len(l) == 4 for numi, i in enumerate(l): for numj, j in enumerate(l): From fijal at codespeak.net Thu Jan 4 09:49:39 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 4 Jan 2007 09:49:39 +0100 (CET) Subject: [py-svn] r36138 - in py/dist/py/test/rsession: . testing Message-ID: <20070104084939.D9B4910070@code0.codespeak.net> Author: fijal Date: Thu Jan 4 09:49:34 2007 New Revision: 36138 Modified: py/dist/py/test/rsession/master.py py/dist/py/test/rsession/reporter.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_boxing.py py/dist/py/test/rsession/testing/test_master.py py/dist/py/test/rsession/testing/test_rsession.py Log: Added check if test was not actually reported. This is intermediate checkin, because it does nothing actually. Modified: py/dist/py/test/rsession/master.py ============================================================================== --- py/dist/py/test/rsession/master.py (original) +++ py/dist/py/test/rsession/master.py Thu Jan 4 09:49:34 2007 @@ -6,15 +6,21 @@ from py.__.test.rsession import report class MasterNode(object): - def __init__(self, channel, reporter): + def __init__(self, channel, reporter, done_dict): self.channel = channel self.reporter = reporter - channel.setcallback(self.receive_result) + + def callback(outcome): + #import pdb;pdb.set_trace() + item = self.pending.pop() + if not item in done_dict: + self.receive_result(outcome, item) + done_dict[item] = True + channel.setcallback(callback) self.pending = [] - def receive_result(self, outcomestring): + def receive_result(self, outcomestring, item): repr_outcome = ReprOutcome(outcomestring) - item = self.pending.pop() # send finish report self.reporter(report.ReceivedItemOutcome( self.channel, item, repr_outcome)) @@ -48,7 +54,6 @@ break waiter() - def setup_slave(gateway, pkgpath, options): from py.__.test.rsession import slave import os Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Thu Jan 4 09:49:34 2007 @@ -2,9 +2,7 @@ """ reporter - different reporter for different purposes ;-) Still lacks: - 1. Reporting of Failed to load module inside the module - 2. Tests for remote reporter - 3. Hanging nodes are not good done + 1. Hanging nodes are not good done """ import py Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Thu Jan 4 09:49:34 2007 @@ -203,14 +203,16 @@ yield y itemgenerator = itemgen() - dispatch_loop(nodes, itemgenerator, checkfun) - teardown_hosts(reporter, [node.channel for node in nodes], nodes, - exitfirst=self.config.option.exitfirst) + try: + dispatch_loop(nodes, itemgenerator, checkfun) + finally: + teardown_hosts(reporter, [node.channel for node in nodes], nodes, + exitfirst=self.config.option.exitfirst) + if startserverflag: + from py.__.test.rsession.web import kill_server + kill_server() reporter(report.Nodes(nodes)) retval = reporter(report.TestFinished()) - if startserverflag: - from py.__.test.rsession.web import kill_server - kill_server() return retval class LSession(AbstractSession): Modified: py/dist/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/dist/py/test/rsession/testing/test_boxing.py (original) +++ py/dist/py/test/rsession/testing/test_boxing.py Thu Jan 4 09:49:34 2007 @@ -92,4 +92,3 @@ os.kill(pid, 15) par(pid) assert b.signal == 15 - Modified: py/dist/py/test/rsession/testing/test_master.py ============================================================================== --- py/dist/py/test/rsession/testing/test_master.py (original) +++ py/dist/py/test/rsession/testing/test_master.py Thu Jan 4 09:49:34 2007 @@ -42,7 +42,7 @@ ch = DummyChannel() reportlist = [] - mnode = MasterNode(ch, reportlist.append) + mnode = MasterNode(ch, reportlist.append, {}) mnode.send(py.test.Item("ok")) mnode.send(py.test.Item("notok")) ch.callback(Outcome().make_repr()) @@ -53,6 +53,16 @@ assert received[0].outcome.passed assert not received[1].outcome.passed +def test_unique_nodes(): + ch = DummyChannel() + reportlist = [] + mnode = MasterNode(ch, reportlist.append, {}) + mnode.send(py.test.Item("ok")) + mnode.send(py.test.Item("ok")) + ch.callback(Outcome().make_repr()) + ch.callback(Outcome().make_repr()) + assert len(reportlist) == 3 + def test_outcome_repr(): out = ReprOutcome(Outcome(skipped=True).make_repr()) s = repr(out) @@ -97,7 +107,7 @@ def open_gw(): gw = py.execnet.PopenGateway() channel = setup_slave(gw, pkgdir, remote_options.d) - mn = MasterNode(channel, simple_report) + mn = MasterNode(channel, simple_report, {}) return mn master_nodes = [open_gw(), open_gw(), open_gw()] Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Thu Jan 4 09:49:34 2007 @@ -165,6 +165,7 @@ passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] skippedevents = [i for i in testevents if i.outcome.skipped] + assert len(testevents) == 4 assert len(passevents) == 1 assert len(failevents) == 3 tb = failevents[0].outcome.excinfo.traceback From fijal at codespeak.net Thu Jan 4 09:52:34 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 4 Jan 2007 09:52:34 +0100 (CET) Subject: [py-svn] r36139 - py/dist/py/test/rsession Message-ID: <20070104085234.9259D10070@code0.codespeak.net> Author: fijal Date: Thu Jan 4 09:52:33 2007 New Revision: 36139 Modified: py/dist/py/test/rsession/hostmanage.py Log: Forgotten to check that one in Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Thu Jan 4 09:52:33 2007 @@ -63,10 +63,12 @@ hosts.append((num, host, gw, str(pkgdir.dirpath()))) return hosts -# XXX: Options has grown a bit too much, but most of them is just for tests +# XXX: Options has grown a bit too much, but most of them are just for tests def init_hosts(reporter, sshhosts, relpath, pkgdir, rsync_roots=None, \ remote_python=None, remote_options={}, optimise_localhost=True,\ - do_sync=True): + do_sync=True, done_dict=None): + if done_dict is None: + done_dict = {} assert pkgdir.join("__init__.py").check(), ( "%s probably wrong" %(pkgdir,)) assert relpath, relpath @@ -98,14 +100,14 @@ rsync.send(pkgdir.dirpath()) # hosts ready - return setup_nodes(hosts, pkgdir, remote_options, reporter) + return setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict) -def setup_nodes(hosts, pkgdir, remote_options, reporter): +def setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict): nodes = [] for num, host, gw, remoterootpath in hosts: ch = setup_slave(gw, os.path.join(remoterootpath, pkgdir.basename), remote_options) - nodes.append(MasterNode(ch, reporter)) + nodes.append(MasterNode(ch, reporter, done_dict)) return nodes From fijal at codespeak.net Thu Jan 4 11:18:13 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 4 Jan 2007 11:18:13 +0100 (CET) Subject: [py-svn] r36141 - py/dist/py/test/rsession/testing Message-ID: <20070104101813.9F7A110070@code0.codespeak.net> Author: fijal Date: Thu Jan 4 11:18:06 2007 New Revision: 36141 Modified: py/dist/py/test/rsession/testing/test_webjs.py Log: Fixed tests to new pypy.js api. Modified: py/dist/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/dist/py/test/rsession/testing/test_webjs.py (original) +++ py/dist/py/test/rsession/testing/test_webjs.py Thu Jan 4 11:18:06 2007 @@ -13,6 +13,7 @@ from pypy.translator.js.modules import dom mod.dom = dom dom.window = dom.Window(html) + dom.document = dom.window.document config, args = py.test.Config.parse([]) from py.__.test.rsession.rsession import session_options session_options.bind_config(config) @@ -120,5 +121,5 @@ webjs.process(msg) schedule_callbacks(exported_methods) # ouch - assert dom.get_document().getElementById('modules/foo.py').childNodes[1].\ + assert dom.document.getElementById('modules/foo.py').childNodes[1].\ childNodes[0].childNodes[0].childNodes[0].nodeValue == 'F' From fijal at codespeak.net Mon Jan 8 10:09:17 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 10:09:17 +0100 (CET) Subject: [py-svn] r36221 - in py/dist/py/test/rsession: . testing Message-ID: <20070108090917.5DD4A10071@code0.codespeak.net> Author: fijal Date: Mon Jan 8 10:09:11 2007 New Revision: 36221 Modified: py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/master.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_master.py py/dist/py/test/rsession/testing/test_rsession.py py/dist/py/test/rsession/web.py Log: Added rescheduling of tests when there are empty hosts. Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Mon Jan 8 10:09:11 2007 @@ -3,7 +3,7 @@ import time import thread, threading from py.__.test.rsession.master import \ - setup_slave, MasterNode, dispatch_loop + setup_slave, MasterNode from py.__.test.rsession import report from py.__.test.rsession.rsync import RSync Modified: py/dist/py/test/rsession/master.py ============================================================================== --- py/dist/py/test/rsession/master.py (original) +++ py/dist/py/test/rsession/master.py Mon Jan 8 10:09:11 2007 @@ -35,24 +35,47 @@ # send start report self.reporter(report.SendItem(self.channel, item)) +def itemgen(colitems, reporter, keyword, reporterror): + for x in colitems: + for y in x.tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword): + yield y + +def randomgen(items, done_dict): + """ Generator, which randomly gets all the tests from items as long + as they're not in done_dict + """ + import random + while items: + values = items.keys() + item = values[int(random.random()*len(values))] + if item in done_dict: + del items[item] + else: + yield item + def dispatch_loop(masternodes, itemgenerator, shouldstop, - waiter = lambda: py.std.time.sleep(0.1)): - from py.__.test.rsession.rsession import session_options + waiter = lambda: py.std.time.sleep(0.1), + max_tasks_per_node=None): + if not max_tasks_per_node: + from py.__.test.rsession.rsession import session_options - max_tasks_per_node = session_options.max_tasks_per_node + max_tasks_per_node = session_options.max_tasks_per_node + all_tests = {} while 1: try: for node in masternodes: if len(node.pending) < max_tasks_per_node: item = itemgenerator.next() + all_tests[item] = True if shouldstop(): for _node in masternodes: _node.send(StopIteration) # magic connector - return + return None node.send(item) except StopIteration: break waiter() + return all_tests def setup_slave(gateway, pkgpath, options): from py.__.test.rsession import slave Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 10:09:11 2007 @@ -10,7 +10,7 @@ from py.__.test.rsession import report from py.__.test.rsession.master import \ - setup_slave, MasterNode, dispatch_loop + setup_slave, MasterNode, dispatch_loop, itemgen, randomgen from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ @@ -168,7 +168,7 @@ class RSession(AbstractSession): """ Remote version of session """ - def main(self, args, reporter=None): + def main(self, args, reporter=None, override_checkfun=None): """ main loop for running tests. """ if not args: args = [py.path.local()] @@ -182,6 +182,8 @@ session_options.bind_config(self.config) reporter, checkfun, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) + if override_checkfun: + checkfun = override_checkfun reporter(report.TestStarted(sshhosts)) pkgdir = self.getpkgdir(args[0]) colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) @@ -190,21 +192,24 @@ except: remotepython = None + done_dict = {} nodes = init_hosts(reporter, sshhosts, directories, pkgdir, rsync_roots, remotepython, remote_options=remote_options.d, - optimise_localhost=self.optimise_localhost) + optimise_localhost=self.optimise_localhost, done_dict=done_dict) reporter(report.RsyncFinished()) keyword = self.config.option.keyword - - def itemgen(): - for x in colitems: - for y in x.tryiter(reporterror = lambda x: self.reporterror(reporter, x), keyword = keyword): - yield y - itemgenerator = itemgen() + itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) try: - dispatch_loop(nodes, itemgenerator, checkfun) + all_tests = dispatch_loop(nodes, itemgenerator, checkfun) + if all_tests: + todo = {} + for key, value in all_tests.items(): + if key not in done_dict: + todo[key] = True + rg = randomgen(todo, done_dict) + dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) finally: teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) Modified: py/dist/py/test/rsession/testing/test_master.py ============================================================================== --- py/dist/py/test/rsession/testing/test_master.py (original) +++ py/dist/py/test/rsession/testing/test_master.py Mon Jan 8 10:09:11 2007 @@ -9,7 +9,7 @@ if sys.platform == 'win32': py.test.skip("rsession is unsupported on Windows.") -from py.__.test.rsession.master import dispatch_loop, setup_slave, MasterNode +from py.__.test.rsession.master import dispatch_loop, setup_slave, MasterNode, randomgen from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec from py.__.test.rsession import report @@ -118,3 +118,16 @@ [funcpass_item] * 5 + [funcfail_item] * 5) shouldstop = lambda : False dispatch_loop(master_nodes, itemgenerator, shouldstop) + +def test_randomgen(): + d = {} + gen = randomgen({1:True, 2:True, 3:True}, d) + for i in range(100): + assert gen.next() in [1,2,3] + d[3] = True + for i in range(100): + assert gen.next() in [1,2] + d[2] = True + d[1] = True + py.test.raises(StopIteration, "gen.next()") + Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Mon Jan 8 10:09:11 2007 @@ -109,7 +109,8 @@ ## res = channel.receive() ## assert res == "ok" - def test_example_distribution_minus_x(self): + def test_example_distribution_minus_x(self): + #py.test.skip("Works, but does not test what it should") # XXX find a better way for the below tmpdir = py.path.local(py.__file__).dirpath().dirpath() tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" @@ -131,7 +132,11 @@ config, args = py.test.Config.parse(args) rsession = RSession(config) allevents = [] - rsession.main(args, reporter=allevents.append) + def check(): + return [x for x in allevents if isinstance(x, report.ReceivedItemOutcome) and + not x.outcome.passed] + + rsession.main(args, reporter=allevents.append, override_checkfun=check) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 2 Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Mon Jan 8 10:09:11 2007 @@ -24,11 +24,6 @@ FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", "show_host", "hide_host"] -def escape(s): - return s - #return s.replace("&", "&").replace("<", "<").replace(">", ">"). \ - # replace("'", "\\'").replace(" ", " ").replace("\n", "
") - try: try: if not session_options.import_pypy: @@ -160,12 +155,12 @@ def show_skip(self, item_name="aa"): return {'item_name': item_name, - 'reason': escape(self.skip_reasons[item_name])} + 'reason': self.skip_reasons[item_name]} show_skip = described(retval={"aa": "aa"})(show_skip) def show_fail(self, item_name="aa"): return {'item_name':item_name, - 'traceback':escape(str(self.fail_reasons[item_name])), + 'traceback':str(self.fail_reasons[item_name]), 'stdout':self.stdout[item_name], 'stderr':self.stderr[item_name]} show_fail = described(retval={"aa": "aa"})(show_fail) @@ -260,7 +255,7 @@ args['reason'] = str(event.excinfo.value) else: args = {} - args['event'] = escape(str(event)) + args['event'] = str(event) args['type'] = event.__class__.__name__ return args @@ -287,6 +282,7 @@ self.pending_events.put(event) def report_ItemStart(self, event): + #if isinstance(event.item, py.test.collect.Module): self.pending_events.put(event) def report_unknown(self, event): @@ -391,7 +387,6 @@ javascript_source = rpython2javascript(webjs, FUNCTION_LIST, use_pdb=False) - # XXX: This did not work for some reason, no idea why open(str(js_name), "w").write(javascript_source) self.serve_data("text/javascript", javascript_source) else: From fijal at codespeak.net Mon Jan 8 10:12:09 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 10:12:09 +0100 (CET) Subject: [py-svn] r36222 - py/dist/py/documentation Message-ID: <20070108091209.6058310071@code0.codespeak.net> Author: fijal Date: Mon Jan 8 10:12:08 2007 New Revision: 36222 Modified: py/dist/py/documentation/test.txt Log: Updated to more current state. Modified: py/dist/py/documentation/test.txt ============================================================================== --- py/dist/py/documentation/test.txt (original) +++ py/dist/py/documentation/test.txt Mon Jan 8 10:12:08 2007 @@ -720,10 +720,7 @@ Differences from local tests ---------------------------- -* The only working command line options are ``--session=R``, ``-x`` and ``-k``. * Test order is *not* guaranteed. -* ``-x`` is currently not working in the web reporter. -* Hanging nodes or tests are not detected. * ``conftest.py`` cannot reference files outside of the copied packages. Configuration From fijal at codespeak.net Mon Jan 8 10:44:22 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 10:44:22 +0100 (CET) Subject: [py-svn] r36224 - in py/dist/py/test/rsession: . webdata Message-ID: <20070108094422.9A98D10075@code0.codespeak.net> Author: fijal Date: Mon Jan 8 10:44:17 2007 New Revision: 36224 Modified: py/dist/py/test/rsession/webdata/source.js py/dist/py/test/rsession/webjs.py Log: Hopefully resolve the mozilla issue with displaying tests. Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/dist/py/test/rsession/webjs.py ============================================================================== --- py/dist/py/test/rsession/webjs.py (original) +++ py/dist/py/test/rsession/webjs.py Mon Jan 8 10:44:17 2007 @@ -228,14 +228,16 @@ def show_host(host_name="aa"): elem = dom.document.getElementById("jobs") - while len(elem.childNodes): + if elem.childNodes: elem.removeChild(elem.childNodes[0]) + tbody = create_elem("tbody") for item in glob.host_pending[host_name]: tr = create_elem("tr") td = create_elem("td") td.appendChild(create_text_elem(item)) tr.appendChild(td) - elem.appendChild(tr) + tbody.appendChild(tr) + elem.appendChild(tbody) elem.style.visibility = "visible" glob.host = host_name dom.setTimeout(reshow_host, 100) From fijal at codespeak.net Mon Jan 8 10:44:34 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 10:44:34 +0100 (CET) Subject: [py-svn] r36225 - py/dist/py/test/rsession/webdata Message-ID: <20070108094434.D89FA1007A@code0.codespeak.net> Author: fijal Date: Mon Jan 8 10:44:32 2007 New Revision: 36225 Modified: py/dist/py/test/rsession/webdata/index.html Log: Forgotten to check that one in. Modified: py/dist/py/test/rsession/webdata/index.html ============================================================================== --- py/dist/py/test/rsession/webdata/index.html (original) +++ py/dist/py/test/rsession/webdata/index.html Mon Jan 8 10:44:32 2007 @@ -81,9 +81,7 @@ - - - +
From fijal at codespeak.net Mon Jan 8 11:24:26 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 11:24:26 +0100 (CET) Subject: [py-svn] r36232 - py/dist/py/test/rsession/testing Message-ID: <20070108102426.4B0031007D@code0.codespeak.net> Author: fijal Date: Mon Jan 8 11:24:24 2007 New Revision: 36232 Modified: py/dist/py/test/rsession/testing/test_rsession.py Log: Make test use ensuretemp. Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Mon Jan 8 11:24:24 2007 @@ -110,12 +110,9 @@ ## assert res == "ok" def test_example_distribution_minus_x(self): - #py.test.skip("Works, but does not test what it should") - # XXX find a better way for the below - tmpdir = py.path.local(py.__file__).dirpath().dirpath() + tmpdir = py.test.ensuretemp("example_distribution_minus_x") tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" disthosts = [%r] - distrsync_roots = ["sub", "py"] """ % 'localhost')) tmpdir.ensure("sub", "__init__.py") tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" From fijal at codespeak.net Mon Jan 8 11:25:40 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 11:25:40 +0100 (CET) Subject: [py-svn] r36233 - py/dist/py/test/rsession Message-ID: <20070108102540.58FF31007D@code0.codespeak.net> Author: fijal Date: Mon Jan 8 11:25:39 2007 New Revision: 36233 Modified: py/dist/py/test/rsession/rsession.py Log: Kill some dead code. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 11:25:39 2007 @@ -247,9 +247,7 @@ if runner is None and self.config.option.apigen: from py.__.apigen.tracer.tracer import Tracer - # XXX module = py - #module = __import__(str(pkgdir.join('__init__.py'))) try: self.docstorage = self.config.getinitialvalue('ApiGen').get_doc_storage() except (ValueError, AttributeError): @@ -257,24 +255,13 @@ "way of generating DocStorage") self.tracer = Tracer(self.docstorage) runner = apigen_runner - #elif runner is None and (self.config.option.usepdb or self.config.option.nocapture): - # runner = plain_runner - #elif runner is None: - # runner = box_runner + elif runner is None: runner = RunnerPolicy[session_options.runner_policy] keyword = self.config.option.keyword - def itemgen(): - for x in colitems: - for y in x.tryiter(reporterror = lambda x: self.reporterror(reporter, x), keyword = keyword): - yield y - - itemgenerator = itemgen() - #assert 0, "\n".join([",".join(x.listnames()) for x in - # list(itemgenerator)]) - # XXX: We have to decide which runner to use at this point + itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) local_loop(self, reporter, itemgenerator, checkfun, self.config, runner=runner) retval = reporter(report.TestFinished()) @@ -282,7 +269,6 @@ from py.__.test.rsession.web import kill_server kill_server() - #py.test.Function.state.teardown_all() if not self.config.option.nomagic: py.magic.revoke(assertion=1) From hpk at codespeak.net Mon Jan 8 11:27:09 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 11:27:09 +0100 (CET) Subject: [py-svn] r36234 - py/dist/py/documentation Message-ID: <20070108102709.33DAD1007D@code0.codespeak.net> Author: hpk Date: Mon Jan 8 11:27:06 2007 New Revision: 36234 Modified: py/dist/py/documentation/TODO.txt Log: tracking todo-items for 0.9 in py/documentation ... Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Mon Jan 8 11:27:06 2007 @@ -1,6 +1,34 @@ -Things to do before 0.8.0 +Things to do before 0.9.0 ========================= +review all py lib documentation +------------------------------------- + +streamline exported API +------------------------------------- + +* move not-to-be-exported Gateway() methods to _ - methods. +* docstrings for all exported API + +testing +----------- + +* windows tests +* these should all work on 0.9 and on the py lib and pypy: + - running "py.test -s" + - running "py.test --pdb" + - running "py.test --looponfailing" + - running "py.test" distributed on some hosts + +* tests should not create any tempdirectories in the source code base + +* try to be as 2.2 compatible as possible + + +quality +--------- + +* don't have distutils install ----------------- From fijal at codespeak.net Mon Jan 8 11:27:37 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 11:27:37 +0100 (CET) Subject: [py-svn] r36235 - py/dist/py/test/rsession Message-ID: <20070108102737.27E261007F@code0.codespeak.net> Author: fijal Date: Mon Jan 8 11:27:30 2007 New Revision: 36235 Modified: py/dist/py/test/rsession/rsession.py Log: move kill server to a separate method Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 11:27:30 2007 @@ -152,6 +152,12 @@ reporter(report.FailedTryiter(excinfo, item)) reporterror = staticmethod(reporterror) + def kill_server(self, startserverflag): + if startserverflag: + from py.__.test.rsession.web import kill_server + kill_server() + + def parse_directories(sshhosts): # dictionary containing directories for hosts # XXX: should be class with some info like key, etc. in future @@ -213,9 +219,7 @@ finally: teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) - if startserverflag: - from py.__.test.rsession.web import kill_server - kill_server() + self.kill_server(startserverflag) reporter(report.Nodes(nodes)) retval = reporter(report.TestFinished()) return retval @@ -265,9 +269,7 @@ local_loop(self, reporter, itemgenerator, checkfun, self.config, runner=runner) retval = reporter(report.TestFinished()) - if startserverflag: - from py.__.test.rsession.web import kill_server - kill_server() + self.kill_server(startserverflag) if not self.config.option.nomagic: py.magic.revoke(assertion=1) From fijal at codespeak.net Mon Jan 8 11:35:41 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 11:35:41 +0100 (CET) Subject: [py-svn] r36237 - py/dist/py/test/rsession Message-ID: <20070108103541.616EB10080@code0.codespeak.net> Author: fijal Date: Mon Jan 8 11:35:40 2007 New Revision: 36237 Modified: py/dist/py/test/rsession/rsession.py Log: a bit of cleanup of rsession.main Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 11:35:40 2007 @@ -190,39 +190,43 @@ sshhosts, RemoteReporter) if override_checkfun: checkfun = override_checkfun + reporter(report.TestStarted(sshhosts)) - pkgdir = self.getpkgdir(args[0]) - colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) try: remotepython = self.config.getinitialvalue("dist_remotepython") except: remotepython = None + pkgdir = self.getpkgdir(args[0]) done_dict = {} nodes = init_hosts(reporter, sshhosts, directories, pkgdir, rsync_roots, remotepython, remote_options=remote_options.d, optimise_localhost=self.optimise_localhost, done_dict=done_dict) reporter(report.RsyncFinished()) - keyword = self.config.option.keyword - - itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) try: - all_tests = dispatch_loop(nodes, itemgenerator, checkfun) - if all_tests: - todo = {} - for key, value in all_tests.items(): - if key not in done_dict: - todo[key] = True - rg = randomgen(todo, done_dict) - dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) + self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) finally: teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) self.kill_server(startserverflag) reporter(report.Nodes(nodes)) - retval = reporter(report.TestFinished()) - return retval + return reporter(report.TestFinished()) + + def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): + colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) + keyword = self.config.option.keyword + itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) + + all_tests = dispatch_loop(nodes, itemgenerator, checkfun) + if all_tests: + todo = {} + for key, value in all_tests.items(): + if key not in done_dict: + todo[key] = True + rg = randomgen(todo, done_dict) + dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) + class LSession(AbstractSession): """ Local version of session From fijal at codespeak.net Mon Jan 8 11:38:33 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 11:38:33 +0100 (CET) Subject: [py-svn] r36238 - py/dist/py/test/rsession Message-ID: <20070108103833.6FD591007F@code0.codespeak.net> Author: fijal Date: Mon Jan 8 11:38:32 2007 New Revision: 36238 Modified: py/dist/py/test/rsession/rsession.py Log: Another round of cleanups Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 11:38:32 2007 @@ -178,24 +178,15 @@ """ main loop for running tests. """ if not args: args = [py.path.local()] - sshhosts = self.config.getinitialvalue("disthosts") - directories = parse_directories(sshhosts) - try: - rsync_roots = self.config.getinitialvalue("distrsync_roots") - except: - rsync_roots = None # all files and directories in the pkgdir session_options.bind_config(self.config) + sshhosts, directories, remotepython, rsync_roots = self.read_distributed_config() reporter, checkfun, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) if override_checkfun: checkfun = override_checkfun reporter(report.TestStarted(sshhosts)) - try: - remotepython = self.config.getinitialvalue("dist_remotepython") - except: - remotepython = None pkgdir = self.getpkgdir(args[0]) done_dict = {} @@ -213,6 +204,19 @@ reporter(report.Nodes(nodes)) return reporter(report.TestFinished()) + def read_distributed_config(self): + try: + rsync_roots = self.config.getinitialvalue("distrsync_roots") + except: + rsync_roots = None # all files and directories in the pkgdir + sshhosts = self.config.getinitialvalue("disthosts") + directories = parse_directories(sshhosts) + try: + remotepython = self.config.getinitialvalue("dist_remotepython") + except: + remotepython = None + return sshhosts, directories, remotepython, rsync_roots + def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) keyword = self.config.option.keyword From fijal at codespeak.net Mon Jan 8 11:41:08 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 11:41:08 +0100 (CET) Subject: [py-svn] r36239 - py/dist/py/test/rsession Message-ID: <20070108104108.831F510080@code0.codespeak.net> Author: fijal Date: Mon Jan 8 11:41:07 2007 New Revision: 36239 Modified: py/dist/py/test/rsession/rsession.py Log: Cleanup of Lsession.main Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 11:41:07 2007 @@ -256,20 +256,9 @@ pkgdir = self.getpkgdir(args[0]) colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) reporter(report.RsyncFinished()) - - if runner is None and self.config.option.apigen: - from py.__.apigen.tracer.tracer import Tracer - module = py - try: - self.docstorage = self.config.getinitialvalue('ApiGen').get_doc_storage() - except (ValueError, AttributeError): - raise NotImplementedError("Need to provide conftest " - "way of generating DocStorage") - self.tracer = Tracer(self.docstorage) - runner = apigen_runner - elif runner is None: - runner = RunnerPolicy[session_options.runner_policy] + if runner is None: + runner = self.init_runner() keyword = self.config.option.keyword @@ -281,7 +270,11 @@ if not self.config.option.nomagic: py.magic.revoke(assertion=1) - + + self.write_docs() + return retval + + def write_docs(self): if self.config.option.apigen: try: apigen = self.config.getinitialvalue('ApiGen') @@ -290,4 +283,18 @@ "provided way of doing that in conftest") else: apigen.write_docs(self.docstorage) - return retval + + + def init_runner(self): + if self.config.option.apigen: + from py.__.apigen.tracer.tracer import Tracer + module = py + try: + self.docstorage = self.config.getinitialvalue('ApiGen').get_doc_storage() + except (ValueError, AttributeError): + raise NotImplementedError("Need to provide conftest " + "way of generating DocStorage") + self.tracer = Tracer(self.docstorage) + return apigen_runner + else: + return RunnerPolicy[session_options.runner_policy] From hpk at codespeak.net Mon Jan 8 11:44:41 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 11:44:41 +0100 (CET) Subject: [py-svn] r36240 - py/dist/py/test/rsession/testing Message-ID: <20070108104441.8EDD010082@code0.codespeak.net> Author: hpk Date: Mon Jan 8 11:44:40 2007 New Revision: 36240 Modified: py/dist/py/test/rsession/testing/test_lsession.py Log: fix tempdir creation Modified: py/dist/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_lsession.py (original) +++ py/dist/py/test/rsession/testing/test_lsession.py Mon Jan 8 11:44:40 2007 @@ -7,14 +7,8 @@ from py.__.test.rsession import report from py.__.test.rsession.local import box_runner, plain_runner -basepath = py.path.local(py.__file__).dirpath().dirpath() - -try: - tmp = basepath.mkdir("pytemp") -except py.error.EEXIST: - tmp = basepath.join("pytemp") - tmp.remove(rec=1) - tmp = basepath.mkdir("pytemp") +def setup_module(mod): + mod.tmp = py.test.ensuretemp("lsession_module") class TestLSession(object): # XXX: Some tests of that should be run as well on RSession, while From hpk at codespeak.net Mon Jan 8 11:47:18 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 11:47:18 +0100 (CET) Subject: [py-svn] r36241 - in py/dist/py/documentation: . apigen talk Message-ID: <20070108104718.A832D10084@code0.codespeak.net> Author: hpk Date: Mon Jan 8 11:47:15 2007 New Revision: 36241 Modified: py/dist/py/documentation/ (props changed) py/dist/py/documentation/TODO.txt py/dist/py/documentation/apigen/ (props changed) py/dist/py/documentation/talk/ (props changed) Log: more todo, ignore html files Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Mon Jan 8 11:47:15 2007 @@ -23,12 +23,8 @@ * tests should not create any tempdirectories in the source code base * try to be as 2.2 compatible as possible + (use e.g. py.builtin.enumerate instead of "enumerate" directly) - -quality ---------- - -* don't have distutils install ----------------- From fijal at codespeak.net Mon Jan 8 11:53:15 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 11:53:15 +0100 (CET) Subject: [py-svn] r36242 - py/dist/py/test/rsession Message-ID: <20070108105315.AA7D710083@code0.codespeak.net> Author: fijal Date: Mon Jan 8 11:53:14 2007 New Revision: 36242 Modified: py/dist/py/test/rsession/rsession.py Log: Comment out not working code. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 11:53:14 2007 @@ -223,13 +223,13 @@ itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) all_tests = dispatch_loop(nodes, itemgenerator, checkfun) - if all_tests: - todo = {} - for key, value in all_tests.items(): - if key not in done_dict: - todo[key] = True - rg = randomgen(todo, done_dict) - dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) + #if all_tests: + # todo = {} + # for key, value in all_tests.items(): + # if key not in done_dict: + # todo[key] = True + # rg = randomgen(todo, done_dict) + # dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) class LSession(AbstractSession): From hpk at codespeak.net Mon Jan 8 12:16:54 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 12:16:54 +0100 (CET) Subject: [py-svn] r36245 - py/dist/py/documentation Message-ID: <20070108111654.6983F10070@code0.codespeak.net> Author: hpk Date: Mon Jan 8 12:16:51 2007 New Revision: 36245 Modified: py/dist/py/documentation/TODO.txt Log: adding more tasks (apigen related) Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Mon Jan 8 12:16:51 2007 @@ -10,6 +10,18 @@ * move not-to-be-exported Gateway() methods to _ - methods. * docstrings for all exported API +packaging +------------------------------------- + +APIGEN / source viewer +------------------------------------- + +* deploying all that is neccessary on codespeak.net + +* (cfbolz, guido) writing the ReST directive, make it available + with eg py.__.misc.docutils.install_doclink(NAME, urlbase ...) + in a tested way. call the install from e.g. py/documentation/conftest.py + testing ----------- @@ -25,6 +37,21 @@ * try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) +distributed testing +---------------------- + +* the main rsession methods should have docstrings + explaining the purpose + +* move RSync class to py.execnet.RSync, document its usage and + features in the class and method docstrings + + +other +------- + +* no lines longer than 80 characters + distutils install ----------------- From hpk at codespeak.net Mon Jan 8 12:39:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 12:39:13 +0100 (CET) Subject: [py-svn] r36249 - in py/dist/py/documentation: . future Message-ID: <20070108113913.74DA01007D@code0.codespeak.net> Author: hpk Date: Mon Jan 8 12:39:06 2007 New Revision: 36249 Added: py/dist/py/documentation/future/ (props changed) py/dist/py/documentation/future/code_template.txt - copied unchanged from r36246, py/dist/py/documentation/code_template.txt py/dist/py/documentation/future/future.txt - copied, changed from r36246, py/dist/py/documentation/future.txt Removed: py/dist/py/documentation/code_template.txt py/dist/py/documentation/future.txt Modified: py/dist/py/documentation/coding-style.txt py/dist/py/documentation/getting-started.txt py/dist/py/documentation/home.txt py/dist/py/documentation/index.txt py/dist/py/documentation/misc.txt py/dist/py/documentation/why_py.txt Log: creating a future subdir and starting to move things into it to have a cleaner "documentation" directory Deleted: /py/dist/py/documentation/code_template.txt ============================================================================== --- /py/dist/py/documentation/code_template.txt Mon Jan 8 12:39:06 2007 +++ (empty file) @@ -1,640 +0,0 @@ -=============================================================== -py.code_template: Lightweight and flexible code template system -=============================================================== - -.. contents:: -.. sectnum:: - -Motivation -========== - -There are as many python templating systems as there are web frameworks -(a lot). This is partly because it is so darned easy to write a templating -system in Python. What are the distinguishing characteristics of the -py.code_template templating system? - - * Optimized for generating code (Python, C, bash scripts, etc.), - not XML or HTML - - * Designed for use by Python programmers, not by web artists - - + Aesthetic sensibilities are different - - + The templates should be an organic part of a module -- just more code - - + Templates do not need to be incredibly full-featured, because - programmers are perfectly capable of escaping to Python for - advanced features. - - - No requirement to support inheritance - - No requirement to support exec - - * Designed so that templates can be coded in the most natural way - for the task at hand - - + Generation of code and scripts often does not follow MVC paradigm! - - + Small template fragments are typically coded *inside* Python modules - - + Sometimes it is natural to put strings inside code; sometimes it is - natural to put code inside strings. Both should be supported as - reasonably and naturally as possible. - -Imaginary-world examples -======================== - -These would be real-world examples, but, not only is this module not yet -implemented, as of now, PyPy is not incredibly useful to the average -programmer... - -translator/c/genc.py --------------------- - -The original function:: - - def gen_readable_parts_of_main_c_file(f, database, preimplementationlines=[]): - # - # All declarations - # - structdeflist = database.getstructdeflist() - print >> f - print >> f, '/***********************************************************/' - print >> f, '/*** Structure definitions ***/' - print >> f - for node in structdeflist: - print >> f, 'struct %s;' % node.name - print >> f - for node in structdeflist: - for line in node.definition(): - print >> f, line - print >> f - print >> f, '/***********************************************************/' - print >> f, '/*** Forward declarations ***/' - print >> f - for node in database.globalcontainers(): - for line in node.forward_declaration(): - print >> f, line - - # - # Implementation of functions and global structures and arrays - # - print >> f - print >> f, '/***********************************************************/' - print >> f, '/*** Implementations ***/' - print >> f - for line in preimplementationlines: - print >> f, line - print >> f, '#include "src/g_include.h"' - print >> f - blank = True - for node in database.globalcontainers(): - if blank: - print >> f - blank = False - for line in node.implementation(): - print >> f, line - blank = True - -This could be refactored heavily. An initial starting point -would look something like this, although later, the template -instance could be passed in and reused directly, rather than -passing the file handle around:: - - def gen_readable_parts_of_main_c_file(f, database, preimplementationlines=[]): - def container_implementation(): - # Helper function designed to introduce blank lines - # between container implementations - - blank = True - for node in database.globalcontainers(): - if blank: - yield '' - blank = False - for line in node.implementation(): - yield line - blank = True - - t = code_template.Template() - # - # All declarations - # - structdeflist = database.getstructdeflist() - t.write(dedent=8, text=''' - - /***********************************************************/ - /*** Structure definitions ***/ - - {for node in structdeflist} - struct {node.name}; - {endfor} - - {for node in structdeflist} - {for line in node.definition} - {line} - {endfor} - {endfor} - - /***********************************************************/ - /*** Forward declarations ***/ - - {for node in database.globalcontainers()} - {for line in node.forward_declaration()} - {line} - {endfor} - {endfor} - - {** - ** Implementation of functions and global structures and arrays - **} - - /***********************************************************/ - /*** Implementations ***/ - - {for line in preimplementationlines} - {line} - {endfor} - - #include "src/g_include.h" - - {for line in container_implementation()} - {line} - {endfor} - """) - t.output(f) - -translator/c/genc.py gen_makefile ---------------------------------- - -The original code:: - - MAKEFILE = ''' - CC = gcc - - $(TARGET): $(OBJECTS) - \t$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBDIRS) $(LIBS) - - %.o: %.c - \t$(CC) $(CFLAGS) -o $@ -c $< $(INCLUDEDIRS) - - clean: - \trm -f $(OBJECTS) - ''' - - def gen_makefile(self, targetdir): - def write_list(lst, prefix): - for i, fn in enumerate(lst): - print >> f, prefix, fn, - if i < len(lst)-1: - print >> f, '\\' - else: - print >> f - prefix = ' ' * len(prefix) - - compiler = self.getccompiler(extra_includes=['.']) - cfiles = [] - ofiles = [] - for fn in compiler.cfilenames: - fn = py.path.local(fn).basename - assert fn.endswith('.c') - cfiles.append(fn) - ofiles.append(fn[:-2] + '.o') - - f = targetdir.join('Makefile').open('w') - print >> f, '# automatically generated Makefile' - print >> f - print >> f, 'TARGET =', py.path.local(compiler.outputfilename).basename - print >> f - write_list(cfiles, 'SOURCES =') - print >> f - write_list(ofiles, 'OBJECTS =') - print >> f - args = ['-l'+libname for libname in compiler.libraries] - print >> f, 'LIBS =', ' '.join(args) - args = ['-L'+path for path in compiler.library_dirs] - print >> f, 'LIBDIRS =', ' '.join(args) - args = ['-I'+path for path in compiler.include_dirs] - write_list(args, 'INCLUDEDIRS =') - print >> f - print >> f, 'CFLAGS =', ' '.join(compiler.compile_extra) - print >> f, 'LDFLAGS =', ' '.join(compiler.link_extra) - print >> f, MAKEFILE.strip() - f.close() - - -Could look something like this:: - - MAKEFILE = ''' - # automatically generated Makefile - - TARGET = {py.path.local(compiler.outputfilename).basename} - - {for line in write_list(cfiles, 'SOURCES =')} - {line} - {endfor} - - {for line in write_list(ofiles, 'OBJECTS =')} - {line} - {endfor} - - LIBS ={for libname in compiler.libraries} -l{libname}{endfor} - LIBDIRS ={for path in compiler.library_dirs} -L{path}{endfor} - INCLUDEDIRS ={for path in compiler.include_dirs} -I{path}{endfor} - - CFLAGS ={for extra in compiler.compile_extra} {extra}{endfor} - LDFLAGS ={for extra in compiler.link_extra} {extra}{endfor} - - CC = gcc - - $(TARGET): $(OBJECTS) - \t$(CC) $(LDFLAGS) -o $@ $(OBJECTS) $(LIBDIRS) $(LIBS) - - %.o: %.c - \t$(CC) $(CFLAGS) -o $@ -c $< $(INCLUDEDIRS) - - clean: - \trm -f $(OBJECTS) - ''' - - def gen_makefile(self, targetdir): - def write_list(lst, prefix): - for i, fn in enumerate(lst): - yield '%s %s %s' % (prefix, fn, i < len(list)-1 and '\\' or '') - prefix = ' ' * len(prefix) - - compiler = self.getccompiler(extra_includes=['.']) - cfiles = [] - ofiles = [] - for fn in compiler.cfilenames: - fn = py.path.local(fn).basename - assert fn.endswith('.c') - cfiles.append(fn) - ofiles.append(fn[:-2] + '.o') - - code_template.Template(MAKEFILE).output(targetdir.join('Makefile')) - - -translator/llvm/module/excsupport.py ------------------------------------- - -The original string:: - - invokeunwind_code = ''' - ccc %(returntype)s%%__entrypoint__%(entrypointname)s { - %%result = invoke %(cconv)s %(returntype)s%%%(entrypointname)s to label %%no_exception except label %%exception - - no_exception: - store %%RPYTHON_EXCEPTION_VTABLE* null, %%RPYTHON_EXCEPTION_VTABLE** %%last_exception_type - ret %(returntype)s %%result - - exception: - ret %(noresult)s - } - - ccc int %%__entrypoint__raised_LLVMException() { - %%tmp = load %%RPYTHON_EXCEPTION_VTABLE** %%last_exception_type - %%result = cast %%RPYTHON_EXCEPTION_VTABLE* %%tmp to int - ret int %%result - } - - internal fastcc void %%unwind() { - unwind - } - ''' - -Could look something like this if it was used in conjunction with a template:: - - invokeunwind_code = ''' - ccc {returntype}%__entrypoint__{entrypointname} { - %result = invoke {cconv} {returntype}%{entrypointname} to label %no_exception except label %exception - - no_exception: - store %RPYTHON_EXCEPTION_VTABLE* null, %RPYTHON_EXCEPTION_VTABLE** %last_exception_type - ret {returntype} %result - - exception: - ret {noresult} - } - - ccc int %__entrypoint__raised_LLVMException() { - %tmp = load %RPYTHON_EXCEPTION_VTABLE** %last_exception_type - %result = cast %RPYTHON_EXCEPTION_VTABLE* %tmp to int - ret int %result - } - - internal fastcc void %unwind() { - unwind - } - ''' - - -Template syntax -=============== - -Design decision ---------------- - -As all programmers must know by now, all the special symbols on the keyboard -are quite heavily overloaded. Often, template systems work around this fact -by having special notation like `<*` ... `*>` or {% ... %}. Some template systems -even have multiple special notations -- one for comments, one for statements, -one for expressions, etc. - -I find these hard to type and ugly. Other markups are either too lightweight, -or use characters which occur so frequently in the target languages that it -becomes hard to distinguish marked-up content from content which should be -rendered as-is. - -The compromise taken by *code_template* is to use braces (**{}**) for markup. - -This immediately raises the question: what about when the marked-up language -is C or C++? The answer is that if the leading brace is immediately followed -by whitespace, it is normal text; if not it is the start of markup. - -To support normal text which has a leading brace immediately followed by -an identifier, if the first whitespace character after the brace is a space -character (e.g. not a newline or tab), it will be removed from the output. - -Examples:: - - { This is normal text and the space between { and This will be removed} - {'this must be a valid Python expression' + ' because it is treated as markup'} - { - This is normal text, but nothing is altered (the newline is kept intact) - } - - {{1:'Any valid Python expression is allowed as markup'}[1].ljust(30)} - -.. _`Code element`: - -Elements --------- - -Templates consist of normal text and code elements. -(Comments are considered to be code elements.) - -All code elements start with a `left brace`_ which is not followed by -whitespace. - -Keyword element -~~~~~~~~~~~~~~~ - -A keyword element is a `code element`_ which starts with a keyword_. - -For example, *{if foo}* is a keyword element, but *{foo}* is a `substituted expression`_. - -Keyword -~~~~~~~ - -A keyword is a word used in `conditional text`_ or in `repeated text`_, e.g. -one of *if*, *elif*, *else*, *endif*, *for*, or *endfor*. - -Keywords are designed to match their Python equivalents. However, since -templates cannot use spacing to indicate expression nesting, the additional -keywords *endif* and *endfor* are required. - -Left brace -~~~~~~~~~~ - -All elements other than normal text start with a left brace -- the symbol '{', -sometimes known as a 'curly bracket'. A left brace is itself considered -to be normal text if it is followed by whitespace. If the whitespace starts -with a space character, that space character will be stripped from the output. -If the whitespace starts with a tab or linefeed character, the whitespace will -be left in the output. - -Normal Text -~~~~~~~~~~~ - -Normal text remains unsubstituted. Transition from text to the other elements -is effected by use of a `left brace`_ which is not followed by whitespace. - -Comment -~~~~~~~ - -A comment starts with a left brace followed by an asterisk ('{`*`'), and -ends with an asterisk followed by a right brace ('`*`}'):: - - This is a template -- this text will be copied to the output. - {* This is a comment and this text will not be copied to the output *} - - {* - Comments can span lines, - but cannot be nested - *} - -Substituted expression -~~~~~~~~~~~~~~~~~~~~~~ - -Any python expression may be used:: - - Dear {record.name}, - we are sorry to inform you that you did not win {record.contest}. - -The expression must be surrounded by braces, and there must not be any -whitespace between the leftmost brace and the start of the expression. - -The expression will automatically be converted to a string with str(). - -Conditional text -~~~~~~~~~~~~~~~~ - -The following template has text which is included conditionally:: - - This text will always be included in the output - {if foo} - This text will be included if foo is true - {elif bar} - This text will be included if foo is not true but bar is true - {else} - This text will be included if neither foo nor bar is true - {endif} - -The {elif} and {else} elements are optional. - -Repeated text -~~~~~~~~~~~~~ - -The following template shows how to pull multiple items out of a list:: - - {for student, score in sorted(scorelist)} - {student.ljust(20)} {score} - {endfor} - -Whitespace removal or modification ----------------------------------- - -In general, whitespace in `Normal Text`_ is transferred unchanged to the -output. There are three exceptions to this rule: - -Line separators -~~~~~~~~~~~~~~~ - -Each newline is converted to the final output using os.linesep. - -Beginning or end of string -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -py.code_template is designed to allow easy use of templates inside of python -modules. The canonical way to write a template is inside a triple-quoted -string, e.g.:: - - my_template = ''' - This is my template. It can have any text at all in it except - another triple-single-quote. - ''' - -To support this usage, if the first character is a newline, it will be -removed, and if the last line consists solely of whitespace with no -trailing newline, it will also be removed. - -A comment or single keyword element on a line -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Whenever a `keyword element`_ or comment_ is on a line -*by itself*, that line will not be copied to the output. - -This happens when: - - There is nothing on the line before the keyword element - or comment except whitespace (spaces and/or tabs). - - - There is nothing on the line after the keyword element - or comment except a newline. - -Note that even a multi-line comment or keyword element can -have the preceding whitespace and subsequent newline stripped -by this rule. - -The primary purpose of this rule is to allow the Python -programmer to use indentation, **even inside a template**:: - - This is a template - - {if mylist} - List items: - {for item in mylist} - - {item} - {endfor} - {endif} - -Template usage -============== - -Templates are used by importing the Template class from py.code_template, -constructing a template, and then sending data with the write() method. - -In general, there are four methods for getting the formatted data back out -of the template object: - - - read() reads all the data currently in the object - - - output(fobj) outputs the data to a file - - fobj can either be an open file object, or a string. If it is - a string, the file will be opened, written, and closed. - - - open(fobj) (or calling the object constructor with a file object) - - If the open() method is used, or if a file object is passed to - the constructor, each write() will automatically flush the data - out to the file. If the fobj is a string, it is considered to - be *owned*, otherwise it is considered to be *borrowed*. *Owned* - file objects are closed when the class is deleted. - - - write() can be explicitly called with a file object, in which case - it will invoke output() on that object after it generates the data. - -Template instantiation and methods -================================== - -template = code_template.Template(outf=None, cache=None) - -If outf is given, it will be passed to the open() method - -cache may be given as a mapping. If not given, the template will use -the shared default cache. This is not thread safe. - -template.open -------------- - -template.open(outf, borrowed = None) - -The open method closes the internal file object if it was already open, -and then re-opens it on the given file. It is an error to call open() -if there is data in the object left over from previous writes. (Call -output() instead.) - -borrowed defaults to 0 if outf is a string, and 1 if it is a file object. - -borrowed can also be set explicitly if required. - -template.close --------------- - -close() disassociates the file from the template, and closes the file if -it was not borrowed. close() is automatically called by the destructor. - -template.write --------------- - -template.write(text='', outf=None, dedent=0, localvars=None, globalvars=None, -framelevel=1) - -The write method has the following parameters: - - - text is the template itself - - - if outf is not None, the output method will be invoked on the object - after the current template is processed. If no outf is given, data - will be accumulated internal to the instance until a write() with outf - is processed, or read() or output() is called, whichever comes first, if - there is no file object. If there is a file object, data will be flushed - to the file after every write. - - - dedent, if given is applied to each line in the template, to "de-indent" - - - localvars and globalvars default to the dictionaries of the caller. A copy - of localvars is made so that the __TrueSpace__ identifier can be added. - - - cache may be given as a mapping. If not given, the template will use - the shared default cache. This is not thread safe. - - - framelevel is used to determine which stackframe to access for globals - and locals if localvars and/or globalvars are not specified. The default - is to use the caller's frame. - -The write method supports the print >> file protocol by deleting the softspace -attribute on every invocation. This allows code like:: - - t = code_template.Template() - print >> t, "Hello, world" - - -template.read --------------- - -This method reads and flushes all accumulated data in the object. Note that -if a file has been associated with the object, there will never be any data -to read. - -template.output ---------------- - -This method takes one parameter, outf. template.output() first -invokes template.read() to read and flush all accumulated data, -and then outputs the data to the file specified by outf. - -If outf has a write() method, that will be invoked with the -data. If outf has no write() method, it will be treated as -a filename, and that file will be replaced. - -Caching and thread safety -========================= - -The compiled version of every template is cached internal to the -code_template module (unless a separate cache object is specified). - -This allows efficient template reuse, but is not currently thread-safe. -Alternatively, each invocation of a template object can specify a -cache object. This is thread-safe, but not very efficient. A shared -model could be implemented later. - Modified: py/dist/py/documentation/coding-style.txt ============================================================================== --- py/dist/py/documentation/coding-style.txt (original) +++ py/dist/py/documentation/coding-style.txt Mon Jan 8 12:39:06 2007 @@ -70,4 +70,4 @@ .. _`PEP 8 Style Guide for Python Code`: http://www.python.org/peps/pep-0008.html .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev -.. _`future`: future.html +.. _`future`: future/future.html Deleted: /py/dist/py/documentation/future.txt ============================================================================== --- /py/dist/py/documentation/future.txt Mon Jan 8 12:39:06 2007 +++ (empty file) @@ -1,423 +0,0 @@ -======================================================= -Visions and ideas for further development of the py lib -======================================================= - -.. contents:: -.. sectnum:: - -This document tries to describe directions and guiding ideas -for the near-future development of the py lib. *Note that all -statements within this document - even if they sound factual - -mostly just express thoughts and ideas. They not always refer to -real code so read with some caution. This is not a reference guide -(tm). Moreover, the order in which appear here in the file does -not reflect the order in which they may be implemented.* - -.. _`general-path`: -.. _`a more general view on path objects`: - -A more general view on ``py.path`` objects -========================================== - -Seen from a more general persective, the current ``py.path.extpy`` path -offers a way to go from a file to the structured content of -a file, namely a python object. The ``extpy`` path retains some -common ``path`` operations and semantics but offers additional -methods, e.g. ``resolve()`` gets you a true python object. - -But apart from python files there are many other examples -of structured content like xml documents or INI-style -config files. While some tasks will only be convenient -to perform in a domain specific manner (e.g. applying xslt -etc.pp) ``py.path`` offers a common behaviour for -structured content paths. So far only ``py.path.extpy`` -is implemented and used by py.test to address tests -and traverse into test files. - -*You are in a maze of twisty passages, all alike* -------------------------------------------------- - -Now, for the sake of finding out a good direction, -let's consider some code that wants to find all -*sections* which have a certain *option* value -within some given ``startpath``:: - - def find_option(startpath, optionname): - for section in startpath.listdir(dir=1): - opt = section.join(optionname) - if opt.check(): # does the option exist here? - print section.basename, "found:", opt.read() - -Now the point is that ``find_option()`` would obviously work -when ``startpath`` is a filesystem-like path like a local -filesystem path or a subversion URL path. It would then see -directories as sections and files as option-names and the -content of the file as values. - -But it also works (today) for ``extpy`` paths if you put the following -python code in a file:: - - class Section1: - someoption = "i am an option value" - - class Section2: - someoption = "i am another option value" - -An ``extpy()`` path maps classes and modules to directories and -name-value bindings to file/read() operations. - -And it could also work for 'xml' paths if you put -the following xml string in a file:: - - - - - value - - value - -where tags containing non-text tags map to directories -and tags with just text-children map to files (which -upon read() return the joined content of the text -tags possibly as unicode. - -Now, to complete the picture, we could make Config-Parser -*ini-style* config files also available:: - - [section1] - name = value - - [section2] - othername = value - -where sections map to directories and name=value mappings -to file/contents. - -So it seems that our above ``find_option()`` function would -work nicely on all these *mappings*. - -Of course, the somewhat open question is how to make the -transition from a filesystem path to structured content -useful and unified, as much as possible without overdoing it. - -Again, there are tasks that will need fully domain specific -solutions (DOM/XSLT/...) but i think the above view warrants -some experiments and refactoring. The degree of uniformity -still needs to be determined and thought about. - -path objects should be stackable --------------------------------- - -Oh, and btw, a ``py.path.extpy`` file could live on top of a -'py.path.xml' path as well, i.e. take:: - - - - - - - import py - ... - - def getmsg(x): pass - -and use it to have a ``extpy`` path living on it:: - - p = py.path.local(xmlfilename) - xmlp = py.path.extxml(p, 'py/magic/exprinfo') - p = py.path.extpy(xmlp, 'getmsg') - - assert p.check(func=1, basename='getmsg') - getmsg = p.resolve() - # we now have a *live* getmsg() function taken and compiled from - # the above xml fragment - -There could be generic converters which convert between -different content formats ... allowing configuration files to e.g. -be in XML/Ini/python or filesystem-format with some common way -to find and iterate values. - -*After all the unix filesystem and the python namespaces are -two honking great ideas, why not do more of them? :-)* - - -.. _importexport: - -Revising and improving the import/export system -=============================================== - - or let's wrap the world all around - -the export/import interface ---------------------------- - -The py lib already incorporates a mechanism to select which -namespaces and names get exposed to a user of the library. -Apart from reducing the outside visible namespaces complexity -this allows to quickly rename and refactor stuff in the -implementation without affecting the caller side. This export -control can be used by other python packages as well. - -However, all is not fine as the import/export has a -few major deficiencies and shortcomings: - -- it doesn't allow to specify doc-strings -- it is a bit hackish (see py/initpkg.py) -- it doesn't present a complete and consistent view of the API. -- ``help(constructed_namespace)`` doesn't work for the root - package namespace -- when the py lib implementation accesses parts of itself - it uses the native python import mechanism which is - limiting in some respects. Especially for distributed - programs as encouraged by `py.execnet`_ it is not clear - how the mechanism can nicely integrate to support remote - lazy importing. - -Discussions have been going on for a while but it is -still not clear how to best tackle the problem. Personally, -i believe the main missing thing for the first major release -is the docstring one. The current specification -of exported names is dictionary based. It would be -better to declare it in terms of Objects. - - -Example sketch for a new export specification ---------------------------------------------- - -Here is a sketch of how the py libs ``__init__.py`` file -might or should look like:: - - """ - the py lib version 0.8 - http://codespeak.net/py/0.8 - """ - - from py import pkg - pkg.export(__name__, - pkg.Module('path', - '''provides path objects for local filesystem, - subversion url and working copy, and extension paths. - ''', - pkg.Class('local', ''' - the local filesystem path offering a single - point of interaction for many purposes. - ''', extpy='./path/local.LocalPath'), - - pkg.Class('svnurl', ''' - the subversion url path. - ''', extpy='./path/local/svn/urlcommand.SvnUrlPath'), - ), - # it goes on ... - ) - -The current ``initpkg.py`` code can be cleaned up to support -this new more explicit style of stating things. Note that -in principle there is nothing that stops us from retrieving -implementations over the network, e.g. a subversion repository. - - -Let there be alternatives -------------------------- - -We could also specify alternative implementations easily:: - - pkg.Class('svnwc', ''' - the subversion working copy. - ''', extpy=('./path/local/svn/urlbinding.SvnUrlPath', - './path/local/svn/urlcommand.SvnUrlPath',) - ) - -This would prefer the python binding based implementation over -the one working through he 'svn' command line utility. And -of course, it could uniformly signal if no implementation is -available at all. - - -Problems problems ------------------ - -Now there are reasons there isn't a clear conclusion so far. -For example, the above approach has some implications, the -main one being that implementation classes like -``py/path/local.LocalPath`` are visible to the caller side but -this presents an inconsistency because the user started out with -``py.path.local`` and expects that the two classes are really much -the same. We have the same problem today, of course. - -The naive solution strategy of wrapping the "implementation -level" objects into their exported representations may remind -of the `wrapping techniques PyPy uses`_. But it -*may* result in a slightly heavyweight mechanism that affects -runtime speed. However, I guess that this standard strategy -is probably the cleanest. - - -Every problem can be solved with another level ... --------------------------------------------------- - -The wrapping of implementation level classes in their export -representations objects adds another level of indirection. -But this indirection would have interesting advantages: - -- we could easily present a consistent view of the library -- it could take care of exceptions as well -- it provides natural interception points for logging -- it enables remote lazy loading of implementations - or certain versions of interfaces - -And quite likely the extra indirection wouldn't hurt so much -as it is not much more than a function call and we cared -we could even generate some c-code (with PyPy :-) to speed -it up. - -But it can lead to new problems ... ------------------------------------ - -However, it is critical to avoid to burden the implementation -code of being aware of its wrapping. This is what we have -to do in PyPy but the import/export mechanism works at -a higher level of the language, i think. - -Oh, and we didn't talk about bootstrapping :-) - -.. _`py.execnet`: execnet.html -.. _`wrapping techniques PyPy uses`: http://codespeak.net/pypy/index.cgi?doc/wrapping.html -.. _`lightweight xml generation`: - -Extension of py.path.local.sysexec() -==================================== - -The `sysexec mechanism`_ allows to directly execute -binaries on your system. Especially after we'll have this -nicely integrated into Win32 we may also want to run python -scripts both locally and from the net:: - - vadm = py.path.svnurl('http://codespeak.net/svn/vadm/dist/vadm/cmdline.py') - stdoutput = vadm.execute('diff') - -To be able to execute this code fragement, we need either or all of - -- an improved import system that allows remote imports - -- a way to specify what the "neccessary" python import - directories are. for example, the above scriptlet will - require a certain root included in the python search for module - in order to execute something like "import vadm". - -- a way to specify dependencies ... which opens up another - interesting can of worms, suitable for another chapter - in the neverending `future book`_. - -.. _`sysexec mechanism`: misc.html#sysexec -.. _`compile-on-the-fly`: - -we need a persistent storage for the py lib -------------------------------------------- - -A somewhat open question is where to store the underlying -generated pyc-files and other files generated on the fly -with `CPython's distutils`_. We want to have a -*persistent location* in order to avoid runtime-penalties -when switching python versions and platforms (think NFS). - -A *persistent location* for the py lib would be a good idea -maybe also for other reasons. We could cache some of the -expensive test setups, like the multi-revision subversion -repository that is created for each run of the tests. - -.. _`CPython's distutils`: http://www.python.org/dev/doc/devel/lib/module-distutils.html - -.. _`getting started`: getting-started.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 -.. _`under the xpy tree`: http://codespeak.net/svn/user/hpk/xpy/xml.py -.. _`future book`: future.html -.. _`PEP-324 subprocess module`: http://www.python.org/peps/pep-0324.html -.. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ -.. _`py.test`: test.html - -Refactor path implementations to use a Filesystem Abstraction -============================================================= - -It seems like a good idea to refactor all python implementations to -use an internal Filesystem abstraction. The current code base -would be transformed to have Filesystem implementations for e.g. -local, subversion and subversion "working copy" filesystems. Today -the according code is scattered through path-handling code. - -On a related note, Armin Rigo has hacked `pylufs`_ which allows to -implement kernel-level linux filesystems with pure python. Now -the idea is that the mentioned filesystem implementations would -be directly usable for such linux-filesystem glue code. - -In other words, implementing a `memoryfs`_ or a `dictfs`_ would -give you two things for free: a filesystem mountable at kernel level -as well as a uniform "path" object allowing you to access your -filesystem in convenient ways. (At some point it might -even become interesting to think about interfacing to -`reiserfs v4 features`_ at the Filesystem level but that -is a can of subsequent worms). - -.. _`memoryfs`: http://codespeak.net/svn/user/arigo/hack/pyfuse/memoryfs.py -.. _`dictfs`: http://codespeak.net/pipermail/py-dev/2005-January/000191.html -.. _`pylufs`: http://codespeak.net/svn/user/arigo/hack/pylufs/ -.. _`reiserfs v4 features`: http://www.namesys.com/v4/v4.html - - -Improve and unify Path API -========================== - -visit() grows depth control ---------------------------- - -Add a ``maxdepth`` argument to the path.visit() method, -which will limit traversal to subdirectories. Example:: - - x = py.path.local.get_tmproot() - for x in p.visit('bin', stop=N): - ... - -This will yield all file or directory paths whose basename -is 'bin', depending on the values of ``stop``:: - - p # stop == 0 or higher (and p.basename == 'bin') - p / bin # stop == 1 or higher - p / ... / bin # stop == 2 or higher - p / ... / ... / bin # stop == 3 or higher - -The default for stop would be `255`. - -But what if `stop < 0`? We could let that mean to go upwards:: - - for x in x.visit('py/bin', stop=-255): - # will yield all parent direcotires which have a - # py/bin subpath - -visit() returning a lazy list? ------------------------------- - -There is a very nice "no-API" `lazy list`_ implementation from -Armin Rigo which presents a complete list interface, given some -iterable. The iterable is consumed only on demand and retains -memory efficiency as much as possible. The lazy list -provides a number of advantages in addition to the fact that -a list interface is nicer to deal with than an iterator. -For example it lets you do:: - - for x in p1.visit('*.cfg') + p2.visit('*.cfg'): - # will iterate through all results - -Here the for-iter expression will retain all lazyness (with -the result of adding lazy lists being another another lazy -list) by internally concatenating the underlying -lazylists/iterators. Moreover, the lazylist implementation -will know that there are no references left to the lazy list -and throw away iterated elements. This makes the iteration -over the sum of the two visit()s as efficient as if we had -used iterables to begin with! - -For this, we would like to move the lazy list into the -py lib's namespace, most probably at `py.builtin.lazylist`. - -.. _`lazy list`: http://codespeak.net/svn/user/arigo/hack/misc/collect.py Copied: py/dist/py/documentation/future/future.txt (from r36246, py/dist/py/documentation/future.txt) ============================================================================== --- py/dist/py/documentation/future.txt (original) +++ py/dist/py/documentation/future/future.txt Mon Jan 8 12:39:06 2007 @@ -280,7 +280,7 @@ Oh, and we didn't talk about bootstrapping :-) -.. _`py.execnet`: execnet.html +.. _`py.execnet`: ../execnet.html .. _`wrapping techniques PyPy uses`: http://codespeak.net/pypy/index.cgi?doc/wrapping.html .. _`lightweight xml generation`: @@ -308,7 +308,7 @@ interesting can of worms, suitable for another chapter in the neverending `future book`_. -.. _`sysexec mechanism`: misc.html#sysexec +.. _`sysexec mechanism`: ../misc.html#sysexec .. _`compile-on-the-fly`: we need a persistent storage for the py lib @@ -327,7 +327,7 @@ .. _`CPython's distutils`: http://www.python.org/dev/doc/devel/lib/module-distutils.html -.. _`getting started`: getting-started.html +.. _`getting started`: ../getting-started.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 @@ -335,7 +335,7 @@ .. _`future book`: future.html .. _`PEP-324 subprocess module`: http://www.python.org/peps/pep-0324.html .. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ -.. _`py.test`: test.html +.. _`py.test`: ../test.html Refactor path implementations to use a Filesystem Abstraction ============================================================= Modified: py/dist/py/documentation/getting-started.txt ============================================================================== --- py/dist/py/documentation/getting-started.txt (original) +++ py/dist/py/documentation/getting-started.txt Mon Jan 8 12:39:06 2007 @@ -104,7 +104,7 @@ .. _`zope3`: http://zope3.zwiki.org/ .. _twisted: http://www.twistedmatrix.org -.. _future: future.html +.. _future: future/future.html .. _`get an account`: Modified: py/dist/py/documentation/home.txt ============================================================================== --- py/dist/py/documentation/home.txt (original) +++ py/dist/py/documentation/home.txt Mon Jan 8 12:39:06 2007 @@ -4,9 +4,3 @@ documentation issues - seen primarily from the perspective of a FOSS (Free and Open Source) developer. -Heading towards a first 0.8.0 py.test/py lib release! -====================================================== - -``py.test`` and the py lib are heading towards their first release. -The main missing feature is proper testing and integration with win32 -platforms and improved documentation. *holger (06/11/2005)* Modified: py/dist/py/documentation/index.txt ============================================================================== --- py/dist/py/documentation/index.txt (original) +++ py/dist/py/documentation/index.txt Mon Jan 8 12:39:06 2007 @@ -27,7 +27,7 @@ .. _`py.test`: test.html .. _`py.xml`: xml.html .. _`Why What how py?`: why_py.html -.. _`future`: future.html +.. _`future`: future/future.html .. _`getting started`: getting-started.html .. _`miscellaneous features`: misc.html Modified: py/dist/py/documentation/misc.txt ============================================================================== --- py/dist/py/documentation/misc.txt (original) +++ py/dist/py/documentation/misc.txt Mon Jan 8 12:39:06 2007 @@ -136,11 +136,11 @@ will have to install the `pywin32 package`_. -.. _`compile-on-the-fly`: future.html#compile-on-the-fly -.. _`future book`: future.html +.. _`compile-on-the-fly`: future/future.html#compile-on-the-fly +.. _`future book`: future/future.html .. _`PEP-324 subprocess module`: http://www.python.org/peps/pep-0324.html .. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ -.. _`a more general view on path objects`: future.html#general-path +.. _`a more general view on path objects`: future/future.html#general-path .. _`pywin32 package`: http://pywin32.sourceforge.net/ finding an executable local path Modified: py/dist/py/documentation/why_py.txt ============================================================================== --- py/dist/py/documentation/why_py.txt (original) +++ py/dist/py/documentation/why_py.txt Mon Jan 8 12:39:06 2007 @@ -266,8 +266,8 @@ .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev .. _`test environment`: test.html .. _`PyPy`: http://codespeak.net/pypy -.. _`envisioned import/export system`: future.html#importexport -.. _future: future.html +.. _`envisioned import/export system`: future/future.html#importexport +.. _future: future/future.html .. _`py.test tool and library`: test.html .. _`py API`: api.html .. _`great work that Fred L. Drake, Jr. is doing`: http://www.python.org/doc/2.3.4/lib/lib.html From fijal at codespeak.net Mon Jan 8 12:43:13 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 12:43:13 +0100 (CET) Subject: [py-svn] r36253 - py/dist/py/test/rsession/testing Message-ID: <20070108114313.32BD410084@code0.codespeak.net> Author: fijal Date: Mon Jan 8 12:43:09 2007 New Revision: 36253 Modified: py/dist/py/test/rsession/testing/test_lsession.py py/dist/py/test/rsession/testing/test_rsession.py Log: Cleanup a bit sub directories and add additional check for sub being different sub. Modified: py/dist/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_lsession.py (original) +++ py/dist/py/test/rsession/testing/test_lsession.py Mon Jan 8 12:43:09 2007 @@ -16,7 +16,7 @@ def example_distribution(self, runner): # XXX find a better way for the below tmpdir = tmp - dirname = "sub"+runner.func_name + dirname = "sub_lsession"+runner.func_name tmpdir.ensure(dirname, "__init__.py") tmpdir.ensure(dirname, "test_one.py").write(py.code.Source(""" def test_1(): @@ -72,8 +72,9 @@ def test_pdb_run(self): # we make sure that pdb is engaged tmpdir = tmp - tmpdir.ensure("sub", "__init__.py") - tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" + subdir = "sub_pdb_run" + tmpdir.ensure(subdir, "__init__.py") + tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" def test_1(): assert 0 """)) @@ -83,7 +84,7 @@ l.append(args) pdb.post_mortem = some_fun - args = [str(tmpdir.join("sub")), '--pdb'] + args = [str(tmpdir.join(subdir)), '--pdb'] config, args = py.test.Config.parse(args) lsession = LSession(config) allevents = [] @@ -102,8 +103,9 @@ if not hasattr(py.std.os, 'fork'): py.test.skip('operating system not supported') tmpdir = tmp - tmpdir.ensure("sub2", "__init__.py") - tmpdir.ensure("sub2", "test_one.py").write(py.code.Source(""" + subdir = "sub_lsession_minus_x" + tmpdir.ensure(subdir, "__init__.py") + tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" def test_1(): pass def test_2(): @@ -113,7 +115,7 @@ def test_4(someargs): pass """)) - args = [str(tmpdir.join("sub2")), '-x'] + args = [str(tmpdir.join(subdir)), '-x'] config, args = py.test.Config.parse(args) assert config.option.exitfirst lsession = LSession(config) Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Mon Jan 8 12:43:09 2007 @@ -138,15 +138,15 @@ if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 2 - def test_example_distribution(self): - # XXX find a better way for the below - tmpdir = py.path.local(py.__file__).dirpath().dirpath() - tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" + def test_example_distribution(self): + subdir = "sub_example_dist" + tmpdir = py.test.ensuretemp("example_distribution") + tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" disthosts = [%r] - distrsync_roots = ["sub", "py"] - """ % 'localhost')) - tmpdir.ensure("sub", "__init__.py") - tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" + distrsync_roots = ["%s"] + """ % ('localhost', subdir))) + tmpdir.ensure(subdir, "__init__.py") + tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" def test_1(): pass def test_2(): @@ -155,8 +155,10 @@ raise ValueError(23) def test_4(someargs): pass - """)) - args = [str(tmpdir.join("sub"))] + def test_5(): + assert __file__ != '%s' + """ % str(tmpdir.join(subdir)))) + args = [str(tmpdir.join(subdir))] config, args = py.test.Config.parse(args) rsession = RSession(config, optimise_localhost=False) allevents = [] @@ -167,8 +169,8 @@ passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] skippedevents = [i for i in testevents if i.outcome.skipped] - assert len(testevents) == 4 - assert len(passevents) == 1 + assert len(testevents) == 5 + assert len(passevents) == 2 assert len(failevents) == 3 tb = failevents[0].outcome.excinfo.traceback assert tb[0].path.find("test_one") != -1 From hpk at codespeak.net Mon Jan 8 12:58:47 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 12:58:47 +0100 (CET) Subject: [py-svn] r36255 - py/dist/py Message-ID: <20070108115847.0EA9110078@code0.codespeak.net> Author: hpk Date: Mon Jan 8 12:58:45 2007 New Revision: 36255 Modified: py/dist/py/__init__.py Log: shift to 0.8.80-alpha2 for now Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Mon Jan 8 12:58:45 2007 @@ -5,17 +5,19 @@ """ from initpkg import initpkg +version = "0.8.80-alpha2" + initpkg(__name__, description = "py.test and the py lib", revision = int('$LastChangedRevision$'.split(':')[1][:-1]), lastchangedate = '$LastChangedDate$', - version = "0.8.0-alpha2", + version = version, url = "http://codespeak.net/py", - download_url = "http://codespeak.net/download/py/py-0.8.0-alpha2.tar.gz", + download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,), license = "MIT license", platforms = ['unix', 'linux', 'cygwin'], author = "holger krekel & others", - author_email = "hpk at merlinux.de", + author_email = "py-dev at codespeak.net", long_description = globals()['__doc__'], exportdefs = { From hpk at codespeak.net Mon Jan 8 12:58:59 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 12:58:59 +0100 (CET) Subject: [py-svn] r36256 - py/dist/py/test/rsession/testing Message-ID: <20070108115859.C757510086@code0.codespeak.net> Author: hpk Date: Mon Jan 8 12:58:57 2007 New Revision: 36256 Modified: py/dist/py/test/rsession/testing/test_rsession.py Log: better test class name Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Mon Jan 8 12:58:57 2007 @@ -69,7 +69,7 @@ assert len(events) == 2 assert str(events[1][0].value) == "Reason" -class TestWithRealSshHosts: +class TestRSessionRemote: #def setup_class(cls): # from py.__.test.rsession.conftest import option # if not option.disthosts: From afayolle at codespeak.net Mon Jan 8 13:23:04 2007 From: afayolle at codespeak.net (afayolle at codespeak.net) Date: Mon, 8 Jan 2007 13:23:04 +0100 (CET) Subject: [py-svn] r36259 - in py/dist/py: misc/testing path/local path/local/testing path/svn/testing rest rest/testing Message-ID: <20070108122304.C9DA61007F@code0.codespeak.net> Author: afayolle Date: Mon Jan 8 13:23:02 2007 New Revision: 36259 Removed: py/dist/py/rest/testing/shared_helpers.py Modified: py/dist/py/misc/testing/test_initpkg.py py/dist/py/misc/testing/test_svnlook.py py/dist/py/path/local/local.py py/dist/py/path/local/testing/test_local.py py/dist/py/path/svn/testing/test_test_repo.py py/dist/py/path/svn/testing/test_urlcommand.py py/dist/py/path/svn/testing/test_wccommand.py py/dist/py/rest/convert.py py/dist/py/rest/latex.py py/dist/py/rest/testing/test_convert.py py/dist/py/rest/testing/test_directive.py py/dist/py/rest/testing/test_htmlrest.py py/dist/py/rest/testing/test_rst2pdf.py Log: * Changed py.path.local.sysfind to return None instead of raising ENOENT when file is not found. * Fixed tests and files using sysfind. * Removed py/rest/testing/shared_helpers.py and fixed code using the is_on_path function defined therein to use the new sysfind semantics. Modified: py/dist/py/misc/testing/test_initpkg.py ============================================================================== --- py/dist/py/misc/testing/test_initpkg.py (original) +++ py/dist/py/misc/testing/test_initpkg.py Mon Jan 8 13:23:02 2007 @@ -77,9 +77,8 @@ def test_getrev(): d = py.__package__.getrev() - try: - svnversion = py.path.local.sysfind('svnversion') - except py.error.ENOENT: + svnversion = py.path.local.sysfind('svnversion') + if svnversion is None: py.test.skip("cannot test svnversion, 'svnversion' binary not found") v = svnversion.sysexec(py.path.local(py.__file__).dirpath()) assert v.startswith(str(d)) Modified: py/dist/py/misc/testing/test_svnlook.py ============================================================================== --- py/dist/py/misc/testing/test_svnlook.py (original) +++ py/dist/py/misc/testing/test_svnlook.py Mon Jan 8 13:23:02 2007 @@ -3,10 +3,8 @@ from py.__.misc import svnlook data = py.magic.autopath().dirpath('data') -try: - py.path.local.sysfind('svnlook') - py.path.local.sysfind('svnadmin') -except py.error.ENOENT: +if py.path.local.sysfind('svnlook') is None or \ + py.path.local.sysfind('svnadmin') is None: py.test.skip("cannot test py.misc.svnlook, svn binaries not found") def test_svnlook(): Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Mon Jan 8 13:23:02 2007 @@ -491,7 +491,7 @@ """ return a path object found by looking at the systems underlying PATH specification. If the checker is not None it will be invoked to filter matching paths. If a binary - cannot be found, py.error.ENOENT is raised. + cannot be found, None is returned Note: This is probably not working on plain win32 systems but may work on cygwin. """ @@ -525,7 +525,7 @@ return p except py.error.EACCES: pass - raise py.error.ENOENT(name) + return None sysfind = classmethod(sysfind) #""" #special class constructors for local filesystem paths Modified: py/dist/py/path/local/testing/test_local.py ============================================================================== --- py/dist/py/path/local/testing/test_local.py (original) +++ py/dist/py/path/local/testing/test_local.py Mon Jan 8 13:23:02 2007 @@ -175,9 +175,7 @@ def test_sysfind(self): x = py.path.local.sysfind('cmd') assert x.check(file=1) - py.test.raises(py.error.ENOENT, """ - py.path.local.sysfind('jaksdkasldqwe') - """) + assert py.path.local.sysfind('jaksdkasldqwe') is None class TestExecution(LocalSetup): disabled = py.std.sys.platform == 'win32' @@ -185,9 +183,7 @@ def test_sysfind(self): x = py.path.local.sysfind('test') assert x.check(file=1) - py.test.raises(py.error.ENOENT, """ - py.path.local.sysfind('jaksdkasldqwe') - """) + assert py.path.local.sysfind('jaksdkasldqwe') is None def test_sysfind_no_permisson(self): dir = py.test.ensuretemp('sysfind') @@ -197,8 +193,8 @@ noperm = dir.ensure('noperm', dir=True) env['PATH'] += ":%s" % (noperm) noperm.chmod(0) - py.test.raises(py.error.ENOENT, """ py.path.local.sysfind('a') - """) + assert py.path.local.sysfind('a') is None + finally: env['PATH'] = oldpath noperm.chmod(0644) @@ -224,9 +220,7 @@ assert x.basename == 'a' assert x.dirpath().basename == 'b' checker = lambda x: None - py.test.raises(py.error.ENOENT, """ - py.path.local.sysfind('a', checker=checker) - """) + assert py.path.local.sysfind('a', checker=checker) is None finally: env['PATH'] = oldpath #dir.remove() Modified: py/dist/py/path/svn/testing/test_test_repo.py ============================================================================== --- py/dist/py/path/svn/testing/test_test_repo.py (original) +++ py/dist/py/path/svn/testing/test_test_repo.py Mon Jan 8 13:23:02 2007 @@ -1,9 +1,8 @@ import py from py.__.path.svn.testing.svntestbase import make_test_repo -try: - py.path.local.sysfind('svn') -except py.error.ENOENT: + +if py.path.local.sysfind('svn') is None: py.test.skip("cannot test py.path.svn, 'svn' binary not found") class TestMakeRepo(object): Modified: py/dist/py/path/svn/testing/test_urlcommand.py ============================================================================== --- py/dist/py/path/svn/testing/test_urlcommand.py (original) +++ py/dist/py/path/svn/testing/test_urlcommand.py Mon Jan 8 13:23:02 2007 @@ -5,9 +5,8 @@ import datetime import time -try: - svnversion = py.path.local.sysfind('svn') -except py.error.ENOENT: + +if py.path.local.sysfind('svn') is None: py.test.skip("cannot test py.path.svn, 'svn' binary not found") class TestSvnCommandPath(CommonCommandAndBindingTests): Modified: py/dist/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/dist/py/path/svn/testing/test_wccommand.py (original) +++ py/dist/py/path/svn/testing/test_wccommand.py Mon Jan 8 13:23:02 2007 @@ -4,9 +4,8 @@ from py.__.path.svn.wccommand import parse_wcinfotime from py.__.path.svn import svncommon -try: - svnversion = py.path.local.sysfind('svn') -except py.error.ENOENT: + +if py.path.local.sysfind('svn') is None: py.test.skip("cannot test py.path.svn, 'svn' binary not found") Modified: py/dist/py/rest/convert.py ============================================================================== --- py/dist/py/rest/convert.py (original) +++ py/dist/py/rest/convert.py Mon Jan 8 13:23:02 2007 @@ -1,7 +1,6 @@ import py from py.__.process.cmdexec import ExecutionFailed -from py.__.rest.testing.shared_helpers import is_on_path # utility functions to convert between various formats format_to_dotargument = {"png": "png", @@ -12,7 +11,8 @@ def ps2eps(ps): # XXX write a pure python version - if not is_on_path("ps2epsi") and not is_on_path("ps2eps"): + if not py.path.local.sysfind("ps2epsi") and \ + not py.path.local.sysfind("ps2eps"): raise SystemExit("neither ps2eps nor ps2epsi found") try: eps = ps.new(ext=".eps") @@ -21,7 +21,7 @@ py.process.cmdexec("ps2eps -l -f %s" % ps) def ps2pdf(ps, compat_level="1.2"): - if not is_on_path("gs"): + if not py.path.local.sysfind("gs"): raise SystemExit("ERROR: gs not found") pdf = ps.new(ext=".pdf") options = dict(OPTIONS="-dSAFER -dCompatibilityLevel=%s" % compat_level, @@ -34,7 +34,7 @@ def eps2pdf(eps): # XXX write a pure python version - if not is_on_path("epstopdf"): + if not py.path.local.sysfind("epstopdf"): raise SystemExit("ERROR: epstopdf not found") py.process.cmdexec("epstopdf %s" % eps) @@ -42,12 +42,12 @@ if dest is None: dest = eps.new(ext=".eps") command = 'dvips -q -E -n 1 -D 600 -p 1 -o %s %s' % (dest, dvi) - if not is_on_path("dvips"): + if not py.path.local.sysfind("dvips"): raise SystemExit("ERROR: dvips not found") py.process.cmdexec(command) def convert_dot(fn, new_extension): - if not is_on_path("dot"): + if not py.path.local.sysfind("dot"): raise SystemExit("ERROR: dot not found") result = fn.new(ext=new_extension) print result Modified: py/dist/py/rest/latex.py ============================================================================== --- py/dist/py/rest/latex.py (original) +++ py/dist/py/rest/latex.py Mon Jan 8 13:23:02 2007 @@ -2,7 +2,6 @@ from docutils.core import publish_cmdline from py.__.process.cmdexec import ExecutionFailed -from py.__.rest.testing.shared_helpers import is_on_path font_to_package = {"times": "times", "helvetica": "times", "new century schoolbock": "newcent", "avant garde": "newcent", @@ -108,7 +107,7 @@ old.chdir() def process_rest_file(restfile, stylesheet=None, debug=False, rest_options=None): - if not is_on_path("pdflatex"): + if not py.path.local.sysfind("pdflatex"): raise SystemExit("ERROR: pdflatex not found") old = py.path.local() f = py.path.local(restfile) Deleted: /py/dist/py/rest/testing/shared_helpers.py ============================================================================== --- /py/dist/py/rest/testing/shared_helpers.py Mon Jan 8 13:23:02 2007 +++ (empty file) @@ -1,11 +0,0 @@ -import py - -def is_on_path(name): - try: - py.path.local.sysfind(name) - except py.error.ENOENT: - return False - else: - return True - - Modified: py/dist/py/rest/testing/test_convert.py ============================================================================== --- py/dist/py/rest/testing/test_convert.py (original) +++ py/dist/py/rest/testing/test_convert.py Mon Jan 8 13:23:02 2007 @@ -1,11 +1,12 @@ import py from py.__.rest.convert import convert_dot, latexformula2png -from shared_helpers import is_on_path datadir = py.magic.autopath().dirpath().join("data") def setup_module(mod): - if not is_on_path("gs") or not is_on_path("dot") or not is_on_path("latex"): + 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") def test_convert_dot(): Modified: py/dist/py/rest/testing/test_directive.py ============================================================================== --- py/dist/py/rest/testing/test_directive.py (original) +++ py/dist/py/rest/testing/test_directive.py Mon Jan 8 13:23:02 2007 @@ -5,16 +5,15 @@ py.test.skip("docutils not present") from py.__.misc import rest from py.__.rest.latex import process_rest_file -from py.__.rest.testing.shared_helpers import is_on_path datadir = py.magic.autopath().dirpath().join("data") def test_graphviz_html(): - if not is_on_path("dot"): + if not py.path.local.sysfind("dot"): py.test.skip("graphviz needed") directive.set_backend_and_register_directives("html") #for reasons that elude me rest.process expects svnwcs??? - if not is_on_path("svn"): + if not py.path.local.sysfind("svn"): py.test.skip("svn needed") txt = py.path.svnwc(datadir.join("graphviz.txt")) html = txt.new(ext="html") @@ -28,7 +27,7 @@ png.remove() def test_graphviz_pdf(): - if not is_on_path("dot") or not is_on_path("latex"): + if not py.path.local.sysfind("dot") or not py.path.local.sysfind("latex"): py.test.skip("graphviz and latex needed") directive.set_backend_and_register_directives("latex") Modified: py/dist/py/rest/testing/test_htmlrest.py ============================================================================== --- py/dist/py/rest/testing/test_htmlrest.py (original) +++ py/dist/py/rest/testing/test_htmlrest.py Mon Jan 8 13:23:02 2007 @@ -2,10 +2,11 @@ import py from py.__.misc import rest -from py.__.rest.testing.shared_helpers import is_on_path def setup_module(mod): - if not is_on_path("gs") or not is_on_path("dot") or not is_on_path("latex"): + if not py.path.local.sysfind("gs") or \ + not py.path.local.sysfind("dot") or \ + not py.path.local.sysfind("latex"): py.test.skip("ghostscript, graphviz and latex needed") try: import docutils Modified: py/dist/py/rest/testing/test_rst2pdf.py ============================================================================== --- py/dist/py/rest/testing/test_rst2pdf.py (original) +++ py/dist/py/rest/testing/test_rst2pdf.py Mon Jan 8 13:23:02 2007 @@ -6,10 +6,11 @@ except ImportError: py.test.skip("docutils not present") -from py.__.rest.testing.shared_helpers import is_on_path def setup_module(mod): - if not is_on_path("gs") or not is_on_path("dot") or not is_on_path("latex"): + 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") data = py.magic.autopath().dirpath().join("data") From hpk at codespeak.net Mon Jan 8 14:43:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 14:43:01 +0100 (CET) Subject: [py-svn] r36267 - py/dist/py/documentation Message-ID: <20070108134301.2070310075@code0.codespeak.net> Author: hpk Date: Mon Jan 8 14:43:00 2007 New Revision: 36267 Modified: py/dist/py/documentation/apigen.txt Log: a bit of rewriting the apigen introduction ... the real thing is to consider all apigen docs and sort/merge them. Modified: py/dist/py/documentation/apigen.txt ============================================================================== --- py/dist/py/documentation/apigen.txt (original) +++ py/dist/py/documentation/apigen.txt Mon Jan 8 14:43:00 2007 @@ -5,18 +5,27 @@ ------------ Apigen is a tool for automatically generating API reference documentation for -Python projects. It distinguishes itself from comparable tools by examining -code at runtime, rather than at compile time. This way it is capable of -displaying more information about registered and discovered classes and -functions (although it may find less of them). The tool can either be used -from code, or from py.test, in the latter case it will gather information -about modules registered using `initpkg`_. +Python projects. It works by examining code at runtime rather than at +compile time. This way it is capable of displaying information +about the code base after initialization. A drawback is that +you cannot easily document source code that automatically +starts server processes upon getting imported. + +The apigen functionality can either be used from code, or from +py.test, in the latter case it will gather information about +modules and names exported explicitely via the `initpkg`_ mechanism. + +Please note that apigen is currently geared towards documenting the +py library itself, making it nicely work for other projects +may still require a bit of adaption and refinement work. Using from code ---------------- +XXX how to use it without any Tracer? + The library provides a simple API to generate a py.rest.rst tree (which -can be converted to ReStructuredText), along with some helper classes to +represents a ReStructuredText document), along with some helper classes to control the output. The most important objects are the Tracer, which traces code execution by registering itself with sys.settrace, the DocStorage class, that stores Tracer information, and the RestGen class which creates a ReST From hpk at codespeak.net Mon Jan 8 14:44:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 14:44:27 +0100 (CET) Subject: [py-svn] r36268 - in py/dist/py/documentation: . future Message-ID: <20070108134427.5146A10075@code0.codespeak.net> Author: hpk Date: Mon Jan 8 14:44:25 2007 New Revision: 36268 Added: py/dist/py/documentation/future/planning.txt - copied, changed from r36258, py/dist/py/documentation/planning.txt Removed: py/dist/py/documentation/planning.txt Log: refine and move old planning file Copied: py/dist/py/documentation/future/planning.txt (from r36258, py/dist/py/documentation/planning.txt) ============================================================================== --- py/dist/py/documentation/planning.txt (original) +++ py/dist/py/documentation/future/planning.txt Mon Jan 8 14:44:25 2007 @@ -60,14 +60,3 @@ * fix -k option to py.test * add --report=(text|terminal|session|rest|tkinter|rest) to py.test -PyCon sprint -============= - -HP -== - -Vision -====== - - * define necessary minimal abstractions to make a working path documentation - Deleted: /py/dist/py/documentation/planning.txt ============================================================================== --- /py/dist/py/documentation/planning.txt Mon Jan 8 14:44:25 2007 +++ (empty file) @@ -1,73 +0,0 @@ -Release -======= - -currently working configurations --------------------------------- - -2.3 - 2.4.2 work - -2.5 has obscure problems - -with setuptools: 2.3 - 2.4.2 as 'develop' - -regular installation: works mostly, strange test-failures - -to be tested: 2.2, windows - -absolutely necessary steps: ----------------------------- - - * documentation - - * improving getting started, describe install methods - * describe the rest stuff? - * py.log - * py.path is mostly undocumented, API documentation - - * basic windows testing, maybe disabling execnet?, what about the scripts in windows? - - * are all c extensions compiled when installing globally? - - * refactoring py.log - - * write/read methods on py.path should be renamed/deprecated: setcontent, getcontent instead? - - * what about _subprocess.c? - - * warning for docutils - - * don't expose _extpy - - * py/bin should be nicefied, get optparse interface - - * _findpy.py - * py.cleanup: - * py.lookup: add -i option - * pytest.cmd - * rst2pdf.py: merge with py.rest, add warnings when missing tex - * _makepyrelease.py: move somewhere - * py.countloc - * py.test - * py.rest - * win32 - - * skip tests if dependencies are not installed - -nice to have ------------- - - * sets.py, subprocess.py in compat - * fix -k option to py.test - * add --report=(text|terminal|session|rest|tkinter|rest) to py.test - -PyCon sprint -============= - -HP -== - -Vision -====== - - * define necessary minimal abstractions to make a working path documentation - From cfbolz at codespeak.net Mon Jan 8 14:50:00 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Mon, 8 Jan 2007 14:50:00 +0100 (CET) Subject: [py-svn] r36269 - in py/dist/py: misc test/terminal Message-ID: <20070108135000.469B610076@code0.codespeak.net> Author: cfbolz Date: Mon Jan 8 14:49:46 2007 New Revision: 36269 Added: py/dist/py/misc/terminal_helper.py Modified: py/dist/py/test/terminal/out.py Log: introduce a terminal_helper file that contains ansi_print and terminal_width, currently. use this from the terminal output of py.test Added: py/dist/py/misc/terminal_helper.py ============================================================================== --- (empty file) +++ py/dist/py/misc/terminal_helper.py Mon Jan 8 14:49:46 2007 @@ -0,0 +1,21 @@ +import sys, os + +terminal_width = int(os.environ.get('COLUMNS', 80))-1 + +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() + + Modified: py/dist/py/test/terminal/out.py ============================================================================== --- py/dist/py/test/terminal/out.py (original) +++ py/dist/py/test/terminal/out.py Mon Jan 8 14:49:46 2007 @@ -2,10 +2,11 @@ import sys import os import py +from py.misc import terminal_helper class Out(object): tty = False - fullwidth = int(os.environ.get('COLUMNS', 80))-1 + fullwidth = terminal_helper.terminal_width def __init__(self, file): self.file = py.io.dupfile(file) From cfbolz at codespeak.net Mon Jan 8 14:51:15 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Mon, 8 Jan 2007 14:51:15 +0100 (CET) Subject: [py-svn] r36270 - py/dist/py/bin Message-ID: <20070108135115.4B14910076@code0.codespeak.net> Author: cfbolz Date: Mon Jan 8 14:51:13 2007 New Revision: 36270 Modified: py/dist/py/bin/py.lookup Log: color the search term coloured in the output Modified: py/dist/py/bin/py.lookup ============================================================================== --- py/dist/py/bin/py.lookup (original) +++ py/dist/py/bin/py.lookup Mon Jan 8 14:51:13 2007 @@ -3,6 +3,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 import re curdir = py.path.local() @@ -17,6 +18,18 @@ 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] @@ -34,11 +47,20 @@ else: searchlines = lines for i, (line, searchline) in enumerate(zip(lines, searchlines)): - if searchline.find(string) != -1: - if not options.context: - print "%s:%d: %s" %(x.relto(curdir), i+1, line.rstrip()) - 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 "-"*50 + 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 From fijal at codespeak.net Mon Jan 8 15:13:54 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 15:13:54 +0100 (CET) Subject: [py-svn] r36273 - in py/dist/py/test/rsession: . testing Message-ID: <20070108141354.A9A9010078@code0.codespeak.net> Author: fijal Date: Mon Jan 8 15:13:52 2007 New Revision: 36273 Modified: py/dist/py/test/rsession/reporter.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_lsession.py py/dist/py/test/rsession/testing/test_rsession.py Log: Refine the checkfun interface to something cleaner. Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Mon Jan 8 15:13:52 2007 @@ -239,9 +239,6 @@ itempath = " ".join(event.item.listnames()[1:]) print "%10s: %s %s" %(sshhost[:10], status, itempath) - def is_failing(self): - return len(self.failed_tests_outcome) != 0 - def report_Nodes(self, event): self.nodes = event.nodes Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 15:13:52 2007 @@ -117,7 +117,6 @@ startserverflag = self.config.option.startserver restflag = self.config.option.restreport - checkfun = lambda: None if startserverflag and reporter is None: from py.__.test.rsession.web import start_server, exported_methods @@ -135,12 +134,10 @@ else: reporter_instance = reporter_class(self.config, sshhosts) reporter = reporter_instance.report - checkfun = lambda : self.config.option.exitfirst and \ - reporter_instance.is_failing() else: startserverflag = False - return reporter, checkfun, startserverflag + return reporter, startserverflag def reporterror(reporter, data): excinfo, item = data @@ -157,6 +154,20 @@ from py.__.test.rsession.web import kill_server kill_server() + def wrap_reporter(self, reporter): + """ We wrap reporter around, which makes it possible to us to track + number of failures + """ + self.was_failure = False + def new_reporter(event): + if isinstance(event, report.ReceivedItemOutcome) and not event.outcome.passed: + self.was_failure = True + return reporter(event) + checkfun = lambda : self.config.option.exitfirst and \ + self.was_failure + # for tests + self.checkfun = checkfun + return new_reporter, checkfun def parse_directories(sshhosts): # dictionary containing directories for hosts @@ -174,17 +185,16 @@ class RSession(AbstractSession): """ Remote version of session """ - def main(self, args, reporter=None, override_checkfun=None): + def main(self, args, reporter=None): """ main loop for running tests. """ if not args: args = [py.path.local()] - + session_options.bind_config(self.config) sshhosts, directories, remotepython, rsync_roots = self.read_distributed_config() - reporter, checkfun, startserverflag = self.init_reporter(reporter, + reporter, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) - if override_checkfun: - checkfun = override_checkfun + reporter, checkfun = self.wrap_reporter(reporter) reporter(report.TestStarted(sshhosts)) @@ -235,7 +245,7 @@ class LSession(AbstractSession): """ Local version of session """ - def main(self, args, reporter=None, runner=None, shouldstop=None): + def main(self, args, reporter=None, runner=None): # check out if used options makes any sense if not args: @@ -247,10 +257,9 @@ py.magic.invoke(assertion=1) session_options.bind_config(self.config) - reporter, checkfun, startserverflag = self.init_reporter(reporter, + reporter, startserverflag = self.init_reporter(reporter, sshhosts, LocalReporter, args[0]) - if shouldstop: - checkfun = shouldstop + reporter, checkfun = self.wrap_reporter(reporter) reporter(report.TestStarted(sshhosts)) pkgdir = self.getpkgdir(args[0]) Modified: py/dist/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_lsession.py (original) +++ py/dist/py/test/rsession/testing/test_lsession.py Mon Jan 8 15:13:52 2007 @@ -121,14 +121,7 @@ lsession = LSession(config) allevents = [] - def check_stop(): - testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] - failevents = [i for i in testevents if i.outcome.excinfo] - return len(failevents) > 0 - - lsession.main(args, reporter=allevents.append, runner=box_runner, - shouldstop=check_stop) + lsession.main(args, reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -187,6 +180,7 @@ allruns = [] def dummy_runner(item, config, reporter): allruns.append(item) + item.passed = True return item lsession.main(args, reporter=allevents.append, runner=dummy_runner) Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Mon Jan 8 15:13:52 2007 @@ -113,7 +113,7 @@ tmpdir = py.test.ensuretemp("example_distribution_minus_x") tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" disthosts = [%r] - """ % 'localhost')) + """ % ('localhost',))) tmpdir.ensure("sub", "__init__.py") tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" def test_1(): @@ -129,13 +129,10 @@ config, args = py.test.Config.parse(args) rsession = RSession(config) allevents = [] - def check(): - return [x for x in allevents if isinstance(x, report.ReceivedItemOutcome) and - not x.outcome.passed] - - rsession.main(args, reporter=allevents.append, override_checkfun=check) + rsession.main(args, reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] + assert rsession.checkfun() assert len(testevents) == 2 def test_example_distribution(self): From guido at codespeak.net Mon Jan 8 15:38:30 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 8 Jan 2007 15:38:30 +0100 (CET) Subject: [py-svn] r36275 - py/dist/py/test/terminal Message-ID: <20070108143830.AA8411007A@code0.codespeak.net> Author: guido Date: Mon Jan 8 15:38:27 2007 New Revision: 36275 Modified: py/dist/py/test/terminal/out.py Log: (cfbolz, guido) fixed import Modified: py/dist/py/test/terminal/out.py ============================================================================== --- py/dist/py/test/terminal/out.py (original) +++ py/dist/py/test/terminal/out.py Mon Jan 8 15:38:27 2007 @@ -2,7 +2,7 @@ import sys import os import py -from py.misc import terminal_helper +from py.__.misc import terminal_helper class Out(object): tty = False From fijal at codespeak.net Mon Jan 8 15:48:04 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 15:48:04 +0100 (CET) Subject: [py-svn] r36278 - in py/dist/py/test/rsession: . testing webdata Message-ID: <20070108144804.E97A610071@code0.codespeak.net> Author: fijal Date: Mon Jan 8 15:48:00 2007 New Revision: 36278 Modified: py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_webjs.py py/dist/py/test/rsession/webdata/source.js Log: Undo the breakage of tests finished. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Jan 8 15:48:00 2007 @@ -210,9 +210,10 @@ finally: teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) - self.kill_server(startserverflag) reporter(report.Nodes(nodes)) - return reporter(report.TestFinished()) + retval = reporter(report.TestFinished()) + self.kill_server(startserverflag) + return retval def read_distributed_config(self): try: Modified: py/dist/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/dist/py/test/rsession/testing/test_webjs.py (original) +++ py/dist/py/test/rsession/testing/test_webjs.py Mon Jan 8 15:48:00 2007 @@ -123,3 +123,6 @@ # ouch assert dom.document.getElementById('modules/foo.py').childNodes[1].\ childNodes[0].childNodes[0].childNodes[0].nodeValue == 'F' + +# XXX: Write down test for full run + Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From guido at codespeak.net Mon Jan 8 16:46:34 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 8 Jan 2007 16:46:34 +0100 (CET) Subject: [py-svn] r36281 - py/dist/py/apigen/source Message-ID: <20070108154634.D0D6210075@code0.codespeak.net> Author: guido Date: Mon Jan 8 16:46:12 2007 New Revision: 36281 Added: py/dist/py/apigen/source/index.cgi (contents, props changed) Modified: py/dist/py/apigen/source/html.py py/dist/py/apigen/source/server.py Log: Added CGI script as replacement for server.py (which is not removed yet), moved some HTML related stuff to html.py. Modified: py/dist/py/apigen/source/html.py ============================================================================== --- py/dist/py/apigen/source/html.py (original) +++ py/dist/py/apigen/source/html.py Mon Jan 8 16:46:12 2007 @@ -4,6 +4,7 @@ from py.xml import html, raw from compiler import ast +import time from py.__.apigen.source.color import Tokenizer, PythonSchema class HtmlEnchanter(object): @@ -162,3 +163,88 @@ doc.add_row(i + 1, row) return unicode(doc) +style = html.style(""" + + body, p, td { + background-color: #FFF; + color: black; + font-family: monospace; + } + + td.type { + width: 2em; + } + + td.name { + width: 30em; + } + + td.mtime { + width: 13em; + } + + td.size { + text-alignment: right; + } + +""") + +def create_dir_html(path, href_prefix=''): + h = html.html( + html.head( + html.title('directory listing of %s' % (path,)), + style, + ), + ) + body = html.body( + html.h1('directory listing of %s' % (path,)), + ) + h.append(body) + table = html.table() + body.append(table) + tbody = html.tbody() + table.append(tbody) + items = list(path.listdir()) + items.sort(key=lambda p: p.basename) + items.sort(key=lambda p: not p.check(dir=True)) + for fpath in items: + tr = html.tr() + tbody.append(tr) + td1 = html.td(fpath.check(dir=True) and 'D' or 'F', class_='type') + tr.append(td1) + href = fpath.basename + if href_prefix: + href = '%s%s' % (href_prefix, href) + if fpath.check(dir=True): + href += '/' + td2 = html.td(html.a(fpath.basename, href=href), class_='name') + tr.append(td2) + td3 = html.td(time.strftime('%Y-%m-%d %H:%M:%S', + time.gmtime(fpath.mtime())), class_='mtime') + tr.append(td3) + if fpath.check(dir=True): + size = '' + unit = '' + else: + size = fpath.size() + unit = 'B' + for u in ['kB', 'MB', 'GB', 'TB']: + if size > 1024: + size = round(size / 1024.0, 2) + unit = u + td4 = html.td('%s %s' % (size, unit), class_='size') + tr.append(td4) + return unicode(h) + +def create_unknown_html(path): + h = html.html( + html.head( + html.title('Can not display page'), + style, + ), + html.body( + html.p('The data URL (%s) does not contain Python code.' % (path,)) + ), + ) + return h.unicode() + Added: py/dist/py/apigen/source/index.cgi ============================================================================== --- (empty file) +++ py/dist/py/apigen/source/index.cgi Mon Jan 8 16:46:12 2007 @@ -0,0 +1,28 @@ +#!/usr/bin/python + +import cgitb;cgitb.enable() +import py +from py.__.apigen.source.browser import parse_path +from py.__.apigen.source.html import create_html, create_dir_html, \ + create_unknown_html + +BASE_URL='http://codespeak.net/svn/py/dist' +def cgi_main(): + import os + reqpath = os.environ.get('PATH_INFO', '') + path = py.path.svnurl('%s%s' % (BASE_URL, reqpath)) + if not path.check(): + return create_unknown_html(path) + if path.check(file=True): + return unicode(create_html(parse_path(path))) + elif path.check(dir=True): + prefix = '' + if not reqpath: + prefix = 'index.cgi/' + return create_dir_html(path, href_prefix=prefix) + else: + return create_unknown_html(path) + +print 'Content-Type: text/html; charset=UTF-8' +print +print cgi_main() Modified: py/dist/py/apigen/source/server.py ============================================================================== --- py/dist/py/apigen/source/server.py (original) +++ py/dist/py/apigen/source/server.py Mon Jan 8 16:46:12 2007 @@ -3,98 +3,14 @@ """ import py -import time try: from pypy.translator.js.examples import server except ImportError: py.test.skip("PyPy not found") from py.__.apigen.source.browser import parse_path -from py.__.apigen.source.html import create_html +from py.__.apigen.source.html import create_html, create_dir_html, create_unknown_html from py.xml import html -style = html.style(""" - - body, p, td { - background-color: #FFF; - color: black; - font-family: monospace; - } - - td.type { - width: 2em; - } - - td.name { - width: 30em; - } - - td.mtime { - width: 13em; - } - - td.size { - text-alignment: right; - } - -""") - -def create_dir_html(path): - h = html.html( - html.head( - html.title('directory listing of %s' % (path,)), - style, - ), - ) - body = html.body( - html.h1('directory listing of %s' % (path,)), - ) - h.append(body) - table = html.table() - body.append(table) - tbody = html.tbody() - table.append(tbody) - items = list(path.listdir()) - items.sort(key=lambda p: p.basename) - items.sort(key=lambda p: not p.check(dir=True)) - for fpath in items: - tr = html.tr() - tbody.append(tr) - td1 = html.td(fpath.check(dir=True) and 'D' or 'F', class_='type') - tr.append(td1) - href = fpath.basename - if fpath.check(dir=True): - href += '/' - td2 = html.td(html.a(fpath.basename, href=href), class_='name') - tr.append(td2) - td3 = html.td(time.strftime('%Y-%m-%d %H:%M:%S', - time.gmtime(fpath.mtime())), class_='mtime') - tr.append(td3) - if fpath.check(dir=True): - size = '' - unit = '' - else: - size = fpath.size() - unit = 'B' - for u in ['kB', 'MB', 'GB', 'TB']: - if size > 1024: - size = round(size / 1024.0, 2) - unit = u - td4 = html.td('%s %s' % (size, unit), class_='size') - tr.append(td4) - return unicode(h) - -def create_unknown_html(path): - h = html.html( - html.head( - html.title('Can not display page'), - style, - ), - html.body( - html.p('The data URL (%s) does not contain Python code.' % (path,)) - ), - ) - return h.unicode() - class Handler(server.TestHandler): BASE_URL='http://codespeak.net/svn/py/dist' @@ -129,3 +45,4 @@ if __name__ == '__main__': _main() + From fijal at codespeak.net Mon Jan 8 16:50:21 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 16:50:21 +0100 (CET) Subject: [py-svn] r36282 - in py/dist/py/apigen: rest rest/testing source tracer Message-ID: <20070108155021.C70A810071@code0.codespeak.net> Author: fijal Date: Mon Jan 8 16:50:20 2007 New Revision: 36282 Modified: py/dist/py/apigen/rest/genrest.py py/dist/py/apigen/rest/testing/test_rest.py py/dist/py/apigen/source/server.py py/dist/py/apigen/tracer/docstorage.py Log: Add a link to source viewer. Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Mon Jan 8 16:50:20 2007 @@ -17,6 +17,9 @@ There should exist various classes for that, different for Trac, different for CVSView, etc. """ + def getlinkobj(self, obj, name): + return None + def getlink(self, filename, lineno, funcname): raise NotImplementedError("Abstract link writer") @@ -57,6 +60,8 @@ self.baseurl = baseurl[:-1] def getlink(self, filename, lineno, funcname): + if filename.endswith('.pyc'): + filename = filename[:-1] if filename is None: return ":%s" % funcname,"" pkgpath = self.getpkgpath(filename) @@ -73,6 +78,13 @@ return "%s:%s" % (relname, funcname),\ "%s%s#%s" % (self.baseurl, relname, funcname) + def getlinkobj(self, name, obj): + try: + filename = sys.modules[obj.__module__].__file__ + return self.getlink(filename, 0, name) + except AttributeError: + return None + class DirectPaste(AbstractLinkWriter): """ No-link writer (inliner) """ @@ -272,6 +284,9 @@ for cls, bases, functions in classes: rest = [Title('class: %s' % (cls,), belowchar='='), LiteralBlock(self.dsa.get_doc(cls))] + # link to source + self.dsa.get_class_definition(cls) + if bases: rest.append(Title('base classes:', belowchar='^')), for base in bases: @@ -378,8 +393,12 @@ else: title = Title('function: %s' % (functionname,), belowchar=belowchar) + lst = [title, LiteralBlock(self.dsa.get_doc(functionname)), LiteralBlock(self.dsa.get_function_definition(functionname))] + link_to_function = self.linkgen.getlinkobj(functionname, self.dsa.get_obj(functionname)) + if link_to_function: + lst.insert(1, Paragraph(Text("source: "), Link(*link_to_function))) opar = Paragraph(Strong('origin'), ":") if origin: Modified: py/dist/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/dist/py/apigen/rest/testing/test_rest.py (original) +++ py/dist/py/apigen/rest/testing/test_rest.py Mon Jan 8 16:50:20 2007 @@ -437,3 +437,20 @@ assert tempdir.join('traceback_A.method.0.txt').open().read().find( '.. _`/py/apigen/rest/testing/test\_rest.py\:A.method`: http://localhost:8000/py/apigen/rest/testing/test_rest.py#A.method') != -1 + def test_sourceview_fun(self): + def f(): + pass + + descs = {'f':f} + ds = DocStorage().from_dict(descs) + t = Tracer(ds) + t.start_tracing() + f() + t.end_tracing() + tempdir = temppath.ensure("sourceview_fun", dir=True) + lg = SourceView('http://localhost:8000') + r = RestGen(ds, lg, DirWriter(tempdir)) + r.write() + self.check_rest(tempdir) + assert tempdir.join('function_f.txt').open().read().find( + '.. _`/py/apigen/rest/testing/test\_rest.py\:f`: http://localhost:8000/py/apigen/rest/testing/test_rest.py#f') != -1 Modified: py/dist/py/apigen/source/server.py ============================================================================== --- py/dist/py/apigen/source/server.py (original) +++ py/dist/py/apigen/source/server.py Mon Jan 8 16:50:20 2007 @@ -41,7 +41,7 @@ return f def _main(): - server.start_server(handler=Handler, start_new=False) + server.start_server(handler=Handler) if __name__ == '__main__': _main() Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Mon Jan 8 16:50:20 2007 @@ -288,6 +288,9 @@ retval.append(desc) return retval + def get_class_definition(self, name): + desc = self.ds.descs[name] + def desc_from_pyobj(self, pyobj, name): for desc in self.ds.descs.values(): if isinstance(desc, ClassDesc) and desc.pyobj is pyobj: @@ -299,3 +302,5 @@ # and make sure we'll not try to link to it directly return desc + def get_obj(self, name): + return self.ds.descs[name].pyobj From hpk at codespeak.net Mon Jan 8 16:51:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 16:51:11 +0100 (CET) Subject: [py-svn] r36283 - in py/dist/py/documentation: . apigen Message-ID: <20070108155111.D411510075@code0.codespeak.net> Author: hpk Date: Mon Jan 8 16:51:10 2007 New Revision: 36283 Added: py/dist/py/documentation/apigen/source-viewer.txt - copied unchanged from r36258, py/dist/py/documentation/source-viewer.txt Removed: py/dist/py/documentation/source-viewer.txt Log: move source-viewer docs to apigen to hide it a bit from the documentation hierarchy. Deleted: /py/dist/py/documentation/source-viewer.txt ============================================================================== --- /py/dist/py/documentation/source-viewer.txt Mon Jan 8 16:51:10 2007 +++ (empty file) @@ -1,44 +0,0 @@ -======================= -Source viewer for pylib -======================= - -Purpose: --------- - -As usual, main driving force to develop sth new is lack of several -possibilities between existing solutions. Major lack of features are: - -* Impossible to link to certain function like http://filename#function_name - -* Impossible to properly link - most informations coming from AST - -* We want this to nicely integrate with apigen - so crosslinking from - one to another makes sense (also backwards - like info for a function) - -Idea: ------ - -Basic idea is to take module as a py.path object, compile it (using compiler -module), than try to get some information and eventually import it and -get even some more information. Importing is optional and can be not performed -at all, but:: - - if 1: - def f(x): - pass - if 0: - def g(x): - pass - -could be only parsed well in case of importing stuff. There are also plans for -integrating more features ie. caching it by code and attaching a name to a code -generated by some magic functions. - -Status: -------- - -Right now there is ready `server`_ and along with an `API viewer`_. Next step -is to improve a look & feel of API viewer and to link one to another. - -.. _`server`: http://johnnydebris.net:8000/ -.. _`API viewer`: http://johnnydebris.net/pyapi From fijal at codespeak.net Mon Jan 8 16:52:06 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 16:52:06 +0100 (CET) Subject: [py-svn] r36284 - py/dist/py/apigen/rest Message-ID: <20070108155206.B193810075@code0.codespeak.net> Author: fijal Date: Mon Jan 8 16:52:05 2007 New Revision: 36284 Modified: py/dist/py/apigen/rest/genrest.py Log: Add the same for the class. Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Mon Jan 8 16:52:05 2007 @@ -285,6 +285,11 @@ rest = [Title('class: %s' % (cls,), belowchar='='), LiteralBlock(self.dsa.get_doc(cls))] # link to source + link_to_class = self.linkgen.getlinkobj(cls, self.dsa.get_obj(cls)) + if link_to_class: + rest.append(Paragraph(Text("source: "), Link(*link_to_class))) + + self.dsa.get_class_definition(cls) if bases: From hpk at codespeak.net Mon Jan 8 16:57:30 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 8 Jan 2007 16:57:30 +0100 (CET) Subject: [py-svn] r36285 - py/dist/py/documentation Message-ID: <20070108155730.08DB010075@code0.codespeak.net> Author: hpk Date: Mon Jan 8 16:57:21 2007 New Revision: 36285 Modified: py/dist/py/documentation/index.txt py/dist/py/documentation/log.txt Log: refining the py.log documentation and its integration a bit and marking it alpha, because i haven't checked in detail about the status. in any case, many thanks to Francois for providing it. Modified: py/dist/py/documentation/index.txt ============================================================================== --- py/dist/py/documentation/index.txt (original) +++ py/dist/py/documentation/index.txt Mon Jan 8 16:57:21 2007 @@ -9,6 +9,8 @@ `py.xml`_ a fast'n'easy way to generate xml/html documents (including CSS-styling) +`py.log`_ an alpha document about the ad-hoc logging facilities + `miscellaneous features`_ describes some more py lib features `future`_ handles development visions and plans for the near future. @@ -24,6 +26,7 @@ .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev .. _`py.execnet`: execnet.html .. _`py.magic.greenlet`: greenlet.html +.. _`py.log`: log.html .. _`py.test`: test.html .. _`py.xml`: xml.html .. _`Why What how py?`: why_py.html Modified: py/dist/py/documentation/log.txt ============================================================================== --- py/dist/py/documentation/log.txt (original) +++ py/dist/py/documentation/log.txt Mon Jan 8 16:57:21 2007 @@ -11,12 +11,12 @@ Foreword ======== -I attempt here to briefly state the actual specification of the -:code:`py.log` module, as currently found within the nice and fertile -`py library`__ project. I also want to either present or summarize a -few ideas for improving or developping this module further. +This document is an attempt to briefly state the actual specification of the +:code:`py.log` module. It was written by Francois Pinard and also contains +some ideas for enhancing the py.log facilities. -__ http://codespeak.net/py/current/doc/home.html + +NOTE that `py.log` is an ALPHA interface, it can change any time. This document is meant to trigger or facilitate discussions. It shamelessly steals from the `Agile Testing`__ comments, and from other sources as well, From fijal at codespeak.net Mon Jan 8 17:02:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 17:02:28 +0100 (CET) Subject: [py-svn] r36287 - py/branch/rsession-cleanup/py/documentation Message-ID: <20070108160228.7DF4410075@code0.codespeak.net> Author: fijal Date: Mon Jan 8 17:02:25 2007 New Revision: 36287 Modified: py/branch/rsession-cleanup/py/documentation/test.txt Log: Updated a bit. Modified: py/branch/rsession-cleanup/py/documentation/test.txt ============================================================================== --- py/branch/rsession-cleanup/py/documentation/test.txt (original) +++ py/branch/rsession-cleanup/py/documentation/test.txt Mon Jan 8 17:02:25 2007 @@ -713,17 +713,15 @@ distributed at the function and method level. As tests are completed, each node reports the results back to the ``Reporter`` on the master. -Two reporters are available, command line and web based. +Three reporters are available, command line, rest output and ajaxy web based. .. _`py.execnet`: execnet.html Differences from local tests ---------------------------- -* The only working command line options are ``--session=R``, ``-x`` and ``-k``. * Test order is *not* guaranteed. -* ``-x`` is currently not working in the web reporter. -* Hanging nodes or tests are not detected. +* Hanging nodes or tests are not detected properly. * ``conftest.py`` cannot reference files outside of the copied packages. Configuration @@ -773,7 +771,6 @@ .. _`pypy`: http://codespeak.net/pypy .. _`L/Rsession document`: test-distributed.html - Future/Planned Features of py.test ================================== From fijal at codespeak.net Mon Jan 8 17:05:24 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 17:05:24 +0100 (CET) Subject: [py-svn] r36289 - py/branch/rsession-cleanup/py/documentation Message-ID: <20070108160524.BA1D810075@code0.codespeak.net> Author: fijal Date: Mon Jan 8 17:05:22 2007 New Revision: 36289 Modified: py/branch/rsession-cleanup/py/documentation/test-distributed.txt Log: Updated a bit, now it more resembles actual state. Modified: py/branch/rsession-cleanup/py/documentation/test-distributed.txt ============================================================================== --- py/branch/rsession-cleanup/py/documentation/test-distributed.txt (original) +++ py/branch/rsession-cleanup/py/documentation/test-distributed.txt Mon Jan 8 17:05:22 2007 @@ -11,9 +11,7 @@ There are two new session objects, both located in `rsession.py`_. `RSession` and `LSession`. `RSession` is responsible for running tests distributedly, while `LSession` runs tests locally, but with different implementation -details (as well as using details). Options available are varying, I'll try to -write them down later. - +details (as well as using details). Abstraction layers: =================== @@ -24,7 +22,7 @@ represents them to the user. Default reporter is command line one (to be precise, there are different reporters for L and R Sessions because of different needs), but there is existing reporter which runs web server -(`web.py`_)and communicates over XMLHttp requests with the browser. +(`web.py`_) and communicates over XMLHttp requests with the browser. Writing down new reporter is relatively easy, way easier than writing session from a scratch, so one can write down GTK reporter and whatnot. @@ -55,9 +53,7 @@ ===================== Actually most of normal py.test features are implemented in distributed -version. These are command line options: -v -x -k --pdb (only for LSession). ---exec (moderatly easy for L/R Sessions), --pdb for RSession (with screen, -unsure if it'll be implemented). Quite missing is testing LSession under +version. Quite missing is testing LSession under OSX/Windows or other machines than mine. Unique features: @@ -70,7 +66,6 @@ as it's not gathered during setup/teardown stuff. Another unique feature is web server which allows you to track down tests and -how they goes (but I'm not sure whether anyone really wants to use it - it's -just a cool feature). +how they goes. .. _`box.py`: http://codespeak.net/svn/py/dist/py/test/rsession/box.py From guido at codespeak.net Mon Jan 8 17:09:03 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 8 Jan 2007 17:09:03 +0100 (CET) Subject: [py-svn] r36290 - py/dist/py/apigen/source Message-ID: <20070108160903.C103B10069@code0.codespeak.net> Author: guido Date: Mon Jan 8 17:08:57 2007 New Revision: 36290 Added: py/dist/py/apigen/source/path.py Modified: py/dist/py/apigen/source/index.cgi Log: Fixed problems importing the py lib in CGI. Modified: py/dist/py/apigen/source/index.cgi ============================================================================== --- py/dist/py/apigen/source/index.cgi (original) +++ py/dist/py/apigen/source/index.cgi Mon Jan 8 17:08:57 2007 @@ -1,6 +1,7 @@ #!/usr/bin/python import cgitb;cgitb.enable() +import path import py from py.__.apigen.source.browser import parse_path from py.__.apigen.source.html import create_html, create_dir_html, \ Added: py/dist/py/apigen/source/path.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/source/path.py Mon Jan 8 17:08:57 2007 @@ -0,0 +1,2 @@ +import os, sys +sys.path = ['/'.join(os.path.dirname(__file__).split(os.sep)[:-3])] + sys.path From fijal at codespeak.net Mon Jan 8 17:49:41 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 17:49:41 +0100 (CET) Subject: [py-svn] r36295 - py/dist/py/documentation Message-ID: <20070108164941.3DE5810076@code0.codespeak.net> Author: fijal Date: Mon Jan 8 17:49:40 2007 New Revision: 36295 Modified: py/dist/py/documentation/TODO.txt Log: Added note. Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Mon Jan 8 17:49:40 2007 @@ -52,6 +52,8 @@ * no lines longer than 80 characters +* review the pylib issue tracker + distutils install ----------------- From fijal at codespeak.net Mon Jan 8 17:51:19 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 17:51:19 +0100 (CET) Subject: [py-svn] r36296 - py/dist/py/documentation Message-ID: <20070108165119.A5C3710076@code0.codespeak.net> Author: fijal Date: Mon Jan 8 17:51:15 2007 New Revision: 36296 Modified: py/dist/py/documentation/test-distributed.txt py/dist/py/documentation/test.txt Log: Argh argh argh. commited in branch. Modified: py/dist/py/documentation/test-distributed.txt ============================================================================== --- py/dist/py/documentation/test-distributed.txt (original) +++ py/dist/py/documentation/test-distributed.txt Mon Jan 8 17:51:15 2007 @@ -11,9 +11,7 @@ There are two new session objects, both located in `rsession.py`_. `RSession` and `LSession`. `RSession` is responsible for running tests distributedly, while `LSession` runs tests locally, but with different implementation -details (as well as using details). Options available are varying, I'll try to -write them down later. - +details (as well as using details). Abstraction layers: =================== @@ -24,7 +22,7 @@ represents them to the user. Default reporter is command line one (to be precise, there are different reporters for L and R Sessions because of different needs), but there is existing reporter which runs web server -(`web.py`_)and communicates over XMLHttp requests with the browser. +(`web.py`_) and communicates over XMLHttp requests with the browser. Writing down new reporter is relatively easy, way easier than writing session from a scratch, so one can write down GTK reporter and whatnot. @@ -55,9 +53,7 @@ ===================== Actually most of normal py.test features are implemented in distributed -version. These are command line options: -v -x -k --pdb (only for LSession). ---exec (moderatly easy for L/R Sessions), --pdb for RSession (with screen, -unsure if it'll be implemented). Quite missing is testing LSession under +version. Quite missing is testing LSession under OSX/Windows or other machines than mine. Unique features: @@ -70,7 +66,6 @@ as it's not gathered during setup/teardown stuff. Another unique feature is web server which allows you to track down tests and -how they goes (but I'm not sure whether anyone really wants to use it - it's -just a cool feature). +how they goes. .. _`box.py`: http://codespeak.net/svn/py/dist/py/test/rsession/box.py Modified: py/dist/py/documentation/test.txt ============================================================================== --- py/dist/py/documentation/test.txt (original) +++ py/dist/py/documentation/test.txt Mon Jan 8 17:51:15 2007 @@ -713,7 +713,7 @@ distributed at the function and method level. As tests are completed, each node reports the results back to the ``Reporter`` on the master. -Two reporters are available, command line and web based. +Three reporters are available, command line, rest output and ajaxy web based. .. _`py.execnet`: execnet.html @@ -721,6 +721,7 @@ ---------------------------- * Test order is *not* guaranteed. +* Hanging nodes or tests are not detected properly. * ``conftest.py`` cannot reference files outside of the copied packages. Configuration @@ -770,7 +771,6 @@ .. _`pypy`: http://codespeak.net/pypy .. _`L/Rsession document`: test-distributed.html - Future/Planned Features of py.test ================================== From fijal at codespeak.net Mon Jan 8 18:02:46 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 8 Jan 2007 18:02:46 +0100 (CET) Subject: [py-svn] r36299 - in py/dist/py/apigen/source: . testing Message-ID: <20070108170246.E10D510079@code0.codespeak.net> Author: fijal Date: Mon Jan 8 18:02:33 2007 New Revision: 36299 Modified: py/dist/py/apigen/source/browser.py py/dist/py/apigen/source/html.py py/dist/py/apigen/source/testing/test_html.py Log: Added method links as well. Modified: py/dist/py/apigen/source/browser.py ============================================================================== --- py/dist/py/apigen/source/browser.py (original) +++ py/dist/py/apigen/source/browser.py Mon Jan 8 18:02:33 2007 @@ -15,7 +15,10 @@ blockers = [ast.Function, ast.Class] class BaseElem(object): - pass # purely for testing isinstance + def listnames(self): + if getattr(self, 'parent', None): + return self.parent.listnames() + '.' + self.name + return self.name class Module(BaseElem): def __init__(self, path, _dict): @@ -29,7 +32,11 @@ raise AttributeError(attr) def get_children(self): - return self.dict.values() + values = self.dict.values() + all = values[:] + for v in values: + all += v.get_children() + return all def get_endline(start, lst): l = reversed(lst) @@ -42,22 +49,27 @@ return start class Function(BaseElem): - def __init__(self, name, firstlineno, endlineno): + def __init__(self, name, parent, firstlineno, endlineno): self.firstlineno = firstlineno self.endlineno = endlineno self.name = name + self.parent = parent + + def get_children(self): + return [] class Method(BaseElem): - def __init__(self, name, firstlineno, endlineno): + def __init__(self, name, parent, firstlineno, endlineno): self.name = name self.firstlineno = firstlineno self.endlineno = endlineno + self.parent = parent -def function_from_ast(ast, cls=Function): +def function_from_ast(ast, cls_ast, cls=Function): startline = ast.lineno endline = get_endline(startline, ast.getChildNodes()) assert endline - return cls(ast.name, startline, endline) + return cls(ast.name, cls_ast, startline, endline) def class_from_ast(cls_ast): bases = [i.name for i in cls_ast.bases if isinstance(i, ast.Name)] @@ -66,9 +78,10 @@ startline = cls_ast.lineno name = cls_ast.name endline = get_endline(startline, cls_ast.getChildNodes()) - methods = dict([(i.name, function_from_ast(i, Method)) for i in \ + cls = Class(name, startline, endline, bases, []) + cls.methods = dict([(i.name, function_from_ast(i, cls, Method)) for i in \ cls_ast.code.nodes if isinstance(i, ast.Function)]) - return Class(name, startline, endline, bases, methods) + return cls class Class(BaseElem): def __init__(self, name, firstlineno, endlineno, bases, methods): @@ -113,7 +126,7 @@ nodes = dir_nodes(st) function_ast = [i for i in nodes if isinstance(i, ast.Function)] classes_ast = [i for i in nodes if isinstance(i, ast.Class)] - mod_dict = dict([(i.name, function_from_ast(i)) for i in function_ast] + mod_dict = dict([(i.name, function_from_ast(i, None)) for i in function_ast] + [(i.name, class_from_ast(i)) for i in classes_ast]) # we check all the elements, if they're really there try: Modified: py/dist/py/apigen/source/html.py ============================================================================== --- py/dist/py/apigen/source/html.py (original) +++ py/dist/py/apigen/source/html.py Mon Jan 8 18:02:33 2007 @@ -31,8 +31,8 @@ pos = row.find(item.name) assert pos != -1 end = len(item.name) + pos - chunk = html.a(row[pos:end], href="#" + item.name, - name=item.name) + chunk = html.a(row[pos:end], href="#" + item.listnames(), + name=item.listnames()) return [row[:pos], chunk, row[end:]] except KeyError: return [row] # no more info Modified: py/dist/py/apigen/source/testing/test_html.py ============================================================================== --- py/dist/py/apigen/source/testing/test_html.py (original) +++ py/dist/py/apigen/source/testing/test_html.py Mon Jan 8 18:02:33 2007 @@ -45,6 +45,7 @@ assert data.find(' Author: fijal Date: Mon Jan 8 18:11:35 2007 New Revision: 36302 Modified: py/dist/py/test/rsession/web.py py/dist/py/test/rsession/webdata/source.js py/dist/py/test/rsession/webjs.py Log: Fix issue with web tests and something that is not exactly Module Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Mon Jan 8 18:11:35 2007 @@ -57,11 +57,11 @@ fullitemname = "/".join(item.listnames()) d = {'fullitemname': fullitemname, 'itemtype': itemtype, 'itemname': itemname} - if itemtype == 'Module': - try: - d['length'] = str(len(list(event.item.tryiter()))) - except: - d['length'] = "?" + #if itemtype == 'Module': + try: + d['length'] = str(len(list(event.item.tryiter()))) + except: + d['length'] = "?" return d class MultiQueue(object): @@ -282,8 +282,8 @@ self.pending_events.put(event) def report_ItemStart(self, event): - #if isinstance(event.item, py.test.collect.Module): - self.pending_events.put(event) + if isinstance(event.item, py.test.collect.Module): + self.pending_events.put(event) def report_unknown(self, event): # XXX: right now, we just pass it for showing Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/dist/py/test/rsession/webjs.py ============================================================================== --- py/dist/py/test/rsession/webjs.py (original) +++ py/dist/py/test/rsession/webjs.py Mon Jan 8 18:11:35 2007 @@ -140,9 +140,9 @@ main_t = dom.document.getElementById("main_table") if msg['type'] == 'ItemStart': # we start a new directory or what - if msg['itemtype'] == 'Module': - tr = make_module_box(msg) - main_t.appendChild(tr) + #if msg['itemtype'] == 'Module': + tr = make_module_box(msg) + main_t.appendChild(tr) elif msg['type'] == 'SendItem': host_elem = dom.document.getElementById(msg['hostkey']) glob.host_pending[msg['hostkey']].insert(0, msg['fullitemname']) From cfbolz at codespeak.net Mon Jan 8 18:51:09 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Mon, 8 Jan 2007 18:51:09 +0100 (CET) Subject: [py-svn] r36305 - in py/dist/py/rest: . testing Message-ID: <20070108175109.A26C310064@code0.codespeak.net> Author: cfbolz Date: Mon Jan 8 18:51:02 2007 New Revision: 36305 Modified: py/dist/py/rest/directive.py py/dist/py/rest/testing/test_directive.py Log: add some support code for a interpreted text role that produces links Modified: py/dist/py/rest/directive.py ============================================================================== --- py/dist/py/rest/directive.py (original) +++ py/dist/py/rest/directive.py Mon Jan 8 18:51:02 2007 @@ -103,3 +103,13 @@ return [imagenode], [] latexformula_role.content = True latexformula_role.options = {} + +def register_linkrole(role_name, callback): + def source_role(name, rawtext, text, lineno, inliner, options={}, + content=[]): + text, target = callback(name, text) + reference_node = nodes.reference(rawtext, text, name=text, refuri=target) + return [reference_node], [] + source_role.content = True + source_role.options = {} + roles.register_canonical_role(role_name, source_role) Modified: py/dist/py/rest/testing/test_directive.py ============================================================================== --- py/dist/py/rest/testing/test_directive.py (original) +++ py/dist/py/rest/testing/test_directive.py Mon Jan 8 18:51:02 2007 @@ -7,6 +7,7 @@ from py.__.rest.latex import process_rest_file datadir = py.magic.autopath().dirpath().join("data") +testdir = py.test.ensuretemp("rest") def test_graphviz_html(): if not py.path.local.sysfind("dot"): @@ -39,4 +40,20 @@ assert dotpdf.check() pdf.remove() dotpdf.remove() - + +def test_own_links(): + def callback(name, text): + assert name == "foo" + return "bar xyz", "http://codespeak.net/noclue" + directive.register_linkrole("foo", callback) + txt = testdir.join("link-role.txt") + txt.write(""" +:foo:`whatever` +""") + process_rest_file(txt) + html = txt.new(ext="html") + rest.process(txt) + assert html.check() + htmlcontent = html.read() + assert "http://codespeak.net/noclue" in htmlcontent + assert "bar xyz" in htmlcontent From hpk at codespeak.net Tue Jan 9 11:03:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 11:03:53 +0100 (CET) Subject: [py-svn] r36321 - py/dist/py/documentation Message-ID: <20070109100353.2CF0C10075@code0.codespeak.net> Author: hpk Date: Tue Jan 9 11:03:49 2007 New Revision: 36321 Modified: py/dist/py/documentation/index.txt py/dist/py/documentation/test.txt Log: some test documentation rework (particularly the distributed part) Modified: py/dist/py/documentation/index.txt ============================================================================== --- py/dist/py/documentation/index.txt (original) +++ py/dist/py/documentation/index.txt Tue Jan 9 11:03:49 2007 @@ -1,7 +1,7 @@ py.test and the py lib - documentation ---------------------------------------- -`py.test`_ introduces to the new'n'easy **py.test** utility +`py.test`_ introduces to the **py.test** testing utility `py.execnet`_ an innovative way to distribute programs across the net (not stable on win32 yet) Modified: py/dist/py/documentation/test.txt ============================================================================== --- py/dist/py/documentation/test.txt (original) +++ py/dist/py/documentation/test.txt Tue Jan 9 11:03:49 2007 @@ -659,27 +659,40 @@ .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev -Distributed Testing -=================== +Automated Distributed Testing +================================== -If you have a project with a large number of tests, and you have several -machines available ``py.test`` can now speed up your test runs. +If you have a project with a large number of tests, and you have +machines accessible through SSH, ``py.test`` can distribute +tests across the machines. It does not require any particular +installation on the remote machine sides as it uses `py.execnet`_ +mechanisms to distribute execution. + +*Warning*: support for distributed testing is in alpha state +and its mechanics and configuration options may change without +prior notice. Benefits -------- -The main benefit is speed. There is some overhead required to setup the test -environment on the remote machines, but if the project has a lot of tests and -several machines available, the speed up can be quite good. The more machines -available, the better results you will get. - -Distributing the tests can be also be useful if you need to use remote resouces -which you do not have locally (perhaps a server has much more RAM or disk -available). Of course, you could also just ssh into the remote server and run -``py.test`` there, but only for committed changes. You really don't want to -manually copy your sources every time you make a change, do you? - -Also, the web interface is quite helpful. +The main benefit for using distributed testing is speed. There +is some runtime overhead required to setup the test environment on the +remote machines, but if the project has a lot of tests and +several machines available, the speed up should be significant +in most cases. The more machines available, the better results +you will get. + +Automatically distributing the tests can be also be useful if you +need to use remote resouces which you do not have locally (perhaps +a server has much more RAM or disk available). +Of course, you could also just ssh into the remote server, +rsync or checkout your source code and run py.test +but that is obviously more cumbersome for the development +process. + +The distributed testing part of ``py.test`` also provides +a web server interface which tells you about the progress +of your distributed test run interactively. Requirements ------------ @@ -691,11 +704,10 @@ Remote requirements: -* ssh server running +* ssh daemon running * ssh keys setup to allow login without a password -* python +* python (2.3 or 2.4 should work) * unix like machine (``os.fork``) -* network access to the machine How it works ------------ @@ -729,7 +741,7 @@ You must create a conftest.py in any parent directory above your tests. -The options are: +The options that you need to specify in that conftest.py file are: * `disthosts` - a list of ssh addresses (including a specific path if it should be different than the default: ``$HOME/pytestcache-hostname``) From fijal at codespeak.net Tue Jan 9 12:03:00 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 12:03:00 +0100 (CET) Subject: [py-svn] r36330 - py/dist/py/test/rsession Message-ID: <20070109110300.8449910075@code0.codespeak.net> Author: fijal Date: Tue Jan 9 12:02:59 2007 New Revision: 36330 Modified: py/dist/py/test/rsession/rsession.py Log: ouch. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Tue Jan 9 12:02:59 2007 @@ -160,7 +160,7 @@ """ self.was_failure = False def new_reporter(event): - if isinstance(event, report.ReceivedItemOutcome) and not event.outcome.passed: + if isinstance(event, report.ReceivedItemOutcome) and not event.outcome.passed and not event.outcome.skipped: self.was_failure = True return reporter(event) checkfun = lambda : self.config.option.exitfirst and \ From cfbolz at codespeak.net Tue Jan 9 12:21:23 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 12:21:23 +0100 (CET) Subject: [py-svn] r36334 - py/dist/py/misc Message-ID: <20070109112123.72EB110078@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 12:21:14 2007 New Revision: 36334 Added: py/dist/py/misc/findmissingdocstrings.py (contents, props changed) Log: add a small helper script that finds missing docstrings of exported things Added: py/dist/py/misc/findmissingdocstrings.py ============================================================================== --- (empty file) +++ py/dist/py/misc/findmissingdocstrings.py Tue Jan 9 12:21:14 2007 @@ -0,0 +1,26 @@ +#!/usr/bin/env python + +import py + +def report_if_missing(name, obj): + if obj.__doc__ is None: + print "%s misses a docstring" % (name, ) + if obj.__doc__ == "": + print "%s has an empty" % (name, ) + +if __name__ == '__main__': + stack = [(name, getattr(py, name)) for name in dir(py)[::-1] + if not name.startswith("__") and name != "compat"] + seen = {} + while stack: + name, obj = stack.pop() + if id(obj) in seen: + continue + else: + seen[id(obj)] = True + if callable(obj): + report_if_missing(name, obj) + if isinstance(obj, type) or isinstance(obj, type(py)): + stack.extend([("%s.%s" % (name, s), getattr(obj, s)) for s in dir(obj) + if len(s) <= 1 or not (s[0] == '_' and s[1] != '_')]) + From cfbolz at codespeak.net Tue Jan 9 12:27:01 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 12:27:01 +0100 (CET) Subject: [py-svn] r36336 - py/dist/py/misc Message-ID: <20070109112701.73C571007D@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 12:26:50 2007 New Revision: 36336 Modified: py/dist/py/misc/findmissingdocstrings.py Log: find XXXs in docstrings too Modified: py/dist/py/misc/findmissingdocstrings.py ============================================================================== --- py/dist/py/misc/findmissingdocstrings.py (original) +++ py/dist/py/misc/findmissingdocstrings.py Tue Jan 9 12:26:50 2007 @@ -5,8 +5,10 @@ def report_if_missing(name, obj): if obj.__doc__ is None: print "%s misses a docstring" % (name, ) - if obj.__doc__ == "": + elif obj.__doc__ == "": print "%s has an empty" % (name, ) + elif "XXX" in obj.__doc__: + print "%s has an 'XXX' in its docstring" % (name, ) if __name__ == '__main__': stack = [(name, getattr(py, name)) for name in dir(py)[::-1] From cfbolz at codespeak.net Tue Jan 9 12:40:14 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 12:40:14 +0100 (CET) Subject: [py-svn] r36337 - in py/dist/py: documentation path/svn Message-ID: <20070109114014.791B810069@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 12:40:11 2007 New Revision: 36337 Modified: py/dist/py/documentation/TODO.txt py/dist/py/path/svn/urlcommand.py py/dist/py/path/svn/wccommand.py Log: add missing docstrings to open. add a list of all missing docstrings to the TODO file. Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 12:40:11 2007 @@ -77,3 +77,112 @@ using Armin's collect class * hide py.test.TerminalSession and TkinterSession? (questionable) + + +Missing docstrings +------------------ + +_thread.WorkerPool misses a docstring +_thread.WorkerPool.shutdown misses a docstring +_thread.WorkerPool.join misses a docstring +_thread.WorkerPool.__init__ misses a docstring +_thread.ThreadOut misses a docstring +_thread.ThreadOut.write misses a docstring +_thread.ThreadOut.setwritefunc misses a docstring +_thread.ThreadOut.setdefaultwriter misses a docstring +_thread.ThreadOut.resetdefault misses a docstring +_thread.NamedThreadPool misses a docstring +code.Traceback misses a docstring +code.Traceback.Entry misses a docstring +code.Source.getblockend misses a docstring +code.Code misses a docstring +execnet.SshGateway misses a docstring +execnet.SocketGateway misses a docstring +execnet.PopenGateway misses a docstring +initpkg misses a docstring +io.dupfile misses a docstring +log.setconsumer misses a docstring +log.get misses a docstring +log.Syslog misses a docstring +log.STDOUT misses a docstring +log.STDERR misses a docstring +log.Producer.Message misses a docstring +log.Path misses a docstring +magic.greenlet misses a docstring +magic.greenlet.getcurrent misses a docstring +magic.greenlet.error misses a docstring +magic.greenlet.GreenletExit misses a docstring +magic.View.__viewkey__ misses a docstring +magic.View.__repr__ misses a docstring +magic.View.__new__ misses a docstring +magic.View.__matchkey__ misses a docstring +magic.View.__getattr__ misses a docstring +magic.AssertionError misses a docstring +path.svnwc misses a docstring +path.svnwc.visit misses a docstring +path.svnwc.revert misses a docstring +path.svnwc.rename misses a docstring +path.svnwc.readlines misses a docstring +path.svnwc.propset misses a docstring +path.svnwc.proplist misses a docstring +path.svnwc.propget misses a docstring +path.svnwc.propdel misses a docstring +path.svnwc.new misses a docstring +path.svnwc.move misses a docstring +path.svnwc.mkdir misses a docstring +path.svnwc.log misses a docstring +path.svnwc.getpycodeobj misses a docstring +path.svnwc.dump misses a docstring +path.svnwc.diff misses a docstring +path.svnwc.copy misses a docstring +path.svnwc.check misses a docstring +path.svnwc.blame misses a docstring +path.svnwc.add misses a docstring +path.svnwc.__str__ misses a docstring +path.svnwc.__repr__ misses a docstring +path.svnwc.__new__ misses a docstring +path.svnwc.__iter__ misses a docstring +path.svnwc.__hash__ misses a docstring +path.svnwc.__eq__ misses a docstring +path.svnwc.Checkers misses a docstring +path.svnurl misses a docstring +path.svnurl.__new__ misses a docstring +path.svnurl.Checkers misses a docstring +path.local.visit misses a docstring +path.local.sysexec has an 'XXX' in its docstring +path.local.Checkers misses a docstring +path.extpy.__new__ misses a docstring +path.extpy.Checkers misses a docstring +test.rest.RestReporter misses a docstring +test.rest.RelLinkWriter misses a docstring +test.rest.NoLinkWriter misses a docstring +test.rest.LinkWriter misses a docstring +test.exit misses a docstring +test.deprecated_call misses a docstring +test.collect.Module misses a docstring +test.collect.Module.Skipped misses a docstring +test.collect.Module.Passed misses a docstring +test.collect.Module.Outcome misses a docstring +test.collect.Module.Failed misses a docstring +test.collect.Module.ExceptionFailure misses a docstring +test.collect.Instance misses a docstring +test.collect.Generator misses a docstring +test.collect.DoctestFile misses a docstring +test.collect.Directory misses a docstring +test.collect.Collector misses a docstring +test.collect.Class misses a docstring +test.cmdline.main misses a docstring +test.TkinterSession misses a docstring +test.TerminalSession misses a docstring +test.RSession.reporterror misses a docstring +test.RSession.make_colitems misses a docstring +test.RSession.getpkgdir misses a docstring +test.Item misses a docstring +xml.html misses a docstring +xml.html.__tagclass__ misses a docstring +xml.html.__tagclass__.Attr misses a docstring +xml.html.__metaclass__ misses a docstring +xml.html.Style misses a docstring +xml.escape misses a docstring +xml.Tag misses a docstring +xml.Namespace misses a docstring Modified: py/dist/py/path/svn/urlcommand.py ============================================================================== --- py/dist/py/path/svn/urlcommand.py (original) +++ py/dist/py/path/svn/urlcommand.py Tue Jan 9 12:40:11 2007 @@ -70,6 +70,7 @@ return self._escape(self.strpath) def open(self, mode='r'): + """ return an opened file with the given mode. """ assert 'w' not in mode and 'a' not in mode, "XXX not implemented for svn cmdline" assert self.check(file=1) # svn cat returns an empty file otherwise def popen(cmd): Modified: py/dist/py/path/svn/wccommand.py ============================================================================== --- py/dist/py/path/svn/wccommand.py (original) +++ py/dist/py/path/svn/wccommand.py Tue Jan 9 12:40:11 2007 @@ -430,6 +430,7 @@ return paths def open(self, mode='r'): + """ return an opened file with the given mode. """ return open(self.strpath, mode) def _getbyspec(self, spec): From fijal at codespeak.net Tue Jan 9 13:03:56 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 13:03:56 +0100 (CET) Subject: [py-svn] r36338 - in py/dist/py: . test/rsession test/rsession/testing Message-ID: <20070109120356.20CA710069@code0.codespeak.net> Author: fijal Date: Tue Jan 9 13:03:45 2007 New Revision: 36338 Modified: py/dist/py/__init__.py py/dist/py/test/rsession/box.py py/dist/py/test/rsession/outcome.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/slave.py py/dist/py/test/rsession/testing/test_rsession.py py/dist/py/test/rsession/testing/test_slave.py Log: Cleanup a bit, test new features and fix it. Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Tue Jan 9 13:03:45 2007 @@ -30,7 +30,6 @@ 'test.fail' : ('./test/item.py', 'fail'), 'test.exit' : ('./test/session.py', 'exit'), 'test.compat.TestCase' : ('./test/compat.py', 'TestCase'), - 'test.remote' : ('./test/rsession/rsession.py', 'remote_options'), # configuration/initialization related test api 'test.Config' : ('./test/config.py', 'Config'), Modified: py/dist/py/test/rsession/box.py ============================================================================== --- py/dist/py/test/rsession/box.py (original) +++ py/dist/py/test/rsession/box.py Tue Jan 9 13:03:45 2007 @@ -54,8 +54,9 @@ self.PYTESTRETVAL = tempdir.join('retval') self.PYTESTSTDOUT = tempdir.join('stdout') self.PYTESTSTDERR = tempdir.join('stderr') - - nice_level = py.test.remote.nice_level + + from py.__.test.rsession.rsession import remote_options + nice_level = remote_options.nice_level pid = os.fork() if pid: if not continuation: Modified: py/dist/py/test/rsession/outcome.py ============================================================================== --- py/dist/py/test/rsession/outcome.py (original) +++ py/dist/py/test/rsession/outcome.py Tue Jan 9 13:03:45 2007 @@ -32,7 +32,8 @@ relline = lineno - tb_entry.frame.code.firstlineno path = str(tb_entry.path) try: - if py.test.remote.tbstyle == 'long': + from py.__.test.rsession.rsession import remote_options + if remote_options.tbstyle == 'long': source = str(tb_entry.getsource()) else: source = str(tb_entry.getsource()).split("\n")[relline] Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Tue Jan 9 13:03:45 2007 @@ -160,7 +160,9 @@ """ self.was_failure = False def new_reporter(event): - if isinstance(event, report.ReceivedItemOutcome) and not event.outcome.passed and not event.outcome.skipped: + if isinstance(event, report.ReceivedItemOutcome) and \ + not event.outcome.passed and \ + not event.outcome.skipped: self.was_failure = True return reporter(event) checkfun = lambda : self.config.option.exitfirst and \ Modified: py/dist/py/test/rsession/slave.py ============================================================================== --- py/dist/py/test/rsession/slave.py (original) +++ py/dist/py/test/rsession/slave.py Tue Jan 9 13:03:45 2007 @@ -62,7 +62,8 @@ excinfo = py.code.ExceptionInfo() send(Outcome(excinfo=excinfo, is_critical=True).make_repr()) else: - if not res[0] and py.test.remote.exitfirst: + from py.__.test.rsession.rsession import remote_options + if not res[0] and not res[3] and remote_options.exitfirst: # we're finished, but need to eat what we can send(res) break @@ -96,10 +97,11 @@ from py.__.test.rsession.rsession import RemoteOptions from py.__.test.rsession.slave import Info Info.pid = 0 - py.test.remote = RemoteOptions(options) + from py.__.test.rsession import rsession + rsession.remote_options = RemoteOptions(options) # XXX the following assumes that py lib is there, a bit - # much of an assumtion - if not py.test.remote.nomagic: + # much of an assumtion + if not rsession.remote_options.nomagic: py.magic.invoke(assertion=1) mod = __import__(pkgname) assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir) @@ -107,5 +109,5 @@ queue = Queue() channel.setcallback(callback_gen(queue)) slave_main(queue.get, channel.send, basedir) - if not py.test.remote.nomagic: + if not rsession.remote_options.nomagic: py.magic.revoke(assertion=1) Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Tue Jan 9 13:03:45 2007 @@ -118,6 +118,9 @@ tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" def test_1(): pass + def test_x(): + import py + py.test.skip("aaa") def test_2(): assert 0 def test_3(): @@ -132,8 +135,8 @@ rsession.main(args, reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] + assert len(testevents) == 3 assert rsession.checkfun() - assert len(testevents) == 2 def test_example_distribution(self): subdir = "sub_example_dist" Modified: py/dist/py/test/rsession/testing/test_slave.py ============================================================================== --- py/dist/py/test/rsession/testing/test_slave.py (original) +++ py/dist/py/test/rsession/testing/test_slave.py Tue Jan 9 13:03:45 2007 @@ -35,10 +35,12 @@ asddsa def funcoption(): - assert py.test.remote.we_are_remote + from py.__.test.rsession.rsession import remote_options + assert remote_options.we_are_remote def funcoptioncustom(): - assert py.test.remote.custom == "custom" + from py.__.test.rsession.rsession import remote_options + assert remote_options.custom == "custom" def funchang(): import time From fijal at codespeak.net Tue Jan 9 13:07:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 13:07:11 +0100 (CET) Subject: [py-svn] r36339 - in py/dist/py/apigen: rest rest/testing tracer tracer/testing Message-ID: <20070109120711.EA80110060@code0.codespeak.net> Author: fijal Date: Tue Jan 9 13:07:00 2007 New Revision: 36339 Added: py/dist/py/apigen/tracer/permastore.py Modified: py/dist/py/apigen/rest/genrest.py py/dist/py/apigen/rest/testing/test_rest.py py/dist/py/apigen/tracer/description.py py/dist/py/apigen/tracer/docstorage.py py/dist/py/apigen/tracer/model.py py/dist/py/apigen/tracer/testing/test_docgen.py Log: Cleanup a bit and add a permastorage. Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Tue Jan 9 13:07:00 2007 @@ -175,10 +175,10 @@ return '%s.html' % (targetfilename,) class RestGen(object): - def __init__(self, ds, linkgen, writer=PipeWriter()): + def __init__(self, dsa, linkgen, writer=PipeWriter()): #assert isinstance(linkgen, DirectPaste), ( # "Cannot use different linkgen by now") - self.dsa = DocStorageAccessor(ds) + self.dsa = dsa self.linkgen = linkgen self.writer = writer self.tracebacks = {} @@ -285,13 +285,11 @@ rest = [Title('class: %s' % (cls,), belowchar='='), LiteralBlock(self.dsa.get_doc(cls))] # link to source - link_to_class = self.linkgen.getlinkobj(cls, self.dsa.get_obj(cls)) - if link_to_class: - rest.append(Paragraph(Text("source: "), Link(*link_to_class))) + # XXX kill get_obj + #link_to_class = self.linkgen.getlinkobj(cls, self.dsa.get_obj(cls)) + #if link_to_class: + # rest.append(Paragraph(Text("source: "), Link(*link_to_class))) - - self.dsa.get_class_definition(cls) - if bases: rest.append(Title('base classes:', belowchar='^')), for base in bases: @@ -401,9 +399,10 @@ lst = [title, LiteralBlock(self.dsa.get_doc(functionname)), LiteralBlock(self.dsa.get_function_definition(functionname))] - link_to_function = self.linkgen.getlinkobj(functionname, self.dsa.get_obj(functionname)) - if link_to_function: - lst.insert(1, Paragraph(Text("source: "), Link(*link_to_function))) + # XXX: investigate how to do it without getobj + #link_to_function = self.linkgen.getlinkobj(functionname, self.dsa.get_obj(functionname)) + #if link_to_function: + # lst.insert(1, Paragraph(Text("source: "), Link(*link_to_function))) opar = Paragraph(Strong('origin'), ":") if origin: @@ -441,7 +440,7 @@ lst.append(Paragraph(Strong('exceptions that might appear during ' 'execution'), ":")) for exc in exceptions: - lst.append(ListItem(exc.__name__)) + lst.append(ListItem(exc)) # XXX: right now we leave it alone # XXX missing implementation of dsa.get_function_location() @@ -481,7 +480,7 @@ linktarget = self.writer.getlink('traceback', tbname, 'traceback_%s' % (tbname,)) - frest = [Paragraph("called in %s" % call_site[0].code.filename), + frest = [Paragraph("called in %s" % call_site[0].filename), Paragraph(Link("traceback %s" % (tbname,), linktarget))] return frest, (tbname, tbrest) @@ -491,8 +490,8 @@ self.tracebacks[funcname].append(call_site) tbrest = [Title('traceback for %s' % (funcname,))] for line in call_site: - lineno = line.lineno - line.code.firstlineno - linkname, linktarget = self.linkgen.getlink(line.code.filename, + lineno = line.lineno - line.firstlineno + linkname, linktarget = self.linkgen.getlink(line.filename, line.lineno + 1, funcname) if linktarget: @@ -500,7 +499,7 @@ else: tbrest.append(Paragraph(linkname)) try: - source = line.code.source() + source = line.source except IOError: source = "*cannot get source*" mangled = [] Modified: py/dist/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/dist/py/apigen/rest/testing/test_rest.py (original) +++ py/dist/py/apigen/rest/testing/test_rest.py Tue Jan 9 13:07:00 2007 @@ -10,7 +10,9 @@ DirectPaste, DirectFS, \ HTMLDirWriter, SourceView from py.__.apigen.tracer.tracer import Tracer -from py.__.apigen.tracer.docstorage import DocStorage +from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor +from py.__.apigen.tracer.permastore import PermaDocStorage +import pickle from py.__.apigen.tracer.testing.runtest import cut_pyc from py.__.documentation.conftest import genlinkchecks @@ -152,7 +154,7 @@ s2.method(1,2,3) fun(1, 3, s2) t.end_tracing() - return ds + return DocStorageAccessor(ds) def get_filled_docstorage_modules(self): import somemodule @@ -171,7 +173,7 @@ s2.method(1, 2, 3) someothermodule.fun(1, 3, s2) t.end_tracing() - return ds + return DocStorageAccessor(ds) def check_rest(self, tempdir): from py.__.misc import rest @@ -208,6 +210,14 @@ assert sorted(basenames) == expected # now we check out... self.check_rest(tempdir) + tempdir = temppath.ensure("simple_api_ps", dir=True) + ps = PermaDocStorage(ds) + r = RestGen(ps, lg, DirWriter(tempdir)) + r.write() + basenames = [p.basename for p in tempdir.listdir('*.txt')] + assert sorted(basenames) == expected + self.check_rest(tempdir) + pickle.dumps(ps) def test_generation_modules(self): ds = self.get_filled_docstorage_modules() @@ -256,6 +266,14 @@ data = _nl(tempfile.read()) assert data.find('.. _`fun`: #function-fun\n') > -1 assert data.find('.. _`fun`: function_fun.html') == -1 + tempfile = temppath.ensure("internal_links_ps.txt", file=True) + ps = PermaDocStorage(ds) + r = RestGen(ps, lg, FileWriter(tempfile)) + r.write() + data = _nl(tempfile.read()) + assert data.find('.. _`fun`: #function-fun\n') > -1 + assert data.find('.. _`fun`: function_fun.html') == -1 + pickle.dumps(ps) def test_check_section_order(self): # we use the previous method's data @@ -264,7 +282,6 @@ py.test.skip('depends on previous test, which failed') data = _nl(tempfile.read()) # index should be above the rest - print data assert data.find('classes\\:') > -1 assert data.find('classes\\:') < data.find('function\\: fun') assert data.find('classes\\:') < data.find( @@ -293,7 +310,7 @@ t.end_tracing() lg = DirectPaste() tempdir = temppath.ensure("some_fun", dir=True) - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() self.check_rest(tempdir) @@ -311,10 +328,14 @@ t.end_tracing() lg = DirectPaste() tempdir = temppath.ensure("function_source", dir=True) - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() assert tempdir.join("function_blah.txt").read().find("a = 3") != -1 self.check_rest(tempdir) + ps = DocStorageAccessor(ds) + r = RestGen(ps, lg, DirWriter(tempdir)) + r.write() + assert tempdir.join("function_blah.txt").read().find("a = 3") != -1 def test_function_arguments(self): def blah(a, b, c): @@ -331,7 +352,7 @@ t.end_tracing() lg = DirectPaste() tempdir = temppath.ensure("function_args", dir=True) - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() source = tempdir.join("function_blah.txt").read() call_point = source.find("call sites\:") @@ -365,7 +386,7 @@ t.end_tracing() lg = DirectPaste() tempdir = temppath.ensure("classargs", dir=True) - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() source = tempdir.join("function_xxx.txt").read() call_point = source.find("call sites\:") @@ -392,7 +413,7 @@ t.end_tracing() lg = DirectPaste() tempdir = temppath.ensure("exc_raising", dir=True) - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() source = tempdir.join('function_x.txt').open().read() assert source.find('ZeroDivisionError') < source.find('call sites\:') @@ -414,7 +435,7 @@ t.end_tracing() lg = DirectPaste() tempdir = temppath.ensure("nonexit_origin", dir=True) - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() self.check_rest(tempdir) @@ -431,13 +452,14 @@ t.end_tracing() lg = SourceView('http://localhost:8000') tempdir = temppath.ensure("sourceview", dir=True) - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() self.check_rest(tempdir) assert tempdir.join('traceback_A.method.0.txt').open().read().find( '.. _`/py/apigen/rest/testing/test\_rest.py\:A.method`: http://localhost:8000/py/apigen/rest/testing/test_rest.py#A.method') != -1 def test_sourceview_fun(self): + py.test.skip("killed for a while") def f(): pass @@ -449,7 +471,7 @@ t.end_tracing() tempdir = temppath.ensure("sourceview_fun", dir=True) lg = SourceView('http://localhost:8000') - r = RestGen(ds, lg, DirWriter(tempdir)) + r = RestGen(DocStorageAccessor(ds), lg, DirWriter(tempdir)) r.write() self.check_rest(tempdir) assert tempdir.join('function_f.txt').open().read().find( Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Tue Jan 9 13:07:00 2007 @@ -8,28 +8,48 @@ MAX_CALL_SITES = 20 +class CallFrame(object): + def __init__(self, frame): + self.filename = frame.code.raw.co_filename + self.lineno = frame.lineno + self.firstlineno = frame.code.firstlineno + self.source = frame.code.source() + + def _getval(self): + return (self.filename, self.lineno) + + def __hash__(self): + return hash(self._getval()) + + def __eq__(self, other): + return self._getval() == other._getval() + + def __ne__(self, other): + return not self == other + class CallStack(object): def __init__(self, tb): - if isinstance(tb, py.code.Traceback): - self.tb = tb - else: - self.tb = py.code.Traceback(tb) - - def _getval(self): - return [(frame.code.raw.co_filename, frame.lineno+1) for frame - in self] + #if isinstance(tb, py.code.Traceback): + # self.tb = tb + #else: + # self.tb = py.code.Traceback(tb) + self.tb = [CallFrame(frame) for frame in tb] + + #def _getval(self): + # return [(frame.code.raw.co_filename, frame.lineno+1) for frame + # in self] def __hash__(self): - return hash(tuple(self._getval())) + return hash(tuple(self.tb)) def __eq__(self, other): - return self._getval() == other._getval() + return self.tb == other.tb def __ne__(self, other): return not self == other - def __getattr__(self, attr): - return getattr(self.tb, attr) + #def __getattr__(self, attr): + # return getattr(self.tb, attr) def __iter__(self): return iter(self.tb) @@ -41,7 +61,7 @@ return len(self.tb) def __cmp__(self, other): - return cmp(self._getval(), other._getval()) + return cmp(self.tb, other.tb) def cut_stack(stack, frame, upward_frame=None): if hasattr(frame, 'raw'): Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Tue Jan 9 13:07:00 2007 @@ -219,7 +219,10 @@ def get_function_local_changes(self, name): return self.ds.descs[name].get_local_changes() - + + def get_function_exceptions(self, name): + return sorted([i.__name__ for i in self.ds.descs[name].exceptions.keys()]) + def get_module_name(self): if hasattr(self.ds, 'module'): return self.ds.module.__name__ @@ -229,20 +232,7 @@ desc = self.ds.descs[name] assert isinstance(desc, ClassDesc) return sorted(desc.getfields()) - - def get_type_desc(self, _type): - # XXX We provide only classes here - if not isinstance(_type, model.SomeClass): - return None - # XXX we might want to cache it at some point - for key, desc in self.ds.descs.iteritems(): - if desc.pyobj == _type.cls: - return key, 'class', desc.is_degenerated - return None - - #def get_object_info(self, key): - # - + def get_module_info(self): module = getattr(self.ds, 'module', None) if module is None: @@ -255,8 +245,15 @@ pass return retval - def get_function_exceptions(self, name): - return sorted(self.ds.descs[name].exceptions.keys()) + def get_type_desc(self, _type): + # XXX We provide only classes here + if not isinstance(_type, model.SomeClass): + return None + # XXX we might want to cache it at some point + for key, desc in self.ds.descs.iteritems(): + if desc.pyobj == _type.cls: + return key, 'class', desc.is_degenerated + return None def get_method_origin(self, name): method = self.ds.descs[name].pyobj @@ -288,9 +285,6 @@ retval.append(desc) return retval - def get_class_definition(self, name): - desc = self.ds.descs[name] - def desc_from_pyobj(self, pyobj, name): for desc in self.ds.descs.values(): if isinstance(desc, ClassDesc) and desc.pyobj is pyobj: Modified: py/dist/py/apigen/tracer/model.py ============================================================================== --- py/dist/py/apigen/tracer/model.py (original) +++ py/dist/py/apigen/tracer/model.py Tue Jan 9 13:07:00 2007 @@ -89,22 +89,31 @@ def __init__(self, cls): self.cls = cls + self.name = cls.__name__ + self.id = id(cls) + + def __getstate__(self): + return (self.name, self.id) + + def __setstate__(self, state): + self.name, self.id = state + self.cls = None def __hash__(self): - return hash("Class") ^ hash(self.cls) + return hash("Class") ^ hash(self.id) def __eq__(self, other): if type(other) is not SomeClass: return False - return self.cls == other.cls + return self.id == other.id def unionof(self, other): - if type(other) is not SomeClass or self.cls is not other.cls: + if type(other) is not SomeClass or self.id is not other.id: return super(SomeClass, self).unionof(other) return self def __repr__(self): - return "Class %s" % self.cls.__name__ + return "Class %s" % self.name class SomeCode(SomeObject): typedef = types.CodeType Added: py/dist/py/apigen/tracer/permastore.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/tracer/permastore.py Tue Jan 9 13:07:00 2007 @@ -0,0 +1,103 @@ + +class DescPlaceholder(object): + pass + +class ClassPlaceholder(object): + pass + +class SerialisableClassDesc(object): + def __init__(self, original_desc): + self.is_degenerated = original_desc.is_degenerated + self.name = original_desc.name + +class PermaDocStorage(object): + """ Picklable version of docstorageaccessor + """ + function_fields = ['source', 'signature', 'definition', 'callpoints', + 'local_changes', 'exceptions'] + + def __init__(self, dsa): + """ Initialise from original doc storage accessor + """ + self.names = {} + self.module_info = dsa.get_module_info() + self.module_name = dsa.get_module_name() + self._save_functions(dsa) + self._save_classes(dsa) + + def _save_functions(self, dsa): + names = dsa.get_function_names() + self.function_names = names + for name in names: + self._save_function(dsa, name) + + def _save_function(self, dsa, name): + ph = DescPlaceholder() + ph.__doc__ = dsa.get_doc(name) + for field in self.function_fields: + setattr(ph, field, getattr(dsa, 'get_function_%s' % field)(name)) + self.names[name] = ph + return ph + + def _save_classes(self, dsa): + names = dsa.get_class_names() + self.class_names = names + for name in names: + ph = ClassPlaceholder() + ph.__doc__ = dsa.get_doc(name) + methods = dsa.get_class_methods(name) + ph.methods = methods + ph.base_classes = [SerialisableClassDesc(i) for i in + dsa.get_possible_base_classes(name)] + + for method in methods: + method_name = name + "." + method + mh = self._save_function(dsa, name + "." + method) + mh.origin = SerialisableClassDesc(dsa.get_method_origin( + method_name)) + self.names[name] = ph + + def get_class_methods(self, name): + desc = self.names[name] + assert isinstance(desc, ClassPlaceholder) + return desc.methods + + def get_doc(self, name): + return self.names[name].__doc__ + + def get_module_info(self): + return self.module_info + + def get_module_name(self): + return self.module_name + + def get_class_names(self): + return self.class_names + + def get_function_names(self): + return self.function_names + + def get_method_origin(self, name): + # returns a DESCRIPTION of a method origin, to make sure where we + # write it + return self.names[name].origin + + def get_possible_base_classes(self, name): + # returns list of descs of base classes + return self.names[name].base_classes + + # This are placeholders to provide something more reliable + def get_type_desc(self, _type): + return None + + #def get_obj(self, name): + # This is quite hairy, get rid of it soon + # # returns a pyobj + # pass + +for field in PermaDocStorage.function_fields: + def f(self, name, field=field): + return getattr(self.names[name], field) + func_name = 'get_function_%s' % field + f.func_name = func_name + setattr(PermaDocStorage, func_name, f) Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Tue Jan 9 13:07:00 2007 @@ -11,6 +11,7 @@ from py.__.apigen.tracer.testing.runtest import cut_pyc from py.__.apigen.tracer.description import FunctionDesc from py.__.apigen.tracer import model +from py.__.apigen.tracer.permastore import PermaDocStorage # from pypy.annotation import model #except ImportError, s: # py.test.skip("Cannot import: %s" % str(s)) @@ -44,11 +45,18 @@ f_name = cut_pyc(__file__) assert len(cs[0]) == 1 assert len(cs[1]) == 1 - assert cs[0][0].code.filename == f_name + assert cs[1][0].filename == f_name # lines are counted from 0 - assert cs[0][0].lineno == test_basic.func_code.co_firstlineno + 4 - assert cs[1][0].code.filename == f_name - assert cs[1][0].lineno == test_basic.func_code.co_firstlineno + 5 + num = test_basic.func_code.co_firstlineno + assert cs[1][0].lineno == num + 4 or cs[1][0].lineno == num + 5 + assert cs[0][0].filename == f_name + assert cs[0][0].lineno == num + 5 or cs[0][0].lineno == num + 4 + pds = PermaDocStorage(DocStorageAccessor(ds)) + assert pds.get_function_names() == ['fun'] + sig = pds.get_function_signature('fun') + assert sig[0][0][0] == 'a' + assert isinstance(sig[0][0][1], model.SomeUnion) + assert len(pds.get_function_callpoints('fun')) == 2 class AClass(object): """ Class docstring @@ -92,7 +100,7 @@ cs = sorted(desc.fields['__init__'].call_sites.keys()) assert len(cs) == 1 assert len(cs[0]) == 1 - assert cs[0][0].code.filename == f_name + assert cs[0][0].filename == f_name assert cs[0][0].lineno == test_class.func_code.co_firstlineno + 4 # method check assert sorted(desc.getfields()) == ['__init__', 'exposed_method'] @@ -105,6 +113,9 @@ assert isinstance(inputcells[2], model.SomeFloat) assert isinstance(inputcells[3], model.SomeList) assert isinstance(desc.fields['exposed_method'].retval, model.SomeString) + pds = PermaDocStorage(DocStorageAccessor(ds)) + assert pds.get_class_names() == ['AClass'] + assert len(pds.get_function_signature('AClass.exposed_method')[0]) == 4 def other_fun(): pass @@ -130,7 +141,9 @@ t.end_tracing() desc = ds.descs["other_fun"] assert len(desc.call_sites.keys()) == 1 - assert isinstance(desc.call_sites.values()[0][0], py.code.Frame) + #assert isinstance(desc.call_sites.values()[0][0], py.code.Frame) + pds = PermaDocStorage(DocStorageAccessor(ds)) + assert len(pds.get_function_callpoints("other_fun")) == 1 class A(object): def method(self, x): @@ -153,6 +166,7 @@ model.SomeInt) assert isinstance(ds.descs['B'].fields['method'].inputcells[1], model.SomeInt) + pds = PermaDocStorage(DocStorageAccessor(ds)) def test_local_changes(): class testclass(object): @@ -170,6 +184,7 @@ methdesc = desc.fields['bar'] #assert methdesc.old_dict != methdesc.new_dict assert methdesc.get_local_changes() == {'foo': set(['changed'])} + return ds def test_local_changes_nochange(): class testclass(object): @@ -185,6 +200,7 @@ desc = ds.descs['testclass'] methdesc = desc.fields['bar'] assert methdesc.get_local_changes() == {} + return ds def test_multiple_classes_with_same_init(): class A: @@ -202,6 +218,7 @@ t.end_tracing() assert len(ds.descs['A'].fields['__init__'].call_sites) == 1 assert len(ds.descs['B'].fields['__init__'].call_sites) == 1 + return ds def test_exception_raise(): def x(): @@ -224,6 +241,7 @@ assert ds.descs['x'].exceptions.keys() == [ZeroDivisionError] assert ds.descs['y'].exceptions.keys() == [ZeroDivisionError] assert ds.descs['z'].exceptions.keys() == [] + return ds def test_subclass(): descs = {'ANotherClass': ANotherClass} @@ -241,6 +259,7 @@ assert len(inputcells) == 2 bases = desc.bases assert len(bases) == 2 + return ds def test_bases(): class A: @@ -256,6 +275,7 @@ dsa = DocStorageAccessor(ds) for desc in dsa.get_possible_base_classes('C'): assert desc is ds.descs['B'] or desc.is_degenerated + return ds def test_desc_from_pyobj(): class A: @@ -267,6 +287,7 @@ ds = DocStorage().from_dict({'A': A, 'B': B}) dsa = DocStorageAccessor(ds) assert dsa.desc_from_pyobj(A, 'A') is ds.descs['A'] + return ds def test_method_origin(): class A: @@ -284,6 +305,7 @@ dsa = DocStorageAccessor(ds) origin = dsa.get_method_origin('C.bar') assert origin is ds.descs['B'] + return ds def test_multiple_methods(): class A(object): @@ -305,3 +327,4 @@ t.end_tracing() assert len(ds.descs['B'].fields['meth'].call_sites) == 1 assert len(ds.descs['C'].fields['meth'].call_sites) == 1 + return ds From fijal at codespeak.net Tue Jan 9 13:07:59 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 13:07:59 +0100 (CET) Subject: [py-svn] r36340 - py/dist/py Message-ID: <20070109120759.85EF510071@code0.codespeak.net> Author: fijal Date: Tue Jan 9 13:07:57 2007 New Revision: 36340 Modified: py/dist/py/conftest.py Log: Added a pickling possibility Modified: py/dist/py/conftest.py ============================================================================== --- py/dist/py/conftest.py (original) +++ py/dist/py/conftest.py Tue Jan 9 13:07:57 2007 @@ -33,14 +33,25 @@ get_doc_storage = staticmethod(get_doc_storage) def write_docs(ds): + #from py.__.apigen.rest.genrest import DirectPaste, RestGen, \ + # HTMLDirWriter, SourceView + #from py.__.apigen.rest.htmlhandlers import IndexHandler, PageHandler + #outdir = py.path.local('/tmp/output') + #RestGen(ds, SourceView("http://codespeak.net/source"), + # HTMLDirWriter(IndexHandler, PageHandler, outdir)).write() + #if not outdir.join('style.css').check(): + # py.magic.autopath().dirpath().join('apigen/style.css').copy(outdir) + #if not outdir.join('apigen.js').check(): + # py.magic.autopath().dirpath().join('apigen/apigen.js').copy(outdir) from py.__.apigen.rest.genrest import DirectPaste, RestGen, \ HTMLDirWriter, SourceView from py.__.apigen.rest.htmlhandlers import IndexHandler, PageHandler - outdir = py.path.local('/tmp/output') - RestGen(ds, SourceView("http://johnnydebris.net:8000/"), - HTMLDirWriter(IndexHandler, PageHandler, outdir)).write() - if not outdir.join('style.css').check(): - py.magic.autopath().dirpath().join('apigen/style.css').copy(outdir) - if not outdir.join('apigen.js').check(): - py.magic.autopath().dirpath().join('apigen/apigen.js').copy(outdir) + from py.__.apigen.tracer.permastore import PermaDocStorage + from py.__.apigen.tracer.docstorage import DocStorageAccessor + ps = PermaDocStorage(DocStorageAccessor(ds)) + try: + from cPickle import dumps + except ImportError: + from pickle import dumps + open("/tmp/output.pickle", "w").write(dumps(ps)) write_docs = staticmethod(write_docs) From hpk at codespeak.net Tue Jan 9 13:11:38 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 13:11:38 +0100 (CET) Subject: [py-svn] r36342 - py/dist/py/documentation Message-ID: <20070109121138.CC45A10075@code0.codespeak.net> Author: hpk Date: Tue Jan 9 13:11:30 2007 New Revision: 36342 Modified: py/dist/py/documentation/test.txt Log: some streamlining of the distributed testing description (needs more work) Modified: py/dist/py/documentation/test.txt ============================================================================== --- py/dist/py/documentation/test.txt (original) +++ py/dist/py/documentation/test.txt Tue Jan 9 13:11:30 2007 @@ -712,20 +712,22 @@ How it works ------------ -Distributed testing is implemented as a new ``Session`` object named -``RSession`` which is used instead of the default session. It splits the -collection and reporting duties into two separate classes. - -There is one master and one or more nodes. At start up, the master connects to -each node using `py.execnet`_ and copies the tests and all specified python -packages to all nodes. Once all of the files have been transferred, the master -begins to collect all of the tests and immediately sends test item -descriptions. Each node has a local queue of tests to run, and begins to -execute the tests with proper py.test setup and teardown. The tests are -distributed at the function and method level. As tests are completed, each node -reports the results back to the ``Reporter`` on the master. +When you issue ``py.test --session=R`` then your computer becomes +the distributor of tests ("master") and will start distributing tests +to several machines which need to be specified in a ``conftest.py`` +file. + +At start up, the master connects to each node using `py.execnet`_ and +its SSHGateways and *rsyncs* all specified python packages to all nodes. +Then the master collects all of the tests and immediately sends test item +descriptions to its connected nodes. Each node has a local queue of tests +to run and begins to execute the tests, following the setup and teardown +semantics. The test are distributed at function and method level. +When a test run on a node is completed it reports back the result +to the master. -Three reporters are available, command line, rest output and ajaxy web based. +The master can run one of three reporters to process the events +from the testing nodes: command line, rest output and ajaxy web based. .. _`py.execnet`: execnet.html From hpk at codespeak.net Tue Jan 9 13:12:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 13:12:31 +0100 (CET) Subject: [py-svn] r36343 - in py/dist/py: documentation execnet Message-ID: <20070109121231.3B04D10069@code0.codespeak.net> Author: hpk Date: Tue Jan 9 13:12:23 2007 New Revision: 36343 Modified: py/dist/py/documentation/TODO.txt py/dist/py/execnet/channel.py py/dist/py/execnet/gateway.py py/dist/py/execnet/register.py Log: streamline gateway exported methods, move the internal helper methods to "_" names. Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 13:12:23 2007 @@ -7,7 +7,7 @@ streamline exported API ------------------------------------- -* move not-to-be-exported Gateway() methods to _ - methods. +* (hpk, should be done) move not-to-be-exported Gateway() methods to _ - methods. * docstrings for all exported API packaging Modified: py/dist/py/execnet/channel.py ============================================================================== --- py/dist/py/execnet/channel.py (original) +++ py/dist/py/execnet/channel.py Tue Jan 9 13:12:23 2007 @@ -69,7 +69,7 @@ def __del__(self): if self.gateway is None: # can be None in tests return - self.gateway.trace("Channel(%d).__del__" % self.id) + self.gateway._trace("Channel(%d).__del__" % self.id) # no multithreading issues here, because we have the last ref to 'self' if self._closed: # state transition "closed" --> "deleted" Modified: py/dist/py/execnet/gateway.py ============================================================================== --- py/dist/py/execnet/gateway.py (original) +++ py/dist/py/execnet/gateway.py Tue Jan 9 13:12:23 2007 @@ -50,7 +50,7 @@ sender = self.thread_sender) def __repr__(self): - addr = self.getremoteaddress() + addr = self._getremoteaddress() if addr: addr = '[%s]' % (addr,) else: @@ -63,13 +63,13 @@ return "<%s%s %s/%s (%d active channels)>" %( self.__class__.__name__, addr, r, s, i) - def getremoteaddress(self): + def _getremoteaddress(self): return None ## def _local_trystopexec(self): ## self._execpool.shutdown() - def trace(self, *args): + def _trace(self, *args): if debug: try: l = "\n".join(args).split(os.linesep) @@ -82,14 +82,14 @@ except: traceback.print_exc() - def traceex(self, excinfo): + def _traceex(self, excinfo): try: l = traceback.format_exception(*excinfo) errortext = "".join(l) except: errortext = '%s: %s' % (excinfo[0].__name__, excinfo[1]) - self.trace(errortext) + self._trace(errortext) def thread_receiver(self): """ thread to read and handle Messages half-sync-half-async. """ @@ -98,19 +98,19 @@ while 1: try: msg = Message.readfrom(self.io) - self.trace("received <- %r" % msg) + self._trace("received <- %r" % msg) msg.received(self) except sysex: raise except EOFError: break except: - self.traceex(exc_info()) + self._traceex(exc_info()) break finally: self._outgoing.put(None) self.channelfactory._finished_receiving() - self.trace('leaving %r' % threading.currentThread()) + self._trace('leaving %r' % threading.currentThread()) def thread_sender(self): """ thread to send Messages over the wire. """ @@ -125,15 +125,15 @@ msg.writeto(self.io) except: excinfo = exc_info() - self.traceex(excinfo) + self._traceex(excinfo) if msg is not None: msg.post_sent(self, excinfo) raise else: - self.trace('sent -> %r' % msg) + self._trace('sent -> %r' % msg) msg.post_sent(self) finally: - self.trace('leaving %r' % threading.currentThread()) + self._trace('leaving %r' % threading.currentThread()) def _local_redirect_thread_output(self, outid, errid): l = [] @@ -154,7 +154,7 @@ from sys import exc_info try: loc = { 'channel' : channel } - self.trace("execution starts:", repr(source)[:50]) + self._trace("execution starts:", repr(source)[:50]) close = self._local_redirect_thread_output(outid, errid) try: co = compile(source+'\n', '', 'exec', @@ -162,7 +162,7 @@ exec co in loc finally: close() - self.trace("execution finished:", repr(source)[:50]) + self._trace("execution finished:", repr(source)[:50]) except (KeyboardInterrupt, SystemExit): raise except: @@ -170,12 +170,12 @@ l = traceback.format_exception(*excinfo) errortext = "".join(l) channel.close(errortext) - self.trace(errortext) + self._trace(errortext) else: channel.close() def _local_schedulexec(self, channel, sourcetask): - self.trace("dispatching exec") + self._trace("dispatching exec") self._execpool.dispatch(self.thread_executor, channel, sourcetask) def _newredirectchannelid(self, callback): @@ -264,7 +264,7 @@ ## if not self.pool.getstarted('sender'): ## raise IOError("sender thread not alive anymore!") ## self._outgoing.put(None) -## self.trace("exit procedure triggered, pid %d " % (os.getpid(),)) +## self._trace("exit procedure triggered, pid %d " % (os.getpid(),)) ## _gateways.remove(self) ## finally: ## self._exitlock.release() @@ -280,12 +280,12 @@ current = threading.currentThread() for x in self.pool.getstarted(): if x != current: - self.trace("joining %s" % x) + self._trace("joining %s" % x) x.join() - self.trace("joining sender/reciver threads finished, current %r" % current) + self._trace("joining sender/reciver threads finished, current %r" % current) if joinexec: self._execpool.join() - self.trace("joining execution threads finished, current %r" % current) + self._trace("joining execution threads finished, current %r" % current) def getid(gw, cache={}): name = gw.__class__.__name__ Modified: py/dist/py/execnet/register.py ============================================================================== --- py/dist/py/execnet/register.py (original) +++ py/dist/py/execnet/register.py Tue Jan 9 13:12:23 2007 @@ -44,7 +44,7 @@ bootstrap += [getsource(x) for x in startup_modules] bootstrap += [io.server_stmt, "Gateway(io=io, startcount=2).join(joinexec=False)",] source = "\n".join(bootstrap) - self.trace("sending gateway bootstrap code") + self._trace("sending gateway bootstrap code") io.write('%r\n' % source) class PopenCmdGateway(InstallableGateway): @@ -62,12 +62,12 @@ ## self._pidchannel.waitclose(timeout=0.5) ## pid = self._pidchannel.receive() ## except IOError: -## self.trace("IOError: could not receive child PID:") -## self.traceex(sys.exc_info()) +## self._trace("IOError: could not receive child PID:") +## self._traceex(sys.exc_info()) ## pid = None ## super(PopenCmdGateway, self).exit() ## if pid is not None: -## self.trace("waiting for pid %s" % pid) +## self._trace("waiting for pid %s" % pid) ## try: ## os.waitpid(pid, 0) ## except KeyboardInterrupt: @@ -75,7 +75,7 @@ ## os.kill(pid, 15) ## raise ## except OSError, e: -## self.trace("child process %s already dead? error:%s" % +## self._trace("child process %s already dead? error:%s" % ## (pid, str(e))) class PopenGateway(PopenCmdGateway): @@ -137,7 +137,7 @@ io = inputoutput.SocketIO(sock) InstallableGateway.__init__(self, io=io) - def getremoteaddress(self): + def _getremoteaddress(self): return '%s:%d' % (self.host, self.port) def remote_install(cls, gateway, hostport=None): @@ -162,7 +162,7 @@ hostname, (realhost, realport) = channel.receive() if hostport is None: realhost = hostname - #gateway.trace("remote_install received" + #gateway._trace("remote_install received" # "port=%r, hostname = %r" %(realport, hostname)) return py.execnet.SocketGateway(realhost, realport) remote_install = classmethod(remote_install) @@ -181,7 +181,7 @@ cmdline.insert(0, cmd) super(SshGateway, self).__init__(' '.join(cmdline)) - def getremoteaddress(self): + def _getremoteaddress(self): return self.sshaddress class ExecGateway(PopenGateway): From fijal at codespeak.net Tue Jan 9 13:43:03 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 13:43:03 +0100 (CET) Subject: [py-svn] r36345 - py/dist/py/documentation Message-ID: <20070109124303.E58F110068@code0.codespeak.net> Author: fijal Date: Tue Jan 9 13:43:02 2007 New Revision: 36345 Modified: py/dist/py/documentation/apigen.txt Log: Add a link to source viewer. Modified: py/dist/py/documentation/apigen.txt ============================================================================== --- py/dist/py/documentation/apigen.txt (original) +++ py/dist/py/documentation/apigen.txt Tue Jan 9 13:43:02 2007 @@ -39,7 +39,7 @@ will be stored. An example:: >>> import py - >>> from py.__.apigen.tracer.docstorage import DocStorage + >>> from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor >>> from py.__.apigen.tracer.tracer import Tracer >>> toregister = {'py.path.local': py.path.local, ... 'py.path.svnwc': py.path.svnwc} @@ -83,7 +83,7 @@ >>> from py.__.apigen.rest.genrest import RestGen, DirectPaste, DirWriter >>> # create a temp dir in /tmp/pytest- >>> tempdir = py.test.ensuretemp('apigen_example') - >>> rg = RestGen(ds, DirectPaste(), DirWriter(tempdir)) + >>> rg = RestGen(DocStorageAccessor(ds), DirectPaste(), DirWriter(tempdir)) >>> rg.write() An example of a somewhat more 'real-life' use case, writing to a directory of @@ -93,7 +93,7 @@ >>> from py.__.apigen.rest.genrest import ViewVC, HTMLDirWriter >>> from py.__.apigen.rest.htmlhandlers import HTMLHandler >>> tempdir = py.test.ensuretemp('apigen_example_2') - >>> rg = RestGen(ds, ViewVC('http://some.host.com/viewvc/myproj/trunk/'), + >>> rg = RestGen(DocStorageAccessor(ds), ViewVC('http://some.host.com/viewvc/myproj/trunk/'), ... HTMLDirWriter(HTMLHandler, HTMLHandler, tempdir)) >>> rg.write() From hpk at codespeak.net Tue Jan 9 13:46:15 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 13:46:15 +0100 (CET) Subject: [py-svn] r36346 - py/dist/py/rest/testing Message-ID: <20070109124615.D9A1010069@code0.codespeak.net> Author: hpk Date: Tue Jan 9 13:46:14 2007 New Revision: 36346 Modified: py/dist/py/rest/testing/test_directive.py Log: don't use latex-processing and cleanup import check Modified: py/dist/py/rest/testing/test_directive.py ============================================================================== --- py/dist/py/rest/testing/test_directive.py (original) +++ py/dist/py/rest/testing/test_directive.py Tue Jan 9 13:46:14 2007 @@ -1,8 +1,10 @@ import py try: - from py.__.rest import directive + import docutils except ImportError: py.test.skip("docutils not present") + +from py.__.rest import directive from py.__.misc import rest from py.__.rest.latex import process_rest_file @@ -50,7 +52,6 @@ txt.write(""" :foo:`whatever` """) - process_rest_file(txt) html = txt.new(ext="html") rest.process(txt) assert html.check() From cfbolz at codespeak.net Tue Jan 9 13:56:11 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 13:56:11 +0100 (CET) Subject: [py-svn] r36347 - py/dist/py/misc Message-ID: <20070109125611.6770E10069@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 13:56:10 2007 New Revision: 36347 Modified: py/dist/py/misc/findmissingdocstrings.py Log: also search for different argument names in overwritten methods Modified: py/dist/py/misc/findmissingdocstrings.py ============================================================================== --- py/dist/py/misc/findmissingdocstrings.py (original) +++ py/dist/py/misc/findmissingdocstrings.py Tue Jan 9 13:56:10 2007 @@ -1,8 +1,10 @@ #!/usr/bin/env python import py +import inspect +import types -def report_if_missing(name, obj): +def report_strange_docstring(name, obj): if obj.__doc__ is None: print "%s misses a docstring" % (name, ) elif obj.__doc__ == "": @@ -10,19 +12,65 @@ elif "XXX" in obj.__doc__: print "%s has an 'XXX' in its docstring" % (name, ) -if __name__ == '__main__': +def find_code(method): + return getattr(getattr(method, "im_func", None), "func_code", None) + +def report_different_parameter_names(name, cls): + bases = cls.__mro__ + for base in bases: + for attr in dir(base): + meth1 = getattr(base, attr) + code1 = find_code(meth1) + if code1 is None: + continue + if not callable(meth1): + continue + if not hasattr(cls, attr): + continue + meth2 = getattr(cls, attr) + code2 = find_code(meth2) + if not callable(meth2): + continue + if code2 is None: + continue + args1 = inspect.getargs(code1)[0] + args2 = inspect.getargs(code2)[0] + for a1, a2 in zip(args1, args2): + if a1 != a2: + print "%s.%s have different argument names %s, %s than the version in %s" % (name, attr, a1, a2, base) + + +def find_all_exported(): stack = [(name, getattr(py, name)) for name in dir(py)[::-1] - if not name.startswith("__") and name != "compat"] + if not name.startswith("_") and name != "compat"] seen = {} + exported = [] while stack: name, obj = stack.pop() if id(obj) in seen: continue else: seen[id(obj)] = True - if callable(obj): - report_if_missing(name, obj) + exported.append((name, obj)) if isinstance(obj, type) or isinstance(obj, type(py)): stack.extend([("%s.%s" % (name, s), getattr(obj, s)) for s in dir(obj) if len(s) <= 1 or not (s[0] == '_' and s[1] != '_')]) + return exported + + +if __name__ == '__main__': + all_exported = find_all_exported() + print "strange docstrings" + print "==================" + print + for name, obj in all_exported: + if callable(obj): + report_strange_docstring(name, obj) + print "\n\ndifferent parameters" + print "====================" + print + for name, obj in all_exported: + if isinstance(obj, type): + report_different_parameter_names(name, obj) + From cfbolz at codespeak.net Tue Jan 9 13:57:36 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 13:57:36 +0100 (CET) Subject: [py-svn] r36348 - py/dist/py/documentation Message-ID: <20070109125736.7309D10069@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 13:57:33 2007 New Revision: 36348 Modified: py/dist/py/documentation/TODO.txt Log: apparently there are more missing docstrings Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 13:57:33 2007 @@ -82,23 +82,65 @@ Missing docstrings ------------------ -_thread.WorkerPool misses a docstring -_thread.WorkerPool.shutdown misses a docstring -_thread.WorkerPool.join misses a docstring -_thread.WorkerPool.__init__ misses a docstring -_thread.ThreadOut misses a docstring -_thread.ThreadOut.write misses a docstring -_thread.ThreadOut.setwritefunc misses a docstring -_thread.ThreadOut.setdefaultwriter misses a docstring -_thread.ThreadOut.resetdefault misses a docstring -_thread.NamedThreadPool misses a docstring code.Traceback misses a docstring +code.Traceback.recursionindex misses a docstring +code.Traceback.filter misses a docstring +code.Traceback.cut misses a docstring +code.Traceback.__init__ misses a docstring +code.Traceback.__getitem__ misses a docstring code.Traceback.Entry misses a docstring +code.Traceback.Entry.ishidden misses a docstring +code.Traceback.Entry.getfirstlinesource misses a docstring +code.Traceback.Entry.__str__ misses a docstring +code.Traceback.Entry.__repr__ misses a docstring +code.Traceback.Entry.__init__ misses a docstring code.Source.getblockend misses a docstring +code.Source.__str__ misses a docstring +code.Source.__len__ misses a docstring +code.Source.__init__ misses a docstring +code.Source.__getslice__ misses a docstring +code.Source.__getitem__ misses a docstring +code.Source.__eq__ misses a docstring +code.Frame.repr misses a docstring +code.Frame.is_true misses a docstring +code.Frame.getargs misses a docstring +code.Frame.exec_ misses a docstring +code.Frame.eval misses a docstring +code.Frame.__init__ misses a docstring +code.ExceptionInfo.exconly misses a docstring +code.ExceptionInfo.errisinstance misses a docstring +code.ExceptionInfo.__str__ misses a docstring +code.ExceptionInfo.__init__ misses a docstring code.Code misses a docstring +code.Code.source misses a docstring +code.Code.getargs misses a docstring +code.Code.__init__ misses a docstring +code.Code.__eq__ misses a docstring execnet.SshGateway misses a docstring +execnet.SshGateway.join misses a docstring +execnet.SshGateway.exit misses a docstring +execnet.SshGateway.__repr__ misses a docstring +execnet.SshGateway.__init__ misses a docstring +execnet.SshGateway.ThreadOut misses a docstring +execnet.SshGateway.ThreadOut.write misses a docstring +execnet.SshGateway.ThreadOut.setwritefunc misses a docstring +execnet.SshGateway.ThreadOut.setdefaultwriter misses a docstring +execnet.SshGateway.ThreadOut.resetdefault misses a docstring +execnet.SshGateway.ThreadOut.isatty misses a docstring +execnet.SshGateway.ThreadOut.flush misses a docstring +execnet.SshGateway.ThreadOut.delwritefunc misses a docstring +execnet.SshGateway.ThreadOut.deinstall misses a docstring execnet.SocketGateway misses a docstring +execnet.SocketGateway.join misses a docstring +execnet.SocketGateway.exit misses a docstring +execnet.SocketGateway.__repr__ misses a docstring +execnet.SocketGateway.__init__ misses a docstring execnet.PopenGateway misses a docstring +execnet.PopenGateway.remote_bootstrap_gateway misses a docstring +execnet.PopenGateway.join misses a docstring +execnet.PopenGateway.exit misses a docstring +execnet.PopenGateway.__repr__ misses a docstring +execnet.PopenGateway.__init__ misses a docstring initpkg misses a docstring io.dupfile misses a docstring log.setconsumer misses a docstring @@ -106,8 +148,20 @@ log.Syslog misses a docstring log.STDOUT misses a docstring log.STDERR misses a docstring +log.Producer.set_consumer misses a docstring +log.Producer.get_consumer misses a docstring +log.Producer.__repr__ misses a docstring +log.Producer.__init__ misses a docstring +log.Producer.__getattr__ misses a docstring +log.Producer.__call__ misses a docstring log.Producer.Message misses a docstring +log.Producer.Message.prefix misses a docstring +log.Producer.Message.content misses a docstring +log.Producer.Message.__str__ misses a docstring +log.Producer.Message.__init__ misses a docstring log.Path misses a docstring +log.Path.__init__ misses a docstring +log.Path.__call__ misses a docstring magic.greenlet misses a docstring magic.greenlet.getcurrent misses a docstring magic.greenlet.error misses a docstring @@ -144,45 +198,405 @@ path.svnwc.__iter__ misses a docstring path.svnwc.__hash__ misses a docstring path.svnwc.__eq__ misses a docstring +path.svnwc.__div__ misses a docstring +path.svnwc.__contains__ misses a docstring +path.svnwc.__cmp__ misses a docstring +path.svnwc.__add__ misses a docstring path.svnwc.Checkers misses a docstring path.svnurl misses a docstring +path.svnurl.visit misses a docstring +path.svnurl.rename misses a docstring +path.svnurl.remove misses a docstring +path.svnurl.readlines misses a docstring +path.svnurl.move misses a docstring +path.svnurl.mkdir misses a docstring +path.svnurl.log misses a docstring +path.svnurl.getpycodeobj misses a docstring +path.svnurl.copy misses a docstring +path.svnurl.check misses a docstring +path.svnurl.__repr__ misses a docstring path.svnurl.__new__ misses a docstring +path.svnurl.__ne__ misses a docstring +path.svnurl.__iter__ misses a docstring +path.svnurl.__hash__ misses a docstring +path.svnurl.__div__ misses a docstring +path.svnurl.__contains__ misses a docstring +path.svnurl.__cmp__ misses a docstring +path.svnurl.__add__ misses a docstring path.svnurl.Checkers misses a docstring path.local.visit misses a docstring path.local.sysexec has an 'XXX' in its docstring +path.local.rename misses a docstring +path.local.readlines misses a docstring +path.local.move misses a docstring +path.local.getpymodule misses a docstring +path.local.getpycodeobj misses a docstring +path.local.copy misses a docstring +path.local.check misses a docstring +path.local.__repr__ misses a docstring +path.local.__iter__ misses a docstring +path.local.__hash__ misses a docstring +path.local.__eq__ misses a docstring +path.local.__div__ misses a docstring +path.local.__contains__ misses a docstring +path.local.__cmp__ misses a docstring +path.local.__add__ misses a docstring path.local.Checkers misses a docstring +path.extpy.visit misses a docstring +path.extpy.samefile misses a docstring +path.extpy.relto misses a docstring +path.extpy.listobj misses a docstring +path.extpy.listdir misses a docstring +path.extpy.join misses a docstring +path.extpy.getpymodule misses a docstring +path.extpy.getfilelineno misses a docstring +path.extpy.dirpath misses a docstring +path.extpy.check misses a docstring +path.extpy.__str__ misses a docstring +path.extpy.__repr__ misses a docstring path.extpy.__new__ misses a docstring +path.extpy.__iter__ misses a docstring +path.extpy.__hash__ misses a docstring +path.extpy.__contains__ misses a docstring +path.extpy.__cmp__ misses a docstring +path.extpy.__add__ misses a docstring path.extpy.Checkers misses a docstring test.rest.RestReporter misses a docstring +test.rest.RestReporter.summary misses a docstring +test.rest.RestReporter.skips misses a docstring +test.rest.RestReporter.repr_traceback misses a docstring +test.rest.RestReporter.repr_source misses a docstring +test.rest.RestReporter.repr_signal misses a docstring +test.rest.RestReporter.repr_failure misses a docstring +test.rest.RestReporter.report_unknown misses a docstring +test.rest.RestReporter.report_TestStarted misses a docstring +test.rest.RestReporter.report_TestFinished misses a docstring +test.rest.RestReporter.report_SkippedTryiter misses a docstring +test.rest.RestReporter.report_SendItem misses a docstring +test.rest.RestReporter.report_RsyncFinished misses a docstring +test.rest.RestReporter.report_ReceivedItemOutcome misses a docstring +test.rest.RestReporter.report_Nodes misses a docstring +test.rest.RestReporter.report_ItemStart misses a docstring +test.rest.RestReporter.report_ImmediateFailure misses a docstring +test.rest.RestReporter.report_HostReady misses a docstring +test.rest.RestReporter.report_HostRSyncing misses a docstring +test.rest.RestReporter.report_FailedTryiter misses a docstring +test.rest.RestReporter.report misses a docstring +test.rest.RestReporter.print_summary misses a docstring +test.rest.RestReporter.prepare_source misses a docstring +test.rest.RestReporter.hangs misses a docstring +test.rest.RestReporter.get_rootpath misses a docstring +test.rest.RestReporter.get_path_from_item misses a docstring +test.rest.RestReporter.get_linkwriter misses a docstring +test.rest.RestReporter.get_item_name misses a docstring +test.rest.RestReporter.get_host misses a docstring +test.rest.RestReporter.failures misses a docstring +test.rest.RestReporter.add_rest misses a docstring +test.rest.RestReporter.__init__ misses a docstring test.rest.RelLinkWriter misses a docstring +test.rest.RelLinkWriter.get_link misses a docstring test.rest.NoLinkWriter misses a docstring +test.rest.NoLinkWriter.get_link misses a docstring test.rest.LinkWriter misses a docstring +test.rest.LinkWriter.get_link misses a docstring +test.rest.LinkWriter.__init__ misses a docstring test.exit misses a docstring test.deprecated_call misses a docstring test.collect.Module misses a docstring +test.collect.Module.tryiter has an 'XXX' in its docstring +test.collect.Module.teardown misses a docstring +test.collect.Module.startcapture misses a docstring +test.collect.Module.skipbykeyword misses a docstring +test.collect.Module.setup misses a docstring +test.collect.Module.run misses a docstring +test.collect.Module.makeitem misses a docstring +test.collect.Module.listnames misses a docstring +test.collect.Module.join misses a docstring +test.collect.Module.haskeyword misses a docstring +test.collect.Module.getsortvalue misses a docstring +test.collect.Module.getpathlineno misses a docstring +test.collect.Module.getouterr misses a docstring +test.collect.Module.getitembynames misses a docstring +test.collect.Module.funcnamefilter misses a docstring +test.collect.Module.finishcapture misses a docstring +test.collect.Module.classnamefilter misses a docstring +test.collect.Module.buildname2items misses a docstring +test.collect.Module.__repr__ misses a docstring +test.collect.Module.__ne__ misses a docstring +test.collect.Module.__init__ misses a docstring +test.collect.Module.__hash__ misses a docstring +test.collect.Module.__eq__ misses a docstring +test.collect.Module.__cmp__ misses a docstring test.collect.Module.Skipped misses a docstring test.collect.Module.Passed misses a docstring test.collect.Module.Outcome misses a docstring test.collect.Module.Failed misses a docstring test.collect.Module.ExceptionFailure misses a docstring test.collect.Instance misses a docstring +test.collect.Instance.tryiter has an 'XXX' in its docstring +test.collect.Instance.teardown misses a docstring +test.collect.Instance.startcapture misses a docstring +test.collect.Instance.skipbykeyword misses a docstring +test.collect.Instance.setup misses a docstring +test.collect.Instance.run misses a docstring +test.collect.Instance.makeitem misses a docstring +test.collect.Instance.listnames misses a docstring +test.collect.Instance.join misses a docstring +test.collect.Instance.haskeyword misses a docstring +test.collect.Instance.getsortvalue misses a docstring +test.collect.Instance.getpathlineno misses a docstring +test.collect.Instance.getouterr misses a docstring +test.collect.Instance.getitembynames misses a docstring +test.collect.Instance.funcnamefilter misses a docstring +test.collect.Instance.finishcapture misses a docstring +test.collect.Instance.classnamefilter misses a docstring +test.collect.Instance.buildname2items misses a docstring +test.collect.Instance.__repr__ misses a docstring +test.collect.Instance.__ne__ misses a docstring +test.collect.Instance.__init__ misses a docstring +test.collect.Instance.__hash__ misses a docstring +test.collect.Instance.__eq__ misses a docstring +test.collect.Instance.__cmp__ misses a docstring test.collect.Generator misses a docstring +test.collect.Generator.tryiter has an 'XXX' in its docstring +test.collect.Generator.teardown misses a docstring +test.collect.Generator.startcapture misses a docstring +test.collect.Generator.skipbykeyword misses a docstring +test.collect.Generator.setup misses a docstring +test.collect.Generator.run misses a docstring +test.collect.Generator.listnames misses a docstring +test.collect.Generator.join misses a docstring +test.collect.Generator.haskeyword misses a docstring +test.collect.Generator.getsortvalue misses a docstring +test.collect.Generator.getpathlineno misses a docstring +test.collect.Generator.getouterr misses a docstring +test.collect.Generator.getitembynames misses a docstring +test.collect.Generator.getcallargs misses a docstring +test.collect.Generator.finishcapture misses a docstring +test.collect.Generator.buildname2items misses a docstring +test.collect.Generator.__repr__ misses a docstring +test.collect.Generator.__ne__ misses a docstring +test.collect.Generator.__init__ misses a docstring +test.collect.Generator.__hash__ misses a docstring +test.collect.Generator.__eq__ misses a docstring +test.collect.Generator.__cmp__ misses a docstring test.collect.DoctestFile misses a docstring +test.collect.DoctestFile.tryiter has an 'XXX' in its docstring +test.collect.DoctestFile.teardown misses a docstring +test.collect.DoctestFile.startcapture misses a docstring +test.collect.DoctestFile.skipbykeyword misses a docstring +test.collect.DoctestFile.setup misses a docstring +test.collect.DoctestFile.run misses a docstring +test.collect.DoctestFile.makeitem misses a docstring +test.collect.DoctestFile.listnames misses a docstring +test.collect.DoctestFile.join misses a docstring +test.collect.DoctestFile.haskeyword misses a docstring +test.collect.DoctestFile.getsortvalue misses a docstring +test.collect.DoctestFile.getpathlineno misses a docstring +test.collect.DoctestFile.getouterr misses a docstring +test.collect.DoctestFile.getitembynames misses a docstring +test.collect.DoctestFile.funcnamefilter misses a docstring +test.collect.DoctestFile.finishcapture misses a docstring +test.collect.DoctestFile.classnamefilter misses a docstring +test.collect.DoctestFile.buildname2items misses a docstring +test.collect.DoctestFile.__repr__ misses a docstring +test.collect.DoctestFile.__ne__ misses a docstring +test.collect.DoctestFile.__init__ misses a docstring +test.collect.DoctestFile.__hash__ misses a docstring +test.collect.DoctestFile.__eq__ misses a docstring +test.collect.DoctestFile.__cmp__ misses a docstring test.collect.Directory misses a docstring +test.collect.Directory.tryiter has an 'XXX' in its docstring +test.collect.Directory.teardown misses a docstring +test.collect.Directory.startcapture misses a docstring +test.collect.Directory.skipbykeyword misses a docstring +test.collect.Directory.setup misses a docstring +test.collect.Directory.run misses a docstring +test.collect.Directory.recfilter misses a docstring +test.collect.Directory.makeitem misses a docstring +test.collect.Directory.listnames misses a docstring +test.collect.Directory.join misses a docstring +test.collect.Directory.haskeyword misses a docstring +test.collect.Directory.getsortvalue misses a docstring +test.collect.Directory.getpathlineno misses a docstring +test.collect.Directory.getouterr misses a docstring +test.collect.Directory.getitembynames misses a docstring +test.collect.Directory.finishcapture misses a docstring +test.collect.Directory.filefilter misses a docstring +test.collect.Directory.buildname2items misses a docstring +test.collect.Directory.__repr__ misses a docstring +test.collect.Directory.__ne__ misses a docstring +test.collect.Directory.__init__ misses a docstring +test.collect.Directory.__hash__ misses a docstring +test.collect.Directory.__eq__ misses a docstring +test.collect.Directory.__cmp__ misses a docstring test.collect.Collector misses a docstring +test.collect.Collector.tryiter has an 'XXX' in its docstring +test.collect.Collector.teardown misses a docstring +test.collect.Collector.startcapture misses a docstring +test.collect.Collector.skipbykeyword misses a docstring +test.collect.Collector.setup misses a docstring +test.collect.Collector.run misses a docstring +test.collect.Collector.listnames misses a docstring +test.collect.Collector.join misses a docstring +test.collect.Collector.haskeyword misses a docstring +test.collect.Collector.getsortvalue misses a docstring +test.collect.Collector.getpathlineno misses a docstring +test.collect.Collector.getouterr misses a docstring +test.collect.Collector.getitembynames misses a docstring +test.collect.Collector.finishcapture misses a docstring +test.collect.Collector.buildname2items misses a docstring +test.collect.Collector.__repr__ misses a docstring +test.collect.Collector.__ne__ misses a docstring +test.collect.Collector.__init__ misses a docstring +test.collect.Collector.__hash__ misses a docstring +test.collect.Collector.__eq__ misses a docstring +test.collect.Collector.__cmp__ misses a docstring test.collect.Class misses a docstring +test.collect.Class.tryiter has an 'XXX' in its docstring +test.collect.Class.teardown misses a docstring +test.collect.Class.startcapture misses a docstring +test.collect.Class.skipbykeyword misses a docstring +test.collect.Class.setup misses a docstring +test.collect.Class.run misses a docstring +test.collect.Class.makeitem misses a docstring +test.collect.Class.listnames misses a docstring +test.collect.Class.join misses a docstring +test.collect.Class.haskeyword misses a docstring +test.collect.Class.getsortvalue misses a docstring +test.collect.Class.getpathlineno misses a docstring +test.collect.Class.getouterr misses a docstring +test.collect.Class.getitembynames misses a docstring +test.collect.Class.funcnamefilter misses a docstring +test.collect.Class.finishcapture misses a docstring +test.collect.Class.classnamefilter misses a docstring +test.collect.Class.buildname2items misses a docstring +test.collect.Class.__repr__ misses a docstring +test.collect.Class.__ne__ misses a docstring +test.collect.Class.__init__ misses a docstring +test.collect.Class.__hash__ misses a docstring +test.collect.Class.__eq__ misses a docstring +test.collect.Class.__cmp__ misses a docstring test.cmdline.main misses a docstring test.TkinterSession misses a docstring test.TerminalSession misses a docstring +test.TerminalSession.summaryline misses a docstring +test.TerminalSession.startiteration misses a docstring +test.TerminalSession.start_Module misses a docstring +test.TerminalSession.start_Item misses a docstring +test.TerminalSession.start misses a docstring +test.TerminalSession.skippedreasons misses a docstring +test.TerminalSession.skipbykeyword misses a docstring +test.TerminalSession.shouldclose misses a docstring +test.TerminalSession.runtraced misses a docstring +test.TerminalSession.run misses a docstring +test.TerminalSession.repr_source misses a docstring +test.TerminalSession.repr_progress_short_result misses a docstring +test.TerminalSession.repr_progress_module_result misses a docstring +test.TerminalSession.repr_progress_long_result misses a docstring +test.TerminalSession.repr_out_err misses a docstring +test.TerminalSession.repr_locals misses a docstring +test.TerminalSession.repr_failure_tbshort misses a docstring +test.TerminalSession.repr_failure_tbno misses a docstring +test.TerminalSession.repr_failure_tblong misses a docstring +test.TerminalSession.repr_failure_info misses a docstring +test.TerminalSession.repr_failure_explanation misses a docstring +test.TerminalSession.repr_failure misses a docstring +test.TerminalSession.main misses a docstring +test.TerminalSession.header misses a docstring +test.TerminalSession.getlastvisible misses a docstring +test.TerminalSession.getitemoutcomepairs misses a docstring +test.TerminalSession.getentrysource misses a docstring +test.TerminalSession.footer misses a docstring +test.TerminalSession.finish misses a docstring +test.TerminalSession.failures misses a docstring +test.TerminalSession.__init__ misses a docstring +test.Session.startiteration misses a docstring +test.Session.start misses a docstring +test.Session.skipbykeyword misses a docstring +test.Session.shouldclose misses a docstring +test.Session.runtraced misses a docstring +test.Session.run misses a docstring +test.Session.getitemoutcomepairs misses a docstring +test.Session.finish misses a docstring +test.Session.__init__ misses a docstring test.RSession.reporterror misses a docstring +test.RSession.read_distributed_config misses a docstring test.RSession.make_colitems misses a docstring +test.RSession.kill_server misses a docstring +test.RSession.init_reporter misses a docstring test.RSession.getpkgdir misses a docstring +test.RSession.dispatch_tests misses a docstring +test.RSession.__init__ misses a docstring +test.LSession.write_docs misses a docstring +test.LSession.main misses a docstring +test.LSession.kill_server misses a docstring +test.LSession.init_runner misses a docstring +test.LSession.init_reporter misses a docstring +test.LSession.__init__ misses a docstring test.Item misses a docstring +test.Item.tryiter has an 'XXX' in its docstring +test.Item.teardown misses a docstring +test.Item.startcapture misses a docstring +test.Item.skipbykeyword misses a docstring +test.Item.setup misses a docstring +test.Item.run misses a docstring +test.Item.listnames misses a docstring +test.Item.join misses a docstring +test.Item.haskeyword misses a docstring +test.Item.getsortvalue misses a docstring +test.Item.getpathlineno misses a docstring +test.Item.getouterr misses a docstring +test.Item.getitembynames misses a docstring +test.Item.finishcapture misses a docstring +test.Item.buildname2items misses a docstring +test.Item.__repr__ misses a docstring +test.Item.__ne__ misses a docstring +test.Item.__init__ misses a docstring +test.Item.__hash__ misses a docstring +test.Item.__eq__ misses a docstring +test.Item.__cmp__ misses a docstring +test.Function.tryiter has an 'XXX' in its docstring +test.Function.teardown misses a docstring +test.Function.startcapture misses a docstring +test.Function.skipbykeyword misses a docstring +test.Function.setup misses a docstring +test.Function.run misses a docstring +test.Function.listnames misses a docstring +test.Function.join misses a docstring +test.Function.haskeyword misses a docstring +test.Function.getsortvalue misses a docstring +test.Function.getpathlineno misses a docstring +test.Function.getouterr misses a docstring +test.Function.getitembynames misses a docstring +test.Function.finishcapture misses a docstring +test.Function.buildname2items misses a docstring +test.Function.__repr__ misses a docstring +test.Function.__ne__ misses a docstring +test.Function.__init__ misses a docstring +test.Function.__hash__ misses a docstring +test.Function.__eq__ misses a docstring +test.Function.__cmp__ misses a docstring +test.Config.__init__ misses a docstring +xml.raw.__init__ misses a docstring xml.html misses a docstring xml.html.__tagclass__ misses a docstring +xml.html.__tagclass__.unicode misses a docstring +xml.html.__tagclass__.__unicode__ misses a docstring +xml.html.__tagclass__.__repr__ misses a docstring +xml.html.__tagclass__.__init__ misses a docstring xml.html.__tagclass__.Attr misses a docstring +xml.html.__tagclass__.Attr.__init__ misses a docstring xml.html.__metaclass__ misses a docstring +xml.html.__metaclass__.__getattr__ misses a docstring xml.html.Style misses a docstring +xml.html.Style.__init__ misses a docstring xml.escape misses a docstring xml.Tag misses a docstring +xml.Tag.unicode misses a docstring +xml.Tag.__unicode__ misses a docstring +xml.Tag.__repr__ misses a docstring +xml.Tag.__init__ misses a docstring xml.Namespace misses a docstring + + From hpk at codespeak.net Tue Jan 9 14:23:37 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 14:23:37 +0100 (CET) Subject: [py-svn] r36352 - in py/dist/py: test/testing thread Message-ID: <20070109132337.EE29510078@code0.codespeak.net> Author: hpk Date: Tue Jan 9 14:23:32 2007 New Revision: 36352 Modified: py/dist/py/test/testing/test_session.py py/dist/py/thread/io.py py/dist/py/thread/pool.py Log: adding docstrings to py/thread and avoiding timeout issues for test_loop_on_failing Modified: py/dist/py/test/testing/test_session.py ============================================================================== --- py/dist/py/test/testing/test_session.py (original) +++ py/dist/py/test/testing/test_session.py Tue Jan 9 14:23:32 2007 @@ -389,7 +389,7 @@ pool = py._thread.WorkerPool() reply = pool.dispatch(session.main, [str(o)]) while 1: - s = out.get(timeout=2.0) + s = out.get() if s.find('1 failed') != -1: break else: Modified: py/dist/py/thread/io.py ============================================================================== --- py/dist/py/thread/io.py (original) +++ py/dist/py/thread/io.py Tue Jan 9 14:23:32 2007 @@ -2,6 +2,11 @@ import thread class ThreadOut(object): + """ A file like object that diverts writing operations + to per-thread writefuncs. + This is a py lib internal class and not meant for outer use + or modification. + """ def __new__(cls, obj, attrname): """ Divert file output to per-thread writefuncs. the given obj and attrname describe the destination Modified: py/dist/py/thread/pool.py ============================================================================== --- py/dist/py/thread/pool.py (original) +++ py/dist/py/thread/pool.py Tue Jan 9 14:23:32 2007 @@ -6,6 +6,10 @@ ERRORMARKER = object() class Reply(object): + """ reply instances provide access to the result + of a function execution that got dispatched + through WorkerPool.dispatch() + """ _excinfo = None def __init__(self, task): self.task = task @@ -34,6 +38,11 @@ time.sleep(delay) #reduce CPU usage by using a sleep def get(self, timeout=None): + """ get the result object from an asynchronous function execution. + if the function execution raised an exception, + then calling get() will reraise that exception + including its traceback. + """ if self._queue is None: raise EOFError("reply has already been delivered") if timeout is not None: @@ -91,8 +100,22 @@ self._queue.put(SystemExit) class WorkerPool(object): + """ A WorkerPool allows to dispatch function executions + to threads. Each Worker Thread is reused for multiple + function executions. The dispatching operation + takes care to create and dispatch to existing + threads. + + You need to call shutdown() to signal + the WorkerThreads to terminate and join() + in order to wait until all worker threads + have terminated. + """ _shuttingdown = False def __init__(self, maxthreads=None): + """ init WorkerPool instance which may + create up to `maxthreads` worker threads. + """ self.maxthreads = maxthreads self._ready = {} self._alive = {} @@ -120,12 +143,16 @@ return thread def shutdown(self): + """ signal all worker threads to terminate. + call join() to wait until all threads termination. + """ if not self._shuttingdown: self._shuttingdown = True for t in self._alive.keys(): t.stop() def join(self, timeout=None): + """ wait until all worker threads have terminated. """ current = threading.currentThread() deadline = delta = None if timeout is not None: From hpk at codespeak.net Tue Jan 9 14:34:29 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 14:34:29 +0100 (CET) Subject: [py-svn] r36353 - py/dist/py/code Message-ID: <20070109133429.0A94310069@code0.codespeak.net> Author: hpk Date: Tue Jan 9 14:34:28 2007 New Revision: 36353 Modified: py/dist/py/code/traceback2.py Log: docstring for Traceback Modified: py/dist/py/code/traceback2.py ============================================================================== --- py/dist/py/code/traceback2.py (original) +++ py/dist/py/code/traceback2.py Tue Jan 9 14:34:28 2007 @@ -79,9 +79,12 @@ return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line) class Traceback(list): + """ Traceback objects encapsulate and offer higher level + access to Traceback entries. + """ Entry = TracebackEntry - def __init__(self, tb): + """ initialize from given python traceback object. """ if hasattr(tb, 'tb_next'): def f(cur): while cur is not None: From cfbolz at codespeak.net Tue Jan 9 14:41:27 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 14:41:27 +0100 (CET) Subject: [py-svn] r36355 - py/dist/py/c-extension/greenlet Message-ID: <20070109134127.7284310069@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 14:41:26 2007 New Revision: 36355 Modified: py/dist/py/c-extension/greenlet/greenlet.c Log: (cfbolz, arigo with a watchful eye on him) add docstrings to greenlets. Modified: py/dist/py/c-extension/greenlet/greenlet.c ============================================================================== --- py/dist/py/c-extension/greenlet/greenlet.c (original) +++ py/dist/py/c-extension/greenlet/greenlet.c Tue Jan 9 14:41:26 2007 @@ -845,7 +845,10 @@ 0, /* tp_setattro */ 0, /* tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* tp_doc */ + "greenlet(run=None, parent=None)\n\ +Create a new greenlet object (without running it). \"run\" is the\n\ +callable to invoke, and \"parent\" is the parent greenlet, which\n\ +defaults to the current greenlet.", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -876,8 +879,11 @@ } static PyMethodDef GreenMethods[] = { - {"getcurrent", (PyCFunction)mod_getcurrent, METH_NOARGS, /*XXX*/ NULL}, - {NULL, NULL} /* Sentinel */ + {"getcurrent", (PyCFunction)mod_getcurrent, METH_NOARGS, + "greenlet.getcurrent()\n\ +Returns the current greenlet (i.e. the one which called this\n\ +function)."}, + {NULL, NULL} /* Sentinel */ }; static char* copy_on_greentype[] = { @@ -887,6 +893,9 @@ void initgreenlet(void) { PyObject* m; + PyObject* greenletexit_doc; + PyObject* greenleterror_doc; + int error; char** p; _PyGreen_switchstack = g_switchstack; _PyGreen_slp_switch = slp_switch; @@ -902,10 +911,28 @@ PyExc_GreenletError = PyErr_NewException("greenlet.error", NULL, NULL); if (PyExc_GreenletError == NULL) return; + greenleterror_doc = PyString_FromString("internal greenlet error"); + if (greenleterror_doc == NULL) + return; + error = PyObject_SetAttrString( + PyExc_GreenletError, "__doc__", greenleterror_doc); + Py_DECREF(greenleterror_doc); + if (error == -1) + return; PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit", NULL, NULL); if (PyExc_GreenletExit == NULL) return; + greenletexit_doc = PyString_FromString("greenlet.GreenletExit\n\ +This special exception does not propagate to the parent greenlet; it\n\ +can be used to kill a single greenlet.\n"); + if (greenletexit_doc == NULL) + return; + error = PyObject_SetAttrString( + PyExc_GreenletExit, "__doc__", greenletexit_doc); + Py_DECREF(greenletexit_doc); + if (error == -1) + return; ts_current = green_create_main(); if (ts_current == NULL) @@ -918,7 +945,7 @@ Py_INCREF(PyExc_GreenletExit); PyModule_AddObject(m, "GreenletExit", PyExc_GreenletExit); - /* also publish module-level data as attributes of the greentype. */ + /* also publish module-level data as attributes of the greentype. */ for (p=copy_on_greentype; *p; p++) { PyObject* o = PyObject_GetAttrString(m, *p); if (!o) continue; From cfbolz at codespeak.net Tue Jan 9 15:56:26 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 15:56:26 +0100 (CET) Subject: [py-svn] r36359 - in py/dist/py/path: . local svn Message-ID: <20070109145626.ED75910071@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 15:56:24 2007 New Revision: 36359 Modified: py/dist/py/path/common.py py/dist/py/path/local/local.py py/dist/py/path/svn/urlcommand.py Log: add some doc strings to paths Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Tue Jan 9 15:56:24 2007 @@ -316,6 +316,8 @@ f.close() def readlines(self, cr=1): + """ read and return a list of lines from the path. if cr is False, the +newline will be removed from the end of each line. """ if not cr: content = self.read('rU') return content.split('\n') @@ -336,6 +338,7 @@ f.close() def move(self, target): + """ move this path to target. """ if target.relto(self): raise py.error.EINVAL(target, "cannot move path into a subdirectory of itself") try: Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Tue Jan 9 15:56:24 2007 @@ -246,6 +246,7 @@ newx.ensure(dir=1) def rename(self, target): + """ rename this path to target. """ return self._callex(os.rename, str(self), str(target)) def dump(self, obj, bin=1): Modified: py/dist/py/path/svn/urlcommand.py ============================================================================== --- py/dist/py/path/svn/urlcommand.py (original) +++ py/dist/py/path/svn/urlcommand.py Tue Jan 9 15:56:24 2007 @@ -83,7 +83,7 @@ 'svn cat -r %s "%s"' % (self.rev, self._escape(self.strpath))) def dirpath(self, *args, **kwargs): - """ return the directory Path of the current Path joined + """ return the directory path of the current path joined with any given path arguments. """ l = self.strpath.split(self.sep) @@ -96,6 +96,8 @@ # modifying methods (cache must be invalidated) def mkdir(self, *args, **kwargs): + """ create & return the directory joined with args. You can provide +a checkin message by giving a keyword argument 'msg'""" commit_msg=kwargs.get('msg', "mkdir by py lib invocation") createpath = self.join(*args) createpath._svnwrite('mkdir', '-m', commit_msg) @@ -103,6 +105,7 @@ return createpath def copy(self, target, msg='copied by py lib invocation'): + """ copy path to target with checkin msg msg.""" if getattr(target, 'rev', None) is not None: raise py.error.EINVAL(target, "revisions are immutable") process.cmdexec('svn copy -m "%s" "%s" "%s"' %(msg, @@ -110,6 +113,7 @@ self._lsnorevcache.delentry(target.dirpath().strpath) def rename(self, target, msg="renamed by py lib invocation"): + """ rename this path to target with checkin message msg. """ if getattr(self, 'rev', None) is not None: raise py.error.EINVAL(self, "revisions are immutable") py.process.cmdexec('svn move -m "%s" --force "%s" "%s"' %( @@ -118,6 +122,8 @@ self._lsnorevcache.delentry(self.strpath) def remove(self, rec=1, msg='removed by py lib invocation'): + """ remove a file or directory (or a directory tree if rec=1) with +checkin message msg.""" if self.rev is not None: raise py.error.EINVAL(self, "revisions are immutable") process.cmdexec('svn rm -m "%s" "%s"' %(msg, self._escape(self))) From fijal at codespeak.net Tue Jan 9 16:02:53 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 16:02:53 +0100 (CET) Subject: [py-svn] r36361 - py/dist/py/apigen/tracer/testing Message-ID: <20070109150253.93DC91007D@code0.codespeak.net> Author: fijal Date: Tue Jan 9 16:02:52 2007 New Revision: 36361 Modified: py/dist/py/apigen/tracer/testing/test_docgen.py Log: Turn off testing of permastore. Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Tue Jan 9 16:02:52 2007 @@ -20,6 +20,7 @@ # data_path = py.path.local(mod.__file__).dirpath().join("data") # sys.path.insert(0, str(data_path)) +# XXX: Perma doc storage disabled a bit def fun(a, b, c): "Some docstring" @@ -51,12 +52,13 @@ assert cs[1][0].lineno == num + 4 or cs[1][0].lineno == num + 5 assert cs[0][0].filename == f_name assert cs[0][0].lineno == num + 5 or cs[0][0].lineno == num + 4 - pds = PermaDocStorage(DocStorageAccessor(ds)) - assert pds.get_function_names() == ['fun'] - sig = pds.get_function_signature('fun') - assert sig[0][0][0] == 'a' - assert isinstance(sig[0][0][1], model.SomeUnion) - assert len(pds.get_function_callpoints('fun')) == 2 + if 0: + pds = PermaDocStorage(DocStorageAccessor(ds)) + assert pds.get_function_names() == ['fun'] + sig = pds.get_function_signature('fun') + assert sig[0][0][0] == 'a' + assert isinstance(sig[0][0][1], model.SomeUnion) + assert len(pds.get_function_callpoints('fun')) == 2 class AClass(object): """ Class docstring @@ -113,9 +115,10 @@ assert isinstance(inputcells[2], model.SomeFloat) assert isinstance(inputcells[3], model.SomeList) assert isinstance(desc.fields['exposed_method'].retval, model.SomeString) - pds = PermaDocStorage(DocStorageAccessor(ds)) - assert pds.get_class_names() == ['AClass'] - assert len(pds.get_function_signature('AClass.exposed_method')[0]) == 4 + if 0: + pds = PermaDocStorage(DocStorageAccessor(ds)) + assert pds.get_class_names() == ['AClass'] + assert len(pds.get_function_signature('AClass.exposed_method')[0]) == 4 def other_fun(): pass @@ -142,8 +145,9 @@ desc = ds.descs["other_fun"] assert len(desc.call_sites.keys()) == 1 #assert isinstance(desc.call_sites.values()[0][0], py.code.Frame) - pds = PermaDocStorage(DocStorageAccessor(ds)) - assert len(pds.get_function_callpoints("other_fun")) == 1 + if 0: + pds = PermaDocStorage(DocStorageAccessor(ds)) + assert len(pds.get_function_callpoints("other_fun")) == 1 class A(object): def method(self, x): @@ -166,7 +170,8 @@ model.SomeInt) assert isinstance(ds.descs['B'].fields['method'].inputcells[1], model.SomeInt) - pds = PermaDocStorage(DocStorageAccessor(ds)) + if 0: + pds = PermaDocStorage(DocStorageAccessor(ds)) def test_local_changes(): class testclass(object): From fijal at codespeak.net Tue Jan 9 16:03:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 16:03:11 +0100 (CET) Subject: [py-svn] r36362 - in py/dist/py: . test test/rsession Message-ID: <20070109150311.A2C511008B@code0.codespeak.net> Author: fijal Date: Tue Jan 9 16:03:09 2007 New Revision: 36362 Modified: py/dist/py/conftest.py py/dist/py/test/defaultconftest.py py/dist/py/test/rsession/rsession.py Log: Moved --apigen into --apigen=script, moving stuff away from a conftest Modified: py/dist/py/conftest.py ============================================================================== --- py/dist/py/conftest.py (original) +++ py/dist/py/conftest.py Tue Jan 9 16:03:09 2007 @@ -25,33 +25,3 @@ help=("target to run tests requiring ssh, e.g. " "user at codespeak.net")), ) - -class ApiGen: - def get_doc_storage(): - from py.__.apigen.tracer.docstorage import DocStorage - return DocStorage().from_pkg(py) - get_doc_storage = staticmethod(get_doc_storage) - - def write_docs(ds): - #from py.__.apigen.rest.genrest import DirectPaste, RestGen, \ - # HTMLDirWriter, SourceView - #from py.__.apigen.rest.htmlhandlers import IndexHandler, PageHandler - #outdir = py.path.local('/tmp/output') - #RestGen(ds, SourceView("http://codespeak.net/source"), - # HTMLDirWriter(IndexHandler, PageHandler, outdir)).write() - #if not outdir.join('style.css').check(): - # py.magic.autopath().dirpath().join('apigen/style.css').copy(outdir) - #if not outdir.join('apigen.js').check(): - # py.magic.autopath().dirpath().join('apigen/apigen.js').copy(outdir) - from py.__.apigen.rest.genrest import DirectPaste, RestGen, \ - HTMLDirWriter, SourceView - from py.__.apigen.rest.htmlhandlers import IndexHandler, PageHandler - from py.__.apigen.tracer.permastore import PermaDocStorage - from py.__.apigen.tracer.docstorage import DocStorageAccessor - ps = PermaDocStorage(DocStorageAccessor(ds)) - try: - from cPickle import dumps - except ImportError: - from pickle import dumps - open("/tmp/output.pickle", "w").write(dumps(ps)) - write_docs = staticmethod(write_docs) Modified: py/dist/py/test/defaultconftest.py ============================================================================== --- py/dist/py/test/defaultconftest.py (original) +++ py/dist/py/test/defaultconftest.py Tue Jan 9 16:03:09 2007 @@ -51,9 +51,9 @@ action="store_true", dest="traceconfig", default=False, help="trace considerations of conftest.py files."), Option('', '--apigen', - action="store_true", dest="apigen", default=False, - help="generate api documentation while testing (requires " - "initpkg/conftest config)."), + action="store", dest="apigen", + help="generate api documentation while testing (requires" + "argument pointing to a script)."), ) py.test.Config.addoptions('test-session related options', Option('', '--tkinter', Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Tue Jan 9 16:03:09 2007 @@ -288,24 +288,28 @@ def write_docs(self): if self.config.option.apigen: + from py.__.apigen.tracer.docstorage import DocStorageAccessor + apigen = py.path.local(self.config.option.apigen).pyimport() + pkgpath = py.path.local(py.__file__).dirpath() try: - apigen = self.config.getinitialvalue('ApiGen') - except ValueError: - raise NotImplementedError("Cannot create docs - didn't " - "provided way of doing that in conftest") - else: - apigen.write_docs(self.docstorage) - + apigen.build(pkgpath, DocStorageAccessor(self.docstorage)) + except (ValueError, AttributeError): + raise NotImplementedError("Provided script does not seem " + "to contain build function") def init_runner(self): if self.config.option.apigen: - from py.__.apigen.tracer.tracer import Tracer + from py.__.apigen.tracer.tracer import Tracer, DocStorage module = py try: - self.docstorage = self.config.getinitialvalue('ApiGen').get_doc_storage() + apigen = py.path.local(self.config.option.apigen).pyimport() + self.docstorage = DocStorage().from_dict( + apigen.get_documentable_items()) + except ImportError: + raise ImportError("Provided script cannot be imported") except (ValueError, AttributeError): - raise NotImplementedError("Need to provide conftest " - "way of generating DocStorage") + raise NotImplementedError("Provided script does not seem " + "to contain get_documentable_items") self.tracer = Tracer(self.docstorage) return apigen_runner else: From fijal at codespeak.net Tue Jan 9 16:05:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 16:05:11 +0100 (CET) Subject: [py-svn] r36363 - py/dist/py/apigen/rest Message-ID: <20070109150511.E70CE1007D@code0.codespeak.net> Author: fijal Date: Tue Jan 9 16:05:11 2007 New Revision: 36363 Modified: py/dist/py/apigen/rest/genrest.py Log: Added removed stuff. Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Tue Jan 9 16:05:11 2007 @@ -285,10 +285,9 @@ rest = [Title('class: %s' % (cls,), belowchar='='), LiteralBlock(self.dsa.get_doc(cls))] # link to source - # XXX kill get_obj - #link_to_class = self.linkgen.getlinkobj(cls, self.dsa.get_obj(cls)) - #if link_to_class: - # rest.append(Paragraph(Text("source: "), Link(*link_to_class))) + link_to_class = self.linkgen.getlinkobj(cls, self.dsa.get_obj(cls)) + if link_to_class: + rest.append(Paragraph(Text("source: "), Link(*link_to_class))) if bases: rest.append(Title('base classes:', belowchar='^')), @@ -399,10 +398,9 @@ lst = [title, LiteralBlock(self.dsa.get_doc(functionname)), LiteralBlock(self.dsa.get_function_definition(functionname))] - # XXX: investigate how to do it without getobj - #link_to_function = self.linkgen.getlinkobj(functionname, self.dsa.get_obj(functionname)) - #if link_to_function: - # lst.insert(1, Paragraph(Text("source: "), Link(*link_to_function))) + link_to_function = self.linkgen.getlinkobj(functionname, self.dsa.get_obj(functionname)) + if link_to_function: + lst.insert(1, Paragraph(Text("source: "), Link(*link_to_function))) opar = Paragraph(Strong('origin'), ":") if origin: From fijal at codespeak.net Tue Jan 9 16:06:55 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 16:06:55 +0100 (CET) Subject: [py-svn] r36364 - py/dist/py/apigen/rest/testing Message-ID: <20070109150655.B9C531007D@code0.codespeak.net> Author: fijal Date: Tue Jan 9 16:06:54 2007 New Revision: 36364 Modified: py/dist/py/apigen/rest/testing/test_rest.py Log: Fix tests. Modified: py/dist/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/dist/py/apigen/rest/testing/test_rest.py (original) +++ py/dist/py/apigen/rest/testing/test_rest.py Tue Jan 9 16:06:54 2007 @@ -211,13 +211,14 @@ # now we check out... self.check_rest(tempdir) tempdir = temppath.ensure("simple_api_ps", dir=True) - ps = PermaDocStorage(ds) - r = RestGen(ps, lg, DirWriter(tempdir)) - r.write() - basenames = [p.basename for p in tempdir.listdir('*.txt')] - assert sorted(basenames) == expected - self.check_rest(tempdir) - pickle.dumps(ps) + if 0: + ps = PermaDocStorage(ds) + r = RestGen(ps, lg, DirWriter(tempdir)) + r.write() + basenames = [p.basename for p in tempdir.listdir('*.txt')] + assert sorted(basenames) == expected + self.check_rest(tempdir) + pickle.dumps(ps) def test_generation_modules(self): ds = self.get_filled_docstorage_modules() @@ -267,13 +268,14 @@ assert data.find('.. _`fun`: #function-fun\n') > -1 assert data.find('.. _`fun`: function_fun.html') == -1 tempfile = temppath.ensure("internal_links_ps.txt", file=True) - ps = PermaDocStorage(ds) - r = RestGen(ps, lg, FileWriter(tempfile)) - r.write() - data = _nl(tempfile.read()) - assert data.find('.. _`fun`: #function-fun\n') > -1 - assert data.find('.. _`fun`: function_fun.html') == -1 - pickle.dumps(ps) + if 0: + ps = PermaDocStorage(ds) + r = RestGen(ps, lg, FileWriter(tempfile)) + r.write() + data = _nl(tempfile.read()) + assert data.find('.. _`fun`: #function-fun\n') > -1 + assert data.find('.. _`fun`: function_fun.html') == -1 + pickle.dumps(ps) def test_check_section_order(self): # we use the previous method's data @@ -459,7 +461,6 @@ '.. _`/py/apigen/rest/testing/test\_rest.py\:A.method`: http://localhost:8000/py/apigen/rest/testing/test_rest.py#A.method') != -1 def test_sourceview_fun(self): - py.test.skip("killed for a while") def f(): pass From hpk at codespeak.net Tue Jan 9 16:20:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 16:20:46 +0100 (CET) Subject: [py-svn] r36366 - in py/dist/py/documentation: . future Message-ID: <20070109152046.3C14410082@code0.codespeak.net> Author: hpk Date: Tue Jan 9 16:20:44 2007 New Revision: 36366 Modified: py/dist/py/documentation/TODO.txt py/dist/py/documentation/future/planning.txt Log: some reorganisation and new tasks Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 16:20:44 2007 @@ -1,26 +1,61 @@ Things to do before 0.9.0 ========================= + +py.compat +------------- + +* (cfbolz) needs py/documentation/compat.txt + +* (cfbolz) reimport/check python 2.4.4 modules + review all py lib documentation ------------------------------------- +* rename py/documentation to py/doc + (check web page and pypy usage of it) + streamline exported API ------------------------------------- * (hpk, should be done) move not-to-be-exported Gateway() methods to _ - methods. * docstrings for all exported API +* remove: + test.compat.TestCAse + +* remove from public namespace: + py.test.Session / TerminalSession / RSession / LSession / TkinterSession + py.test.rest + py.path.checker + XXX consider py.magic. invoke/revoke/patch/revert + +* make "_" namespace: + py.path.extpy -> py.path._extpy + py.log -> py._log (pypy!) + +* review py.io and write py.io.dupfile docstring + packaging ------------------------------------- +* debian package for py lib +* + APIGEN / source viewer ------------------------------------- -* deploying all that is neccessary on codespeak.net - -* (cfbolz, guido) writing the ReST directive, make it available - with eg py.__.misc.docutils.install_doclink(NAME, urlbase ...) - in a tested way. call the install from e.g. py/documentation/conftest.py +* make py.test --apigen=PATH_TO_SCRIPT + collect tracing information and call the apigen + script to produce api and source code documentation + +* deploy the above "py.test --apigen" run on codespeak + regularly, determine good locations and a good + directory and URL scheme . + +* integrate rest directive into py/documentation/conftest.py + with help code from py.__.rest.directive.... + make sure that py/documentation/ *.txt files testing ----------- @@ -46,9 +81,10 @@ * move RSync class to py.execnet.RSync, document its usage and features in the class and method docstrings +code quality +----------------- -other -------- +* no function implementation longer than 30 lines * no lines longer than 80 characters @@ -71,11 +107,6 @@ * document py.test's conftest.py approach (somewhat done) -* put Armin's collect class into py.__builtin__ (not done) - -* try get rid of Collect.tryiter() in favour of (not done) - using Armin's collect class - * hide py.test.TerminalSession and TkinterSession? (questionable) Modified: py/dist/py/documentation/future/planning.txt ============================================================================== --- py/dist/py/documentation/future/planning.txt (original) +++ py/dist/py/documentation/future/planning.txt Tue Jan 9 16:20:44 2007 @@ -59,4 +59,8 @@ * sets.py, subprocess.py in compat * fix -k option to py.test * add --report=(text|terminal|session|rest|tkinter|rest) to py.test + * put Armin's collect class into py.__builtin__ (not done) + * try get rid of Collect.tryiter() in favour of (not done) + using Armin's collect class + From hpk at codespeak.net Tue Jan 9 16:24:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 16:24:11 +0100 (CET) Subject: [py-svn] r36367 - py/dist/py/documentation Message-ID: <20070109152411.11FF310083@code0.codespeak.net> Author: hpk Date: Tue Jan 9 16:24:08 2007 New Revision: 36367 Modified: py/dist/py/documentation/TODO.txt Log: (cfbolz, holger) add py/bin related tasks for 0.9 Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 16:24:08 2007 @@ -1,6 +1,15 @@ Things to do before 0.9.0 ========================= +py/bin +----------- + +* document and review py/bin scripts abit + py.test + py.rest + py.lookup + py.cleanup + py.countloc py.compat ------------- From hpk at codespeak.net Tue Jan 9 16:26:06 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 16:26:06 +0100 (CET) Subject: [py-svn] r36368 - py/dist/py/bin Message-ID: <20070109152606.2418010083@code0.codespeak.net> Author: hpk Date: Tue Jan 9 16:26:03 2007 New Revision: 36368 Removed: py/dist/py/bin/rst2pdf.py Log: remove the old script that cf already integrated into bin/py.rest Deleted: /py/dist/py/bin/rst2pdf.py ============================================================================== --- /py/dist/py/bin/rst2pdf.py Tue Jan 9 16:26:03 2007 +++ (empty file) @@ -1,42 +0,0 @@ -#!/usr/bin/env python -docstring = """ -invoke - - %s filename1.txt - -to generate a pdf file from restructered text. - -http://docutils.sourceforge.net/docs/user/rst/quickref.html - -""" % __file__ - -from _findpy import py - -from py.__.rest import directive -from py.__.rest.latex import process_rest_file, process_configfile - -optparse = py.compat.optparse - -parser = optparse.OptionParser(usage=docstring) -parser.add_option("-c", "--config", dest="configfile", - help="use config file") -parser.add_option("--stylesheet", dest="stylesheet", default=None, - help="use 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__': - print "This file is deprecated. Please use py.rest --topdf instead" - directive.BackendStore("latex") - (options, args) = parser.parse_args() - if options.configfile is not None: - configfile = py.path.local(options.configfile) - process_configfile(options.configfile, options.debug) - elif len(args) != 1: - parser.error("please supply a file name") - else: - f = py.path.local(args[0]) - process_rest_file(args[0], options.stylesheet, options.debug) - - From cfbolz at codespeak.net Tue Jan 9 16:41:13 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 16:41:13 +0100 (CET) Subject: [py-svn] r36371 - py/dist/py/documentation Message-ID: <20070109154113.D7E681007D@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 16:41:12 2007 New Revision: 36371 Modified: py/dist/py/documentation/TODO.txt Log: some docstrings are there now. View should not be exported according to Armin. reconsider the read/write problem. Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 16:41:12 2007 @@ -37,6 +37,7 @@ py.test.Session / TerminalSession / RSession / LSession / TkinterSession py.test.rest py.path.checker + View XXX consider py.magic. invoke/revoke/patch/revert * make "_" namespace: @@ -45,6 +46,9 @@ * review py.io and write py.io.dupfile docstring +* re-consider what to do with read and write methods of py.path classes (since + there are places that check for file-ness by doing hasattr(... 'write')) + packaging ------------------------------------- @@ -202,10 +206,6 @@ log.Path misses a docstring log.Path.__init__ misses a docstring log.Path.__call__ misses a docstring -magic.greenlet misses a docstring -magic.greenlet.getcurrent misses a docstring -magic.greenlet.error misses a docstring -magic.greenlet.GreenletExit misses a docstring magic.View.__viewkey__ misses a docstring magic.View.__repr__ misses a docstring magic.View.__new__ misses a docstring @@ -216,13 +216,11 @@ path.svnwc.visit misses a docstring path.svnwc.revert misses a docstring path.svnwc.rename misses a docstring -path.svnwc.readlines misses a docstring path.svnwc.propset misses a docstring path.svnwc.proplist misses a docstring path.svnwc.propget misses a docstring path.svnwc.propdel misses a docstring path.svnwc.new misses a docstring -path.svnwc.move misses a docstring path.svnwc.mkdir misses a docstring path.svnwc.log misses a docstring path.svnwc.getpycodeobj misses a docstring @@ -245,14 +243,8 @@ path.svnwc.Checkers misses a docstring path.svnurl misses a docstring path.svnurl.visit misses a docstring -path.svnurl.rename misses a docstring -path.svnurl.remove misses a docstring -path.svnurl.readlines misses a docstring -path.svnurl.move misses a docstring -path.svnurl.mkdir misses a docstring path.svnurl.log misses a docstring path.svnurl.getpycodeobj misses a docstring -path.svnurl.copy misses a docstring path.svnurl.check misses a docstring path.svnurl.__repr__ misses a docstring path.svnurl.__new__ misses a docstring @@ -266,9 +258,6 @@ path.svnurl.Checkers misses a docstring path.local.visit misses a docstring path.local.sysexec has an 'XXX' in its docstring -path.local.rename misses a docstring -path.local.readlines misses a docstring -path.local.move misses a docstring path.local.getpymodule misses a docstring path.local.getpycodeobj misses a docstring path.local.copy misses a docstring @@ -639,4 +628,3 @@ xml.Tag.__init__ misses a docstring xml.Namespace misses a docstring - From fijal at codespeak.net Tue Jan 9 17:11:41 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 9 Jan 2007 17:11:41 +0100 (CET) Subject: [py-svn] r36373 - py/dist/py/documentation Message-ID: <20070109161141.869071007D@code0.codespeak.net> Author: fijal Date: Tue Jan 9 17:11:39 2007 New Revision: 36373 Modified: py/dist/py/documentation/apigen.txt Log: written down a bit of refined interface. Modified: py/dist/py/documentation/apigen.txt ============================================================================== --- py/dist/py/documentation/apigen.txt (original) +++ py/dist/py/documentation/apigen.txt Tue Jan 9 17:11:39 2007 @@ -9,11 +9,11 @@ compile time. This way it is capable of displaying information about the code base after initialization. A drawback is that you cannot easily document source code that automatically -starts server processes upon getting imported. +starts server processes or has some other irreversible effects upon getting imported. The apigen functionality can either be used from code, or from py.test, in the latter case it will gather information about -modules and names exported explicitely via the `initpkg`_ mechanism. +modules, classes to export explicitely by using provided script. Please note that apigen is currently geared towards documenting the py library itself, making it nicely work for other projects @@ -22,8 +22,6 @@ Using from code ---------------- -XXX how to use it without any Tracer? - The library provides a simple API to generate a py.rest.rst tree (which represents a ReStructuredText document), along with some helper classes to control the output. The most important objects are the Tracer, which traces @@ -100,20 +98,18 @@ Using from py.test ------------------- -XXX: fijal, please help me here a bit ;) - Running unit tests forms an ideal opportunity for apigen to find out about what happens when code is executed (assuming you have proper test coverage ;). There -are hooks built into py.test that allow you to do that: just write a simple -class called 'ApiGen' in your conftest (see the `py.test documentation`_ for -more details about conftest files) that implements a set of special methods -called 'get_doc_storage()' and 'write_docs()', that essentially implement the -functionality discussed above. Calling py.test with an --apigen -argument will cause the methods to be called. - -For an example, see the 'conftest.py' file in the py lib itself. To see it in -action you run `py.test --apigen` in the root of the py lib; this -will result in documentation (in HTML) being written to /tmp/output. +are hooks built into py.test that allow you to do that: + +* Write down a python script which contains at least two functions + - `get_documentable_items() -> {}` - function which will return dictionary + of name to object of exported items + - `build(pkgpath, docstorageaccessor)` - function which will be invoked afterwards + with DocStorageAccessor instance as an argument (you should read DocStorageAccessor + interface to know how you can access it) + +XXX: Write down some example usage after guido implement the script Comparison with other documentation generation tools ---------------------------------------------------- From cfbolz at codespeak.net Tue Jan 9 17:18:36 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 17:18:36 +0100 (CET) Subject: [py-svn] r36375 - in py/dist/py/path: . local svn Message-ID: <20070109161836.6C2041007F@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 17:18:34 2007 New Revision: 36375 Modified: py/dist/py/path/common.py py/dist/py/path/local/local.py py/dist/py/path/svn/svncommon.py py/dist/py/path/svn/urlcommand.py py/dist/py/path/svn/wccommand.py Log: clean up doc strings some more Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Tue Jan 9 17:18:34 2007 @@ -296,10 +296,12 @@ return self.new(basename='').join(*args, **kwargs) def ext(self): + """ extension of the path (including the '.').""" return self._getbyspec('ext')[0] ext = property(ext, None, None, 'extension part of path') def purebasename(self): + """ pure base name of the path.""" return self._getbyspec('purebasename')[0] purebasename = property(purebasename, None, None, 'basename without extension') @@ -368,6 +370,7 @@ return mod def getpycodeobj(self): + """ read the path and compile it to a py.code.Code object. """ s = self.read('rU') # XXX str(self) should show up somewhere in the code's filename return py.code.compile(s) Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Tue Jan 9 17:18:34 2007 @@ -225,6 +225,7 @@ return self.stat().mtime def copy(self, target, archive=False): + """ copy path to target.""" assert not archive, "XXX archive-mode not supported" if self.check(file=1): if target.check(dir=1): @@ -420,6 +421,7 @@ return mod def getpymodule(self): + """resolve this path to a module python object. """ if self.ext != '.c': return super(LocalPath, self).getpymodule() from py.__.misc.buildcmodule import make_module_from_c @@ -427,6 +429,7 @@ return mod def getpycodeobj(self): + """ read the path and compile it to a code object. """ dotpy = self.check(ext='.py') if dotpy: my_magic = py.std.imp.get_magic() Modified: py/dist/py/path/svn/svncommon.py ============================================================================== --- py/dist/py/path/svn/svncommon.py (original) +++ py/dist/py/path/svn/svncommon.py Tue Jan 9 17:18:34 2007 @@ -151,12 +151,9 @@ content = self._proplist() return content - # XXX unify argument naming with LocalPath.listdir def listdir(self, fil=None, sort=None): - """ return a sequence of Paths. - - listdir will return either a tuple or a list of paths - depending on implementation choices. + """ list directory contents, possibly filter by the given fil func + and possibly sorted. """ if isinstance(fil, str): fil = common.fnmatch(fil) Modified: py/dist/py/path/svn/urlcommand.py ============================================================================== --- py/dist/py/path/svn/urlcommand.py (original) +++ py/dist/py/path/svn/urlcommand.py Tue Jan 9 17:18:34 2007 @@ -105,7 +105,7 @@ return createpath def copy(self, target, msg='copied by py lib invocation'): - """ copy path to target with checkin msg msg.""" + """ copy path to target with checkin message msg.""" if getattr(target, 'rev', None) is not None: raise py.error.EINVAL(target, "revisions are immutable") process.cmdexec('svn copy -m "%s" "%s" "%s"' %(msg, Modified: py/dist/py/path/svn/wccommand.py ============================================================================== --- py/dist/py/path/svn/wccommand.py (original) +++ py/dist/py/path/svn/wccommand.py Tue Jan 9 17:18:34 2007 @@ -178,9 +178,11 @@ self._svn('remove', *flags) def copy(self, target): + """ copy path to target.""" py.process.cmdexec("svn copy %s %s" %(str(self), str(target))) def rename(self, target): + """ rename this path to target. """ py.process.cmdexec("svn move --force %s %s" %(str(self), str(target))) rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(\S+)\s+(.*)') @@ -292,6 +294,9 @@ return rootstatus def diff(self, rev=None): + """ return a diff of the current path against revision rev (defaulting + to the last one). + """ if rev is None: out = self._svn('diff') else: @@ -299,6 +304,8 @@ return out def blame(self): + """ return a list of tuples of three elements: +(revision, commiter, line)""" out = self._svn('blame') result = [] blamelines = out.splitlines() @@ -326,24 +333,30 @@ m = self.rex_commit.match(out) return int(m.group(1)) - def propset(self, propname, value, *args): + def propset(self, name, value, *args): + """ set property name to value on this path. """ d = py.path.local.mkdtemp() try: p = d.join('value') p.write(value) - self._svn('propset', propname, '--file', str(p), *args) + self._svn('propset', name, '--file', str(p), *args) finally: d.remove() def propget(self, name): + """ get property name on this path. """ res = self._svn('propget', name) return res[:-1] # strip trailing newline def propdel(self, name): + """ delete property name on this path. """ res = self._svn('propdel', name) return res[:-1] # strip trailing newline def proplist(self, rec=0): + """ return a mapping of property names to property values. +If rec is True, then return a dictionary mapping sub-paths to such mappings. +""" if rec: res = self._svn('proplist -R') return make_recursive_propdict(self, res) @@ -354,6 +367,8 @@ return svncommon.PropListDict(self, lines) def revert(self, rec=0): + """ revert the local changes of this path. if rec is True, do so +recursively. """ if rec: result = self._svn('revert -R') else: @@ -361,6 +376,16 @@ return result def new(self, **kw): + """ create a modified version of this path. A 'rev' argument + indicates a new revision. + the following keyword arguments modify various path parts: + + http://host.com/repo/path/file.ext + |-----------------------| dirname + |------| basename + |--| purebasename + |--| ext + """ if kw: localpath = self.localpath.new(**kw) else: @@ -453,6 +478,11 @@ return True def log(self, rev_start=None, rev_end=1, verbose=False): + """ return a list of LogEntry instances for this path. +rev_start is the starting revision (defaulting to the first one). +rev_end is the last revision (defaulting to HEAD). +if verbose is True, then the LogEntry instances also know which files changed. +""" from py.__.path.svn.urlcommand import _Head, LogEntry assert self.check() # make it simpler for the pipe rev_start = rev_start is None and _Head or rev_start From cfbolz at codespeak.net Tue Jan 9 17:32:27 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 17:32:27 +0100 (CET) Subject: [py-svn] r36377 - in py/dist/py: documentation path/svn Message-ID: <20070109163227.DF6101007F@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 17:32:25 2007 New Revision: 36377 Modified: py/dist/py/documentation/TODO.txt py/dist/py/path/svn/urlcommand.py py/dist/py/path/svn/wccommand.py Log: some more docstrings. update missing list. Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 17:32:25 2007 @@ -126,11 +126,9 @@ Missing docstrings ------------------ -code.Traceback misses a docstring code.Traceback.recursionindex misses a docstring code.Traceback.filter misses a docstring code.Traceback.cut misses a docstring -code.Traceback.__init__ misses a docstring code.Traceback.__getitem__ misses a docstring code.Traceback.Entry misses a docstring code.Traceback.Entry.ishidden misses a docstring @@ -165,7 +163,6 @@ execnet.SshGateway.exit misses a docstring execnet.SshGateway.__repr__ misses a docstring execnet.SshGateway.__init__ misses a docstring -execnet.SshGateway.ThreadOut misses a docstring execnet.SshGateway.ThreadOut.write misses a docstring execnet.SshGateway.ThreadOut.setwritefunc misses a docstring execnet.SshGateway.ThreadOut.setdefaultwriter misses a docstring @@ -212,23 +209,10 @@ magic.View.__matchkey__ misses a docstring magic.View.__getattr__ misses a docstring magic.AssertionError misses a docstring -path.svnwc misses a docstring path.svnwc.visit misses a docstring -path.svnwc.revert misses a docstring -path.svnwc.rename misses a docstring -path.svnwc.propset misses a docstring -path.svnwc.proplist misses a docstring -path.svnwc.propget misses a docstring -path.svnwc.propdel misses a docstring -path.svnwc.new misses a docstring path.svnwc.mkdir misses a docstring -path.svnwc.log misses a docstring -path.svnwc.getpycodeobj misses a docstring path.svnwc.dump misses a docstring -path.svnwc.diff misses a docstring -path.svnwc.copy misses a docstring path.svnwc.check misses a docstring -path.svnwc.blame misses a docstring path.svnwc.add misses a docstring path.svnwc.__str__ misses a docstring path.svnwc.__repr__ misses a docstring @@ -241,10 +225,7 @@ path.svnwc.__cmp__ misses a docstring path.svnwc.__add__ misses a docstring path.svnwc.Checkers misses a docstring -path.svnurl misses a docstring path.svnurl.visit misses a docstring -path.svnurl.log misses a docstring -path.svnurl.getpycodeobj misses a docstring path.svnurl.check misses a docstring path.svnurl.__repr__ misses a docstring path.svnurl.__new__ misses a docstring @@ -258,9 +239,6 @@ path.svnurl.Checkers misses a docstring path.local.visit misses a docstring path.local.sysexec has an 'XXX' in its docstring -path.local.getpymodule misses a docstring -path.local.getpycodeobj misses a docstring -path.local.copy misses a docstring path.local.check misses a docstring path.local.__repr__ misses a docstring path.local.__iter__ misses a docstring @@ -627,4 +605,3 @@ xml.Tag.__repr__ misses a docstring xml.Tag.__init__ misses a docstring xml.Namespace misses a docstring - Modified: py/dist/py/path/svn/urlcommand.py ============================================================================== --- py/dist/py/path/svn/urlcommand.py (original) +++ py/dist/py/path/svn/urlcommand.py Tue Jan 9 17:32:25 2007 @@ -15,6 +15,9 @@ DEBUG=False class SvnCommandPath(svncommon.SvnPathBase): + """ path implementation that offers access to (possibly remote) subversion + repositories. """ + _lsrevcache = BuildcostAccessCache(maxentries=128) _lsnorevcache = AgingCache(maxentries=1000, maxseconds=60.0) @@ -203,6 +206,11 @@ return self._lsnorevcache.getorbuild(self.strpath, builder) def log(self, rev_start=None, rev_end=1, verbose=False): + """ return a list of LogEntry instances for this path. +rev_start is the starting revision (defaulting to the first one). +rev_end is the last revision (defaulting to HEAD). +if verbose is True, then the LogEntry instances also know which files changed. +""" assert self.check() #make it simpler for the pipe rev_start = rev_start is None and _Head or rev_start rev_end = rev_end is None and _Head or rev_end Modified: py/dist/py/path/svn/wccommand.py ============================================================================== --- py/dist/py/path/svn/wccommand.py (original) +++ py/dist/py/path/svn/wccommand.py Tue Jan 9 17:32:25 2007 @@ -19,6 +19,10 @@ rex_blame = re.compile(r'\s*(\d+)\s*(\S+) (.*)') class SvnWCCommandPath(common.FSPathBase): + """ path implementation offering access/modification to svn working copies. + It has methods similar to the functions in os.path and similar to the + commands of the svn client. + """ sep = os.sep def __new__(cls, wcpath=None): From cfbolz at codespeak.net Tue Jan 9 17:35:13 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 17:35:13 +0100 (CET) Subject: [py-svn] r36378 - py/dist/py/documentation Message-ID: <20070109163513.0741B1007F@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 17:35:11 2007 New Revision: 36378 Modified: py/dist/py/documentation/TODO.txt py/dist/py/documentation/apigen.txt Log: fix rest Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 17:35:11 2007 @@ -68,7 +68,7 @@ * integrate rest directive into py/documentation/conftest.py with help code from py.__.rest.directive.... - make sure that py/documentation/ *.txt files + make sure that the txt files in py/documentation/ use it testing ----------- @@ -126,482 +126,484 @@ Missing docstrings ------------------ -code.Traceback.recursionindex misses a docstring -code.Traceback.filter misses a docstring -code.Traceback.cut misses a docstring -code.Traceback.__getitem__ misses a docstring -code.Traceback.Entry misses a docstring -code.Traceback.Entry.ishidden misses a docstring -code.Traceback.Entry.getfirstlinesource misses a docstring -code.Traceback.Entry.__str__ misses a docstring -code.Traceback.Entry.__repr__ misses a docstring -code.Traceback.Entry.__init__ misses a docstring -code.Source.getblockend misses a docstring -code.Source.__str__ misses a docstring -code.Source.__len__ misses a docstring -code.Source.__init__ misses a docstring -code.Source.__getslice__ misses a docstring -code.Source.__getitem__ misses a docstring -code.Source.__eq__ misses a docstring -code.Frame.repr misses a docstring -code.Frame.is_true misses a docstring -code.Frame.getargs misses a docstring -code.Frame.exec_ misses a docstring -code.Frame.eval misses a docstring -code.Frame.__init__ misses a docstring -code.ExceptionInfo.exconly misses a docstring -code.ExceptionInfo.errisinstance misses a docstring -code.ExceptionInfo.__str__ misses a docstring -code.ExceptionInfo.__init__ misses a docstring -code.Code misses a docstring -code.Code.source misses a docstring -code.Code.getargs misses a docstring -code.Code.__init__ misses a docstring -code.Code.__eq__ misses a docstring -execnet.SshGateway misses a docstring -execnet.SshGateway.join misses a docstring -execnet.SshGateway.exit misses a docstring -execnet.SshGateway.__repr__ misses a docstring -execnet.SshGateway.__init__ misses a docstring -execnet.SshGateway.ThreadOut.write misses a docstring -execnet.SshGateway.ThreadOut.setwritefunc misses a docstring -execnet.SshGateway.ThreadOut.setdefaultwriter misses a docstring -execnet.SshGateway.ThreadOut.resetdefault misses a docstring -execnet.SshGateway.ThreadOut.isatty misses a docstring -execnet.SshGateway.ThreadOut.flush misses a docstring -execnet.SshGateway.ThreadOut.delwritefunc misses a docstring -execnet.SshGateway.ThreadOut.deinstall misses a docstring -execnet.SocketGateway misses a docstring -execnet.SocketGateway.join misses a docstring -execnet.SocketGateway.exit misses a docstring -execnet.SocketGateway.__repr__ misses a docstring -execnet.SocketGateway.__init__ misses a docstring -execnet.PopenGateway misses a docstring -execnet.PopenGateway.remote_bootstrap_gateway misses a docstring -execnet.PopenGateway.join misses a docstring -execnet.PopenGateway.exit misses a docstring -execnet.PopenGateway.__repr__ misses a docstring -execnet.PopenGateway.__init__ misses a docstring -initpkg misses a docstring -io.dupfile misses a docstring -log.setconsumer misses a docstring -log.get misses a docstring -log.Syslog misses a docstring -log.STDOUT misses a docstring -log.STDERR misses a docstring -log.Producer.set_consumer misses a docstring -log.Producer.get_consumer misses a docstring -log.Producer.__repr__ misses a docstring -log.Producer.__init__ misses a docstring -log.Producer.__getattr__ misses a docstring -log.Producer.__call__ misses a docstring -log.Producer.Message misses a docstring -log.Producer.Message.prefix misses a docstring -log.Producer.Message.content misses a docstring -log.Producer.Message.__str__ misses a docstring -log.Producer.Message.__init__ misses a docstring -log.Path misses a docstring -log.Path.__init__ misses a docstring -log.Path.__call__ misses a docstring -magic.View.__viewkey__ misses a docstring -magic.View.__repr__ misses a docstring -magic.View.__new__ misses a docstring -magic.View.__matchkey__ misses a docstring -magic.View.__getattr__ misses a docstring -magic.AssertionError misses a docstring -path.svnwc.visit misses a docstring -path.svnwc.mkdir misses a docstring -path.svnwc.dump misses a docstring -path.svnwc.check misses a docstring -path.svnwc.add misses a docstring -path.svnwc.__str__ misses a docstring -path.svnwc.__repr__ misses a docstring -path.svnwc.__new__ misses a docstring -path.svnwc.__iter__ misses a docstring -path.svnwc.__hash__ misses a docstring -path.svnwc.__eq__ misses a docstring -path.svnwc.__div__ misses a docstring -path.svnwc.__contains__ misses a docstring -path.svnwc.__cmp__ misses a docstring -path.svnwc.__add__ misses a docstring -path.svnwc.Checkers misses a docstring -path.svnurl.visit misses a docstring -path.svnurl.check misses a docstring -path.svnurl.__repr__ misses a docstring -path.svnurl.__new__ misses a docstring -path.svnurl.__ne__ misses a docstring -path.svnurl.__iter__ misses a docstring -path.svnurl.__hash__ misses a docstring -path.svnurl.__div__ misses a docstring -path.svnurl.__contains__ misses a docstring -path.svnurl.__cmp__ misses a docstring -path.svnurl.__add__ misses a docstring -path.svnurl.Checkers misses a docstring -path.local.visit misses a docstring -path.local.sysexec has an 'XXX' in its docstring -path.local.check misses a docstring -path.local.__repr__ misses a docstring -path.local.__iter__ misses a docstring -path.local.__hash__ misses a docstring -path.local.__eq__ misses a docstring -path.local.__div__ misses a docstring -path.local.__contains__ misses a docstring -path.local.__cmp__ misses a docstring -path.local.__add__ misses a docstring -path.local.Checkers misses a docstring -path.extpy.visit misses a docstring -path.extpy.samefile misses a docstring -path.extpy.relto misses a docstring -path.extpy.listobj misses a docstring -path.extpy.listdir misses a docstring -path.extpy.join misses a docstring -path.extpy.getpymodule misses a docstring -path.extpy.getfilelineno misses a docstring -path.extpy.dirpath misses a docstring -path.extpy.check misses a docstring -path.extpy.__str__ misses a docstring -path.extpy.__repr__ misses a docstring -path.extpy.__new__ misses a docstring -path.extpy.__iter__ misses a docstring -path.extpy.__hash__ misses a docstring -path.extpy.__contains__ misses a docstring -path.extpy.__cmp__ misses a docstring -path.extpy.__add__ misses a docstring -path.extpy.Checkers misses a docstring -test.rest.RestReporter misses a docstring -test.rest.RestReporter.summary misses a docstring -test.rest.RestReporter.skips misses a docstring -test.rest.RestReporter.repr_traceback misses a docstring -test.rest.RestReporter.repr_source misses a docstring -test.rest.RestReporter.repr_signal misses a docstring -test.rest.RestReporter.repr_failure misses a docstring -test.rest.RestReporter.report_unknown misses a docstring -test.rest.RestReporter.report_TestStarted misses a docstring -test.rest.RestReporter.report_TestFinished misses a docstring -test.rest.RestReporter.report_SkippedTryiter misses a docstring -test.rest.RestReporter.report_SendItem misses a docstring -test.rest.RestReporter.report_RsyncFinished misses a docstring -test.rest.RestReporter.report_ReceivedItemOutcome misses a docstring -test.rest.RestReporter.report_Nodes misses a docstring -test.rest.RestReporter.report_ItemStart misses a docstring -test.rest.RestReporter.report_ImmediateFailure misses a docstring -test.rest.RestReporter.report_HostReady misses a docstring -test.rest.RestReporter.report_HostRSyncing misses a docstring -test.rest.RestReporter.report_FailedTryiter misses a docstring -test.rest.RestReporter.report misses a docstring -test.rest.RestReporter.print_summary misses a docstring -test.rest.RestReporter.prepare_source misses a docstring -test.rest.RestReporter.hangs misses a docstring -test.rest.RestReporter.get_rootpath misses a docstring -test.rest.RestReporter.get_path_from_item misses a docstring -test.rest.RestReporter.get_linkwriter misses a docstring -test.rest.RestReporter.get_item_name misses a docstring -test.rest.RestReporter.get_host misses a docstring -test.rest.RestReporter.failures misses a docstring -test.rest.RestReporter.add_rest misses a docstring -test.rest.RestReporter.__init__ misses a docstring -test.rest.RelLinkWriter misses a docstring -test.rest.RelLinkWriter.get_link misses a docstring -test.rest.NoLinkWriter misses a docstring -test.rest.NoLinkWriter.get_link misses a docstring -test.rest.LinkWriter misses a docstring -test.rest.LinkWriter.get_link misses a docstring -test.rest.LinkWriter.__init__ misses a docstring -test.exit misses a docstring -test.deprecated_call misses a docstring -test.collect.Module misses a docstring -test.collect.Module.tryiter has an 'XXX' in its docstring -test.collect.Module.teardown misses a docstring -test.collect.Module.startcapture misses a docstring -test.collect.Module.skipbykeyword misses a docstring -test.collect.Module.setup misses a docstring -test.collect.Module.run misses a docstring -test.collect.Module.makeitem misses a docstring -test.collect.Module.listnames misses a docstring -test.collect.Module.join misses a docstring -test.collect.Module.haskeyword misses a docstring -test.collect.Module.getsortvalue misses a docstring -test.collect.Module.getpathlineno misses a docstring -test.collect.Module.getouterr misses a docstring -test.collect.Module.getitembynames misses a docstring -test.collect.Module.funcnamefilter misses a docstring -test.collect.Module.finishcapture misses a docstring -test.collect.Module.classnamefilter misses a docstring -test.collect.Module.buildname2items misses a docstring -test.collect.Module.__repr__ misses a docstring -test.collect.Module.__ne__ misses a docstring -test.collect.Module.__init__ misses a docstring -test.collect.Module.__hash__ misses a docstring -test.collect.Module.__eq__ misses a docstring -test.collect.Module.__cmp__ misses a docstring -test.collect.Module.Skipped misses a docstring -test.collect.Module.Passed misses a docstring -test.collect.Module.Outcome misses a docstring -test.collect.Module.Failed misses a docstring -test.collect.Module.ExceptionFailure misses a docstring -test.collect.Instance misses a docstring -test.collect.Instance.tryiter has an 'XXX' in its docstring -test.collect.Instance.teardown misses a docstring -test.collect.Instance.startcapture misses a docstring -test.collect.Instance.skipbykeyword misses a docstring -test.collect.Instance.setup misses a docstring -test.collect.Instance.run misses a docstring -test.collect.Instance.makeitem misses a docstring -test.collect.Instance.listnames misses a docstring -test.collect.Instance.join misses a docstring -test.collect.Instance.haskeyword misses a docstring -test.collect.Instance.getsortvalue misses a docstring -test.collect.Instance.getpathlineno misses a docstring -test.collect.Instance.getouterr misses a docstring -test.collect.Instance.getitembynames misses a docstring -test.collect.Instance.funcnamefilter misses a docstring -test.collect.Instance.finishcapture misses a docstring -test.collect.Instance.classnamefilter misses a docstring -test.collect.Instance.buildname2items misses a docstring -test.collect.Instance.__repr__ misses a docstring -test.collect.Instance.__ne__ misses a docstring -test.collect.Instance.__init__ misses a docstring -test.collect.Instance.__hash__ misses a docstring -test.collect.Instance.__eq__ misses a docstring -test.collect.Instance.__cmp__ misses a docstring -test.collect.Generator misses a docstring -test.collect.Generator.tryiter has an 'XXX' in its docstring -test.collect.Generator.teardown misses a docstring -test.collect.Generator.startcapture misses a docstring -test.collect.Generator.skipbykeyword misses a docstring -test.collect.Generator.setup misses a docstring -test.collect.Generator.run misses a docstring -test.collect.Generator.listnames misses a docstring -test.collect.Generator.join misses a docstring -test.collect.Generator.haskeyword misses a docstring -test.collect.Generator.getsortvalue misses a docstring -test.collect.Generator.getpathlineno misses a docstring -test.collect.Generator.getouterr misses a docstring -test.collect.Generator.getitembynames misses a docstring -test.collect.Generator.getcallargs misses a docstring -test.collect.Generator.finishcapture misses a docstring -test.collect.Generator.buildname2items misses a docstring -test.collect.Generator.__repr__ misses a docstring -test.collect.Generator.__ne__ misses a docstring -test.collect.Generator.__init__ misses a docstring -test.collect.Generator.__hash__ misses a docstring -test.collect.Generator.__eq__ misses a docstring -test.collect.Generator.__cmp__ misses a docstring -test.collect.DoctestFile misses a docstring -test.collect.DoctestFile.tryiter has an 'XXX' in its docstring -test.collect.DoctestFile.teardown misses a docstring -test.collect.DoctestFile.startcapture misses a docstring -test.collect.DoctestFile.skipbykeyword misses a docstring -test.collect.DoctestFile.setup misses a docstring -test.collect.DoctestFile.run misses a docstring -test.collect.DoctestFile.makeitem misses a docstring -test.collect.DoctestFile.listnames misses a docstring -test.collect.DoctestFile.join misses a docstring -test.collect.DoctestFile.haskeyword misses a docstring -test.collect.DoctestFile.getsortvalue misses a docstring -test.collect.DoctestFile.getpathlineno misses a docstring -test.collect.DoctestFile.getouterr misses a docstring -test.collect.DoctestFile.getitembynames misses a docstring -test.collect.DoctestFile.funcnamefilter misses a docstring -test.collect.DoctestFile.finishcapture misses a docstring -test.collect.DoctestFile.classnamefilter misses a docstring -test.collect.DoctestFile.buildname2items misses a docstring -test.collect.DoctestFile.__repr__ misses a docstring -test.collect.DoctestFile.__ne__ misses a docstring -test.collect.DoctestFile.__init__ misses a docstring -test.collect.DoctestFile.__hash__ misses a docstring -test.collect.DoctestFile.__eq__ misses a docstring -test.collect.DoctestFile.__cmp__ misses a docstring -test.collect.Directory misses a docstring -test.collect.Directory.tryiter has an 'XXX' in its docstring -test.collect.Directory.teardown misses a docstring -test.collect.Directory.startcapture misses a docstring -test.collect.Directory.skipbykeyword misses a docstring -test.collect.Directory.setup misses a docstring -test.collect.Directory.run misses a docstring -test.collect.Directory.recfilter misses a docstring -test.collect.Directory.makeitem misses a docstring -test.collect.Directory.listnames misses a docstring -test.collect.Directory.join misses a docstring -test.collect.Directory.haskeyword misses a docstring -test.collect.Directory.getsortvalue misses a docstring -test.collect.Directory.getpathlineno misses a docstring -test.collect.Directory.getouterr misses a docstring -test.collect.Directory.getitembynames misses a docstring -test.collect.Directory.finishcapture misses a docstring -test.collect.Directory.filefilter misses a docstring -test.collect.Directory.buildname2items misses a docstring -test.collect.Directory.__repr__ misses a docstring -test.collect.Directory.__ne__ misses a docstring -test.collect.Directory.__init__ misses a docstring -test.collect.Directory.__hash__ misses a docstring -test.collect.Directory.__eq__ misses a docstring -test.collect.Directory.__cmp__ misses a docstring -test.collect.Collector misses a docstring -test.collect.Collector.tryiter has an 'XXX' in its docstring -test.collect.Collector.teardown misses a docstring -test.collect.Collector.startcapture misses a docstring -test.collect.Collector.skipbykeyword misses a docstring -test.collect.Collector.setup misses a docstring -test.collect.Collector.run misses a docstring -test.collect.Collector.listnames misses a docstring -test.collect.Collector.join misses a docstring -test.collect.Collector.haskeyword misses a docstring -test.collect.Collector.getsortvalue misses a docstring -test.collect.Collector.getpathlineno misses a docstring -test.collect.Collector.getouterr misses a docstring -test.collect.Collector.getitembynames misses a docstring -test.collect.Collector.finishcapture misses a docstring -test.collect.Collector.buildname2items misses a docstring -test.collect.Collector.__repr__ misses a docstring -test.collect.Collector.__ne__ misses a docstring -test.collect.Collector.__init__ misses a docstring -test.collect.Collector.__hash__ misses a docstring -test.collect.Collector.__eq__ misses a docstring -test.collect.Collector.__cmp__ misses a docstring -test.collect.Class misses a docstring -test.collect.Class.tryiter has an 'XXX' in its docstring -test.collect.Class.teardown misses a docstring -test.collect.Class.startcapture misses a docstring -test.collect.Class.skipbykeyword misses a docstring -test.collect.Class.setup misses a docstring -test.collect.Class.run misses a docstring -test.collect.Class.makeitem misses a docstring -test.collect.Class.listnames misses a docstring -test.collect.Class.join misses a docstring -test.collect.Class.haskeyword misses a docstring -test.collect.Class.getsortvalue misses a docstring -test.collect.Class.getpathlineno misses a docstring -test.collect.Class.getouterr misses a docstring -test.collect.Class.getitembynames misses a docstring -test.collect.Class.funcnamefilter misses a docstring -test.collect.Class.finishcapture misses a docstring -test.collect.Class.classnamefilter misses a docstring -test.collect.Class.buildname2items misses a docstring -test.collect.Class.__repr__ misses a docstring -test.collect.Class.__ne__ misses a docstring -test.collect.Class.__init__ misses a docstring -test.collect.Class.__hash__ misses a docstring -test.collect.Class.__eq__ misses a docstring -test.collect.Class.__cmp__ misses a docstring -test.cmdline.main misses a docstring -test.TkinterSession misses a docstring -test.TerminalSession misses a docstring -test.TerminalSession.summaryline misses a docstring -test.TerminalSession.startiteration misses a docstring -test.TerminalSession.start_Module misses a docstring -test.TerminalSession.start_Item misses a docstring -test.TerminalSession.start misses a docstring -test.TerminalSession.skippedreasons misses a docstring -test.TerminalSession.skipbykeyword misses a docstring -test.TerminalSession.shouldclose misses a docstring -test.TerminalSession.runtraced misses a docstring -test.TerminalSession.run misses a docstring -test.TerminalSession.repr_source misses a docstring -test.TerminalSession.repr_progress_short_result misses a docstring -test.TerminalSession.repr_progress_module_result misses a docstring -test.TerminalSession.repr_progress_long_result misses a docstring -test.TerminalSession.repr_out_err misses a docstring -test.TerminalSession.repr_locals misses a docstring -test.TerminalSession.repr_failure_tbshort misses a docstring -test.TerminalSession.repr_failure_tbno misses a docstring -test.TerminalSession.repr_failure_tblong misses a docstring -test.TerminalSession.repr_failure_info misses a docstring -test.TerminalSession.repr_failure_explanation misses a docstring -test.TerminalSession.repr_failure misses a docstring -test.TerminalSession.main misses a docstring -test.TerminalSession.header misses a docstring -test.TerminalSession.getlastvisible misses a docstring -test.TerminalSession.getitemoutcomepairs misses a docstring -test.TerminalSession.getentrysource misses a docstring -test.TerminalSession.footer misses a docstring -test.TerminalSession.finish misses a docstring -test.TerminalSession.failures misses a docstring -test.TerminalSession.__init__ misses a docstring -test.Session.startiteration misses a docstring -test.Session.start misses a docstring -test.Session.skipbykeyword misses a docstring -test.Session.shouldclose misses a docstring -test.Session.runtraced misses a docstring -test.Session.run misses a docstring -test.Session.getitemoutcomepairs misses a docstring -test.Session.finish misses a docstring -test.Session.__init__ misses a docstring -test.RSession.reporterror misses a docstring -test.RSession.read_distributed_config misses a docstring -test.RSession.make_colitems misses a docstring -test.RSession.kill_server misses a docstring -test.RSession.init_reporter misses a docstring -test.RSession.getpkgdir misses a docstring -test.RSession.dispatch_tests misses a docstring -test.RSession.__init__ misses a docstring -test.LSession.write_docs misses a docstring -test.LSession.main misses a docstring -test.LSession.kill_server misses a docstring -test.LSession.init_runner misses a docstring -test.LSession.init_reporter misses a docstring -test.LSession.__init__ misses a docstring -test.Item misses a docstring -test.Item.tryiter has an 'XXX' in its docstring -test.Item.teardown misses a docstring -test.Item.startcapture misses a docstring -test.Item.skipbykeyword misses a docstring -test.Item.setup misses a docstring -test.Item.run misses a docstring -test.Item.listnames misses a docstring -test.Item.join misses a docstring -test.Item.haskeyword misses a docstring -test.Item.getsortvalue misses a docstring -test.Item.getpathlineno misses a docstring -test.Item.getouterr misses a docstring -test.Item.getitembynames misses a docstring -test.Item.finishcapture misses a docstring -test.Item.buildname2items misses a docstring -test.Item.__repr__ misses a docstring -test.Item.__ne__ misses a docstring -test.Item.__init__ misses a docstring -test.Item.__hash__ misses a docstring -test.Item.__eq__ misses a docstring -test.Item.__cmp__ misses a docstring -test.Function.tryiter has an 'XXX' in its docstring -test.Function.teardown misses a docstring -test.Function.startcapture misses a docstring -test.Function.skipbykeyword misses a docstring -test.Function.setup misses a docstring -test.Function.run misses a docstring -test.Function.listnames misses a docstring -test.Function.join misses a docstring -test.Function.haskeyword misses a docstring -test.Function.getsortvalue misses a docstring -test.Function.getpathlineno misses a docstring -test.Function.getouterr misses a docstring -test.Function.getitembynames misses a docstring -test.Function.finishcapture misses a docstring -test.Function.buildname2items misses a docstring -test.Function.__repr__ misses a docstring -test.Function.__ne__ misses a docstring -test.Function.__init__ misses a docstring -test.Function.__hash__ misses a docstring -test.Function.__eq__ misses a docstring -test.Function.__cmp__ misses a docstring -test.Config.__init__ misses a docstring -xml.raw.__init__ misses a docstring -xml.html misses a docstring -xml.html.__tagclass__ misses a docstring -xml.html.__tagclass__.unicode misses a docstring -xml.html.__tagclass__.__unicode__ misses a docstring -xml.html.__tagclass__.__repr__ misses a docstring -xml.html.__tagclass__.__init__ misses a docstring -xml.html.__tagclass__.Attr misses a docstring -xml.html.__tagclass__.Attr.__init__ misses a docstring -xml.html.__metaclass__ misses a docstring -xml.html.__metaclass__.__getattr__ misses a docstring -xml.html.Style misses a docstring -xml.html.Style.__init__ misses a docstring -xml.escape misses a docstring -xml.Tag misses a docstring -xml.Tag.unicode misses a docstring -xml.Tag.__unicode__ misses a docstring -xml.Tag.__repr__ misses a docstring -xml.Tag.__init__ misses a docstring -xml.Namespace misses a docstring +:: + + code.Traceback.recursionindex misses a docstring + code.Traceback.filter misses a docstring + code.Traceback.cut misses a docstring + code.Traceback.__getitem__ misses a docstring + code.Traceback.Entry misses a docstring + code.Traceback.Entry.ishidden misses a docstring + code.Traceback.Entry.getfirstlinesource misses a docstring + code.Traceback.Entry.__str__ misses a docstring + code.Traceback.Entry.__repr__ misses a docstring + code.Traceback.Entry.__init__ misses a docstring + code.Source.getblockend misses a docstring + code.Source.__str__ misses a docstring + code.Source.__len__ misses a docstring + code.Source.__init__ misses a docstring + code.Source.__getslice__ misses a docstring + code.Source.__getitem__ misses a docstring + code.Source.__eq__ misses a docstring + code.Frame.repr misses a docstring + code.Frame.is_true misses a docstring + code.Frame.getargs misses a docstring + code.Frame.exec_ misses a docstring + code.Frame.eval misses a docstring + code.Frame.__init__ misses a docstring + code.ExceptionInfo.exconly misses a docstring + code.ExceptionInfo.errisinstance misses a docstring + code.ExceptionInfo.__str__ misses a docstring + code.ExceptionInfo.__init__ misses a docstring + code.Code misses a docstring + code.Code.source misses a docstring + code.Code.getargs misses a docstring + code.Code.__init__ misses a docstring + code.Code.__eq__ misses a docstring + execnet.SshGateway misses a docstring + execnet.SshGateway.join misses a docstring + execnet.SshGateway.exit misses a docstring + execnet.SshGateway.__repr__ misses a docstring + execnet.SshGateway.__init__ misses a docstring + execnet.SshGateway.ThreadOut.write misses a docstring + execnet.SshGateway.ThreadOut.setwritefunc misses a docstring + execnet.SshGateway.ThreadOut.setdefaultwriter misses a docstring + execnet.SshGateway.ThreadOut.resetdefault misses a docstring + execnet.SshGateway.ThreadOut.isatty misses a docstring + execnet.SshGateway.ThreadOut.flush misses a docstring + execnet.SshGateway.ThreadOut.delwritefunc misses a docstring + execnet.SshGateway.ThreadOut.deinstall misses a docstring + execnet.SocketGateway misses a docstring + execnet.SocketGateway.join misses a docstring + execnet.SocketGateway.exit misses a docstring + execnet.SocketGateway.__repr__ misses a docstring + execnet.SocketGateway.__init__ misses a docstring + execnet.PopenGateway misses a docstring + execnet.PopenGateway.remote_bootstrap_gateway misses a docstring + execnet.PopenGateway.join misses a docstring + execnet.PopenGateway.exit misses a docstring + execnet.PopenGateway.__repr__ misses a docstring + execnet.PopenGateway.__init__ misses a docstring + initpkg misses a docstring + io.dupfile misses a docstring + log.setconsumer misses a docstring + log.get misses a docstring + log.Syslog misses a docstring + log.STDOUT misses a docstring + log.STDERR misses a docstring + log.Producer.set_consumer misses a docstring + log.Producer.get_consumer misses a docstring + log.Producer.__repr__ misses a docstring + log.Producer.__init__ misses a docstring + log.Producer.__getattr__ misses a docstring + log.Producer.__call__ misses a docstring + log.Producer.Message misses a docstring + log.Producer.Message.prefix misses a docstring + log.Producer.Message.content misses a docstring + log.Producer.Message.__str__ misses a docstring + log.Producer.Message.__init__ misses a docstring + log.Path misses a docstring + log.Path.__init__ misses a docstring + log.Path.__call__ misses a docstring + magic.View.__viewkey__ misses a docstring + magic.View.__repr__ misses a docstring + magic.View.__new__ misses a docstring + magic.View.__matchkey__ misses a docstring + magic.View.__getattr__ misses a docstring + magic.AssertionError misses a docstring + path.svnwc.visit misses a docstring + path.svnwc.mkdir misses a docstring + path.svnwc.dump misses a docstring + path.svnwc.check misses a docstring + path.svnwc.add misses a docstring + path.svnwc.__str__ misses a docstring + path.svnwc.__repr__ misses a docstring + path.svnwc.__new__ misses a docstring + path.svnwc.__iter__ misses a docstring + path.svnwc.__hash__ misses a docstring + path.svnwc.__eq__ misses a docstring + path.svnwc.__div__ misses a docstring + path.svnwc.__contains__ misses a docstring + path.svnwc.__cmp__ misses a docstring + path.svnwc.__add__ misses a docstring + path.svnwc.Checkers misses a docstring + path.svnurl.visit misses a docstring + path.svnurl.check misses a docstring + path.svnurl.__repr__ misses a docstring + path.svnurl.__new__ misses a docstring + path.svnurl.__ne__ misses a docstring + path.svnurl.__iter__ misses a docstring + path.svnurl.__hash__ misses a docstring + path.svnurl.__div__ misses a docstring + path.svnurl.__contains__ misses a docstring + path.svnurl.__cmp__ misses a docstring + path.svnurl.__add__ misses a docstring + path.svnurl.Checkers misses a docstring + path.local.visit misses a docstring + path.local.sysexec has an 'XXX' in its docstring + path.local.check misses a docstring + path.local.__repr__ misses a docstring + path.local.__iter__ misses a docstring + path.local.__hash__ misses a docstring + path.local.__eq__ misses a docstring + path.local.__div__ misses a docstring + path.local.__contains__ misses a docstring + path.local.__cmp__ misses a docstring + path.local.__add__ misses a docstring + path.local.Checkers misses a docstring + path.extpy.visit misses a docstring + path.extpy.samefile misses a docstring + path.extpy.relto misses a docstring + path.extpy.listobj misses a docstring + path.extpy.listdir misses a docstring + path.extpy.join misses a docstring + path.extpy.getpymodule misses a docstring + path.extpy.getfilelineno misses a docstring + path.extpy.dirpath misses a docstring + path.extpy.check misses a docstring + path.extpy.__str__ misses a docstring + path.extpy.__repr__ misses a docstring + path.extpy.__new__ misses a docstring + path.extpy.__iter__ misses a docstring + path.extpy.__hash__ misses a docstring + path.extpy.__contains__ misses a docstring + path.extpy.__cmp__ misses a docstring + path.extpy.__add__ misses a docstring + path.extpy.Checkers misses a docstring + test.rest.RestReporter misses a docstring + test.rest.RestReporter.summary misses a docstring + test.rest.RestReporter.skips misses a docstring + test.rest.RestReporter.repr_traceback misses a docstring + test.rest.RestReporter.repr_source misses a docstring + test.rest.RestReporter.repr_signal misses a docstring + test.rest.RestReporter.repr_failure misses a docstring + test.rest.RestReporter.report_unknown misses a docstring + test.rest.RestReporter.report_TestStarted misses a docstring + test.rest.RestReporter.report_TestFinished misses a docstring + test.rest.RestReporter.report_SkippedTryiter misses a docstring + test.rest.RestReporter.report_SendItem misses a docstring + test.rest.RestReporter.report_RsyncFinished misses a docstring + test.rest.RestReporter.report_ReceivedItemOutcome misses a docstring + test.rest.RestReporter.report_Nodes misses a docstring + test.rest.RestReporter.report_ItemStart misses a docstring + test.rest.RestReporter.report_ImmediateFailure misses a docstring + test.rest.RestReporter.report_HostReady misses a docstring + test.rest.RestReporter.report_HostRSyncing misses a docstring + test.rest.RestReporter.report_FailedTryiter misses a docstring + test.rest.RestReporter.report misses a docstring + test.rest.RestReporter.print_summary misses a docstring + test.rest.RestReporter.prepare_source misses a docstring + test.rest.RestReporter.hangs misses a docstring + test.rest.RestReporter.get_rootpath misses a docstring + test.rest.RestReporter.get_path_from_item misses a docstring + test.rest.RestReporter.get_linkwriter misses a docstring + test.rest.RestReporter.get_item_name misses a docstring + test.rest.RestReporter.get_host misses a docstring + test.rest.RestReporter.failures misses a docstring + test.rest.RestReporter.add_rest misses a docstring + test.rest.RestReporter.__init__ misses a docstring + test.rest.RelLinkWriter misses a docstring + test.rest.RelLinkWriter.get_link misses a docstring + test.rest.NoLinkWriter misses a docstring + test.rest.NoLinkWriter.get_link misses a docstring + test.rest.LinkWriter misses a docstring + test.rest.LinkWriter.get_link misses a docstring + test.rest.LinkWriter.__init__ misses a docstring + test.exit misses a docstring + test.deprecated_call misses a docstring + test.collect.Module misses a docstring + test.collect.Module.tryiter has an 'XXX' in its docstring + test.collect.Module.teardown misses a docstring + test.collect.Module.startcapture misses a docstring + test.collect.Module.skipbykeyword misses a docstring + test.collect.Module.setup misses a docstring + test.collect.Module.run misses a docstring + test.collect.Module.makeitem misses a docstring + test.collect.Module.listnames misses a docstring + test.collect.Module.join misses a docstring + test.collect.Module.haskeyword misses a docstring + test.collect.Module.getsortvalue misses a docstring + test.collect.Module.getpathlineno misses a docstring + test.collect.Module.getouterr misses a docstring + test.collect.Module.getitembynames misses a docstring + test.collect.Module.funcnamefilter misses a docstring + test.collect.Module.finishcapture misses a docstring + test.collect.Module.classnamefilter misses a docstring + test.collect.Module.buildname2items misses a docstring + test.collect.Module.__repr__ misses a docstring + test.collect.Module.__ne__ misses a docstring + test.collect.Module.__init__ misses a docstring + test.collect.Module.__hash__ misses a docstring + test.collect.Module.__eq__ misses a docstring + test.collect.Module.__cmp__ misses a docstring + test.collect.Module.Skipped misses a docstring + test.collect.Module.Passed misses a docstring + test.collect.Module.Outcome misses a docstring + test.collect.Module.Failed misses a docstring + test.collect.Module.ExceptionFailure misses a docstring + test.collect.Instance misses a docstring + test.collect.Instance.tryiter has an 'XXX' in its docstring + test.collect.Instance.teardown misses a docstring + test.collect.Instance.startcapture misses a docstring + test.collect.Instance.skipbykeyword misses a docstring + test.collect.Instance.setup misses a docstring + test.collect.Instance.run misses a docstring + test.collect.Instance.makeitem misses a docstring + test.collect.Instance.listnames misses a docstring + test.collect.Instance.join misses a docstring + test.collect.Instance.haskeyword misses a docstring + test.collect.Instance.getsortvalue misses a docstring + test.collect.Instance.getpathlineno misses a docstring + test.collect.Instance.getouterr misses a docstring + test.collect.Instance.getitembynames misses a docstring + test.collect.Instance.funcnamefilter misses a docstring + test.collect.Instance.finishcapture misses a docstring + test.collect.Instance.classnamefilter misses a docstring + test.collect.Instance.buildname2items misses a docstring + test.collect.Instance.__repr__ misses a docstring + test.collect.Instance.__ne__ misses a docstring + test.collect.Instance.__init__ misses a docstring + test.collect.Instance.__hash__ misses a docstring + test.collect.Instance.__eq__ misses a docstring + test.collect.Instance.__cmp__ misses a docstring + test.collect.Generator misses a docstring + test.collect.Generator.tryiter has an 'XXX' in its docstring + test.collect.Generator.teardown misses a docstring + test.collect.Generator.startcapture misses a docstring + test.collect.Generator.skipbykeyword misses a docstring + test.collect.Generator.setup misses a docstring + test.collect.Generator.run misses a docstring + test.collect.Generator.listnames misses a docstring + test.collect.Generator.join misses a docstring + test.collect.Generator.haskeyword misses a docstring + test.collect.Generator.getsortvalue misses a docstring + test.collect.Generator.getpathlineno misses a docstring + test.collect.Generator.getouterr misses a docstring + test.collect.Generator.getitembynames misses a docstring + test.collect.Generator.getcallargs misses a docstring + test.collect.Generator.finishcapture misses a docstring + test.collect.Generator.buildname2items misses a docstring + test.collect.Generator.__repr__ misses a docstring + test.collect.Generator.__ne__ misses a docstring + test.collect.Generator.__init__ misses a docstring + test.collect.Generator.__hash__ misses a docstring + test.collect.Generator.__eq__ misses a docstring + test.collect.Generator.__cmp__ misses a docstring + test.collect.DoctestFile misses a docstring + test.collect.DoctestFile.tryiter has an 'XXX' in its docstring + test.collect.DoctestFile.teardown misses a docstring + test.collect.DoctestFile.startcapture misses a docstring + test.collect.DoctestFile.skipbykeyword misses a docstring + test.collect.DoctestFile.setup misses a docstring + test.collect.DoctestFile.run misses a docstring + test.collect.DoctestFile.makeitem misses a docstring + test.collect.DoctestFile.listnames misses a docstring + test.collect.DoctestFile.join misses a docstring + test.collect.DoctestFile.haskeyword misses a docstring + test.collect.DoctestFile.getsortvalue misses a docstring + test.collect.DoctestFile.getpathlineno misses a docstring + test.collect.DoctestFile.getouterr misses a docstring + test.collect.DoctestFile.getitembynames misses a docstring + test.collect.DoctestFile.funcnamefilter misses a docstring + test.collect.DoctestFile.finishcapture misses a docstring + test.collect.DoctestFile.classnamefilter misses a docstring + test.collect.DoctestFile.buildname2items misses a docstring + test.collect.DoctestFile.__repr__ misses a docstring + test.collect.DoctestFile.__ne__ misses a docstring + test.collect.DoctestFile.__init__ misses a docstring + test.collect.DoctestFile.__hash__ misses a docstring + test.collect.DoctestFile.__eq__ misses a docstring + test.collect.DoctestFile.__cmp__ misses a docstring + test.collect.Directory misses a docstring + test.collect.Directory.tryiter has an 'XXX' in its docstring + test.collect.Directory.teardown misses a docstring + test.collect.Directory.startcapture misses a docstring + test.collect.Directory.skipbykeyword misses a docstring + test.collect.Directory.setup misses a docstring + test.collect.Directory.run misses a docstring + test.collect.Directory.recfilter misses a docstring + test.collect.Directory.makeitem misses a docstring + test.collect.Directory.listnames misses a docstring + test.collect.Directory.join misses a docstring + test.collect.Directory.haskeyword misses a docstring + test.collect.Directory.getsortvalue misses a docstring + test.collect.Directory.getpathlineno misses a docstring + test.collect.Directory.getouterr misses a docstring + test.collect.Directory.getitembynames misses a docstring + test.collect.Directory.finishcapture misses a docstring + test.collect.Directory.filefilter misses a docstring + test.collect.Directory.buildname2items misses a docstring + test.collect.Directory.__repr__ misses a docstring + test.collect.Directory.__ne__ misses a docstring + test.collect.Directory.__init__ misses a docstring + test.collect.Directory.__hash__ misses a docstring + test.collect.Directory.__eq__ misses a docstring + test.collect.Directory.__cmp__ misses a docstring + test.collect.Collector misses a docstring + test.collect.Collector.tryiter has an 'XXX' in its docstring + test.collect.Collector.teardown misses a docstring + test.collect.Collector.startcapture misses a docstring + test.collect.Collector.skipbykeyword misses a docstring + test.collect.Collector.setup misses a docstring + test.collect.Collector.run misses a docstring + test.collect.Collector.listnames misses a docstring + test.collect.Collector.join misses a docstring + test.collect.Collector.haskeyword misses a docstring + test.collect.Collector.getsortvalue misses a docstring + test.collect.Collector.getpathlineno misses a docstring + test.collect.Collector.getouterr misses a docstring + test.collect.Collector.getitembynames misses a docstring + test.collect.Collector.finishcapture misses a docstring + test.collect.Collector.buildname2items misses a docstring + test.collect.Collector.__repr__ misses a docstring + test.collect.Collector.__ne__ misses a docstring + test.collect.Collector.__init__ misses a docstring + test.collect.Collector.__hash__ misses a docstring + test.collect.Collector.__eq__ misses a docstring + test.collect.Collector.__cmp__ misses a docstring + test.collect.Class misses a docstring + test.collect.Class.tryiter has an 'XXX' in its docstring + test.collect.Class.teardown misses a docstring + test.collect.Class.startcapture misses a docstring + test.collect.Class.skipbykeyword misses a docstring + test.collect.Class.setup misses a docstring + test.collect.Class.run misses a docstring + test.collect.Class.makeitem misses a docstring + test.collect.Class.listnames misses a docstring + test.collect.Class.join misses a docstring + test.collect.Class.haskeyword misses a docstring + test.collect.Class.getsortvalue misses a docstring + test.collect.Class.getpathlineno misses a docstring + test.collect.Class.getouterr misses a docstring + test.collect.Class.getitembynames misses a docstring + test.collect.Class.funcnamefilter misses a docstring + test.collect.Class.finishcapture misses a docstring + test.collect.Class.classnamefilter misses a docstring + test.collect.Class.buildname2items misses a docstring + test.collect.Class.__repr__ misses a docstring + test.collect.Class.__ne__ misses a docstring + test.collect.Class.__init__ misses a docstring + test.collect.Class.__hash__ misses a docstring + test.collect.Class.__eq__ misses a docstring + test.collect.Class.__cmp__ misses a docstring + test.cmdline.main misses a docstring + test.TkinterSession misses a docstring + test.TerminalSession misses a docstring + test.TerminalSession.summaryline misses a docstring + test.TerminalSession.startiteration misses a docstring + test.TerminalSession.start_Module misses a docstring + test.TerminalSession.start_Item misses a docstring + test.TerminalSession.start misses a docstring + test.TerminalSession.skippedreasons misses a docstring + test.TerminalSession.skipbykeyword misses a docstring + test.TerminalSession.shouldclose misses a docstring + test.TerminalSession.runtraced misses a docstring + test.TerminalSession.run misses a docstring + test.TerminalSession.repr_source misses a docstring + test.TerminalSession.repr_progress_short_result misses a docstring + test.TerminalSession.repr_progress_module_result misses a docstring + test.TerminalSession.repr_progress_long_result misses a docstring + test.TerminalSession.repr_out_err misses a docstring + test.TerminalSession.repr_locals misses a docstring + test.TerminalSession.repr_failure_tbshort misses a docstring + test.TerminalSession.repr_failure_tbno misses a docstring + test.TerminalSession.repr_failure_tblong misses a docstring + test.TerminalSession.repr_failure_info misses a docstring + test.TerminalSession.repr_failure_explanation misses a docstring + test.TerminalSession.repr_failure misses a docstring + test.TerminalSession.main misses a docstring + test.TerminalSession.header misses a docstring + test.TerminalSession.getlastvisible misses a docstring + test.TerminalSession.getitemoutcomepairs misses a docstring + test.TerminalSession.getentrysource misses a docstring + test.TerminalSession.footer misses a docstring + test.TerminalSession.finish misses a docstring + test.TerminalSession.failures misses a docstring + test.TerminalSession.__init__ misses a docstring + test.Session.startiteration misses a docstring + test.Session.start misses a docstring + test.Session.skipbykeyword misses a docstring + test.Session.shouldclose misses a docstring + test.Session.runtraced misses a docstring + test.Session.run misses a docstring + test.Session.getitemoutcomepairs misses a docstring + test.Session.finish misses a docstring + test.Session.__init__ misses a docstring + test.RSession.reporterror misses a docstring + test.RSession.read_distributed_config misses a docstring + test.RSession.make_colitems misses a docstring + test.RSession.kill_server misses a docstring + test.RSession.init_reporter misses a docstring + test.RSession.getpkgdir misses a docstring + test.RSession.dispatch_tests misses a docstring + test.RSession.__init__ misses a docstring + test.LSession.write_docs misses a docstring + test.LSession.main misses a docstring + test.LSession.kill_server misses a docstring + test.LSession.init_runner misses a docstring + test.LSession.init_reporter misses a docstring + test.LSession.__init__ misses a docstring + test.Item misses a docstring + test.Item.tryiter has an 'XXX' in its docstring + test.Item.teardown misses a docstring + test.Item.startcapture misses a docstring + test.Item.skipbykeyword misses a docstring + test.Item.setup misses a docstring + test.Item.run misses a docstring + test.Item.listnames misses a docstring + test.Item.join misses a docstring + test.Item.haskeyword misses a docstring + test.Item.getsortvalue misses a docstring + test.Item.getpathlineno misses a docstring + test.Item.getouterr misses a docstring + test.Item.getitembynames misses a docstring + test.Item.finishcapture misses a docstring + test.Item.buildname2items misses a docstring + test.Item.__repr__ misses a docstring + test.Item.__ne__ misses a docstring + test.Item.__init__ misses a docstring + test.Item.__hash__ misses a docstring + test.Item.__eq__ misses a docstring + test.Item.__cmp__ misses a docstring + test.Function.tryiter has an 'XXX' in its docstring + test.Function.teardown misses a docstring + test.Function.startcapture misses a docstring + test.Function.skipbykeyword misses a docstring + test.Function.setup misses a docstring + test.Function.run misses a docstring + test.Function.listnames misses a docstring + test.Function.join misses a docstring + test.Function.haskeyword misses a docstring + test.Function.getsortvalue misses a docstring + test.Function.getpathlineno misses a docstring + test.Function.getouterr misses a docstring + test.Function.getitembynames misses a docstring + test.Function.finishcapture misses a docstring + test.Function.buildname2items misses a docstring + test.Function.__repr__ misses a docstring + test.Function.__ne__ misses a docstring + test.Function.__init__ misses a docstring + test.Function.__hash__ misses a docstring + test.Function.__eq__ misses a docstring + test.Function.__cmp__ misses a docstring + test.Config.__init__ misses a docstring + xml.raw.__init__ misses a docstring + xml.html misses a docstring + xml.html.__tagclass__ misses a docstring + xml.html.__tagclass__.unicode misses a docstring + xml.html.__tagclass__.__unicode__ misses a docstring + xml.html.__tagclass__.__repr__ misses a docstring + xml.html.__tagclass__.__init__ misses a docstring + xml.html.__tagclass__.Attr misses a docstring + xml.html.__tagclass__.Attr.__init__ misses a docstring + xml.html.__metaclass__ misses a docstring + xml.html.__metaclass__.__getattr__ misses a docstring + xml.html.Style misses a docstring + xml.html.Style.__init__ misses a docstring + xml.escape misses a docstring + xml.Tag misses a docstring + xml.Tag.unicode misses a docstring + xml.Tag.__unicode__ misses a docstring + xml.Tag.__repr__ misses a docstring + xml.Tag.__init__ misses a docstring + xml.Namespace misses a docstring Modified: py/dist/py/documentation/apigen.txt ============================================================================== --- py/dist/py/documentation/apigen.txt (original) +++ py/dist/py/documentation/apigen.txt Tue Jan 9 17:35:11 2007 @@ -103,8 +103,10 @@ are hooks built into py.test that allow you to do that: * Write down a python script which contains at least two functions + - `get_documentable_items() -> {}` - function which will return dictionary - of name to object of exported items + of name to object of exported items + - `build(pkgpath, docstorageaccessor)` - function which will be invoked afterwards with DocStorageAccessor instance as an argument (you should read DocStorageAccessor interface to know how you can access it) From cfbolz at codespeak.net Tue Jan 9 17:55:55 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 17:55:55 +0100 (CET) Subject: [py-svn] r36379 - in py/dist/py: . compat compat/testing Message-ID: <20070109165555.6ED4E1007D@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 17:55:49 2007 New Revision: 36379 Added: py/dist/py/compat/testing/test_subprocess.py Modified: py/dist/py/__init__.py py/dist/py/compat/doctest.py py/dist/py/compat/optparse.py py/dist/py/compat/subprocess.py py/dist/py/compat/testing/test_doctest.py py/dist/py/compat/testing/test_doctest2.py py/dist/py/compat/testing/test_optparse.py py/dist/py/compat/testing/test_textwrap.py py/dist/py/compat/textwrap.py Log: import the newest versions of the modules in compat from python 2.4.4 Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Tue Jan 9 17:55:49 2007 @@ -130,7 +130,7 @@ 'log.Syslog' : ('./log/consumer.py', 'Syslog'), 'log.get' : ('./log/logger.py', 'get'), - # compatibility modules (taken from 2.4.1) + # compatibility modules (taken from 2.4.4) 'compat.doctest' : ('./compat/doctest.py', '*'), 'compat.optparse' : ('./compat/optparse.py', '*'), 'compat.textwrap' : ('./compat/textwrap.py', '*'), Modified: py/dist/py/compat/doctest.py ============================================================================== --- py/dist/py/compat/doctest.py (original) +++ py/dist/py/compat/doctest.py Tue Jan 9 17:55:49 2007 @@ -128,9 +128,8 @@ OPTIONFLAGS_BY_NAME = {} def register_optionflag(name): - flag = 1 << len(OPTIONFLAGS_BY_NAME) - OPTIONFLAGS_BY_NAME[name] = flag - return flag + # Create a new flag unless `name` is already known. + return OPTIONFLAGS_BY_NAME.setdefault(name, 1 << len(OPTIONFLAGS_BY_NAME)) DONT_ACCEPT_TRUE_FOR_1 = register_optionflag('DONT_ACCEPT_TRUE_FOR_1') DONT_ACCEPT_BLANKLINE = register_optionflag('DONT_ACCEPT_BLANKLINE') @@ -849,6 +848,11 @@ # Recursively expore `obj`, extracting DocTests. tests = [] self._find(tests, obj, name, module, source_lines, globs, {}) + # Sort the tests by alpha order of names, for consistency in + # verbose-mode output. This was a feature of doctest in Pythons + # <= 2.3 that got lost by accident in 2.4. It was repaired in + # 2.4.4 and 2.5. + tests.sort() return tests def _filter(self, obj, prefix, base): @@ -1045,12 +1049,13 @@ >>> tests = DocTestFinder().find(_TestClass) >>> runner = DocTestRunner(verbose=False) + >>> tests.sort(key = lambda test: test.name) >>> for test in tests: - ... print runner.run(test) - (0, 2) - (0, 1) - (0, 2) - (0, 2) + ... print test.name, '->', runner.run(test) + _TestClass -> (0, 2) + _TestClass.__init__ -> (0, 2) + _TestClass.get -> (0, 2) + _TestClass.square -> (0, 1) The `summarize` method prints a summary of all the test cases that have been run by the runner, and returns an aggregated `(f, t)` @@ -2472,6 +2477,7 @@ blah # # Ho hum + """ output = [] for piece in DocTestParser().parse(s): @@ -2494,7 +2500,8 @@ while output and output[0] == '#': output.pop(0) # Combine the output, and return it. - return '\n'.join(output) + # Add a courtesy newline to prevent exec from choking (see bug #1172785) + return '\n'.join(output) + '\n' def testsource(module, name): """Extract the test sources from a doctest docstring as a script. Modified: py/dist/py/compat/optparse.py ============================================================================== --- py/dist/py/compat/optparse.py (original) +++ py/dist/py/compat/optparse.py Tue Jan 9 17:55:49 2007 @@ -68,12 +68,9 @@ import sys, os import types +import textwrap from gettext import gettext as _ -import py -textwrap = py.compat.textwrap - - def _repr(self): return "<%s at 0x%x: %s>" % (self.__class__.__name__, id(self), self) @@ -550,8 +547,10 @@ else: setattr(self, attr, None) if attrs: + attrs = attrs.keys() + attrs.sort() raise OptionError( - "invalid keyword arguments: %s" % ", ".join(attrs.keys()), + "invalid keyword arguments: %s" % ", ".join(attrs), self) @@ -1559,6 +1558,7 @@ raise BadOptionError(_("no such option: %s") % s) else: # More than one possible completion: ambiguous prefix. + possibilities.sort() raise BadOptionError(_("ambiguous option: %s (%s?)") % (s, ", ".join(possibilities))) Modified: py/dist/py/compat/subprocess.py ============================================================================== --- py/dist/py/compat/subprocess.py (original) +++ py/dist/py/compat/subprocess.py Tue Jan 9 17:55:49 2007 @@ -1,32 +1,13 @@ -# Copied from CPython 2.4 with one modification to dynamically -# fall back to pywin32 code when _subprocess is not available. - # subprocess - Subprocesses with accessible I/O streams # # For more information about this module, see PEP 324. # -# Copyright (c) 2003-2004 by Peter Astrand -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: +# This module should remain compatible with Python 2.2, see PEP 291. # -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of the -# author not be used in advertising or publicity pertaining to -# distribution of the software without specific, written prior -# permission. +# Copyright (c) 2003-2005 by Peter Astrand # -# THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, -# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR -# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS -# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, -# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# Licensed to PSF under a Contributor Agreement. +# See http://www.python.org/2.4/license for licensing details. r"""subprocess - Subprocesses with accessible I/O streams @@ -369,16 +350,7 @@ if mswindows: import threading import msvcrt - try: - from _subprocess import * - class STARTUPINFO: - dwFlags = 0 - hStdInput = None - hStdOutput = None - hStdError = None - class pywintypes: - error = IOError - except ImportError: + if 0: # <-- change this to use pywin32 instead of the _subprocess driver import pywintypes from win32api import GetStdHandle, STD_INPUT_HANDLE, \ STD_OUTPUT_HANDLE, STD_ERROR_HANDLE @@ -390,6 +362,16 @@ GetExitCodeProcess, STARTF_USESTDHANDLES, \ STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 + else: + from _subprocess import * + class STARTUPINFO: + dwFlags = 0 + hStdInput = None + hStdOutput = None + hStdError = None + wShowWindow = 0 + class pywintypes: + error = IOError else: import select import errno @@ -681,18 +663,17 @@ args = list2cmdline(args) # Process startup details - default_startupinfo = STARTUPINFO() if startupinfo == None: - startupinfo = default_startupinfo - if not None in (p2cread, c2pwrite, errwrite): + startupinfo = STARTUPINFO() + if None not in (p2cread, c2pwrite, errwrite): startupinfo.dwFlags |= STARTF_USESTDHANDLES startupinfo.hStdInput = p2cread startupinfo.hStdOutput = c2pwrite startupinfo.hStdError = errwrite if shell: - default_startupinfo.dwFlags |= STARTF_USESHOWWINDOW - default_startupinfo.wShowWindow = SW_HIDE + startupinfo.dwFlags |= STARTF_USESHOWWINDOW + startupinfo.wShowWindow = SW_HIDE comspec = os.environ.get("COMSPEC", "cmd.exe") args = comspec + " /c " + args if (GetVersion() >= 0x80000000L or @@ -886,7 +867,7 @@ def _close_fds(self, but): - for i in range(3, MAXFD): + for i in xrange(3, MAXFD): if i == but: continue try: Modified: py/dist/py/compat/testing/test_doctest.py ============================================================================== --- py/dist/py/compat/testing/test_doctest.py (original) +++ py/dist/py/compat/testing/test_doctest.py Tue Jan 9 17:55:49 2007 @@ -2,14 +2,15 @@ Test script for doctest. """ +from test import test_support +import warnings + import py doctest = py.compat.doctest import sys sys.modules['doctest'] = py.compat.doctest -from test import test_support -import warnings ###################################################################### ## Sample Objects (used by test cases) @@ -424,7 +425,6 @@ >>> finder = doctest.DocTestFinder() >>> tests = finder.find(SampleClass) - >>> tests.sort() >>> for t in tests: ... print '%2s %s' % (len(t.examples), t.name) 3 SampleClass @@ -440,7 +440,6 @@ New-style classes are also supported: >>> tests = finder.find(SampleNewStyleClass) - >>> tests.sort() >>> for t in tests: ... print '%2s %s' % (len(t.examples), t.name) 1 SampleNewStyleClass @@ -480,7 +479,6 @@ >>> # ignoring the objects since they weren't defined in m. >>> import test.test_doctest >>> tests = finder.find(m, module=test.test_doctest) - >>> tests.sort() >>> for t in tests: ... print '%2s %s' % (len(t.examples), t.name) 1 some_module @@ -504,7 +502,6 @@ >>> from test import doctest_aliases >>> tests = excl_empty_finder.find(doctest_aliases) - >>> tests.sort() >>> print len(tests) 2 >>> print tests[0].name @@ -526,7 +523,6 @@ >>> def namefilter(prefix, base): ... return base.startswith('a_') >>> tests = doctest.DocTestFinder(_namefilter=namefilter).find(SampleClass) - >>> tests.sort() >>> for t in tests: ... print '%2s %s' % (len(t.examples), t.name) 3 SampleClass @@ -543,7 +539,6 @@ >>> tests = doctest.DocTestFinder(_namefilter=namefilter, ... exclude_empty=False).find(SampleClass) - >>> tests.sort() >>> for t in tests: ... print '%2s %s' % (len(t.examples), t.name) 3 SampleClass @@ -587,7 +582,6 @@ using the `recurse` flag: >>> tests = doctest.DocTestFinder(recurse=False).find(SampleClass) - >>> tests.sort() >>> for t in tests: ... print '%2s %s' % (len(t.examples), t.name) 3 SampleClass @@ -1286,6 +1280,26 @@ ValueError: 2 (3, 5) +New option flags can also be registered, via register_optionflag(). Here +we reach into doctest's internals a bit. + + >>> unlikely = "UNLIKELY_OPTION_NAME" + >>> unlikely in doctest.OPTIONFLAGS_BY_NAME + False + >>> new_flag_value = doctest.register_optionflag(unlikely) + >>> unlikely in doctest.OPTIONFLAGS_BY_NAME + True + +Before 2.4.4/2.5, registering a name more than once erroneously created +more than one flag value. Here we verify that's fixed: + + >>> redundant_flag_value = doctest.register_optionflag(unlikely) + >>> redundant_flag_value == new_flag_value + True + +Clean up. + >>> del doctest.OPTIONFLAGS_BY_NAME[unlikely] + """ def option_directives(): r""" @@ -1522,6 +1536,7 @@ ## 44 # # Yee ha! + >>> name = 'test.test_doctest.SampleNewStyleClass' >>> print doctest.testsource(test.test_doctest, name) @@ -1530,6 +1545,7 @@ ## 1 ## 2 ## 3 + >>> name = 'test.test_doctest.SampleClass.a_classmethod' >>> print doctest.testsource(test.test_doctest, name) @@ -1539,6 +1555,7 @@ print SampleClass(0).a_classmethod(10) # Expected: ## 12 + """ def test_debug(): r""" Modified: py/dist/py/compat/testing/test_doctest2.py ============================================================================== --- py/dist/py/compat/testing/test_doctest2.py (original) +++ py/dist/py/compat/testing/test_doctest2.py Tue Jan 9 17:55:49 2007 @@ -11,8 +11,6 @@ ????? """ -from _findpy import py -print py.__file__ from test import test_support Modified: py/dist/py/compat/testing/test_optparse.py ============================================================================== --- py/dist/py/compat/testing/test_optparse.py (original) +++ py/dist/py/compat/testing/test_optparse.py Tue Jan 9 17:55:49 2007 @@ -5,7 +5,7 @@ # (taradino at softhome.net) -- translated from the original Optik # test suite to this PyUnit-based version. # -# $Id: test_optparse.py,v 1.10 2004/10/27 02:43:25 tim_one Exp $ +# $Id: test_optparse.py 46506 2006-05-28 18:15:43Z armin.rigo $ # import sys @@ -19,21 +19,13 @@ import py optparse = py.compat.optparse -make_option = optparse.make_option -Option = optparse.Option -IndentedHelpFormatter = optparse.IndentedHelpFormatter -TitledHelpFormatter = optparse.TitledHelpFormatter -OptionParser = optparse.OptionParser -OptionContainer = optparse.OptionContainer -OptionGroup = optparse.OptionGroup -SUPPRESS_HELP = optparse.SUPPRESS_HELP -SUPPRESS_USAGE = optparse.SUPPRESS_USAGE -OptionError = optparse.OptionError -OptionConflictError = optparse.OptionConflictError -BadOptionError = optparse.BadOptionError -OptionValueError = optparse.OptionValueError -Values = optparse.Values -_match_abbrev = optparse._match_abbrev +import sys +sys.modules['optparse'] = optparse + +from optparse import make_option, Option, IndentedHelpFormatter, \ + TitledHelpFormatter, OptionParser, OptionContainer, OptionGroup, \ + SUPPRESS_HELP, SUPPRESS_USAGE, OptionError, OptionConflictError, \ + BadOptionError, OptionValueError, Values, _match_abbrev # Do the right thing with boolean values for all known Python versions. try: @@ -225,7 +217,7 @@ def test_attr_invalid(self): self.assertOptionError( - "option -b: invalid keyword arguments: foo, bar", + "option -b: invalid keyword arguments: bar, foo", ["-b"], {'foo': None, 'bar': None}) def test_action_invalid(self): @@ -688,9 +680,8 @@ def test_ambiguous_option(self): self.parser.add_option("--foz", action="store", type="string", dest="foo") - possibilities = ", ".join({"--foz": None, "--foo": None}.keys()) self.assertParseFail(["--f=bar"], - "ambiguous option: --f (%s?)" % possibilities) + "ambiguous option: --f (--foo, --foz?)") def test_short_and_long_option_split(self): @@ -1490,10 +1481,9 @@ def test_match_abbrev_error(self): s = "--f" wordmap = {"--foz": None, "--foo": None, "--fie": None} - possibilities = ", ".join(wordmap.keys()) self.assertRaises( _match_abbrev, (s, wordmap), None, - BadOptionError, "ambiguous option: --f (%s?)" % possibilities) + BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)") def _testclasses(): Added: py/dist/py/compat/testing/test_subprocess.py ============================================================================== --- (empty file) +++ py/dist/py/compat/testing/test_subprocess.py Tue Jan 9 17:55:49 2007 @@ -0,0 +1,563 @@ +import unittest +from test import test_support +from py.compat import subprocess +import sys +import signal +import os +import tempfile +import time +import re + +mswindows = (sys.platform == "win32") + +# +# Depends on the following external programs: Python +# + +if mswindows: + SETBINARY = ('import msvcrt; msvcrt.setmode(sys.stdout.fileno(), ' + 'os.O_BINARY);') +else: + SETBINARY = '' + +# In a debug build, stuff like "[6580 refs]" is printed to stderr at +# shutdown time. That frustrates tests trying to check stderr produced +# from a spawned Python process. +def remove_stderr_debug_decorations(stderr): + return re.sub(r"\[\d+ refs\]\r?\n?$", "", stderr) + +class ProcessTestCase(unittest.TestCase): + def mkstemp(self): + """wrapper for mkstemp, calling mktemp if mkstemp is not available""" + if hasattr(tempfile, "mkstemp"): + return tempfile.mkstemp() + else: + fname = tempfile.mktemp() + return os.open(fname, os.O_RDWR|os.O_CREAT), fname + + # + # Generic tests + # + def test_call_seq(self): + # call() function with sequence argument + rc = subprocess.call([sys.executable, "-c", + "import sys; sys.exit(47)"]) + self.assertEqual(rc, 47) + + def test_call_kwargs(self): + # call() function with keyword args + newenv = os.environ.copy() + newenv["FRUIT"] = "banana" + rc = subprocess.call([sys.executable, "-c", + 'import sys, os;' \ + 'sys.exit(os.getenv("FRUIT")=="banana")'], + env=newenv) + self.assertEqual(rc, 1) + + def test_stdin_none(self): + # .stdin is None when not redirected + p = subprocess.Popen([sys.executable, "-c", 'print "banana"'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + self.assertEqual(p.stdin, None) + + def test_stdout_none(self): + # .stdout is None when not redirected + p = subprocess.Popen([sys.executable, "-c", + 'print " this bit of output is from a ' + 'test of stdout in a different ' + 'process ..."'], + stdin=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + self.assertEqual(p.stdout, None) + + def test_stderr_none(self): + # .stderr is None when not redirected + p = subprocess.Popen([sys.executable, "-c", 'print "banana"'], + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + p.wait() + self.assertEqual(p.stderr, None) + + def test_executable(self): + p = subprocess.Popen(["somethingyoudonthave", + "-c", "import sys; sys.exit(47)"], + executable=sys.executable) + p.wait() + self.assertEqual(p.returncode, 47) + + def test_stdin_pipe(self): + # stdin redirection + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.exit(sys.stdin.read() == "pear")'], + stdin=subprocess.PIPE) + p.stdin.write("pear") + p.stdin.close() + p.wait() + self.assertEqual(p.returncode, 1) + + def test_stdin_filedes(self): + # stdin is set to open file descriptor + tf = tempfile.TemporaryFile() + d = tf.fileno() + os.write(d, "pear") + os.lseek(d, 0, 0) + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.exit(sys.stdin.read() == "pear")'], + stdin=d) + p.wait() + self.assertEqual(p.returncode, 1) + + def test_stdin_fileobj(self): + # stdin is set to open file object + tf = tempfile.TemporaryFile() + tf.write("pear") + tf.seek(0) + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.exit(sys.stdin.read() == "pear")'], + stdin=tf) + p.wait() + self.assertEqual(p.returncode, 1) + + def test_stdout_pipe(self): + # stdout redirection + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stdout.write("orange")'], + stdout=subprocess.PIPE) + self.assertEqual(p.stdout.read(), "orange") + + def test_stdout_filedes(self): + # stdout is set to open file descriptor + tf = tempfile.TemporaryFile() + d = tf.fileno() + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stdout.write("orange")'], + stdout=d) + p.wait() + os.lseek(d, 0, 0) + self.assertEqual(os.read(d, 1024), "orange") + + def test_stdout_fileobj(self): + # stdout is set to open file object + tf = tempfile.TemporaryFile() + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stdout.write("orange")'], + stdout=tf) + p.wait() + tf.seek(0) + self.assertEqual(tf.read(), "orange") + + def test_stderr_pipe(self): + # stderr redirection + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stderr.write("strawberry")'], + stderr=subprocess.PIPE) + self.assertEqual(remove_stderr_debug_decorations(p.stderr.read()), + "strawberry") + + def test_stderr_filedes(self): + # stderr is set to open file descriptor + tf = tempfile.TemporaryFile() + d = tf.fileno() + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stderr.write("strawberry")'], + stderr=d) + p.wait() + os.lseek(d, 0, 0) + self.assertEqual(remove_stderr_debug_decorations(os.read(d, 1024)), + "strawberry") + + def test_stderr_fileobj(self): + # stderr is set to open file object + tf = tempfile.TemporaryFile() + p = subprocess.Popen([sys.executable, "-c", + 'import sys; sys.stderr.write("strawberry")'], + stderr=tf) + p.wait() + tf.seek(0) + self.assertEqual(remove_stderr_debug_decorations(tf.read()), + "strawberry") + + def test_stdout_stderr_pipe(self): + # capture stdout and stderr to the same pipe + p = subprocess.Popen([sys.executable, "-c", + 'import sys;' \ + 'sys.stdout.write("apple");' \ + 'sys.stdout.flush();' \ + 'sys.stderr.write("orange")'], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + output = p.stdout.read() + stripped = remove_stderr_debug_decorations(output) + self.assertEqual(stripped, "appleorange") + + def test_stdout_stderr_file(self): + # capture stdout and stderr to the same open file + tf = tempfile.TemporaryFile() + p = subprocess.Popen([sys.executable, "-c", + 'import sys;' \ + 'sys.stdout.write("apple");' \ + 'sys.stdout.flush();' \ + 'sys.stderr.write("orange")'], + stdout=tf, + stderr=tf) + p.wait() + tf.seek(0) + output = tf.read() + stripped = remove_stderr_debug_decorations(output) + self.assertEqual(stripped, "appleorange") + + def test_cwd(self): + tmpdir = os.getenv("TEMP", "/tmp") + # We cannot use os.path.realpath to canonicalize the path, + # since it doesn't expand Tru64 {memb} strings. See bug 1063571. + cwd = os.getcwd() + os.chdir(tmpdir) + tmpdir = os.getcwd() + os.chdir(cwd) + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' \ + 'sys.stdout.write(os.getcwd())'], + stdout=subprocess.PIPE, + cwd=tmpdir) + normcase = os.path.normcase + self.assertEqual(normcase(p.stdout.read()), normcase(tmpdir)) + + def test_env(self): + newenv = os.environ.copy() + newenv["FRUIT"] = "orange" + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' \ + 'sys.stdout.write(os.getenv("FRUIT"))'], + stdout=subprocess.PIPE, + env=newenv) + self.assertEqual(p.stdout.read(), "orange") + + def test_communicate(self): + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' \ + 'sys.stderr.write("pineapple");' \ + 'sys.stdout.write(sys.stdin.read())'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (stdout, stderr) = p.communicate("banana") + self.assertEqual(stdout, "banana") + self.assertEqual(remove_stderr_debug_decorations(stderr), + "pineapple") + + def test_communicate_returns(self): + # communicate() should return None if no redirection is active + p = subprocess.Popen([sys.executable, "-c", + "import sys; sys.exit(47)"]) + (stdout, stderr) = p.communicate() + self.assertEqual(stdout, None) + self.assertEqual(stderr, None) + + def test_communicate_pipe_buf(self): + # communicate() with writes larger than pipe_buf + # This test will probably deadlock rather than fail, if + # communicate() does not work properly. + x, y = os.pipe() + if mswindows: + pipe_buf = 512 + else: + pipe_buf = os.fpathconf(x, "PC_PIPE_BUF") + os.close(x) + os.close(y) + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' + 'sys.stdout.write(sys.stdin.read(47));' \ + 'sys.stderr.write("xyz"*%d);' \ + 'sys.stdout.write(sys.stdin.read())' % pipe_buf], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + string_to_write = "abc"*pipe_buf + (stdout, stderr) = p.communicate(string_to_write) + self.assertEqual(stdout, string_to_write) + + def test_writes_before_communicate(self): + # stdin.write before communicate() + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' \ + 'sys.stdout.write(sys.stdin.read())'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + p.stdin.write("banana") + (stdout, stderr) = p.communicate("split") + self.assertEqual(stdout, "bananasplit") + self.assertEqual(remove_stderr_debug_decorations(stderr), "") + + def test_universal_newlines(self): + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' + SETBINARY + + 'sys.stdout.write("line1\\n");' + 'sys.stdout.flush();' + 'sys.stdout.write("line2\\r");' + 'sys.stdout.flush();' + 'sys.stdout.write("line3\\r\\n");' + 'sys.stdout.flush();' + 'sys.stdout.write("line4\\r");' + 'sys.stdout.flush();' + 'sys.stdout.write("\\nline5");' + 'sys.stdout.flush();' + 'sys.stdout.write("\\nline6");'], + stdout=subprocess.PIPE, + universal_newlines=1) + stdout = p.stdout.read() + if hasattr(open, 'newlines'): + # Interpreter with universal newline support + self.assertEqual(stdout, + "line1\nline2\nline3\nline4\nline5\nline6") + else: + # Interpreter without universal newline support + self.assertEqual(stdout, + "line1\nline2\rline3\r\nline4\r\nline5\nline6") + + def test_universal_newlines_communicate(self): + # universal newlines through communicate() + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' + SETBINARY + + 'sys.stdout.write("line1\\n");' + 'sys.stdout.flush();' + 'sys.stdout.write("line2\\r");' + 'sys.stdout.flush();' + 'sys.stdout.write("line3\\r\\n");' + 'sys.stdout.flush();' + 'sys.stdout.write("line4\\r");' + 'sys.stdout.flush();' + 'sys.stdout.write("\\nline5");' + 'sys.stdout.flush();' + 'sys.stdout.write("\\nline6");'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=1) + (stdout, stderr) = p.communicate() + if hasattr(open, 'newlines'): + # Interpreter with universal newline support + self.assertEqual(stdout, + "line1\nline2\nline3\nline4\nline5\nline6") + else: + # Interpreter without universal newline support + self.assertEqual(stdout, "line1\nline2\rline3\r\nline4\r\nline5\nline6") + + def test_no_leaking(self): + # Make sure we leak no resources + if test_support.is_resource_enabled("subprocess") and not mswindows: + max_handles = 1026 # too much for most UNIX systems + else: + max_handles = 65 + for i in range(max_handles): + p = subprocess.Popen([sys.executable, "-c", + "import sys;sys.stdout.write(sys.stdin.read())"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + data = p.communicate("lime")[0] + self.assertEqual(data, "lime") + + + def test_list2cmdline(self): + self.assertEqual(subprocess.list2cmdline(['a b c', 'd', 'e']), + '"a b c" d e') + self.assertEqual(subprocess.list2cmdline(['ab"c', '\\', 'd']), + 'ab\\"c \\ d') + self.assertEqual(subprocess.list2cmdline(['a\\\\\\b', 'de fg', 'h']), + 'a\\\\\\b "de fg" h') + self.assertEqual(subprocess.list2cmdline(['a\\"b', 'c', 'd']), + 'a\\\\\\"b c d') + self.assertEqual(subprocess.list2cmdline(['a\\\\b c', 'd', 'e']), + '"a\\\\b c" d e') + self.assertEqual(subprocess.list2cmdline(['a\\\\b\\ c', 'd', 'e']), + '"a\\\\b\\ c" d e') + + + def test_poll(self): + p = subprocess.Popen([sys.executable, + "-c", "import time; time.sleep(1)"]) + count = 0 + while p.poll() is None: + time.sleep(0.1) + count += 1 + # We expect that the poll loop probably went around about 10 times, + # but, based on system scheduling we can't control, it's possible + # poll() never returned None. It "should be" very rare that it + # didn't go around at least twice. + self.assert_(count >= 2) + # Subsequent invocations should just return the returncode + self.assertEqual(p.poll(), 0) + + + def test_wait(self): + p = subprocess.Popen([sys.executable, + "-c", "import time; time.sleep(2)"]) + self.assertEqual(p.wait(), 0) + # Subsequent invocations should just return the returncode + self.assertEqual(p.wait(), 0) + + + def test_invalid_bufsize(self): + # an invalid type of the bufsize argument should raise + # TypeError. + try: + subprocess.Popen([sys.executable, "-c", "pass"], "orange") + except TypeError: + pass + else: + self.fail("Expected TypeError") + + # + # POSIX tests + # + if not mswindows: + def test_exceptions(self): + # catched & re-raised exceptions + try: + p = subprocess.Popen([sys.executable, "-c", ""], + cwd="/this/path/does/not/exist") + except OSError, e: + # The attribute child_traceback should contain "os.chdir" + # somewhere. + self.assertNotEqual(e.child_traceback.find("os.chdir"), -1) + else: + self.fail("Expected OSError") + + def test_run_abort(self): + # returncode handles signal termination + p = subprocess.Popen([sys.executable, + "-c", "import os; os.abort()"]) + p.wait() + self.assertEqual(-p.returncode, signal.SIGABRT) + + def test_preexec(self): + # preexec function + p = subprocess.Popen([sys.executable, "-c", + 'import sys,os;' \ + 'sys.stdout.write(os.getenv("FRUIT"))'], + stdout=subprocess.PIPE, + preexec_fn=lambda: os.putenv("FRUIT", "apple")) + self.assertEqual(p.stdout.read(), "apple") + + def test_args_string(self): + # args is a string + f, fname = self.mkstemp() + os.write(f, "#!/bin/sh\n") + os.write(f, "exec %s -c 'import sys; sys.exit(47)'\n" % + sys.executable) + os.close(f) + os.chmod(fname, 0700) + p = subprocess.Popen(fname) + p.wait() + os.remove(fname) + self.assertEqual(p.returncode, 47) + + def test_invalid_args(self): + # invalid arguments should raise ValueError + self.assertRaises(ValueError, subprocess.call, + [sys.executable, + "-c", "import sys; sys.exit(47)"], + startupinfo=47) + self.assertRaises(ValueError, subprocess.call, + [sys.executable, + "-c", "import sys; sys.exit(47)"], + creationflags=47) + + def test_shell_sequence(self): + # Run command through the shell (sequence) + newenv = os.environ.copy() + newenv["FRUIT"] = "apple" + p = subprocess.Popen(["echo $FRUIT"], shell=1, + stdout=subprocess.PIPE, + env=newenv) + self.assertEqual(p.stdout.read().strip(), "apple") + + def test_shell_string(self): + # Run command through the shell (string) + newenv = os.environ.copy() + newenv["FRUIT"] = "apple" + p = subprocess.Popen("echo $FRUIT", shell=1, + stdout=subprocess.PIPE, + env=newenv) + self.assertEqual(p.stdout.read().strip(), "apple") + + def test_call_string(self): + # call() function with string argument on UNIX + f, fname = self.mkstemp() + os.write(f, "#!/bin/sh\n") + os.write(f, "exec %s -c 'import sys; sys.exit(47)'\n" % + sys.executable) + os.close(f) + os.chmod(fname, 0700) + rc = subprocess.call(fname) + os.remove(fname) + self.assertEqual(rc, 47) + + + # + # Windows tests + # + if mswindows: + def test_startupinfo(self): + # startupinfo argument + # We uses hardcoded constants, because we do not want to + # depend on win32all. + STARTF_USESHOWWINDOW = 1 + SW_MAXIMIZE = 3 + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags = STARTF_USESHOWWINDOW + startupinfo.wShowWindow = SW_MAXIMIZE + # Since Python is a console process, it won't be affected + # by wShowWindow, but the argument should be silently + # ignored + subprocess.call([sys.executable, "-c", "import sys; sys.exit(0)"], + startupinfo=startupinfo) + + def test_creationflags(self): + # creationflags argument + CREATE_NEW_CONSOLE = 16 + sys.stderr.write(" a DOS box should flash briefly ...\n") + subprocess.call(sys.executable + + ' -c "import time; time.sleep(0.25)"', + creationflags=CREATE_NEW_CONSOLE) + + def test_invalid_args(self): + # invalid arguments should raise ValueError + self.assertRaises(ValueError, subprocess.call, + [sys.executable, + "-c", "import sys; sys.exit(47)"], + preexec_fn=lambda: 1) + self.assertRaises(ValueError, subprocess.call, + [sys.executable, + "-c", "import sys; sys.exit(47)"], + close_fds=True) + + def test_shell_sequence(self): + # Run command through the shell (sequence) + newenv = os.environ.copy() + newenv["FRUIT"] = "physalis" + p = subprocess.Popen(["set"], shell=1, + stdout=subprocess.PIPE, + env=newenv) + self.assertNotEqual(p.stdout.read().find("physalis"), -1) + + def test_shell_string(self): + # Run command through the shell (string) + newenv = os.environ.copy() + newenv["FRUIT"] = "physalis" + p = subprocess.Popen("set", shell=1, + stdout=subprocess.PIPE, + env=newenv) + self.assertNotEqual(p.stdout.read().find("physalis"), -1) + + def test_call_string(self): + # call() function with string argument on Windows + rc = subprocess.call(sys.executable + + ' -c "import sys; sys.exit(47)"') + self.assertEqual(rc, 47) + + +def test_main(): + test_support.run_unittest(ProcessTestCase) + +if __name__ == "__main__": + test_main() Modified: py/dist/py/compat/testing/test_textwrap.py ============================================================================== --- py/dist/py/compat/testing/test_textwrap.py (original) +++ py/dist/py/compat/testing/test_textwrap.py Tue Jan 9 17:55:49 2007 @@ -5,7 +5,7 @@ # Converted to PyUnit by Peter Hansen . # Currently maintained by Greg Ward. # -# $Id: test_textwrap.py,v 1.27.4.1 2005/03/05 02:38:33 gward Exp $ +# $Id: test_textwrap.py 38573 2005-03-05 02:38:33Z gward $ # import unittest @@ -18,7 +18,6 @@ fill = textwrap.fill dedent = textwrap.dedent - class BaseTestCase(unittest.TestCase): '''Parent class with utility methods for textwrap tests.''' Modified: py/dist/py/compat/textwrap.py ============================================================================== --- py/dist/py/compat/textwrap.py (original) +++ py/dist/py/compat/textwrap.py Tue Jan 9 17:55:49 2007 @@ -161,7 +161,7 @@ else: i += 1 - def _handle_long_word(self, chunks, cur_line, cur_len, width): + def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width): """_handle_long_word(chunks : [string], cur_line : [string], cur_len : int, width : int) @@ -174,14 +174,14 @@ # If we're allowed to break long words, then do so: put as much # of the next chunk onto the current line as will fit. if self.break_long_words: - cur_line.append(chunks[0][0:space_left]) - chunks[0] = chunks[0][space_left:] + cur_line.append(reversed_chunks[-1][:space_left]) + reversed_chunks[-1] = reversed_chunks[-1][space_left:] # Otherwise, we have to preserve the long word intact. Only add # it to the current line if there's nothing already there -- # that minimizes how much we violate the width constraint. elif not cur_line: - cur_line.append(chunks.pop(0)) + cur_line.append(reversed_chunks.pop()) # If we're not allowed to break long words, and there's already # text on the current line, do nothing. Next time through the @@ -206,6 +206,10 @@ if self.width <= 0: raise ValueError("invalid width %r (must be > 0)" % self.width) + # Arrange in reverse order so items can be efficiently popped + # from a stack of chucks. + chunks.reverse() + while chunks: # Start the list of chunks that will make up the current line. @@ -224,15 +228,15 @@ # First chunk on line is whitespace -- drop it, unless this # is the very beginning of the text (ie. no lines started yet). - if chunks[0].strip() == '' and lines: - del chunks[0] + if chunks[-1].strip() == '' and lines: + del chunks[-1] while chunks: - l = len(chunks[0]) + l = len(chunks[-1]) # Can at least squeeze this chunk onto the current line. if cur_len + l <= width: - cur_line.append(chunks.pop(0)) + cur_line.append(chunks.pop()) cur_len += l # Nope, this line is full. @@ -241,7 +245,7 @@ # The current line is full, and the next chunk is too big to # fit on *any* line (not just this one). - if chunks and len(chunks[0]) > width: + if chunks and len(chunks[-1]) > width: self._handle_long_word(chunks, cur_line, cur_len, width) # If the last chunk on this line is all whitespace, drop it. @@ -268,7 +272,6 @@ converted to space. """ text = self._munge_whitespace(text) - indent = self.initial_indent chunks = self._split(text) if self.fix_sentence_endings: self._fix_sentence_endings(chunks) From cfbolz at codespeak.net Tue Jan 9 18:04:56 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 18:04:56 +0100 (CET) Subject: [py-svn] r36380 - in py/dist/py: path/svn rest Message-ID: <20070109170456.93B7F1007D@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 18:04:55 2007 New Revision: 36380 Modified: py/dist/py/path/svn/wccommand.py py/dist/py/rest/rst.py Log: use our own version of enumerate Modified: py/dist/py/path/svn/wccommand.py ============================================================================== --- py/dist/py/path/svn/wccommand.py (original) +++ py/dist/py/path/svn/wccommand.py Tue Jan 9 18:04:55 2007 @@ -314,7 +314,8 @@ result = [] blamelines = out.splitlines() reallines = py.path.svnurl(self.url).readlines() - for i, (blameline, line) in enumerate(zip(blamelines, reallines)): + for i, (blameline, line) in py.builtin.enumerate( + zip(blamelines, reallines)): m = rex_blame.match(blameline) if not m: raise ValueError("output line %r of svn blame does not match " Modified: py/dist/py/rest/rst.py ============================================================================== --- py/dist/py/rest/rst.py (original) +++ py/dist/py/rest/rst.py Tue Jan 9 18:04:55 2007 @@ -156,7 +156,7 @@ def __init__(self, *args, **kwargs): # make shortcut args = list(args) - for num, arg in enumerate(args): + for num, arg in py.builtin.enumerate(args): if isinstance(arg, str): args[num] = Text(arg) super(Paragraph, self).__init__(*args, **kwargs) @@ -247,7 +247,7 @@ def text(self): text = self.escape(self._text).split('\n') - for i, line in enumerate(text): + for i, line in py.builtin.enumerate(text): if line.strip(): text[i] = ' %s' % (line,) return self.start + '\n'.join(text) From cfbolz at codespeak.net Tue Jan 9 18:19:09 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 9 Jan 2007 18:19:09 +0100 (CET) Subject: [py-svn] r36381 - py/dist/py/documentation Message-ID: <20070109171909.D627910083@code0.codespeak.net> Author: cfbolz Date: Tue Jan 9 18:19:07 2007 New Revision: 36381 Modified: py/dist/py/documentation/TODO.txt py/dist/py/documentation/misc.txt Log: check in documentation about py.compat and py.builtin Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Tue Jan 9 18:19:07 2007 @@ -14,10 +14,6 @@ py.compat ------------- -* (cfbolz) needs py/documentation/compat.txt - -* (cfbolz) reimport/check python 2.4.4 modules - review all py lib documentation ------------------------------------- Modified: py/dist/py/documentation/misc.txt ============================================================================== --- py/dist/py/documentation/misc.txt (original) +++ py/dist/py/documentation/misc.txt Tue Jan 9 18:19:07 2007 @@ -169,3 +169,39 @@ return p binsvn = py.path.local.sysfind('svn', checker=mysvn) + + +Python version compatibility helpers +===================================== + +The py-lib contains some helpers that make writing scripts that work on various +Python versions easier. + +``py.compat`` +------------- + +``py.compat`` provides fixed versions (currently from Python 2.4.4) of various +newer modules to be able to use them in various Python versions. Currently these +are: + + * doctest + * optparse + * subprocess + * textwrap + +They are used by replacing the normal ``import ...`` byr +``from py.compat import ...``. + +``py.builtin`` +-------------- + +``py.builtin`` provides various builtins that were added in later Python +versions. If the used Python version used does not provide these builtins, they +are pure-Python reimplementations. These currently are: + + * enumerate + * reversed + * sorted + * BaseException + +``py.builtin.BaseException`` is just ``Exception`` before Python 2.5. From hpk at codespeak.net Tue Jan 9 19:16:32 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 19:16:32 +0100 (CET) Subject: [py-svn] r36386 - in py/dist/py/test: . testing Message-ID: <20070109181632.AD1A610078@code0.codespeak.net> Author: hpk Date: Tue Jan 9 19:16:26 2007 New Revision: 36386 Modified: py/dist/py/test/collect.py py/dist/py/test/testing/test_collect.py Log: now the collection of generative tests follows the setup/teardown procedures (setup_module and friends) which fixes a problem reported by Pierre Rouleau Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Tue Jan 9 19:16:26 2007 @@ -206,9 +206,8 @@ def tryiter(self, yieldtype=None, reporterror=None, keyword=None): """ yield stop item instances from flattening the collector. XXX deprecated: this way of iteration is not safe in all - cases. Mostly fixed, need to introduce skipped-by-keyword + cases. """ - if yieldtype is None: yieldtype = py.test.Item if isinstance(self, yieldtype): @@ -225,7 +224,8 @@ if reporterror is not None: reporterror((None, self)) for x in self.run(): - for y in self.join(x).tryiter(yieldtype, reporterror, keyword): + for y in self.join(x).tryiter(yieldtype, + reporterror, keyword): yield y except KeyboardInterrupt: raise @@ -459,12 +459,14 @@ def buildname2items(self): d = {} + # slightly hackish to invoke setup-states on + # collection ... + self.Function.state.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 - #XXX name = "%s(%r)" %(call.__name__, str(args and args[0] or i)) # # XXX d[name] = self.Function(name, self, args, obj=call, sort_value = i) return d Modified: py/dist/py/test/testing/test_collect.py ============================================================================== --- py/dist/py/test/testing/test_collect.py (original) +++ py/dist/py/test/testing/test_collect.py Tue Jan 9 19:16:26 2007 @@ -366,7 +366,7 @@ l = [] list(col.tryiter(reporterror=l.append)) assert len(l) == 2 - excinfo, item = l[1] + excinfo, item = l[-1] assert isinstance(excinfo, py.code.ExceptionInfo) def test_tryiter_handles_keyboardinterrupt(): @@ -391,7 +391,6 @@ assert col != fn def test_check_generator_collect_problems(): - py.test.skip("Fails") tmp = py.test.ensuretemp("gener_coll") tmp.ensure("test_one.py").write(py.code.Source(""" def setup_module(mod): @@ -408,7 +407,7 @@ col = py.test.collect.Module(tmp.join("test_one.py")) errors = [] l = list(col.tryiter(reporterror=errors.append)) - assert len(errors) == 0 + assert len(errors) == 2 def test_check_collect_hashes(): tmp = py.test.ensuretemp("check_collect_hashes") From hpk at codespeak.net Tue Jan 9 19:18:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 9 Jan 2007 19:18:05 +0100 (CET) Subject: [py-svn] r36387 - in py/dist/py: . misc/testing Message-ID: <20070109181805.CA8BC1007A@code0.codespeak.net> Author: hpk Date: Tue Jan 9 19:18:04 2007 New Revision: 36387 Modified: py/dist/py/initpkg.py py/dist/py/misc/testing/test_api.py Log: forget about trying to fix __module__ and __name__ of exported classes for now. Modified: py/dist/py/initpkg.py ============================================================================== --- py/dist/py/initpkg.py (original) +++ py/dist/py/initpkg.py Tue Jan 9 19:18:04 2007 @@ -182,10 +182,10 @@ result = self.__package__._resolve(extpy) del self.__map__[name] setattr(self, name, result) - self._fixinspection(result, name) + #self._fixinspection(result, name) return result - def _fixinspection(self, result, name): + def _deprecated_fixinspection(self, result, name): # modify some attrs to make a class appear at export level if hasattr(result, '__module__'): if not result.__module__.startswith('py.__'): Modified: py/dist/py/misc/testing/test_api.py ============================================================================== --- py/dist/py/misc/testing/test_api.py (original) +++ py/dist/py/misc/testing/test_api.py Tue Jan 9 19:18:04 2007 @@ -14,7 +14,6 @@ assert_class('py.path', 'checker') def test_magic_entrypoints(self): - assert_class('py.magic', 'View') assert_function('py.magic', 'invoke') assert_function('py.magic', 'revoke') assert_function('py.magic', 'patch') @@ -31,10 +30,14 @@ def assert_class(modpath, name): mod = __import__(modpath, None, None, [name]) obj = getattr(mod, name) - fullpath = modpath + '.' + name - assert obj.__module__ == modpath - if sys.version_info >= (2,3): - assert obj.__name__ == name + assert inspect.isclass(obj) + + # we don't test anymore that the exported classes have + # the exported module path and name on them. + #fullpath = modpath + '.' + name + #assert obj.__module__ == modpath + #if sys.version_info >= (2,3): + # assert obj.__name__ == name def assert_function(modpath, name): mod = __import__(modpath, None, None, [name]) From guido at codespeak.net Wed Jan 10 19:14:03 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 10 Jan 2007 19:14:03 +0100 (CET) Subject: [py-svn] r36426 - py/dist/py/apigen/source Message-ID: <20070110181403.169B61007E@code0.codespeak.net> Author: guido Date: Wed Jan 10 19:14:01 2007 New Revision: 36426 Modified: py/dist/py/apigen/source/html.py Log: Added fallback monospace font for Safari. Modified: py/dist/py/apigen/source/html.py ============================================================================== --- py/dist/py/apigen/source/html.py (original) +++ py/dist/py/apigen/source/html.py Wed Jan 10 19:14:01 2007 @@ -58,7 +58,7 @@ body, td { background-color: #FFF; color: black; - font-family: monospace; + font-family: monospace, Monaco; } table, tr { @@ -168,7 +168,7 @@ body, p, td { background-color: #FFF; color: black; - font-family: monospace; + font-family: monospace, Monaco; } td.type { From fijal at codespeak.net Wed Jan 10 19:44:15 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 10 Jan 2007 19:44:15 +0100 (CET) Subject: [py-svn] r36430 - in py/dist/py/test/rsession: . webdata Message-ID: <20070110184415.4333710078@code0.codespeak.net> Author: fijal Date: Wed Jan 10 19:44:11 2007 New Revision: 36430 Modified: py/dist/py/test/rsession/webdata/source.js py/dist/py/test/rsession/webjs.py Log: Add rsyncing info, right now without any progress bar. Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/dist/py/test/rsession/webjs.py ============================================================================== --- py/dist/py/test/rsession/webjs.py (original) +++ py/dist/py/test/rsession/webjs.py Wed Jan 10 19:44:11 2007 @@ -190,6 +190,8 @@ td.appendChild(txt) tr.appendChild(td) module_part.appendChild(tr) + elif msg['type'] == 'RsyncFinished': + glob.rsync_done = True if glob.data_empty: mbox = dom.document.getElementById('messagebox') mbox.parentNode.scrollIntoView() @@ -248,7 +250,19 @@ elem.removeChild(elem.childNodes[0]) elem.style.visibility = "hidden" glob.host = "" - + +def update_rsync(): + elem = dom.document.getElementById("Tests") + if glob.rsync_done is True: + elem.childNodes[0].nodeValue = "Tests" + return + text = "Rsyncing" + '.' * glob.rsync_dots + glob.rsync_dots += 1 + if glob.rsync_dots > 5: + glob.rsync_dots = 0 + elem.childNodes[0].nodeValue = "Tests [%s]" % text + dom.setTimeout(update_rsync, 1000) + def host_init(host_dict): tbody = dom.document.getElementById("hostsbody") for host in host_dict.keys(): @@ -262,6 +276,9 @@ tr.appendChild(td) td.setAttribute("onmouseover", "show_host('%s')" % host) td.setAttribute("onmouseout", "hide_host()") + glob.rsync_dots = 0 + glob.rsync_done = False + dom.setTimeout(update_rsync, 1000) glob.host_dict = host_dict glob.host_pending = {} for key in host_dict.keys(): From fijal at codespeak.net Wed Jan 10 19:55:02 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 10 Jan 2007 19:55:02 +0100 (CET) Subject: [py-svn] r36431 - in py/dist/py/test/rsession: . testing Message-ID: <20070110185502.EBCC510078@code0.codespeak.net> Author: fijal Date: Wed Jan 10 19:55:01 2007 New Revision: 36431 Modified: py/dist/py/test/rsession/reporter.py py/dist/py/test/rsession/testing/test_reporter.py Log: Added information about hosts which needs to be rsynced (and a test :) Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Wed Jan 10 19:55:01 2007 @@ -64,10 +64,16 @@ item.remoterootpath) def report_HostReady(self, item): - print "%10s: READY" % item.hostname[:10] + self.hosts_to_rsync -= 1 + if self.hosts_to_rsync: + print "%10s: READY (still %d to go)" % (item.hostname[:10], + self.hosts_to_rsync) + else: + print "%10s: READY" % item.hostname[:10] def report_TestStarted(self, item): txt = " Test started, hosts: %s " % ", ".join(item.hosts) + self.hosts_to_rsync = len(item.hosts) self.out.sep("=", txt) self.timestart = item.timestart Modified: py/dist/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/dist/py/test/rsession/testing/test_reporter.py (original) +++ py/dist/py/test/rsession/testing/test_reporter.py Wed Jan 10 19:55:01 2007 @@ -133,6 +133,26 @@ sys.stdout = stdoutcopy assert s.getvalue().find("NameError: name 'sadsadsa' is not defined") != -1 + def _test_still_to_go(self): + tmpdir = py.test.ensuretemp("stilltogo") + tmpdir.ensure("__init__.py") + s = StringIO() + stdoutcopy = sys.stdout + sys.stdout = s + config, args = py.test.Config.parse([str(tmpdir)]) + hosts = ["host1", "host2", "host3"] + r = self.reporter(config, hosts) + r.report(report.TestStarted(hosts)) + r.report(report.HostReady("host1", "host1")) + r.report(report.HostReady("host2", "host2")) + r.report(report.HostReady("host3", "host3")) + sys.stdout = stdoutcopy + expected = """================= Test started, hosts: host1, host2, host3 ================== + host1: READY (still 2 to go) + host2: READY (still 1 to go) + host3: READY""" + assert s.getvalue().startswith(expected) + class TestLocalReporter(AbstractTestReporter): reporter = LocalReporter @@ -154,6 +174,9 @@ class TestRemoteReporter(AbstractTestReporter): reporter = RemoteReporter + 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 = """ localhost: FAILED py test rsession testing test_slave.py funcpass From fijal at codespeak.net Wed Jan 10 20:09:27 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 10 Jan 2007 20:09:27 +0100 (CET) Subject: [py-svn] r36433 - py/dist/py/test/rsession Message-ID: <20070110190927.A44E110080@code0.codespeak.net> Author: fijal Date: Wed Jan 10 20:09:26 2007 New Revision: 36433 Modified: py/dist/py/test/rsession/rsync.py Log: typo Modified: py/dist/py/test/rsession/rsync.py ============================================================================== --- py/dist/py/test/rsession/rsync.py (original) +++ py/dist/py/test/rsession/rsync.py Wed Jan 10 20:09:26 2007 @@ -47,7 +47,7 @@ # too early! we must have got an error channel.waitclose() # or else we raise one - raise IOError('connexion unexpectedly closed: %s ' % ( + raise IOError('connection unexpectedly closed: %s ' % ( channel.gateway,)) else: modified_rel_path, checksum = req From fijal at codespeak.net Wed Jan 10 21:50:43 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 10 Jan 2007 21:50:43 +0100 (CET) Subject: [py-svn] r36435 - py/dist/py/test/rsession/testing Message-ID: <20070110205043.7B2E610078@code0.codespeak.net> Author: fijal Date: Wed Jan 10 21:50:41 2007 New Revision: 36435 Modified: py/dist/py/test/rsession/testing/test_webjs.py Log: Fix the test to work in case of LSession as well Modified: py/dist/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/dist/py/test/rsession/testing/test_webjs.py (original) +++ py/dist/py/test/rsession/testing/test_webjs.py Wed Jan 10 21:50:41 2007 @@ -10,6 +10,7 @@ def setup_module(mod): # load HTML into window object html = here.join('../webdata/index.html').read() + mod.html = html from pypy.translator.js.modules import dom mod.dom = dom dom.window = dom.Window(html) @@ -23,6 +24,10 @@ mod.webjs = webjs mod.exported_methods = exported_methods +def setup_function(f): + dom.window = dom.Window(html) + dom.document = dom.window.document + def test_html_loaded(): body = dom.window.document.getElementsByTagName('body')[0] assert len(body.childNodes) > 0 @@ -121,7 +126,7 @@ webjs.process(msg) schedule_callbacks(exported_methods) # ouch - assert dom.document.getElementById('modules/foo.py').childNodes[1].\ + assert dom.document.getElementById('modules/foo.py').childNodes[0].\ childNodes[0].childNodes[0].childNodes[0].nodeValue == 'F' # XXX: Write down test for full run From fijal at codespeak.net Wed Jan 10 22:39:43 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 10 Jan 2007 22:39:43 +0100 (CET) Subject: [py-svn] r36441 - py/dist/py/test/rsession Message-ID: <20070110213943.F3BD31007C@code0.codespeak.net> Author: fijal Date: Wed Jan 10 22:39:39 2007 New Revision: 36441 Added: py/dist/py/test/rsession/rsync_remote.py Modified: py/dist/py/test/rsession/rsync.py Log: Move remote part of rsync to separate file. Modified: py/dist/py/test/rsession/rsync.py ============================================================================== --- py/dist/py/test/rsession/rsync.py (original) +++ py/dist/py/test/rsession/rsync.py Wed Jan 10 22:39:39 2007 @@ -6,7 +6,7 @@ def __init__(self, **options): for name in options: - assert name in ('delete',) + assert name in ('delete','callback',) self.options = options self.channels = {} self.receivequeue = Queue() @@ -32,16 +32,7 @@ # send modified file to clients while self.channels: channel, req = self.receivequeue.get() - if req == 42: - for link in self.links: - channel.send(link) - # completion marker, this host is done - channel.send(42) - elif req == 41: - finishedcallback = self.channels.pop(channel) - if finishedcallback: - finishedcallback() - elif req is None: + if req is None: # end-of-channel if channel in self.channels: # too early! we must have got an error @@ -50,21 +41,36 @@ raise IOError('connection unexpectedly closed: %s ' % ( channel.gateway,)) else: - modified_rel_path, checksum = req - modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) - f = open(modifiedpath, 'rb') - data = f.read() - f.close() - if checksum is not None and checksum == md5.md5(data).digest(): - data = None # not really modified + command, data = req + if command == "links": + for link in self.links: + channel.send(link) + # completion marker, this host is done + channel.send(42) + elif command == "done": + finishedcallback = self.channels.pop(channel) + if finishedcallback: + finishedcallback() + elif command == "ack": + pass + elif command == "send": + modified_rel_path, checksum = data + modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) + f = open(modifiedpath, 'rb') + data = f.read() + f.close() + if checksum is not None and checksum == md5.md5(data).digest(): + data = None # not really modified + else: + # ! there is a reason for the interning: + # sharing multiple copies of the file's data + data = intern(data) + #print channel.gateway.getremoteaddress(), + #print '/'.join(modified_rel_path) + channel.send(data) + del data else: - # ! there is a reason for the interning: - # sharing multiple copies of the file's data - data = intern(data) - #print channel.gateway.getremoteaddress(), - #print '/'.join(modified_rel_path) - channel.send(data) - del data + assert "Unknown command %s" % command def _broadcast(self, msg): for channel in self.channels: @@ -104,87 +110,6 @@ else: raise ValueError, "cannot sync %r" % (path,) +REMOTE_SOURCE = py.path.local(__file__).dirpath().\ + join('rsync_remote.py').open().read() -REMOTE_SOURCE = """ - import os, stat, shutil, md5 - destdir, options = channel.receive() - modifiedfiles = [] - - def remove(path): - assert path.startswith(destdir) - try: - os.unlink(path) - except OSError: - # assume it's a dir - shutil.rmtree(path) - - def receive_directory_structure(path, relcomponents): - try: - st = os.lstat(path) - except OSError: - st = None - msg = channel.receive() - if isinstance(msg, list): - if st and not stat.S_ISDIR(st.st_mode): - os.unlink(path) - st = None - if not st: - os.makedirs(path) - entrynames = {} - for entryname in msg: - receive_directory_structure(os.path.join(path, entryname), - relcomponents + [entryname]) - entrynames[entryname] = True - if options.get('delete'): - for othername in os.listdir(path): - if othername not in entrynames: - otherpath = os.path.join(path, othername) - remove(otherpath) - elif msg is not None: - checksum = None - if st: - if stat.S_ISREG(st.st_mode): - msg_mtime, msg_size = msg - if msg_size != st.st_size: - pass - elif msg_mtime != st.st_mtime: - f = open(path, 'rb') - checksum = md5.md5(f.read()).digest() - f.close() - else: - return # already fine - else: - remove(path) - channel.send((relcomponents, checksum)) - modifiedfiles.append((path, msg)) - receive_directory_structure(destdir, []) - - STRICT_CHECK = False # seems most useful this way for py.test - - for path, (time, size) in modifiedfiles: - data = channel.receive() - if data is not None: - if STRICT_CHECK and len(data) != size: - raise IOError('file modified during rsync: %r' % (path,)) - f = open(path, 'wb') - f.write(data) - f.close() - os.utime(path, (time, time)) - del data - channel.send(42) - - msg = channel.receive() - while msg is not 42: - # we get symlink - _type, relpath, linkpoint = msg - assert _type == "link" - path = os.path.join(destdir, relpath) - try: - os.unlink(path) - except OSError: - pass - - os.symlink(os.path.join(destdir, linkpoint), path) - msg = channel.receive() - channel.send(41) -""" Added: py/dist/py/test/rsession/rsync_remote.py ============================================================================== --- (empty file) +++ py/dist/py/test/rsession/rsync_remote.py Wed Jan 10 22:39:39 2007 @@ -0,0 +1,82 @@ +import os, stat, shutil, md5 +destdir, options = channel.receive() +modifiedfiles = [] + +def remove(path): + assert path.startswith(destdir) + try: + os.unlink(path) + except OSError: + # assume it's a dir + shutil.rmtree(path) + +def receive_directory_structure(path, relcomponents): + try: + st = os.lstat(path) + except OSError: + st = None + msg = channel.receive() + if isinstance(msg, list): + if st and not stat.S_ISDIR(st.st_mode): + os.unlink(path) + st = None + if not st: + os.makedirs(path) + entrynames = {} + for entryname in msg: + receive_directory_structure(os.path.join(path, entryname), + relcomponents + [entryname]) + entrynames[entryname] = True + if options.get('delete'): + for othername in os.listdir(path): + if othername not in entrynames: + otherpath = os.path.join(path, othername) + remove(otherpath) + elif msg is not None: + checksum = None + if st: + if stat.S_ISREG(st.st_mode): + msg_mtime, msg_size = msg + if msg_size != st.st_size: + pass + elif msg_mtime != st.st_mtime: + f = open(path, 'rb') + checksum = md5.md5(f.read()).digest() + f.close() + else: + return # already fine + else: + remove(path) + channel.send(("send", (relcomponents, checksum))) + modifiedfiles.append((path, msg)) +receive_directory_structure(destdir, []) + +STRICT_CHECK = False # seems most useful this way for py.test + +for path, (time, size) in modifiedfiles: + data = channel.receive() + channel.send(("ack", path)) + if data is not None: + if STRICT_CHECK and len(data) != size: + raise IOError('file modified during rsync: %r' % (path,)) + f = open(path, 'wb') + f.write(data) + f.close() + os.utime(path, (time, time)) + del data +channel.send(("links", None)) + +msg = channel.receive() +while msg is not 42: + # we get symlink + _type, relpath, linkpoint = msg + assert _type == "link" + path = os.path.join(destdir, relpath) + try: + os.unlink(path) + except OSError: + pass + + os.symlink(os.path.join(destdir, linkpoint), path) + msg = channel.receive() +channel.send(("done", None)) From fijal at codespeak.net Wed Jan 10 23:13:29 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 10 Jan 2007 23:13:29 +0100 (CET) Subject: [py-svn] r36442 - in py/dist/py/test/rsession: . testing Message-ID: <20070110221329.2408C1007C@code0.codespeak.net> Author: fijal Date: Wed Jan 10 23:13:24 2007 New Revision: 36442 Modified: py/dist/py/test/rsession/rsync.py py/dist/py/test/rsession/rsync_remote.py py/dist/py/test/rsession/testing/test_rsync.py Log: Add support for callback which tells a bit about progress Modified: py/dist/py/test/rsession/rsync.py ============================================================================== --- py/dist/py/test/rsession/rsync.py (original) +++ py/dist/py/test/rsession/rsync.py Wed Jan 10 23:13:24 2007 @@ -4,10 +4,11 @@ class RSync(object): - def __init__(self, **options): + def __init__(self, callback=None, **options): for name in options: - assert name in ('delete','callback',) + assert name in ('delete') self.options = options + self.callback = callback self.channels = {} self.receivequeue = Queue() self.links = [] @@ -29,6 +30,8 @@ self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x')) # send directory structure and file timestamps/sizes self._send_directory_structure(self.sourcedir) + self.paths = {} + self.to_send = {} # send modified file to clients while self.channels: channel, req = self.receivequeue.get() @@ -52,12 +55,23 @@ if finishedcallback: finishedcallback() elif command == "ack": - pass + if self.callback: + self.callback("ack", self.paths[data], channel) + elif command == "list_done": + # sum up all to send + if self.callback: + s = sum([self.paths[i] for i in self.to_send[channel]]) + self.callback("list", s, channel) elif command == "send": modified_rel_path, checksum = data modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) f = open(modifiedpath, 'rb') data = f.read() + modified_rel_path = "/".join(modified_rel_path) + self.paths[modified_rel_path] = len(data) + if channel not in self.to_send: + self.to_send[channel] = [] + self.to_send[channel].append(modified_rel_path) f.close() if checksum is not None and checksum == md5.md5(data).digest(): data = None # not really modified Modified: py/dist/py/test/rsession/rsync_remote.py ============================================================================== --- py/dist/py/test/rsession/rsync_remote.py (original) +++ py/dist/py/test/rsession/rsync_remote.py Wed Jan 10 23:13:24 2007 @@ -52,10 +52,11 @@ receive_directory_structure(destdir, []) STRICT_CHECK = False # seems most useful this way for py.test +channel.send(("list_done", None)) for path, (time, size) in modifiedfiles: data = channel.receive() - channel.send(("ack", path)) + channel.send(("ack", path[len(destdir) + 1:])) if data is not None: if STRICT_CHECK and len(data) != size: raise IOError('file modified during rsync: %r' % (path,)) Modified: py/dist/py/test/rsession/testing/test_rsync.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsync.py (original) +++ py/dist/py/test/rsession/testing/test_rsync.py Wed Jan 10 23:13:24 2007 @@ -60,3 +60,21 @@ assert dest.join('rellink').readlink() == dest.join("existant") assert dest.join('abslink').readlink() == dest.join("existant") + +def test_callback(): + base = py.test.ensuretemp('callback') + dest = base.join("dest") + source = base.join("source") + source.ensure("existant").write("a" * 100) + source.ensure("existant2").write("a" * 10) + total = {} + def callback(cmd, lgt, channel): + total[(cmd, lgt)] = True + + rsync = RSync(callback=callback) + #rsync = RSync() + rsync.add_target(gw, dest) + rsync.send(source) + + assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True} + From hpk at codespeak.net Thu Jan 11 08:03:51 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 11 Jan 2007 08:03:51 +0100 (CET) Subject: [py-svn] r36447 - py/dist/py/test/rsession Message-ID: <20070111070351.2266310080@code0.codespeak.net> Author: hpk Date: Thu Jan 11 08:03:49 2007 New Revision: 36447 Modified: py/dist/py/test/rsession/rsync.py Log: added some comments to fijals progress-callback addition (not sure introducing callbacks here is worth it and may not have bad side effects such as keeping channels alive longer than neccessary). Modified: py/dist/py/test/rsession/rsync.py ============================================================================== --- py/dist/py/test/rsession/rsync.py (original) +++ py/dist/py/test/rsession/rsync.py Thu Jan 11 08:03:49 2007 @@ -30,8 +30,12 @@ self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x')) # send directory structure and file timestamps/sizes self._send_directory_structure(self.sourcedir) + + # paths and to_send are only used for doing + # progress-related callbacks self.paths = {} self.to_send = {} + # send modified file to clients while self.channels: channel, req = self.receivequeue.get() @@ -67,11 +71,14 @@ modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) f = open(modifiedpath, 'rb') data = f.read() + + # provide info to progress callback function modified_rel_path = "/".join(modified_rel_path) self.paths[modified_rel_path] = len(data) if channel not in self.to_send: self.to_send[channel] = [] self.to_send[channel].append(modified_rel_path) + f.close() if checksum is not None and checksum == md5.md5(data).digest(): data = None # not really modified From fijal at codespeak.net Thu Jan 11 13:05:37 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 13:05:37 +0100 (CET) Subject: [py-svn] r36469 - py/dist/py/documentation/future Message-ID: <20070111120537.B83B510098@code0.codespeak.net> Author: fijal Date: Thu Jan 11 13:05:36 2007 New Revision: 36469 Added: py/dist/py/documentation/future/rsession_todo.txt Log: Add a file which describes what I plan to do in RSession Added: py/dist/py/documentation/future/rsession_todo.txt ============================================================================== --- (empty file) +++ py/dist/py/documentation/future/rsession_todo.txt Thu Jan 11 13:05:36 2007 @@ -0,0 +1,11 @@ +Various tasks which needs to be done at some point +================================================== + +* Write down pinging interface, so we'll know if hosts are responding or + are mostly down (detecting hanging nodes) + +* Write down support for rsync progress + +* Discovery of nodes which are available for accepting distributed testing + + \ No newline at end of file From fijal at codespeak.net Thu Jan 11 13:07:26 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 13:07:26 +0100 (CET) Subject: [py-svn] r36470 - py/dist/py/documentation/future Message-ID: <20070111120726.8ADD01009A@code0.codespeak.net> Author: fijal Date: Thu Jan 11 13:07:25 2007 New Revision: 36470 Modified: py/dist/py/documentation/future/rsession_todo.txt Log: another point Modified: py/dist/py/documentation/future/rsession_todo.txt ============================================================================== --- py/dist/py/documentation/future/rsession_todo.txt (original) +++ py/dist/py/documentation/future/rsession_todo.txt Thu Jan 11 13:07:25 2007 @@ -8,4 +8,5 @@ * Discovery of nodes which are available for accepting distributed testing - \ No newline at end of file +* Test the tests rescheduling, so high-latency nodes would not take part + in that. \ No newline at end of file From guido at codespeak.net Thu Jan 11 13:57:09 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 11 Jan 2007 13:57:09 +0100 (CET) Subject: [py-svn] r36476 - in py/dist/py/apigen/source: . testing Message-ID: <20070111125709.EE35710088@code0.codespeak.net> Author: guido Date: Thu Jan 11 13:57:08 2007 New Revision: 36476 Added: py/dist/py/apigen/source/linker.py py/dist/py/apigen/source/testing/test_linker.py Log: (hpk, guido) class to provide lazy linking between html documents Added: py/dist/py/apigen/source/linker.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/source/linker.py Thu Jan 11 13:57:08 2007 @@ -0,0 +1,36 @@ +import py +html = py.xml.html + +def getrelfspath(dotted_name): + # XXX need to make sure its imported on non-py lib + return eval(dotted_name, {"py": py}) + +class LazyHref(object): + def __init__(self, linker, linkid): + self._linker = linker + self._linkid = linkid + + def __unicode__(self): + return unicode(self._linker.get_target(self._linkid)) + +class Linker(object): + def __init__(self): + self.root = None + self._linkid2target = {} + + def set_root(self, root): + assert self.root is None + self.root = root + + def repr_link(self, linkid, linkcontent=None): + if linkcontent is None: + linkcontent = linkid + return html.a(linkcontent, href=LazyHref(self, linkid)) + + def set_link(self, linkid, target): + assert linkid not in self._linkid2target + self._linkid2target[linkid] = target + + def get_target(self, linkid): + return '%s/%s' % (self.root, self._linkid2target[linkid]) + Added: py/dist/py/apigen/source/testing/test_linker.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/source/testing/test_linker.py Thu Jan 11 13:57:08 2007 @@ -0,0 +1,75 @@ +import py +html = py.xml.html +from py.__.apigen.source.linker import Linker, getrelfspath + +class TestLinker(object): + def test_one_lazy_link(self): + linker = Linker() + linker.set_root('/root') + tag = linker.repr_link('py.path.local') + linker.set_link('py.path.local', 'py/path/local.html') + resolved = tag.unicode() + assert (resolved == + 'py.path.local') + + def test_lazy_link_different_text(self): + linker = Linker() + linker.set_root('/root') + content = html.em('foo') + tag = linker.repr_link('py.path.local', content) + linker.set_link('py.path.local', 'py/path/local.html') + resolved = tag.unicode(indent=0) + assert (resolved == + 'foo') + + def test_source_link_getrelfspath(self): + linker = Linker() + linker.set_root('/root') + + relfspath = getrelfspath('py.path.local') + tag = linker.repr_link(relfspath, "sourcelink") + linker.set_link(relfspath, "hello/world") + resolved = tag.unicode(indent=0) + assert (resolved == + 'sourcelink') + +# considerations for futurue use +""" +snippet = create_method_html('foo.bar', dsa, linker) +snippet.unicode() # links get resolved here? + +def create_method_html(dotted_name, dsa, linker): + ... + # + a = linker.make_source_link(dotted_name) + ... + +def create_source_code_view(srcpath, linker): + ... + # dirpath() + anchor = linker.provide_anchor(dotted_name) + +def convert_srcpath_to_htmlpath(srcpath): + return 'source/%s.html' % (srcpath,) + +def write_sources(root, linker): + for fpath in root.visit(fil='*.py'): + relpath = fpath.relto(root) + htmlpath = convert_srcpath_to_htmlpath(relpath) + linker.set_link(relpath, htmlpath) + gen_source_code_view(relpath, htmlpath) + + +Linker: +def make_source_link(self, dotted_name): + relpath = py.path.local(inspect.getfile(dottedname).... + anchor = linker.get_anchor_name(dotted_name) + #html.a(text, href=#anchor) + ... upon resolving time ... + linkref = linker.get_html_relpath(relpath) + linkref += "#" + anchor + +def _make_anchored_link(self, relpath, anchor, text): + #html.a(text, href=#anchor) + +""" From guido at codespeak.net Thu Jan 11 14:00:30 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 11 Jan 2007 14:00:30 +0100 (CET) Subject: [py-svn] r36477 - in py/dist/py/apigen: . source source/testing testing Message-ID: <20070111130030.7EF2B10088@code0.codespeak.net> Author: guido Date: Thu Jan 11 14:00:28 2007 New Revision: 36477 Added: py/dist/py/apigen/linker.py - copied unchanged from r36476, py/dist/py/apigen/source/linker.py py/dist/py/apigen/testing/ py/dist/py/apigen/testing/__init__.py py/dist/py/apigen/testing/test_linker.py - copied unchanged from r36476, py/dist/py/apigen/source/testing/test_linker.py Removed: py/dist/py/apigen/source/linker.py py/dist/py/apigen/source/testing/test_linker.py Log: (hpk, guido) Moved the linker to the apigen dir Deleted: /py/dist/py/apigen/source/linker.py ============================================================================== --- /py/dist/py/apigen/source/linker.py Thu Jan 11 14:00:28 2007 +++ (empty file) @@ -1,36 +0,0 @@ -import py -html = py.xml.html - -def getrelfspath(dotted_name): - # XXX need to make sure its imported on non-py lib - return eval(dotted_name, {"py": py}) - -class LazyHref(object): - def __init__(self, linker, linkid): - self._linker = linker - self._linkid = linkid - - def __unicode__(self): - return unicode(self._linker.get_target(self._linkid)) - -class Linker(object): - def __init__(self): - self.root = None - self._linkid2target = {} - - def set_root(self, root): - assert self.root is None - self.root = root - - def repr_link(self, linkid, linkcontent=None): - if linkcontent is None: - linkcontent = linkid - return html.a(linkcontent, href=LazyHref(self, linkid)) - - def set_link(self, linkid, target): - assert linkid not in self._linkid2target - self._linkid2target[linkid] = target - - def get_target(self, linkid): - return '%s/%s' % (self.root, self._linkid2target[linkid]) - Deleted: /py/dist/py/apigen/source/testing/test_linker.py ============================================================================== --- /py/dist/py/apigen/source/testing/test_linker.py Thu Jan 11 14:00:28 2007 +++ (empty file) @@ -1,75 +0,0 @@ -import py -html = py.xml.html -from py.__.apigen.source.linker import Linker, getrelfspath - -class TestLinker(object): - def test_one_lazy_link(self): - linker = Linker() - linker.set_root('/root') - tag = linker.repr_link('py.path.local') - linker.set_link('py.path.local', 'py/path/local.html') - resolved = tag.unicode() - assert (resolved == - 'py.path.local') - - def test_lazy_link_different_text(self): - linker = Linker() - linker.set_root('/root') - content = html.em('foo') - tag = linker.repr_link('py.path.local', content) - linker.set_link('py.path.local', 'py/path/local.html') - resolved = tag.unicode(indent=0) - assert (resolved == - 'foo') - - def test_source_link_getrelfspath(self): - linker = Linker() - linker.set_root('/root') - - relfspath = getrelfspath('py.path.local') - tag = linker.repr_link(relfspath, "sourcelink") - linker.set_link(relfspath, "hello/world") - resolved = tag.unicode(indent=0) - assert (resolved == - 'sourcelink') - -# considerations for futurue use -""" -snippet = create_method_html('foo.bar', dsa, linker) -snippet.unicode() # links get resolved here? - -def create_method_html(dotted_name, dsa, linker): - ... - # - a = linker.make_source_link(dotted_name) - ... - -def create_source_code_view(srcpath, linker): - ... - # dirpath() - anchor = linker.provide_anchor(dotted_name) - -def convert_srcpath_to_htmlpath(srcpath): - return 'source/%s.html' % (srcpath,) - -def write_sources(root, linker): - for fpath in root.visit(fil='*.py'): - relpath = fpath.relto(root) - htmlpath = convert_srcpath_to_htmlpath(relpath) - linker.set_link(relpath, htmlpath) - gen_source_code_view(relpath, htmlpath) - - -Linker: -def make_source_link(self, dotted_name): - relpath = py.path.local(inspect.getfile(dottedname).... - anchor = linker.get_anchor_name(dotted_name) - #html.a(text, href=#anchor) - ... upon resolving time ... - linkref = linker.get_html_relpath(relpath) - linkref += "#" + anchor - -def _make_anchored_link(self, relpath, anchor, text): - #html.a(text, href=#anchor) - -""" Added: py/dist/py/apigen/testing/__init__.py ============================================================================== From fijal at codespeak.net Thu Jan 11 15:06:30 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 15:06:30 +0100 (CET) Subject: [py-svn] r36481 - py/branch/rsession-cleanup/dist Message-ID: <20070111140630.8E1EA10063@code0.codespeak.net> Author: fijal Date: Thu Jan 11 15:06:29 2007 New Revision: 36481 Added: py/branch/rsession-cleanup/dist/ - copied from r36480, py/dist/ Log: Created new branch to do RSession refactoring From fijal at codespeak.net Thu Jan 11 15:07:44 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 15:07:44 +0100 (CET) Subject: [py-svn] r36482 - py/branch/rsession-cleanup Message-ID: <20070111140744.D2F9B10063@code0.codespeak.net> Author: fijal Date: Thu Jan 11 15:07:44 2007 New Revision: 36482 Removed: py/branch/rsession-cleanup/ Log: Wrong name From fijal at codespeak.net Thu Jan 11 15:08:02 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 15:08:02 +0100 (CET) Subject: [py-svn] r36483 - py/branch/rsession-cleanup Message-ID: <20070111140802.6B86410072@code0.codespeak.net> Author: fijal Date: Thu Jan 11 15:08:01 2007 New Revision: 36483 Added: py/branch/rsession-cleanup/ - copied from r36482, py/dist/py/ Log: Created a branch for RSession cleanup From fijal at codespeak.net Thu Jan 11 16:07:35 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 16:07:35 +0100 (CET) Subject: [py-svn] r36492 - py/dist/py/documentation/future Message-ID: <20070111150735.31F2710079@code0.codespeak.net> Author: fijal Date: Thu Jan 11 16:07:34 2007 New Revision: 36492 Modified: py/dist/py/documentation/future/rsession_todo.txt Log: Another point Modified: py/dist/py/documentation/future/rsession_todo.txt ============================================================================== --- py/dist/py/documentation/future/rsession_todo.txt (original) +++ py/dist/py/documentation/future/rsession_todo.txt Thu Jan 11 16:07:34 2007 @@ -9,4 +9,7 @@ * Discovery of nodes which are available for accepting distributed testing * Test the tests rescheduling, so high-latency nodes would not take part - in that. \ No newline at end of file + in that. + +* make sure that C-c semantics are ok (nodes are killed properly). + There was an attempt to do so, but it's not tested and not always work. \ No newline at end of file From fijal at codespeak.net Thu Jan 11 16:07:49 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 16:07:49 +0100 (CET) Subject: [py-svn] r36493 - in py/dist/py: . test test/rsession test/terminal test/testing test/tkinter Message-ID: <20070111150749.CB35510080@code0.codespeak.net> Author: fijal Date: Thu Jan 11 16:07:47 2007 New Revision: 36493 Modified: py/dist/py/__init__.py py/dist/py/test/config.py py/dist/py/test/defaultconftest.py py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/report.py py/dist/py/test/rsession/rsync.py py/dist/py/test/rsession/rsync_remote.py py/dist/py/test/terminal/terminal.py py/dist/py/test/testing/test_session.py py/dist/py/test/tkinter/reportsession.py py/dist/py/test/tkinter/util.py Log: Kill the globally exported sessions. Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Thu Jan 11 16:07:47 2007 @@ -37,11 +37,6 @@ 'test.cmdline.main' : ('./test/cmdline.py', 'main'), # for customization of collecting/running tests - 'test.Session' : ('./test/session.py', 'Session'), - 'test.TerminalSession' : ('./test/terminal/terminal.py', 'TerminalSession'), - 'test.RSession' : ('./test/rsession/rsession.py', 'RSession'), - 'test.LSession' : ('./test/rsession/rsession.py', 'LSession'), - 'test.TkinterSession' : ('./test/tkinter/tksession.py', 'TkSession'), 'test.collect.Collector' : ('./test/collect.py', 'Collector'), 'test.collect.Directory' : ('./test/collect.py', 'Directory'), 'test.collect.Module' : ('./test/collect.py', 'Module'), @@ -51,10 +46,6 @@ 'test.collect.Generator' : ('./test/collect.py', 'Generator'), 'test.Item' : ('./test/item.py', 'Item'), 'test.Function' : ('./test/item.py', 'Function'), - 'test.rest.RestReporter' : ('./test/rsession/rest.py', 'RestReporter'), - 'test.rest.NoLinkWriter' : ('./test/rsession/rest.py', 'NoLinkWriter'), - 'test.rest.LinkWriter' : ('./test/rsession/rest.py', 'LinkWriter'), - 'test.rest.RelLinkWriter': ('./test/rsession/rest.py', 'RelLinkWriter'), # thread related API (still in early design phase) '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), Modified: py/dist/py/test/config.py ============================================================================== --- py/dist/py/test/config.py (original) +++ py/dist/py/test/config.py Thu Jan 11 16:07:47 2007 @@ -112,12 +112,21 @@ """ return Session class determined from cmdline options and looked up in initial config modules. """ + # we need to import all the sessions here + from py.__.test.session import Session + from py.__.test.rsession.rsession import RSession, LSession + from py.__.test.terminal.terminal import TerminalSession + from py.__.test.tkinter.reportsession import ReportSession + sessions = {'RSession':RSession, 'LSession':LSession, + 'TerminalSession':TerminalSession, + 'TkinterSession':ReportSession} + name = self.option.session name += 'Session' try: - return self.getinitialvalue(name) + return self.getinitialvalue(name) except ValueError: - return getattr(py.test, name) + return sessions[name] Config._reset() Modified: py/dist/py/test/defaultconftest.py ============================================================================== --- py/dist/py/test/defaultconftest.py (original) +++ py/dist/py/test/defaultconftest.py Thu Jan 11 16:07:47 2007 @@ -10,8 +10,6 @@ additionalinfo = None -linkwriter = py.test.rest.RelLinkWriter() - def adddefaultoptions(): Option = py.test.Config.Option py.test.Config.addoptions('general options', Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Thu Jan 11 16:07:47 2007 @@ -9,8 +9,8 @@ class HostRSync(RSync): - def __init__(self, rsync_roots): - RSync.__init__(self, delete=True) + def __init__(self, rsync_roots, **args): + RSync.__init__(self, delete=True, **args) self.rsync_roots = rsync_roots def filter(self, path): @@ -63,6 +63,21 @@ hosts.append((num, host, gw, str(pkgdir.dirpath()))) return hosts +class ReportRsyncProgress: + def __init__(self, reporter): + self.reporter = reporter + self.total = {} + self.done = {} + + def callback(self, cmd, data, channel): + if cmd == "list": + self.total[channel] = data + self.done[channel] = 0 + else: + self.done[channel] += data + fraction = float(self.done[channel])/self.total[channel] + self.reporter(report.RsyncProgress(None, fraction)) + # XXX: Options has grown a bit too much, but most of them are just for tests def init_hosts(reporter, sshhosts, relpath, pkgdir, rsync_roots=None, \ remote_python=None, remote_options={}, optimise_localhost=True,\ @@ -79,8 +94,10 @@ # rsyncing rsynced = {} + rsync_progress = ReportRsyncProgress(reporter) if do_sync: + #rsync = HostRSync(rsync_roots, callback=rsync_progress.callback) rsync = HostRSync(rsync_roots) for num, host, gw, remoterootpath in hosts: if (host, remoterootpath) in rsynced or (host == 'localhost' \ @@ -97,6 +114,7 @@ rsync.add_target(gw, remoterootpath, done) if not do_sync: return # for testing only + rsync.send(pkgdir.dirpath()) # hosts ready Modified: py/dist/py/test/rsession/report.py ============================================================================== --- py/dist/py/test/rsession/report.py (original) +++ py/dist/py/test/rsession/report.py Thu Jan 11 16:07:47 2007 @@ -117,3 +117,8 @@ def __init__(self, item, outcome): self.item = item self.outcome = outcome + +class RsyncProgress(ReportEvent): + def __init__(self, hostid, progress): + self.progress = progress + self.hostid = hostid Modified: py/dist/py/test/rsession/rsync.py ============================================================================== --- py/dist/py/test/rsession/rsync.py (original) +++ py/dist/py/test/rsession/rsync.py Thu Jan 11 16:07:47 2007 @@ -132,5 +132,5 @@ raise ValueError, "cannot sync %r" % (path,) REMOTE_SOURCE = py.path.local(__file__).dirpath().\ - join('rsync_remote.py').open().read() + join('rsync_remote.py').open().read() + "\nf()" Modified: py/dist/py/test/rsession/rsync_remote.py ============================================================================== --- py/dist/py/test/rsession/rsync_remote.py (original) +++ py/dist/py/test/rsession/rsync_remote.py Thu Jan 11 16:07:47 2007 @@ -1,83 +1,86 @@ -import os, stat, shutil, md5 -destdir, options = channel.receive() -modifiedfiles = [] - -def remove(path): - assert path.startswith(destdir) - try: - os.unlink(path) - except OSError: - # assume it's a dir - shutil.rmtree(path) - -def receive_directory_structure(path, relcomponents): - try: - st = os.lstat(path) - except OSError: - st = None - msg = channel.receive() - if isinstance(msg, list): - if st and not stat.S_ISDIR(st.st_mode): + +def f(): + import os, stat, shutil, md5 + destdir, options = channel.receive() + modifiedfiles = [] + + def remove(path): + assert path.startswith(destdir) + try: os.unlink(path) + except OSError: + # assume it's a dir + shutil.rmtree(path) + + def receive_directory_structure(path, relcomponents): + try: + st = os.lstat(path) + except OSError: st = None - if not st: - os.makedirs(path) - entrynames = {} - for entryname in msg: - receive_directory_structure(os.path.join(path, entryname), - relcomponents + [entryname]) - entrynames[entryname] = True - if options.get('delete'): - for othername in os.listdir(path): - if othername not in entrynames: - otherpath = os.path.join(path, othername) - remove(otherpath) - elif msg is not None: - checksum = None - if st: - if stat.S_ISREG(st.st_mode): - msg_mtime, msg_size = msg - if msg_size != st.st_size: - pass - elif msg_mtime != st.st_mtime: - f = open(path, 'rb') - checksum = md5.md5(f.read()).digest() - f.close() + msg = channel.receive() + if isinstance(msg, list): + if st and not stat.S_ISDIR(st.st_mode): + os.unlink(path) + st = None + if not st: + os.makedirs(path) + entrynames = {} + for entryname in msg: + receive_directory_structure(os.path.join(path, entryname), + relcomponents + [entryname]) + entrynames[entryname] = True + if options.get('delete'): + for othername in os.listdir(path): + if othername not in entrynames: + otherpath = os.path.join(path, othername) + remove(otherpath) + elif msg is not None: + checksum = None + if st: + if stat.S_ISREG(st.st_mode): + msg_mtime, msg_size = msg + if msg_size != st.st_size: + pass + elif msg_mtime != st.st_mtime: + f = open(path, 'rb') + checksum = md5.md5(f.read()).digest() + f.close() + else: + return # already fine else: - return # already fine - else: - remove(path) - channel.send(("send", (relcomponents, checksum))) - modifiedfiles.append((path, msg)) -receive_directory_structure(destdir, []) - -STRICT_CHECK = False # seems most useful this way for py.test -channel.send(("list_done", None)) - -for path, (time, size) in modifiedfiles: - data = channel.receive() - channel.send(("ack", path[len(destdir) + 1:])) - if data is not None: - if STRICT_CHECK and len(data) != size: - raise IOError('file modified during rsync: %r' % (path,)) - f = open(path, 'wb') - f.write(data) - f.close() - os.utime(path, (time, time)) - del data -channel.send(("links", None)) - -msg = channel.receive() -while msg is not 42: - # we get symlink - _type, relpath, linkpoint = msg - assert _type == "link" - path = os.path.join(destdir, relpath) - try: - os.unlink(path) - except OSError: - pass + remove(path) + channel.send(("send", (relcomponents, checksum))) + modifiedfiles.append((path, msg)) + receive_directory_structure(destdir, []) + + STRICT_CHECK = False # seems most useful this way for py.test + channel.send(("list_done", None)) + + for path, (time, size) in modifiedfiles: + data = channel.receive() + channel.send(("ack", path[len(destdir) + 1:])) + if data is not None: + if STRICT_CHECK and len(data) != size: + raise IOError('file modified during rsync: %r' % (path,)) + f = open(path, 'wb') + f.write(data) + f.close() + os.utime(path, (time, time)) + del data + channel.send(("links", None)) - os.symlink(os.path.join(destdir, linkpoint), path) msg = channel.receive() -channel.send(("done", None)) + while msg is not 42: + # we get symlink + _type, relpath, linkpoint = msg + assert _type == "link" + path = os.path.join(destdir, relpath) + try: + os.unlink(path) + except OSError: + pass + + os.symlink(os.path.join(destdir, linkpoint), path) + msg = channel.receive() + channel.send(("done", None)) + Modified: py/dist/py/test/terminal/terminal.py ============================================================================== --- py/dist/py/test/terminal/terminal.py (original) +++ py/dist/py/test/terminal/terminal.py Thu Jan 11 16:07:47 2007 @@ -16,7 +16,9 @@ target = dest.sep.join(('..', )*n + (reldest, )) return target -class TerminalSession(py.test.Session): +from py.__.test.session import Session + +class TerminalSession(Session): def __init__(self, config, file=None): super(TerminalSession, self).__init__(config) if file is None: Modified: py/dist/py/test/testing/test_session.py ============================================================================== --- py/dist/py/test/testing/test_session.py (original) +++ py/dist/py/test/testing/test_session.py Thu Jan 11 16:07:47 2007 @@ -32,13 +32,15 @@ l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l - def test_session_parsing(self): + def test_session_parsing(self): + from py.__.test.terminal.terminal import TerminalSession + from py.__.test.tkinter.reportsession import ReportSession config, args = py.test.Config.parse(['--session=terminal']) - assert issubclass(config.getsessionclass(), py.test.TerminalSession) + assert issubclass(config.getsessionclass(), TerminalSession) config, args = py.test.Config.parse(['--session=tkinter']) - assert issubclass(config.getsessionclass(), py.test.TkinterSession) + assert issubclass(config.getsessionclass(), ReportSession) config, args = py.test.Config.parse(['--tkinter']) - assert issubclass(config.getsessionclass(), py.test.TkinterSession) + assert issubclass(config.getsessionclass(), ReportSession) class TestKeywordSelection: def test_select_simple(self): @@ -92,9 +94,10 @@ (datadir / 'syntax_error.py').remove() def setup_method(self, method): + from py.__.test.terminal.terminal import TerminalSession self.file = StringIO() config, args = py.test.Config.parse([]) - self.session = py.test.TerminalSession(config, file=self.file) + self.session = TerminalSession(config, file=self.file) #print >>f, "session %r is setup for %r" %(self.session, method) #f.flush() #print "session is setup", self.session Modified: py/dist/py/test/tkinter/reportsession.py ============================================================================== --- py/dist/py/test/tkinter/reportsession.py (original) +++ py/dist/py/test/tkinter/reportsession.py Thu Jan 11 16:07:47 2007 @@ -1,8 +1,9 @@ '''GuiSession builds TestReport instances and sends them to tkgui.Manager''' import py from util import TestReport +from py.__.test.session import Session -class ReportSession(py.test.Session): +class ReportSession(Session): def __init__(self, config = None, channel = None): super(ReportSession, self).__init__(config) Modified: py/dist/py/test/tkinter/util.py ============================================================================== --- py/dist/py/test/tkinter/util.py (original) +++ py/dist/py/test/tkinter/util.py Thu Jan 11 16:07:47 2007 @@ -2,7 +2,8 @@ import sys import py -from py.__.test.terminal import out +from py.__.test.terminal import out +from py.__.test.terminal.terminal import TerminalSession class Null: """ Null objects always and reliably "do nothing." """ @@ -201,7 +202,7 @@ def report_failed(self, config, item, res): #XXX hack abuse of TerminalSession - terminal = py.test.TerminalSession(config) + terminal = TerminalSession(config) out = OutBuffer() terminal.out = out terminal.repr_failure(item, res) @@ -210,7 +211,7 @@ def report_skipped(self, config, item, outcome): texts = {} - terminal = py.test.TerminalSession(config) + terminal = TerminalSession(config) raisingtb = terminal.getlastvisible(outcome.excinfo.traceback) fn = raisingtb.frame.code.path lineno = raisingtb.lineno From fijal at codespeak.net Thu Jan 11 16:09:59 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 16:09:59 +0100 (CET) Subject: [py-svn] r36495 - in py/dist/py: . documentation path/testing Message-ID: <20070111150959.86B851007E@code0.codespeak.net> Author: fijal Date: Thu Jan 11 16:09:58 2007 New Revision: 36495 Modified: py/dist/py/__init__.py py/dist/py/documentation/TODO.txt py/dist/py/path/testing/test_api.py Log: another went away Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Thu Jan 11 16:09:58 2007 @@ -62,7 +62,6 @@ 'path.svnurl' : ('./path/svn/urlcommand.py', 'SvnCommandPath'), 'path.local' : ('./path/local/local.py', 'LocalPath'), 'path.extpy' : ('./path/extpy/extpy.py', 'Extpy'), - 'path.checker' : ('./path/common.py', 'checker'), # deprecated # some nice slightly magic APIs 'magic.greenlet' : ('./magic/greenlet.py', 'greenlet'), Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Thu Jan 11 16:09:58 2007 @@ -30,9 +30,6 @@ test.compat.TestCAse * remove from public namespace: - py.test.Session / TerminalSession / RSession / LSession / TkinterSession - py.test.rest - py.path.checker View XXX consider py.magic. invoke/revoke/patch/revert Modified: py/dist/py/path/testing/test_api.py ============================================================================== --- py/dist/py/path/testing/test_api.py (original) +++ py/dist/py/path/testing/test_api.py Thu Jan 11 16:09:58 2007 @@ -13,9 +13,6 @@ y = eval(r) assert y == p - def test_deprecated_checker(self): - py.test.deprecated_call(py.path.checker) - def test_defaultlocal(self): p = path.local() assert hasattr(p, 'atime') From fijal at codespeak.net Thu Jan 11 16:11:36 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 16:11:36 +0100 (CET) Subject: [py-svn] r36496 - in py/dist/py: . documentation magic Message-ID: <20070111151136.BABDA1007E@code0.codespeak.net> Author: fijal Date: Thu Jan 11 16:11:34 2007 New Revision: 36496 Modified: py/dist/py/__init__.py py/dist/py/documentation/TODO.txt py/dist/py/magic/exprinfo.py Log: Kille py.magic.View Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Thu Jan 11 16:11:34 2007 @@ -70,7 +70,6 @@ 'magic.patch' : ('./magic/patch.py', 'patch'), 'magic.revert' : ('./magic/patch.py', 'revert'), 'magic.autopath' : ('./magic/autopath.py', 'autopath'), - 'magic.View' : ('./magic/viewtype.py', 'View'), 'magic.AssertionError' : ('./magic/assertion.py', 'AssertionError'), # python inspection/code-generation API Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Thu Jan 11 16:11:34 2007 @@ -30,7 +30,6 @@ test.compat.TestCAse * remove from public namespace: - View XXX consider py.magic. invoke/revoke/patch/revert * make "_" namespace: Modified: py/dist/py/magic/exprinfo.py ============================================================================== --- py/dist/py/magic/exprinfo.py (original) +++ py/dist/py/magic/exprinfo.py Thu Jan 11 16:11:34 2007 @@ -11,7 +11,9 @@ #import traceback #traceback.print_exc() -class Interpretable(py.magic.View): +from py.__.magic.viewtype import View + +class Interpretable(View): """A parse tree node with a few extra methods.""" explanation = None From fijal at codespeak.net Thu Jan 11 16:13:26 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 16:13:26 +0100 (CET) Subject: [py-svn] r36497 - py/dist/py/documentation Message-ID: <20070111151326.6BDCF10072@code0.codespeak.net> Author: fijal Date: Thu Jan 11 16:13:24 2007 New Revision: 36497 Modified: py/dist/py/documentation/TODO.txt Log: Update a bit Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Thu Jan 11 16:13:24 2007 @@ -112,9 +112,6 @@ * document py.test's conftest.py approach (somewhat done) -* hide py.test.TerminalSession and TkinterSession? (questionable) - - Missing docstrings ------------------ @@ -479,62 +476,6 @@ test.collect.Class.__eq__ misses a docstring test.collect.Class.__cmp__ misses a docstring test.cmdline.main misses a docstring - test.TkinterSession misses a docstring - test.TerminalSession misses a docstring - test.TerminalSession.summaryline misses a docstring - test.TerminalSession.startiteration misses a docstring - test.TerminalSession.start_Module misses a docstring - test.TerminalSession.start_Item misses a docstring - test.TerminalSession.start misses a docstring - test.TerminalSession.skippedreasons misses a docstring - test.TerminalSession.skipbykeyword misses a docstring - test.TerminalSession.shouldclose misses a docstring - test.TerminalSession.runtraced misses a docstring - test.TerminalSession.run misses a docstring - test.TerminalSession.repr_source misses a docstring - test.TerminalSession.repr_progress_short_result misses a docstring - test.TerminalSession.repr_progress_module_result misses a docstring - test.TerminalSession.repr_progress_long_result misses a docstring - test.TerminalSession.repr_out_err misses a docstring - test.TerminalSession.repr_locals misses a docstring - test.TerminalSession.repr_failure_tbshort misses a docstring - test.TerminalSession.repr_failure_tbno misses a docstring - test.TerminalSession.repr_failure_tblong misses a docstring - test.TerminalSession.repr_failure_info misses a docstring - test.TerminalSession.repr_failure_explanation misses a docstring - test.TerminalSession.repr_failure misses a docstring - test.TerminalSession.main misses a docstring - test.TerminalSession.header misses a docstring - test.TerminalSession.getlastvisible misses a docstring - test.TerminalSession.getitemoutcomepairs misses a docstring - test.TerminalSession.getentrysource misses a docstring - test.TerminalSession.footer misses a docstring - test.TerminalSession.finish misses a docstring - test.TerminalSession.failures misses a docstring - test.TerminalSession.__init__ misses a docstring - test.Session.startiteration misses a docstring - test.Session.start misses a docstring - test.Session.skipbykeyword misses a docstring - test.Session.shouldclose misses a docstring - test.Session.runtraced misses a docstring - test.Session.run misses a docstring - test.Session.getitemoutcomepairs misses a docstring - test.Session.finish misses a docstring - test.Session.__init__ misses a docstring - test.RSession.reporterror misses a docstring - test.RSession.read_distributed_config misses a docstring - test.RSession.make_colitems misses a docstring - test.RSession.kill_server misses a docstring - test.RSession.init_reporter misses a docstring - test.RSession.getpkgdir misses a docstring - test.RSession.dispatch_tests misses a docstring - test.RSession.__init__ misses a docstring - test.LSession.write_docs misses a docstring - test.LSession.main misses a docstring - test.LSession.kill_server misses a docstring - test.LSession.init_runner misses a docstring - test.LSession.init_reporter misses a docstring - test.LSession.__init__ misses a docstring test.Item misses a docstring test.Item.tryiter has an 'XXX' in its docstring test.Item.teardown misses a docstring From guido at codespeak.net Thu Jan 11 16:20:05 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 11 Jan 2007 16:20:05 +0100 (CET) Subject: [py-svn] r36498 - in py/dist/py/apigen: . rest/testing source source/testing testing tracer Message-ID: <20070111152005.8492610072@code0.codespeak.net> Author: guido Date: Thu Jan 11 16:19:54 2007 New Revision: 36498 Added: py/dist/py/apigen/htmlgen.py (contents, props changed) py/dist/py/apigen/testing/test_apigen_example.py (contents, props changed) Modified: py/dist/py/apigen/linker.py (props changed) py/dist/py/apigen/rest/testing/test_htmlhandlers.py (props changed) py/dist/py/apigen/source/color.py (props changed) py/dist/py/apigen/source/path.py (props changed) py/dist/py/apigen/source/testing/test_color.py (props changed) py/dist/py/apigen/testing/ (props changed) py/dist/py/apigen/testing/__init__.py (props changed) py/dist/py/apigen/testing/test_linker.py (props changed) py/dist/py/apigen/tracer/docstorage.py py/dist/py/apigen/tracer/permastore.py (props changed) Log: (hpk, guido) added first bits of html generation, fixeol Added: py/dist/py/apigen/htmlgen.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/htmlgen.py Thu Jan 11 16:19:54 2007 @@ -0,0 +1,35 @@ +import py + +html = py.xml.html +class H(html): + class ClassDescription(html.div): + pass + + class MethodDescription(html.div): + pass + + class MethodSignature(html.div): + pass + + class ParameterDescription(html.div): + pass + + class Docstring(html.div): + pass + +def get_param_htmldesc(linker, func): + import inspect + # XXX copy and modify formatargspec to produce html + return H.em(inspect.formatargspec(*inspect.getargspec(func))) + +def build_method_html(linker, dsa, dotted_name): + func = dsa.get_obj(dotted_name) + params = get_param_htmldesc(linker, func) + docstring = func.__doc__ + local_methodname = func.__name__ + snippet = H.MethodDescription( + H.MethodSignature(local_methodname, "(", params, "):"), + H.Docstring(docstring), + ) + return snippet + Added: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/testing/test_apigen_example.py Thu Jan 11 16:19:54 2007 @@ -0,0 +1,44 @@ +import py +html = py.xml.html +from py.__.apigen.linker import Linker +from py.__.apigen.htmlgen import * +from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor +from xml.dom.minidom import parseString + +def setup_fs_project(): + temp = py.test.ensuretemp('apigen_example') + temp.ensure("pkg/func.py").write(py.code.Source(""" + def func(arg1): + "docstring" + """)) + temp.ensure("pkg/__init__.py").write(py.code.Source(""" + from py.initpkg import initpkg + initpkg(__name__, exportdefs = { + 'pkg.sub.func': ("./func.py", "func") + }) + """)) + return temp, 'pkg' + +def get_dsa(fsroot, pkgname): + py.std.sys.path.insert(0, str(fsroot)) + pkg = __import__(pkgname) + ds = DocStorage() + ds.from_pkg(pkg) + dsa = DocStorageAccessor(ds) + return dsa + +class TestExampleProject(object): + def setup_class(cls): + cls.fs_root, cls.pkg_name = setup_fs_project() + cls.dsa = get_dsa(cls.fs_root, cls.pkg_name) + + def setup_method(self, meth): + self.linker = Linker() + + def test_method_description(self): + snippet = build_method_html(self.linker, self.dsa, 'pkg.sub.func') + html = snippet.unicode() + print html + # XXX (x)html validity testing would be nice... + parseString(html) + Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Thu Jan 11 16:19:54 2007 @@ -67,6 +67,7 @@ if to_key: self.descs[to_key] = to_val self.make_cache() + # XXX return self # XXX: This function becomes slowly outdated and even might go away at some @@ -121,6 +122,7 @@ base = getattr(base, elem) d[key] = base self.from_dict(d, keep_frames) + # XXX return self def from_module(self, func): From cfbolz at codespeak.net Thu Jan 11 16:40:50 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Thu, 11 Jan 2007 16:40:50 +0100 (CET) Subject: [py-svn] r36503 - py/dist/py/bin Message-ID: <20070111154050.B65A310075@code0.codespeak.net> Author: cfbolz Date: Thu Jan 11 16:40:49 2007 New Revision: 36503 Modified: py/dist/py/bin/py.lookup Log: better help string for py.lookup Modified: py/dist/py/bin/py.lookup ============================================================================== --- py/dist/py/bin/py.lookup (original) +++ py/dist/py/bin/py.lookup Thu Jan 11 16:40:49 2007 @@ -1,5 +1,12 @@ #!/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 @@ -12,7 +19,7 @@ optparse = py.compat.optparse -parser = optparse.OptionParser() +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", From guido at codespeak.net Thu Jan 11 16:44:42 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 11 Jan 2007 16:44:42 +0100 (CET) Subject: [py-svn] r36505 - in py/dist/py/apigen: . testing Message-ID: <20070111154442.D30391007D@code0.codespeak.net> Author: guido Date: Thu Jan 11 16:44:40 2007 New Revision: 36505 Modified: py/dist/py/apigen/linker.py py/dist/py/apigen/testing/test_linker.py Log: (hpk, guido) simplified lazy linking Modified: py/dist/py/apigen/linker.py ============================================================================== --- py/dist/py/apigen/linker.py (original) +++ py/dist/py/apigen/linker.py Thu Jan 11 16:44:40 2007 @@ -22,10 +22,8 @@ assert self.root is None self.root = root - def repr_link(self, linkid, linkcontent=None): - if linkcontent is None: - linkcontent = linkid - return html.a(linkcontent, href=LazyHref(self, linkid)) + def get_lazyhref(self, linkid): + return LazyHref(self, linkid) def set_link(self, linkid, target): assert linkid not in self._linkid2target Modified: py/dist/py/apigen/testing/test_linker.py ============================================================================== --- py/dist/py/apigen/testing/test_linker.py (original) +++ py/dist/py/apigen/testing/test_linker.py Thu Jan 11 16:44:40 2007 @@ -1,37 +1,25 @@ import py html = py.xml.html -from py.__.apigen.source.linker import Linker, getrelfspath +from py.__.apigen.linker import Linker, getrelfspath class TestLinker(object): def test_one_lazy_link(self): linker = Linker() linker.set_root('/root') - tag = linker.repr_link('py.path.local') + lazyhref = linker.get_lazyhref('py.path.local') linker.set_link('py.path.local', 'py/path/local.html') - resolved = tag.unicode() - assert (resolved == - 'py.path.local') + href = unicode(lazyhref) + assert href == '/root/py/path/local.html' - def test_lazy_link_different_text(self): - linker = Linker() - linker.set_root('/root') - content = html.em('foo') - tag = linker.repr_link('py.path.local', content) - linker.set_link('py.path.local', 'py/path/local.html') - resolved = tag.unicode(indent=0) - assert (resolved == - 'foo') - def test_source_link_getrelfspath(self): linker = Linker() linker.set_root('/root') relfspath = getrelfspath('py.path.local') - tag = linker.repr_link(relfspath, "sourcelink") + lazyhref = linker.get_lazyhref(relfspath) linker.set_link(relfspath, "hello/world") - resolved = tag.unicode(indent=0) - assert (resolved == - 'sourcelink') + href = unicode(lazyhref) + assert href == '/root/hello/world' # considerations for futurue use """ From cfbolz at codespeak.net Thu Jan 11 16:46:58 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Thu, 11 Jan 2007 16:46:58 +0100 (CET) Subject: [py-svn] r36506 - py/dist/py/bin Message-ID: <20070111154658.4656510071@code0.codespeak.net> Author: cfbolz Date: Thu Jan 11 16:46:57 2007 New Revision: 36506 Modified: py/dist/py/bin/py.cleanup Log: make py.cleanup use optparse (for future additions and a better help message). Modified: py/dist/py/bin/py.cleanup ============================================================================== --- py/dist/py/bin/py.cleanup (original) +++ py/dist/py/bin/py.cleanup Thu Jan 11 16:46:57 2007 @@ -1,12 +1,26 @@ #!/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 +from py.compat import optparse + +parser = optparse.OptionParser(usage=__doc__) -if len(py.std.sys.argv) > 1: - path = py.path.local(py.std.sys.argv[1]) -else: - path = py.path.local() -print "cleaning path", path -for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)): - x.remove() +if __name__ == '__main__': + (options, args) = parser.parse_args() + + string = args[0] + if len(args) >= 1: + path = py.path.local(args) + else: + path = py.path.local() + print "cleaning path", path + for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)): + x.remove() From cfbolz at codespeak.net Thu Jan 11 16:58:56 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Thu, 11 Jan 2007 16:58:56 +0100 (CET) Subject: [py-svn] r36509 - in py/dist/py: bin misc/cmdline Message-ID: <20070111155856.A916A10071@code0.codespeak.net> Author: cfbolz Date: Thu Jan 11 16:58:55 2007 New Revision: 36509 Modified: py/dist/py/bin/py.countloc py/dist/py/misc/cmdline/countloc.py Log: use optparse in py.countloc for extensibility and --help purposes. add a nice --help string. Modified: py/dist/py/bin/py.countloc ============================================================================== --- py/dist/py/bin/py.countloc (original) +++ py/dist/py/bin/py.countloc Thu Jan 11 16:58:55 2007 @@ -3,6 +3,21 @@ # 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 -countloc() + +parser = optparse.OptionParser(usage=__doc__) + +if __name__ == '__main__': + (options, args) = parser.parse_args() + countloc(args) Modified: py/dist/py/misc/cmdline/countloc.py ============================================================================== --- py/dist/py/misc/cmdline/countloc.py (original) +++ py/dist/py/misc/cmdline/countloc.py Thu Jan 11 16:58:55 2007 @@ -66,11 +66,10 @@ return counter, numfiles, numlines, numtestfiles, numtestlines -def countloc(): - args = py.std.sys.argv[1:] - if not args: - args = ['.'] - locations = [py.path.local(x) for x in args] +def countloc(paths=None): + if not paths: + paths = ['.'] + locations = [py.path.local(x) for x in paths] (counter, numfiles, numlines, numtestfiles, numtestlines) = get_loccount(locations) From fijal at codespeak.net Thu Jan 11 17:04:40 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 11 Jan 2007 17:04:40 +0100 (CET) Subject: [py-svn] r36511 - in py/dist/py: . magic/testing misc/testing path/local/testing path/testing Message-ID: <20070111160440.B2B3210072@code0.codespeak.net> Author: fijal Date: Thu Jan 11 17:04:38 2007 New Revision: 36511 Modified: py/dist/py/initpkg.py py/dist/py/magic/testing/test_viewtype.py py/dist/py/misc/testing/test_api.py py/dist/py/path/local/testing/test_local.py py/dist/py/path/testing/common.py Log: Fixed imports. Modified: py/dist/py/initpkg.py ============================================================================== --- py/dist/py/initpkg.py (original) +++ py/dist/py/initpkg.py Thu Jan 11 17:04:38 2007 @@ -107,7 +107,7 @@ return base def _iterfiles(self): - from py.path import checker + from py.__.path.common import checker base = self.getpath() for x in base.visit(checker(file=1, notext='.pyc'), rec=checker(dotfile=0)): Modified: py/dist/py/magic/testing/test_viewtype.py ============================================================================== --- py/dist/py/magic/testing/test_viewtype.py (original) +++ py/dist/py/magic/testing/test_viewtype.py Thu Jan 11 17:04:38 2007 @@ -1,4 +1,4 @@ -from py.magic import View +from py.__.magic.viewtype import View def test_class_dispatch(): ### Use a custom class hierarchy with existing instances Modified: py/dist/py/misc/testing/test_api.py ============================================================================== --- py/dist/py/misc/testing/test_api.py (original) +++ py/dist/py/misc/testing/test_api.py Thu Jan 11 17:04:38 2007 @@ -11,7 +11,6 @@ assert_class('py.path', 'svnwc') assert_class('py.path', 'svnurl') assert_class('py.path', 'extpy') - assert_class('py.path', 'checker') def test_magic_entrypoints(self): assert_function('py.magic', 'invoke') Modified: py/dist/py/path/local/testing/test_local.py ============================================================================== --- py/dist/py/path/local/testing/test_local.py (original) +++ py/dist/py/path/local/testing/test_local.py Thu Jan 11 17:04:38 2007 @@ -1,5 +1,6 @@ import py -from py.path import local, checker +from py.path import local +from py.__.path.common import checker from py.__.path.testing.fscommon import CommonFSTests, setuptestfs class LocalSetup: Modified: py/dist/py/path/testing/common.py ============================================================================== --- py/dist/py/path/testing/common.py (original) +++ py/dist/py/path/testing/common.py Thu Jan 11 17:04:38 2007 @@ -1,4 +1,4 @@ -from py.path import checker +from py.__.path.common import checker import py class CommonPathTests: From cfbolz at codespeak.net Thu Jan 11 17:04:54 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Thu, 11 Jan 2007 17:04:54 +0100 (CET) Subject: [py-svn] r36512 - py/dist/py/documentation Message-ID: <20070111160454.70FC010074@code0.codespeak.net> Author: cfbolz Date: Thu Jan 11 17:04:53 2007 New Revision: 36512 Added: py/dist/py/documentation/binary.txt Log: a start of documenting the things in py/bin Added: py/dist/py/documentation/binary.txt ============================================================================== --- (empty file) +++ py/dist/py/documentation/binary.txt Thu Jan 11 17:04:53 2007 @@ -0,0 +1,49 @@ +================== +py-lib executables +================== + +The py-lib contains some executables which are described here. Most of them are +relatively small scripts (apart from ``py.test``). We are open for feature +requests! + +``py.test`` +=========== + +The ``py.test`` executable is the main entry point into the py-lib testing tool, +see the `py.test documentation`_. + +.. _`py.test documentation`: test.html + +``py.cleanup`` +============== + +Usage: ``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 ".". + + +``py.countloc`` +=============== + +Usage: ``py.countloc [PATHS]`` + +Count (non-empty) lines of python code and number of python files recursively +starting from a ``PATHS`` given on the command line (starting from the current +working directory). Distinguish between test files and normal ones and report +them separately. + +``py.lookup`` +============= + +Usage: ``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. + +``py.rest`` +=========== + +XXX to be documented From guido at codespeak.net Thu Jan 11 19:45:38 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 11 Jan 2007 19:45:38 +0100 (CET) Subject: [py-svn] r36526 - in py/dist/py/apigen: . testing Message-ID: <20070111184538.DA74D10071@code0.codespeak.net> Author: guido Date: Thu Jan 11 19:45:36 2007 New Revision: 36526 Added: py/dist/py/apigen/layout.py py/dist/py/apigen/project.py Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py Log: first page gets generated now (quite minimally) Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Thu Jan 11 19:45:36 2007 @@ -1,14 +1,18 @@ import py +from py.__.apigen.layout import LayoutPage html = py.xml.html class H(html): class ClassDescription(html.div): pass + class ClassDef(html.div): + pass + class MethodDescription(html.div): pass - class MethodSignature(html.div): + class MethodDef(html.div): pass class ParameterDescription(html.div): @@ -17,19 +21,110 @@ class Docstring(html.div): pass + class Navigation(html.div): + pass + + class NavigationItem(html.div): + pass + def get_param_htmldesc(linker, func): + """ get the html for the parameters of a function """ import inspect # XXX copy and modify formatargspec to produce html return H.em(inspect.formatargspec(*inspect.getargspec(func))) -def build_method_html(linker, dsa, dotted_name): +def build_full_method_view(linker, dsa, dotted_name): + """ build the html for a class method """ func = dsa.get_obj(dotted_name) params = get_param_htmldesc(linker, func) docstring = func.__doc__ local_methodname = func.__name__ snippet = H.MethodDescription( - H.MethodSignature(local_methodname, "(", params, "):"), + H.MethodDef(local_methodname, "(", params, "):"), H.Docstring(docstring), ) return snippet +def build_short_method_view(linker, dsa, dotted_name): + """ build the html for a class method """ + func = dsa.get_obj(dotted_name) + params = get_param_htmldesc(linker, func) + docstring = func.__doc__ + local_methodname = func.__name__ + snippet = H.MethodDescription( + H.MethodDef(local_methodname, "(", params, "):"), + H.Docstring(docstring), + # XXX more info here... + ) + return snippet + +def build_class_view(linker, dsa, dotted_name): + cls = dsa.get_obj(dotted_name) + docstring = cls.__doc__ + bases = dsa.get_possible_base_classes(dotted_name) + methods = dsa.get_class_methods(dotted_name) + snippet = H.ClassDescription( + # XXX bases HTML + H.ClassDef('%s(%s):' % (cls.__name__, ','.join(bases))), + H.Docstring(docstring), + ) + for method in methods: + # XXX perhaps we should pass in a + snippet += build_short_method_view(linker, dsa, method.name) + return snippet + +def build_method_view(linker, dsa, dotted_name): + pass + +def build_package_navigation(linker, dotted_name): + # import dotted name + # build nav from sub items + pass + +def build_class_navigation(linker, dotted_name): + # import dotted name + # build nav from sub items + parent + pass + +def build_method_navigation(linker, dotted_name): + # import dotted name + # build nav from self + parent + parent's items + return H.Navigation('navigation') + +def build_source_pages(linker, base): + for fspath in base.visit("*.py"): + relfspath = fspath.relto(base) + reloutputpath = "source/" + relfspath + ".html" + linker.set_link(relfspath, reloutputpath) + targetpath = outputbase.join(reloutputpath) + page = build_source_page(fspath) + targetpath.write(page.unicode().encode("utf8")) + +def build_class_api_pages(project, linker, dsa, base, classes_dotted_names): + for dotted_name in classes_dotted_names: + tag = build_class_view(linker, dsa, dotted_name) + nav = build_class_navigation(linker, dotted_name) + page = wrap_page(project, linker, tag, nav) + reltargetpath = "api/%s.html" % (dotted_name,) + linker.set_link(dotted_name, reltargetpath) + content = unicode(page) + targetpath = base.ensure(reltargetpath) + targetpath.write(content.encode("utf8")) + +def build_method_api_pages(project, linker, dsa, base, methods_dotted_names): + for dotted_name in methods_dotted_names: + tag = build_full_method_view(linker, dsa, dotted_name) + nav = build_method_navigation(linker, dotted_name) + page = wrap_page(project, linker, tag, nav) + reltargetpath = "api/%s.html" % (dotted_name,) + linker.set_link(dotted_name, reltargetpath) + content = page.unicode() + targetpath = base.join(reltargetpath) + targetpath.ensure() + targetpath.write(content.encode("utf8")) + +def wrap_page(project, linker, contentel, navel): + page = LayoutPage(project, 'XXX Page Title', nav=navel) + page.set_content(contentel) + return page + Added: py/dist/py/apigen/layout.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/layout.py Thu Jan 11 19:45:36 2007 @@ -0,0 +1,21 @@ +""" layout definition for generating api/source documents + + this is the place where customization can be done +""" + +import py +from py.__.documentation.confrest import Page + +class LayoutPage(Page): + """ this provides the layout and style information """ + def __init__(self, *args, **kwargs): + self.nav = kwargs.pop('nav') + super(LayoutPage, self).__init__(*args, **kwargs) + + def set_content(self, contentel): + self.contentspace += contentel + + def fill(self): + super(LayoutPage, self).fill() + self.menubar[:] = self.nav + Added: py/dist/py/apigen/project.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/project.py Thu Jan 11 19:45:36 2007 @@ -0,0 +1,65 @@ +""" this contains the code that actually builds the pages using layout.py + + building the docs happens in two passes: the first one takes care of + collecting contents and navigation items, the second builds the actual + HTML +""" + +import py +from layout import LayoutPage + +class Project(py.__.documentation.confrest.Project): + """ a full project + + this takes care of storing information on the first pass, and building + pages + indexes on the second + """ + + def __init__(self): + self.content_items = {} + + def add_item(self, path, content): + """ add a single item (page) + + path is a (relative) path to the object, used for building links + and navigation + + content is an instance of some py.xml.html item + """ + assert path not in self.content_items, 'duplicate path %s' % (path,) + self.content_items[path] = content + + def build(self, outputpath): + """ convert the tree to actual HTML + + uses the LayoutPage class below for each page and takes care of + building index documents for the root and each sub directory + """ + opath = py.path.local(outputpath) + opath.ensure(dir=True) + paths = self.content_items.keys() + paths.sort() + for path in paths: + # build the page using the LayoutPage class + page = self.Page(self, path, stylesheeturl=self.stylesheet) + page.contentspace.append(self.content_items[path]) + ipath = opath.join(path) + if not ipath.dirpath().check(): + # XXX create index.html(?) + ipath.ensure(file=True) + ipath.write(page.unicode().encode(self.encoding)) + + def process(self, txtpath): + """ this allows using the project from confrest """ + # XXX not interesting yet, but who knows later (because of the + # cool nav) + +if __name__ == '__main__': + # XXX just to have an idea of how to use this... + proj = Project() + here = py.path.local('.') + for fpath in here.visit(): + if fpath.check(file=True): + proj.add_item(fpath, convert_to_html_somehow(fpath)) + proj.build() + Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Thu Jan 11 19:45:36 2007 @@ -3,6 +3,7 @@ from py.__.apigen.linker import Linker from py.__.apigen.htmlgen import * from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor +from py.__.apigen.project import Project from xml.dom.minidom import parseString def setup_fs_project(): @@ -31,14 +32,22 @@ def setup_class(cls): cls.fs_root, cls.pkg_name = setup_fs_project() cls.dsa = get_dsa(cls.fs_root, cls.pkg_name) + cls.project = Project() def setup_method(self, meth): self.linker = Linker() - def test_method_description(self): - snippet = build_method_html(self.linker, self.dsa, 'pkg.sub.func') + def test_build_full_method_view(self): + snippet = build_full_method_view(self.linker, self.dsa, 'pkg.sub.func') html = snippet.unicode() print html # XXX (x)html validity testing would be nice... parseString(html) + def test_build_method_api_pages(self): + base = py.test.ensuretemp('build_method_api_pages') + build_method_api_pages(self.project, self.linker, self.dsa, base, + ['pkg.sub.func']) + funcfile = base.join('api/pkg.sub.func.html') + assert funcfile.check() + From guido at codespeak.net Fri Jan 12 11:58:58 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 12 Jan 2007 11:58:58 +0100 (CET) Subject: [py-svn] r36546 - py/dist/py/test/web Message-ID: <20070112105858.B15581006F@code0.codespeak.net> Author: guido Date: Fri Jan 12 11:58:56 2007 New Revision: 36546 Added: py/dist/py/test/web/ py/dist/py/test/web/__init__.py py/dist/py/test/web/exception.py py/dist/py/test/web/post_multipart.py py/dist/py/test/web/webcheck.py Log: Test XHTML wellformedness and validity (using validator.w3.org). Added: py/dist/py/test/web/__init__.py ============================================================================== Added: py/dist/py/test/web/exception.py ============================================================================== --- (empty file) +++ py/dist/py/test/web/exception.py Fri Jan 12 11:58:56 2007 @@ -0,0 +1,6 @@ +class CSSError(Exception): + """raised when there's a problem with the CSS""" + +class HTMLError(Exception): + """raised when there's a problem with the HTML""" + Added: py/dist/py/test/web/post_multipart.py ============================================================================== --- (empty file) +++ py/dist/py/test/web/post_multipart.py Fri Jan 12 11:58:56 2007 @@ -0,0 +1,58 @@ +import httplib, mimetypes + +"""Copied from the cookbook + + see ActiveState's ASPN + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 +""" + +def post_multipart(host, selector, fields, files): + """ + Post fields and files to an http host as multipart/form-data. + fields is a sequence of (name, value) elements for regular form fields. + files is a sequence of (name, filename, value) elements for data to be + uploaded as files + + Return the server's response page. + """ + content_type, body = encode_multipart_formdata(fields, files) + h = httplib.HTTP(host) + h.putrequest('POST', selector) + h.putheader('content-type', content_type) + h.putheader('content-length', str(len(body))) + h.endheaders() + h.send(body) + errcode, errmsg, headers = h.getreply() + return h.file.read() + +def encode_multipart_formdata(fields, files): + """ + fields is a sequence of (name, value) elements for regular form fields. + files is a sequence of (name, filename, value) elements for data to be + uploaded as files + + Return (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' + CRLF = '\r\n' + L = [] + for (key, value) in fields: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + for (key, filename, value) in files: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % + (key, filename)) + L.append('Content-Type: %s' % get_content_type(filename)) + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + +def get_content_type(filename): + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' Added: py/dist/py/test/web/webcheck.py ============================================================================== --- (empty file) +++ py/dist/py/test/web/webcheck.py Fri Jan 12 11:58:56 2007 @@ -0,0 +1,41 @@ +import py +import re +from exception import * +from post_multipart import post_multipart +#import css_checker + +def check_html(string): + """check an HTML string for wellformedness and validity""" + tempdir = py.test.ensuretemp('check_html') + filename = 'temp%s.html' % (hash(string), ) + tempfile = tempdir.join(filename) + tempfile.write(string) + ret = post_multipart('validator.w3.org', '/check', [], + [('uploaded_file', 'somehtml.html', string)]) + is_valid = get_validation_result_from_w3_html(ret) + return is_valid + +reg_validation_result = re.compile(']*class="(in)?valid"[^>]*>([^<]*)<', + re.M | re.S) +def get_validation_result_from_w3_html(html): + match = reg_validation_result.search(html) + valid = match.group(1) is None + text = match.group(2).strip() + if not valid: + 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) + return valid + +#def check_css(string, basepath, htmlpath='/'): +# """check the CSS of an HTML string +# +# check whether an HTML string contains CSS rels, and if so check whether +# any classes defined in the HTML actually have a matching CSS selector +# """ +# c = css_checker.css_checker(string, basepath, htmlpath) +# # raises a CSSError when failing, this is done from the tester class to +# # allow being more verbose than just 'something went wrong' +# return c.check() + From guido at codespeak.net Fri Jan 12 12:01:02 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 12 Jan 2007 12:01:02 +0100 (CET) Subject: [py-svn] r36547 - in py/dist/py/apigen: . testing Message-ID: <20070112110102.6CFFF10072@code0.codespeak.net> Author: guido Date: Fri Jan 12 12:01:00 2007 New Revision: 36547 Added: py/dist/py/apigen/conftest.py Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py Log: Testing + fixed class page generation (very simple still), added optional web (XHTML validity) testing. Added: py/dist/py/apigen/conftest.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/conftest.py Fri Jan 12 12:01:00 2007 @@ -0,0 +1,10 @@ +import py + +Option = py.test.Config.Option +option = py.test.Config.addoptions("apigen test options", + Option('', '--webcheck', + action="store_true", dest="webcheck", default=False, + help="run XHTML validation tests" + ), +) + Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 12 12:01:00 2007 @@ -61,7 +61,7 @@ def build_class_view(linker, dsa, dotted_name): cls = dsa.get_obj(dotted_name) docstring = cls.__doc__ - bases = dsa.get_possible_base_classes(dotted_name) + bases = [str(b) for b in dsa.get_possible_base_classes(dotted_name)] methods = dsa.get_class_methods(dotted_name) snippet = H.ClassDescription( # XXX bases HTML @@ -69,8 +69,8 @@ H.Docstring(docstring), ) for method in methods: - # XXX perhaps we should pass in a - snippet += build_short_method_view(linker, dsa, method.name) + snippet += build_short_method_view(linker, dsa, '%s.%s' % (dotted_name, + method)) return snippet def build_method_view(linker, dsa, dotted_name): Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Fri Jan 12 12:01:00 2007 @@ -5,6 +5,8 @@ from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor from py.__.apigen.project import Project from xml.dom.minidom import parseString +from py.__.test.web import webcheck +from py.__.apigen.conftest import option def setup_fs_project(): temp = py.test.ensuretemp('apigen_example') @@ -12,10 +14,19 @@ def func(arg1): "docstring" """)) + temp.ensure('pkg/someclass.py').write(py.code.Source(""" + class SomeClass(object): + " docstring someclass " + def __init__(self, somevar): + self.somevar = somevar + def get_somevar(self): + return self.somevar + """)) temp.ensure("pkg/__init__.py").write(py.code.Source(""" from py.initpkg import initpkg initpkg(__name__, exportdefs = { - 'pkg.sub.func': ("./func.py", "func") + 'pkg.sub.func': ("./func.py", "func"), + 'pkg.SomeClass': ('./someclass.py', 'SomeClass'), }) """)) return temp, 'pkg' @@ -41,7 +52,6 @@ snippet = build_full_method_view(self.linker, self.dsa, 'pkg.sub.func') html = snippet.unicode() print html - # XXX (x)html validity testing would be nice... parseString(html) def test_build_method_api_pages(self): @@ -50,4 +60,14 @@ ['pkg.sub.func']) funcfile = base.join('api/pkg.sub.func.html') assert funcfile.check() + html = funcfile.read() + parseString(html) + if option.webcheck: + webcheck.check_html(html) + + def test_build_class_view(self): + snippet = build_class_view(self.linker, self.dsa, 'pkg.SomeClass') + html = snippet.unicode() + print html + parseString(html) From guido at codespeak.net Fri Jan 12 13:35:48 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 12 Jan 2007 13:35:48 +0100 (CET) Subject: [py-svn] r36560 - in py/dist/py/apigen: . testing Message-ID: <20070112123548.1C2191007C@code0.codespeak.net> Author: guido Date: Fri Jan 12 13:35:47 2007 New Revision: 36560 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/layout.py py/dist/py/apigen/testing/test_apigen_example.py Log: First bits of linking + nav added, doing two-pass page generation for classes now. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 12 13:35:47 2007 @@ -24,7 +24,10 @@ class Navigation(html.div): pass - class NavigationItem(html.div): + class NavigationItem(html.span): + pass + + class BaseDescription(html.a): pass def get_param_htmldesc(linker, func): @@ -59,13 +62,26 @@ return snippet def build_class_view(linker, dsa, dotted_name): + """ build the html for a class """ cls = dsa.get_obj(dotted_name) docstring = cls.__doc__ - bases = [str(b) for b in dsa.get_possible_base_classes(dotted_name)] methods = dsa.get_class_methods(dotted_name) + basehtml = [] + bases = dsa.get_possible_base_classes(dotted_name) + for base in bases: + try: + obj = dsa.get_obj(base.name) + except KeyError: + basehtml.append(base.name) + else: + href = linker.get_lazyhref(base.name) + basehtml.append(H.BaseDescription(base.name, href=href)) + basehtml.append(',') + basehtml.pop() + basehtml.append('):') snippet = H.ClassDescription( # XXX bases HTML - H.ClassDef('%s(%s):' % (cls.__name__, ','.join(bases))), + H.ClassDef('%s(' % (cls.__name__,), *basehtml), H.Docstring(docstring), ) for method in methods: @@ -79,45 +95,92 @@ def build_package_navigation(linker, dotted_name): # import dotted name # build nav from sub items - pass + return H.Navigation('navigation') -def build_class_navigation(linker, dotted_name): +def build_class_navigation(linker, dsa, dotted_name): # import dotted name # build nav from sub items + parent - pass + navitems = [] + path = dotted_name.split('.') + indent = 0 + if len(path) > 1: + parent_dotted_name = '.'.join(path[:-1]) + navitems.append(indent * 2 * u'\xa0') + try: + obj = dsa.get_obj(parent_dotted_name) + except KeyError: + navitems.append(parent_dotted_name) + else: + navitems.append(H.NavigationItem( + H.a(parent_dotted_name, + href=linker.get_lazyhref( + parent_dotted_name)))) + navitems.append(H.br()) + indent += 1 + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(dotted_name.split('.')[-1], + href=linker.get_lazyhref(dotted_name)))) + navitems.append(H.br()) + indent += 1 + for funcname in dsa.get_class_methods(dotted_name): + func_dotted_name = '%s.%s' % (dotted_name, funcname) + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(funcname, + href=linker.get_lazyhref(func_dotted_name)))) + navitems.append(H.br()) + return H.Navigation(*navitems) def build_method_navigation(linker, dotted_name): # import dotted name # build nav from self + parent + parent's items return H.Navigation('navigation') +# the build_*_pages methods all do two-pass page generation (first pass builds +# the link db, second uses it) def build_source_pages(linker, base): + """ build syntax-colored source views """ + passed = [] for fspath in base.visit("*.py"): relfspath = fspath.relto(base) reloutputpath = "source/" + relfspath + ".html" linker.set_link(relfspath, reloutputpath) + passed.append((fspath, reloutputpath)) + for fspath, reloutputpath in passed: targetpath = outputbase.join(reloutputpath) page = build_source_page(fspath) targetpath.write(page.unicode().encode("utf8")) def build_class_api_pages(project, linker, dsa, base, classes_dotted_names): + """ build the full api pages for a set of classes """ + passed = [] for dotted_name in classes_dotted_names: tag = build_class_view(linker, dsa, dotted_name) - nav = build_class_navigation(linker, dotted_name) - page = wrap_page(project, linker, tag, nav) + nav = build_class_navigation(linker, dsa, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) - content = unicode(page) + passed.append((dotted_name, tag, nav, reltargetpath)) + method_dotted_names = ['%s.%s' % (dotted_name, method_name) for + method_name in + dsa.get_class_methods(dotted_name)] + build_method_api_pages(project, linker, dsa, base, method_dotted_names) + for dotted_name, tag, nav, reltargetpath in passed: + page = wrap_page(project, linker, tag, nav) + content = page.unicode() targetpath = base.ensure(reltargetpath) targetpath.write(content.encode("utf8")) def build_method_api_pages(project, linker, dsa, base, methods_dotted_names): + passed = [] for dotted_name in methods_dotted_names: tag = build_full_method_view(linker, dsa, dotted_name) nav = build_method_navigation(linker, dotted_name) - page = wrap_page(project, linker, tag, nav) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) + passed.append((dotted_name, tag, nav, reltargetpath)) + for dotted_name, tag, nav, reltargetpath in passed: + page = wrap_page(project, linker, tag, nav) content = page.unicode() targetpath = base.join(reltargetpath) targetpath.ensure() Modified: py/dist/py/apigen/layout.py ============================================================================== --- py/dist/py/apigen/layout.py (original) +++ py/dist/py/apigen/layout.py Fri Jan 12 13:35:47 2007 @@ -17,5 +17,6 @@ def fill(self): super(LayoutPage, self).fill() - self.menubar[:] = self.nav + self.menubar[:] = [] + self.menubar.append(self.nav) Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Fri Jan 12 13:35:47 2007 @@ -19,14 +19,23 @@ " docstring someclass " def __init__(self, somevar): self.somevar = somevar + def get_somevar(self): + " get_somevar docstring " return self.somevar """)) + temp.ensure('pkg/somesubclass.py').write(py.code.Source(""" + from someclass import SomeClass + class SomeSubClass(SomeClass): + def get_somevar(self): + return self.somevar + 1 + """)) temp.ensure("pkg/__init__.py").write(py.code.Source(""" from py.initpkg import initpkg initpkg(__name__, exportdefs = { 'pkg.sub.func': ("./func.py", "func"), 'pkg.SomeClass': ('./someclass.py', 'SomeClass'), + 'pkg.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), }) """)) return temp, 'pkg' @@ -47,6 +56,7 @@ def setup_method(self, meth): self.linker = Linker() + self.linker.set_root(self.fs_root.strpath) def test_build_full_method_view(self): snippet = build_full_method_view(self.linker, self.dsa, 'pkg.sub.func') @@ -61,6 +71,7 @@ funcfile = base.join('api/pkg.sub.func.html') assert funcfile.check() html = funcfile.read() + print html parseString(html) if option.webcheck: webcheck.check_html(html) @@ -71,3 +82,42 @@ print html parseString(html) + def test_build_class_api_pages(self): + base = py.test.ensuretemp('build_class_api_pages') + build_class_api_pages(self.project, self.linker, self.dsa, base, + ['pkg.SomeClass']) + clsfile = base.join('api/pkg.SomeClass.html') + assert clsfile.check() + html = clsfile.read() + print html + parseString(html) + if option.webcheck: + webcheck.check_html(html) + + def test_build_class_api_pages_nav_links(self): + base = py.test.ensuretemp('build_class_api_pages_nav_links') + self.linker.root = base.strpath + build_class_api_pages(self.project, self.linker, self.dsa, base, + ['pkg.SomeSubClass', 'pkg.SomeClass']) + clsfile = base.join('api/pkg.SomeClass.html') + assert clsfile.check() + html = clsfile.read() + print html + parseString(html) + if option.webcheck: + webcheck.check_html(html) + assert html.find('href="%s/api/pkg.SomeClass.__init__.html">__init__' % ( + base,)) > -1 + + def test_build_class_api_pages_base_link(self): + base = py.test.ensuretemp('build_class_api_pages_base_link') + self.linker.root = base.strpath + build_class_api_pages(self.project, self.linker, self.dsa, base, + ['pkg.SomeSubClass', 'pkg.SomeClass']) + clsfile = base.join('api/pkg.SomeSubClass.html') + assert clsfile.check() + html = clsfile.read() + print html + assert html.find('href="%s/api/pkg.SomeClass.html">pkg.SomeClass' % ( + base,)) > -1 + From guido at codespeak.net Fri Jan 12 14:06:45 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 12 Jan 2007 14:06:45 +0100 (CET) Subject: [py-svn] r36570 - in py/dist/py/apigen: . testing Message-ID: <20070112130645.098E910087@code0.codespeak.net> Author: guido Date: Fri Jan 12 14:06:35 2007 New Revision: 36570 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py Log: Building function page navigation, small HTML beautifications. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 12 14:06:35 2007 @@ -6,13 +6,13 @@ class ClassDescription(html.div): pass - class ClassDef(html.div): + class ClassDef(html.h1): pass class MethodDescription(html.div): pass - class MethodDef(html.div): + class MethodDef(html.h2): pass class ParameterDescription(html.div): @@ -43,7 +43,7 @@ docstring = func.__doc__ local_methodname = func.__name__ snippet = H.MethodDescription( - H.MethodDef(local_methodname, "(", params, "):"), + H.MethodDef(local_methodname, params, ":"), H.Docstring(docstring), ) return snippet @@ -55,7 +55,7 @@ docstring = func.__doc__ local_methodname = func.__name__ snippet = H.MethodDescription( - H.MethodDef(local_methodname, "(", params, "):"), + H.MethodDef(local_methodname, params, ":"), H.Docstring(docstring), # XXX more info here... ) @@ -84,6 +84,7 @@ H.ClassDef('%s(' % (cls.__name__,), *basehtml), H.Docstring(docstring), ) + snippet.append(H.h2('Functions:')) for method in methods: snippet += build_short_method_view(linker, dsa, '%s.%s' % (dotted_name, method)) @@ -98,8 +99,6 @@ return H.Navigation('navigation') def build_class_navigation(linker, dsa, dotted_name): - # import dotted name - # build nav from sub items + parent navitems = [] path = dotted_name.split('.') indent = 0 @@ -137,6 +136,45 @@ # build nav from self + parent + parent's items return H.Navigation('navigation') +def build_function_navigation(linker, dsa, dotted_name): + navitems = [] + path = dotted_name.split('.') + indent = 0 + if len(path) > 1: + parent_dotted_name = '.'.join(path[:-1]) + navitems.append(indent * 2 * u'\xa0') + try: + obj = dsa.get_obj(parent_dotted_name) + except KeyError: + navitems.append(parent_dotted_name) + navitems.append(H.br()) + indent += 1 + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(dotted_name, + href=linker.get_lazyhref(dotted_name)))) + else: + navitems.append(H.NavigationItem( + H.a(parent_dotted_name, + href=linker.get_lazyhref( + parent_dotted_name)))) + navitems.append(H.br()) + indent += 1 + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(dotted_name.split('.')[-1], + href=linker.get_lazyhref(dotted_name)))) + navitems.append(H.br()) + indent += 1 + for funcname in dsa.get_class_methods(dotted_name): + func_dotted_name = '%s.%s' % (dotted_name, funcname) + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(funcname, + href=linker.get_lazyhref(func_dotted_name)))) + navitems.append(H.br()) + return H.Navigation(*navitems) + # the build_*_pages methods all do two-pass page generation (first pass builds # the link db, second uses it) def build_source_pages(linker, base): @@ -166,7 +204,8 @@ dsa.get_class_methods(dotted_name)] build_method_api_pages(project, linker, dsa, base, method_dotted_names) for dotted_name, tag, nav, reltargetpath in passed: - page = wrap_page(project, linker, tag, nav) + page = wrap_page(project, linker, + 'api documentation for %s' % (dotted_name,), tag, nav) content = page.unicode() targetpath = base.ensure(reltargetpath) targetpath.write(content.encode("utf8")) @@ -174,20 +213,41 @@ def build_method_api_pages(project, linker, dsa, base, methods_dotted_names): passed = [] for dotted_name in methods_dotted_names: + print 'building full method view for', dotted_name + tag = build_full_method_view(linker, dsa, dotted_name) + nav = build_class_navigation(linker, dsa, + '.'.join(dotted_name.split('.')[:-1])) + reltargetpath = "api/%s.html" % (dotted_name,) + linker.set_link(dotted_name, reltargetpath) + passed.append((dotted_name, tag, nav, reltargetpath)) + for dotted_name, tag, nav, reltargetpath in passed: + page = wrap_page(project, linker, + 'api documentation for %s' % (dotted_name,), tag, nav) + content = page.unicode() + targetpath = base.join(reltargetpath) + targetpath.ensure() + targetpath.write(content.encode("utf8")) + +def build_function_api_pages(project, linker, dsa, base, methods_dotted_names): + passed = [] + for dotted_name in methods_dotted_names: + print 'building function view for', dotted_name + # XXX should we call build_full_function_view instead? tag = build_full_method_view(linker, dsa, dotted_name) - nav = build_method_navigation(linker, dotted_name) + nav = build_function_navigation(linker, dsa, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) for dotted_name, tag, nav, reltargetpath in passed: - page = wrap_page(project, linker, tag, nav) + page = wrap_page(project, linker, + 'api documentation for %s' % (dotted_name,), tag, nav) content = page.unicode() targetpath = base.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) -def wrap_page(project, linker, contentel, navel): - page = LayoutPage(project, 'XXX Page Title', nav=navel) +def wrap_page(project, linker, title, contentel, navel): + page = LayoutPage(project, title, nav=navel) page.set_content(contentel) return page Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Fri Jan 12 14:06:35 2007 @@ -27,6 +27,7 @@ temp.ensure('pkg/somesubclass.py').write(py.code.Source(""" from someclass import SomeClass class SomeSubClass(SomeClass): + " docstring somesubclass " def get_somevar(self): return self.somevar + 1 """)) @@ -64,9 +65,9 @@ print html parseString(html) - def test_build_method_api_pages(self): - base = py.test.ensuretemp('build_method_api_pages') - build_method_api_pages(self.project, self.linker, self.dsa, base, + def test_build_function_api_pages(self): + base = py.test.ensuretemp('build_function_api_pages') + build_function_api_pages(self.project, self.linker, self.dsa, base, ['pkg.sub.func']) funcfile = base.join('api/pkg.sub.func.html') assert funcfile.check() From guido at codespeak.net Fri Jan 12 17:21:48 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 12 Jan 2007 17:21:48 +0100 (CET) Subject: [py-svn] r36589 - in py/dist/py/apigen: . source testing Message-ID: <20070112162148.99C141007D@code0.codespeak.net> Author: guido Date: Fri Jan 12 17:21:16 2007 New Revision: 36589 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/layout.py py/dist/py/apigen/source/browser.py py/dist/py/apigen/testing/test_apigen_example.py Log: First source pages get built and linked to. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 12 17:21:16 2007 @@ -1,7 +1,12 @@ import py +import inspect from py.__.apigen.layout import LayoutPage +from py.__.apigen.source import browser as source_browser +from py.__.apigen.source import html as source_html html = py.xml.html +raw = py.xml.raw + class H(html): class ClassDescription(html.div): pass @@ -30,13 +35,16 @@ class BaseDescription(html.a): pass + class SourceDef(html.pre): + style = html.Style(whitespace='pre') + def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect # XXX copy and modify formatargspec to produce html return H.em(inspect.formatargspec(*inspect.getargspec(func))) -def build_full_method_view(linker, dsa, dotted_name): +def build_full_method_view(base, linker, dsa, dotted_name): """ build the html for a class method """ func = dsa.get_obj(dotted_name) params = get_param_htmldesc(linker, func) @@ -48,7 +56,7 @@ ) return snippet -def build_short_method_view(linker, dsa, dotted_name): +def build_short_method_view(base, linker, dsa, dotted_name): """ build the html for a class method """ func = dsa.get_obj(dotted_name) params = get_param_htmldesc(linker, func) @@ -61,9 +69,12 @@ ) return snippet -def build_class_view(linker, dsa, dotted_name): +def build_class_view(base, linker, dsa, dotted_name): """ build the html for a class """ cls = dsa.get_obj(dotted_name) + sourcefile = inspect.getsourcefile(cls) + if sourcefile[-1] in ['o', 'c']: + sourcefile = sourcefile[:-1] docstring = cls.__doc__ methods = dsa.get_class_methods(dotted_name) basehtml = [] @@ -82,17 +93,19 @@ snippet = H.ClassDescription( # XXX bases HTML H.ClassDef('%s(' % (cls.__name__,), *basehtml), + H.a('source', href=linker.get_lazyhref(sourcefile)), H.Docstring(docstring), ) snippet.append(H.h2('Functions:')) for method in methods: - snippet += build_short_method_view(linker, dsa, '%s.%s' % (dotted_name, - method)) + snippet += build_short_method_view(base, linker, dsa, + '%s.%s' % (dotted_name, method)) return snippet -def build_method_view(linker, dsa, dotted_name): +def build_method_view(base, linker, dsa, dotted_name): pass +# XXX nav functions need cleanup!! def build_package_navigation(linker, dotted_name): # import dotted name # build nav from sub items @@ -175,26 +188,61 @@ navitems.append(H.br()) return H.Navigation(*navitems) +re = py.std.re +_reg_body = re.compile(r']*>(.*)', re.S) +def build_source_python_page(fspath): + mod = source_browser.parse_path(fspath) + # XXX let's cheat a bit here... there should be a different function using + # the linker, and returning a proper py.xml.html element, at some point + html = source_html.create_html(mod) + snippet = _reg_body.search(html).group(1) + tag = H.SourceDef(raw(snippet)) + nav = H.Navigation('navigation') + return tag, nav + +def build_source_dir_page(fspath): + tag = H.div('content') + nav = H.Navigation('navigation') + return tag, nav + +def build_source_nonpython_page(fspath): + tag = H.div('content') + nav = H.Navigation('navigation') + return tag, nav + # the build_*_pages methods all do two-pass page generation (first pass builds # the link db, second uses it) -def build_source_pages(linker, base): - """ build syntax-colored source views """ +def prepare_source_pages(linker, base, outputbase): passed = [] - for fspath in base.visit("*.py"): + for fspath in base.visit(): relfspath = fspath.relto(base) - reloutputpath = "source/" + relfspath + ".html" - linker.set_link(relfspath, reloutputpath) - passed.append((fspath, reloutputpath)) - for fspath, reloutputpath in passed: - targetpath = outputbase.join(reloutputpath) - page = build_source_page(fspath) - targetpath.write(page.unicode().encode("utf8")) + if fspath.check(dir=True): + reloutputpath = 'source/%s/index.html' % (relfspath,) + else: + reloutputpath = "source/%s.html" % (relfspath,) + print reloutputpath + outputpath = outputbase.join(reloutputpath) + linker.set_link(str(fspath), reloutputpath) + passed.append((fspath, outputpath)) + return passed -def build_class_api_pages(project, linker, dsa, base, classes_dotted_names): - """ build the full api pages for a set of classes """ +def build_source_pages(data, project, outputbase): + """ build syntax-colored source views """ + for fspath, outputpath in data: + if fspath.check(ext='.py'): + tag, nav = build_source_python_page(fspath) + elif fspath.check(dir=True): + tag, nav = build_source_dir_page(fspath) + else: + tag, nav = build_source_nonpython_page(fspath) + page = wrap_page(project, 'sources for %s' % (fspath,), tag, nav) + outputpath.ensure() + outputpath.write(page.unicode().encode("utf8")) + +def prepare_class_api_pages(linker, dsa, base, classes_dotted_names): passed = [] for dotted_name in classes_dotted_names: - tag = build_class_view(linker, dsa, dotted_name) + tag = build_class_view(base, linker, dsa, dotted_name) nav = build_class_navigation(linker, dsa, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) @@ -202,51 +250,59 @@ method_dotted_names = ['%s.%s' % (dotted_name, method_name) for method_name in dsa.get_class_methods(dotted_name)] - build_method_api_pages(project, linker, dsa, base, method_dotted_names) - for dotted_name, tag, nav, reltargetpath in passed: - page = wrap_page(project, linker, - 'api documentation for %s' % (dotted_name,), tag, nav) + methodsdata = prepare_method_api_pages(linker, dsa, base, + method_dotted_names) + return passed, methodsdata + +def build_class_api_pages(data, project, outputbase): + """ build the full api pages for a set of classes """ + for dotted_name, tag, nav, reltargetpath in data: + page = wrap_page(project, 'api documentation for %s' % (dotted_name,), tag, nav) content = page.unicode() - targetpath = base.ensure(reltargetpath) + targetpath = outputbase.ensure(reltargetpath) targetpath.write(content.encode("utf8")) -def build_method_api_pages(project, linker, dsa, base, methods_dotted_names): +def prepare_method_api_pages(linker, dsa, base, methods_dotted_names): passed = [] for dotted_name in methods_dotted_names: - print 'building full method view for', dotted_name - tag = build_full_method_view(linker, dsa, dotted_name) + tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_class_navigation(linker, dsa, '.'.join(dotted_name.split('.')[:-1])) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) - for dotted_name, tag, nav, reltargetpath in passed: - page = wrap_page(project, linker, - 'api documentation for %s' % (dotted_name,), tag, nav) + return passed + +def build_method_api_pages(data, project, outputbase): + for dotted_name, tag, nav, reltargetpath in data: + page = wrap_page(project, 'api documentation for %s' % (dotted_name,), + tag, nav) content = page.unicode() - targetpath = base.join(reltargetpath) + targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) -def build_function_api_pages(project, linker, dsa, base, methods_dotted_names): +def prepare_function_api_pages(linker, dsa, base, methods_dotted_names): passed = [] for dotted_name in methods_dotted_names: - print 'building function view for', dotted_name - # XXX should we call build_full_function_view instead? - tag = build_full_method_view(linker, dsa, dotted_name) + # XXX should we create a build_full_function_view instead? + tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_function_navigation(linker, dsa, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) - for dotted_name, tag, nav, reltargetpath in passed: - page = wrap_page(project, linker, - 'api documentation for %s' % (dotted_name,), tag, nav) + return passed + +def build_function_api_pages(data, project, outputbase): + for dotted_name, tag, nav, reltargetpath in data: + page = wrap_page(project, 'api documentation for %s' % (dotted_name,), + tag, nav) content = page.unicode() - targetpath = base.join(reltargetpath) + targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) -def wrap_page(project, linker, title, contentel, navel): +def wrap_page(project, title, contentel, navel): page = LayoutPage(project, title, nav=navel) page.set_content(contentel) return page Modified: py/dist/py/apigen/layout.py ============================================================================== --- py/dist/py/apigen/layout.py (original) +++ py/dist/py/apigen/layout.py Fri Jan 12 17:21:16 2007 @@ -13,7 +13,7 @@ super(LayoutPage, self).__init__(*args, **kwargs) def set_content(self, contentel): - self.contentspace += contentel + self.contentspace.append(contentel) def fill(self): super(LayoutPage, self).fill() Modified: py/dist/py/apigen/source/browser.py ============================================================================== --- py/dist/py/apigen/source/browser.py (original) +++ py/dist/py/apigen/source/browser.py Fri Jan 12 17:21:16 2007 @@ -135,3 +135,4 @@ except (ImportError, AttributeError): pass return Module(path, mod_dict) + Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Fri Jan 12 17:21:16 2007 @@ -10,11 +10,11 @@ def setup_fs_project(): temp = py.test.ensuretemp('apigen_example') - temp.ensure("pkg/func.py").write(py.code.Source(""" + temp.ensure("pkg/func.py").write(py.code.Source("""\ def func(arg1): "docstring" """)) - temp.ensure('pkg/someclass.py').write(py.code.Source(""" + temp.ensure('pkg/someclass.py').write(py.code.Source("""\ class SomeClass(object): " docstring someclass " def __init__(self, somevar): @@ -24,14 +24,14 @@ " get_somevar docstring " return self.somevar """)) - temp.ensure('pkg/somesubclass.py').write(py.code.Source(""" + temp.ensure('pkg/somesubclass.py').write(py.code.Source("""\ from someclass import SomeClass class SomeSubClass(SomeClass): " docstring somesubclass " def get_somevar(self): return self.somevar + 1 """)) - temp.ensure("pkg/__init__.py").write(py.code.Source(""" + temp.ensure("pkg/__init__.py").write(py.code.Source("""\ from py.initpkg import initpkg initpkg(__name__, exportdefs = { 'pkg.sub.func': ("./func.py", "func"), @@ -60,15 +60,18 @@ self.linker.set_root(self.fs_root.strpath) def test_build_full_method_view(self): - snippet = build_full_method_view(self.linker, self.dsa, 'pkg.sub.func') + base = py.test.ensuretemp('build_full_method_view') + snippet = build_full_method_view(base, self.linker, self.dsa, + 'pkg.sub.func') html = snippet.unicode() print html parseString(html) def test_build_function_api_pages(self): base = py.test.ensuretemp('build_function_api_pages') - build_function_api_pages(self.project, self.linker, self.dsa, base, - ['pkg.sub.func']) + data = prepare_function_api_pages(self.linker, self.dsa, + base, ['pkg.sub.func']) + build_function_api_pages(data, self.project, base) funcfile = base.join('api/pkg.sub.func.html') assert funcfile.check() html = funcfile.read() @@ -78,15 +81,21 @@ webcheck.check_html(html) def test_build_class_view(self): - snippet = build_class_view(self.linker, self.dsa, 'pkg.SomeClass') + base = py.test.ensuretemp('build_class_view') + prepare_source_pages(self.linker, self.fs_root, base) + snippet = build_class_view(base, self.linker, self.dsa, + 'pkg.SomeClass') html = snippet.unicode() print html parseString(html) def test_build_class_api_pages(self): base = py.test.ensuretemp('build_class_api_pages') - build_class_api_pages(self.project, self.linker, self.dsa, base, - ['pkg.SomeClass']) + data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, + base, ['pkg.SomeClass']) + prepare_source_pages(self.linker, self.fs_root, base) + build_class_api_pages(data, self.project, base) + build_method_api_pages(methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() @@ -98,8 +107,11 @@ def test_build_class_api_pages_nav_links(self): base = py.test.ensuretemp('build_class_api_pages_nav_links') self.linker.root = base.strpath - build_class_api_pages(self.project, self.linker, self.dsa, base, - ['pkg.SomeSubClass', 'pkg.SomeClass']) + data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, + base, ['pkg.SomeSubClass', 'pkg.SomeClass']) + prepare_source_pages(self.linker, self.fs_root, base) + build_class_api_pages(data, self.project, base) + build_method_api_pages(methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() @@ -107,14 +119,18 @@ parseString(html) if option.webcheck: webcheck.check_html(html) - assert html.find('href="%s/api/pkg.SomeClass.__init__.html">__init__' % ( - base,)) > -1 + assert html.find( + 'href="%s/api/pkg.SomeClass.__init__.html">__init__' % (base,) + ) > -1 def test_build_class_api_pages_base_link(self): base = py.test.ensuretemp('build_class_api_pages_base_link') self.linker.root = base.strpath - build_class_api_pages(self.project, self.linker, self.dsa, base, - ['pkg.SomeSubClass', 'pkg.SomeClass']) + data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, + base, ['pkg.SomeSubClass', 'pkg.SomeClass']) + prepare_source_pages(self.linker, self.fs_root, base) + build_class_api_pages(data, self.project, base) + build_method_api_pages(methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeSubClass.html') assert clsfile.check() html = clsfile.read() @@ -122,3 +138,27 @@ assert html.find('href="%s/api/pkg.SomeClass.html">pkg.SomeClass' % ( base,)) > -1 + def test_build_source_pages(self): + base = py.test.ensuretemp('build_source_pages') + data = prepare_source_pages(self.linker, self.fs_root, base) + build_source_pages(data, self.project, base) + somesource = base.join('source/pkg/func.py.html').read() + parseString(somesource) + if option.webcheck: + webcheck.check_html(somesource) + + def test_api_source_links(self): + base = py.test.ensuretemp('api_source_links') + self.linker.root = base.strpath + data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, + base, ['pkg.SomeSubClass', 'pkg.SomeClass']) + sourcedata = prepare_source_pages(self.linker, self.fs_root, base) + build_class_api_pages(data, self.project, base) + build_method_api_pages(methodsdata, self.project, base) + build_source_pages(sourcedata, self.project, base) + funchtml = base.join('api/pkg.SomeClass.html').read() + parseString(funchtml) + assert funchtml.find( + 'href="%s/source/pkg/someclass.py.html"' % (base,) + ) > -1 + From guido at codespeak.net Fri Jan 12 18:51:35 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 12 Jan 2007 18:51:35 +0100 (CET) Subject: [py-svn] r36606 - in py/dist/py/apigen: . testing Message-ID: <20070112175135.9F10010078@code0.codespeak.net> Author: guido Date: Fri Jan 12 18:51:17 2007 New Revision: 36606 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/layout.py py/dist/py/apigen/linker.py py/dist/py/apigen/style.css py/dist/py/apigen/testing/test_apigen_example.py Log: Added source links in function pages, made all paths relative (using base tag). Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 12 18:51:17 2007 @@ -35,7 +35,7 @@ class BaseDescription(html.a): pass - class SourceDef(html.pre): + class SourceDef(html.div): style = html.Style(whitespace='pre') def get_param_htmldesc(linker, func): @@ -47,11 +47,13 @@ def build_full_method_view(base, linker, dsa, dotted_name): """ build the html for a class method """ func = dsa.get_obj(dotted_name) + sourcefile = inspect.getsourcefile(func) params = get_param_htmldesc(linker, func) docstring = func.__doc__ local_methodname = func.__name__ snippet = H.MethodDescription( H.MethodDef(local_methodname, params, ":"), + H.a('source', href=linker.get_lazyhref(sourcefile)), H.Docstring(docstring), ) return snippet @@ -235,7 +237,8 @@ tag, nav = build_source_dir_page(fspath) else: tag, nav = build_source_nonpython_page(fspath) - page = wrap_page(project, 'sources for %s' % (fspath,), tag, nav) + page = wrap_page(project, 'sources for %s' % (fspath.basename,), + tag, nav, outputbase) outputpath.ensure() outputpath.write(page.unicode().encode("utf8")) @@ -257,7 +260,8 @@ def build_class_api_pages(data, project, outputbase): """ build the full api pages for a set of classes """ for dotted_name, tag, nav, reltargetpath in data: - page = wrap_page(project, 'api documentation for %s' % (dotted_name,), tag, nav) + page = wrap_page(project, 'api documentation for %s' % (dotted_name,), + tag, nav, outputbase) content = page.unicode() targetpath = outputbase.ensure(reltargetpath) targetpath.write(content.encode("utf8")) @@ -276,7 +280,7 @@ def build_method_api_pages(data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav) + tag, nav, outputbase) content = page.unicode() targetpath = outputbase.join(reltargetpath) targetpath.ensure() @@ -296,14 +300,19 @@ def build_function_api_pages(data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav) + tag, nav, outputbase) content = page.unicode() targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) -def wrap_page(project, title, contentel, navel): - page = LayoutPage(project, title, nav=navel) +def wrap_page(project, title, contentel, navel, outputpath): + page = LayoutPage(project, title, nav=navel, encoding='UTF-8', + stylesheeturl='style.css', + base='file://%s/' % (outputpath,)) page.set_content(contentel) + here = py.magic.autopath().dirpath() + style = here.join('style.css').read() + outputpath.join('style.css').write(style) return page Modified: py/dist/py/apigen/layout.py ============================================================================== --- py/dist/py/apigen/layout.py (original) +++ py/dist/py/apigen/layout.py Fri Jan 12 18:51:17 2007 @@ -8,8 +8,12 @@ class LayoutPage(Page): """ this provides the layout and style information """ + + base = None + def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') + self.base = kwargs.pop('base', None) super(LayoutPage, self).__init__(*args, **kwargs) def set_content(self, contentel): @@ -19,4 +23,6 @@ super(LayoutPage, self).fill() self.menubar[:] = [] self.menubar.append(self.nav) + if self.base: + self.head.insert(0, py.xml.html.base(href=self.base)) Modified: py/dist/py/apigen/linker.py ============================================================================== --- py/dist/py/apigen/linker.py (original) +++ py/dist/py/apigen/linker.py Fri Jan 12 18:51:17 2007 @@ -15,13 +15,8 @@ class Linker(object): def __init__(self): - self.root = None self._linkid2target = {} - def set_root(self, root): - assert self.root is None - self.root = root - def get_lazyhref(self, linkid): return LazyHref(self, linkid) @@ -30,5 +25,5 @@ self._linkid2target[linkid] = target def get_target(self, linkid): - return '%s/%s' % (self.root, self._linkid2target[linkid]) + return self._linkid2target[linkid] Modified: py/dist/py/apigen/style.css ============================================================================== --- py/dist/py/apigen/style.css (original) +++ py/dist/py/apigen/style.css Fri Jan 12 18:51:17 2007 @@ -40,3 +40,44 @@ list-style-type: none; } +a { + color: blue; + font-weight: bold; + text-decoration: none; +} + +a:hover { + color: #005; +} + +.lineno { + text-align: right; + color: #555; + width: 3em; + padding-right: 1em; + border: 0px solid black; + border-right-width: 1px; +} + +.code { + padding-left: 1em; + white-space: pre; + font-family: monospace, Monaco; +} + +.comment { + color: purple; +} + +.string { + color: #777; +} + +.keyword { + color: blue; +} + +.alt_keyword { + color: green; +} + Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Fri Jan 12 18:51:17 2007 @@ -57,10 +57,10 @@ def setup_method(self, meth): self.linker = Linker() - self.linker.set_root(self.fs_root.strpath) def test_build_full_method_view(self): base = py.test.ensuretemp('build_full_method_view') + prepare_source_pages(self.linker, self.fs_root, base) snippet = build_full_method_view(base, self.linker, self.dsa, 'pkg.sub.func') html = snippet.unicode() @@ -69,6 +69,7 @@ def test_build_function_api_pages(self): base = py.test.ensuretemp('build_function_api_pages') + prepare_source_pages(self.linker, self.fs_root, base) data = prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) build_function_api_pages(data, self.project, base) @@ -106,7 +107,6 @@ def test_build_class_api_pages_nav_links(self): base = py.test.ensuretemp('build_class_api_pages_nav_links') - self.linker.root = base.strpath data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, ['pkg.SomeSubClass', 'pkg.SomeClass']) prepare_source_pages(self.linker, self.fs_root, base) @@ -120,12 +120,10 @@ if option.webcheck: webcheck.check_html(html) assert html.find( - 'href="%s/api/pkg.SomeClass.__init__.html">__init__' % (base,) - ) > -1 + 'href="api/pkg.SomeClass.__init__.html">__init__') > -1 def test_build_class_api_pages_base_link(self): base = py.test.ensuretemp('build_class_api_pages_base_link') - self.linker.root = base.strpath data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, ['pkg.SomeSubClass', 'pkg.SomeClass']) prepare_source_pages(self.linker, self.fs_root, base) @@ -135,8 +133,7 @@ assert clsfile.check() html = clsfile.read() print html - assert html.find('href="%s/api/pkg.SomeClass.html">pkg.SomeClass' % ( - base,)) > -1 + assert html.find('href="api/pkg.SomeClass.html">pkg.SomeClass') > -1 def test_build_source_pages(self): base = py.test.ensuretemp('build_source_pages') @@ -149,7 +146,6 @@ def test_api_source_links(self): base = py.test.ensuretemp('api_source_links') - self.linker.root = base.strpath data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, ['pkg.SomeSubClass', 'pkg.SomeClass']) sourcedata = prepare_source_pages(self.linker, self.fs_root, base) @@ -158,7 +154,5 @@ build_source_pages(sourcedata, self.project, base) funchtml = base.join('api/pkg.SomeClass.html').read() parseString(funchtml) - assert funchtml.find( - 'href="%s/source/pkg/someclass.py.html"' % (base,) - ) > -1 + assert funchtml.find('href="source/pkg/someclass.py.html"') > -1 From guido at codespeak.net Fri Jan 12 19:07:54 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 12 Jan 2007 19:07:54 +0100 (CET) Subject: [py-svn] r36608 - in py/dist/py/apigen: . testing Message-ID: <20070112180754.ED40C1007C@code0.codespeak.net> Author: guido Date: Fri Jan 12 19:07:43 2007 New Revision: 36608 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/style.css py/dist/py/apigen/testing/test_linker.py Log: Some style fixes, updated linker tests (oops). Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 12 19:07:43 2007 @@ -8,13 +8,16 @@ raw = py.xml.raw class H(html): - class ClassDescription(html.div): + class Description(html.div): + style = html.Style(margin_left='10em') + + class ClassDescription(Description): pass class ClassDef(html.h1): pass - class MethodDescription(html.div): + class MethodDescription(Description): pass class MethodDef(html.h2): @@ -27,7 +30,7 @@ pass class Navigation(html.div): - pass + style = html.Style(min_height='99%', float='left', margin_top='1.2em') class NavigationItem(html.span): pass @@ -36,7 +39,7 @@ pass class SourceDef(html.div): - style = html.Style(whitespace='pre') + pass def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ Modified: py/dist/py/apigen/style.css ============================================================================== --- py/dist/py/apigen/style.css (original) +++ py/dist/py/apigen/style.css Fri Jan 12 19:07:43 2007 @@ -20,6 +20,19 @@ display: none; } +#logo { + /* float: left; */ +} + +#logo img { + border-width: 0px; +} + +.project_title { + font-size: 2em; + font-weight: bold; +} + body, div, p, h1, h2, h3, h4 { font-family: Trebuchet MS, Verdana, Arial; background-color: #FFE; @@ -40,7 +53,7 @@ list-style-type: none; } -a { +.code a { color: blue; font-weight: bold; text-decoration: none; Modified: py/dist/py/apigen/testing/test_linker.py ============================================================================== --- py/dist/py/apigen/testing/test_linker.py (original) +++ py/dist/py/apigen/testing/test_linker.py Fri Jan 12 19:07:43 2007 @@ -5,21 +5,19 @@ class TestLinker(object): def test_one_lazy_link(self): linker = Linker() - linker.set_root('/root') lazyhref = linker.get_lazyhref('py.path.local') linker.set_link('py.path.local', 'py/path/local.html') href = unicode(lazyhref) - assert href == '/root/py/path/local.html' + assert href == 'py/path/local.html' def test_source_link_getrelfspath(self): linker = Linker() - linker.set_root('/root') relfspath = getrelfspath('py.path.local') lazyhref = linker.get_lazyhref(relfspath) linker.set_link(relfspath, "hello/world") href = unicode(lazyhref) - assert href == '/root/hello/world' + assert href == 'hello/world' # considerations for futurue use """ @@ -28,7 +26,7 @@ def create_method_html(dotted_name, dsa, linker): ... - # + # a = linker.make_source_link(dotted_name) ... From hpk at codespeak.net Sat Jan 13 07:35:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 07:35:25 +0100 (CET) Subject: [py-svn] r36629 - py/dist/py/bin Message-ID: <20070113063525.047BD10080@code0.codespeak.net> Author: hpk Date: Sat Jan 13 07:35:23 2007 New Revision: 36629 Modified: py/dist/py/bin/py.cleanup Log: remove unused variable (requiring an sys.arg) Modified: py/dist/py/bin/py.cleanup ============================================================================== --- py/dist/py/bin/py.cleanup (original) +++ py/dist/py/bin/py.cleanup Sat Jan 13 07:35:23 2007 @@ -16,7 +16,6 @@ if __name__ == '__main__': (options, args) = parser.parse_args() - string = args[0] if len(args) >= 1: path = py.path.local(args) else: From hpk at codespeak.net Sat Jan 13 07:36:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 07:36:50 +0100 (CET) Subject: [py-svn] r36630 - py/dist/py/documentation Message-ID: <20070113063650.D858310080@code0.codespeak.net> Author: hpk Date: Sat Jan 13 07:36:49 2007 New Revision: 36630 Modified: py/dist/py/documentation/TODO.txt Log: small updates to TODO.txt file Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Sat Jan 13 07:36:49 2007 @@ -55,8 +55,7 @@ script to produce api and source code documentation * deploy the above "py.test --apigen" run on codespeak - regularly, determine good locations and a good - directory and URL scheme . + regularly, determine directory locations and URL namespace design. * integrate rest directive into py/documentation/conftest.py with help code from py.__.rest.directive.... @@ -65,7 +64,7 @@ testing ----------- -* windows tests +* windows tests (rev 36514 passes without errors, many skips) * these should all work on 0.9 and on the py lib and pypy: - running "py.test -s" - running "py.test --pdb" From hpk at codespeak.net Sat Jan 13 08:16:20 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 08:16:20 +0100 (CET) Subject: [py-svn] r36631 - py/dist/py/execnet Message-ID: <20070113071620.616E610080@code0.codespeak.net> Author: hpk Date: Sat Jan 13 08:16:19 2007 New Revision: 36631 Modified: py/dist/py/execnet/register.py Log: fix slightly weird logic for getting to a sensible hostname in remote socket server installation Modified: py/dist/py/execnet/register.py ============================================================================== --- py/dist/py/execnet/register.py (original) +++ py/dist/py/execnet/register.py Sat Jan 13 08:16:19 2007 @@ -160,7 +160,7 @@ # execute the above socketserverbootstrap on the other side channel = gateway.remote_exec(socketserverbootstrap) hostname, (realhost, realport) = channel.receive() - if hostport is None: + if not hostname: realhost = hostname #gateway._trace("remote_install received" # "port=%r, hostname = %r" %(realport, hostname)) From hpk at codespeak.net Sat Jan 13 08:18:38 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 08:18:38 +0100 (CET) Subject: [py-svn] r36632 - in py/dist: . py/apigen py/builtin py/builtin/testing py/code py/code/testing py/compat py/compat/testing py/documentation py/documentation/apigen py/documentation/future py/documentation/talk py/execnet/script py/misc py/misc/testing py/path/local/testing py/path/svn py/path/svn/testing py/rest py/rest/testing py/rest/testing/data py/test/rsession py/test/rsession/testing py/test/testing/data py/test/web Message-ID: <20070113071838.75C8F10080@code0.codespeak.net> Author: hpk Date: Sat Jan 13 08:18:32 2007 New Revision: 36632 Modified: py/dist/py/apigen/conftest.py (props changed) py/dist/py/apigen/layout.py (props changed) py/dist/py/apigen/project.py (props changed) py/dist/py/builtin/sorted.py (props changed) py/dist/py/builtin/testing/test_sorted.py (props changed) py/dist/py/code/safe_repr.py (props changed) py/dist/py/code/testing/test_safe_repr.py (props changed) py/dist/py/compat/subprocess.py (props changed) py/dist/py/compat/testing/test_subprocess.py (props changed) py/dist/py/documentation/apigen/ (props changed) py/dist/py/documentation/apigen.txt (props changed) py/dist/py/documentation/binary.txt (props changed) py/dist/py/documentation/future/ (props changed) py/dist/py/documentation/future/code_template.txt (props changed) py/dist/py/documentation/future/planning.txt (props changed) py/dist/py/documentation/future/rsession_todo.txt (props changed) py/dist/py/documentation/links.txt (props changed) py/dist/py/documentation/log.txt (props changed) py/dist/py/documentation/talk/ (props changed) py/dist/py/execnet/script/socketserverservice.py (contents, props changed) py/dist/py/misc/_dist.py (contents, props changed) py/dist/py/misc/findmissingdocstrings.py (props changed) py/dist/py/misc/svnlook.py (props changed) py/dist/py/misc/terminal_helper.py (props changed) py/dist/py/misc/testing/test_svnlook.py (props changed) py/dist/py/path/local/testing/test_win.py (contents, props changed) py/dist/py/path/svn/quoting.txt (props changed) py/dist/py/path/svn/testing/test_test_repo.py (props changed) py/dist/py/rest/testing/data/formula.txt (props changed) py/dist/py/rest/testing/data/formula1.txt (props changed) py/dist/py/rest/testing/test_htmlrest.py (props changed) py/dist/py/rest/testing/test_transform.py (props changed) py/dist/py/rest/transform.py (props changed) py/dist/py/test/rsession/rsync_remote.py (props changed) py/dist/py/test/rsession/testing/test_webjs.py (props changed) py/dist/py/test/testing/data/brokenrepr.py (props changed) py/dist/py/test/testing/data/disabled_module.py (props changed) py/dist/py/test/web/ (props changed) py/dist/py/test/web/__init__.py (props changed) py/dist/py/test/web/exception.py (props changed) py/dist/py/test/web/post_multipart.py (props changed) py/dist/py/test/web/webcheck.py (props changed) py/dist/setup.py (props changed) Log: fixeol Modified: py/dist/py/execnet/script/socketserverservice.py ============================================================================== --- py/dist/py/execnet/script/socketserverservice.py (original) +++ py/dist/py/execnet/script/socketserverservice.py Sat Jan 13 08:18:32 2007 @@ -1,91 +1,91 @@ -""" -A windows service wrapper for the py.execnet socketserver. - -To use, run: - python socketserverservice.py register - net start ExecNetSocketServer -""" - -import sys -import os -import time -import win32serviceutil -import win32service -import win32event -import win32evtlogutil -import servicemanager -import threading -import socketserver - - -appname = 'ExecNetSocketServer' - - -class SocketServerService(win32serviceutil.ServiceFramework): - _svc_name_ = appname - _svc_display_name_ = "%s" % appname - _svc_deps_ = ["EventLog"] - def __init__(self, args): - # The exe-file has messages for the Event Log Viewer. - # Register the exe-file as event source. - # - # Probably it would be better if this is done at installation time, - # so that it also could be removed if the service is uninstalled. - # Unfortunately it cannot be done in the 'if __name__ == "__main__"' - # block below, because the 'frozen' exe-file does not run this code. - # - win32evtlogutil.AddSourceToRegistry(self._svc_display_name_, - servicemanager.__file__, - "Application") - win32serviceutil.ServiceFramework.__init__(self, args) - self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) - self.WAIT_TIME = 1000 # in milliseconds - - - def SvcStop(self): - self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) - win32event.SetEvent(self.hWaitStop) - - - def SvcDoRun(self): - # Redirect stdout and stderr to prevent "IOError: [Errno 9] - # Bad file descriptor". Windows services don't have functional - # output streams. - sys.stdout = sys.stderr = open('nul', 'w') - - # Write a 'started' event to the event log... - win32evtlogutil.ReportEvent(self._svc_display_name_, - servicemanager.PYS_SERVICE_STARTED, - 0, # category - servicemanager.EVENTLOG_INFORMATION_TYPE, - (self._svc_name_, '')) - print "Begin: %s" % (self._svc_display_name_) - - hostport = ':8888' - print 'Starting py.execnet SocketServer on %s' % hostport - serversock = socketserver.bind_and_listen(hostport) - thread = threading.Thread(target=socketserver.startserver, - args=(serversock,), - kwargs={'loop':True}) - thread.setDaemon(True) - thread.start() - - # wait to be stopped or self.WAIT_TIME to pass - while True: - result = win32event.WaitForSingleObject(self.hWaitStop, - self.WAIT_TIME) - if result == win32event.WAIT_OBJECT_0: - break - - # write a 'stopped' event to the event log. - win32evtlogutil.ReportEvent(self._svc_display_name_, - servicemanager.PYS_SERVICE_STOPPED, - 0, # category - servicemanager.EVENTLOG_INFORMATION_TYPE, - (self._svc_name_, '')) - print "End: %s" % appname - - -if __name__ == '__main__': - # Note that this code will not be run in the 'frozen' exe-file!!! - win32serviceutil.HandleCommandLine(SocketServerService) +""" +A windows service wrapper for the py.execnet socketserver. + +To use, run: + python socketserverservice.py register + net start ExecNetSocketServer +""" + +import sys +import os +import time +import win32serviceutil +import win32service +import win32event +import win32evtlogutil +import servicemanager +import threading +import socketserver + + +appname = 'ExecNetSocketServer' + + +class SocketServerService(win32serviceutil.ServiceFramework): + _svc_name_ = appname + _svc_display_name_ = "%s" % appname + _svc_deps_ = ["EventLog"] + def __init__(self, args): + # The exe-file has messages for the Event Log Viewer. + # Register the exe-file as event source. + # + # Probably it would be better if this is done at installation time, + # so that it also could be removed if the service is uninstalled. + # Unfortunately it cannot be done in the 'if __name__ == "__main__"' + # block below, because the 'frozen' exe-file does not run this code. + # + win32evtlogutil.AddSourceToRegistry(self._svc_display_name_, + servicemanager.__file__, + "Application") + win32serviceutil.ServiceFramework.__init__(self, args) + self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) + self.WAIT_TIME = 1000 # in milliseconds + + + def SvcStop(self): + self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) + win32event.SetEvent(self.hWaitStop) + + + def SvcDoRun(self): + # Redirect stdout and stderr to prevent "IOError: [Errno 9] + # Bad file descriptor". Windows services don't have functional + # output streams. + sys.stdout = sys.stderr = open('nul', 'w') + + # Write a 'started' event to the event log... + win32evtlogutil.ReportEvent(self._svc_display_name_, + servicemanager.PYS_SERVICE_STARTED, + 0, # category + servicemanager.EVENTLOG_INFORMATION_TYPE, + (self._svc_name_, '')) + print "Begin: %s" % (self._svc_display_name_) + + hostport = ':8888' + print 'Starting py.execnet SocketServer on %s' % hostport + serversock = socketserver.bind_and_listen(hostport) + thread = threading.Thread(target=socketserver.startserver, + args=(serversock,), + kwargs={'loop':True}) + thread.setDaemon(True) + thread.start() + + # wait to be stopped or self.WAIT_TIME to pass + while True: + result = win32event.WaitForSingleObject(self.hWaitStop, + self.WAIT_TIME) + if result == win32event.WAIT_OBJECT_0: + break + + # write a 'stopped' event to the event log. + win32evtlogutil.ReportEvent(self._svc_display_name_, + servicemanager.PYS_SERVICE_STOPPED, + 0, # category + servicemanager.EVENTLOG_INFORMATION_TYPE, + (self._svc_name_, '')) + print "End: %s" % appname + + +if __name__ == '__main__': + # Note that this code will not be run in the 'frozen' exe-file!!! + win32serviceutil.HandleCommandLine(SocketServerService) Modified: py/dist/py/misc/_dist.py ============================================================================== --- py/dist/py/misc/_dist.py (original) +++ py/dist/py/misc/_dist.py Sat Jan 13 08:18:32 2007 @@ -100,7 +100,7 @@ reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" path = get_registry_value(reg, key, "Path") - path += ";" + bindir + path += ";" + bindir print "Setting PATH to:", path set_registry_value(reg, key, "Path", path) #print "Current PATH is:", get_registry_value(reg, key, "Path") @@ -120,16 +120,16 @@ 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": + 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) - + ### end helpers def setup(pkg, **kw): Modified: py/dist/py/path/local/testing/test_win.py ============================================================================== --- py/dist/py/path/local/testing/test_win.py (original) +++ py/dist/py/path/local/testing/test_win.py Sat Jan 13 08:18:32 2007 @@ -1,34 +1,34 @@ -import py - -class TestWINLocalPath: - #root = local(TestLocalPath.root) - disabled = py.std.sys.platform != 'win32' - - def setup_class(cls): - cls.root = py.test.ensuretemp(cls.__name__) - - def setup_method(self, method): - name = method.im_func.func_name - self.tmpdir = self.root.ensure(name, dir=1) - - def test_chmod_simple_int(self): - print "self.root is", self.root - mode = self.root.stat().st_mode - # Ensure that we actually change the mode to something different. - self.root.chmod(mode == 0 and 1 or 0) - try: - print self.root.stat().st_mode - print mode - assert self.root.stat().st_mode != mode - finally: - self.root.chmod(mode) - assert self.root.stat().st_mode == mode - - def test_allow_unix_style_paths(self): - t1 = self.root.join('a_path') - assert t1 == str(self.root) + '\\a_path' - t1 = self.root.join('a_path/') - assert t1 == str(self.root) + '\\a_path' - t1 = self.root.join('dir/a_path') - assert t1 == str(self.root) + '\\dir\\a_path' - +import py + +class TestWINLocalPath: + #root = local(TestLocalPath.root) + disabled = py.std.sys.platform != 'win32' + + def setup_class(cls): + cls.root = py.test.ensuretemp(cls.__name__) + + def setup_method(self, method): + name = method.im_func.func_name + self.tmpdir = self.root.ensure(name, dir=1) + + def test_chmod_simple_int(self): + print "self.root is", self.root + mode = self.root.stat().st_mode + # Ensure that we actually change the mode to something different. + self.root.chmod(mode == 0 and 1 or 0) + try: + print self.root.stat().st_mode + print mode + assert self.root.stat().st_mode != mode + finally: + self.root.chmod(mode) + assert self.root.stat().st_mode == mode + + def test_allow_unix_style_paths(self): + t1 = self.root.join('a_path') + assert t1 == str(self.root) + '\\a_path' + t1 = self.root.join('a_path/') + assert t1 == str(self.root) + '\\a_path' + t1 = self.root.join('dir/a_path') + assert t1 == str(self.root) + '\\dir\\a_path' + From hpk at codespeak.net Sat Jan 13 12:02:41 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 12:02:41 +0100 (CET) Subject: [py-svn] r36633 - py/dist/py/apigen/testing Message-ID: <20070113110241.A19701007D@code0.codespeak.net> Author: hpk Date: Sat Jan 13 12:02:33 2007 New Revision: 36633 Modified: py/dist/py/apigen/testing/test_apigen_example.py Log: mostly systematically use webcheck now Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Sat Jan 13 12:02:33 2007 @@ -4,7 +4,6 @@ from py.__.apigen.htmlgen import * from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor from py.__.apigen.project import Project -from xml.dom.minidom import parseString from py.__.test.web import webcheck from py.__.apigen.conftest import option @@ -49,6 +48,21 @@ dsa = DocStorageAccessor(ds) return dsa + +def _checkhtml(htmlstring): + assert isinstance(htmlstring, (unicode, str)) + if option.webcheck: + webcheck.check_html(htmlstring) + else: + py.test.skip("pass --webcheck to validate html produced in tests") + +def _checkhtmlsnippet(htmlstring): + # XXX wrap page around snippet and validate + pass + #newstring = """\n""" + unicode(h) + #_checkhtml(newstring) + class TestExampleProject(object): def setup_class(cls): cls.fs_root, cls.pkg_name = setup_fs_project() @@ -64,8 +78,7 @@ snippet = build_full_method_view(base, self.linker, self.dsa, 'pkg.sub.func') html = snippet.unicode() - print html - parseString(html) + _checkhtmlsnippet(html) def test_build_function_api_pages(self): base = py.test.ensuretemp('build_function_api_pages') @@ -76,10 +89,7 @@ funcfile = base.join('api/pkg.sub.func.html') assert funcfile.check() html = funcfile.read() - print html - parseString(html) - if option.webcheck: - webcheck.check_html(html) + _checkhtml(html) def test_build_class_view(self): base = py.test.ensuretemp('build_class_view') @@ -87,8 +97,7 @@ snippet = build_class_view(base, self.linker, self.dsa, 'pkg.SomeClass') html = snippet.unicode() - print html - parseString(html) + _checkhtmlsnippet(html) def test_build_class_api_pages(self): base = py.test.ensuretemp('build_class_api_pages') @@ -100,10 +109,7 @@ clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() - print html - parseString(html) - if option.webcheck: - webcheck.check_html(html) + _checkhtml(html) def test_build_class_api_pages_nav_links(self): base = py.test.ensuretemp('build_class_api_pages_nav_links') @@ -115,12 +121,9 @@ clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() - print html - parseString(html) - if option.webcheck: - webcheck.check_html(html) assert html.find( 'href="api/pkg.SomeClass.__init__.html">__init__') > -1 + _checkhtml(html) def test_build_class_api_pages_base_link(self): base = py.test.ensuretemp('build_class_api_pages_base_link') @@ -132,17 +135,15 @@ clsfile = base.join('api/pkg.SomeSubClass.html') assert clsfile.check() html = clsfile.read() - print html assert html.find('href="api/pkg.SomeClass.html">pkg.SomeClass') > -1 + _checkhtml(html) def test_build_source_pages(self): base = py.test.ensuretemp('build_source_pages') data = prepare_source_pages(self.linker, self.fs_root, base) build_source_pages(data, self.project, base) somesource = base.join('source/pkg/func.py.html').read() - parseString(somesource) - if option.webcheck: - webcheck.check_html(somesource) + _checkhtml(somesource) def test_api_source_links(self): base = py.test.ensuretemp('api_source_links') @@ -153,6 +154,6 @@ build_method_api_pages(methodsdata, self.project, base) build_source_pages(sourcedata, self.project, base) funchtml = base.join('api/pkg.SomeClass.html').read() - parseString(funchtml) assert funchtml.find('href="source/pkg/someclass.py.html"') > -1 + _checkhtml(funchtml) From cfbolz at codespeak.net Sat Jan 13 12:48:12 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sat, 13 Jan 2007 12:48:12 +0100 (CET) Subject: [py-svn] r36637 - py/dist/py/documentation Message-ID: <20070113114812.CDBE21007D@code0.codespeak.net> Author: cfbolz Date: Sat Jan 13 12:48:07 2007 New Revision: 36637 Added: py/dist/py/documentation/executables.txt - copied unchanged from r36636, py/dist/py/documentation/binary.txt Removed: py/dist/py/documentation/binary.txt Log: rename to executables Deleted: /py/dist/py/documentation/binary.txt ============================================================================== --- /py/dist/py/documentation/binary.txt Sat Jan 13 12:48:07 2007 +++ (empty file) @@ -1,49 +0,0 @@ -================== -py-lib executables -================== - -The py-lib contains some executables which are described here. Most of them are -relatively small scripts (apart from ``py.test``). We are open for feature -requests! - -``py.test`` -=========== - -The ``py.test`` executable is the main entry point into the py-lib testing tool, -see the `py.test documentation`_. - -.. _`py.test documentation`: test.html - -``py.cleanup`` -============== - -Usage: ``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 ".". - - -``py.countloc`` -=============== - -Usage: ``py.countloc [PATHS]`` - -Count (non-empty) lines of python code and number of python files recursively -starting from a ``PATHS`` given on the command line (starting from the current -working directory). Distinguish between test files and normal ones and report -them separately. - -``py.lookup`` -============= - -Usage: ``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. - -``py.rest`` -=========== - -XXX to be documented From fijal at codespeak.net Sat Jan 13 13:19:08 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 13 Jan 2007 13:19:08 +0100 (CET) Subject: [py-svn] r36640 - py/dist/py/documentation/future Message-ID: <20070113121908.455D410086@code0.codespeak.net> Author: fijal Date: Sat Jan 13 13:19:05 2007 New Revision: 36640 Added: py/dist/py/documentation/future/pylib_pypy.txt Log: Added (incomplete) list of stuff that fails in pylib tests on top of pypy. Need another translations to take a deeper look. Added: py/dist/py/documentation/future/pylib_pypy.txt ============================================================================== --- (empty file) +++ py/dist/py/documentation/future/pylib_pypy.txt Sat Jan 13 13:19:05 2007 @@ -0,0 +1,29 @@ +Here I'm trying to list all problems regarding pypy-c <-> pylib interaction +=========================================================================== + +* in test/terminal/terminal.py lines around 141:: + rev = py.__package__.getrev() + self.out.line("using py lib: %s " % ( + py.path.local(py.__file__).dirpath(), rev)) + +* py.code issues:: + def __init__(self, rawcode): + rawcode = getattr(rawcode, 'im_func', rawcode) + rawcode = getattr(rawcode, 'func_code', rawcode) + self.raw = rawcode + self.filename = rawcode.co_filename + AttributeError: 'internal-code' object has no attribute 'co_filename' + +* types.BuiltinFunctionType == types.MethodType which confuses apigen + +* compiler module problems - some bogus IndentationError + communicates by inspect.getsource() + +* execnet just hangs + +* lack of tmpfile + +* assertion error magic is not working + +* sha counting hangs (misc/testing/test\_initpkg) + From cfbolz at codespeak.net Sat Jan 13 13:24:58 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sat, 13 Jan 2007 13:24:58 +0100 (CET) Subject: [py-svn] r36641 - py/dist/py/documentation Message-ID: <20070113122458.3F32C10083@code0.codespeak.net> Author: cfbolz Date: Sat Jan 13 13:24:56 2007 New Revision: 36641 Modified: py/dist/py/documentation/executables.txt Log: add docs for py.rest. explain the graphviz directive. I don't think much more should be explained currently, it's too unstable. Modified: py/dist/py/documentation/executables.txt ============================================================================== --- py/dist/py/documentation/executables.txt (original) +++ py/dist/py/documentation/executables.txt Sat Jan 13 13:24:56 2007 @@ -46,4 +46,19 @@ ``py.rest`` =========== -XXX to be documented +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). + +``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 +not going to change is the `graphviz`_ directive. With that you can embed .dot +files into your document and have them be converted to png (when outputting +html) and to eps (when outputting pdf). Otherwise the directive works mostly +like the image directive:: + + .. graphviz:: example.dot + :scale: 90 + +.. _`graphviz`: http://www.graphviz.org From hpk at codespeak.net Sat Jan 13 13:29:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 13:29:14 +0100 (CET) Subject: [py-svn] r36642 - py/dist/py/documentation Message-ID: <20070113122914.299FF10083@code0.codespeak.net> Author: hpk Date: Sat Jan 13 13:29:12 2007 New Revision: 36642 Removed: py/dist/py/documentation/maciej-implementation-notes.txt Log: after review i think that most of maciej's notes have been either resolved already or are in other issues, so there is no need to keep it in the main doc directory. Deleted: /py/dist/py/documentation/maciej-implementation-notes.txt ============================================================================== --- /py/dist/py/documentation/maciej-implementation-notes.txt Sat Jan 13 13:29:12 2007 +++ (empty file) @@ -1,99 +0,0 @@ - -Pylib roadmap, from my point of view -==================================== - -Author: Maciek Fijalkowski --------------------------- - -Date: 7.X.2006 - -Rough specifications of what needs to be done regarding py.test -(especially distributed version), py.api and others. - -So main targets (for near future) of pylib developement would be: - -* further py.test distributed developement, especially incorporating - some command line options (I think -x and -k are done). This might be: - - - DONE options transfer to clients (maybe whole conftest) to allow them - to perform special things. - - -s (how???) --view for pypy, etc. - - screen/--pdb (there is some work done in this area) - - If someone (merlinux?) set up test farm (few machines connected - together for running tests), provide convenient interface for - running at least nightly tests (web one? offline html?) or even - just running tests for branch, trunk, custom svn without need to - upload local changes every time (RSync daemon?). I'm not sure if - it does make any sense, but makes convinient if we provide - front machine with access, not all the nodes (XXX and what with - screen?) - - Make sure web frontend works when more than one client (webbrowser) - connects to it (actually it does not). - - XXX: anything else? - -* integration from py.test distributed into normal py.test, especially - cool reporting features as well as web frontend (and extend it a bit) - -* integrate some kind of TestRunner, which will run the tests and perform - some additional stuff. This is related to (but not limited): - - - benchmarks - - - py.api integration - -PY.API: -------- - -(actually lying down in py.test.tracer in branch, maybe not -too nice name for that, will go to py.api). - -So, todo list is (in random order): - -* Make interface for every possible single item on the end (function, - method, generator) to provide better coverage. Function/method DONE. - -* Make more... (frontend for API extraction, backend for output - generation, different source link generators, etc. etc.) - -* Provide some model of determining which methods are exposed as API - and which are internal (just called directly in unittest looks like - enough for now, altough we do not have method of determining that yet) - -* Provide some command line debugger-like tool (PDB hook?) which will expose - API information additionally to debugging info. - -* Make clean vision of pypy annotation typesystem, probably even - extend it a bit (it's still easier to use than providing all - the interface on our own). Possible extensions: - - - Provide SomeUnion instead of SomeObject which will behave like - gathering all possibilities together (this might be beneficient to - pypy as well). - - Provide some convinient way of getting common interface out of - objects that are contained in SomeObject. Like "what are the - common methods of both, even if they do not share a baseclass". - In this place, it should be quite easy to add different type - joining functions by user, because different projects might have - different needs at this point. - -* Test it on generating pylib documentation. - -* Make some API definition tool (object format) for defining API in - pypy. This will make a lot easier annotating the pypy source with - info (like "what the heck type comes here as an argument"....) - -* Add several different things to trace, mostly tracing of side effects - (track what method can change what attributes, as well as in child nodes. - Maybe global variables as well). - -Timeline: ---------- - -This is quite huge wishlist.... I don't know exactly what are priorities -on all the stuff, what I would go first is to make API tool usable and -easy to use as soon as possible, to show it at HHU on sprint in some -running fashion. It should be well tested and maybe not containing -all-the-possible features, but runnable. - -Next thing to do is to extend py.test distributed, especially with -screen functionality. From hpk at codespeak.net Sat Jan 13 13:33:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 13:33:11 +0100 (CET) Subject: [py-svn] r36643 - py/dist/py/test Message-ID: <20070113123311.43C0710086@code0.codespeak.net> Author: hpk Date: Sat Jan 13 13:33:09 2007 New Revision: 36643 Modified: py/dist/py/test/config.py Log: load the default options first, so that custom options appear always at the end Modified: py/dist/py/test/config.py ============================================================================== --- py/dist/py/test/config.py (original) +++ py/dist/py/test/config.py Sat Jan 13 13:33:09 2007 @@ -179,9 +179,9 @@ config = Config._config # trigger loading conftest files which might add options! + config.loadconfig(defaultconfig).adddefaultoptions() for configpath in configpaths: config.loadconfig(configpath) - config.loadconfig(defaultconfig).adddefaultoptions() # each time we make a config object (which assembled cmdline # options through py.test.addoptions() invocations) # we want to reset and clean up the global state From fijal at codespeak.net Sat Jan 13 13:41:32 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 13 Jan 2007 13:41:32 +0100 (CET) Subject: [py-svn] r36645 - in py/dist/py/test/rsession: . webdata Message-ID: <20070113124132.7A58810088@code0.codespeak.net> Author: fijal Date: Sat Jan 13 13:41:20 2007 New Revision: 36645 Modified: py/dist/py/test/rsession/web.py py/dist/py/test/rsession/webdata/index.html py/dist/py/test/rsession/webdata/source.js py/dist/py/test/rsession/webjs.py Log: Improve a bit feel of scroll-into-view. Still to be improved. Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Sat Jan 13 13:41:20 2007 @@ -22,7 +22,7 @@ DATADIR = py.path.local(__file__).dirpath("webdata") FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", - "show_host", "hide_host"] + "show_host", "hide_host", "hide_messagebox"] try: try: Modified: py/dist/py/test/rsession/webdata/index.html ============================================================================== --- py/dist/py/test/rsession/webdata/index.html (original) +++ py/dist/py/test/rsession/webdata/index.html Sat Jan 13 13:41:20 2007 @@ -102,7 +102,7 @@
- Data: + Data [hide]:
Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/dist/py/test/rsession/webjs.py ============================================================================== --- py/dist/py/test/rsession/webjs.py (original) +++ py/dist/py/test/rsession/webjs.py Sat Jan 13 13:41:20 2007 @@ -62,6 +62,17 @@ info = dom.document.getElementById("info") info.style.visibility = "hidden" +SCROLL_LINES = 50 + +def scroll_down_if_needed(mbox): + if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES: + mbox.parentNode.scrollIntoView() + +def hide_messagebox(): + mbox = dom.document.getElementById("messagebox") + while mbox.childNodes: + mbox.removeChild(mbox.childNodes[0]) + def make_module_box(msg): tr = create_elem("tr") td = create_elem("td") @@ -75,8 +86,7 @@ td.setAttribute("onmouseover", "show_info('%s')" % (msg['fullitemname'],)) td.setAttribute("onmouseout", "hide_info()") - - td2 = create_elem('td') + td2 = create_elem('td') tr.appendChild(td2) table = create_elem("table") td2.appendChild(table) @@ -194,7 +204,7 @@ glob.rsync_done = True if glob.data_empty: mbox = dom.document.getElementById('messagebox') - mbox.parentNode.scrollIntoView() + scroll_down_if_needed(mbox) return True def show_skip(item_name="aa"): From hpk at codespeak.net Sat Jan 13 13:44:29 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 13:44:29 +0100 (CET) Subject: [py-svn] r36646 - py/dist/py/documentation Message-ID: <20070113124429.F0D8F10088@code0.codespeak.net> Author: hpk Date: Sat Jan 13 13:44:28 2007 New Revision: 36646 Removed: py/dist/py/documentation/releaseplan.txt Modified: py/dist/py/documentation/TODO.txt Log: * move some releaseplan.txt issues to TODO.txt * most tasks have been tackled (not really according to the plan we once agreed on) * removing the releaseplan.txt file thus. Modified: py/dist/py/documentation/TODO.txt ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/documentation/TODO.txt Sat Jan 13 13:44:28 2007 @@ -1,6 +1,8 @@ Things to do before 0.9.0 ========================= + + py/bin ----------- @@ -11,9 +13,6 @@ py.cleanup py.countloc -py.compat -------------- - review all py lib documentation ------------------------------------- @@ -44,8 +43,23 @@ packaging ------------------------------------- -* debian package for py lib -* +* debian and TAR/zip packages for py lib, + particularly look into C module issues (greenlet most importantly) + +* do something about c-extensions both on unix-ish + versus win32 systems + +* ensure compatibility with Python 2.3 - 2.5, + see what is missing for 2.2 + +* optional: support setuptools (eggs?) installs, and instally + from pypi (and register pylib there) + +* see if things work on Win32 (partially done) + +* refine and implement `releasescheme`_ + +.. _releasescheme: releasescheme.html APIGEN / source viewer ------------------------------------- @@ -94,14 +108,6 @@ * review the pylib issue tracker -distutils install ------------------ - -* see if things work on Win32 (partially done) - -* do something about c-extensions both on unix-ish - and win32 systems - py.test ------- Deleted: /py/dist/py/documentation/releaseplan.txt ============================================================================== --- /py/dist/py/documentation/releaseplan.txt Sat Jan 13 13:44:28 2007 +++ (empty file) @@ -1,105 +0,0 @@ -========================================================= -py lib release related tasks - notes and plans -========================================================= - -$Date$ - -Current time plan regarding releases: - -- 0.9 stable release end November/early December 2006 - -- 1.0 stable release Beginning February 2007 - incorporating apigen tool and fully featured - py.test tool, including distribution among - many hosts. - -py lib 0.9 release blockers: -======================================= - -* document and sort out py.log API approaches - -* have APIGEN generate nice Web documentation of the py lib API - -* support packaging with TARs and for Debian and possibly - other distros, particularly look into C module issues. - -* support both setup.py and setuptools (eggs?) installs - -* support for installs from pypi (and listing py lib there) - -* document and refine "py.*" helper tools. - -* go through Issue tracker and determine 0.9 relevant issues - -* get py.test (and distributed) to a sufficiently good/usable state. - -* refine and implement `releasescheme`_ - -* ensure compatibility with Python 2.2 - 2.5 - (possibly update py/compat package) - -* ensure that py.tests pass on win32, linux and OSX - -.. _releasescheme: releasescheme.html - -Task planning -=================== - -APIGEN Tool (py.apigen) ------------------------------- - -main developers: Maciej, Guido: reviewers: Holger, Carl - -1st goal: have a clean abstractions on iterating a package's to-be-documented - namespaces (modules), classes and functions, being able - to extract information (docstrings, arguments) in a nice manner. - - Implement a ReST backend which represents information - extracted from apigen. For now, the new "apigen" namespace - should not be exported, but only be available via - py.__.apigen style imports. - - the ReST backend, the API info extraction tool, - (and extending py.test to collect additional information) - should all be completely separated tasks. py.test - is not involved in this first goal, though. - - important: be sure to make things configurable - and use a nice local Config() object from the - beginning so that variations (initpkg'ed versus - otherwise-specified APIs) can be incorporated - somewhat obviously later. - -2nd goal: incorporate information obtained from tests - XXX how should py.test write out information? Create an issue, - handle as separate task. - -3rd goal: maybe: write a pygame frontend, integrate it into PyPy :) - - -py.test distribution ---------------------------- - -main developer: Maciej, reviewer: Holger, Carl - -1st Goal: have basically all normal py.test features present in py.test distributed - merge current code into one approach (making "local" execution - a special case of distributed execution), be careful - that conftest.py config handling continues to work, - particularly for PyPy. - -py.test ReST / HTML / PDF backend -------------------------------------- - -main developer: Guido, reviewer: Carl, Maciej - -1st Goal: do a ReST backend (maybe about the py.test/distributed merge) - probably related to the above py.test distribution task - (the distributed testing approach is - better for having different reporters, so basing - it on the current py.test mainline approach does - not make too much sense). - Do some incremental steps, also to see if - the upcoming py.rest API (see py APIGEN) - is nice enough etc. - From hpk at codespeak.net Sat Jan 13 13:50:00 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 13:50:00 +0100 (CET) Subject: [py-svn] r36647 - py/dist/py/documentation Message-ID: <20070113125000.9A32410088@code0.codespeak.net> Author: hpk Date: Sat Jan 13 13:49:59 2007 New Revision: 36647 Added: py/dist/py/documentation/bin.txt - copied, changed from r36642, py/dist/py/documentation/executables.txt Removed: py/dist/py/documentation/executables.txt Modified: py/dist/py/documentation/index.txt Log: * moving executables.txt to bin.txt (to reflect the relation to namespaces) and amended the introduction a bit. * reference it from index.txt Copied: py/dist/py/documentation/bin.txt (from r36642, py/dist/py/documentation/executables.txt) ============================================================================== --- py/dist/py/documentation/executables.txt (original) +++ py/dist/py/documentation/bin.txt Sat Jan 13 13:49:59 2007 @@ -1,10 +1,13 @@ -================== -py-lib executables -================== - -The py-lib contains some executables which are described here. Most of them are -relatively small scripts (apart from ``py.test``). We are open for feature -requests! +====================== +``py/bin/`` scripts +====================== + +The py-lib contains some scripts, most of which are +small ones (apart from ``py.test``) that help during +the python development process. If working +from a svn-checkout of py lib you may add ``py/bin`` +to your shell ``PATH`` which should make the scripts +available on your command prompt. ``py.test`` =========== Deleted: /py/dist/py/documentation/executables.txt ============================================================================== --- /py/dist/py/documentation/executables.txt Sat Jan 13 13:49:59 2007 +++ (empty file) @@ -1,64 +0,0 @@ -================== -py-lib executables -================== - -The py-lib contains some executables which are described here. Most of them are -relatively small scripts (apart from ``py.test``). We are open for feature -requests! - -``py.test`` -=========== - -The ``py.test`` executable is the main entry point into the py-lib testing tool, -see the `py.test documentation`_. - -.. _`py.test documentation`: test.html - -``py.cleanup`` -============== - -Usage: ``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 ".". - - -``py.countloc`` -=============== - -Usage: ``py.countloc [PATHS]`` - -Count (non-empty) lines of python code and number of python files recursively -starting from a ``PATHS`` given on the command line (starting from the current -working directory). Distinguish between test files and normal ones and report -them separately. - -``py.lookup`` -============= - -Usage: ``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. - -``py.rest`` -=========== - -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). - -``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 -not going to change is the `graphviz`_ directive. With that you can embed .dot -files into your document and have them be converted to png (when outputting -html) and to eps (when outputting pdf). Otherwise the directive works mostly -like the image directive:: - - .. graphviz:: example.dot - :scale: 90 - -.. _`graphviz`: http://www.graphviz.org Modified: py/dist/py/documentation/index.txt ============================================================================== --- py/dist/py/documentation/index.txt (original) +++ py/dist/py/documentation/index.txt Sat Jan 13 13:49:59 2007 @@ -11,6 +11,8 @@ `py.log`_ an alpha document about the ad-hoc logging facilities +`py lib scripts`_ describe the scripts contained in the ``py/bin`` directory. + `miscellaneous features`_ describes some more py lib features `future`_ handles development visions and plans for the near future. @@ -28,6 +30,7 @@ .. _`py.magic.greenlet`: greenlet.html .. _`py.log`: log.html .. _`py.test`: test.html +.. _`py lib scripts`: bin.html .. _`py.xml`: xml.html .. _`Why What how py?`: why_py.html .. _`future`: future/future.html From hpk at codespeak.net Sat Jan 13 13:52:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 13:52:11 +0100 (CET) Subject: [py-svn] r36648 - py/dist/py/documentation Message-ID: <20070113125211.A780C10089@code0.codespeak.net> Author: hpk Date: Sat Jan 13 13:52:09 2007 New Revision: 36648 Modified: py/dist/py/documentation/releasescheme.txt Log: small refinements on the intended directory structure Modified: py/dist/py/documentation/releasescheme.txt ============================================================================== --- py/dist/py/documentation/releasescheme.txt (original) +++ py/dist/py/documentation/releasescheme.txt Sat Jan 13 13:52:09 2007 @@ -5,8 +5,8 @@ The directory release layout of the repository is going to follow this scheme:: - svn/py/dist # latest released code - svn/py/tag/X.Y.Z # tagged releases + svn/py/dist # latest stable code base (may or may not be a release) + svn/py/release/X.Y.Z # tagged releases svn/py/branch/X.Y # contains release branch development svn/py/trunk # head development @@ -19,8 +19,8 @@ http://codespeak.net/download/py and pick a suitable archive file. You need to -unpack it and run `setup.py` to install it for -your platform. +unpack it and may want to run `setup.py` to install +it in a system-wide manner. Installation Scenario "svn + stay close to released versions" ------------------------------------------------------------- From hpk at codespeak.net Sat Jan 13 14:10:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 14:10:43 +0100 (CET) Subject: [py-svn] r36651 - py/dist/py/test/rsession Message-ID: <20070113131043.359AC10083@code0.codespeak.net> Author: hpk Date: Sat Jan 13 14:10:42 2007 New Revision: 36651 Modified: py/dist/py/test/rsession/webjs.py Log: fix a syntax error (?) Modified: py/dist/py/test/rsession/webjs.py ============================================================================== --- py/dist/py/test/rsession/webjs.py (original) +++ py/dist/py/test/rsession/webjs.py Sat Jan 13 14:10:42 2007 @@ -86,7 +86,7 @@ td.setAttribute("onmouseover", "show_info('%s')" % (msg['fullitemname'],)) td.setAttribute("onmouseout", "hide_info()") - td2 = create_elem('td') + td2 = create_elem('td') tr.appendChild(td2) table = create_elem("table") td2.appendChild(table) From fijal at codespeak.net Sat Jan 13 14:16:50 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 13 Jan 2007 14:16:50 +0100 (CET) Subject: [py-svn] r36652 - py/dist/py/documentation/future Message-ID: <20070113131650.DF12110083@code0.codespeak.net> Author: fijal Date: Sat Jan 13 14:16:49 2007 New Revision: 36652 Modified: py/dist/py/documentation/future/pylib_pypy.txt Log: Added few points. Modified: py/dist/py/documentation/future/pylib_pypy.txt ============================================================================== --- py/dist/py/documentation/future/pylib_pypy.txt (original) +++ py/dist/py/documentation/future/pylib_pypy.txt Sat Jan 13 14:16:49 2007 @@ -27,3 +27,11 @@ * sha counting hangs (misc/testing/test\_initpkg) +* extpy does not work, because it does not support loops in modules + (while pypy __builtins__ module has a loop), funny :-) + +* py.compat.subprocess hangs for obscure reasons + (possibly the same stuff as execnet - some threading issues and + select.select) + + Armin says: "haha, select.select probably does not release the GIL" \ No newline at end of file From hpk at codespeak.net Sat Jan 13 16:19:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 16:19:46 +0100 (CET) Subject: [py-svn] r36664 - in py/dist/py: apigen apigen/rest/testing doc doc/apigen doc/example doc/future doc/talk documentation misc/testing rest/testing test/rsession/testing test/testing Message-ID: <20070113151946.DB9C010083@code0.codespeak.net> Author: hpk Date: Sat Jan 13 16:19:39 2007 New Revision: 36664 Added: py/dist/py/doc/ - copied from r36652, py/dist/py/documentation/ py/dist/py/doc/TODO.txt - copied, changed from r36661, py/dist/py/documentation/TODO.txt py/dist/py/doc/__init__.py - copied unchanged from r36661, py/dist/py/documentation/__init__.py py/dist/py/doc/api.txt - copied unchanged from r36661, py/dist/py/documentation/api.txt py/dist/py/doc/apigen/ - copied from r36661, py/dist/py/documentation/apigen/ py/dist/py/doc/apigen.txt - copied unchanged from r36661, py/dist/py/documentation/apigen.txt py/dist/py/doc/bin.txt - copied unchanged from r36661, py/dist/py/documentation/bin.txt py/dist/py/doc/coding-style.txt - copied unchanged from r36661, py/dist/py/documentation/coding-style.txt py/dist/py/doc/confrest.py - copied unchanged from r36661, py/dist/py/documentation/confrest.py py/dist/py/doc/conftest.py - copied unchanged from r36661, py/dist/py/documentation/conftest.py py/dist/py/doc/contact.txt - copied unchanged from r36661, py/dist/py/documentation/contact.txt py/dist/py/doc/example/ - copied from r36661, py/dist/py/documentation/example/ py/dist/py/doc/execnet.txt - copied unchanged from r36661, py/dist/py/documentation/execnet.txt py/dist/py/doc/future/ - copied from r36661, py/dist/py/documentation/future/ py/dist/py/doc/getting-started.txt - copied unchanged from r36661, py/dist/py/documentation/getting-started.txt py/dist/py/doc/greenlet.txt - copied unchanged from r36661, py/dist/py/documentation/greenlet.txt py/dist/py/doc/home.txt - copied unchanged from r36661, py/dist/py/documentation/home.txt py/dist/py/doc/index.txt - copied unchanged from r36661, py/dist/py/documentation/index.txt py/dist/py/doc/links.txt - copied unchanged from r36661, py/dist/py/documentation/links.txt py/dist/py/doc/log.txt - copied unchanged from r36661, py/dist/py/documentation/log.txt py/dist/py/doc/misc.txt - copied unchanged from r36661, py/dist/py/documentation/misc.txt py/dist/py/doc/releasescheme.txt - copied unchanged from r36661, py/dist/py/documentation/releasescheme.txt py/dist/py/doc/style.css - copied unchanged from r36661, py/dist/py/documentation/style.css py/dist/py/doc/talk/ - copied from r36661, py/dist/py/documentation/talk/ py/dist/py/doc/test-distributed.txt - copied unchanged from r36661, py/dist/py/documentation/test-distributed.txt py/dist/py/doc/test.txt - copied unchanged from r36661, py/dist/py/documentation/test.txt py/dist/py/doc/test_conftest.py - copied unchanged from r36661, py/dist/py/documentation/test_conftest.py py/dist/py/doc/why_py.txt - copied unchanged from r36661, py/dist/py/documentation/why_py.txt py/dist/py/doc/xml.txt - copied unchanged from r36661, py/dist/py/documentation/xml.txt Removed: py/dist/py/documentation/ Modified: py/dist/py/apigen/layout.py py/dist/py/apigen/project.py py/dist/py/apigen/rest/testing/test_rest.py py/dist/py/misc/testing/test_initpkg.py py/dist/py/rest/testing/test_rst.py py/dist/py/test/rsession/testing/test_slave.py py/dist/py/test/testing/test_collect.py Log: rename documentation to doc, hopefully catched all places where it is used within py lib. Modified: py/dist/py/apigen/layout.py ============================================================================== --- py/dist/py/apigen/layout.py (original) +++ py/dist/py/apigen/layout.py Sat Jan 13 16:19:39 2007 @@ -4,7 +4,7 @@ """ import py -from py.__.documentation.confrest import Page +from py.__.doc.confrest import Page class LayoutPage(Page): """ this provides the layout and style information """ Modified: py/dist/py/apigen/project.py ============================================================================== --- py/dist/py/apigen/project.py (original) +++ py/dist/py/apigen/project.py Sat Jan 13 16:19:39 2007 @@ -8,7 +8,7 @@ import py from layout import LayoutPage -class Project(py.__.documentation.confrest.Project): +class Project(py.__.doc.confrest.Project): """ a full project this takes care of storing information on the first pass, and building Modified: py/dist/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/dist/py/apigen/rest/testing/test_rest.py (original) +++ py/dist/py/apigen/rest/testing/test_rest.py Sat Jan 13 16:19:39 2007 @@ -15,7 +15,7 @@ import pickle from py.__.apigen.tracer.testing.runtest import cut_pyc -from py.__.documentation.conftest import genlinkchecks +from py.__.doc.conftest import genlinkchecks from py.__.rest.rst import Rest, Paragraph from py.__.rest.transform import HTMLHandler # XXX: UUuuuuuuuuuuuuuuuuuuuuuuu, dangerous import Copied: py/dist/py/doc/TODO.txt (from r36661, py/dist/py/documentation/TODO.txt) ============================================================================== --- py/dist/py/documentation/TODO.txt (original) +++ py/dist/py/doc/TODO.txt Sat Jan 13 16:19:39 2007 @@ -16,7 +16,7 @@ review all py lib documentation ------------------------------------- -* rename py/documentation to py/doc +* (hpk, in-progress) rename py/documentation to py/doc (check web page and pypy usage of it) streamline exported API Modified: py/dist/py/misc/testing/test_initpkg.py ============================================================================== --- py/dist/py/misc/testing/test_initpkg.py (original) +++ py/dist/py/misc/testing/test_initpkg.py Sat Jan 13 16:19:39 2007 @@ -43,7 +43,7 @@ base.join('magic', 'greenlet.py'), base.join('path', 'extpy', 'testing', 'test_data'), base.join('path', 'gateway',), - base.join('documentation',), + base.join('doc',), base.join('test', 'testing', 'import_test'), base.join('c-extension',), base.join('magic', 'greenlet.py'), Modified: py/dist/py/rest/testing/test_rst.py ============================================================================== --- py/dist/py/rest/testing/test_rst.py (original) +++ py/dist/py/rest/testing/test_rst.py Sat Jan 13 16:19:39 2007 @@ -3,7 +3,7 @@ """ from py.__.rest.rst import * -from py.__.documentation.conftest import restcheck +from py.__.doc.conftest import restcheck import traceback tempdir = py.test.ensuretemp('rest') Modified: py/dist/py/test/rsession/testing/test_slave.py ============================================================================== --- py/dist/py/test/rsession/testing/test_slave.py (original) +++ py/dist/py/test/rsession/testing/test_slave.py Sat Jan 13 16:19:39 2007 @@ -123,7 +123,7 @@ def test_slave_run_different_stuff(): node = gettestnode() - node.run("py documentation log.txt".split()) + node.run("py doc log.txt".split()) def test_slave_setup_fails_on_import_error(): from py.__.test.rsession.slave import setup Modified: py/dist/py/test/testing/test_collect.py ============================================================================== --- py/dist/py/test/testing/test_collect.py (original) +++ py/dist/py/test/testing/test_collect.py Sat Jan 13 16:19:39 2007 @@ -319,9 +319,9 @@ def test_documentation_virtual_collector_interaction(): - rootdir = py.path.local(py.__file__).dirpath("documentation") + rootdir = py.path.local(py.__file__).dirpath("doc") # HACK - from py.__.documentation import conftest as conf + from py.__.doc import conftest as conf old = conf.option.forcegen try: conf.option.forcegen = 1 From guido at codespeak.net Sat Jan 13 16:29:17 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 13 Jan 2007 16:29:17 +0100 (CET) Subject: [py-svn] r36667 - in py/dist/py/apigen/source: . testing Message-ID: <20070113152917.F3B4A10087@code0.codespeak.net> Author: guido Date: Sat Jan 13 16:29:16 2007 New Revision: 36667 Modified: py/dist/py/apigen/source/browser.py py/dist/py/apigen/source/color.py py/dist/py/apigen/source/testing/test_color.py Log: Using bare except around module import (brrr... importing random python files can lead to strange errors, though), fixed bug in tokenizer (thanks to Carl for the proper string regexp with quote escaping support). Modified: py/dist/py/apigen/source/browser.py ============================================================================== --- py/dist/py/apigen/source/browser.py (original) +++ py/dist/py/apigen/source/browser.py Sat Jan 13 16:29:16 2007 @@ -132,7 +132,8 @@ try: mod = path.pyimport() update_mod_dict(mod, mod_dict) - except (ImportError, AttributeError): + # XXX brrr... importing can result in strange errors, though... + except: # (ImportError, AttributeError): pass return Module(path, mod_dict) Modified: py/dist/py/apigen/source/color.py ============================================================================== --- py/dist/py/apigen/source/color.py (original) +++ py/dist/py/apigen/source/color.py Sat Jan 13 16:29:16 2007 @@ -54,7 +54,10 @@ self._re_strings_multiline = [] self._re_strings_empty = [] for d in schema.string + schema.multiline_string: - self._re_strings_full.append(re.compile('%s[^%s]+%s' % (d, d, d))) + self._re_strings_full.append( + re.compile(r'%s[^\\%s]*(\\.[^\\%s]*)+%s' % (d, d, d, d))) + self._re_strings_full.append( + re.compile(r'%s[^\\%s]+(\\.[^\\%s]*)*%s' % (d, d, d, d))) self._re_strings_empty.append(re.compile('%s%s' % (d, d))) for d in schema.multiline_string: self._re_strings_multiline.append((re.compile('%s.*' % (d,), re.S), Modified: py/dist/py/apigen/source/testing/test_color.py ============================================================================== --- py/dist/py/apigen/source/testing/test_color.py (original) +++ py/dist/py/apigen/source/testing/test_color.py Sat Jan 13 16:29:16 2007 @@ -52,7 +52,6 @@ Token("'bar'", type='string')] def test_string_escape(self): - py.test.skip('not yet implemented') assert self.tokens('"foo \\" bar"') == [Token('"foo \\" bar"', type='string')] From guido at codespeak.net Sat Jan 13 16:30:24 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 13 Jan 2007 16:30:24 +0100 (CET) Subject: [py-svn] r36668 - py/dist/py/apigen Message-ID: <20070113153024.18CC110087@code0.codespeak.net> Author: guido Date: Sat Jan 13 16:30:23 2007 New Revision: 36668 Added: py/dist/py/apigen/apigen.py Modified: py/dist/py/apigen/htmlgen.py Log: Fixed small errors in htmlgen after letting it loose in the wild a bit, added script to use as argument to the --apigen switch to py.test (so run 'py.test --apigen=py/apigen/apigen.py' to build api + source docs). Added: py/dist/py/apigen/apigen.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/apigen.py Sat Jan 13 16:30:23 2007 @@ -0,0 +1,50 @@ +""" run 'py.test --apigen=' to get documentation exported + + exports to /tmp/output by default, set the environment variable + 'APIGEN_TARGET' to override +""" + +import os +import py +from py.__.apigen import htmlgen +from py.__.apigen import linker +from py.__.apigen import project + +def import_pkgdir(pkgdir): + if pkgdir.check(dir=True): + return pkgdir.join('__init__.py').getpymodule() + else: + # XXX not sure if this is ever used normally... + return pkgdir.getpymodule() + +def get_documentable_items(pkgdir): + rootmod = import_pkgdir(pkgdir) + if hasattr(rootmod, '__package__'): + return rootmod + # XXX fix non-initpkg situations(?) + return {} + +def build(pkgdir, dsa): + l = linker.Linker() + proj = project.Project() + + if 'APIGEN_TARGET' in os.environ: + targetdir = py.path.local(os.environ['APIGEN_TARGET']) + else: + targetdir = py.path.local('/tmp/py_apigen_output') + targetdir.ensure(dir=True) + + class_names = dsa.get_class_names() + class_data, method_data = htmlgen.prepare_class_api_pages(l, dsa, + targetdir, + class_names) + function_names = dsa.get_function_names() + func_data = htmlgen.prepare_function_api_pages(l, dsa, targetdir, + function_names) + source_data = htmlgen.prepare_source_pages(l, pkgdir, targetdir) + + htmlgen.build_class_api_pages(class_data, proj, targetdir) + htmlgen.build_method_api_pages(method_data, proj, targetdir) + htmlgen.build_function_api_pages(func_data, proj, targetdir) + htmlgen.build_source_pages(source_data, proj, targetdir) + Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Sat Jan 13 16:30:23 2007 @@ -54,9 +54,13 @@ params = get_param_htmldesc(linker, func) docstring = func.__doc__ local_methodname = func.__name__ + if sourcefile is not None: + sourcelink = H.a('source', href=linker.get_lazyhref(sourcefile)) + else: + sourcelink = H.span('no source file available') snippet = H.MethodDescription( H.MethodDef(local_methodname, params, ":"), - H.a('source', href=linker.get_lazyhref(sourcefile)), + sourcelink, H.Docstring(docstring), ) return snippet @@ -77,7 +81,12 @@ def build_class_view(base, linker, dsa, dotted_name): """ build the html for a class """ cls = dsa.get_obj(dotted_name) - sourcefile = inspect.getsourcefile(cls) + try: + sourcefile = inspect.getsourcefile(cls) + except TypeError: + return H.div('builtin file') + if sourcefile is None: + return H.div('no source available') if sourcefile[-1] in ['o', 'c']: sourcefile = sourcefile[:-1] docstring = cls.__doc__ @@ -93,7 +102,8 @@ href = linker.get_lazyhref(base.name) basehtml.append(H.BaseDescription(base.name, href=href)) basehtml.append(',') - basehtml.pop() + if basehtml: + basehtml.pop() basehtml.append('):') snippet = H.ClassDescription( # XXX bases HTML @@ -221,11 +231,13 @@ passed = [] for fspath in base.visit(): relfspath = fspath.relto(base) - if fspath.check(dir=True): + if relfspath.find('/.') > -1: + # skip hidden dirs and files + continue + elif fspath.check(dir=True): reloutputpath = 'source/%s/index.html' % (relfspath,) else: reloutputpath = "source/%s.html" % (relfspath,) - print reloutputpath outputpath = outputbase.join(reloutputpath) linker.set_link(str(fspath), reloutputpath) passed.append((fspath, outputpath)) @@ -235,7 +247,10 @@ """ build syntax-colored source views """ for fspath, outputpath in data: if fspath.check(ext='.py'): - tag, nav = build_source_python_page(fspath) + try: + tag, nav = build_source_python_page(fspath) + except: # XXX strange stuff going wrong at times... need to fix + tag, nav = build_source_nonpython_page(fspath) elif fspath.check(dir=True): tag, nav = build_source_dir_page(fspath) else: @@ -247,6 +262,7 @@ def prepare_class_api_pages(linker, dsa, base, classes_dotted_names): passed = [] + methodsdata = [] for dotted_name in classes_dotted_names: tag = build_class_view(base, linker, dsa, dotted_name) nav = build_class_navigation(linker, dsa, dotted_name) @@ -256,8 +272,8 @@ method_dotted_names = ['%s.%s' % (dotted_name, method_name) for method_name in dsa.get_class_methods(dotted_name)] - methodsdata = prepare_method_api_pages(linker, dsa, base, - method_dotted_names) + methodsdata += prepare_method_api_pages(linker, dsa, base, + method_dotted_names) return passed, methodsdata def build_class_api_pages(data, project, outputbase): From hpk at codespeak.net Sat Jan 13 16:35:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 16:35:31 +0100 (CET) Subject: [py-svn] r36670 - in py/dist/py/apigen/source: . testing Message-ID: <20070113153531.311A110087@code0.codespeak.net> Author: hpk Date: Sat Jan 13 16:35:29 2007 New Revision: 36670 Modified: py/dist/py/apigen/source/browser.py py/dist/py/apigen/source/testing/test_browser.py Log: do "bare except" in the right way Modified: py/dist/py/apigen/source/browser.py ============================================================================== --- py/dist/py/apigen/source/browser.py (original) +++ py/dist/py/apigen/source/browser.py Sat Jan 13 16:35:29 2007 @@ -131,9 +131,13 @@ # we check all the elements, if they're really there try: mod = path.pyimport() - update_mod_dict(mod, mod_dict) - # XXX brrr... importing can result in strange errors, though... - except: # (ImportError, AttributeError): + except (KeyboardInterrupt, SystemExit): + raise + except: # catch all other import problems generically + # XXX some import problem: we probably should not + # pretend to have an empty module pass + else: + update_mod_dict(mod, mod_dict) return Module(path, mod_dict) Modified: py/dist/py/apigen/source/testing/test_browser.py ============================================================================== --- py/dist/py/apigen/source/testing/test_browser.py (original) +++ py/dist/py/apigen/source/testing/test_browser.py Sat Jan 13 16:35:29 2007 @@ -62,3 +62,19 @@ """)) mod = parse_path(tmp.join("c.py")) # if it does not rise it's ok for now + # + +def test_importing_goes_wrong(): + tmp = py.test.ensuretemp("sourcebrowserimport") + tmp.ensure("x.py").write(py.code.Source(""" + import aslkdjaslkdjasdl + """)) + mod = parse_path(tmp.join("x.py")) + + tmp.ensure("y.py").write(py.code.Source(""" + raise KeyboardInterrupt + """)) + py.test.raises(KeyboardInterrupt, 'parse_path(tmp.join("y.py"))') + + # if it does not rise it's ok for now + # From guido at codespeak.net Sat Jan 13 16:51:13 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 13 Jan 2007 16:51:13 +0100 (CET) Subject: [py-svn] r36671 - py/dist/py/test/rsession Message-ID: <20070113155113.1A85A10083@code0.codespeak.net> Author: guido Date: Sat Jan 13 16:51:11 2007 New Revision: 36671 Modified: py/dist/py/test/rsession/rsession.py Log: Small modifications for --apigen script. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Sat Jan 13 16:51:11 2007 @@ -270,7 +270,7 @@ reporter(report.RsyncFinished()) if runner is None: - runner = self.init_runner() + runner = self.init_runner(pkgdir) keyword = self.config.option.keyword @@ -283,28 +283,30 @@ if not self.config.option.nomagic: py.magic.revoke(assertion=1) - self.write_docs() + self.write_docs(pkgdir) return retval - def write_docs(self): + def write_docs(self, pkgdir): if self.config.option.apigen: from py.__.apigen.tracer.docstorage import DocStorageAccessor apigen = py.path.local(self.config.option.apigen).pyimport() - pkgpath = py.path.local(py.__file__).dirpath() try: - apigen.build(pkgpath, DocStorageAccessor(self.docstorage)) + apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) except (ValueError, AttributeError): raise NotImplementedError("Provided script does not seem " "to contain build function") - def init_runner(self): + def init_runner(self, pkgdir): if self.config.option.apigen: from py.__.apigen.tracer.tracer import Tracer, DocStorage module = py try: apigen = py.path.local(self.config.option.apigen).pyimport() - self.docstorage = DocStorage().from_dict( - apigen.get_documentable_items()) + items = apigen.get_documentable_items(pkgdir) + if isinstance(items, dict): + self.docstorage = DocStorage().from_dict(items) + else: + self.docstorage = DocStorage().from_pkg(items) except ImportError: raise ImportError("Provided script cannot be imported") except (ValueError, AttributeError): @@ -314,3 +316,4 @@ return apigen_runner else: return RunnerPolicy[session_options.runner_policy] + From guido at codespeak.net Sat Jan 13 18:27:38 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 13 Jan 2007 18:27:38 +0100 (CET) Subject: [py-svn] r36683 - py/dist/py/apigen/testing Message-ID: <20070113172738.DE7D310074@code0.codespeak.net> Author: guido Date: Sat Jan 13 18:27:37 2007 New Revision: 36683 Added: py/dist/py/apigen/testing/test_apigen_functional.py Modified: py/dist/py/apigen/testing/test_apigen_example.py Log: Made test_apigen_example.setup_fs_project() get the name of the temp dir as an argument so it can more easily be used from other tests, added simple functional test for 'py.test --apigen', some whitespace issues. Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Sat Jan 13 18:27:37 2007 @@ -7,8 +7,8 @@ from py.__.test.web import webcheck from py.__.apigen.conftest import option -def setup_fs_project(): - temp = py.test.ensuretemp('apigen_example') +def setup_fs_project(tempname='apigen_example'): + temp = py.test.ensuretemp(tempname) temp.ensure("pkg/func.py").write(py.code.Source("""\ def func(arg1): "docstring" @@ -48,9 +48,8 @@ dsa = DocStorageAccessor(ds) return dsa - def _checkhtml(htmlstring): - assert isinstance(htmlstring, (unicode, str)) + assert isinstance(htmlstring, (unicode, str)) if option.webcheck: webcheck.check_html(htmlstring) else: Added: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/testing/test_apigen_functional.py Sat Jan 13 18:27:37 2007 @@ -0,0 +1,45 @@ +""" functional test for apigen.py + + script to build api + source docs from py.test +""" + +import py +from test_apigen_example import setup_fs_project + +class TestApigenFunctional(object): + def setup_class(cls): + cls.fs_root, cls.package_name = setup_fs_project( + 'test_apigen_functional') + + def test_apigen(self): + tempdir = py.test.ensuretemp('test_apigen_functional_results') + parentdir = py.magic.autopath().dirpath().dirpath() + pkgdir = self.fs_root.join('pkg') + output = py.process.cmdexec( + 'APIGEN_TARGET=%s py.test --session=L --apigen=%s/apigen.py %s' % ( + tempdir, parentdir, pkgdir)) + assert output.endswith('=======\n') # no traceback in the end + + # just some quick content checks + apidir = tempdir.join('api') + assert apidir.check(dir=True) + someclass_api = apidir.join('pkg.SomeClass.html') + assert someclass_api.check(file=True) + assert someclass_api.read().find( + 'SomeClass') > -1 + someclass_init_api = apidir.join('pkg.SomeClass.__init__.html') + assert someclass_init_api.check(file=True) + assert someclass_init_api.read().find( + '__init__') > -1 + + sourcedir = tempdir.join('source') + assert sourcedir.check(dir=True) + someclass_source = sourcedir.join('someclass.py.html') + assert someclass_source.check(file=True) + assert someclass_source.read().find( + '
sources for someclass.py
') > -1 + + # XXX later... + #index = sourcedir.join('index.html') + #assert index.check(file=True) + From guido at codespeak.net Sat Jan 13 18:37:47 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 13 Jan 2007 18:37:47 +0100 (CET) Subject: [py-svn] r36685 - py/dist/py/apigen/testing Message-ID: <20070113173747.28C4910077@code0.codespeak.net> Author: guido Date: Sat Jan 13 18:37:46 2007 New Revision: 36685 Modified: py/dist/py/apigen/testing/test_apigen_functional.py Log: No need for this to be a class. Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Sat Jan 13 18:37:46 2007 @@ -6,40 +6,37 @@ import py from test_apigen_example import setup_fs_project -class TestApigenFunctional(object): - def setup_class(cls): - cls.fs_root, cls.package_name = setup_fs_project( - 'test_apigen_functional') - - def test_apigen(self): - tempdir = py.test.ensuretemp('test_apigen_functional_results') - parentdir = py.magic.autopath().dirpath().dirpath() - pkgdir = self.fs_root.join('pkg') - output = py.process.cmdexec( - 'APIGEN_TARGET=%s py.test --session=L --apigen=%s/apigen.py %s' % ( - tempdir, parentdir, pkgdir)) - assert output.endswith('=======\n') # no traceback in the end - - # just some quick content checks - apidir = tempdir.join('api') - assert apidir.check(dir=True) - someclass_api = apidir.join('pkg.SomeClass.html') - assert someclass_api.check(file=True) - assert someclass_api.read().find( - 'SomeClass') > -1 - someclass_init_api = apidir.join('pkg.SomeClass.__init__.html') - assert someclass_init_api.check(file=True) - assert someclass_init_api.read().find( - '__init__') > -1 - - sourcedir = tempdir.join('source') - assert sourcedir.check(dir=True) - someclass_source = sourcedir.join('someclass.py.html') - assert someclass_source.check(file=True) - assert someclass_source.read().find( - '
sources for someclass.py
') > -1 - - # XXX later... - #index = sourcedir.join('index.html') - #assert index.check(file=True) +def test_apigen_functional(): + fs_root, package_name = setup_fs_project( + 'test_apigen_functional') + tempdir = py.test.ensuretemp('test_apigen_functional_results') + parentdir = py.magic.autopath().dirpath().dirpath() + pkgdir = fs_root.join('pkg') + output = py.process.cmdexec( + 'APIGEN_TARGET=%s py.test --session=L --apigen=%s/apigen.py %s' % ( + tempdir, parentdir, pkgdir)) + assert output.endswith('=======\n') # no traceback in the end + + # just some quick content checks + apidir = tempdir.join('api') + assert apidir.check(dir=True) + someclass_api = apidir.join('pkg.SomeClass.html') + assert someclass_api.check(file=True) + assert someclass_api.read().find( + 'SomeClass') > -1 + someclass_init_api = apidir.join('pkg.SomeClass.__init__.html') + assert someclass_init_api.check(file=True) + assert someclass_init_api.read().find( + '__init__') > -1 + + sourcedir = tempdir.join('source') + assert sourcedir.check(dir=True) + someclass_source = sourcedir.join('someclass.py.html') + assert someclass_source.check(file=True) + assert someclass_source.read().find( + '
sources for someclass.py
') > -1 + + # XXX later... + #index = sourcedir.join('index.html') + #assert index.check(file=True) From hpk at codespeak.net Sat Jan 13 19:03:36 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 19:03:36 +0100 (CET) Subject: [py-svn] r36689 - in py/dist/py: . apigen/tracer misc/testing Message-ID: <20070113180336.D812310083@code0.codespeak.net> Author: hpk Date: Sat Jan 13 19:03:34 2007 New Revision: 36689 Modified: py/dist/py/apigen/tracer/description.py py/dist/py/initpkg.py py/dist/py/misc/testing/test_initpkg.py Log: * fix initpkg's Module __dict__ descriptor to deal with being called very early (before __init__() executes) add a test for that * simplify the copy-dict logic for instance dicts, avoiding bare excepts which really hide problems Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Sat Jan 13 19:03:34 2007 @@ -314,26 +314,9 @@ self.old_dict = self.perform_dict_copy(obj.__dict__) def perform_dict_copy(self, d): - #try: - # c = copy.deepcopy(d) - #except: - # c = {} - # for k, v in d.iteritems(): - # try: - # c[k] = copy.deepcopy(v) - # except: - # c[k] = v - #return c - try: - c = {} - for k, v in d.iteritems(): - c[k] = v - except (KeyboardInterrupt, SystemExit): - raise - except: - # cannot perform this - c = d - return c + if d is None: + return {} + return d.copy() def consider_end_locals(self, frame): obj = frame.f_locals[self.pyobj.im_func.func_code.co_varnames[0]] Modified: py/dist/py/initpkg.py ============================================================================== --- py/dist/py/initpkg.py (original) +++ py/dist/py/initpkg.py Sat Jan 13 19:03:34 2007 @@ -209,6 +209,17 @@ # force all the content of the module to be loaded when __dict__ is read dictdescr = ModuleType.__dict__['__dict__'] dict = dictdescr.__get__(self) + if not dict: + # we are called very early, before __init__() + # executed ... so we just return here ... + #if dict is None: + # # is there a more elegant way to trigger + # # creation of the __dict__ from CPython? + # ModuleType.__setattr__(self, '_', None) + # d = dictdescr.__get__(self) + # del d['_'] + # return d + return dict if '*' not in self.__map__: for name in self.__map__.keys(): hasattr(self, name) # force attribute to be loaded, ignore errors Modified: py/dist/py/misc/testing/test_initpkg.py ============================================================================== --- py/dist/py/misc/testing/test_initpkg.py (original) +++ py/dist/py/misc/testing/test_initpkg.py Sat Jan 13 19:03:34 2007 @@ -19,6 +19,18 @@ if not name.startswith('_'): yield checksubpackage, name +from py.initpkg import Module +glob = [] +class MyModule(Module): + def __init__(self, *args): + glob.append(self.__dict__) + assert isinstance(glob[-1], (dict, type(None))) + Module.__init__(self, *args) + +def test_early__dict__access(): + mymod = MyModule("whatever", "myname") + assert isinstance(mymod.__dict__, dict) + def test_resolve_attrerror(): extpyish = "./initpkg.py", "hello" excinfo = py.test.raises(AttributeError, "py.__package__._resolve(extpyish)") From hpk at codespeak.net Sat Jan 13 19:07:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 19:07:23 +0100 (CET) Subject: [py-svn] r36690 - py/dist/py Message-ID: <20070113180723.2F55010083@code0.codespeak.net> Author: hpk Date: Sat Jan 13 19:07:21 2007 New Revision: 36690 Modified: py/dist/py/initpkg.py Log: we don't want to play any magic here, so stripping the comments (i already fixed the tracer invocation to deal with __dict__ being done) Modified: py/dist/py/initpkg.py ============================================================================== --- py/dist/py/initpkg.py (original) +++ py/dist/py/initpkg.py Sat Jan 13 19:07:21 2007 @@ -209,24 +209,14 @@ # force all the content of the module to be loaded when __dict__ is read dictdescr = ModuleType.__dict__['__dict__'] dict = dictdescr.__get__(self) - if not dict: - # we are called very early, before __init__() - # executed ... so we just return here ... - #if dict is None: - # # is there a more elegant way to trigger - # # creation of the __dict__ from CPython? - # ModuleType.__setattr__(self, '_', None) - # d = dictdescr.__get__(self) - # del d['_'] - # return d - return dict - if '*' not in self.__map__: - for name in self.__map__.keys(): - hasattr(self, name) # force attribute to be loaded, ignore errors - assert not self.__map__, "%r not empty" % self.__map__ - else: - fsname = self.__map__['*'][0] - dict.update(self.__package__._loadimpl(fsname[:-3]).__dict__) + if dict is not None: + if '*' not in self.__map__: + for name in self.__map__.keys(): + hasattr(self, name) # force attribute to be loaded, ignore errors + assert not self.__map__, "%r not empty" % self.__map__ + else: + fsname = self.__map__['*'][0] + dict.update(self.__package__._loadimpl(fsname[:-3]).__dict__) return dict __dict__ = property(getdict) From hpk at codespeak.net Sat Jan 13 19:10:55 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 19:10:55 +0100 (CET) Subject: [py-svn] r36691 - py/dist/py/test/testing Message-ID: <20070113181055.425B510083@code0.codespeak.net> Author: hpk Date: Sat Jan 13 19:10:54 2007 New Revision: 36691 Modified: py/dist/py/test/testing/test_config.py Log: added a passing test (which IIRC Armin told me should fail) Modified: py/dist/py/test/testing/test_config.py ============================================================================== --- py/dist/py/test/testing/test_config.py (original) +++ py/dist/py/test/testing/test_config.py Sat Jan 13 19:10:54 2007 @@ -73,3 +73,27 @@ # o.ensure('conftest.py').write('x=1') # assert cfg.getconfigvalue(o, 'x') == 1 # py.test.raises(ValueError, "cfg.getconfigvalue(o, 'y')") +# + +def test_siblingconftest_fails_maybe(): + from py.__.test import config + cfg = config.Config() + o = py.test.ensuretemp('siblingconftest') + o.ensure("sister1", "__init__.py") + o.ensure("sister1", "conftest.py").write(py.code.Source(""" + x = 2 + """)) + + o.ensure("sister2", "__init__.py") + o.ensure("sister2", "conftest.py").write(py.code.Source(""" + raise SyntaxError + """)) + + assert cfg.getvalue(path=o.join('sister1'), name='x') == 2 + old = o.chdir() + try: + print py.process.cmdexec("py.test sister1") + o.join('sister1').chdir() + print py.process.cmdexec("py.test") + finally: + old.chdir() From arigo at codespeak.net Sat Jan 13 19:35:11 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 13 Jan 2007 19:35:11 +0100 (CET) Subject: [py-svn] r36696 - py/dist/py/test/testing Message-ID: <20070113183511.E715B10074@code0.codespeak.net> Author: arigo Date: Sat Jan 13 19:35:10 2007 New Revision: 36696 Modified: py/dist/py/test/testing/test_config.py Log: The test fails when the whole thing is within a package. Modified: py/dist/py/test/testing/test_config.py ============================================================================== --- py/dist/py/test/testing/test_config.py (original) +++ py/dist/py/test/testing/test_config.py Sat Jan 13 19:35:10 2007 @@ -76,9 +76,11 @@ # def test_siblingconftest_fails_maybe(): + py.test.skip("in-progress") from py.__.test import config cfg = config.Config() - o = py.test.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(""" x = 2 From arigo at codespeak.net Sat Jan 13 19:37:59 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 13 Jan 2007 19:37:59 +0100 (CET) Subject: [py-svn] r36697 - in py/dist/py/test: . testing Message-ID: <20070113183759.A391A10087@code0.codespeak.net> Author: arigo Date: Sat Jan 13 19:37:56 2007 New Revision: 36697 Modified: py/dist/py/test/collect.py py/dist/py/test/testing/test_config.py Log: A fix for this test. This is an old diff I've been using for a while without problem. It makes things more lazy in some way but I don't remember the details :-/ Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Sat Jan 13 19:37:56 2007 @@ -288,12 +288,21 @@ return path.check(dotfile=0) and \ path.basename not in ('CVS', '_darcs', '{arch}') + def pathfilter(self, path): + if path.check(file=1) and self.filefilter(path): + return True + elif path.check(dir=1) and self.recfilter(path): + return True + else: + return False + def buildname2items(self): d = {} for p in self.fspath.listdir(): - x = self.makeitem(p.basename, self.filefilter, self.recfilter) - if x is not None: - d[p.basename] = x + if self.pathfilter(p): + x = self.join(p.basename) + if x is not None: + d[p.basename] = x return d def makeitem(self, basename, filefilter=None, recfilter=None): @@ -307,11 +316,15 @@ Directory = py.test.Config.getvalue('Directory', p) return Directory(p, parent=self) - def join(self, name): - x = super(Directory, self).join(name) - if x is None: - x = self.makeitem(name) - return x + def join(self, name): + # cache the results to avoid duplicate instances + if not hasattr(self, '_joincache'): + self._joincache = {} + try: + x = self._joincache[name] + except KeyError: + x = self._joincache[name] = self.makeitem(name) + return x class PyCollectorMixin(object): def funcnamefilter(self, name): Modified: py/dist/py/test/testing/test_config.py ============================================================================== --- py/dist/py/test/testing/test_config.py (original) +++ py/dist/py/test/testing/test_config.py Sat Jan 13 19:37:56 2007 @@ -76,7 +76,6 @@ # def test_siblingconftest_fails_maybe(): - py.test.skip("in-progress") from py.__.test import config cfg = config.Config() o = py.test.ensuretemp('siblingconftest') From guido at codespeak.net Sat Jan 13 19:44:55 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 13 Jan 2007 19:44:55 +0100 (CET) Subject: [py-svn] r36698 - in py/dist/py/apigen: . testing Message-ID: <20070113184455.6C95510087@code0.codespeak.net> Author: guido Date: Sat Jan 13 19:44:52 2007 New Revision: 36698 Modified: py/dist/py/apigen/apigen.py py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_apigen_functional.py Log: Building pages for namespaces, fixed bare except (now catches SystemExit/KeyboardInterrupt). Modified: py/dist/py/apigen/apigen.py ============================================================================== --- py/dist/py/apigen/apigen.py (original) +++ py/dist/py/apigen/apigen.py Sat Jan 13 19:44:52 2007 @@ -34,6 +34,8 @@ targetdir = py.path.local('/tmp/py_apigen_output') targetdir.ensure(dir=True) + all_names = dsa._get_names(filter=lambda x, y: True) + ns_data = htmlgen.prepare_namespace_api_pages(l, dsa, targetdir, all_names) class_names = dsa.get_class_names() class_data, method_data = htmlgen.prepare_class_api_pages(l, dsa, targetdir, @@ -43,6 +45,7 @@ function_names) source_data = htmlgen.prepare_source_pages(l, pkgdir, targetdir) + htmlgen.build_namespace_api_pages(ns_data, proj, targetdir) htmlgen.build_class_api_pages(class_data, proj, targetdir) htmlgen.build_method_api_pages(method_data, proj, targetdir) htmlgen.build_function_api_pages(func_data, proj, targetdir) Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Sat Jan 13 19:44:52 2007 @@ -11,6 +11,15 @@ class Description(html.div): style = html.Style(margin_left='10em') + class NamespaceDescription(Description): + pass + + class NamespaceItem(html.div): + pass + + class NamespaceDef(html.h1): + pass + class ClassDescription(Description): pass @@ -117,14 +126,50 @@ '%s.%s' % (dotted_name, method)) return snippet -def build_method_view(base, linker, dsa, dotted_name): - pass +def build_namespace_view(base, linker, dsa, namespace_dotted_name, + item_dotted_names): + """ build the html for a namespace (module) """ + snippet = H.NamespaceDescription( + H.NamespaceDef(namespace_dotted_name), + ) + for dotted_name in item_dotted_names: + snippet.append( + H.NamespaceItem( + H.a(dotted_name.split('.')[-1], + href=linker.get_lazyhref(dotted_name) + ) + ) + ) + return snippet # XXX nav functions need cleanup!! -def build_package_navigation(linker, dotted_name): - # import dotted name - # build nav from sub items - return H.Navigation('navigation') +def build_namespace_navigation(linker, dsa, dotted_name, item_dotted_names): + navitems = [] + path = dotted_name.split('.') + indent = 0 + if len(path) > 1: + parent_dotted_name = '.'.join(path[:-1]) + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(parent_dotted_name, + href=linker.get_lazyhref( + parent_dotted_name)))) + navitems.append(H.br()) + indent += 1 + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(dotted_name.split('.')[-1], + href=linker.get_lazyhref(dotted_name)))) + navitems.append(H.br()) + indent += 1 + for item_dotted_name in item_dotted_names: + navitems.append(indent * 2 * u'\xa0') + itemname = item_dotted_name.split('.')[-1] + navitems.append(H.NavigationItem( + H.a(itemname, + href=linker.get_lazyhref(item_dotted_name)))) + navitems.append(H.br()) + return H.Navigation(*navitems) def build_class_navigation(linker, dsa, dotted_name): navitems = [] @@ -133,15 +178,10 @@ if len(path) > 1: parent_dotted_name = '.'.join(path[:-1]) navitems.append(indent * 2 * u'\xa0') - try: - obj = dsa.get_obj(parent_dotted_name) - except KeyError: - navitems.append(parent_dotted_name) - else: - navitems.append(H.NavigationItem( - H.a(parent_dotted_name, - href=linker.get_lazyhref( - parent_dotted_name)))) + navitems.append(H.NavigationItem( + H.a(parent_dotted_name, + href=linker.get_lazyhref( + parent_dotted_name)))) navitems.append(H.br()) indent += 1 navitems.append(indent * 2 * u'\xa0') @@ -249,6 +289,8 @@ if fspath.check(ext='.py'): try: tag, nav = build_source_python_page(fspath) + except (KeyboardInterrupt, SystemError): + raise except: # XXX strange stuff going wrong at times... need to fix tag, nav = build_source_nonpython_page(fspath) elif fspath.check(dir=True): @@ -325,6 +367,40 @@ targetpath.ensure() targetpath.write(content.encode("utf8")) +def create_namespace_tree(dsa, dotted_names): + ret = {} + class_names = dsa.get_class_names() + for dn in dotted_names: + if not '.' in dn: + continue + namespace, itemname = dn.rsplit('.', 1) + if namespace in class_names: + continue + ret.setdefault(namespace, []).append(dn) + return ret + +def prepare_namespace_api_pages(linker, dsa, base, all_dotted_names): + passed = [] + namespace_tree = create_namespace_tree(dsa, all_dotted_names) + for dotted_name, subitem_dotted_names in namespace_tree.iteritems(): + tag = build_namespace_view(base, linker, dsa, dotted_name, + subitem_dotted_names) + nav = build_namespace_navigation(linker, dsa, dotted_name, + subitem_dotted_names) + reltargetpath = "api/%s.html" % (dotted_name,) + linker.set_link(dotted_name, reltargetpath) + passed.append((dotted_name, tag, nav, reltargetpath)) + return passed + +def build_namespace_api_pages(data, project, outputbase): + for dotted_name, tag, nav, reltargetpath in data: + page = wrap_page(project, 'index of namespace %s' % (dotted_name,), + tag, nav, outputbase) + content = page.unicode() + targetpath = outputbase.join(reltargetpath) + targetpath.ensure() + targetpath.write(content.encode("utf8")) + def wrap_page(project, title, contentel, navel, outputpath): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', stylesheeturl='style.css', Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Sat Jan 13 19:44:52 2007 @@ -103,6 +103,10 @@ data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, ['pkg.SomeClass']) prepare_source_pages(self.linker, self.fs_root, base) + prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) build_class_api_pages(data, self.project, base) build_method_api_pages(methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') @@ -115,6 +119,10 @@ data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, ['pkg.SomeSubClass', 'pkg.SomeClass']) prepare_source_pages(self.linker, self.fs_root, base) + prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) build_class_api_pages(data, self.project, base) build_method_api_pages(methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') @@ -129,6 +137,10 @@ data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, ['pkg.SomeSubClass', 'pkg.SomeClass']) prepare_source_pages(self.linker, self.fs_root, base) + prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) build_class_api_pages(data, self.project, base) build_method_api_pages(methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeSubClass.html') @@ -149,6 +161,10 @@ data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, ['pkg.SomeSubClass', 'pkg.SomeClass']) sourcedata = prepare_source_pages(self.linker, self.fs_root, base) + prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) build_class_api_pages(data, self.project, base) build_method_api_pages(methodsdata, self.project, base) build_source_pages(sourcedata, self.project, base) @@ -156,3 +172,45 @@ assert funchtml.find('href="source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) + def test_create_namespace_tree(self): + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) + assert namespace_tree == {'pkg.sub': ['pkg.sub.func'], + 'pkg': ['pkg.SomeClass', 'pkg.SomeSubClass']} + + def test_build_namespace_api_pages_index(self): + base = py.test.ensuretemp('build_namespace_api_pages') + data = prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) + prepare_class_api_pages(self.linker, self.dsa, base, + ['pkg.SomeClass', 'pkg.SomeSubClass']) + prepare_function_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func']) + prepare_source_pages(self.linker, self.fs_root, base) + build_namespace_api_pages(data, self.project, base) + pkgfile = base.join('api/pkg.html') + assert pkgfile.check() + html = pkgfile.read() + _checkhtml(html) + + def test_build_namespace_api_pages_subnamespace(self): + base = py.test.ensuretemp('build_namespace_api_pages') + data = prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) + prepare_class_api_pages(self.linker, self.dsa, base, + ['pkg.SomeClass', 'pkg.SomeSubClass']) + prepare_function_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func']) + prepare_source_pages(self.linker, self.fs_root, base) + build_namespace_api_pages(data, self.project, base) + subfile = base.join('api/pkg.sub.html') + assert subfile.check() + html = subfile.read() + _checkhtml(html) + Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Sat Jan 13 19:44:52 2007 @@ -28,6 +28,10 @@ assert someclass_init_api.check(file=True) assert someclass_init_api.read().find( '__init__') > -1 + namespace_api = apidir.join('pkg.html') + assert namespace_api.check(file=True) + assert namespace_api.read().find( + 'SomeClass') > -1 sourcedir = tempdir.join('source') assert sourcedir.check(dir=True) From hpk at codespeak.net Sat Jan 13 20:09:37 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 13 Jan 2007 20:09:37 +0100 (CET) Subject: [py-svn] r36699 - in py/dist/py/compat: . testing Message-ID: <20070113190937.CEAA810086@code0.codespeak.net> Author: hpk Date: Sat Jan 13 20:09:35 2007 New Revision: 36699 Added: py/dist/py/compat/conftest.py - copied unchanged from r36698, py/dist/py/compat/testing/conftest.py Removed: py/dist/py/compat/testing/conftest.py Log: ignore the whole compat hierarchy for collecting tests Deleted: /py/dist/py/compat/testing/conftest.py ============================================================================== --- /py/dist/py/compat/testing/conftest.py Sat Jan 13 20:09:35 2007 +++ (empty file) @@ -1,5 +0,0 @@ -import py - -class Directory(py.test.collect.Directory): - def run(self): - py.test.skip("compat tests currently need to be run manually") From guido at codespeak.net Sat Jan 13 23:45:25 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 13 Jan 2007 23:45:25 +0100 (CET) Subject: [py-svn] r36703 - in py/dist/py/apigen: . testing Message-ID: <20070113224525.8C2E910086@code0.codespeak.net> Author: guido Date: Sat Jan 13 23:45:20 2007 New Revision: 36703 Modified: py/dist/py/apigen/apigen.py py/dist/py/apigen/htmlgen.py py/dist/py/apigen/layout.py py/dist/py/apigen/testing/test_apigen_example.py Log: Building dir overview for source packages (not yet for root, later), displaying source code for non-Python files. Modified: py/dist/py/apigen/apigen.py ============================================================================== --- py/dist/py/apigen/apigen.py (original) +++ py/dist/py/apigen/apigen.py Sat Jan 13 23:45:20 2007 @@ -45,9 +45,9 @@ function_names) source_data = htmlgen.prepare_source_pages(l, pkgdir, targetdir) - htmlgen.build_namespace_api_pages(ns_data, proj, targetdir) - htmlgen.build_class_api_pages(class_data, proj, targetdir) - htmlgen.build_method_api_pages(method_data, proj, targetdir) - htmlgen.build_function_api_pages(func_data, proj, targetdir) - htmlgen.build_source_pages(source_data, proj, targetdir) + htmlgen.build_namespace_api_pages(l, ns_data, proj, targetdir) + htmlgen.build_class_api_pages(l, class_data, proj, targetdir) + htmlgen.build_method_api_pages(l, method_data, proj, targetdir) + htmlgen.build_function_api_pages(l, func_data, proj, targetdir) + htmlgen.build_source_pages(l, source_data, proj, targetdir) Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Sat Jan 13 23:45:20 2007 @@ -50,6 +50,15 @@ class SourceDef(html.div): pass + class NonPythonSource(html.pre): + style = html.Style(margin_left='10em') + + class DirList(html.div): + style = html.Style(margin_left='10em') + + class DirListItem(html.div): + pass + def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect @@ -245,7 +254,7 @@ re = py.std.re _reg_body = re.compile(r']*>(.*)', re.S) -def build_source_python_page(fspath): +def build_source_python_page(linker, fspath): mod = source_browser.parse_path(fspath) # XXX let's cheat a bit here... there should be a different function using # the linker, and returning a proper py.xml.html element, at some point @@ -255,13 +264,21 @@ nav = H.Navigation('navigation') return tag, nav -def build_source_dir_page(fspath): - tag = H.div('content') +def build_source_dir_page(linker, fspath): + tag = H.DirList() + for path in fspath.listdir(): + if path.ext in ['.pyc', '.pyo'] or path.basename.startswith('.'): + continue + tag.append(H.DirListItem(H.a(path.basename, + href=linker.get_lazyhref(str(path))))) nav = H.Navigation('navigation') return tag, nav -def build_source_nonpython_page(fspath): - tag = H.div('content') +def build_source_nonpython_page(linker, fspath): + try: + tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8')) + except UnicodeError: + tag = H.NonPythonSource('no source available (binary file?)') nav = H.Navigation('navigation') return tag, nav @@ -283,20 +300,20 @@ passed.append((fspath, outputpath)) return passed -def build_source_pages(data, project, outputbase): +def build_source_pages(linker, data, project, outputbase): """ build syntax-colored source views """ for fspath, outputpath in data: if fspath.check(ext='.py'): try: - tag, nav = build_source_python_page(fspath) + tag, nav = build_source_python_page(linker, fspath) except (KeyboardInterrupt, SystemError): raise except: # XXX strange stuff going wrong at times... need to fix - tag, nav = build_source_nonpython_page(fspath) + tag, nav = build_source_nonpython_page(linker, fspath) elif fspath.check(dir=True): - tag, nav = build_source_dir_page(fspath) + tag, nav = build_source_dir_page(linker, fspath) else: - tag, nav = build_source_nonpython_page(fspath) + tag, nav = build_source_nonpython_page(linker, fspath) page = wrap_page(project, 'sources for %s' % (fspath.basename,), tag, nav, outputbase) outputpath.ensure() @@ -318,7 +335,7 @@ method_dotted_names) return passed, methodsdata -def build_class_api_pages(data, project, outputbase): +def build_class_api_pages(linker, data, project, outputbase): """ build the full api pages for a set of classes """ for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), @@ -338,7 +355,7 @@ passed.append((dotted_name, tag, nav, reltargetpath)) return passed -def build_method_api_pages(data, project, outputbase): +def build_method_api_pages(linker, data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), tag, nav, outputbase) @@ -358,7 +375,7 @@ passed.append((dotted_name, tag, nav, reltargetpath)) return passed -def build_function_api_pages(data, project, outputbase): +def build_function_api_pages(linker, data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), tag, nav, outputbase) @@ -392,7 +409,7 @@ passed.append((dotted_name, tag, nav, reltargetpath)) return passed -def build_namespace_api_pages(data, project, outputbase): +def build_namespace_api_pages(linker, data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'index of namespace %s' % (dotted_name,), tag, nav, outputbase) @@ -403,8 +420,7 @@ def wrap_page(project, title, contentel, navel, outputpath): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', - stylesheeturl='style.css', - base='file://%s/' % (outputpath,)) + stylesheeturl='style.css') page.set_content(contentel) here = py.magic.autopath().dirpath() style = here.join('style.css').read() Modified: py/dist/py/apigen/layout.py ============================================================================== --- py/dist/py/apigen/layout.py (original) +++ py/dist/py/apigen/layout.py Sat Jan 13 23:45:20 2007 @@ -9,11 +9,11 @@ class LayoutPage(Page): """ this provides the layout and style information """ - base = None + base = 'http://localhost/tmp/py_apigen_output/' def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') - self.base = kwargs.pop('base', None) + self.base = kwargs.pop('base', self.base) super(LayoutPage, self).__init__(*args, **kwargs) def set_content(self, contentel): Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Sat Jan 13 23:45:20 2007 @@ -53,7 +53,8 @@ if option.webcheck: webcheck.check_html(htmlstring) else: - py.test.skip("pass --webcheck to validate html produced in tests") + py.test.skip("pass --webcheck to validate html produced in tests " + "(partial skip: the test has succeeded up until here)") def _checkhtmlsnippet(htmlstring): # XXX wrap page around snippet and validate @@ -84,7 +85,7 @@ prepare_source_pages(self.linker, self.fs_root, base) data = prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) - build_function_api_pages(data, self.project, base) + build_function_api_pages(self.linker, data, self.project, base) funcfile = base.join('api/pkg.sub.func.html') assert funcfile.check() html = funcfile.read() @@ -107,8 +108,8 @@ ['pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - build_class_api_pages(data, self.project, base) - build_method_api_pages(methodsdata, self.project, base) + build_class_api_pages(self.linker, data, self.project, base) + build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() @@ -123,8 +124,8 @@ ['pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - build_class_api_pages(data, self.project, base) - build_method_api_pages(methodsdata, self.project, base) + build_class_api_pages(self.linker, data, self.project, base) + build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() @@ -141,8 +142,8 @@ ['pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - build_class_api_pages(data, self.project, base) - build_method_api_pages(methodsdata, self.project, base) + build_class_api_pages(self.linker, data, self.project, base) + build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeSubClass.html') assert clsfile.check() html = clsfile.read() @@ -152,7 +153,7 @@ def test_build_source_pages(self): base = py.test.ensuretemp('build_source_pages') data = prepare_source_pages(self.linker, self.fs_root, base) - build_source_pages(data, self.project, base) + build_source_pages(self.linker, data, self.project, base) somesource = base.join('source/pkg/func.py.html').read() _checkhtml(somesource) @@ -165,9 +166,9 @@ ['pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - build_class_api_pages(data, self.project, base) - build_method_api_pages(methodsdata, self.project, base) - build_source_pages(sourcedata, self.project, base) + build_class_api_pages(self.linker, data, self.project, base) + build_method_api_pages(self.linker, methodsdata, self.project, base) + build_source_pages(self.linker, sourcedata, self.project, base) funchtml = base.join('api/pkg.SomeClass.html').read() assert funchtml.find('href="source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) @@ -191,7 +192,7 @@ prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) prepare_source_pages(self.linker, self.fs_root, base) - build_namespace_api_pages(data, self.project, base) + build_namespace_api_pages(self.linker, data, self.project, base) pkgfile = base.join('api/pkg.html') assert pkgfile.check() html = pkgfile.read() @@ -208,9 +209,20 @@ prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) prepare_source_pages(self.linker, self.fs_root, base) - build_namespace_api_pages(data, self.project, base) + build_namespace_api_pages(self.linker, data, self.project, base) subfile = base.join('api/pkg.sub.html') assert subfile.check() html = subfile.read() _checkhtml(html) + def test_build_source_dir_page(self): + base = py.test.ensuretemp('build_source_dir_page') + data = prepare_source_pages(self.linker, self.fs_root, base) + build_source_pages(self.linker, data, self.project, base) + pkgindex = base.join('source/pkg/index.html') + assert pkgindex.check(file=True) + html = pkgindex.read() + assert ('someclass.py' + in html) + _checkhtml(html) + From hpk at codespeak.net Sun Jan 14 08:38:36 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 08:38:36 +0100 (CET) Subject: [py-svn] r36712 - in py/dist/py/test: . testing Message-ID: <20070114073836.B9CA010087@code0.codespeak.net> Author: hpk Date: Sun Jan 14 08:38:34 2007 New Revision: 36712 Modified: py/dist/py/test/collect.py py/dist/py/test/testing/test_config.py Log: reverting Armin's 36697 fix to the conftest "sibling" problem in favour of another one. Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Sun Jan 14 08:38:34 2007 @@ -288,21 +288,12 @@ return path.check(dotfile=0) and \ path.basename not in ('CVS', '_darcs', '{arch}') - def pathfilter(self, path): - if path.check(file=1) and self.filefilter(path): - return True - elif path.check(dir=1) and self.recfilter(path): - return True - else: - return False - def buildname2items(self): d = {} for p in self.fspath.listdir(): - if self.pathfilter(p): - x = self.join(p.basename) - if x is not None: - d[p.basename] = x + x = self.makeitem(p.basename, self.filefilter, self.recfilter) + if x is not None: + d[p.basename] = x return d def makeitem(self, basename, filefilter=None, recfilter=None): @@ -316,15 +307,11 @@ Directory = py.test.Config.getvalue('Directory', p) return Directory(p, parent=self) - def join(self, name): - # cache the results to avoid duplicate instances - if not hasattr(self, '_joincache'): - self._joincache = {} - try: - x = self._joincache[name] - except KeyError: - x = self._joincache[name] = self.makeitem(name) - return x + def join(self, name): + x = super(Directory, self).join(name) + if x is None: + x = self.makeitem(name) + return x class PyCollectorMixin(object): def funcnamefilter(self, name): Modified: py/dist/py/test/testing/test_config.py ============================================================================== --- py/dist/py/test/testing/test_config.py (original) +++ py/dist/py/test/testing/test_config.py Sun Jan 14 08:38:34 2007 @@ -76,6 +76,7 @@ # def test_siblingconftest_fails_maybe(): + py.test.skip("in-progress") from py.__.test import config cfg = config.Config() o = py.test.ensuretemp('siblingconftest') From hpk at codespeak.net Sun Jan 14 10:39:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 10:39:23 +0100 (CET) Subject: [py-svn] r36715 - in py/dist/py/test: . rsession/testing testing Message-ID: <20070114093923.E0C6410089@code0.codespeak.net> Author: hpk Date: Sun Jan 14 10:39:22 2007 New Revision: 36715 Modified: py/dist/py/test/collect.py py/dist/py/test/rsession/testing/test_reporter.py py/dist/py/test/testing/test_config.py Log: fixing the sibling problem by making the Directory collector not use the buildname2items/prepare logic at all. The according logic is now shifted to the PyCollectorMixin away from the generic Collector object. disabled a few rsession tests which are testing for exact formatting as far as i can see and added a comment in the test_reporter.py module docstring how to maybe approach reporter tests. Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Sun Jan 14 10:39:22 2007 @@ -234,35 +234,9 @@ excinfo = py.code.ExceptionInfo() reporterror((excinfo, 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 buildname2items(self): - raise NotImplementedError, "abstract" - def getsortvalue(self): return self.name - def run(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) - captured_out = captured_err = None def startcapture(self): return None # by default collectors don't capture output @@ -279,39 +253,40 @@ class Directory(FSCollector): def filefilter(self, path): - b = path.purebasename - ext = path.ext - return (b.startswith('test_') or - b.endswith('_test')) and ext in ('.txt', '.py') + 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): - return path.check(dotfile=0) and \ - path.basename not in ('CVS', '_darcs', '{arch}') + if path.check(dir=1, dotfile=0): + return path.basename not in ('CVS', '_darcs', '{arch}') - def buildname2items(self): - d = {} - for p in self.fspath.listdir(): - x = self.makeitem(p.basename, self.filefilter, self.recfilter) - if x is not None: - d[p.basename] = x - return d - - def makeitem(self, basename, filefilter=None, recfilter=None): - p = self.fspath.join(basename) - if p.check(file=1) and (not filefilter or filefilter(p)): - if p.ext == '.py': - return self.Module(p, parent=self) - elif p.ext == '.txt': - return self.DoctestFile(p, parent=self) - elif p.check(dir=1) and (not recfilter or recfilter(p)): - Directory = py.test.Config.getvalue('Directory', p) - return Directory(p, parent=self) + def run(self): + l = [] + for p in self.fspath.listdir(): + if self.filefilter(p) or self.recfilter(p): + l.append(p.basename) + return l - def join(self, name): - x = super(Directory, self).join(name) - if x is None: - x = self.makeitem(name) - return x + 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.test.Config.getvalue('Directory', p) + res = Directory(p, parent=self) + name2items[name] = res + return res class PyCollectorMixin(object): def funcnamefilter(self, name): @@ -319,7 +294,7 @@ def classnamefilter(self, name): return name.startswith('Test') - def buildname2items(self): + def _buildname2items(self): # NB. we avoid random getattrs and peek in the __dict__ instead d = {} dicts = [getattr(self.obj, '__dict__', {})] @@ -345,12 +320,35 @@ else: return self.Function(name, parent=self) -class Module(PyCollectorMixin, FSCollector): - + 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 run(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): def run(self): if getattr(self.obj, 'disabled', 0): return [] - return FSCollector.run(self) + return PyCollectorMixin.run(self) def join(self, name): res = super(Module, self).join(name) @@ -451,13 +449,13 @@ Collector.Function.__get__(self)) # XXX for python 2.2 Function = property(Function) -class Generator(Collector): +class Generator(Collector, PyCollectorMixin): def run(self): self._prepare() itemlist = self._name2items return [itemlist["[%d]" % num].name for num in xrange(len(itemlist))] - def buildname2items(self): + def _buildname2items(self): d = {} # slightly hackish to invoke setup-states on # collection ... Modified: py/dist/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/dist/py/test/rsession/testing/test_reporter.py (original) +++ py/dist/py/test/rsession/testing/test_reporter.py Sun Jan 14 10:39:22 2007 @@ -1,6 +1,20 @@ """ reporter tests. This is crippled test, because we just test if it *works*, not if the output produced is what we really like + + +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 @@ -14,6 +28,8 @@ import sys from StringIO import StringIO + + class AbstractTestReporter(object): def setup_class(cls): cls.pkgdir = py.path.local(py.__file__).dirpath() @@ -164,6 +180,7 @@ self._test_module() def test_full_module(self): + py.test.skip("XXX rewrite test to not rely on exact formatting") received = self._test_full_module() expected = """ repmod/test_one.py[1] @@ -178,6 +195,7 @@ self._test_still_to_go() def test_report_received_item_outcome(self): + py.test.skip("XXX rewrite test to not rely on exact formatting") val = self.report_received_item_outcome() expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass localhost: SKIPPED py test rsession testing test_slave.py funcpass @@ -187,6 +205,7 @@ assert val == expected def test_module(self): + py.test.skip("XXX rewrite test to not rely on exact formatting") val = self._test_module() print val expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass @@ -197,6 +216,7 @@ assert val == expected def test_full_module(self): + py.test.skip("XXX rewrite test to not rely on exact formatting") val = self._test_full_module() assert val == 'FAILED TO LOAD MODULE: repmod/test_three.py\n'\ '\nSkipped (reason) repmod/test_two.py\n\n' Modified: py/dist/py/test/testing/test_config.py ============================================================================== --- py/dist/py/test/testing/test_config.py (original) +++ py/dist/py/test/testing/test_config.py Sun Jan 14 10:39:22 2007 @@ -76,7 +76,6 @@ # def test_siblingconftest_fails_maybe(): - py.test.skip("in-progress") from py.__.test import config cfg = config.Config() o = py.test.ensuretemp('siblingconftest') From guido at codespeak.net Sun Jan 14 12:16:20 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 14 Jan 2007 12:16:20 +0100 (CET) Subject: [py-svn] r36722 - py/dist/py/apigen Message-ID: <20070114111620.1C88D1008A@code0.codespeak.net> Author: guido Date: Sun Jan 14 12:16:19 2007 New Revision: 36722 Modified: py/dist/py/apigen/htmlgen.py Log: Adjusted the tag so the docs can be used directly from the filesystem (usually file:///tmp/py_apigen_output/, depending on $APIGEN_TARGET). Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Sun Jan 14 12:16:19 2007 @@ -36,7 +36,7 @@ pass class Docstring(html.div): - pass + style = html.Style(white_space='pre') class Navigation(html.div): style = html.Style(min_height='99%', float='left', margin_top='1.2em') @@ -420,7 +420,8 @@ def wrap_page(project, title, contentel, navel, outputpath): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', - stylesheeturl='style.css') + stylesheeturl='style.css', + base='file://%s/' % (outputpath.strpath)) page.set_content(contentel) here = py.magic.autopath().dirpath() style = here.join('style.css').read() From hpk at codespeak.net Sun Jan 14 12:34:47 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 12:34:47 +0100 (CET) Subject: [py-svn] r36725 - in py/dist/py/misc: . testing Message-ID: <20070114113447.E2E2910090@code0.codespeak.net> Author: hpk Date: Sun Jan 14 12:34:46 2007 New Revision: 36725 Added: py/dist/py/misc/testing/test_urlhelp.py (contents, props changed) py/dist/py/misc/urlhelp.py (contents, props changed) Log: added a small helper and tests for computing relative URLs (copied from some other code i wrote years ago) Added: py/dist/py/misc/testing/test_urlhelp.py ============================================================================== --- (empty file) +++ py/dist/py/misc/testing/test_urlhelp.py Sun Jan 14 12:34:46 2007 @@ -0,0 +1,27 @@ +import py + +from py.__.misc.urlhelp import relpath + +testspec = [ + 'a a/b a/b', + '/a /a/b a/b', + 'a b b', + '/a /b b', + 'a/b c/d ../c/d', + '/a/b /c/d ../c/d', + 'a/b a ../a', + '/a/b /a ../a', +] + +def gen_check(frompath, topath, expected): + result = relpath(frompath, topath) + print "linking", frompath, "to", topath + assert result == expected + +def test_gen_check(): + for line in testspec: + frompath, topath, expected = line.split() + yield gen_check, frompath, topath, expected + +def test_check_incompatible(): + py.test.raises(ValueError, "relpath('/a', 'b')") Added: py/dist/py/misc/urlhelp.py ============================================================================== --- (empty file) +++ py/dist/py/misc/urlhelp.py Sun Jan 14 12:34:46 2007 @@ -0,0 +1,40 @@ +""" + +help methods for dealing with URLs + +""" + +from __future__ import generators + +def relpath(p1, p2, sep='/', back='..'): + if (p1.startswith(sep) ^ p2.startswith(sep)): + raise ValueError("mixed absolute relative path: %r -> %r" %(p1, p2)) + fromlist = p1.split(sep) + tolist = p2.split(sep) + + # AA + # AA BB -> AA/BB + # + # AA BB + # AA CC -> CC + # + # AA BB + # AA -> ../AA + + diffindex = 0 + for x1, x2 in zip(fromlist, tolist): + if x1 != x2: + break + diffindex += 1 + commonindex = diffindex - 1 + + fromlist_diff = fromlist[diffindex:] + tolist_diff = tolist[diffindex:] + + if not fromlist_diff: + return sep.join(tolist[commonindex:]) + backcount = len(fromlist_diff) + if tolist_diff: + return sep.join([back,]*(backcount-1) + tolist_diff) + return sep.join([back,]*(backcount) + tolist[commonindex:]) + From hpk at codespeak.net Sun Jan 14 13:08:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 13:08:34 +0100 (CET) Subject: [py-svn] r36728 - in py/dist/py: apigen apigen/testing misc misc/testing Message-ID: <20070114120834.4319010094@code0.codespeak.net> Author: hpk Date: Sun Jan 14 13:08:28 2007 New Revision: 36728 Removed: py/dist/py/misc/testing/test_urlhelp.py py/dist/py/misc/urlhelp.py Modified: py/dist/py/apigen/linker.py py/dist/py/apigen/testing/test_linker.py Log: moving logic to compute relative urls to the Linker module/class and adding a get_target_relative method to the Linker class that makes use of it. Modified: py/dist/py/apigen/linker.py ============================================================================== --- py/dist/py/apigen/linker.py (original) +++ py/dist/py/apigen/linker.py Sun Jan 14 13:08:28 2007 @@ -24,6 +24,42 @@ assert linkid not in self._linkid2target self._linkid2target[linkid] = target - def get_target(self, linkid): + def get_target(self, linkid): return self._linkid2target[linkid] + + def get_target_relative(self, linkid, fromlocation): + ref = self.get_target(linkid) + return relpath(fromlocation, ref) +def relpath(p1, p2, sep='/', back='..'): + if (p1.startswith(sep) ^ p2.startswith(sep)): + raise ValueError("mixed absolute relative path: %r -> %r" %(p1, p2)) + fromlist = p1.split(sep) + tolist = p2.split(sep) + + # AA + # AA BB -> AA/BB + # + # AA BB + # AA CC -> CC + # + # AA BB + # AA -> ../AA + + diffindex = 0 + for x1, x2 in zip(fromlist, tolist): + if x1 != x2: + break + diffindex += 1 + commonindex = diffindex - 1 + + fromlist_diff = fromlist[diffindex:] + tolist_diff = tolist[diffindex:] + + if not fromlist_diff: + return sep.join(tolist[commonindex:]) + backcount = len(fromlist_diff) + if tolist_diff: + return sep.join([back,]*(backcount-1) + tolist_diff) + return sep.join([back,]*(backcount) + tolist[commonindex:]) + Modified: py/dist/py/apigen/testing/test_linker.py ============================================================================== --- py/dist/py/apigen/testing/test_linker.py (original) +++ py/dist/py/apigen/testing/test_linker.py Sun Jan 14 13:08:28 2007 @@ -1,61 +1,42 @@ import py -html = py.xml.html -from py.__.apigen.linker import Linker, getrelfspath +from py.__.apigen.linker import Linker, getrelfspath, relpath class TestLinker(object): - def test_one_lazy_link(self): + def test_get_target(self): linker = Linker() lazyhref = linker.get_lazyhref('py.path.local') linker.set_link('py.path.local', 'py/path/local.html') - href = unicode(lazyhref) - assert href == 'py/path/local.html' - - def test_source_link_getrelfspath(self): + relpath = linker.get_target('py.path.local') + assert relpath == 'py/path/local.html' + + def test_target_relative(self): linker = Linker() + lazyhref = linker.get_lazyhref('py.path.local') + linker.set_link('py.path.local', 'py/path/local.html') + relpath = linker.get_target_relative('py.path.local', 'py/index.html') + assert relpath == 'path/local.html' - relfspath = getrelfspath('py.path.local') - lazyhref = linker.get_lazyhref(relfspath) - linker.set_link(relfspath, "hello/world") - href = unicode(lazyhref) - assert href == 'hello/world' - -# considerations for futurue use -""" -snippet = create_method_html('foo.bar', dsa, linker) -snippet.unicode() # links get resolved here? - -def create_method_html(dotted_name, dsa, linker): - ... - # - a = linker.make_source_link(dotted_name) - ... - -def create_source_code_view(srcpath, linker): - ... - # dirpath() - anchor = linker.provide_anchor(dotted_name) - -def convert_srcpath_to_htmlpath(srcpath): - return 'source/%s.html' % (srcpath,) - -def write_sources(root, linker): - for fpath in root.visit(fil='*.py'): - relpath = fpath.relto(root) - htmlpath = convert_srcpath_to_htmlpath(relpath) - linker.set_link(relpath, htmlpath) - gen_source_code_view(relpath, htmlpath) - -Linker: -def make_source_link(self, dotted_name): - relpath = py.path.local(inspect.getfile(dottedname).... - anchor = linker.get_anchor_name(dotted_name) - #html.a(text, href=#anchor) - ... upon resolving time ... - linkref = linker.get_html_relpath(relpath) - linkref += "#" + anchor - -def _make_anchored_link(self, relpath, anchor, text): - #html.a(text, href=#anchor) +testspec = [ + 'a a/b a/b', + '/a /a/b a/b', + 'a b b', + '/a /b b', + 'a/b c/d ../c/d', + '/a/b /c/d ../c/d', + 'a/b a ../a', + '/a/b /a ../a', +] + +def gen_check(frompath, topath, expected): + result = relpath(frompath, topath) + print "linking", frompath, "to", topath + assert result == expected + +def test_gen_check(): + for line in testspec: + frompath, topath, expected = line.split() + yield gen_check, frompath, topath, expected -""" +def test_check_incompatible(): + py.test.raises(ValueError, "relpath('/a', 'b')") Deleted: /py/dist/py/misc/testing/test_urlhelp.py ============================================================================== --- /py/dist/py/misc/testing/test_urlhelp.py Sun Jan 14 13:08:28 2007 +++ (empty file) @@ -1,27 +0,0 @@ -import py - -from py.__.misc.urlhelp import relpath - -testspec = [ - 'a a/b a/b', - '/a /a/b a/b', - 'a b b', - '/a /b b', - 'a/b c/d ../c/d', - '/a/b /c/d ../c/d', - 'a/b a ../a', - '/a/b /a ../a', -] - -def gen_check(frompath, topath, expected): - result = relpath(frompath, topath) - print "linking", frompath, "to", topath - assert result == expected - -def test_gen_check(): - for line in testspec: - frompath, topath, expected = line.split() - yield gen_check, frompath, topath, expected - -def test_check_incompatible(): - py.test.raises(ValueError, "relpath('/a', 'b')") Deleted: /py/dist/py/misc/urlhelp.py ============================================================================== --- /py/dist/py/misc/urlhelp.py Sun Jan 14 13:08:28 2007 +++ (empty file) @@ -1,40 +0,0 @@ -""" - -help methods for dealing with URLs - -""" - -from __future__ import generators - -def relpath(p1, p2, sep='/', back='..'): - if (p1.startswith(sep) ^ p2.startswith(sep)): - raise ValueError("mixed absolute relative path: %r -> %r" %(p1, p2)) - fromlist = p1.split(sep) - tolist = p2.split(sep) - - # AA - # AA BB -> AA/BB - # - # AA BB - # AA CC -> CC - # - # AA BB - # AA -> ../AA - - diffindex = 0 - for x1, x2 in zip(fromlist, tolist): - if x1 != x2: - break - diffindex += 1 - commonindex = diffindex - 1 - - fromlist_diff = fromlist[diffindex:] - tolist_diff = tolist[diffindex:] - - if not fromlist_diff: - return sep.join(tolist[commonindex:]) - backcount = len(fromlist_diff) - if tolist_diff: - return sep.join([back,]*(backcount-1) + tolist_diff) - return sep.join([back,]*(backcount) + tolist[commonindex:]) - From hpk at codespeak.net Sun Jan 14 13:23:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 13:23:31 +0100 (CET) Subject: [py-svn] r36729 - in py/dist/py/test: . testing Message-ID: <20070114122331.76D911009A@code0.codespeak.net> Author: hpk Date: Sun Jan 14 13:23:30 2007 New Revision: 36729 Modified: py/dist/py/test/collect.py py/dist/py/test/testing/test_collect.py Log: fixing directory collection order (first the files, then the directories, and both parts are sorted alphabetically) Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Sun Jan 14 13:23:30 2007 @@ -264,11 +264,16 @@ return path.basename not in ('CVS', '_darcs', '{arch}') def run(self): - l = [] + files = [] + dirs = [] for p in self.fspath.listdir(): - if self.filefilter(p) or self.recfilter(p): - l.append(p.basename) - return l + 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', {}) Modified: py/dist/py/test/testing/test_collect.py ============================================================================== --- py/dist/py/test/testing/test_collect.py (original) +++ py/dist/py/test/testing/test_collect.py Sun Jan 14 13:23:30 2007 @@ -434,3 +434,22 @@ if numj != numi: assert hash(i) != hash(j) assert i != j + + +def 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) + fnames.append(x.basename) + for i in range(9, -1, -1): + x = tmpdir.ensure("test_file%d.py" % (i,)) + fnames.append(x.basename) + fnames.sort() + tmpdir.ensure('adir', dir=1) + fnames.insert(10, 'adir') + col = py.test.collect.Directory(tmpdir) + names = col.run() + assert names == fnames + + From hpk at codespeak.net Sun Jan 14 22:06:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 22:06:05 +0100 (CET) Subject: [py-svn] r36762 - in py/dist/py/apigen: . testing Message-ID: <20070114210605.ABDA6100D2@code0.codespeak.net> Author: hpk Date: Sun Jan 14 22:06:04 2007 New Revision: 36762 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/layout.py py/dist/py/apigen/linker.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_apigen_functional.py py/dist/py/apigen/testing/test_linker.py Log: produce relative links, and not use a "base" part in the html header anymore. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Sun Jan 14 22:06:04 2007 @@ -317,7 +317,8 @@ page = wrap_page(project, 'sources for %s' % (fspath.basename,), tag, nav, outputbase) outputpath.ensure() - outputpath.write(page.unicode().encode("utf8")) + content = page.unicode() #linker.call_withbase(reltargetpath, page.unicode) + outputpath.write(content.encode("utf8")) def prepare_class_api_pages(linker, dsa, base, classes_dotted_names): passed = [] @@ -340,7 +341,7 @@ for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), tag, nav, outputbase) - content = page.unicode() + content = linker.call_withbase(reltargetpath, page.unicode) targetpath = outputbase.ensure(reltargetpath) targetpath.write(content.encode("utf8")) @@ -359,7 +360,7 @@ for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), tag, nav, outputbase) - content = page.unicode() + content = linker.call_withbase(reltargetpath, page.unicode) targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) @@ -379,7 +380,7 @@ for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'api documentation for %s' % (dotted_name,), tag, nav, outputbase) - content = page.unicode() + content = linker.call_withbase(reltargetpath, page.unicode) targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) @@ -413,15 +414,14 @@ for dotted_name, tag, nav, reltargetpath in data: page = wrap_page(project, 'index of namespace %s' % (dotted_name,), tag, nav, outputbase) - content = page.unicode() + content = linker.call_withbase(reltargetpath, page.unicode) targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) def wrap_page(project, title, contentel, navel, outputpath): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', - stylesheeturl='style.css', - base='file://%s/' % (outputpath.strpath)) + stylesheeturl='style.css',) page.set_content(contentel) here = py.magic.autopath().dirpath() style = here.join('style.css').read() Modified: py/dist/py/apigen/layout.py ============================================================================== --- py/dist/py/apigen/layout.py (original) +++ py/dist/py/apigen/layout.py Sun Jan 14 22:06:04 2007 @@ -9,11 +9,8 @@ class LayoutPage(Page): """ this provides the layout and style information """ - base = 'http://localhost/tmp/py_apigen_output/' - def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') - self.base = kwargs.pop('base', self.base) super(LayoutPage, self).__init__(*args, **kwargs) def set_content(self, contentel): @@ -23,6 +20,4 @@ super(LayoutPage, self).fill() self.menubar[:] = [] self.menubar.append(self.nav) - if self.base: - self.head.insert(0, py.xml.html.base(href=self.base)) Modified: py/dist/py/apigen/linker.py ============================================================================== --- py/dist/py/apigen/linker.py (original) +++ py/dist/py/apigen/linker.py Sun Jan 14 22:06:04 2007 @@ -14,6 +14,8 @@ return unicode(self._linker.get_target(self._linkid)) class Linker(object): + fromlocation = None + def __init__(self): self._linkid2target = {} @@ -25,11 +27,18 @@ self._linkid2target[linkid] = target def get_target(self, linkid): - return self._linkid2target[linkid] - - def get_target_relative(self, linkid, fromlocation): - ref = self.get_target(linkid) - return relpath(fromlocation, ref) + linktarget = self._linkid2target[linkid] + if self.fromlocation is not None: + linktarget = relpath(self.fromlocation, linktarget) + return linktarget + + def call_withbase(self, base, func, *args, **kwargs): + assert self.fromlocation is None + self.fromlocation = base + try: + return func(*args, **kwargs) + finally: + del self.fromlocation def relpath(p1, p2, sep='/', back='..'): if (p1.startswith(sep) ^ p2.startswith(sep)): Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Sun Jan 14 22:06:04 2007 @@ -130,7 +130,7 @@ assert clsfile.check() html = clsfile.read() assert html.find( - 'href="api/pkg.SomeClass.__init__.html">__init__') > -1 + 'href="pkg.SomeClass.__init__.html">__init__') > -1 _checkhtml(html) def test_build_class_api_pages_base_link(self): @@ -147,7 +147,7 @@ clsfile = base.join('api/pkg.SomeSubClass.html') assert clsfile.check() html = clsfile.read() - assert html.find('href="api/pkg.SomeClass.html">pkg.SomeClass') > -1 + assert html.find('href="pkg.SomeClass.html">pkg.SomeClass') > -1 _checkhtml(html) def test_build_source_pages(self): @@ -170,7 +170,7 @@ build_method_api_pages(self.linker, methodsdata, self.project, base) build_source_pages(self.linker, sourcedata, self.project, base) funchtml = base.join('api/pkg.SomeClass.html').read() - assert funchtml.find('href="source/pkg/someclass.py.html"') > -1 + assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) def test_create_namespace_tree(self): Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Sun Jan 14 22:06:04 2007 @@ -23,15 +23,15 @@ someclass_api = apidir.join('pkg.SomeClass.html') assert someclass_api.check(file=True) assert someclass_api.read().find( - 'SomeClass') > -1 + 'SomeClass') > -1 someclass_init_api = apidir.join('pkg.SomeClass.__init__.html') assert someclass_init_api.check(file=True) assert someclass_init_api.read().find( - '__init__') > -1 + '__init__') > -1 namespace_api = apidir.join('pkg.html') assert namespace_api.check(file=True) assert namespace_api.read().find( - 'SomeClass') > -1 + 'SomeClass') > -1 sourcedir = tempdir.join('source') assert sourcedir.check(dir=True) Modified: py/dist/py/apigen/testing/test_linker.py ============================================================================== --- py/dist/py/apigen/testing/test_linker.py (original) +++ py/dist/py/apigen/testing/test_linker.py Sun Jan 14 22:06:04 2007 @@ -13,9 +13,11 @@ linker = Linker() lazyhref = linker.get_lazyhref('py.path.local') linker.set_link('py.path.local', 'py/path/local.html') - relpath = linker.get_target_relative('py.path.local', 'py/index.html') + relpath = linker.call_withbase('py/index.html', + linker.get_target, 'py.path.local') assert relpath == 'path/local.html' + testspec = [ 'a a/b a/b', From hpk at codespeak.net Sun Jan 14 22:11:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 22:11:34 +0100 (CET) Subject: [py-svn] r36763 - in py/dist/py/path: . testing Message-ID: <20070114211134.CA91E100CF@code0.codespeak.net> Author: hpk Date: Sun Jan 14 22:11:33 2007 New Revision: 36763 Modified: py/dist/py/path/common.py py/dist/py/path/testing/common.py Log: remove deprecated 'get()' method Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Sun Jan 14 22:11:33 2007 @@ -162,23 +162,20 @@ return last def __add__(self, other): + """ return new path object with 'other' added to the basename""" return self.new(basename=self.basename+str(other)) def __cmp__(self, other): + """ return sort value (-1, 0, +1). """ try: return cmp(self.strpath, other.strpath) except AttributeError: return cmp(str(self), str(other)) # self.path, other.path) def __repr__(self): + """ return a string representation of this path. """ return repr(str(self)) - def get(self, spec): - """ deprecated, use _getbyspec if you really need it. """ - py.std.warnings.warn("path.get() is deprecated", - DeprecationWarning, stacklevel=2) - return self._getbyspec(spec) - def visit(self, fil=None, rec=None, ignore=_dummyclass): if isinstance(fil, str): fil = fnmatch(fil) Modified: py/dist/py/path/testing/common.py ============================================================================== --- py/dist/py/path/testing/common.py (original) +++ py/dist/py/path/testing/common.py Sun Jan 14 22:11:33 2007 @@ -4,9 +4,6 @@ class CommonPathTests: root = None # subclasses have to setup a 'root' attribute - def test_get_deprecation(self): - py.test.deprecated_call(self.root.get, "basename") - def test_constructor_equality(self): p = self.root.__class__(self.root) assert p == self.root From hpk at codespeak.net Sun Jan 14 22:16:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 14 Jan 2007 22:16:14 +0100 (CET) Subject: [py-svn] r36764 - py/dist/py/doc Message-ID: <20070114211614.CD0FD100CF@code0.codespeak.net> Author: hpk Date: Sun Jan 14 22:16:14 2007 New Revision: 36764 Modified: py/dist/py/doc/TODO.txt Log: let's not forget to go through the current deprecated API and remove some ahead of the release Modified: py/dist/py/doc/TODO.txt ============================================================================== --- py/dist/py/doc/TODO.txt (original) +++ py/dist/py/doc/TODO.txt Sun Jan 14 22:16:14 2007 @@ -27,6 +27,8 @@ * remove: test.compat.TestCAse + +* check and likely remove already deprecated API * remove from public namespace: XXX consider py.magic. invoke/revoke/patch/revert From arigo at codespeak.net Mon Jan 15 11:33:23 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 15 Jan 2007 11:33:23 +0100 (CET) Subject: [py-svn] r36767 - py/dist/py/path/local Message-ID: <20070115103323.17236100DC@code0.codespeak.net> Author: arigo Date: Mon Jan 15 11:33:17 2007 New Revision: 36767 Modified: py/dist/py/path/local/local.py Log: A race condition (found by a PyPy failure on the parallelized autotest run on wyvern). Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Mon Jan 15 11:33:17 2007 @@ -280,7 +280,13 @@ if parent.check(dir=0): parent._ensuredirs() if self.check(dir=0): - self.mkdir() + try: + self.mkdir() + except py.error.EEXIST: + # race condition: file/dir created by another thread/process. + # complain if it is not a dir + if self.check(dir=0): + raise return self def ensure(self, *args, **kwargs): From fijal at codespeak.net Mon Jan 15 20:37:44 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 15 Jan 2007 20:37:44 +0100 (CET) Subject: [py-svn] r36787 - py/dist/py/test/rsession Message-ID: <20070115193744.5697B10074@code0.codespeak.net> Author: fijal Date: Mon Jan 15 20:37:42 2007 New Revision: 36787 Modified: py/dist/py/test/rsession/web.py py/dist/py/test/rsession/webjs.py Log: Adapt to a new interface of pypy's javascript backend. Probably we'll need some additional helpers for having it cleaner, but this at least works. Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Mon Jan 15 20:37:42 2007 @@ -17,7 +17,6 @@ from py.__.test.rsession.rsession import RSession, session_options from py.__.test.rsession import report from py.__.test import collect - from py.__.test.rsession.webdata import json DATADIR = py.path.local(__file__).dirpath("webdata") @@ -34,6 +33,7 @@ described from pypy.translator.js.main import rpython2javascript from pypy.translator.js import commproxy + from pypy.rpython.extfunc import _callable commproxy.USE_MOCHIKIT = False IMPORTED_PYPY = True @@ -46,6 +46,9 @@ return func return decorator + def _callable(*args, **kwargs): + pass + IMPORTED_PYPY = False def add_item(event): @@ -151,19 +154,22 @@ def show_hosts(self): self.start_event.wait() return self.hosts - show_hosts = described(retval={"aa": "aa"})(show_hosts) + show_hosts = described(retval={str:str}, args=[ + _callable([{str:str}])])(show_hosts) def show_skip(self, item_name="aa"): return {'item_name': item_name, 'reason': self.skip_reasons[item_name]} - show_skip = described(retval={"aa": "aa"})(show_skip) + show_skip = described(retval={str:str}, args=[str, + _callable([{str:str}])])(show_skip) def show_fail(self, item_name="aa"): return {'item_name':item_name, 'traceback':str(self.fail_reasons[item_name]), 'stdout':self.stdout[item_name], 'stderr':self.stderr[item_name]} - show_fail = described(retval={"aa": "aa"})(show_fail) + show_fail = described(retval={str:str}, args=[str, + _callable([{str:str}])])(show_fail) _sessids = None _sesslock = py.std.thread.allocate_lock() @@ -182,7 +188,8 @@ finally: self._sesslock.release() return sessid - show_sessid = described(retval="aa")(show_sessid) + show_sessid = described(retval=str, args=[ + _callable([str])])(show_sessid) def failed(self, **kwargs): if not 'sessid' in kwargs: @@ -202,7 +209,8 @@ retlist.append(self.show_status_change(sessid)) retval = retlist return retval - show_all_statuses = described(retval=[{"aa": "aa"}])(show_all_statuses) + show_all_statuses = described(retval=[{str:str}],args= + [str, _callable([[{str:str}]])])(show_all_statuses) def show_status_change(self, sessid): event = self.pending_events.get(sessid) Modified: py/dist/py/test/rsession/webjs.py ============================================================================== --- py/dist/py/test/rsession/webjs.py (original) +++ py/dist/py/test/rsession/webjs.py Mon Jan 15 20:37:42 2007 @@ -36,7 +36,7 @@ glob = Globals() -def comeback(msglist=[{"aa": "aa"}]): +def comeback(msglist): if len(msglist) == 0: return for item in glob.pending[:]: From fijal at codespeak.net Tue Jan 16 12:51:31 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 12:51:31 +0100 (CET) Subject: [py-svn] r36798 - in py/branch/rsession-cleanup/test/rsession: . testing Message-ID: <20070116115131.BBEC81008F@code0.codespeak.net> Author: fijal Date: Tue Jan 16 12:51:25 2007 New Revision: 36798 Modified: py/branch/rsession-cleanup/test/rsession/hostmanage.py py/branch/rsession-cleanup/test/rsession/master.py py/branch/rsession-cleanup/test/rsession/report.py py/branch/rsession-cleanup/test/rsession/reporter.py py/branch/rsession-cleanup/test/rsession/rsession.py py/branch/rsession-cleanup/test/rsession/testing/test_reporter.py py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py Log: Rewrite of hostmanage information keeping of hosts. Commiting to a branch, will try to commit to trunk. Modified: py/branch/rsession-cleanup/test/rsession/hostmanage.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/hostmanage.py (original) +++ py/branch/rsession-cleanup/test/rsession/hostmanage.py Tue Jan 16 12:51:25 2007 @@ -7,8 +7,41 @@ from py.__.test.rsession import report from py.__.test.rsession.rsync import RSync -class HostRSync(RSync): +class HostInfo(object): + """ Class trying to store all necessary attributes + for host + """ + host_ids = {} + + def __init__(self, hostname, relpath=None): + self.hostid = self._getuniqueid(hostname) + self.hostname = hostname + self.relpath = relpath + + def _getuniqueid(cls, hostname): + if not hostname in cls.host_ids: + cls.host_ids[hostname] = 0 + return hostname + retval = hostname + '_' + str(cls.host_ids[hostname]) + cls.host_ids[hostname] += 1 + return retval + _getuniqueid = classmethod(_getuniqueid) + + def __str__(self): + return "" % (self.hostname,) + + def __hash__(self): + return hash(self.hostid) + + def __eq__(self, other): + return self.hostid == other.hostid + + def __ne__(self, other): + return not self == other +class HostRSync(RSync): + """ An rsync wrapper which filters out *~, .svn/ and *.pyc + """ def __init__(self, rsync_roots): RSync.__init__(self, delete=True) self.rsync_roots = rsync_roots @@ -24,57 +57,46 @@ else: return base in self.rsync_roots -def prepare_gateway(sshosts, relpath, rsync_roots, optimise_localhost, +def prepare_gateway(sshosts, optimise_localhost, remote_python, pkgdir, real_create=True): hosts = [] - for num, host in enumerate(sshosts): - if host != 'localhost' or not optimise_localhost: - if isinstance(relpath, str): - assert not os.path.isabs(relpath), relpath - remoterootpath = relpath - else: - remoterootpath = relpath[(num, host)] - # XXX: because of NFS we do create different directories - # otherwise, .pyc files overlap - remoterootpath += "-" + host + for host in sshosts: + if host.hostname != 'localhost' or not optimise_localhost: if real_create: # for tests we want to use somtehing different - if host == 'localhost' and optimise_localhost is False: + if host.hostname == 'localhost' and optimise_localhost is False: from py.__.execnet.register import PopenCmdGateway gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'") - if not remoterootpath.startswith("/"): - remoteroopath = os.environ['HOME'] + '/' + remoterootpath + if not host.relpath.startswith("/"): + host.relpath = os.environ['HOME'] + '/' + host.relpath else: if remote_python is None: - gw = py.execnet.SshGateway(host) + gw = py.execnet.SshGateway(host.hostname) else: - gw = py.execnet.SshGateway(host, remotepython=remote_python) - gw.hostid = host + str(num) + gw = py.execnet.SshGateway(host.hostname, + remotepython=remote_python) else: gw = None - hosts.append((num, host, gw, remoterootpath)) else: if remote_python is None: gw = py.execnet.PopenGateway() else: gw = py.execnet.PopenGateway(remotepython=remote_python) - gw.hostid = 'localhost' + str(num) - gw.sshaddress = 'localhost' - hosts.append((num, host, gw, str(pkgdir.dirpath()))) - return hosts - -# XXX: Options has grown a bit too much, but most of them are just for tests -def init_hosts(reporter, sshhosts, relpath, pkgdir, rsync_roots=None, \ - remote_python=None, remote_options={}, optimise_localhost=True,\ + host.relpath = str(pkgdir.dirpath()) + host.gw = gw + return sshosts + +def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None, + remote_python=None, \ + remote_options={}, optimise_localhost=True,\ do_sync=True, done_dict=None): if done_dict is None: done_dict = {} assert pkgdir.join("__init__.py").check(), ( "%s probably wrong" %(pkgdir,)) - assert relpath, relpath - + exc_info = [None] - hosts = prepare_gateway(sshhosts, relpath, rsync_roots, optimise_localhost, + hosts = prepare_gateway(sshhosts, optimise_localhost, remote_python, pkgdir, real_create=do_sync) # rsyncing @@ -82,19 +104,19 @@ if do_sync: rsync = HostRSync(rsync_roots) - for num, host, gw, remoterootpath in hosts: - if (host, remoterootpath) in rsynced or (host == 'localhost' \ - and optimise_localhost): - key = host + str(num) - reporter(report.HostReady(host, key)) + for host in hosts: + #for num, host, gw, remoterootpath in hosts: + remoterootpath = host.relpath + if (host, remoterootpath) in rsynced or\ + (host.hostname == 'localhost' and optimise_localhost): + reporter(report.HostReady(host)) continue - rsynced[(host, remoterootpath)] = True - def done(host=host, num=num): - key = host + str(num) - reporter(report.HostReady(host, key)) - reporter(report.HostRSyncing(host, remoterootpath)) + rsynced[(host.hostname, host.relpath)] = True + def done(host=host): + reporter(report.HostReady(host)) + reporter(report.HostRSyncing(host)) if do_sync: - rsync.add_target(gw, remoterootpath, done) + rsync.add_target(host.gw, remoterootpath, done) if not do_sync: return # for testing only rsync.send(pkgdir.dirpath()) @@ -104,9 +126,9 @@ def setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict): nodes = [] - for num, host, gw, remoterootpath in hosts: - ch = setup_slave(gw, os.path.join(remoterootpath, pkgdir.basename), - remote_options) + for host in hosts: + ch = setup_slave(host.gw, os.path.join(host.relpath,\ + pkgdir.basename), remote_options) nodes.append(MasterNode(ch, reporter, done_dict)) return nodes @@ -135,16 +157,3 @@ except: pass channel.gateway.exit() - -##def bin_rsync(sources, sshaddress, destpath): -## _rsync = py.path.local.sysfind("rsync") -## assert destpath -## args = ["-az", "--delete-excluded", -## "--delete", "--exclude=.svn/", "--exclude=*.pyc", '--exclude=*~'] -## if isinstance(sources, list): -## args += sources -## else: -## args.append(str(sources) + "/") -## args.append(sshaddress + ":" + str(destpath)) -## print '*', 'rsync', ' '.join(args) -## _rsync.sysexec(*args) Modified: py/branch/rsession-cleanup/test/rsession/master.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/master.py (original) +++ py/branch/rsession-cleanup/test/rsession/master.py Tue Jan 16 12:51:25 2007 @@ -11,7 +11,6 @@ self.reporter = reporter def callback(outcome): - #import pdb;pdb.set_trace() item = self.pending.pop() if not item in done_dict: self.receive_result(outcome, item) Modified: py/branch/rsession-cleanup/test/rsession/report.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/report.py (original) +++ py/branch/rsession-cleanup/test/rsession/report.py Tue Jan 16 12:51:25 2007 @@ -70,14 +70,14 @@ pass class HostRSyncing(ReportEvent): - def __init__(self, hostname, remoterootpath): - self.hostname = hostname - self.remoterootpath = remoterootpath + def __init__(self, host): + self.hostname = host.hostname + self.remoterootpath = host.relpath class HostReady(ReportEvent): - def __init__(self, hostname, hostid): - self.hostname = hostname - self.hostid = hostid + def __init__(self, host): + self.hostname = host.hostname + self.hostid = host.hostid class TestStarted(ReportEvent): def __init__(self, hosts): @@ -117,3 +117,8 @@ def __init__(self, item, outcome): self.item = item self.outcome = outcome + +class PongReceived(ReportEvent): + def __init__(self, hostid, result): + self.hostid = hostid + self.result = result Modified: py/branch/rsession-cleanup/test/rsession/reporter.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/reporter.py (original) +++ py/branch/rsession-cleanup/test/rsession/reporter.py Tue Jan 16 12:51:25 2007 @@ -38,16 +38,16 @@ 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 + #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 def report_unknown(self, what): if self.config.option.verbose: @@ -72,7 +72,8 @@ print "%10s: READY" % item.hostname[:10] def report_TestStarted(self, item): - txt = " Test started, hosts: %s " % ", ".join(item.hosts) + hostnames = [host.hostname for host in item.hosts] + txt = " Test started, hosts: %s " % ", ".join(hostnames) self.hosts_to_rsync = len(item.hosts) self.out.sep("=", txt) self.timestart = item.timestart Modified: py/branch/rsession-cleanup/test/rsession/rsession.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/rsession.py (original) +++ py/branch/rsession-cleanup/test/rsession/rsession.py Tue Jan 16 12:51:25 2007 @@ -11,7 +11,7 @@ from py.__.test.rsession import report from py.__.test.rsession.master import \ setup_slave, MasterNode, dispatch_loop, itemgen, randomgen -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts +from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner, RunnerPolicy @@ -172,17 +172,14 @@ return new_reporter, checkfun def parse_directories(sshhosts): - # dictionary containing directories for hosts - # XXX: should be class with some info like key, etc. in future directories = {} - for num, host in enumerate(sshhosts): - m = re.match("^(.*?):(.*)$", host) + for host in sshhosts: + m = re.match("^(.*?):(.*)$", host.hostname) if m: - directories[(num, m.group(1))] = m.group(2) - sshhosts[num] = m.group(1) + host.hostname = m.group(1) + host.relpath = m.group(2) else: - directories[(num, host)] = "pytestcache" - return directories + host.relpath = "pytestcache-%s" % host.hostname class RSession(AbstractSession): """ Remote version of session @@ -193,7 +190,7 @@ args = [py.path.local()] session_options.bind_config(self.config) - sshhosts, directories, remotepython, rsync_roots = self.read_distributed_config() + sshhosts, remotepython, rsync_roots = self.read_distributed_config() reporter, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) reporter, checkfun = self.wrap_reporter(reporter) @@ -202,7 +199,7 @@ pkgdir = self.getpkgdir(args[0]) done_dict = {} - nodes = init_hosts(reporter, sshhosts, directories, pkgdir, + nodes = init_hosts(reporter, sshhosts, pkgdir, rsync_roots, remotepython, remote_options=remote_options.d, optimise_localhost=self.optimise_localhost, done_dict=done_dict) reporter(report.RsyncFinished()) @@ -222,13 +219,14 @@ rsync_roots = self.config.getinitialvalue("distrsync_roots") except: rsync_roots = None # all files and directories in the pkgdir - sshhosts = self.config.getinitialvalue("disthosts") - directories = parse_directories(sshhosts) + sshhosts = [HostInfo(i) for i in + self.config.getinitialvalue("disthosts")] + parse_directories(sshhosts) try: remotepython = self.config.getinitialvalue("dist_remotepython") except: remotepython = None - return sshhosts, directories, remotepython, rsync_roots + return sshhosts, remotepython, rsync_roots def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) Modified: py/branch/rsession-cleanup/test/rsession/testing/test_reporter.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/testing/test_reporter.py (original) +++ py/branch/rsession-cleanup/test/rsession/testing/test_reporter.py Tue Jan 16 12:51:25 2007 @@ -9,6 +9,7 @@ from py.__.test.rsession import report from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession.testing.test_slave import funcpass_spec, mod_spec +from py.__.test.rsession.hostmanage import HostInfo from py.__.test.rsession.box import Box #from py.__.test. import sys @@ -120,8 +121,9 @@ def boxfun(): config, args = py.test.Config.parse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) - r = self.reporter(config, ["localhost"]) - r.report(report.TestStarted(['localhost'])) + host = HostInfo('localhost') + r = self.reporter(config, [host]) + r.report(report.TestStarted([host])) r.report(report.RsyncFinished()) list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) r.report(report.TestFinished()) @@ -140,12 +142,12 @@ stdoutcopy = sys.stdout sys.stdout = s config, args = py.test.Config.parse([str(tmpdir)]) - hosts = ["host1", "host2", "host3"] + hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) r.report(report.TestStarted(hosts)) - r.report(report.HostReady("host1", "host1")) - r.report(report.HostReady("host2", "host2")) - r.report(report.HostReady("host3", "host3")) + r.report(report.HostReady(hosts[0])) + r.report(report.HostReady(hosts[1])) + r.report(report.HostReady(hosts[2])) sys.stdout = stdoutcopy expected = """================= Test started, hosts: host1, host2, host3 ================== host1: READY (still 2 to go) Modified: py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py (original) +++ py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py Tue Jan 16 12:51:25 2007 @@ -5,8 +5,9 @@ import py from py.__.test.rsession import report from py.__.test.rsession.rsession import RSession, parse_directories,\ - session_options, remote_options -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts + session_options, remote_options, parse_directories +from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\ + HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ funcoptioncustom_spec, funcoption_spec @@ -16,8 +17,8 @@ def test_setup_non_existing_hosts(): setup_events = [] - hosts = ["alskdjalsdkjasldkajlsd"] - cmd = "init_hosts(setup_events.append, hosts, 'pytesttest', pkgdir)" + hosts = [HostInfo("alskdjalsdkjasldkajlsd")] + cmd = "init_hosts(setup_events.append, hosts, pkgdir)" py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) #assert setup_events @@ -70,45 +71,6 @@ assert str(events[1][0].value) == "Reason" class TestRSessionRemote: - #def setup_class(cls): - # from py.__.test.rsession.conftest import option - # if not option.disthosts: - # py.test.skip("no test distribution ssh hosts specified") - # cls.hosts = option.disthosts.split(",") - -## def test_rsync_does_what_it_should(self): -## host = self.hosts[0] -## gw = py.execnet.SshGateway(host) -## channel = gw.remote_exec(""" -## import tempfile -## tmp = tempfile.mkdtemp() -## try: -## channel.send(tmp) -## channel.receive() # sync -## import os -## p = os.path.join(tmp, "a") -## assert os.path.exists(p) -## p2 = os.path.join(p, "__init__.py") -## assert os.path.exists(p2) -## p3 = os.path.join(p, "x.pyc") -## assert not os.path.exists(p3) -## p4 = os.path.join(p, "__init__.pyc") -## assert not os.path.exists(p4) -## channel.send("ok") -## finally: -## import shutil -## shutil.rmtree(tmp) -## """) -## tmpdir = py.test.ensuretemp("rsynctest") -## tmpdir.ensure("a", "__init__.py") -## tmpdir.ensure("a", "no.pyc") -## tmpdir.ensure("a", "__init__.pyc") -## remote_tmpdir = channel.receive() -## bin_rsync(tmpdir, host, remote_tmpdir) -## channel.send(None) -## res = channel.receive() -## assert res == "ok" - def test_example_distribution_minus_x(self): tmpdir = py.test.ensuretemp("example_distribution_minus_x") tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" @@ -187,13 +149,14 @@ assert tb[0].source.find("execute") != -1 def test_setup_teardown_ssh(self): - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) setup_events = [] teardown_events = [] config, args = py.test.Config.parse([]) session_options.bind_config(config) - nodes = init_hosts(setup_events.append, hosts, 'pytesttest', pkgdir, + nodes = init_hosts(setup_events.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -214,12 +177,13 @@ assert len(teardown_wait_ends) == len(hosts) def test_setup_teardown_run_ssh(self): - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) allevents = [] config, args = py.test.Config.parse([]) session_options.bind_config(config) - nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, + nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) from py.__.test.rsession.testing.test_executor \ @@ -257,12 +221,13 @@ """ Tests options object passing master -> server """ allevents = [] - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) config, args = py.test.Config.parse([]) session_options.bind_config(config) d = remote_options.d.copy() d['custom'] = 'custom' - nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, + nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], remote_options=d, optimise_localhost=False) @@ -289,7 +254,8 @@ """ Tests if nice level behaviour is ok """ allevents = [] - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) tmpdir = py.test.ensuretemp("nice") tmpdir.ensure("__init__.py") tmpdir.ensure("conftest.py").write("""disthosts = ['localhost']""") @@ -308,7 +274,8 @@ passevents = [x for x in testevents if x.outcome.passed] assert len(passevents) == 1 -class TestDirectories(object): +class XxxTestDirectories(object): + # need complete rewrite def test_simple_parse(self): sshhosts = ['h1', 'h2', 'h3'] dirs = parse_directories(sshhosts) @@ -328,6 +295,7 @@ class TestInithosts(object): def test_inithosts(self): + py.test.skip("need rewrite") testevents = [] hosts = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] dirs = parse_directories(hosts) From fijal at codespeak.net Tue Jan 16 13:19:58 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 13:19:58 +0100 (CET) Subject: [py-svn] r36800 - in py/dist/py/test/rsession: . testing Message-ID: <20070116121958.34F3710093@code0.codespeak.net> Author: fijal Date: Tue Jan 16 13:19:56 2007 New Revision: 36800 Modified: py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/master.py py/dist/py/test/rsession/report.py py/dist/py/test/rsession/reporter.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_reporter.py py/dist/py/test/rsession/testing/test_rsession.py Log: Merge from a branch, a bit of rewrite of hostmanage.py, introducing new class HostInfo Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Tue Jan 16 13:19:56 2007 @@ -7,10 +7,43 @@ from py.__.test.rsession import report from py.__.test.rsession.rsync import RSync -class HostRSync(RSync): +class HostInfo(object): + """ Class trying to store all necessary attributes + for host + """ + host_ids = {} + + def __init__(self, hostname, relpath=None): + self.hostid = self._getuniqueid(hostname) + self.hostname = hostname + self.relpath = relpath + + def _getuniqueid(cls, hostname): + if not hostname in cls.host_ids: + cls.host_ids[hostname] = 0 + return hostname + retval = hostname + '_' + str(cls.host_ids[hostname]) + cls.host_ids[hostname] += 1 + return retval + _getuniqueid = classmethod(_getuniqueid) + + def __str__(self): + return "" % (self.hostname,) + + def __hash__(self): + return hash(self.hostid) + + def __eq__(self, other): + return self.hostid == other.hostid - def __init__(self, rsync_roots, **args): - RSync.__init__(self, delete=True, **args) + def __ne__(self, other): + return not self == other + +class HostRSync(RSync): + """ An rsync wrapper which filters out *~, .svn/ and *.pyc + """ + def __init__(self, rsync_roots): + RSync.__init__(self, delete=True) self.rsync_roots = rsync_roots def filter(self, path): @@ -24,97 +57,68 @@ else: return base in self.rsync_roots -def prepare_gateway(sshosts, relpath, rsync_roots, optimise_localhost, +def prepare_gateway(sshosts, optimise_localhost, remote_python, pkgdir, real_create=True): hosts = [] - for num, host in enumerate(sshosts): - if host != 'localhost' or not optimise_localhost: - if isinstance(relpath, str): - assert not os.path.isabs(relpath), relpath - remoterootpath = relpath - else: - remoterootpath = relpath[(num, host)] - # XXX: because of NFS we do create different directories - # otherwise, .pyc files overlap - remoterootpath += "-" + host + for host in sshosts: + if host.hostname != 'localhost' or not optimise_localhost: if real_create: # for tests we want to use somtehing different - if host == 'localhost' and optimise_localhost is False: + if host.hostname == 'localhost' and optimise_localhost is False: from py.__.execnet.register import PopenCmdGateway gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'") - if not remoterootpath.startswith("/"): - remoteroopath = os.environ['HOME'] + '/' + remoterootpath + if not host.relpath.startswith("/"): + host.relpath = os.environ['HOME'] + '/' + host.relpath else: if remote_python is None: - gw = py.execnet.SshGateway(host) + gw = py.execnet.SshGateway(host.hostname) else: - gw = py.execnet.SshGateway(host, remotepython=remote_python) - gw.hostid = host + str(num) + gw = py.execnet.SshGateway(host.hostname, + remotepython=remote_python) else: gw = None - hosts.append((num, host, gw, remoterootpath)) else: if remote_python is None: gw = py.execnet.PopenGateway() else: gw = py.execnet.PopenGateway(remotepython=remote_python) - gw.hostid = 'localhost' + str(num) - gw.sshaddress = 'localhost' - hosts.append((num, host, gw, str(pkgdir.dirpath()))) - return hosts - -class ReportRsyncProgress: - def __init__(self, reporter): - self.reporter = reporter - self.total = {} - self.done = {} - - def callback(self, cmd, data, channel): - if cmd == "list": - self.total[channel] = data - self.done[channel] = 0 - else: - self.done[channel] += data - fraction = float(self.done[channel])/self.total[channel] - self.reporter(report.RsyncProgress(None, fraction)) - -# XXX: Options has grown a bit too much, but most of them are just for tests -def init_hosts(reporter, sshhosts, relpath, pkgdir, rsync_roots=None, \ - remote_python=None, remote_options={}, optimise_localhost=True,\ + host.relpath = str(pkgdir.dirpath()) + host.gw = gw + return sshosts + +def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None, + remote_python=None, \ + remote_options={}, optimise_localhost=True,\ do_sync=True, done_dict=None): if done_dict is None: done_dict = {} assert pkgdir.join("__init__.py").check(), ( "%s probably wrong" %(pkgdir,)) - assert relpath, relpath - + exc_info = [None] - hosts = prepare_gateway(sshhosts, relpath, rsync_roots, optimise_localhost, + hosts = prepare_gateway(sshhosts, optimise_localhost, remote_python, pkgdir, real_create=do_sync) # rsyncing rsynced = {} - rsync_progress = ReportRsyncProgress(reporter) if do_sync: - #rsync = HostRSync(rsync_roots, callback=rsync_progress.callback) rsync = HostRSync(rsync_roots) - for num, host, gw, remoterootpath in hosts: - if (host, remoterootpath) in rsynced or (host == 'localhost' \ - and optimise_localhost): - key = host + str(num) - reporter(report.HostReady(host, key)) + for host in hosts: + #for num, host, gw, remoterootpath in hosts: + remoterootpath = host.relpath + if (host, remoterootpath) in rsynced or\ + (host.hostname == 'localhost' and optimise_localhost): + reporter(report.HostReady(host)) continue - rsynced[(host, remoterootpath)] = True - def done(host=host, num=num): - key = host + str(num) - reporter(report.HostReady(host, key)) - reporter(report.HostRSyncing(host, remoterootpath)) + rsynced[(host.hostname, host.relpath)] = True + def done(host=host): + reporter(report.HostReady(host)) + reporter(report.HostRSyncing(host)) if do_sync: - rsync.add_target(gw, remoterootpath, done) + rsync.add_target(host.gw, remoterootpath, done) if not do_sync: return # for testing only - rsync.send(pkgdir.dirpath()) # hosts ready @@ -122,9 +126,9 @@ def setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict): nodes = [] - for num, host, gw, remoterootpath in hosts: - ch = setup_slave(gw, os.path.join(remoterootpath, pkgdir.basename), - remote_options) + for host in hosts: + ch = setup_slave(host.gw, os.path.join(host.relpath,\ + pkgdir.basename), remote_options) nodes.append(MasterNode(ch, reporter, done_dict)) return nodes @@ -153,16 +157,3 @@ except: pass channel.gateway.exit() - -##def bin_rsync(sources, sshaddress, destpath): -## _rsync = py.path.local.sysfind("rsync") -## assert destpath -## args = ["-az", "--delete-excluded", -## "--delete", "--exclude=.svn/", "--exclude=*.pyc", '--exclude=*~'] -## if isinstance(sources, list): -## args += sources -## else: -## args.append(str(sources) + "/") -## args.append(sshaddress + ":" + str(destpath)) -## print '*', 'rsync', ' '.join(args) -## _rsync.sysexec(*args) Modified: py/dist/py/test/rsession/master.py ============================================================================== --- py/dist/py/test/rsession/master.py (original) +++ py/dist/py/test/rsession/master.py Tue Jan 16 13:19:56 2007 @@ -11,7 +11,6 @@ self.reporter = reporter def callback(outcome): - #import pdb;pdb.set_trace() item = self.pending.pop() if not item in done_dict: self.receive_result(outcome, item) Modified: py/dist/py/test/rsession/report.py ============================================================================== --- py/dist/py/test/rsession/report.py (original) +++ py/dist/py/test/rsession/report.py Tue Jan 16 13:19:56 2007 @@ -70,14 +70,14 @@ pass class HostRSyncing(ReportEvent): - def __init__(self, hostname, remoterootpath): - self.hostname = hostname - self.remoterootpath = remoterootpath + def __init__(self, host): + self.hostname = host.hostname + self.remoterootpath = host.relpath class HostReady(ReportEvent): - def __init__(self, hostname, hostid): - self.hostname = hostname - self.hostid = hostid + def __init__(self, host): + self.hostname = host.hostname + self.hostid = host.hostid class TestStarted(ReportEvent): def __init__(self, hosts): @@ -118,7 +118,7 @@ self.item = item self.outcome = outcome -class RsyncProgress(ReportEvent): - def __init__(self, hostid, progress): - self.progress = progress +class PongReceived(ReportEvent): + def __init__(self, hostid, result): self.hostid = hostid + self.result = result Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Tue Jan 16 13:19:56 2007 @@ -38,16 +38,16 @@ 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 + #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 def report_unknown(self, what): if self.config.option.verbose: @@ -72,7 +72,8 @@ print "%10s: READY" % item.hostname[:10] def report_TestStarted(self, item): - txt = " Test started, hosts: %s " % ", ".join(item.hosts) + hostnames = [host.hostname for host in item.hosts] + txt = " Test started, hosts: %s " % ", ".join(hostnames) self.hosts_to_rsync = len(item.hosts) self.out.sep("=", txt) self.timestart = item.timestart Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Tue Jan 16 13:19:56 2007 @@ -11,7 +11,7 @@ from py.__.test.rsession import report from py.__.test.rsession.master import \ setup_slave, MasterNode, dispatch_loop, itemgen, randomgen -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts +from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner, RunnerPolicy @@ -172,17 +172,14 @@ return new_reporter, checkfun def parse_directories(sshhosts): - # dictionary containing directories for hosts - # XXX: should be class with some info like key, etc. in future directories = {} - for num, host in enumerate(sshhosts): - m = re.match("^(.*?):(.*)$", host) + for host in sshhosts: + m = re.match("^(.*?):(.*)$", host.hostname) if m: - directories[(num, m.group(1))] = m.group(2) - sshhosts[num] = m.group(1) + host.hostname = m.group(1) + host.relpath = m.group(2) else: - directories[(num, host)] = "pytestcache" - return directories + host.relpath = "pytestcache-%s" % host.hostname class RSession(AbstractSession): """ Remote version of session @@ -193,7 +190,7 @@ args = [py.path.local()] session_options.bind_config(self.config) - sshhosts, directories, remotepython, rsync_roots = self.read_distributed_config() + sshhosts, remotepython, rsync_roots = self.read_distributed_config() reporter, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) reporter, checkfun = self.wrap_reporter(reporter) @@ -202,7 +199,7 @@ pkgdir = self.getpkgdir(args[0]) done_dict = {} - nodes = init_hosts(reporter, sshhosts, directories, pkgdir, + nodes = init_hosts(reporter, sshhosts, pkgdir, rsync_roots, remotepython, remote_options=remote_options.d, optimise_localhost=self.optimise_localhost, done_dict=done_dict) reporter(report.RsyncFinished()) @@ -222,13 +219,14 @@ rsync_roots = self.config.getinitialvalue("distrsync_roots") except: rsync_roots = None # all files and directories in the pkgdir - sshhosts = self.config.getinitialvalue("disthosts") - directories = parse_directories(sshhosts) + sshhosts = [HostInfo(i) for i in + self.config.getinitialvalue("disthosts")] + parse_directories(sshhosts) try: remotepython = self.config.getinitialvalue("dist_remotepython") except: remotepython = None - return sshhosts, directories, remotepython, rsync_roots + return sshhosts, remotepython, rsync_roots def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) @@ -270,7 +268,7 @@ reporter(report.RsyncFinished()) if runner is None: - runner = self.init_runner(pkgdir) + runner = self.init_runner() keyword = self.config.option.keyword @@ -283,30 +281,28 @@ if not self.config.option.nomagic: py.magic.revoke(assertion=1) - self.write_docs(pkgdir) + self.write_docs() return retval - def write_docs(self, pkgdir): + def write_docs(self): if self.config.option.apigen: from py.__.apigen.tracer.docstorage import DocStorageAccessor apigen = py.path.local(self.config.option.apigen).pyimport() + pkgpath = py.path.local(py.__file__).dirpath() try: - apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) + apigen.build(pkgpath, DocStorageAccessor(self.docstorage)) except (ValueError, AttributeError): raise NotImplementedError("Provided script does not seem " "to contain build function") - def init_runner(self, pkgdir): + def init_runner(self): if self.config.option.apigen: from py.__.apigen.tracer.tracer import Tracer, DocStorage module = py try: apigen = py.path.local(self.config.option.apigen).pyimport() - items = apigen.get_documentable_items(pkgdir) - if isinstance(items, dict): - self.docstorage = DocStorage().from_dict(items) - else: - self.docstorage = DocStorage().from_pkg(items) + self.docstorage = DocStorage().from_dict( + apigen.get_documentable_items()) except ImportError: raise ImportError("Provided script cannot be imported") except (ValueError, AttributeError): @@ -316,4 +312,3 @@ return apigen_runner else: return RunnerPolicy[session_options.runner_policy] - Modified: py/dist/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/dist/py/test/rsession/testing/test_reporter.py (original) +++ py/dist/py/test/rsession/testing/test_reporter.py Tue Jan 16 13:19:56 2007 @@ -1,7 +1,5 @@ -""" reporter tests. This is crippled test, because we just -test if it *works*, not if the output produced is what we really like - +""" reporter tests. XXX there are a few disabled reporting tests because they test for exact formatting as far as i can see. @@ -15,6 +13,7 @@ instead of having to know the order in which things are printed etc. + """ import py, os @@ -23,13 +22,12 @@ from py.__.test.rsession import report from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession.testing.test_slave import funcpass_spec, mod_spec +from py.__.test.rsession.hostmanage import HostInfo from py.__.test.rsession.box import Box #from py.__.test. import sys from StringIO import StringIO - - class AbstractTestReporter(object): def setup_class(cls): cls.pkgdir = py.path.local(py.__file__).dirpath() @@ -136,8 +134,9 @@ def boxfun(): config, args = py.test.Config.parse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) - r = self.reporter(config, ["localhost"]) - r.report(report.TestStarted(['localhost'])) + host = HostInfo('localhost') + r = self.reporter(config, [host]) + r.report(report.TestStarted([host])) r.report(report.RsyncFinished()) list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) r.report(report.TestFinished()) @@ -156,12 +155,12 @@ stdoutcopy = sys.stdout sys.stdout = s config, args = py.test.Config.parse([str(tmpdir)]) - hosts = ["host1", "host2", "host3"] + hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) r.report(report.TestStarted(hosts)) - r.report(report.HostReady("host1", "host1")) - r.report(report.HostReady("host2", "host2")) - r.report(report.HostReady("host3", "host3")) + r.report(report.HostReady(hosts[0])) + r.report(report.HostReady(hosts[1])) + r.report(report.HostReady(hosts[2])) sys.stdout = stdoutcopy expected = """================= Test started, hosts: host1, host2, host3 ================== host1: READY (still 2 to go) @@ -173,9 +172,11 @@ reporter = LocalReporter def test_report_received_item_outcome(self): + py.test.skip("XXX rewrite test to not rely on exact formatting") assert self.report_received_item_outcome() == 'FsF.' def test_module(self): + py.test.skip("XXX rewrite test to not rely on exact formatting") assert self._test_module().endswith("test_slave.py[9] FsF."),\ self._test_module() @@ -205,7 +206,6 @@ assert val == expected def test_module(self): - py.test.skip("XXX rewrite test to not rely on exact formatting") val = self._test_module() print val expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Tue Jan 16 13:19:56 2007 @@ -5,8 +5,9 @@ import py from py.__.test.rsession import report from py.__.test.rsession.rsession import RSession, parse_directories,\ - session_options, remote_options -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts + session_options, remote_options, parse_directories +from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\ + HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ funcoptioncustom_spec, funcoption_spec @@ -16,8 +17,8 @@ def test_setup_non_existing_hosts(): setup_events = [] - hosts = ["alskdjalsdkjasldkajlsd"] - cmd = "init_hosts(setup_events.append, hosts, 'pytesttest', pkgdir)" + hosts = [HostInfo("alskdjalsdkjasldkajlsd")] + cmd = "init_hosts(setup_events.append, hosts, pkgdir)" py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) #assert setup_events @@ -70,45 +71,6 @@ assert str(events[1][0].value) == "Reason" class TestRSessionRemote: - #def setup_class(cls): - # from py.__.test.rsession.conftest import option - # if not option.disthosts: - # py.test.skip("no test distribution ssh hosts specified") - # cls.hosts = option.disthosts.split(",") - -## def test_rsync_does_what_it_should(self): -## host = self.hosts[0] -## gw = py.execnet.SshGateway(host) -## channel = gw.remote_exec(""" -## import tempfile -## tmp = tempfile.mkdtemp() -## try: -## channel.send(tmp) -## channel.receive() # sync -## import os -## p = os.path.join(tmp, "a") -## assert os.path.exists(p) -## p2 = os.path.join(p, "__init__.py") -## assert os.path.exists(p2) -## p3 = os.path.join(p, "x.pyc") -## assert not os.path.exists(p3) -## p4 = os.path.join(p, "__init__.pyc") -## assert not os.path.exists(p4) -## channel.send("ok") -## finally: -## import shutil -## shutil.rmtree(tmp) -## """) -## tmpdir = py.test.ensuretemp("rsynctest") -## tmpdir.ensure("a", "__init__.py") -## tmpdir.ensure("a", "no.pyc") -## tmpdir.ensure("a", "__init__.pyc") -## remote_tmpdir = channel.receive() -## bin_rsync(tmpdir, host, remote_tmpdir) -## channel.send(None) -## res = channel.receive() -## assert res == "ok" - def test_example_distribution_minus_x(self): tmpdir = py.test.ensuretemp("example_distribution_minus_x") tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" @@ -187,13 +149,14 @@ assert tb[0].source.find("execute") != -1 def test_setup_teardown_ssh(self): - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) setup_events = [] teardown_events = [] config, args = py.test.Config.parse([]) session_options.bind_config(config) - nodes = init_hosts(setup_events.append, hosts, 'pytesttest', pkgdir, + nodes = init_hosts(setup_events.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -214,12 +177,13 @@ assert len(teardown_wait_ends) == len(hosts) def test_setup_teardown_run_ssh(self): - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) allevents = [] config, args = py.test.Config.parse([]) session_options.bind_config(config) - nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, + nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) from py.__.test.rsession.testing.test_executor \ @@ -257,12 +221,13 @@ """ Tests options object passing master -> server """ allevents = [] - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) config, args = py.test.Config.parse([]) session_options.bind_config(config) d = remote_options.d.copy() d['custom'] = 'custom' - nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, + nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], remote_options=d, optimise_localhost=False) @@ -289,7 +254,8 @@ """ Tests if nice level behaviour is ok """ allevents = [] - hosts = ['localhost'] + hosts = [HostInfo('localhost')] + parse_directories(hosts) tmpdir = py.test.ensuretemp("nice") tmpdir.ensure("__init__.py") tmpdir.ensure("conftest.py").write("""disthosts = ['localhost']""") @@ -308,7 +274,8 @@ passevents = [x for x in testevents if x.outcome.passed] assert len(passevents) == 1 -class TestDirectories(object): +class XxxTestDirectories(object): + # need complete rewrite def test_simple_parse(self): sshhosts = ['h1', 'h2', 'h3'] dirs = parse_directories(sshhosts) @@ -328,6 +295,7 @@ class TestInithosts(object): def test_inithosts(self): + py.test.skip("need rewrite") testevents = [] hosts = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] dirs = parse_directories(hosts) From fijal at codespeak.net Tue Jan 16 13:57:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 13:57:28 +0100 (CET) Subject: [py-svn] r36802 - py/dist/py/test/rsession Message-ID: <20070116125728.773631009D@code0.codespeak.net> Author: fijal Date: Tue Jan 16 13:57:27 2007 New Revision: 36802 Modified: py/dist/py/test/rsession/rsession.py Log: Fix for test to pass (being able to override from config.option sessionoptions) Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Tue Jan 16 13:57:27 2007 @@ -54,10 +54,7 @@ def bind_config(self, config): self.config = config - # copy to remote all options - for item, val in config.option.__dict__.items(): - remote_options[item] = val - # as well as some options from us + # copy to remote session options try: ses_opt = self.config.getinitialvalue('SessionOptions').__dict__ except ValueError: @@ -68,6 +65,9 @@ except KeyError: val = self.defaults[key] remote_options[key] = val + # copy to remote all options + for item, val in config.option.__dict__.items(): + remote_options[item] = val def __repr__(self): return "" % self.config From fijal at codespeak.net Tue Jan 16 15:54:35 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 15:54:35 +0100 (CET) Subject: [py-svn] r36805 - in py/dist/py/test/rsession: . testing webdata Message-ID: <20070116145435.079ED100A4@code0.codespeak.net> Author: fijal Date: Tue Jan 16 15:54:32 2007 New Revision: 36805 Modified: py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/report.py py/dist/py/test/rsession/reporter.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_master.py py/dist/py/test/rsession/testing/test_reporter.py py/dist/py/test/rsession/webdata/source.js Log: Fix everything here and there a bit, do some cleanup in meantime. Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Tue Jan 16 15:54:32 2007 @@ -84,6 +84,7 @@ gw = py.execnet.PopenGateway(remotepython=remote_python) host.relpath = str(pkgdir.dirpath()) host.gw = gw + gw.host = host return sshosts def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None, Modified: py/dist/py/test/rsession/report.py ============================================================================== --- py/dist/py/test/rsession/report.py (original) +++ py/dist/py/test/rsession/report.py Tue Jan 16 15:54:32 2007 @@ -42,10 +42,14 @@ def __init__(self, channel, item): self.item = item self.channel = channel + if channel: + self.host = channel.gateway.host class ReceivedItemOutcome(ReportEvent): def __init__(self, channel, item, outcome): self.channel = channel + if channel: + self.host = channel.gateway.host self.item = item self.outcome = outcome @@ -71,13 +75,11 @@ class HostRSyncing(ReportEvent): def __init__(self, host): - self.hostname = host.hostname - self.remoterootpath = host.relpath + self.host = host class HostReady(ReportEvent): def __init__(self, host): - self.hostname = host.hostname - self.hostid = host.hostid + self.host = host class TestStarted(ReportEvent): def __init__(self, hosts): Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Tue Jan 16 15:54:32 2007 @@ -26,50 +26,44 @@ #self.count = 0 #self.lgt = 1000 - def get_host(self, item): - if item.channel: - return item.channel.gateway.sshaddress - # XXX: Testing purposes only - return 'localhost' - 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 + 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 def report_unknown(self, what): if self.config.option.verbose: print "Unknown report: %s" % what def report_SendItem(self, item): - address = self.get_host(item) + address = item.host.hostname if self.config.option.verbose: print "Sending %s to %s" % (item.item, address) def report_HostRSyncing(self, item): - print "%10s: RSYNC ==> %s" % (item.hostname[:10], - item.remoterootpath) + print "%10s: RSYNC ==> %s" % (item.host.hostname[:10], + item.host.relpath) def report_HostReady(self, item): self.hosts_to_rsync -= 1 if self.hosts_to_rsync: - print "%10s: READY (still %d to go)" % (item.hostname[:10], + print "%10s: READY (still %d to go)" % (item.host.hostname[:10], self.hosts_to_rsync) else: - print "%10s: READY" % item.hostname[:10] + print "%10s: READY" % item.host.hostname[:10] def report_TestStarted(self, item): hostnames = [host.hostname for host in item.hosts] @@ -113,7 +107,7 @@ self.out.sep("=", " FAILURES ") for event in self.failed_tests_outcome: if isinstance(event, report.ReceivedItemOutcome): - host = self.get_host(event) + host = event.host.hostname self.out.sep('_', "%s on %s" % (" ".join(event.item.listnames()), host)) if event.outcome.signal: @@ -229,7 +223,7 @@ # XXX: right now we do not do anything with it def report_ReceivedItemOutcome(self, event): - host = self.get_host(event) + host = event.host if event.outcome.passed: status = "PASSED " self.passed[host] += 1 @@ -242,16 +236,15 @@ self.failed[host] += 1 self.failed_tests_outcome.append(event) # we'll take care of them later - sshhost = self.get_host(event) itempath = " ".join(event.item.listnames()[1:]) - print "%10s: %s %s" %(sshhost[:10], status, itempath) + print "%10s: %s %s" %(host.hostname[:10], status, itempath) def report_Nodes(self, event): self.nodes = event.nodes class RemoteReporter(AbstractReporter): def get_item_name(self, event, colitem): - return self.get_host(event) + ":" + \ + return event.host.hostname + ":" + \ "/".join(colitem.listnames()) def report_FailedTryiter(self, event): @@ -263,6 +256,17 @@ join(event.item.listnames()))) class LocalReporter(AbstractReporter): + def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)): + self.config = config + self.pkgdir = pkgdir + self.failed_tests_outcome = [] + self.skipped_tests_outcome = [] + self.out = getout(py.std.sys.stdout) + #assert hosts == ['localhost'] + self.failed = 0 + self.skipped = 0 + self.passed = 0 + def get_item_name(self, event, colitem): return "/".join(colitem.listnames()) @@ -277,14 +281,14 @@ def report_ReceivedItemOutcome(self, event): if event.outcome.passed: - self.passed['localhost'] += 1 + self.passed += 1 self.out.write(".") elif event.outcome.skipped: self.skipped_tests_outcome.append(event) - self.skipped['localhost'] += 1 + self.skipped += 1 self.out.write("s") else: - self.failed['localhost'] += 1 + self.failed += 1 self.failed_tests_outcome.append(event) self.out.write("F") # we'll take care of them later Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Tue Jan 16 15:54:32 2007 @@ -252,7 +252,7 @@ if not args: args = [py.path.local()] - sshhosts = ['localhost'] # this is just an info to reporter + sshhosts = [HostInfo('localhost')] # this is just an info to reporter if not self.config.option.nomagic: py.magic.invoke(assertion=1) Modified: py/dist/py/test/rsession/testing/test_master.py ============================================================================== --- py/dist/py/test/rsession/testing/test_master.py (original) +++ py/dist/py/test/rsession/testing/test_master.py Tue Jan 16 15:54:32 2007 @@ -14,6 +14,7 @@ from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec from py.__.test.rsession import report from py.__.test.rsession.rsession import session_options, remote_options +from py.__.test.rsession.hostmanage import HostInfo def setup_module(mod): # bind an empty config @@ -23,9 +24,14 @@ #assert not remote_options.exitfirst mod.pkgdir = py.path.local(py.__file__).dirpath() +class DummyGateway(object): + def __init__(self): + self.host = HostInfo("localhost") + class DummyChannel(object): def __init__(self): self.sent = [] + self.gateway = DummyGateway() def setcallback(self, func): self.callback = func @@ -106,6 +112,8 @@ def open_gw(): gw = py.execnet.PopenGateway() + gw.host = HostInfo("localhost") + gw.host.gw = gw channel = setup_slave(gw, pkgdir, remote_options.d) mn = MasterNode(channel, simple_report, {}) return mn Modified: py/dist/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/dist/py/test/rsession/testing/test_reporter.py (original) +++ py/dist/py/test/rsession/testing/test_reporter.py Tue Jan 16 15:54:32 2007 @@ -16,7 +16,9 @@ """ + import py, os +py.test.skip("in progress") from py.__.test.rsession.rsession import LocalReporter, AbstractSession,\ RemoteReporter from py.__.test.rsession import report Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From fijal at codespeak.net Tue Jan 16 16:10:02 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 16:10:02 +0100 (CET) Subject: [py-svn] r36810 - in py/dist/py/test/rsession: . webdata Message-ID: <20070116151002.400F1100A3@code0.codespeak.net> Author: fijal Date: Tue Jan 16 16:09:59 2007 New Revision: 36810 Modified: py/dist/py/test/rsession/web.py py/dist/py/test/rsession/webdata/source.js Log: Fix the web.py to really adhere to new interface. Now we must perform some tricks to helpers, cause option list have grown a bit. Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Tue Jan 16 16:09:59 2007 @@ -153,23 +153,26 @@ def show_hosts(self): self.start_event.wait() - return self.hosts - show_hosts = described(retval={str:str}, args=[ - _callable([{str:str}])])(show_hosts) + to_send = {} + for host in self.hosts: + to_send[host.hostid] = host.hostname + return to_send + show_hosts = described(retval={str:str}, args=[('callback', + _callable([{str:str}]))])(show_hosts) def show_skip(self, item_name="aa"): return {'item_name': item_name, 'reason': self.skip_reasons[item_name]} - show_skip = described(retval={str:str}, args=[str, - _callable([{str:str}])])(show_skip) + show_skip = described(retval={str:str}, args=[('item_name',str),('callback', + _callable([{str:str}]))])(show_skip) def show_fail(self, item_name="aa"): return {'item_name':item_name, 'traceback':str(self.fail_reasons[item_name]), 'stdout':self.stdout[item_name], 'stderr':self.stderr[item_name]} - show_fail = described(retval={str:str}, args=[str, - _callable([{str:str}])])(show_fail) + show_fail = described(retval={str:str}, args=[('item_name',str),('callback', + _callable([{str:str}]))])(show_fail) _sessids = None _sesslock = py.std.thread.allocate_lock() @@ -188,8 +191,8 @@ finally: self._sesslock.release() return sessid - show_sessid = described(retval=str, args=[ - _callable([str])])(show_sessid) + show_sessid = described(retval=str, args=[('callback', + _callable([str]))])(show_sessid) def failed(self, **kwargs): if not 'sessid' in kwargs: @@ -210,7 +213,7 @@ retval = retlist return retval show_all_statuses = described(retval=[{str:str}],args= - [str, _callable([[{str:str}]])])(show_all_statuses) + [('sessid',str), ('callback',_callable([[{str:str}]]))])(show_all_statuses) def show_status_change(self, sessid): event = self.pending_events.get(sessid) @@ -240,7 +243,7 @@ self.stdout[fullitemname] = outcome.stdout self.stderr[fullitemname] = outcome.stderr if event.channel: - args['hostkey'] = event.channel.gateway.hostid + args['hostkey'] = event.channel.gateway.host.hostid else: args['hostkey'] = '' elif isinstance(event, report.ItemStart): @@ -252,10 +255,10 @@ args['skips'] = str(len(self.skip_reasons)) elif isinstance(event, report.SendItem): args = add_item(event) - args['hostkey'] = event.channel.gateway.hostid + args['hostkey'] = event.channel.gateway.host.hostid elif isinstance(event, report.HostReady): - self.ready_hosts[event.hostid] = True - args = {'hostname' : event.hostname, 'hostkey' : event.hostid} + self.ready_hosts[event.host] = True + args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid} elif isinstance(event, report.FailedTryiter): args = add_item(event) elif isinstance(event, report.SkippedTryiter): @@ -300,10 +303,9 @@ def report_TestStarted(self, event): self.hosts = {} self.ready_hosts = {} - for num, host in enumerate(event.hosts): - host_str = host + str(num) - self.hosts[host_str] = host - self.ready_hosts[host_str] = False + for host in event.hosts: + self.hosts[host] = host + self.ready_hosts[host] = False self.start_event.set() self.pending_events.put(event) @@ -335,6 +337,7 @@ def do_GET(self): path = self.path + print "Called %s" % path if path.endswith("/"): path = path[:-1] if path.startswith("/"): Modified: py/dist/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From fijal at codespeak.net Tue Jan 16 16:25:00 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 16:25:00 +0100 (CET) Subject: [py-svn] r36813 - in py/dist/py/test/rsession: . testing Message-ID: <20070116152500.D22401009C@code0.codespeak.net> Author: fijal Date: Tue Jan 16 16:24:59 2007 New Revision: 36813 Modified: py/dist/py/test/rsession/rest.py py/dist/py/test/rsession/testing/test_reporter.py py/dist/py/test/rsession/testing/test_rest.py Log: Fixes of the rest reporter. Modified: py/dist/py/test/rsession/rest.py ============================================================================== --- py/dist/py/test/rsession/rest.py (original) +++ py/dist/py/test/rsession/rest.py Tue Jan 16 16:24:59 2007 @@ -27,7 +27,7 @@ self.add_rest(Paragraph("Unknown report: %s" % what)) def report_SendItem(self, item): - address = self.get_host(item) + address = item.channel.gateway.host.hostname if self.config.option.verbose: self.add_rest(Paragraph('sending item %s to %s' % (item.item, address))) @@ -86,7 +86,7 @@ self.out.write(self.rest.render_links()) def report_ReceivedItemOutcome(self, event): - host = self.get_host(event) + host = event.channel.gateway.host if event.outcome.passed: status = [Strong("PASSED")] self.passed[host] += 1 @@ -103,7 +103,8 @@ # we'll take care of them later itempath = self.get_path_from_item(event.item) status.append(Text(itempath)) - self.add_rest(ListItem(Text("%10s:" % (host[:10],)), *status)) + hostname = host.hostname + self.add_rest(ListItem(Text("%10s:" % (hostname[:10],)), *status)) def skips(self): # XXX hrmph, copied code @@ -127,6 +128,9 @@ for item in items: self.add_rest(ListItem('%s: %s' % (item, text))) + def get_host(self, event): + return event.channel.gateway.host + def failures(self): self.traceback_num = 0 tbstyle = self.config.option.tbstyle @@ -145,7 +149,7 @@ t.add(Link(itempath, link)) else: t.add(Text(itempath)) - t.add(Text('on %s' % (host,))) + t.add(Text('on %s' % (host.hostname,))) self.add_rest(t) if event.outcome.signal: self.repr_signal(event.item, event.outcome) Modified: py/dist/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/dist/py/test/rsession/testing/test_reporter.py (original) +++ py/dist/py/test/rsession/testing/test_reporter.py Tue Jan 16 16:24:59 2007 @@ -18,7 +18,7 @@ import py, os -py.test.skip("in progress") +#py.test.skip("in progress") from py.__.test.rsession.rsession import LocalReporter, AbstractSession,\ RemoteReporter from py.__.test.rsession import report @@ -30,6 +30,14 @@ import sys from StringIO import StringIO +class DummyGateway(object): + def __init__(self, host): + self.host = host + +class DummyChannel(object): + def __init__(self, host): + self.gateway = DummyGateway(host) + class AbstractTestReporter(object): def setup_class(cls): cls.pkgdir = py.path.local(py.__file__).dirpath() @@ -60,9 +68,11 @@ outcomes = self.prepare_outcomes() def boxfun(config, item, outcomes): - r = self.reporter(config, ["localhost"]) + hosts = [HostInfo("localhost")] + r = self.reporter(config, hosts) + ch = DummyChannel(hosts[0]) for outcome in outcomes: - r.report(report.ReceivedItemOutcome(None, item, outcome)) + r.report(report.ReceivedItemOutcome(ch, item, outcome)) s = StringIO() stdoutcopy = sys.stdout @@ -81,11 +91,13 @@ outcomes = self.prepare_outcomes() def boxfun(pkgdir, config, item, funcitem, outcomes): - r = self.reporter(config, ["localhost"]) + hosts = [HostInfo('localhost')] + r = self.reporter(config, hosts) #r.pkgdir = pkdgir r.report(report.ItemStart(item)) + ch = DummyChannel(hosts[0]) for outcome in outcomes: - r.report(report.ReceivedItemOutcome(None, funcitem, outcome)) + r.report(report.ReceivedItemOutcome(ch, funcitem, outcome)) s = StringIO() stdoutcopy = sys.stdout @@ -114,7 +126,8 @@ def boxfun(): config, args = py.test.Config.parse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) - r = self.reporter(config, ["localhost"]) + hosts = [HostInfo('localhost')] + r = self.reporter(config, hosts) list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) #b = Box(boxfun) @@ -174,16 +187,16 @@ reporter = LocalReporter def test_report_received_item_outcome(self): - py.test.skip("XXX rewrite test to not rely on exact formatting") + #py.test.skip("XXX rewrite test to not rely on exact formatting") assert self.report_received_item_outcome() == 'FsF.' def test_module(self): - py.test.skip("XXX rewrite test to not rely on exact formatting") + #py.test.skip("XXX rewrite test to not rely on exact formatting") assert self._test_module().endswith("test_slave.py[9] FsF."),\ self._test_module() def test_full_module(self): - py.test.skip("XXX rewrite test to not rely on exact formatting") + #py.test.skip("XXX rewrite test to not rely on exact formatting") received = self._test_full_module() expected = """ repmod/test_one.py[1] @@ -198,7 +211,7 @@ self._test_still_to_go() def test_report_received_item_outcome(self): - py.test.skip("XXX rewrite test to not rely on exact formatting") + #py.test.skip("XXX rewrite test to not rely on exact formatting") val = self.report_received_item_outcome() expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass localhost: SKIPPED py test rsession testing test_slave.py funcpass @@ -218,7 +231,7 @@ assert val == expected def test_full_module(self): - py.test.skip("XXX rewrite test to not rely on exact formatting") + #py.test.skip("XXX rewrite test to not rely on exact formatting") val = self._test_full_module() assert val == 'FAILED TO LOAD MODULE: repmod/test_three.py\n'\ '\nSkipped (reason) repmod/test_two.py\n\n' Modified: py/dist/py/test/rsession/testing/test_rest.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rest.py (original) +++ py/dist/py/test/rsession/testing/test_rest.py Tue Jan 16 16:24:59 2007 @@ -3,10 +3,12 @@ """ import py -from py.__.test.rsession.testing.test_reporter import AbstractTestReporter +from py.__.test.rsession.testing.test_reporter import AbstractTestReporter,\ + DummyChannel from py.__.test.rsession import report from py.__.test.rsession.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * +from py.__.test.rsession.hostmanage import HostInfo class RestTestReporter(RestReporter): def __init__(self, *args, **kwargs): @@ -28,8 +30,10 @@ config, args = py.test.Config.parse(["some_sub"]) config.option.verbose = False self.config = config + hosts = [HostInfo('localhost')] + method.im_func.func_globals['ch'] = DummyChannel(hosts[0]) method.im_func.func_globals['reporter'] = r = RestReporter(config, - ['localhost']) + hosts) method.im_func.func_globals['stdout'] = s = py.std.StringIO.StringIO() r.out = s # XXX will need to become a real reporter some time perhaps? r.linkwriter = NoLinkWriter() @@ -41,7 +45,7 @@ self.config.option.verbose = False def test_report_SendItem(self): - item = Container(item='foo/bar.py', channel=False) + item = Container(item='foo/bar.py', channel=ch) reporter.report_SendItem(item) assert stdout.getvalue() == '' stdout.seek(0) @@ -104,7 +108,7 @@ def test_ReceivedItemOutcome_PASSED(self): outcome = Container(passed=True, excinfo=False) item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = Container(channel=False, outcome=outcome, item=item) + event = Container(channel=ch, outcome=outcome, item=item) reporter.report_ReceivedItemOutcome(event) assert stdout.getvalue() == ('* localhost\\: **PASSED** ' 'foo.py/bar()/baz\n\n') @@ -112,7 +116,7 @@ def test_ReceivedItemOutcome_SKIPPED(self): outcome = Container(passed=False, skipped=True, excinfo=False) item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = Container(channel=False, outcome=outcome, item=item) + event = Container(channel=ch, outcome=outcome, item=item) reporter.report_ReceivedItemOutcome(event) assert stdout.getvalue() == ('* localhost\\: **SKIPPED** ' 'foo.py/bar()/baz\n\n') @@ -120,7 +124,7 @@ def test_ReceivedItemOutcome_FAILED(self): outcome = Container(passed=False, skipped=False) item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = Container(channel=False, outcome=outcome, item=item) + event = Container(channel=ch, outcome=outcome, item=item) reporter.report_ReceivedItemOutcome(event) assert stdout.getvalue() == """\ * localhost\: **FAILED** `traceback0`_ foo.py/bar()/baz @@ -179,7 +183,7 @@ parent=parent, fspath=py.path.local('.'), ), - channel=None, + channel=ch, ), ] reporter.config.option.tbstyle = 'no' From fijal at codespeak.net Tue Jan 16 16:37:43 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 16:37:43 +0100 (CET) Subject: [py-svn] r36814 - in py/dist/py/test/rsession: . testing Message-ID: <20070116153743.41464100AB@code0.codespeak.net> Author: fijal Date: Tue Jan 16 16:37:41 2007 New Revision: 36814 Modified: py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/testing/test_rsession.py Log: Unskip the test and a fix related to this skip. Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Tue Jan 16 16:37:41 2007 @@ -57,6 +57,9 @@ else: return base in self.rsync_roots +class DummyGateway(object): + pass + def prepare_gateway(sshosts, optimise_localhost, remote_python, pkgdir, real_create=True): hosts = [] @@ -76,7 +79,7 @@ gw = py.execnet.SshGateway(host.hostname, remotepython=remote_python) else: - gw = None + gw = DummyGateway() else: if remote_python is None: gw = py.execnet.PopenGateway() @@ -108,7 +111,7 @@ for host in hosts: #for num, host, gw, remoterootpath in hosts: remoterootpath = host.relpath - if (host, remoterootpath) in rsynced or\ + if (host.hostname, remoterootpath) in rsynced or\ (host.hostname == 'localhost' and optimise_localhost): reporter(report.HostReady(host)) continue Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Tue Jan 16 16:37:41 2007 @@ -295,19 +295,19 @@ class TestInithosts(object): def test_inithosts(self): - py.test.skip("need rewrite") testevents = [] - hosts = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] - dirs = parse_directories(hosts) - init_hosts(testevents.append, hosts, dirs, pkgdir, do_sync=False) + hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] + hosts = [HostInfo(i) for i in hostnames] + parse_directories(hosts) + init_hosts(testevents.append, hosts, pkgdir, do_sync=False) events = [i for i in testevents if isinstance(i, report.HostRSyncing)] assert len(events) == 4 - assert events[0].hostname == 'h1' - assert events[0].remoterootpath == '/tmp-h1' - assert events[1].hostname == 'h1' - assert events[1].remoterootpath == '/other-h1' - assert events[2].hostname == 'h2' - assert events[2].remoterootpath == 'pytestcache-h2' - assert events[3].hostname == 'h2' - assert events[3].remoterootpath == 'home-h2' + assert events[0].host.hostname == 'h1' + assert events[0].host.relpath == '/tmp' + assert events[1].host.hostname == 'h1' + assert events[1].host.relpath == '/other' + assert events[2].host.hostname == 'h2' + assert events[2].host.relpath == 'pytestcache-h2' + assert events[3].host.hostname == 'h2' + assert events[3].host.relpath == 'home' From fijal at codespeak.net Tue Jan 16 16:42:24 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 16:42:24 +0100 (CET) Subject: [py-svn] r36816 - py/dist/py/test/rsession/testing Message-ID: <20070116154224.2A990100B7@code0.codespeak.net> Author: fijal Date: Tue Jan 16 16:42:22 2007 New Revision: 36816 Modified: py/dist/py/test/rsession/testing/test_rsession.py Log: Move remote part of rsync to separate file. Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Tue Jan 16 16:42:22 2007 @@ -275,12 +275,10 @@ assert len(passevents) == 1 class XxxTestDirectories(object): - # need complete rewrite + # need complete rewrite, and unsure if it makes sense at all def test_simple_parse(self): - sshhosts = ['h1', 'h2', 'h3'] - dirs = parse_directories(sshhosts) - assert len(set(dirs.values())) == 1 - assert sorted(dirs.keys()) == [(0, 'h1'), (1, 'h2'), (2, 'h3')] + sshhosts = [HostInfo(i) for i in ['h1', 'h2', 'h3']] + parse_directories(sshhosts) def test_sophisticated_parse(self): sshhosts = ['a at h1:/tmp', 'h2:tmp', 'h3'] From fijal at codespeak.net Tue Jan 16 16:53:23 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 16:53:23 +0100 (CET) Subject: [py-svn] r36817 - py/dist/py/test/rsession Message-ID: <20070116155323.03F4310097@code0.codespeak.net> Author: fijal Date: Tue Jan 16 16:53:22 2007 New Revision: 36817 Modified: py/dist/py/test/rsession/web.py Log: Removed debug print. Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Tue Jan 16 16:53:22 2007 @@ -337,7 +337,6 @@ def do_GET(self): path = self.path - print "Called %s" % path if path.endswith("/"): path = path[:-1] if path.startswith("/"): From fijal at codespeak.net Tue Jan 16 16:55:47 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 16:55:47 +0100 (CET) Subject: [py-svn] r36818 - py/dist/py/test/rsession Message-ID: <20070116155547.767D010097@code0.codespeak.net> Author: fijal Date: Tue Jan 16 16:55:45 2007 New Revision: 36818 Modified: py/dist/py/test/rsession/box.py Log: Remove dead code Modified: py/dist/py/test/rsession/box.py ============================================================================== --- py/dist/py/test/rsession/box.py (original) +++ py/dist/py/test/rsession/box.py Tue Jan 16 16:55:45 2007 @@ -15,26 +15,6 @@ import tempfile from StringIO import StringIO -class SimpleBox(object): - def __init__(self, fun, args = [], kwargs = {}): - self.fun = fun - self.args = args - self.info = info - self.kwargs = kwargs - - def run(self): - self.stdout = StringIO() - sys.stdout = self.stdout - self.stderr = StringIO() - sys.stderr = self.stderr - self.retval = self.fun(*self.args, **self.kwargs) - self.stdoutrepr = self.stdout.getvalue() - self.stderrrepr = self.stderr.getvalue() - self.signal = 0 - self.exitstat = 0 - return 123 - - class FileBox(object): count = 0 @@ -123,59 +103,4 @@ self.stderrrepr = self.PYTESTSTDERR.read() return self.stdoutrepr, self.stderrrepr -def screen_run(): - # XXX: we *do* bootstrapping once again here - # which sucks a lot - # XXX: We need to make some kind of *blocking* fifo, cause - # we have really no idea when we'll arrive - import os - import py - - try: - pass - except: - pass - - import pdb;pdb.set_trace() - -class ScreenBox(FileBox): - def run(self): - tempdir = py.test.ensuretemp("box") - self.tempdir = tempdir - self.PYTESTRETVAL = tempdir.join('retval') - self.PYTESTINPUT = tempdir.join('input') - #self.PYTESTINPUT.write(marshal.dumps({'args':self.args, \ - # 'kwargs':self.kwargs, 'fun':self.fun})) - - pid = os.fork() - if pid: - self.parent() - else: - os.chdir(str(py.path.local(py.__file__).dirpath().dirpath())) - screen = str(py.path.local.sysfind("screen")) - #os.execv(screen, ["python"]) - os.execv(screen, ["screen", "-d", "-m", "-L", "--", "python", \ - "-c", "from py.__.test.rsession import box\nbox.screen_run()"]) - return pid - - def parent(self): - pid, exitstat = os.wait() - self.signal = exitstat & 0x7f - self.exitstat = exitstat & 0xff00 - import pdb;pdb.set_trace() - #if not exitstat: - # retval = self.PYTESTRETVAL.open() - # try: - # retval_data = retval.read() - # finally: - # retval.close() - # self.retval = marshal.loads(retval_data) - #else: - self.retval = None - - self.stdoutrepr = py.path.local(__file__).dirpath().join("screenlog.0").read() - self.stderrrepr = "" - return self.stdoutrepr, self.stderrrepr - -RealBox = FileBox Box = FileBox From fijal at codespeak.net Tue Jan 16 16:57:02 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 16:57:02 +0100 (CET) Subject: [py-svn] r36819 - in py/dist/py/test/rsession: . testing Message-ID: <20070116155702.2998E1009C@code0.codespeak.net> Author: fijal Date: Tue Jan 16 16:57:01 2007 New Revision: 36819 Modified: py/dist/py/test/rsession/conftest.py py/dist/py/test/rsession/testing/test_boxing.py Log: Remove dead code, turn on option, which should be on by default. Modified: py/dist/py/test/rsession/conftest.py ============================================================================== --- py/dist/py/test/rsession/conftest.py (original) +++ py/dist/py/test/rsession/conftest.py Tue Jan 16 16:57:01 2007 @@ -1,21 +1,10 @@ import py Option = py.test.Config.Option -#defaultwait = 100.0 - option = py.test.Config.addoptions("boxing test options", - Option('', '--skip-kill-test', action='store_true', dest='skip_kill_test', + Option('', '--skip-kill-test', action='store_true', default=False, + dest='skip_kill_test', help='skip a certain test that checks for os.kill results, this ' 'should be used when kill() is not allowed for the current ' 'user'), ) - -#option = py.test.Config.addoptions("distributed testing options", -# Option('-D', '--disthosts', -# action="store", dest="disthosts", default=None, -# help="comma separated list of testhosts"), -# Option('', '--waittime', -# action="store", dest="waittime", default=defaultwait, -# help="How long (in seconds) to wait for hanging nodes" -# " (default=%s sec)" % defaultwait), -# ) Modified: py/dist/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/dist/py/test/rsession/testing/test_boxing.py (original) +++ py/dist/py/test/rsession/testing/test_boxing.py Tue Jan 16 16:57:01 2007 @@ -7,7 +7,7 @@ if sys.platform == 'win32': py.test.skip("rsession is unsupported on Windows.") -from py.__.test.rsession.box import Box, RealBox, ScreenBox +from py.__.test.rsession.box import Box from py.__.test.rsession.testing import example2 from py.__.test.rsession.conftest import option @@ -28,7 +28,7 @@ assert b.retval == 1 def test_boxing_on_fds(): - b = RealBox(example2.boxf2) + b = Box(example2.boxf2) b.run() assert b.stdoutrepr == "someout" assert b.stderrrepr == "someerr" @@ -37,13 +37,13 @@ assert b.retval == 2 def test_boxing_signal(): - b = RealBox(example2.boxseg) + b = Box(example2.boxseg) b.run() assert b.signal == 11 assert b.retval is None def test_boxing_huge_data(): - b = RealBox(example2.boxhuge) + b = Box(example2.boxhuge) b.run() assert b.stdoutrepr assert b.exitstat == 0 @@ -53,7 +53,7 @@ def test_box_seq(): # we run many boxes with huge data, just one after another for i in xrange(100): - b = RealBox(example2.boxhuge) + b = Box(example2.boxhuge) b.run() assert b.stdoutrepr assert b.exitstat == 0 @@ -62,13 +62,13 @@ def test_box_in_a_box(): def boxfun(): - b = RealBox(example2.boxf2) + b = Box(example2.boxf2) b.run() print b.stdoutrepr print >>sys.stderr, b.stderrrepr return b.retval - b = RealBox(boxfun) + b = Box(boxfun) b.run() assert b.stdoutrepr == "someout\n" assert b.stderrrepr == "someerr\n" @@ -87,7 +87,7 @@ def box_fun(): time.sleep(10) # we don't want to last forever here - b = RealBox(box_fun) + b = Box(box_fun) par, pid = b.run(continuation=True) os.kill(pid, 15) par(pid) From fijal at codespeak.net Tue Jan 16 17:06:18 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 17:06:18 +0100 (CET) Subject: [py-svn] r36822 - py/dist/py/test/rsession Message-ID: <20070116160618.A4B92100AA@code0.codespeak.net> Author: fijal Date: Tue Jan 16 17:06:16 2007 New Revision: 36822 Modified: py/dist/py/test/rsession/executor.py Log: Make lines shorter Modified: py/dist/py/test/rsession/executor.py ============================================================================== --- py/dist/py/test/rsession/executor.py (original) +++ py/dist/py/test/rsession/executor.py Tue Jan 16 17:06:16 2007 @@ -39,8 +39,8 @@ ReprOutcome(outcome.make_repr()))) import pdb pdb.post_mortem(excinfo._excinfo[2]) - # XXX hmm, we probably will not like to continue from that point - # or we do? + # XXX hmm, we probably will not like to continue from that + # point raise SystemExit() outcome.stdout = "" outcome.stderr = "" @@ -59,11 +59,13 @@ pid = b.run() assert pid if b.retval is not None: - passed, setupfailure, excinfo, skipped, critical, _, _, _ = b.retval + passed, setupfailure, excinfo, skipped, critical, _, _, _\ + = b.retval return (passed, setupfailure, excinfo, skipped, critical, 0, b.stdoutrepr, b.stderrrepr) else: - return (False, False, None, False, False, b.signal, b.stdoutrepr, b.stderrrepr) + return (False, False, None, False, False, b.signal, + b.stdoutrepr, b.stderrrepr) class AsyncExecutor(RunExecutor): """ same as box executor, but instead it returns function to continue @@ -82,10 +84,12 @@ def cont(): parent(pid) if b.retval is not None: - passed, setupfailure, excinfo, skipped, critical, _, _, _ = b.retval + passed, setupfailure, excinfo, skipped,\ + critical, _, _, _ = b.retval return (passed, setupfailure, excinfo, skipped, critical, 0, b.stdoutrepr, b.stderrrepr) else: - return (False, False, None, False, False, b.signal, b.stdoutrepr, b.stderrrepr) + return (False, False, None, False, False, + b.signal, b.stdoutrepr, b.stderrrepr) return cont, pid From fijal at codespeak.net Tue Jan 16 17:06:36 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 17:06:36 +0100 (CET) Subject: [py-svn] r36823 - py/dist/py/test/rsession/testing Message-ID: <20070116160636.38FE4100AA@code0.codespeak.net> Author: fijal Date: Tue Jan 16 17:06:35 2007 New Revision: 36823 Modified: py/dist/py/test/rsession/testing/test_reporter.py Log: Adapt test_reporter to use py.io.OutErrCapture Modified: py/dist/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/dist/py/test/rsession/testing/test_reporter.py (original) +++ py/dist/py/test/rsession/testing/test_reporter.py Tue Jan 16 17:06:35 2007 @@ -74,13 +74,11 @@ for outcome in outcomes: r.report(report.ReceivedItemOutcome(ch, item, outcome)) - s = StringIO() - stdoutcopy = sys.stdout - sys.stdout = s + cap = py.io.OutErrCapture() boxfun(config, item, outcomes) - sys.stdout = stdoutcopy - - return s.getvalue() + out, err = cap.reset() + assert not err + return out def _test_module(self): config, args = py.test.Config.parse(["some_sub"]) @@ -99,14 +97,11 @@ for outcome in outcomes: r.report(report.ReceivedItemOutcome(ch, funcitem, outcome)) - s = StringIO() - stdoutcopy = sys.stdout - sys.stdout = s + cap = py.io.OutErrCapture() boxfun(self.pkgdir, config, moditem, funcitem, outcomes) - sys.stdout = stdoutcopy - - return s.getvalue() - + out, err = cap.reset() + assert not err + return out def _test_full_module(self): tmpdir = py.test.ensuretemp("repmod") @@ -129,16 +124,12 @@ hosts = [HostInfo('localhost')] r = self.reporter(config, hosts) list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) - - #b = Box(boxfun) - #b.run() - s = StringIO() - stdoutcopy = sys.stdout - sys.stdout = s + + cap = py.io.OutErrCapture() boxfun() - sys.stdout = stdoutcopy - - return s.getvalue() + out, err = cap.reset() + assert not err + return out def test_failed_to_load(self): tmpdir = py.test.ensuretemp("failedtoload") @@ -156,19 +147,16 @@ list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) r.report(report.TestFinished()) - s = StringIO() - stdoutcopy = sys.stdout - sys.stdout = s + cap = py.io.OutErrCapture() boxfun() - sys.stdout = stdoutcopy - assert s.getvalue().find("NameError: name 'sadsadsa' is not defined") != -1 + out, err = cap.reset() + assert not err + assert out.find("NameError: name 'sadsadsa' is not defined") != -1 def _test_still_to_go(self): tmpdir = py.test.ensuretemp("stilltogo") tmpdir.ensure("__init__.py") - s = StringIO() - stdoutcopy = sys.stdout - sys.stdout = s + cap = py.io.OutErrCapture() config, args = py.test.Config.parse([str(tmpdir)]) hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) @@ -176,12 +164,13 @@ r.report(report.HostReady(hosts[0])) r.report(report.HostReady(hosts[1])) r.report(report.HostReady(hosts[2])) - sys.stdout = stdoutcopy + out, err = cap.reset() + assert not err expected = """================= Test started, hosts: host1, host2, host3 ================== host1: READY (still 2 to go) host2: READY (still 1 to go) host3: READY""" - assert s.getvalue().startswith(expected) + assert out.startswith(expected) class TestLocalReporter(AbstractTestReporter): reporter = LocalReporter From fijal at codespeak.net Tue Jan 16 17:26:10 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 17:26:10 +0100 (CET) Subject: [py-svn] r36824 - py/dist/py/test/rsession Message-ID: <20070116162610.1F64A100B5@code0.codespeak.net> Author: fijal Date: Tue Jan 16 17:26:08 2007 New Revision: 36824 Modified: py/dist/py/test/rsession/reporter.py py/dist/py/test/rsession/web.py Log: This should make LSession work properly. Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Tue Jan 16 17:26:08 2007 @@ -15,16 +15,13 @@ def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)): self.config = config self.pkgdir = pkgdir + self.hosts = hosts self.failed_tests_outcome = [] self.skipped_tests_outcome = [] self.out = getout(py.std.sys.stdout) - #assert hosts == ['localhost'] 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]) - # XXX: This is for tests to work - #self.count = 0 - #self.lgt = 1000 def get_item_name(self, event, colitem): return "/".join(colitem.listnames()) @@ -107,7 +104,7 @@ self.out.sep("=", " FAILURES ") for event in self.failed_tests_outcome: if isinstance(event, report.ReceivedItemOutcome): - host = event.host.hostname + host = self.gethost(event) self.out.sep('_', "%s on %s" % (" ".join(event.item.listnames()), host)) if event.outcome.signal: @@ -118,6 +115,9 @@ self.out.sep('_', " ".join(event.item.listnames())) out = outcome.Outcome(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 @@ -256,17 +256,6 @@ join(event.item.listnames()))) class LocalReporter(AbstractReporter): - def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)): - self.config = config - self.pkgdir = pkgdir - self.failed_tests_outcome = [] - self.skipped_tests_outcome = [] - self.out = getout(py.std.sys.stdout) - #assert hosts == ['localhost'] - self.failed = 0 - self.skipped = 0 - self.passed = 0 - def get_item_name(self, event, colitem): return "/".join(colitem.listnames()) @@ -280,23 +269,18 @@ self.failed_tests_outcome.append(event) def report_ReceivedItemOutcome(self, event): + host = self.hosts[0] if event.outcome.passed: - self.passed += 1 + self.passed[host] += 1 self.out.write(".") elif event.outcome.skipped: self.skipped_tests_outcome.append(event) - self.skipped += 1 + self.skipped[host] += 1 self.out.write("s") else: - self.failed += 1 + self.failed[host] += 1 self.failed_tests_outcome.append(event) self.out.write("F") - # we'll take care of them later - #self.count += 1 - #if self.count >= self.lgt: - # self.out.write("\n") - #itempath = " ".join(event.item.listnames()[1:]) - #print "%10s: %s %s" %(sshhost[:10], status, itempath) def report_ItemStart(self, event): self.show_item(event.item) @@ -317,6 +301,9 @@ if local and name.startswith(local): name = name[len(local) + 1:] self.out.write("\n%s[%d] " % (name, lgt)) + + def gethost(self, event): + return 'localhost' def hangs(self): pass Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Tue Jan 16 17:26:08 2007 @@ -301,6 +301,7 @@ self.pending_events.put(event) def report_TestStarted(self, event): + # XXX: It overrides out self.hosts self.hosts = {} self.ready_hosts = {} for host in event.hosts: From fijal at codespeak.net Tue Jan 16 18:01:51 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 18:01:51 +0100 (CET) Subject: [py-svn] r36826 - py/branch/rsession-cleanup Message-ID: <20070116170151.51C66100C4@code0.codespeak.net> Author: fijal Date: Tue Jan 16 18:01:49 2007 New Revision: 36826 Removed: py/branch/rsession-cleanup/ Log: Delete this branch From fijal at codespeak.net Tue Jan 16 18:03:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 18:03:11 +0100 (CET) Subject: [py-svn] r36827 - py/branch/rsession-cleanup Message-ID: <20070116170311.AC695100CC@code0.codespeak.net> Author: fijal Date: Tue Jan 16 18:03:10 2007 New Revision: 36827 Added: py/branch/rsession-cleanup/ - copied from r36826, py/dist/py/ Log: Brutforce merge of reviewed rsession to a branch for new features From fijal at codespeak.net Tue Jan 16 18:07:40 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 18:07:40 +0100 (CET) Subject: [py-svn] r36828 - in py/dist/py/test/rsession: . testing Message-ID: <20070116170740.44927100CC@code0.codespeak.net> Author: fijal Date: Tue Jan 16 18:07:37 2007 New Revision: 36828 Modified: py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_rsession.py Log: Revert the changes not to change semantics. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Tue Jan 16 18:07:37 2007 @@ -177,7 +177,7 @@ m = re.match("^(.*?):(.*)$", host.hostname) if m: host.hostname = m.group(1) - host.relpath = m.group(2) + host.relpath = m.group(2) + "-" + host.hostname else: host.relpath = "pytestcache-%s" % host.hostname Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Tue Jan 16 18:07:37 2007 @@ -301,11 +301,11 @@ events = [i for i in testevents if isinstance(i, report.HostRSyncing)] assert len(events) == 4 assert events[0].host.hostname == 'h1' - assert events[0].host.relpath == '/tmp' + assert events[0].host.relpath == '/tmp-h1' assert events[1].host.hostname == 'h1' - assert events[1].host.relpath == '/other' + assert events[1].host.relpath == '/other-h1' assert events[2].host.hostname == 'h2' assert events[2].host.relpath == 'pytestcache-h2' assert events[3].host.hostname == 'h2' - assert events[3].host.relpath == 'home' + assert events[3].host.relpath == 'home-h2' From hpk at codespeak.net Tue Jan 16 18:25:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 18:25:50 +0100 (CET) Subject: [py-svn] r36830 - py/dist/py/test Message-ID: <20070116172550.70DCD100D8@code0.codespeak.net> Author: hpk Date: Tue Jan 16 18:25:49 2007 New Revision: 36830 Modified: py/dist/py/test/collect.py Log: avoid an early import Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Tue Jan 16 18:25:49 2007 @@ -25,7 +25,6 @@ """ from __future__ import generators import py -isclass = py.std.inspect.isclass def configproperty(name): def fget(self): @@ -317,7 +316,8 @@ return d def makeitem(self, name, obj, usefilters=True): - if (not usefilters or self.classnamefilter(name)) and isclass(obj): + 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 From fijal at codespeak.net Tue Jan 16 18:26:56 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 18:26:56 +0100 (CET) Subject: [py-svn] r36832 - in py/branch/rsession-cleanup/test/rsession: . webdata Message-ID: <20070116172656.1B2B6100E2@code0.codespeak.net> Author: fijal Date: Tue Jan 16 18:26:53 2007 New Revision: 36832 Modified: py/branch/rsession-cleanup/test/rsession/rsession.py py/branch/rsession-cleanup/test/rsession/webdata/source.js Log: Turn on experimental support for rescheduling tests. Modified: py/branch/rsession-cleanup/test/rsession/rsession.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/rsession.py (original) +++ py/branch/rsession-cleanup/test/rsession/rsession.py Tue Jan 16 18:26:53 2007 @@ -234,13 +234,13 @@ itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) all_tests = dispatch_loop(nodes, itemgenerator, checkfun) - #if all_tests: - # todo = {} - # for key, value in all_tests.items(): - # if key not in done_dict: - # todo[key] = True - # rg = randomgen(todo, done_dict) - # dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) + if all_tests: + todo = {} + for key, value in all_tests.items(): + if key not in done_dict: + todo[key] = True + rg = randomgen(todo, done_dict) + dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) class LSession(AbstractSession): Modified: py/branch/rsession-cleanup/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From hpk at codespeak.net Tue Jan 16 18:27:04 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 18:27:04 +0100 (CET) Subject: [py-svn] r36833 - py/branch/conftest Message-ID: <20070116172704.5B40E100E7@code0.codespeak.net> Author: hpk Date: Tue Jan 16 18:27:03 2007 New Revision: 36833 Added: py/branch/conftest/ - copied from r36832, py/dist/ Log: conftest handling refactoring/streamlining branch From arigo at codespeak.net Tue Jan 16 18:46:43 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Tue, 16 Jan 2007 18:46:43 +0100 (CET) Subject: [py-svn] r36834 - py/dist/py/test/rsession Message-ID: <20070116174643.9347A100BB@code0.codespeak.net> Author: arigo Date: Tue Jan 16 18:46:41 2007 New Revision: 36834 Modified: py/dist/py/test/rsession/rsync.py Log: Display the name of the files that are queued to be sent, and the machine where it will go. Modified: py/dist/py/test/rsession/rsync.py ============================================================================== --- py/dist/py/test/rsession/rsync.py (original) +++ py/dist/py/test/rsession/rsync.py Tue Jan 16 18:46:41 2007 @@ -86,8 +86,9 @@ # ! there is a reason for the interning: # sharing multiple copies of the file's data data = intern(data) - #print channel.gateway.getremoteaddress(), - #print '/'.join(modified_rel_path) + print '%s <= %s' % ( + channel.gateway._getremoteaddress(), + modified_rel_path) channel.send(data) del data else: From hpk at codespeak.net Tue Jan 16 19:08:20 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 19:08:20 +0100 (CET) Subject: [py-svn] r36840 - py/branch/conftest/py/test Message-ID: <20070116180820.8E2C1100BA@code0.codespeak.net> Author: hpk Date: Tue Jan 16 19:08:19 2007 New Revision: 36840 Modified: py/branch/conftest/py/test/config.py Log: removing all caching for now Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Tue Jan 16 19:08:19 2007 @@ -26,11 +26,11 @@ class Config(object): """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option - _configs_cache = {} def __init__(self): self.option = optparse.Values() - self._parser = optparse.OptionParser(usage="usage: %prog [options] [query] [filenames of tests]") + self._parser = optparse.OptionParser( + usage="usage: %prog [options] [query] [filenames of tests]") self._initialconfigmodules = [] # class level attributes @@ -46,11 +46,7 @@ if trydefaultconfig: configpaths.append(defaultconfig) for p in configpaths: - try: - mod = cls._configs_cache[p] - except (KeyError, IndexError): - mod = importconfig(p) - cls._configs_cache[p] = mod + mod = importconfig(p) try: return getattr(mod, name) except AttributeError: @@ -188,15 +184,8 @@ Config._reset() return config -_config_paths_cache = {} - def guessconfigpaths(*paths): """ return test configuration paths from skimming the args. """ - key = tuple(paths) - try: - return _config_paths_cache[key] - except KeyError: - pass d = {} l = [] for anchor in paths: @@ -207,7 +196,6 @@ d[x] = True l.append(x) l.reverse() - _config_paths_cache[key] = l return l def getanchorpaths(args): From fijal at codespeak.net Tue Jan 16 19:23:58 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 19:23:58 +0100 (CET) Subject: [py-svn] r36845 - py/branch/rsession-cleanup/test/rsession/testing Message-ID: <20070116182358.5EF061009F@code0.codespeak.net> Author: fijal Date: Tue Jan 16 19:23:55 2007 New Revision: 36845 Modified: py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py Log: Add a test for tests rescheduling. The hairy part of it is that it may hang if test rescheduling does not work. Still better than lack of tests. Modified: py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py (original) +++ py/branch/rsession-cleanup/test/rsession/testing/test_rsession.py Tue Jan 16 19:23:55 2007 @@ -5,7 +5,7 @@ import py from py.__.test.rsession import report from py.__.test.rsession.rsession import RSession, parse_directories,\ - session_options, remote_options, parse_directories + session_options, remote_options, parse_directories, AbstractSession from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\ HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ @@ -308,4 +308,59 @@ assert events[2].host.relpath == 'pytestcache-h2' assert events[3].host.hostname == 'h2' assert events[3].host.relpath == 'home' - + +class AbstractNode(object): + def __init__(self, done_dict, reporter): + self.done_dict = done_dict + self.pending = [] + self.reporter = reporter + +class DummyWorkingNode(AbstractNode): + def send(self, item): + self.done_dict[item] = True + self.reporter(item) + +class DummyNotWorkingNode(AbstractNode): + def send(self, item): + if len(self.pending) > 2: + py.test.fail("Too much items in pending") + self.pending.insert(0, item) + +class TestSession(RSession): + def __init__(self): + pass + + def test_dispatch_tests(self): + # we need to perform some initialisation here + tmpdir = py.test.ensuretemp("dispatch_tests") + tmpdir.ensure("__init__.py") + source = py.code.Source(""" + def test_1(): + pass + def test_2(): + pass + def test_3(): + pass + def test_4(): + pass + def test_5(): + pass + def test_6(): + pass + """) + tmpdir.ensure("test_one.py").write(source) + tmpdir.ensure("test_two.py").write(source) + config, args = py.test.Config.parse([str(tmpdir)]) + self.config = config + reports = [] + self.config.option.keyword = None + session_options.bind_config(config) + session_options.max_tasks_per_node = 1 + done_dict = {} + wn = DummyWorkingNode(done_dict, reports.append) + nwn = DummyNotWorkingNode(done_dict, reports.append) + nodes = [wn, nwn] + self.dispatch_tests(nodes, args, tmpdir, reports.append, lambda:False, + done_dict) + assert len([i for i in reports if isinstance(i, py.test.Item)]) == 12 + assert len(nwn.pending) == 1 From fijal at codespeak.net Tue Jan 16 19:59:06 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 16 Jan 2007 19:59:06 +0100 (CET) Subject: [py-svn] r36849 - py/branch/rsession-cleanup/test/rsession Message-ID: <20070116185906.919FC1009C@code0.codespeak.net> Author: fijal Date: Tue Jan 16 19:59:05 2007 New Revision: 36849 Modified: py/branch/rsession-cleanup/test/rsession/rsession.py py/branch/rsession-cleanup/test/rsession/webjs.py Log: Fix stupid bug. Modified: py/branch/rsession-cleanup/test/rsession/rsession.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/rsession.py (original) +++ py/branch/rsession-cleanup/test/rsession/rsession.py Tue Jan 16 19:59:05 2007 @@ -234,13 +234,13 @@ itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) all_tests = dispatch_loop(nodes, itemgenerator, checkfun) - if all_tests: + if all_tests and not checkfun(): todo = {} for key, value in all_tests.items(): if key not in done_dict: todo[key] = True rg = randomgen(todo, done_dict) - dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) + dispatch_loop(nodes, rg, checkfun, max_tasks_per_node=1) class LSession(AbstractSession): Modified: py/branch/rsession-cleanup/test/rsession/webjs.py ============================================================================== --- py/branch/rsession-cleanup/test/rsession/webjs.py (original) +++ py/branch/rsession-cleanup/test/rsession/webjs.py Tue Jan 16 19:59:05 2007 @@ -84,7 +84,7 @@ td.id = '_txt_' + msg['fullitemname'] #tr.setAttribute("id", msg['fullitemname']) td.setAttribute("onmouseover", - "show_info('%s')" % (msg['fullitemname'],)) + "show_info('%s')" % (msg['fullitemname'],)) td.setAttribute("onmouseout", "hide_info()") td2 = create_elem('td') tr.appendChild(td2) From hpk at codespeak.net Tue Jan 16 20:23:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 20:23:48 +0100 (CET) Subject: [py-svn] r36851 - in py/branch/conftest/py/test: . testing Message-ID: <20070116192348.9583F1008A@code0.codespeak.net> Author: hpk Date: Tue Jan 16 20:23:47 2007 New Revision: 36851 Added: py/branch/conftest/py/test/conftesthandle.py py/branch/conftest/py/test/testing/test_conftesthandle.py Log: added tested conftesthandling code (not used yet from other code) Added: py/branch/conftest/py/test/conftesthandle.py ============================================================================== --- (empty file) +++ py/branch/conftest/py/test/conftesthandle.py Tue Jan 16 20:23:47 2007 @@ -0,0 +1,58 @@ + +class Conftest(object): + """ the single place for accessing values and interacting + towards conftest modules from py.test objects. + + Note that triggering Conftest instances to import + conftest.py files may result in added cmdline options. + XXX + """ + def __init__(self, path): + self._path2confmods = {} + self._path2confmods[None] = self.getconftestmodules(path) + + def getconftestmodules(self, path): + """ return a list of imported conftest modules for the given path. """ + try: + clist = self._path2confmods[path] + except KeyError: + dp = path.dirpath() + if dp == path: + return [] + clist = self.getconftestmodules(dp) + conftestpath = path.join("conftest.py") + if conftestpath.check(file=1): + # found a conftest.py + clist.append(importconfig(conftestpath)) + print "setting %s to %s" %(path, clist) + self._path2confmods[path] = clist + # be defensive: avoid changes from caller side to + # affect us by always returning a copy of the actual list + return clist[:] + + def lget(self, name, path=None): + modules = self.getconftestmodules(path) + return self._get(name, modules) + + def rget(self, name, path=None): + modules = self.getconftestmodules(path) + modules.reverse() + return self._get(name, modules) + + def _get(self, name, modules): + for mod in modules: + try: + return getattr(mod, name) + except AttributeError: + continue + raise KeyError, name + + +def importconfig(configpath): + if not configpath.dirpath('__init__.py').check(file=1): + # HACK: we don't want any "globally" imported conftest.py, + # prone to conflicts and subtle problems + modname = str(configpath).replace('.', configpath.sep) + return configpath.pyimport(modname=modname) + else: + return configpath.pyimport() Added: py/branch/conftest/py/test/testing/test_conftesthandle.py ============================================================================== --- (empty file) +++ py/branch/conftest/py/test/testing/test_conftesthandle.py Tue Jan 16 20:23:47 2007 @@ -0,0 +1,62 @@ +import py +from py.__.test.conftesthandle import Conftest + +class TestConftestValueAccessInPackage: + def setup_class(cls): + # with init + cls.basedir = d = py.test.ensuretemp(cls.__name__) + d.ensure("a/__init__.py") + d.ensure("a/conftest.py").write("a=1") + d.ensure("a/b/__init__.py") + d.ensure("a/b/conftest.py").write("b=2 ; a = 1.5") + + def test_immediate_initialiation_and_incremental_are_the_same(self): + conftest = Conftest(self.basedir) + snap1 = len(conftest._path2confmods) + conftest.getconftestmodules(self.basedir.join('a')) + assert len(conftest._path2confmods) == snap1 + 1 + conftest.getconftestmodules(self.basedir.join('b')) + assert len(conftest._path2confmods) == snap1 + 2 + + def test_value_access_not_existing(self): + conftest = Conftest(self.basedir) + 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) + assert conftest.rget("a", self.basedir.join('a')) == 1 + assert conftest.lget("a", self.basedir.join('a')) == 1 + assert conftest.rget("a", self.basedir.join('a', 'b')) == 1.5 + assert conftest.lget("a", self.basedir.join('a', 'b')) == 1 + assert conftest.lget("b", self.basedir.join('a', 'b')) == 2 + assert py.test.raises(KeyError, + 'conftest.lget("b", self.basedir.join("a"))' + ) + + def test_value_access_with_init_one_conftest(self): + conftest = Conftest(self.basedir.join('a')) + assert conftest.rget("a") == 1 + assert conftest.lget("a") == 1 + + def test_value_access_with_init_two_conftests(self): + conftest = Conftest(self.basedir.join("a", "b")) + conftest.rget("a") == 1.5 + conftest.lget("a") == 1 + conftest.lget("b") == 1 + +class TestConftestValueAccessGlobal(TestConftestValueAccessInPackage): + def setup_class(cls): + # now we have "global" conftests (i.e. no __init__.py + # and thus no further import scope) and it should still all work + # because "global" conftests are upon import canonically mangled + # (according to their actual path) to avoid having bad + cls.basedir = d = py.test.ensuretemp(cls.__name__) + d.ensure("a/conftest.py").write("a=1") + d.ensure("a/b/conftest.py").write("b=2 ; a = 1.5") + + + + + + From hpk at codespeak.net Tue Jan 16 20:48:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 20:48:43 +0100 (CET) Subject: [py-svn] r36853 - in py/branch/conftest/py/test: . testing Message-ID: <20070116194843.7787810069@code0.codespeak.net> Author: hpk Date: Tue Jan 16 20:48:42 2007 New Revision: 36853 Modified: py/branch/conftest/py/test/conftesthandle.py py/branch/conftest/py/test/testing/test_conftesthandle.py Log: incorporate default options, slightly add and streamline the tests Modified: py/branch/conftest/py/test/conftesthandle.py ============================================================================== --- py/branch/conftest/py/test/conftesthandle.py (original) +++ py/branch/conftest/py/test/conftesthandle.py Tue Jan 16 20:48:42 2007 @@ -1,3 +1,5 @@ +import py +defaultconftestpath = py.magic.autopath().dirpath('defaultconftest.py') class Conftest(object): """ the single place for accessing values and interacting @@ -9,6 +11,8 @@ """ def __init__(self, path): self._path2confmods = {} + # conftest-lookups without a path actually mean + # lookups with our initial path. self._path2confmods[None] = self.getconftestmodules(path) def getconftestmodules(self, path): @@ -18,13 +22,11 @@ except KeyError: dp = path.dirpath() if dp == path: - return [] + return [importconfig(defaultconftestpath)] clist = self.getconftestmodules(dp) conftestpath = path.join("conftest.py") if conftestpath.check(file=1): - # found a conftest.py clist.append(importconfig(conftestpath)) - print "setting %s to %s" %(path, clist) self._path2confmods[path] = clist # be defensive: avoid changes from caller side to # affect us by always returning a copy of the actual list Modified: py/branch/conftest/py/test/testing/test_conftesthandle.py ============================================================================== --- py/branch/conftest/py/test/testing/test_conftesthandle.py (original) +++ py/branch/conftest/py/test/testing/test_conftesthandle.py Tue Jan 16 20:48:42 2007 @@ -1,13 +1,14 @@ import py from py.__.test.conftesthandle import Conftest -class TestConftestValueAccessInPackage: +class TestConftestValueAccessGlobal: def setup_class(cls): - # with init + # if we have "global" conftests (i.e. no __init__.py + # 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.test.ensuretemp(cls.__name__) - d.ensure("a/__init__.py") - d.ensure("a/conftest.py").write("a=1") - d.ensure("a/b/__init__.py") + d.ensure("a/conftest.py").write("a=1 ; Directory = 3") d.ensure("a/b/conftest.py").write("b=2 ; a = 1.5") def test_immediate_initialiation_and_incremental_are_the_same(self): @@ -17,6 +18,17 @@ assert len(conftest._path2confmods) == snap1 + 1 conftest.getconftestmodules(self.basedir.join('b')) assert len(conftest._path2confmods) == snap1 + 2 + + def test_default_Module_setting_is_visible_always(self): + for path in self.basedir.parts(): + conftest = Conftest(path) + 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("a")) + assert conftest.rget('Directory') == 3 + assert conftest.lget('Directory') == py.test.collect.Directory def test_value_access_not_existing(self): conftest = Conftest(self.basedir) @@ -45,15 +57,12 @@ conftest.lget("a") == 1 conftest.lget("b") == 1 -class TestConftestValueAccessGlobal(TestConftestValueAccessInPackage): +class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): def setup_class(cls): - # now we have "global" conftests (i.e. no __init__.py - # and thus no further import scope) and it should still all work - # because "global" conftests are upon import canonically mangled - # (according to their actual path) to avoid having bad - cls.basedir = d = py.test.ensuretemp(cls.__name__) - d.ensure("a/conftest.py").write("a=1") - d.ensure("a/b/conftest.py").write("b=2 ; a = 1.5") + TestConftestValueAccessGlobal.__dict__['setup_class'](cls) + d = cls.basedir + d.ensure("a/__init__.py") + d.ensure("a/b/__init__.py") From hpk at codespeak.net Tue Jan 16 22:05:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 22:05:01 +0100 (CET) Subject: [py-svn] r36854 - in py/branch/conftest/py/test: . terminal tkinter Message-ID: <20070116210501.06A2810076@code0.codespeak.net> Author: hpk Date: Tue Jan 16 22:04:58 2007 New Revision: 36854 Modified: py/branch/conftest/py/test/cmdline.py py/branch/conftest/py/test/config.py py/branch/conftest/py/test/defaultconftest.py py/branch/conftest/py/test/terminal/terminal.py py/branch/conftest/py/test/tkinter/reportsession.py Log: teaching config and py.test to use the new way of accessing conftest files. removes lots of code, but currently breaks rsession (but we are in a branch, so this is fine) Modified: py/branch/conftest/py/test/cmdline.py ============================================================================== --- py/branch/conftest/py/test/cmdline.py (original) +++ py/branch/conftest/py/test/cmdline.py Tue Jan 16 22:04:58 2007 @@ -31,15 +31,6 @@ session = sessionclass(config) - if config.option.runbrowser and not config.option.startserver: - print "Cannot point browser when not starting server" - config.option.startserver = True - try: - if config.getinitialvalue('startserver'): - py.std.warnings.warn("Startserver flag in config is deprecated, use commandline option instead") - except ValueError: - pass - try: failures = session.main(args) if failures: Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Tue Jan 16 22:04:58 2007 @@ -1,16 +1,9 @@ from __future__ import generators import py -optparse = py.compat.optparse - -defaultconfig = py.magic.autopath().dirpath('defaultconftest.py') -dummy = object() - -# -# configuration file handling -# -configbasename = 'conftest.py' +from conftesthandle import Conftest +optparse = py.compat.optparse # XXX move to Config class basetemp = None @@ -31,37 +24,24 @@ self.option = optparse.Values() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - self._initialconfigmodules = [] # class level attributes def _reset(cls): - cls._config = cls() + cls._config = cls() _reset = classmethod(_reset) - def getvalue(cls, name, path=None, default=dummy, trydefaultconfig=True): - """ return 'name' value looked up from the first conftest file - found up the path (including the path itself). - """ - configpaths = guessconfigpaths(path) - if trydefaultconfig: - configpaths.append(defaultconfig) - for p in configpaths: - mod = importconfig(p) - try: - return getattr(mod, name) - except AttributeError: - pass - if default is not dummy: - return default - raise ValueError("config value not found: %r, path=%r" % (name, path)) - getvalue = classmethod(getvalue) - def parse(cls, args): """ return Config object and remaining arguments from parsing command line arguments. """ - configpaths = guessconfigpaths(*getanchorpaths(args)) - config = bootstrapconfig(configpaths) + if not hasattr(cls, 'conftest'): + cls.conftest = getconftest(args) + else: + for arg in args: + p = py.path.local().join(arg, abs=1) + cls.conftest.getconftestmodules(p) + config = cls._config + cls.conftest.lget('adddefaultoptions')() config._origargs = args cmdlineoption, remaining = config._parser.parse_args(args) for name, value in vars(cmdlineoption).items(): @@ -70,6 +50,7 @@ if not remaining: remaining.append(py.std.os.getcwd()) config.remaining = remaining + cls._reset() return config, remaining parse = classmethod(parse) @@ -87,42 +68,32 @@ return cls._config.option addoptions = classmethod(addoptions) - # instance methods dealing with initial config module handling - - def loadconfig(self, configpath): - """ load configpath as an initial config module. """ - mod = importconfig(configpath) - self._initialconfigmodules.append(mod) - return mod + # methods dealing with config module handling + # + def getvalue(cls, name, path=None): + """ return 'name' value looked up from the first conftest file + found up the path (including the path itself). + """ + return cls.conftest.rget(name, path) + getvalue = classmethod(getvalue) - def getinitialvalue(self, name, default=dummy): + def getinitialvalue(self, name): """ return first value found in initial config modules. """ - for confmodule in self._initialconfigmodules: - if hasattr(confmodule, name): - return getattr(confmodule, name) - if default is not dummy: - return default - raise ValueError("initial config value not found: %r" % name) + return self.conftest.lget(name) def getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. """ - # we need to import all the sessions here - from py.__.test.session import Session - from py.__.test.rsession.rsession import RSession, LSession - from py.__.test.terminal.terminal import TerminalSession - from py.__.test.tkinter.reportsession import ReportSession - sessions = {'RSession':RSession, 'LSession':LSession, - 'TerminalSession':TerminalSession, - 'TkinterSession':ReportSession} - - name = self.option.session - name += 'Session' - try: - return self.getinitialvalue(name) - except ValueError: - return sessions[name] + sessionname = self.option.session + 'Session' + try: + return self.conftest.lget(sessionname) + except KeyError: + pass + sessionimportpaths = self.conftest.lget('sessionimportpaths') + importpath = sessionimportpaths[sessionname] + mod = __import__(importpath, None, None, ['__doc__']) + return getattr(mod, sessionname) Config._reset() @@ -163,67 +134,24 @@ name = name.capitalize() option.session = name -def bootstrapconfig(configpaths): - """ return 'current' config object, after initializing - it with the given configpaths. - """ - # XXX Config._config holds all options that are added from - # conftest's py.test.Config.addoptions() invocations. This "global state" - # manipulation is unfortunate but how else could we allow - # applications to add cmdline options and provide them - # access to the resulting config values? - - config = Config._config - # trigger loading conftest files which might add options! - config.loadconfig(defaultconfig).adddefaultoptions() - for configpath in configpaths: - config.loadconfig(configpath) - # each time we make a config object (which assembled cmdline - # options through py.test.addoptions() invocations) - # we want to reset and clean up the global state - Config._reset() - return config - -def guessconfigpaths(*paths): - """ return test configuration paths from skimming the args. """ - d = {} - l = [] - for anchor in paths: - if anchor: - for p in anchor.parts(): - x = p.join(configbasename) - if x not in d and x.check(file=1): - d[x] = True - l.append(x) - l.reverse() - return l + if option.runbrowser and not option.startserver: + print "Cannot point browser when not starting server" + option.startserver = True + -def getanchorpaths(args): - """ yield "anchors" from skimming the args for existing files/dirs. """ +def getconftest(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 + and we thus have no completely safe way of determining + which parts of the arguments are actually related to options. + """ current = py.path.local() - l = [] - for arg in args: + for arg in args + [current]: anchor = current.join(arg, abs=1) - if anchor.check(): - l.append(anchor) - if not l: - l = [current] - return l - -def importconfig(configpath): - if not configpath.dirpath('__init__.py').check(file=1): - # HACK: we don't want a "globally" imported conftest.py, - # prone to conflicts and subtle problems - modname = str(configpath).replace('.', configpath.sep) - return configpath.pyimport(modname=modname) - else: - return configpath.pyimport() - - -#XXX was needed for extracting defaults, not needed anymore? -#def flattenoptions(parser): -# for group in parser.option_groups: -# for y in group.option_list: -# yield y -# for x in parser.option_list: -# yield x + if anchor.check(): # we found some file object + print "initializing conftest from", anchor + conftest = Conftest(anchor) + #print " -> ", conftest._path2confmods + return conftest Modified: py/branch/conftest/py/test/defaultconftest.py ============================================================================== --- py/branch/conftest/py/test/defaultconftest.py (original) +++ py/branch/conftest/py/test/defaultconftest.py Tue Jan 16 22:04:58 2007 @@ -10,8 +10,16 @@ additionalinfo = None +Option = py.test.Config.Option + +sessionimportpaths = { + 'RSession': 'py.__.test.rsession.rsession', + 'LSession': 'py.__.test.rsession.rsession', + 'TerminalSession': 'py.__.test.terminal.terminal', + 'TkinterSession': 'py.__.test.tkinter.reportsession', +} + def adddefaultoptions(): - Option = py.test.Config.Option py.test.Config.addoptions('general options', Option('-v', '--verbose', action="count", dest="verbose", default=0, @@ -53,6 +61,7 @@ help="generate api documentation while testing (requires" "argument pointing to a script)."), ) + py.test.Config.addoptions('test-session related options', Option('', '--tkinter', action="store_true", dest="tkinter", default=False, Modified: py/branch/conftest/py/test/terminal/terminal.py ============================================================================== --- py/branch/conftest/py/test/terminal/terminal.py (original) +++ py/branch/conftest/py/test/terminal/terminal.py Tue Jan 16 22:04:58 2007 @@ -145,7 +145,8 @@ for x in colitems: self.out.line("test target: %s" %(x.fspath,)) - for i,x in py.builtin.enumerate(self.config._initialconfigmodules): + conftestmodules = self.config.conftest.getconftestmodules(None) + for i,x in py.builtin.enumerate(conftestmodules): self.out.line("initial conf %d: %s" %(i, x.__file__)) #for i, x in py.builtin.enumerate(py.test.config.configpaths): Modified: py/branch/conftest/py/test/tkinter/reportsession.py ============================================================================== --- py/branch/conftest/py/test/tkinter/reportsession.py (original) +++ py/branch/conftest/py/test/tkinter/reportsession.py Tue Jan 16 22:04:58 2007 @@ -44,4 +44,5 @@ def sendreport(self, report): self.channel.send(report.to_channel()) +TkinterSession = ReportSession From hpk at codespeak.net Tue Jan 16 22:12:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 22:12:25 +0100 (CET) Subject: [py-svn] r36855 - py/branch/conftest/py/test/rsession Message-ID: <20070116211225.4D05B10076@code0.codespeak.net> Author: hpk Date: Tue Jan 16 22:12:24 2007 New Revision: 36855 Modified: py/branch/conftest/py/test/rsession/rsession.py Log: very shallow fix for most of the rsession failures ... the real thing is to cleanup the rsession option handling considerably. Maciej, are you up for that? (I will need to understand the original intention more before making any suggestions) Modified: py/branch/conftest/py/test/rsession/rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/rsession.py (original) +++ py/branch/conftest/py/test/rsession/rsession.py Tue Jan 16 22:12:24 2007 @@ -46,7 +46,7 @@ def getvalue(self, opt): try: return getattr(self.config.getinitialvalue('SessionOptions'), opt) - except (ValueError, AttributeError): + except (KeyError, AttributeError): try: return self.defaults[opt] except KeyError: @@ -57,7 +57,7 @@ # copy to remote session options try: ses_opt = self.config.getinitialvalue('SessionOptions').__dict__ - except ValueError: + except KeyError: ses_opt = self.defaults for key in self.defaults: try: From hpk at codespeak.net Tue Jan 16 22:45:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 16 Jan 2007 22:45:31 +0100 (CET) Subject: [py-svn] r36856 - in py/branch/conftest/py/test: . testing Message-ID: <20070116214531.F007610077@code0.codespeak.net> Author: hpk Date: Tue Jan 16 22:45:30 2007 New Revision: 36856 Modified: py/branch/conftest/py/test/config.py py/branch/conftest/py/test/conftesthandle.py py/branch/conftest/py/test/testing/test_conftesthandle.py Log: remove some redundancy for conftest initialization Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Tue Jan 16 22:45:30 2007 @@ -19,6 +19,7 @@ class Config(object): """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option + conftest = Conftest() def __init__(self): self.option = optparse.Values() @@ -34,12 +35,7 @@ """ return Config object and remaining arguments from parsing command line arguments. """ - if not hasattr(cls, 'conftest'): - cls.conftest = getconftest(args) - else: - for arg in args: - p = py.path.local().join(arg, abs=1) - cls.conftest.getconftestmodules(p) + cls.conftest.setinitial(args) config = cls._config cls.conftest.lget('adddefaultoptions')() config._origargs = args @@ -138,20 +134,3 @@ print "Cannot point browser when not starting server" option.startserver = True - -def getconftest(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 - and we thus have no completely safe way of determining - which parts of the arguments are actually related to options. - """ - current = py.path.local() - for arg in args + [current]: - anchor = current.join(arg, abs=1) - if anchor.check(): # we found some file object - print "initializing conftest from", anchor - conftest = Conftest(anchor) - #print " -> ", conftest._path2confmods - return conftest Modified: py/branch/conftest/py/test/conftesthandle.py ============================================================================== --- py/branch/conftest/py/test/conftesthandle.py (original) +++ py/branch/conftest/py/test/conftesthandle.py Tue Jan 16 22:45:30 2007 @@ -9,11 +9,30 @@ conftest.py files may result in added cmdline options. XXX """ - def __init__(self, path): + def __init__(self, path=None): self._path2confmods = {} - # conftest-lookups without a path actually mean - # lookups with our initial path. - self._path2confmods[None] = self.getconftestmodules(path) + if path is not None: + 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 + and we thus have no completely safe way of determining + which parts of the arguments are actually related to options. + """ + current = py.path.local() + for arg in args + [current]: + anchor = current.join(arg, abs=1) + if anchor.check(): # we found some file object + print "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. """ Modified: py/branch/conftest/py/test/testing/test_conftesthandle.py ============================================================================== --- py/branch/conftest/py/test/testing/test_conftesthandle.py (original) +++ py/branch/conftest/py/test/testing/test_conftesthandle.py Tue Jan 16 22:45:30 2007 @@ -12,8 +12,11 @@ d.ensure("a/b/conftest.py").write("b=2 ; a = 1.5") def test_immediate_initialiation_and_incremental_are_the_same(self): - conftest = Conftest(self.basedir) + conftest = Conftest() + snap0 = len(conftest._path2confmods) + conftest.getconftestmodules(self.basedir) snap1 = len(conftest._path2confmods) + #assert len(conftest._path2confmods) == snap1 + 1 conftest.getconftestmodules(self.basedir.join('a')) assert len(conftest._path2confmods) == snap1 + 1 conftest.getconftestmodules(self.basedir.join('b')) From hpk at codespeak.net Wed Jan 17 12:22:12 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 12:22:12 +0100 (CET) Subject: [py-svn] r36865 - py/branch/conftest/py/test/rsession/testing Message-ID: <20070117112212.E81401007A@code0.codespeak.net> Author: hpk Date: Wed Jan 17 12:22:11 2007 New Revision: 36865 Modified: py/branch/conftest/py/test/rsession/testing/test_executor.py Log: relax some tests to not test exact output but only occurence of a particular string. Modified: py/branch/conftest/py/test/rsession/testing/test_executor.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_executor.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_executor.py Wed Jan 17 12:22:11 2007 @@ -100,7 +100,7 @@ outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert outcome.passed - assert outcome.stdout == "samfing\n" + assert outcome.stdout.find("samfing") != -1 def test_box_executor_stdout_error(): rootcol = py.test.collect.Directory(rootdir) @@ -109,7 +109,7 @@ outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert not outcome.passed - assert outcome.stdout == "samfing elz\n" + assert outcome.stdout.find("samfing elz") != -1 def test_cont_executor(): rootcol = py.test.collect.Directory(rootdir) @@ -120,4 +120,4 @@ outcome_repr = cont() outcome = ReprOutcome(outcome_repr) assert not outcome.passed - assert outcome.stdout == "samfing elz\n" + assert outcome.stdout.find("samfing elz") != -1 From hpk at codespeak.net Wed Jan 17 12:29:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 12:29:53 +0100 (CET) Subject: [py-svn] r36866 - py/branch/conftest/py/test/rsession/testing Message-ID: <20070117112953.7ED511007A@code0.codespeak.net> Author: hpk Date: Wed Jan 17 12:29:52 2007 New Revision: 36866 Modified: py/branch/conftest/py/test/rsession/testing/test_reporter.py py/branch/conftest/py/test/rsession/testing/test_rsession.py Log: relax some more rsession tests to not test exact output Modified: py/branch/conftest/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_reporter.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_reporter.py Wed Jan 17 12:29:52 2007 @@ -170,7 +170,7 @@ host1: READY (still 2 to go) host2: READY (still 1 to go) host3: READY""" - assert out.startswith(expected) + assert out.find(expected) != -1 class TestLocalReporter(AbstractTestReporter): reporter = LocalReporter @@ -191,7 +191,7 @@ repmod/test_one.py[1] repmod/test_three.py[0] - FAILED TO LOAD MODULE repmod/test_two.py[0] - skipped (reason)""" - assert received == expected + assert received.find(expected) != -1 class TestRemoteReporter(AbstractTestReporter): reporter = RemoteReporter @@ -207,7 +207,7 @@ localhost: FAILED py test rsession testing test_slave.py funcpass localhost: PASSED py test rsession testing test_slave.py funcpass """ - assert val == expected + assert val.find(expected) != -1 def test_module(self): val = self._test_module() @@ -217,10 +217,10 @@ localhost: FAILED py test rsession testing test_slave.py funcpass localhost: PASSED py test rsession testing test_slave.py funcpass """ - assert val == expected + assert val.find(expected) != -1 def test_full_module(self): #py.test.skip("XXX rewrite test to not rely on exact formatting") val = self._test_full_module() - assert val == 'FAILED TO LOAD MODULE: repmod/test_three.py\n'\ - '\nSkipped (reason) repmod/test_two.py\n\n' + assert val.find('FAILED TO LOAD MODULE: repmod/test_three.py\n'\ + '\nSkipped (reason) repmod/test_two.py') != -1 Modified: py/branch/conftest/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_rsession.py Wed Jan 17 12:29:52 2007 @@ -214,7 +214,7 @@ assert len(skipped) == len(nodes) assert len(events) == 4 * len(nodes) # one of passed for each node has non-empty stdout - passed_stdout = [i for i in passed if i.outcome.stdout == 'samfing\n'] + passed_stdout = [i for i in passed if i.outcome.stdout.find('samfing') != -1] assert len(passed_stdout) == len(nodes), passed def test_config_pass(self): From fijal at codespeak.net Wed Jan 17 17:49:33 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 17 Jan 2007 17:49:33 +0100 (CET) Subject: [py-svn] r36882 - py/dist/py/test/rsession Message-ID: <20070117164933.8632A10076@code0.codespeak.net> Author: fijal Date: Wed Jan 17 17:49:31 2007 New Revision: 36882 Modified: py/dist/py/test/rsession/rsession.py Log: Revert overwriting of changes, due to branch merge. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Wed Jan 17 17:49:31 2007 @@ -268,7 +268,7 @@ reporter(report.RsyncFinished()) if runner is None: - runner = self.init_runner() + runner = self.init_runner(pkgdir) keyword = self.config.option.keyword @@ -295,14 +295,17 @@ raise NotImplementedError("Provided script does not seem " "to contain build function") - def init_runner(self): + def init_runner(self, pkgdir): if self.config.option.apigen: from py.__.apigen.tracer.tracer import Tracer, DocStorage module = py try: apigen = py.path.local(self.config.option.apigen).pyimport() - self.docstorage = DocStorage().from_dict( - apigen.get_documentable_items()) + items = apigen.get_documentable_items(pkgdir) + if isinstance(items, dict): + self.docstorage = DocStorage().from_dict(items) + else: + self.docstorage = DocStorage().from_pkg(items) except ImportError: raise ImportError("Provided script cannot be imported") except (ValueError, AttributeError): From hpk at codespeak.net Wed Jan 17 18:04:08 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 18:04:08 +0100 (CET) Subject: [py-svn] r36883 - py/branch/conftest/py/apigen/testing Message-ID: <20070117170408.9F2571006F@code0.codespeak.net> Author: hpk Date: Wed Jan 17 18:04:07 2007 New Revision: 36883 Modified: py/branch/conftest/py/apigen/testing/test_apigen_functional.py Log: just skip the test for now Modified: py/branch/conftest/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/branch/conftest/py/apigen/testing/test_apigen_functional.py (original) +++ py/branch/conftest/py/apigen/testing/test_apigen_functional.py Wed Jan 17 18:04:07 2007 @@ -7,6 +7,7 @@ from test_apigen_example import setup_fs_project def test_apigen_functional(): + py.test.skip("needs fixing with respect to rev 36800 and probably other problems") fs_root, package_name = setup_fs_project( 'test_apigen_functional') tempdir = py.test.ensuretemp('test_apigen_functional_results') From hpk at codespeak.net Wed Jan 17 18:52:58 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 18:52:58 +0100 (CET) Subject: [py-svn] r36886 - in py/branch/conftest/py: . apigen doc doc/example/pytest test test/rsession test/rsession/testing test/terminal test/testing test/tkinter test/tkinter/testing Message-ID: <20070117175258.4799710070@code0.codespeak.net> Author: hpk Date: Wed Jan 17 18:52:49 2007 New Revision: 36886 Modified: py/branch/conftest/py/__init__.py py/branch/conftest/py/apigen/conftest.py py/branch/conftest/py/conftest.py py/branch/conftest/py/doc/conftest.py py/branch/conftest/py/doc/example/pytest/test_failures.py py/branch/conftest/py/doc/test_conftest.py py/branch/conftest/py/test/cmdline.py py/branch/conftest/py/test/collect.py py/branch/conftest/py/test/config.py py/branch/conftest/py/test/defaultconftest.py py/branch/conftest/py/test/rsession/conftest.py py/branch/conftest/py/test/rsession/testing/test_config.py py/branch/conftest/py/test/rsession/testing/test_lsession.py py/branch/conftest/py/test/rsession/testing/test_master.py py/branch/conftest/py/test/rsession/testing/test_reporter.py py/branch/conftest/py/test/rsession/testing/test_rest.py py/branch/conftest/py/test/rsession/testing/test_rsession.py py/branch/conftest/py/test/rsession/testing/test_slave.py py/branch/conftest/py/test/rsession/testing/test_web.py py/branch/conftest/py/test/rsession/testing/test_webjs.py py/branch/conftest/py/test/terminal/remote.py py/branch/conftest/py/test/testing/test_collect.py py/branch/conftest/py/test/testing/test_config.py py/branch/conftest/py/test/testing/test_conftesthandle.py py/branch/conftest/py/test/testing/test_session.py py/branch/conftest/py/test/tkinter/backend.py py/branch/conftest/py/test/tkinter/testing/test_backend.py py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py Log: * "py.test.config" now contains the main single per-process test config instance, this removes the odd classmethod/instancemethod distinction previously present with py.test.Config. * you cannot parse cmdline arguments twice with the same config object ... but test methods can use py.test.config._reparse(args) to temporarily get a config object (and all conftest readings go into this temporarily set py.test.config object) Modified: py/branch/conftest/py/__init__.py ============================================================================== --- py/branch/conftest/py/__init__.py (original) +++ py/branch/conftest/py/__init__.py Wed Jan 17 18:52:49 2007 @@ -32,7 +32,7 @@ 'test.compat.TestCase' : ('./test/compat.py', 'TestCase'), # configuration/initialization related test api - 'test.Config' : ('./test/config.py', 'Config'), + 'test.config' : ('./test/config.py', 'config'), 'test.ensuretemp' : ('./test/config.py', 'ensuretemp'), 'test.cmdline.main' : ('./test/cmdline.py', 'main'), Modified: py/branch/conftest/py/apigen/conftest.py ============================================================================== --- py/branch/conftest/py/apigen/conftest.py (original) +++ py/branch/conftest/py/apigen/conftest.py Wed Jan 17 18:52:49 2007 @@ -1,7 +1,7 @@ import py -Option = py.test.Config.Option -option = py.test.Config.addoptions("apigen test options", +Option = py.test.config.Option +option = py.test.config.addoptions("apigen test options", Option('', '--webcheck', action="store_true", dest="webcheck", default=False, help="run XHTML validation tests" Modified: py/branch/conftest/py/conftest.py ============================================================================== --- py/branch/conftest/py/conftest.py (original) +++ py/branch/conftest/py/conftest.py Wed Jan 17 18:52:49 2007 @@ -17,9 +17,9 @@ nomagic = False import py -Option = py.test.Config.Option +Option = py.test.config.Option -option = py.test.Config.addoptions("execnet options", +option = py.test.config.addoptions("execnet options", Option('-S', '', action="store", dest="sshtarget", default=None, help=("target to run tests requiring ssh, e.g. " Modified: py/branch/conftest/py/doc/conftest.py ============================================================================== --- py/branch/conftest/py/doc/conftest.py (original) +++ py/branch/conftest/py/doc/conftest.py Wed Jan 17 18:52:49 2007 @@ -2,8 +2,8 @@ import py from py.__.misc import rest -Option = py.test.Config.Option -option = py.test.Config.addoptions("documentation check options", +Option = py.test.config.Option +option = py.test.config.addoptions("documentation check options", Option('-R', '--checkremote', action="store_true", dest="checkremote", default=False, help="check remote links in ReST files" Modified: py/branch/conftest/py/doc/example/pytest/test_failures.py ============================================================================== --- py/branch/conftest/py/doc/example/pytest/test_failures.py (original) +++ py/branch/conftest/py/doc/example/pytest/test_failures.py Wed Jan 17 18:52:49 2007 @@ -3,7 +3,7 @@ failure_demo = py.magic.autopath().dirpath('failure_demo.py') def test_failure_demo_fails_properly(): - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main([failure_demo]) l = session.getitemoutcomepairs(py.test.Item.Failed) Modified: py/branch/conftest/py/doc/test_conftest.py ============================================================================== --- py/branch/conftest/py/doc/test_conftest.py (original) +++ py/branch/conftest/py/doc/test_conftest.py Wed Jan 17 18:52:49 2007 @@ -28,7 +28,7 @@ end """)) - config, args = py.test.Config.parse([str(xtxt)]) + config, args = py.test.config._reparse([str(xtxt)]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main([xtxt]) l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -46,7 +46,7 @@ .. _`blah`: javascript:some_function() """)) - config, args = py.test.Config.parse([str(xtxt)]) + config, args = py.test.config._reparse([str(xtxt)]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main([xtxt]) l = session.getitemoutcomepairs(py.test.Item.Failed) Modified: py/branch/conftest/py/test/cmdline.py ============================================================================== --- py/branch/conftest/py/test/cmdline.py (original) +++ py/branch/conftest/py/test/cmdline.py Wed Jan 17 18:52:49 2007 @@ -10,7 +10,9 @@ args = py.std.sys.argv[1:] elif isinstance(args, basestring): args = args.split(" ") - config, args = py.test.Config.parse(args) + config = py.test.config + config.parse(args) + args = config.remaining sessionclass = config.getsessionclass() # ok, some option checks Modified: py/branch/conftest/py/test/collect.py ============================================================================== --- py/branch/conftest/py/test/collect.py (original) +++ py/branch/conftest/py/test/collect.py Wed Jan 17 18:52:49 2007 @@ -29,7 +29,7 @@ def configproperty(name): def fget(self): #print "retrieving %r property from %s" %(name, self.fspath) - return py.test.Config.getvalue(name, self.fspath) + return py.test.config.getvalue(name, self.fspath) return property(fget) def getfscollector(fspath): @@ -43,7 +43,7 @@ pkgpath = fspath else: pkgpath = fspath.dirpath() - Directory = py.test.Config.getvalue('Directory', pkgpath) + Directory = py.test.config.getvalue('Directory', pkgpath) current = Directory(pkgpath) #print "pkgpath", pkgpath names = filter(None, fspath.relto(pkgpath).split(fspath.sep)) @@ -287,7 +287,7 @@ elif p.ext == '.txt': res = self.DoctestFile(p, parent=self) elif p.check(dir=1): - Directory = py.test.Config.getvalue('Directory', p) + Directory = py.test.config.getvalue('Directory', p) res = Directory(p, parent=self) name2items[name] = res return res Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Wed Jan 17 18:52:49 2007 @@ -20,58 +20,48 @@ """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option conftest = Conftest() + _parsed = False def __init__(self): self.option = optparse.Values() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - # class level attributes - def _reset(cls): - cls._config = cls() - _reset = classmethod(_reset) - - def parse(cls, args): - """ return Config object and remaining arguments from parsing - command line arguments. + def parse(self, args): + """ parse cmdline arguments into this config object. + Note that this can only be called once per testing process. """ - cls.conftest.setinitial(args) - config = cls._config - cls.conftest.lget('adddefaultoptions')() - config._origargs = args - cmdlineoption, remaining = config._parser.parse_args(args) + assert not self._parsed, ( + "can only parse cmdline args once per Config object") + self._parsed = True + self.conftest.setinitial(args) + self.conftest.lget('adddefaultoptions')() + self._origargs = args + cmdlineoption, remaining = self._parser.parse_args(args) for name, value in vars(cmdlineoption).items(): - setattr(config.option, name, value) - fixoptions(config.option) + setattr(self.option, name, value) + fixoptions(self.option) if not remaining: remaining.append(py.std.os.getcwd()) - config.remaining = remaining - cls._reset() - return config, remaining - parse = classmethod(parse) + self.remaining = remaining - def addoptions(cls, groupname, *specs): + def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. This function gets invoked during testing session initialization. """ - parser = cls._config._parser - optgroup = optparse.OptionGroup(parser, groupname) + optgroup = optparse.OptionGroup(self._parser, groupname) optgroup.add_options(specs) - parser.add_option_group(optgroup) + self._parser.add_option_group(optgroup) for opt in specs: if hasattr(opt, 'default') and opt.dest: - setattr(cls._config.option, opt.dest, opt.default) - return cls._config.option - addoptions = classmethod(addoptions) - - # methods dealing with config module handling - # - def getvalue(cls, name, path=None): + setattr(self.option, opt.dest, opt.default) + return self.option + + def getvalue(self, name, path=None): """ return 'name' value looked up from the first conftest file found up the path (including the path itself). """ - return cls.conftest.rget(name, path) - getvalue = classmethod(getvalue) + return self.conftest.rget(name, path) def getinitialvalue(self, name): """ return first value found in initial config modules. """ @@ -91,7 +81,19 @@ mod = __import__(importpath, None, None, ['__doc__']) return getattr(mod, sessionname) -Config._reset() + def _reparse(self, args): + """ this is used from tests that want to re-invoke parse(). """ + global config + oldconfig = py.test.config + try: + config = py.test.config = Config() + config.parse(args) + return config, config.remaining + finally: + config = py.test.config = oldconfig + +# this is the one per-process instance of py.test configuration +config = Config() # # helpers Modified: py/branch/conftest/py/test/defaultconftest.py ============================================================================== --- py/branch/conftest/py/test/defaultconftest.py (original) +++ py/branch/conftest/py/test/defaultconftest.py Wed Jan 17 18:52:49 2007 @@ -10,7 +10,7 @@ additionalinfo = None -Option = py.test.Config.Option +Option = py.test.config.Option sessionimportpaths = { 'RSession': 'py.__.test.rsession.rsession', @@ -20,7 +20,7 @@ } def adddefaultoptions(): - py.test.Config.addoptions('general options', + py.test.config.addoptions('general options', Option('-v', '--verbose', action="count", dest="verbose", default=0, help="increase verbosity."), @@ -62,7 +62,7 @@ "argument pointing to a script)."), ) - py.test.Config.addoptions('test-session related options', + py.test.config.addoptions('test-session related options', Option('', '--tkinter', action="store_true", dest="tkinter", default=False, help="use tkinter test session frontend."), Modified: py/branch/conftest/py/test/rsession/conftest.py ============================================================================== --- py/branch/conftest/py/test/rsession/conftest.py (original) +++ py/branch/conftest/py/test/rsession/conftest.py Wed Jan 17 18:52:49 2007 @@ -1,7 +1,7 @@ import py -Option = py.test.Config.Option +Option = py.test.config.Option -option = py.test.Config.addoptions("boxing test options", +option = py.test.config.addoptions("boxing test options", Option('', '--skip-kill-test', action='store_true', default=False, dest='skip_kill_test', help='skip a certain test that checks for os.kill results, this ' Modified: py/branch/conftest/py/test/rsession/testing/test_config.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_config.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_config.py Wed Jan 17 18:52:49 2007 @@ -14,11 +14,11 @@ """) tmp2 = py.test.ensuretemp("xxx") args = [str(tmpdir)] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) session_options.bind_config(config) assert session_options.max_tasks_per_node == 5 assert remote_options.nice_level == 10 - config, args = py.test.Config.parse([str(tmp2)]) + config, args = py.test.config._reparse([str(tmp2)]) session_options.bind_config(config) assert session_options.max_tasks_per_node == \ SessionOptions.defaults['max_tasks_per_node'] Modified: py/branch/conftest/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_lsession.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_lsession.py Wed Jan 17 18:52:49 2007 @@ -32,7 +32,7 @@ # os.kill(os.getpid(), 11) """)) args = [str(tmpdir.join(dirname))] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) lsession = LSession(config) allevents = [] lsession.main(args, reporter=allevents.append, runner=runner) @@ -85,7 +85,7 @@ pdb.post_mortem = some_fun args = [str(tmpdir.join(subdir)), '--pdb'] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) lsession = LSession(config) allevents = [] try: @@ -116,7 +116,7 @@ pass """)) args = [str(tmpdir.join(subdir)), '-x'] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) assert config.option.exitfirst lsession = LSession(config) allevents = [] @@ -146,7 +146,7 @@ pass """)) args = [str(tmpdir.join("sub3")), '-k', 'test_one'] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) lsession = LSession(config) allevents = [] @@ -174,7 +174,7 @@ """)) args = [str(tmpdir.join("sub4"))] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) lsession = LSession(config) allevents = [] allruns = [] @@ -205,7 +205,7 @@ """)) args = [str(tmpdir.join("sub5"))] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) lsession = LSession(config) allevents = [] lsession.main(args, reporter=allevents.append, runner=box_runner) @@ -231,7 +231,7 @@ assert [0] == x """)) args = [str(tmpdir.join("sub6"))] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) lsession = LSession(config) allevents = [] lsession.main(args, reporter=allevents.append, runner=box_runner) @@ -252,7 +252,7 @@ print 3 """)) args = [str(tmpdir.join("sub7"))] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) lsession = LSession(config) allevents = [] lsession.main(args, reporter=allevents.append, runner=plain_runner) Modified: py/branch/conftest/py/test/rsession/testing/test_master.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_master.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_master.py Wed Jan 17 18:52:49 2007 @@ -18,7 +18,7 @@ def setup_module(mod): # bind an empty config - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) config.option.max_tasks_per_node = 10 session_options.bind_config(config) #assert not remote_options.exitfirst Modified: py/branch/conftest/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_reporter.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_reporter.py Wed Jan 17 18:52:49 2007 @@ -61,7 +61,7 @@ return outcomes def report_received_item_outcome(self): - config, args = py.test.Config.parse(["some_sub"]) + config, args = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) item = rootcol.getitembynames(funcpass_spec) @@ -81,7 +81,7 @@ return out def _test_module(self): - config, args = py.test.Config.parse(["some_sub"]) + config, args = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) funcitem = rootcol.getitembynames(funcpass_spec) @@ -119,7 +119,7 @@ """)) def boxfun(): - config, args = py.test.Config.parse([str(tmpdir)]) + config, args = py.test.config._reparse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) hosts = [HostInfo('localhost')] r = self.reporter(config, hosts) @@ -138,7 +138,7 @@ sadsadsa """)) def boxfun(): - config, args = py.test.Config.parse([str(tmpdir)]) + config, args = py.test.config._reparse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) host = HostInfo('localhost') r = self.reporter(config, [host]) @@ -157,7 +157,7 @@ tmpdir = py.test.ensuretemp("stilltogo") tmpdir.ensure("__init__.py") cap = py.io.OutErrCapture() - config, args = py.test.Config.parse([str(tmpdir)]) + config, args = py.test.config._reparse([str(tmpdir)]) hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) r.report(report.TestStarted(hosts)) Modified: py/branch/conftest/py/test/rsession/testing/test_rest.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_rest.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_rest.py Wed Jan 17 18:52:49 2007 @@ -27,7 +27,7 @@ class TestRestUnits(object): def setup_method(self, method): - config, args = py.test.Config.parse(["some_sub"]) + config, args = py.test.config._reparse(["some_sub"]) config.option.verbose = False self.config = config hosts = [HostInfo('localhost')] Modified: py/branch/conftest/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_rsession.py Wed Jan 17 18:52:49 2007 @@ -91,7 +91,7 @@ pass """)) args = [str(tmpdir.join("sub")), "-x"] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) rsession = RSession(config) allevents = [] rsession.main(args, reporter=allevents.append) @@ -121,7 +121,7 @@ assert __file__ != '%s' """ % str(tmpdir.join(subdir)))) args = [str(tmpdir.join(subdir))] - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) rsession = RSession(config, optimise_localhost=False) allevents = [] rsession.main(args, reporter=allevents.append) @@ -154,7 +154,7 @@ setup_events = [] teardown_events = [] - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) session_options.bind_config(config) nodes = init_hosts(setup_events.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) @@ -181,7 +181,7 @@ parse_directories(hosts) allevents = [] - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) session_options.bind_config(config) nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) @@ -223,7 +223,7 @@ allevents = [] hosts = [HostInfo('localhost')] parse_directories(hosts) - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) session_options.bind_config(config) d = remote_options.d.copy() d['custom'] = 'custom' @@ -264,7 +264,7 @@ assert os.nice(0) == 10 """) - config, args = py.test.Config.parse([str(tmpdir)]) + config, args = py.test.config._reparse([str(tmpdir)]) config.option.nice_level = 10 rsession = RSession(config) allevents = [] Modified: py/branch/conftest/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_slave.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_slave.py Wed Jan 17 18:52:49 2007 @@ -13,7 +13,7 @@ def setup_module(module): from py.__.test.rsession.rsession import session_options module.rootdir = py.path.local(py.__file__).dirpath().dirpath() - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) session_options.bind_config(config) # ---------------------------------------------------------------------- Modified: py/branch/conftest/py/test/rsession/testing/test_web.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_web.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_web.py Wed Jan 17 18:52:49 2007 @@ -13,7 +13,7 @@ py.test.skip("PyPy not found") def setup_module(mod): - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) from py.__.test.rsession.rsession import session_options session_options.bind_config(config) session_options.import_pypy = True Modified: py/branch/conftest/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_webjs.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_webjs.py Wed Jan 17 18:52:49 2007 @@ -15,7 +15,7 @@ mod.dom = dom dom.window = dom.Window(html) dom.document = dom.window.document - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) from py.__.test.rsession.rsession import session_options session_options.bind_config(config) session_options.import_pypy = True Modified: py/branch/conftest/py/test/terminal/remote.py ============================================================================== --- py/branch/conftest/py/test/terminal/remote.py (original) +++ py/branch/conftest/py/test/terminal/remote.py Wed Jan 17 18:52:49 2007 @@ -53,7 +53,7 @@ def failure_slave(channel): """ we run this on the other side. """ args, failures = channel.receive() - config, args = py.test.Config.parse(args) + config, args = py.test.config._reparse(args) # making this session definitely non-remote config.option.executable = py.std.sys.executable config.option.looponfailing = False Modified: py/branch/conftest/py/test/testing/test_collect.py ============================================================================== --- py/branch/conftest/py/test/testing/test_collect.py (original) +++ py/branch/conftest/py/test/testing/test_collect.py Wed Jan 17 18:52:49 2007 @@ -213,7 +213,7 @@ # test that running a session works from the directories old = o.chdir() try: - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main(args) @@ -223,7 +223,7 @@ old.chdir() # test that running the file directly works - config, args = py.test.Config.parse([str(checkfile)]) + config, args = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main(args) @@ -260,7 +260,7 @@ # test that running a session works from the directories old = o.chdir() try: - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main(args) @@ -270,7 +270,7 @@ old.chdir() # test that running the file directly works - config, args = py.test.Config.parse([str(checkfile)]) + config, args = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main(args) Modified: py/branch/conftest/py/test/testing/test_config.py ============================================================================== --- py/branch/conftest/py/test/testing/test_config.py (original) +++ py/branch/conftest/py/test/testing/test_config.py Wed Jan 17 18:52:49 2007 @@ -34,8 +34,8 @@ import py def _callback(option, opt_str, value, parser, *args, **kwargs): option.tdest = True - Option = py.test.Config.Option - option = py.test.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 @@ -45,7 +45,7 @@ """)) old = o.chdir() try: - config, args = py.test.Config.parse(['-g', '17']) + config, args = py.test.config._reparse(['-g', '17']) assert config.option.gdest == 17 finally: old.chdir() Modified: py/branch/conftest/py/test/testing/test_conftesthandle.py ============================================================================== --- py/branch/conftest/py/test/testing/test_conftesthandle.py (original) +++ py/branch/conftest/py/test/testing/test_conftesthandle.py Wed Jan 17 18:52:49 2007 @@ -8,8 +8,8 @@ # because "global" conftests are imported with a # mangled module name (related to their actual path) cls.basedir = d = py.test.ensuretemp(cls.__name__) - d.ensure("a/conftest.py").write("a=1 ; Directory = 3") - d.ensure("a/b/conftest.py").write("b=2 ; a = 1.5") + d.ensure("adir/conftest.py").write("a=1 ; Directory = 3") + d.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") def test_immediate_initialiation_and_incremental_are_the_same(self): conftest = Conftest() @@ -17,7 +17,7 @@ conftest.getconftestmodules(self.basedir) snap1 = len(conftest._path2confmods) #assert len(conftest._path2confmods) == snap1 + 1 - conftest.getconftestmodules(self.basedir.join('a')) + conftest.getconftestmodules(self.basedir.join('adir')) assert len(conftest._path2confmods) == snap1 + 1 conftest.getconftestmodules(self.basedir.join('b')) assert len(conftest._path2confmods) == snap1 + 2 @@ -29,7 +29,7 @@ assert conftest.rget("Module") == py.test.collect.Module def test_default_has_lower_prio(self): - conftest = Conftest(self.basedir.join("a")) + conftest = Conftest(self.basedir.join("adir")) assert conftest.rget('Directory') == 3 assert conftest.lget('Directory') == py.test.collect.Directory @@ -40,22 +40,22 @@ def test_value_access_by_path(self): conftest = Conftest(self.basedir) - assert conftest.rget("a", self.basedir.join('a')) == 1 - assert conftest.lget("a", self.basedir.join('a')) == 1 - assert conftest.rget("a", self.basedir.join('a', 'b')) == 1.5 - assert conftest.lget("a", self.basedir.join('a', 'b')) == 1 - assert conftest.lget("b", self.basedir.join('a', 'b')) == 2 + assert conftest.rget("a", self.basedir.join('adir')) == 1 + assert conftest.lget("a", self.basedir.join('adir')) == 1 + 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.test.raises(KeyError, 'conftest.lget("b", self.basedir.join("a"))' ) def test_value_access_with_init_one_conftest(self): - conftest = Conftest(self.basedir.join('a')) + conftest = Conftest(self.basedir.join('adir')) assert conftest.rget("a") == 1 assert conftest.lget("a") == 1 def test_value_access_with_init_two_conftests(self): - conftest = Conftest(self.basedir.join("a", "b")) + conftest = Conftest(self.basedir.join("adir", "b")) conftest.rget("a") == 1.5 conftest.lget("a") == 1 conftest.lget("b") == 1 @@ -64,8 +64,8 @@ def setup_class(cls): TestConftestValueAccessGlobal.__dict__['setup_class'](cls) d = cls.basedir - d.ensure("a/__init__.py") - d.ensure("a/b/__init__.py") + d.ensure("adir/__init__.py") + d.ensure("adir/b/__init__.py") Modified: py/branch/conftest/py/test/testing/test_session.py ============================================================================== --- py/branch/conftest/py/test/testing/test_session.py (original) +++ py/branch/conftest/py/test/testing/test_session.py Wed Jan 17 18:52:49 2007 @@ -6,7 +6,7 @@ class TestDefaultSession: def test_simple(self): - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main([datadir / 'filetest.py']) l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -15,7 +15,7 @@ assert not l def test_simple_verbose(self): - config, args = py.test.Config.parse(['--verbose']) + config, args = py.test.config._reparse(['--verbose']) session = config.getsessionclass()(config, py.std.sys.stdout) session.main([datadir / 'filetest.py']) l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -24,7 +24,7 @@ assert not l def test_simple_verbose_verbose(self): - config, args = py.test.Config.parse(['-v', '-v']) + config, args = py.test.config._reparse(['-v', '-v']) session = config.getsessionclass()(config, py.std.sys.stdout) session.main([datadir / 'filetest.py']) l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -35,17 +35,17 @@ def test_session_parsing(self): from py.__.test.terminal.terminal import TerminalSession from py.__.test.tkinter.reportsession import ReportSession - config, args = py.test.Config.parse(['--session=terminal']) + config, args = py.test.config._reparse(['--session=terminal']) assert issubclass(config.getsessionclass(), TerminalSession) - config, args = py.test.Config.parse(['--session=tkinter']) + config, args = py.test.config._reparse(['--session=tkinter']) assert issubclass(config.getsessionclass(), ReportSession) - config, args = py.test.Config.parse(['--tkinter']) + config, args = py.test.config._reparse(['--tkinter']) assert issubclass(config.getsessionclass(), ReportSession) class TestKeywordSelection: def test_select_simple(self): for keyword in ['test_one', 'est_on']: - config, args = py.test.Config.parse(['-k', keyword]) + config, args = py.test.config._reparse(['-k', keyword]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main([datadir / 'filetest.py']) l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -74,7 +74,7 @@ for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', 'TestClass test_2', 'xxx TestClass test_2',): f = StringIO() - config, args = py.test.Config.parse(['-k', keyword]) + config, args = py.test.config._reparse(['-k', keyword]) session = config.getsessionclass()(config, f) session.main([o]) print "keyword", repr(keyword) @@ -96,7 +96,7 @@ def setup_method(self, method): from py.__.test.terminal.terminal import TerminalSession self.file = StringIO() - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) self.session = TerminalSession(config, file=self.file) #print >>f, "session %r is setup for %r" %(self.session, method) #f.flush() @@ -362,7 +362,7 @@ assert 1 == 0 """)) print py.std.sys.executable - config, args = py.test.Config.parse( + config, args = py.test.config._reparse( ['--exec=' + py.std.sys.executable, str(o)]) assert config.option._remote @@ -384,7 +384,7 @@ assert 1 == 0 """)) print py.std.sys.executable - config, args = py.test.Config.parse(['--looponfailing', str(o)]) + config, args = py.test.config._reparse(['--looponfailing', str(o)]) assert config.option._remote cls = config.getsessionclass() out = py.std.Queue.Queue() Modified: py/branch/conftest/py/test/tkinter/backend.py ============================================================================== --- py/branch/conftest/py/test/tkinter/backend.py (original) +++ py/branch/conftest/py/test/tkinter/backend.py Wed Jan 17 18:52:49 2007 @@ -229,7 +229,7 @@ from py.__.test.tkinter.reportsession import ReportSession from py.__.test.terminal.remote import getfailureitems - config, testfiles = py.test.Config.parse(args) + config, testfiles = py.test.config._reparse(args) if tests: cols = getfailureitems(tests) else: Modified: py/branch/conftest/py/test/tkinter/testing/test_backend.py ============================================================================== --- py/branch/conftest/py/test/tkinter/testing/test_backend.py (original) +++ py/branch/conftest/py/test/tkinter/testing/test_backend.py Wed Jan 17 18:52:49 2007 @@ -178,7 +178,7 @@ assert l[0] is None def test_start_tests(self): - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) self.backend.start_tests(config = config, args = [str(datadir / 'filetest.py')], tests = []) Modified: py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py ============================================================================== --- py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py (original) +++ py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py Wed Jan 17 18:52:49 2007 @@ -5,7 +5,7 @@ datadir = py.magic.autopath().dirpath('data') def test_capture_out_err(): - config, args = py.test.Config.parse([]) + config, args = py.test.config._reparse([]) backend = ReportBackend() backend.start_tests(config = config, args = [str(datadir / 'filetest.py')], From hpk at codespeak.net Wed Jan 17 19:15:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 19:15:16 +0100 (CET) Subject: [py-svn] r36887 - in py/branch/conftest/py/test: . rsession Message-ID: <20070117181516.8BA6210070@code0.codespeak.net> Author: hpk Date: Wed Jan 17 19:15:13 2007 New Revision: 36887 Modified: py/branch/conftest/py/test/config.py py/branch/conftest/py/test/rsession/rest.py py/branch/conftest/py/test/rsession/rsession.py Log: remove the unneccessary getinitialvalue() which would look up with furthest away conftest file which does not make sense, at least it would be quite arbitrary when this lookup order is used and when it is not used. Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Wed Jan 17 19:15:13 2007 @@ -15,7 +15,14 @@ if basetemp is None: basetemp = py.path.local.make_numbered_dir(prefix='pytest-') return basetemp.ensure(string, dir=dir) - + +class CmdOptions(object): + """ pure container instance for holding cmdline options + as attributes. + """ + def __repr__(self): + return "" %(self.__dict__,) + class Config(object): """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option @@ -23,7 +30,7 @@ _parsed = False def __init__(self): - self.option = optparse.Values() + self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") @@ -38,9 +45,8 @@ self.conftest.lget('adddefaultoptions')() self._origargs = args cmdlineoption, remaining = self._parser.parse_args(args) - for name, value in vars(cmdlineoption).items(): - setattr(self.option, name, value) - fixoptions(self.option) + self.option.__dict__.update(vars(cmdlineoption)) + fixoptions(self.option) # XXX fixing should be moved to sessions if not remaining: remaining.append(py.std.os.getcwd()) self.remaining = remaining @@ -63,10 +69,6 @@ """ return self.conftest.rget(name, path) - def getinitialvalue(self, name): - """ return first value found in initial config modules. """ - return self.conftest.lget(name) - def getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. Modified: py/branch/conftest/py/test/rsession/rest.py ============================================================================== --- py/branch/conftest/py/test/rsession/rest.py (original) +++ py/branch/conftest/py/test/rsession/rest.py Wed Jan 17 19:15:13 2007 @@ -19,7 +19,7 @@ def get_linkwriter(self): if self.linkwriter is None: - self.linkwriter = self.config.getinitialvalue('linkwriter') + self.linkwriter = self.config.getvalue('linkwriter') return self.linkwriter def report_unknown(self, what): Modified: py/branch/conftest/py/test/rsession/rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/rsession.py (original) +++ py/branch/conftest/py/test/rsession/rsession.py Wed Jan 17 19:15:13 2007 @@ -45,7 +45,7 @@ def getvalue(self, opt): try: - return getattr(self.config.getinitialvalue('SessionOptions'), opt) + return getattr(self.config.getvalue('SessionOptions'), opt) except (KeyError, AttributeError): try: return self.defaults[opt] @@ -56,7 +56,7 @@ self.config = config # copy to remote session options try: - ses_opt = self.config.getinitialvalue('SessionOptions').__dict__ + ses_opt = self.config.getvalue('SessionOptions').__dict__ except KeyError: ses_opt = self.defaults for key in self.defaults: @@ -216,14 +216,14 @@ def read_distributed_config(self): try: - rsync_roots = self.config.getinitialvalue("distrsync_roots") + rsync_roots = self.config.getvalue("distrsync_roots") except: rsync_roots = None # all files and directories in the pkgdir sshhosts = [HostInfo(i) for i in - self.config.getinitialvalue("disthosts")] + self.config.getvalue("disthosts")] parse_directories(sshhosts) try: - remotepython = self.config.getinitialvalue("dist_remotepython") + remotepython = self.config.getvalue("dist_remotepython") except: remotepython = None return sshhosts, remotepython, rsync_roots From hpk at codespeak.net Wed Jan 17 19:56:35 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 19:56:35 +0100 (CET) Subject: [py-svn] r36891 - py/branch/conftest/py/test/testing Message-ID: <20070117185635.C22BA10070@code0.codespeak.net> Author: hpk Date: Wed Jan 17 19:56:34 2007 New Revision: 36891 Modified: py/branch/conftest/py/test/testing/test_config.py Log: clean up config tests, remove some dead code Modified: py/branch/conftest/py/test/testing/test_config.py ============================================================================== --- py/branch/conftest/py/test/testing/test_config.py (original) +++ py/branch/conftest/py/test/testing/test_config.py Wed Jan 17 19:56:34 2007 @@ -1,26 +1,5 @@ from __future__ import generators import py -from py.__.test import config - -class MyClass: - def getoptions(self): - yield config.Option('-v', action="count", dest="verbose", help="verbose") - -def xtest_verbose(): - obj = MyClass() - args = config.parseargs(['-v', 'hello'], obj) - assert args == ['hello'] - assert hasattr(obj, 'option') - assert hasattr(obj.option, 'verbose') - assert obj.option.verbose - -def xtest_verbose_default(): - obj = MyClass() - args = config.parseargs(['hello'], obj) - assert args, ['hello'] - assert hasattr(obj, 'option') - assert hasattr(obj.option, 'verbose') - assert not obj.option.verbose def test_tmpdir(): d1 = py.test.ensuretemp('hello') @@ -28,7 +7,7 @@ assert d1 == d2 assert d1.check(dir=1) -def test_config_options(): +def test_config_cmdline_options(): o = py.test.ensuretemp('configoptions') o.ensure("conftest.py").write(py.code.Source(""" import py @@ -46,34 +25,24 @@ old = o.chdir() try: config, args = py.test.config._reparse(['-g', '17']) - assert config.option.gdest == 17 finally: old.chdir() - -#def test_config_order(): -# from py.__.test import config -# o = py.test.ensuretemp('configorder') -# o.ensure('conftest.py').write('x=1 ; import py ; py._x = [x]') -# o.ensure('a/conftest.py').write('x=2 ; import py ; py._x.append(x)') -# o.ensure('a/b/c/conftest.py').write('x=3 ; import py ; py._x.append(x)') -# cfg = config.Config() -# cfg.readconfiguration(o) -# assert cfg.getfirst('x') == 1 -# assert py._x == [1] -# -# cfg = config.Config() -# cfg.readconfiguration(o.join('a/b/c')) -# assert cfg.getfirst('x') == 1 -# assert py._x == [1,2,3] -# -#def test_getconfigvalue(): -# from py.__.test import config -# cfg = config.Config() -# o = py.test.ensuretemp('configtest') -# o.ensure('conftest.py').write('x=1') -# assert cfg.getconfigvalue(o, 'x') == 1 -# py.test.raises(ValueError, "cfg.getconfigvalue(o, 'y')") -# + assert config.option.gdest == 17 + +def test_config_getvalue_honours_conftest(): + o = py.test.ensuretemp('testconfigget') + o.ensure("conftest.py").write("x=1") + o.ensure("sub", "conftest.py").write("x=2 ; y = 3") + config, args = py.test.config._reparse([str(o)]) + assert config.getvalue("x") == 1 + assert config.getvalue("x", o.join('sub')) == 2 + py.test.raises(KeyError, "config.getvalue('y')") + config, args = 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.test.raises(KeyError, 'config.getvalue("y", o)') + def test_siblingconftest_fails_maybe(): from py.__.test import config From hpk at codespeak.net Wed Jan 17 20:30:04 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 20:30:04 +0100 (CET) Subject: [py-svn] r36892 - py/branch/conftest/py/test Message-ID: <20070117193004.084721006F@code0.codespeak.net> Author: hpk Date: Wed Jan 17 20:30:03 2007 New Revision: 36892 Modified: py/branch/conftest/py/test/config.py Log: clarify usage of getvalue() Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Wed Jan 17 20:30:03 2007 @@ -66,6 +66,8 @@ def getvalue(self, name, path=None): """ return 'name' value looked up from the first conftest file found up the path (including the path itself). + if path is None, lookup the value in the initial + conftest modules found during command line parsing. """ return self.conftest.rget(name, path) From hpk at codespeak.net Wed Jan 17 20:34:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 17 Jan 2007 20:34:05 +0100 (CET) Subject: [py-svn] r36893 - in py/branch/conftest/py/test: . testing Message-ID: <20070117193405.37D2E1006F@code0.codespeak.net> Author: hpk Date: Wed Jan 17 20:34:04 2007 New Revision: 36893 Modified: py/branch/conftest/py/test/config.py py/branch/conftest/py/test/testing/test_config.py Log: test that trying to parse twice into a config object fails Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Wed Jan 17 20:34:04 2007 @@ -27,12 +27,12 @@ """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option conftest = Conftest() - _parsed = False def __init__(self): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") + self._parsed = False def parse(self, args): """ parse cmdline arguments into this config object. Modified: py/branch/conftest/py/test/testing/test_config.py ============================================================================== --- py/branch/conftest/py/test/testing/test_config.py (original) +++ py/branch/conftest/py/test/testing/test_config.py Wed Jan 17 20:34:04 2007 @@ -29,6 +29,11 @@ old.chdir() assert config.option.gdest == 17 +def test_parsing_again_fails(): + dir = py.test.ensuretemp("parsing_again_fails") + config, args = py.test.config._reparse([str(dir)]) + py.test.raises(AssertionError, "config.parse([])") + def test_config_getvalue_honours_conftest(): o = py.test.ensuretemp('testconfigget') o.ensure("conftest.py").write("x=1") From hpk at codespeak.net Thu Jan 18 05:11:54 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 05:11:54 +0100 (CET) Subject: [py-svn] r36894 - py/branch/conftest/py/doc Message-ID: <20070118041154.2828F10068@code0.codespeak.net> Author: hpk Date: Thu Jan 18 05:11:52 2007 New Revision: 36894 Modified: py/branch/conftest/py/doc/test.txt Log: some improvements to the testing doc, particularly to the distributed testing part (note that the code does not yet match the option names described in the docs) Modified: py/branch/conftest/py/doc/test.txt ============================================================================== --- py/branch/conftest/py/doc/test.txt (original) +++ py/branch/conftest/py/doc/test.txt Thu Jan 18 05:11:52 2007 @@ -358,6 +358,7 @@ .. _`basicpicture`: + Collecting and running tests / implementation remarks ====================================================== @@ -396,7 +397,7 @@ ------------------------------------------ The collecting process is iterative, i.e. the session -traverses the *collector tree*. Here is an example of such +traverses and generates a *collector tree*. Here is an example of such a tree, generated with the command ``py.test --collectonly py/xmlobj``:: @@ -477,7 +478,8 @@ on a path object (which returns the parent directory as a path object). -* insert this base directory into sys.path as its first item +* insert this base directory into the sys.path list + as its first element * import the root package @@ -485,10 +487,11 @@ at ``path`` ... * if the imported root package has a __package__ object - then call its ``getimportname(path)`` + then call ``__package__.getimportname(path)`` * otherwise use the relative path of the module path to - the base dir and turn slashes into dots. + 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 @@ -666,33 +669,17 @@ machines accessible through SSH, ``py.test`` can distribute tests across the machines. It does not require any particular installation on the remote machine sides as it uses `py.execnet`_ -mechanisms to distribute execution. - -*Warning*: support for distributed testing is in alpha state -and its mechanics and configuration options may change without -prior notice. - -Benefits --------- - -The main benefit for using distributed testing is speed. There -is some runtime overhead required to setup the test environment on the -remote machines, but if the project has a lot of tests and -several machines available, the speed up should be significant -in most cases. The more machines available, the better results -you will get. - -Automatically distributing the tests can be also be useful if you -need to use remote resouces which you do not have locally (perhaps -a server has much more RAM or disk available). -Of course, you could also just ssh into the remote server, -rsync or checkout your source code and run py.test -but that is obviously more cumbersome for the development -process. - -The distributed testing part of ``py.test`` also provides -a web server interface which tells you about the progress -of your distributed test run interactively. +mechanisms to distribute execution. Using distributed testing +can speed up your development process considerably and it +may also be useful where you need to use a remote server +that has more resources (e.g. RAM/diskspace) than your +local machine. + +*WARNING*: support for distributed testing is experimental, +its mechanics and configuration options may change without +prior notice. Particularly, not all reporting features +of the in-process py.test have been integrated into +the distributed testing approach. Requirements ------------ @@ -702,23 +689,23 @@ * ssh client * python -Remote requirements: +requirements for remote machines: * ssh daemon running * ssh keys setup to allow login without a password * python (2.3 or 2.4 should work) -* unix like machine (``os.fork``) +* unix like machine (reliance on ``os.fork``) -How it works ------------- +Hot to use it +----------------------- When you issue ``py.test --session=R`` then your computer becomes -the distributor of tests ("master") and will start distributing tests -to several machines which need to be specified in a ``conftest.py`` -file. +the distributor of tests ("master") and will start collecting +and distributing tests to several machines. The machines +need to be specified in a ``conftest.py`` file. -At start up, the master connects to each node using `py.execnet`_ and -its SSHGateways and *rsyncs* all specified python packages to all nodes. +At start up, the master connects to each node using `py.execnet.SshGateway`_ +and *rsyncs* all specified python packages to all nodes. Then the master collects all of the tests and immediately sends test item descriptions to its connected nodes. Each node has a local queue of tests to run and begins to execute the tests, following the setup and teardown @@ -730,6 +717,7 @@ from the testing nodes: command line, rest output and ajaxy web based. .. _`py.execnet`: execnet.html +.. _`py.execnet.SshGateway`: execnet.html Differences from local tests ---------------------------- @@ -745,19 +733,21 @@ The options that you need to specify in that conftest.py file are: -* `disthosts` - a list of ssh addresses (including a specific path if it +* `dist_hosts` - a list of ssh addresses (including a specific path if it should be different than the default: ``$HOME/pytestcache-hostname``) -* `distrsync_roots` - a list of packages to copy to the remote machines. +* `dist_rsync_roots` - a list of packages to copy to the remote machines. * `dist_remotepython` - the remote python to run. * `SessionOptions` - containing some specific tuning options Sample configuration:: - disthosts = ['localhost', 'user at someserver:/tmp/somedir'] - distrsync_roots = ['pypy', 'py'] + dist_hosts = ['localhost', 'user at someserver:/tmp/somedir'] + dist_rsync_roots = ['pypy', 'py'] dist_remotepython = 'python2.4' - class SessionOptions: - nice_level = 10 + dist_nicelevel = 10 + dist_boxing = True + dist_maxwait = 100 + dist_taskspernode = 10 Running server is done by ``-w`` command line option or ``--startserver`` (the former might change at some point due to conflicts). @@ -767,8 +757,8 @@ * `nice_level` - Level of nice under which tests are run * `runner_policy` - (for `LSession` only) - contains policy for the test boxig. `"plain_runner"` means no boxing, `"box_runner"` means boxing. -* `waittime` - Default waiting time for remote host to finish tests (after - that period we consider node as dead, default to 100s). +* `waittime` - Default maximum waiting time for remote host to execute + one test. * `max_tasks_per_node` - Maximum number of tasks which can be send to one node. * `import_pypy` - Flag to control pypy importing for js regeneration (defaults to False) From hpk at codespeak.net Thu Jan 18 05:31:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 05:31:11 +0100 (CET) Subject: [py-svn] r36895 - in py/branch/conftest/py: doc test Message-ID: <20070118043111.C5CDC10061@code0.codespeak.net> Author: hpk Date: Thu Jan 18 05:31:10 2007 New Revision: 36895 Modified: py/branch/conftest/py/doc/test.txt py/branch/conftest/py/test/collect.py py/branch/conftest/py/test/session.py Log: shift some documentation to docstrings, fix an inheritance problem uncovered by that. Modified: py/branch/conftest/py/doc/test.txt ============================================================================== --- py/branch/conftest/py/doc/test.txt (original) +++ py/branch/conftest/py/doc/test.txt Thu Jan 18 05:31:10 2007 @@ -422,38 +422,6 @@ .. _`collector API`: -collector API invoked by sessions ---------------------------------- - -Apart from initialization the default session object invokes -a very uniform API on collectors and test items: - -*colitem.run()* - - 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. The session - object guards its calls to ``colitem.run()`` in its - ``session.runtraced(colitem)`` method, including - catching of stdout. - -*colitem.join(name)* - - return a child item from the given name. Usually the - session feeds the join method with each name obtained - from ``colitem.run()``. If the return value is None - it means the ``colitem`` was not able to resolve - with the given name. - -*colitem.parent* - - attribute pointing to the parent collector. - -*colitem.name* - - name of this sub item. This is the name that is - passed to ``colitem.join()`` above. - test items are collectors as well --------------------------------- @@ -507,48 +475,16 @@ and test classes and methods. Test functions and methods are prefixed ``test`` by default. Test classes must start with a capitalized ``Test`` prefix. - -Reporting hooks of the session object -------------------------------------- - -Part of the default session API deals with reporting -test outcomes and collection details. These methods -are reponsible for representing the testing process -to the user or other programs: - -*session.header()* - - invoked once by ``session.run()`` before the whole - test session starts. -*session.footer()* - - invoked once by ``session.run()`` after the collection - and running process finished. - - -*session.start(colitem)* - - invoked before each ``colitem.run()`` invocation - - -*session.finish(colitem, outcome)* - - invoked after each ``colitem.run()`` invocation +Customizing the testing process +=============================== +writing conftest.py files +----------------------------------- -XXX the names of these session objects are likely to change -soon because the session object now has too many names that -aren't easily distinguishable regarding their purposes. It is -also likely that collectors/test items will become more -self-responsible for presenting outcomes in textual ways. -Currently, session object have to know too much about the -representation of failures/successes to the user which makes -it harder than neccessary to write custom test items. +XXX -Customizing the testing process -=============================== customizing the collecting and running process ----------------------------------------------- Modified: py/branch/conftest/py/test/collect.py ============================================================================== --- py/branch/conftest/py/test/collect.py (original) +++ py/branch/conftest/py/test/collect.py Thu Jan 18 05:31:10 2007 @@ -55,6 +55,14 @@ return current class Collector(object): + """ Collector instances are iteratively generated + (through their run() and join() methods) + and form a tree. attributes:: + + parent: attribute pointing to the parent collector + (or None if it is the root collector) + name: basename of this collector object + """ def __init__(self, name, parent=None): self.name = name self.parent = parent @@ -113,6 +121,25 @@ #print "cmp", s1, s2 return cmp(s1, s2) + + def run(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. The session + object guards its calls to ``colitem.run()`` in its + ``session.runtraced(colitem)`` method, including + catching of stdout. + """ + raise NotImplementedError("abstract") + + def join(self, name): + """ return a child item for the given name. Usually the + session feeds the join method with each name obtained + from ``colitem.run()``. If the return value is None + it means the ``colitem`` was not able to resolve + with the given name. + """ + def obj(): def fget(self): try: @@ -454,7 +481,7 @@ Collector.Function.__get__(self)) # XXX for python 2.2 Function = property(Function) -class Generator(Collector, PyCollectorMixin): +class Generator(PyCollectorMixin, Collector): def run(self): self._prepare() itemlist = self._name2items Modified: py/branch/conftest/py/test/session.py ============================================================================== --- py/branch/conftest/py/test/session.py (original) +++ py/branch/conftest/py/test/session.py Thu Jan 18 05:31:10 2007 @@ -13,20 +13,21 @@ return False def header(self, colitems): - """ setup any neccessary resources. """ + """ setup any neccessary resources ahead of the test run. """ if not self.config.option.nomagic: py.magic.invoke(assertion=1) def footer(self, colitems): - """ teardown any resources we know about. """ + """ teardown any resources after a test run. """ py.test.Function.state.teardown_all() if not self.config.option.nomagic: py.magic.revoke(assertion=1) def start(self, colitem): - pass + """ hook invoked before each colitem.run() invocation. """ def finish(self, colitem, outcome): + """ hook invoked after each colitem.run() invocation. """ self._memo.append((colitem, outcome)) def startiteration(self, colitem, subitems): From hpk at codespeak.net Thu Jan 18 05:50:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 05:50:52 +0100 (CET) Subject: [py-svn] r36896 - in py/branch/conftest/py/test/testing: . data Message-ID: <20070118045052.3372410068@code0.codespeak.net> Author: hpk Date: Thu Jan 18 05:50:48 2007 New Revision: 36896 Added: py/branch/conftest/py/test/testing/setupdata.py Removed: py/branch/conftest/py/test/testing/data/ Modified: py/branch/conftest/py/test/testing/test_collect.py py/branch/conftest/py/test/testing/test_session.py Log: setup some test data on the fly instead of keeping it version controled. Added: py/branch/conftest/py/test/testing/setupdata.py ============================================================================== --- (empty file) +++ py/branch/conftest/py/test/testing/setupdata.py Thu Jan 18 05:50:48 2007 @@ -0,0 +1,93 @@ +import py + +def setupdatadir(): + datadir = py.test.ensuretemp("datadir") + if not datadir.listdir(): + for name, content in namecontent: + datadir.join(name).write(content) + return datadir + +namecontent = [ +('disabled_module.py', py.code.Source(''' + disabled = True + + def setup_module(mod): + raise ValueError + + class TestClassOne: + def test_func(self): + raise ValueError + + class TestClassTwo: + def setup_class(cls): + raise ValueError + def test_func(self): + raise ValueError +''')), + +('brokenrepr.py', py.code.Source(''' + + import py + + class BrokenRepr1: + """A broken class with lots of broken methods. Let's try to make the test framework + immune to these.""" + foo=0 + def __repr__(self): + raise Exception("Ha Ha fooled you, I'm a broken repr().") + + class BrokenRepr2: + """A broken class with lots of broken methods. Let's try to make the test framework + immune to these.""" + 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.test.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 + ''')), + + ('failingimport.py', py.code.Source(''' + + import gruetzelmuetzel + + ''')), + + ('filetest.py', py.code.Source(''' + def test_one(): + assert 42 == 43 + + class TestClass(object): + def test_method_one(self): + assert 42 == 43 + + ''')), + + ('testspecial_importerror.py', py.code.Source(''' + + import asdasd + + ''')), + + ('disabled.py', py.code.Source(''' + class TestDisabled: + disabled = True + def test_method(self): + pass + ''')), +] + + + Modified: py/branch/conftest/py/test/testing/test_collect.py ============================================================================== --- py/branch/conftest/py/test/testing/test_collect.py (original) +++ py/branch/conftest/py/test/testing/test_collect.py Thu Jan 18 05:50:48 2007 @@ -1,13 +1,13 @@ from __future__ import generators import py -datadir = py.magic.autopath().dirpath('data') +from setupdata import setupdatadir -tmpdir = py.test.ensuretemp('test_collect') +def setup_module(mod): + mod.datadir = setupdatadir() + mod.tmpdir = py.test.ensuretemp('test_collect') def test_failing_import_execfile(): - fn = datadir / 'failingimport.py' - dest = tmpdir.join('failing_import.py') - fn.copy(dest) + dest = datadir / 'failingimport.py' col = py.test.collect.Module(dest) py.test.raises(ImportError, col.run) py.test.raises(ImportError, col.run) Modified: py/branch/conftest/py/test/testing/test_session.py ============================================================================== --- py/branch/conftest/py/test/testing/test_session.py (original) +++ py/branch/conftest/py/test/testing/test_session.py Thu Jan 18 05:50:48 2007 @@ -1,8 +1,9 @@ import py -datadir = py.magic.autopath().dirpath('data') -from cStringIO import StringIO +from setupdata import setupdatadir -tmpdir = py.test.ensuretemp('test_drive') +def setup_module(mod): + mod.datadir = setupdatadir() + mod.tmpdir = py.test.ensuretemp(mod.__name__) class TestDefaultSession: def test_simple(self): @@ -73,7 +74,7 @@ """)) for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', 'TestClass test_2', 'xxx TestClass test_2',): - f = StringIO() + f = py.std.StringIO.StringIO() config, args = py.test.config._reparse(['-k', keyword]) session = config.getsessionclass()(config, f) session.main([o]) @@ -95,7 +96,7 @@ def setup_method(self, method): from py.__.test.terminal.terminal import TerminalSession - self.file = StringIO() + self.file = py.std.StringIO.StringIO() config, args = py.test.config._reparse([]) self.session = TerminalSession(config, file=self.file) #print >>f, "session %r is setup for %r" %(self.session, method) From hpk at codespeak.net Thu Jan 18 06:30:39 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 06:30:39 +0100 (CET) Subject: [py-svn] r36897 - py/branch/conftest/py/test Message-ID: <20070118053039.AE04110068@code0.codespeak.net> Author: hpk Date: Thu Jan 18 06:30:38 2007 New Revision: 36897 Modified: py/branch/conftest/py/test/collect.py py/branch/conftest/py/test/config.py Log: small option cleanup: have --usepdb imply --nocapture directly Modified: py/branch/conftest/py/test/collect.py ============================================================================== --- py/branch/conftest/py/test/collect.py (original) +++ py/branch/conftest/py/test/collect.py Thu Jan 18 06:30:38 2007 @@ -391,7 +391,7 @@ return res def startcapture(self): - if not self.option.nocapture and not self.option.usepdb: + if not self.option.nocapture: assert not hasattr(self, '_capture') #self._capture = py.io.OutErrCapture() # XXX integrate this into py.io / refactor Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Thu Jan 18 06:30:38 2007 @@ -122,6 +122,11 @@ else: option.executable = py.std.sys.executable + if option.usepdb: + if not option.nocapture: + print "--usepdb currently implies --nocapture" + option.nocapture = True + # make information available about wether we should/will be remote option._remote = remote or option.looponfailing option._fromremote = False From hpk at codespeak.net Thu Jan 18 06:32:06 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 06:32:06 +0100 (CET) Subject: [py-svn] r36898 - py/branch/conftest/py/test Message-ID: <20070118053206.F0E2E10068@code0.codespeak.net> Author: hpk Date: Thu Jan 18 06:32:06 2007 New Revision: 36898 Modified: py/branch/conftest/py/test/item.py Log: also check less here Modified: py/branch/conftest/py/test/item.py ============================================================================== --- py/branch/conftest/py/test/item.py (original) +++ py/branch/conftest/py/test/item.py Thu Jan 18 06:32:06 2007 @@ -31,7 +31,7 @@ class Item(py.test.collect.Collector): def startcapture(self): - if not self.option.nocapture and not self.option.usepdb: + if not self.option.nocapture: # XXX refactor integrate capturing #self._capture = py.io.OutErrCapture() from py.__.misc.simplecapture import SimpleOutErrCapture From hpk at codespeak.net Thu Jan 18 07:04:57 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 07:04:57 +0100 (CET) Subject: [py-svn] r36899 - in py/branch/conftest/py: doc doc/example/pytest test test/rsession test/rsession/testing test/terminal test/testing test/tkinter Message-ID: <20070118060457.40DB81006E@code0.codespeak.net> Author: hpk Date: Thu Jan 18 07:04:53 2007 New Revision: 36899 Added: py/branch/conftest/py/test/todo-cleanup.txt Modified: py/branch/conftest/py/doc/example/pytest/test_failures.py py/branch/conftest/py/doc/test_conftest.py py/branch/conftest/py/test/cmdline.py py/branch/conftest/py/test/config.py py/branch/conftest/py/test/rsession/rsession.py py/branch/conftest/py/test/rsession/testing/test_rsession.py py/branch/conftest/py/test/session.py py/branch/conftest/py/test/terminal/remote.py py/branch/conftest/py/test/terminal/terminal.py py/branch/conftest/py/test/testing/test_collect.py py/branch/conftest/py/test/testing/test_session.py py/branch/conftest/py/test/tkinter/backend.py Log: removing redundancy in calling session.main() which previously got command line parameters that are already present in the config object. note the new py/test/todo-cleanup.txt which lists one issue that results from this checkin (--looponfailing not working properly) Modified: py/branch/conftest/py/doc/example/pytest/test_failures.py ============================================================================== --- py/branch/conftest/py/doc/example/pytest/test_failures.py (original) +++ py/branch/conftest/py/doc/example/pytest/test_failures.py Thu Jan 18 07:04:53 2007 @@ -3,9 +3,9 @@ failure_demo = py.magic.autopath().dirpath('failure_demo.py') def test_failure_demo_fails_properly(): - config, args = py.test.config._reparse([]) + config, args = py.test.config._reparse([failure_demo]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([failure_demo]) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 21 l = session.getitemoutcomepairs(py.test.Item.Passed) Modified: py/branch/conftest/py/doc/test_conftest.py ============================================================================== --- py/branch/conftest/py/doc/test_conftest.py (original) +++ py/branch/conftest/py/doc/test_conftest.py Thu Jan 18 07:04:53 2007 @@ -28,9 +28,9 @@ end """)) - config, args = py.test.config._reparse([str(xtxt)]) + config, args = py.test.config._reparse([xtxt]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([xtxt]) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 l = session.getitemoutcomepairs(py.test.Item.Passed) @@ -46,9 +46,9 @@ .. _`blah`: javascript:some_function() """)) - config, args = py.test.config._reparse([str(xtxt)]) + config, args = py.test.config._reparse([xtxt]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([xtxt]) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 l = session.getitemoutcomepairs(py.test.Item.Passed) Modified: py/branch/conftest/py/test/cmdline.py ============================================================================== --- py/branch/conftest/py/test/cmdline.py (original) +++ py/branch/conftest/py/test/cmdline.py Thu Jan 18 07:04:53 2007 @@ -34,7 +34,7 @@ session = sessionclass(config) try: - failures = session.main(args) + failures = session.main() if failures: raise SystemExit, 1 except KeyboardInterrupt: Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Thu Jan 18 07:04:53 2007 @@ -43,6 +43,7 @@ self._parsed = True self.conftest.setinitial(args) self.conftest.lget('adddefaultoptions')() + args = [str(x) for x in args] self._origargs = args cmdlineoption, remaining = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) Modified: py/branch/conftest/py/test/rsession/rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/rsession.py (original) +++ py/branch/conftest/py/test/rsession/rsession.py Thu Jan 18 07:04:53 2007 @@ -184,10 +184,9 @@ class RSession(AbstractSession): """ Remote version of session """ - def main(self, args, reporter=None): + def main(self, reporter=None): """ main loop for running tests. """ - if not args: - args = [py.path.local()] + args = self.config.remaining session_options.bind_config(self.config) sshhosts, remotepython, rsync_roots = self.read_distributed_config() Modified: py/branch/conftest/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_rsession.py Thu Jan 18 07:04:53 2007 @@ -94,7 +94,7 @@ config, args = py.test.config._reparse(args) rsession = RSession(config) allevents = [] - rsession.main(args, reporter=allevents.append) + rsession.main(reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 3 @@ -124,7 +124,7 @@ config, args = py.test.config._reparse(args) rsession = RSession(config, optimise_localhost=False) allevents = [] - rsession.main(args, reporter=allevents.append) + rsession.main(reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -264,11 +264,11 @@ assert os.nice(0) == 10 """) - config, args = py.test.config._reparse([str(tmpdir)]) + config, args = py.test.config._reparse([tmpdir]) config.option.nice_level = 10 rsession = RSession(config) allevents = [] - rsession.main(args, reporter=allevents.append) + rsession.main(reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] passevents = [x for x in testevents if x.outcome.passed] Modified: py/branch/conftest/py/test/session.py ============================================================================== --- py/branch/conftest/py/test/session.py (original) +++ py/branch/conftest/py/test/session.py Thu Jan 18 07:04:53 2007 @@ -36,9 +36,9 @@ def getitemoutcomepairs(self, cls): return [x for x in self._memo if isinstance(x[1], cls)] - def main(self, args): + def main(self): """ main loop for running tests. """ - colitems = self._map2colitems(args) + colitems = self._map2colitems(self.config.remaining) try: self.header(colitems) try: Modified: py/branch/conftest/py/test/terminal/remote.py ============================================================================== --- py/branch/conftest/py/test/terminal/remote.py (original) +++ py/branch/conftest/py/test/terminal/remote.py Thu Jan 18 07:04:53 2007 @@ -66,7 +66,7 @@ #print "processing", cols session = config.getsessionclass()(config) session.shouldclose = channel.isclosed - failures = session.main(cols) + failures = session.main() channel.send(failures) def getfailureitems(failures): Modified: py/branch/conftest/py/test/terminal/terminal.py ============================================================================== --- py/branch/conftest/py/test/terminal/terminal.py (original) +++ py/branch/conftest/py/test/terminal/terminal.py Thu Jan 18 07:04:53 2007 @@ -28,12 +28,12 @@ self._started = {} self._opencollectors = [] - def main(self, args): + def main(self): if self.config.option._remote: from py.__.test.terminal import remote return remote.main(self.config, self._file, self.config._origargs) else: - return super(TerminalSession, self).main(args) + return super(TerminalSession, self).main() # --------------------- # PROGRESS information Modified: py/branch/conftest/py/test/testing/test_collect.py ============================================================================== --- py/branch/conftest/py/test/testing/test_collect.py (original) +++ py/branch/conftest/py/test/testing/test_collect.py Thu Jan 18 07:04:53 2007 @@ -216,7 +216,7 @@ config, args = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 2 finally: @@ -226,7 +226,7 @@ config, args = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 2 @@ -263,7 +263,7 @@ config, args = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 finally: @@ -273,7 +273,7 @@ config, args = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 Modified: py/branch/conftest/py/test/testing/test_session.py ============================================================================== --- py/branch/conftest/py/test/testing/test_session.py (original) +++ py/branch/conftest/py/test/testing/test_session.py Thu Jan 18 07:04:53 2007 @@ -7,27 +7,29 @@ class TestDefaultSession: def test_simple(self): - config, args = py.test.config._reparse([]) + config, args = py.test.config._reparse([datadir/'filetest.py']) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l def test_simple_verbose(self): - config, args = py.test.config._reparse(['--verbose']) + config, args = py.test.config._reparse([datadir/'filetest.py', + '--verbose']) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l def test_simple_verbose_verbose(self): - config, args = py.test.config._reparse(['-v', '-v']) + config, args = py.test.config._reparse([datadir/'filetest.py', + '-v', '-v']) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 l = session.getitemoutcomepairs(py.test.Item.Passed) @@ -46,9 +48,10 @@ class TestKeywordSelection: def test_select_simple(self): for keyword in ['test_one', 'est_on']: - config, args = py.test.config._reparse(['-k', keyword]) + config, args = py.test.config._reparse([datadir/'filetest.py', + '-k', keyword]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 item = l[0][0] @@ -75,9 +78,9 @@ for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', 'TestClass test_2', 'xxx TestClass test_2',): f = py.std.StringIO.StringIO() - config, args = py.test.config._reparse(['-k', keyword]) + config, args = py.test.config._reparse([o, '-k', keyword]) session = config.getsessionclass()(config, f) - session.main([o]) + session.main() print "keyword", repr(keyword) l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 @@ -94,33 +97,23 @@ def teardown_class(cls): (datadir / 'syntax_error.py').remove() - def setup_method(self, method): + def mainsession(self, *args): from py.__.test.terminal.terminal import TerminalSession self.file = py.std.StringIO.StringIO() - config, args = py.test.config._reparse([]) - self.session = TerminalSession(config, file=self.file) - #print >>f, "session %r is setup for %r" %(self.session, method) - #f.flush() - #print "session is setup", self.session - - #def teardown_method(self, method): - #print >>f, "session %r finished method %r" % (self.session, method) - #f.flush() + config, args = py.test.config._reparse(list(args)) + session = TerminalSession(config, file=self.file) + session.main() + return session def test_terminal(self): - self.session.main([datadir / 'filetest.py']) + session = self.mainsession(datadir / 'filetest.py') out = self.file.getvalue() - #print "memo" - #print self.session._memo - #print "out" - #print out - l = self.session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 assert out.find('2 failed') != -1 def test_syntax_error_module(self): - session = self.session - session.main([str(datadir / 'syntax_error.py')]) + session = self.mainsession(datadir / 'syntax_error.py') l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 out = self.file.getvalue() @@ -128,18 +121,18 @@ assert out.find(str('not python')) != -1 def test_exit_first_problem(self): - session = self.session - session.config.option.exitfirst = True - session.main([str(datadir / 'filetest.py')]) + session = self.mainsession("--exitfirst", + datadir / 'filetest.py') + assert session.config.option.exitfirst l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l def test_collectonly(self): - session = self.session - session.config.option.collectonly = True - session.main([str(datadir / 'filetest.py')]) + session = self.mainsession("--collectonly", + datadir / 'filetest.py') + assert session.config.option.collectonly out = self.file.getvalue() #print out l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -163,11 +156,7 @@ f() f() """)) - #print "hello" - session = self.session - #session.config.option.nocapture = True - print "calling main", o - session.main([str(o)]) + session = self.mainsession(o) print "back from main", o out = self.file.getvalue() #print out @@ -181,8 +170,7 @@ def test_1(): yield None """)) - session = self.session - session.main([o]) + session = self.mainsession(o) out = self.file.getvalue() #print out i = out.find('TypeError') @@ -210,8 +198,7 @@ def finishcapture(self): self._testmycapture = self._mycapture.reset() """)) - session = self.session - session.main([o]) + session = self.mainsession(o) l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 item = l[0][0] @@ -237,8 +224,7 @@ def test_raises_doesnt(): py.test.raises(ValueError, int, "3") """)) - session = self.session - session.main([o]) + session = self.mainsession(o) out = self.file.getvalue() if not out.find("DID NOT RAISE") != -1: print out @@ -267,8 +253,7 @@ assert self.reslist == [1,2,1,2,3] """)) - session = self.session - session.main([o]) + session = self.mainsession(o) l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 l = session.getitemoutcomepairs(py.test.Item.Passed) @@ -291,16 +276,14 @@ import does_not_work a = 1 """)) - session = self.session - session.main([o]) + session = self.mainsession(o) l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 item, outcome = l[0] assert str(outcome.excinfo).find('does_not_work') != -1 def test_safe_repr(self): - session = self.session - session.main([str(datadir / 'brokenrepr.py')]) + session = self.mainsession(datadir/'brokenrepr.py') out = self.file.getvalue() print 'Output of simulated "py.test brokenrepr.py":' print out @@ -322,8 +305,7 @@ 'c']) """) tfile.write(source) - session = self.session - session.main([o]) + session = self.mainsession(o) out = self.file.getvalue() print 'Output of simulated "py.test test_correct_line.py":' print out @@ -365,12 +347,12 @@ print py.std.sys.executable config, args = py.test.config._reparse( ['--exec=' + py.std.sys.executable, - str(o)]) + o]) assert config.option._remote cls = config.getsessionclass() out = [] # out = py.std.Queue.Queue() session = cls(config, out.append) - session.main([o]) + session.main() for s in out: if s.find('1 failed') != -1: break @@ -391,7 +373,7 @@ out = py.std.Queue.Queue() session = cls(config, out.put) pool = py._thread.WorkerPool() - reply = pool.dispatch(session.main, [str(o)]) + reply = pool.dispatch(session.main) while 1: s = out.get() if s.find('1 failed') != -1: Modified: py/branch/conftest/py/test/tkinter/backend.py ============================================================================== --- py/branch/conftest/py/test/tkinter/backend.py (original) +++ py/branch/conftest/py/test/tkinter/backend.py Thu Jan 18 07:04:53 2007 @@ -236,5 +236,5 @@ cols = testfiles session = ReportSession(config = config, channel=channel) session.shouldclose = channel.isclosed - session.main(cols) + session.main() Added: py/branch/conftest/py/test/todo-cleanup.txt ============================================================================== --- (empty file) +++ py/branch/conftest/py/test/todo-cleanup.txt Thu Jan 18 07:04:53 2007 @@ -0,0 +1,10 @@ + + +fix --looponfailing: currently in case of failures +all tests are re-run. the problem was introduced +while cleaning up session.main() calls ... +the setup of remote and local config objects +and collectors needs to be reviewed anyway +(and the terminalsession and rsession handling +of such config object should be unified with +respect to this configuration/failure communication) From hpk at codespeak.net Thu Jan 18 07:18:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 07:18:26 +0100 (CET) Subject: [py-svn] r36900 - in py/branch/conftest/py: doc doc/example/pytest test test/rsession test/rsession/testing test/terminal test/testing test/tkinter test/tkinter/testing Message-ID: <20070118061826.429641006E@code0.codespeak.net> Author: hpk Date: Thu Jan 18 07:18:23 2007 New Revision: 36900 Modified: py/branch/conftest/py/doc/example/pytest/test_failures.py py/branch/conftest/py/doc/test_conftest.py py/branch/conftest/py/test/config.py py/branch/conftest/py/test/rsession/rsession.py py/branch/conftest/py/test/rsession/testing/test_config.py py/branch/conftest/py/test/rsession/testing/test_lsession.py py/branch/conftest/py/test/rsession/testing/test_master.py py/branch/conftest/py/test/rsession/testing/test_reporter.py py/branch/conftest/py/test/rsession/testing/test_rest.py py/branch/conftest/py/test/rsession/testing/test_rsession.py py/branch/conftest/py/test/rsession/testing/test_slave.py py/branch/conftest/py/test/rsession/testing/test_web.py py/branch/conftest/py/test/rsession/testing/test_webjs.py py/branch/conftest/py/test/terminal/remote.py py/branch/conftest/py/test/testing/test_collect.py py/branch/conftest/py/test/testing/test_config.py py/branch/conftest/py/test/testing/test_session.py py/branch/conftest/py/test/tkinter/backend.py py/branch/conftest/py/test/tkinter/testing/test_backend.py py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py Log: config._reparse() only returns a config object now (and config.remaining contains the command line arguments) -> fixing various occassions of redundancy Modified: py/branch/conftest/py/doc/example/pytest/test_failures.py ============================================================================== --- py/branch/conftest/py/doc/example/pytest/test_failures.py (original) +++ py/branch/conftest/py/doc/example/pytest/test_failures.py Thu Jan 18 07:18:23 2007 @@ -3,7 +3,7 @@ failure_demo = py.magic.autopath().dirpath('failure_demo.py') def test_failure_demo_fails_properly(): - config, args = py.test.config._reparse([failure_demo]) + config = py.test.config._reparse([failure_demo]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) Modified: py/branch/conftest/py/doc/test_conftest.py ============================================================================== --- py/branch/conftest/py/doc/test_conftest.py (original) +++ py/branch/conftest/py/doc/test_conftest.py Thu Jan 18 07:18:23 2007 @@ -28,7 +28,7 @@ end """)) - config, args = py.test.config._reparse([xtxt]) + config = py.test.config._reparse([xtxt]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -46,7 +46,7 @@ .. _`blah`: javascript:some_function() """)) - config, args = py.test.config._reparse([xtxt]) + config = py.test.config._reparse([xtxt]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) Modified: py/branch/conftest/py/test/config.py ============================================================================== --- py/branch/conftest/py/test/config.py (original) +++ py/branch/conftest/py/test/config.py Thu Jan 18 07:18:23 2007 @@ -93,7 +93,7 @@ try: config = py.test.config = Config() config.parse(args) - return config, config.remaining + return config finally: config = py.test.config = oldconfig Modified: py/branch/conftest/py/test/rsession/rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/rsession.py (original) +++ py/branch/conftest/py/test/rsession/rsession.py Thu Jan 18 07:18:23 2007 @@ -245,11 +245,9 @@ class LSession(AbstractSession): """ Local version of session """ - def main(self, args, reporter=None, runner=None): + def main(self, reporter=None, runner=None): # check out if used options makes any sense - - if not args: - args = [py.path.local()] + args = self.config.remaining sshhosts = [HostInfo('localhost')] # this is just an info to reporter Modified: py/branch/conftest/py/test/rsession/testing/test_config.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_config.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_config.py Thu Jan 18 07:18:23 2007 @@ -14,11 +14,11 @@ """) tmp2 = py.test.ensuretemp("xxx") args = [str(tmpdir)] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) session_options.bind_config(config) assert session_options.max_tasks_per_node == 5 assert remote_options.nice_level == 10 - config, args = py.test.config._reparse([str(tmp2)]) + config = py.test.config._reparse([str(tmp2)]) session_options.bind_config(config) assert session_options.max_tasks_per_node == \ SessionOptions.defaults['max_tasks_per_node'] Modified: py/branch/conftest/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_lsession.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_lsession.py Thu Jan 18 07:18:23 2007 @@ -32,10 +32,10 @@ # os.kill(os.getpid(), 11) """)) args = [str(tmpdir.join(dirname))] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=runner) + lsession.main(reporter=allevents.append, runner=runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -85,11 +85,11 @@ pdb.post_mortem = some_fun args = [str(tmpdir.join(subdir)), '--pdb'] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] try: - lsession.main(args, reporter=allevents.append, runner=plain_runner) + lsession.main(reporter=allevents.append, runner=plain_runner) except SystemExit: pass else: @@ -116,12 +116,12 @@ pass """)) args = [str(tmpdir.join(subdir)), '-x'] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) assert config.option.exitfirst lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -146,11 +146,11 @@ pass """)) args = [str(tmpdir.join("sub3")), '-k', 'test_one'] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -174,7 +174,7 @@ """)) args = [str(tmpdir.join("sub4"))] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] allruns = [] @@ -182,7 +182,7 @@ allruns.append(item) item.passed = True return item - lsession.main(args, reporter=allevents.append, runner=dummy_runner) + lsession.main(reporter=allevents.append, runner=dummy_runner) assert len(allruns) == 4 testevents = [x for x in allevents @@ -205,10 +205,10 @@ """)) args = [str(tmpdir.join("sub5"))] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 0 @@ -231,10 +231,10 @@ assert [0] == x """)) args = [str(tmpdir.join("sub6"))] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] failevents = [i for i in testevents if i.outcome.excinfo] @@ -252,10 +252,10 @@ print 3 """)) args = [str(tmpdir.join("sub7"))] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=plain_runner) + lsession.main(reporter=allevents.append, runner=plain_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 1 Modified: py/branch/conftest/py/test/rsession/testing/test_master.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_master.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_master.py Thu Jan 18 07:18:23 2007 @@ -18,7 +18,7 @@ def setup_module(mod): # bind an empty config - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) config.option.max_tasks_per_node = 10 session_options.bind_config(config) #assert not remote_options.exitfirst Modified: py/branch/conftest/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_reporter.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_reporter.py Thu Jan 18 07:18:23 2007 @@ -61,7 +61,7 @@ return outcomes def report_received_item_outcome(self): - config, args = py.test.config._reparse(["some_sub"]) + config = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) item = rootcol.getitembynames(funcpass_spec) @@ -81,7 +81,7 @@ return out def _test_module(self): - config, args = py.test.config._reparse(["some_sub"]) + config = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) funcitem = rootcol.getitembynames(funcpass_spec) @@ -119,7 +119,7 @@ """)) def boxfun(): - config, args = py.test.config._reparse([str(tmpdir)]) + config = py.test.config._reparse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) hosts = [HostInfo('localhost')] r = self.reporter(config, hosts) @@ -138,7 +138,7 @@ sadsadsa """)) def boxfun(): - config, args = py.test.config._reparse([str(tmpdir)]) + config = py.test.config._reparse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) host = HostInfo('localhost') r = self.reporter(config, [host]) @@ -157,7 +157,7 @@ tmpdir = py.test.ensuretemp("stilltogo") tmpdir.ensure("__init__.py") cap = py.io.OutErrCapture() - config, args = py.test.config._reparse([str(tmpdir)]) + config = py.test.config._reparse([str(tmpdir)]) hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) r.report(report.TestStarted(hosts)) Modified: py/branch/conftest/py/test/rsession/testing/test_rest.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_rest.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_rest.py Thu Jan 18 07:18:23 2007 @@ -27,7 +27,7 @@ class TestRestUnits(object): def setup_method(self, method): - config, args = py.test.config._reparse(["some_sub"]) + config = py.test.config._reparse(["some_sub"]) config.option.verbose = False self.config = config hosts = [HostInfo('localhost')] Modified: py/branch/conftest/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_rsession.py Thu Jan 18 07:18:23 2007 @@ -91,7 +91,7 @@ pass """)) args = [str(tmpdir.join("sub")), "-x"] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) rsession = RSession(config) allevents = [] rsession.main(reporter=allevents.append) @@ -121,7 +121,7 @@ assert __file__ != '%s' """ % str(tmpdir.join(subdir)))) args = [str(tmpdir.join(subdir))] - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) rsession = RSession(config, optimise_localhost=False) allevents = [] rsession.main(reporter=allevents.append) @@ -154,7 +154,7 @@ setup_events = [] teardown_events = [] - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) nodes = init_hosts(setup_events.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) @@ -181,7 +181,7 @@ parse_directories(hosts) allevents = [] - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) @@ -223,7 +223,7 @@ allevents = [] hosts = [HostInfo('localhost')] parse_directories(hosts) - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) d = remote_options.d.copy() d['custom'] = 'custom' @@ -264,7 +264,7 @@ assert os.nice(0) == 10 """) - config, args = py.test.config._reparse([tmpdir]) + config = py.test.config._reparse([tmpdir]) config.option.nice_level = 10 rsession = RSession(config) allevents = [] Modified: py/branch/conftest/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_slave.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_slave.py Thu Jan 18 07:18:23 2007 @@ -13,7 +13,7 @@ def setup_module(module): from py.__.test.rsession.rsession import session_options module.rootdir = py.path.local(py.__file__).dirpath().dirpath() - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) # ---------------------------------------------------------------------- Modified: py/branch/conftest/py/test/rsession/testing/test_web.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_web.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_web.py Thu Jan 18 07:18:23 2007 @@ -13,7 +13,7 @@ py.test.skip("PyPy not found") def setup_module(mod): - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) from py.__.test.rsession.rsession import session_options session_options.bind_config(config) session_options.import_pypy = True Modified: py/branch/conftest/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/branch/conftest/py/test/rsession/testing/test_webjs.py (original) +++ py/branch/conftest/py/test/rsession/testing/test_webjs.py Thu Jan 18 07:18:23 2007 @@ -15,7 +15,7 @@ mod.dom = dom dom.window = dom.Window(html) dom.document = dom.window.document - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) from py.__.test.rsession.rsession import session_options session_options.bind_config(config) session_options.import_pypy = True Modified: py/branch/conftest/py/test/terminal/remote.py ============================================================================== --- py/branch/conftest/py/test/terminal/remote.py (original) +++ py/branch/conftest/py/test/terminal/remote.py Thu Jan 18 07:18:23 2007 @@ -53,7 +53,7 @@ def failure_slave(channel): """ we run this on the other side. """ args, failures = channel.receive() - config, args = py.test.config._reparse(args) + config = py.test.config._reparse(args) # making this session definitely non-remote config.option.executable = py.std.sys.executable config.option.looponfailing = False Modified: py/branch/conftest/py/test/testing/test_collect.py ============================================================================== --- py/branch/conftest/py/test/testing/test_collect.py (original) +++ py/branch/conftest/py/test/testing/test_collect.py Thu Jan 18 07:18:23 2007 @@ -213,7 +213,7 @@ # test that running a session works from the directories old = o.chdir() try: - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main() @@ -223,7 +223,7 @@ old.chdir() # test that running the file directly works - config, args = py.test.config._reparse([str(checkfile)]) + config = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main() @@ -260,7 +260,7 @@ # test that running a session works from the directories old = o.chdir() try: - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main() @@ -270,7 +270,7 @@ old.chdir() # test that running the file directly works - config, args = py.test.config._reparse([str(checkfile)]) + config = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) session.main() Modified: py/branch/conftest/py/test/testing/test_config.py ============================================================================== --- py/branch/conftest/py/test/testing/test_config.py (original) +++ py/branch/conftest/py/test/testing/test_config.py Thu Jan 18 07:18:23 2007 @@ -24,25 +24,25 @@ """)) old = o.chdir() try: - config, args = py.test.config._reparse(['-g', '17']) + config = py.test.config._reparse(['-g', '17']) finally: old.chdir() assert config.option.gdest == 17 def test_parsing_again_fails(): dir = py.test.ensuretemp("parsing_again_fails") - config, args = py.test.config._reparse([str(dir)]) + config = py.test.config._reparse([str(dir)]) py.test.raises(AssertionError, "config.parse([])") def test_config_getvalue_honours_conftest(): o = py.test.ensuretemp('testconfigget') o.ensure("conftest.py").write("x=1") o.ensure("sub", "conftest.py").write("x=2 ; y = 3") - config, args = py.test.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.test.raises(KeyError, "config.getvalue('y')") - config, args = py.test.config._reparse([str(o.join('sub'))]) + 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 Modified: py/branch/conftest/py/test/testing/test_session.py ============================================================================== --- py/branch/conftest/py/test/testing/test_session.py (original) +++ py/branch/conftest/py/test/testing/test_session.py Thu Jan 18 07:18:23 2007 @@ -7,7 +7,7 @@ class TestDefaultSession: def test_simple(self): - config, args = py.test.config._reparse([datadir/'filetest.py']) + config = py.test.config._reparse([datadir/'filetest.py']) session = config.getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -16,7 +16,7 @@ assert not l def test_simple_verbose(self): - config, args = py.test.config._reparse([datadir/'filetest.py', + config = py.test.config._reparse([datadir/'filetest.py', '--verbose']) session = config.getsessionclass()(config, py.std.sys.stdout) session.main() @@ -26,7 +26,7 @@ assert not l def test_simple_verbose_verbose(self): - config, args = py.test.config._reparse([datadir/'filetest.py', + config = py.test.config._reparse([datadir/'filetest.py', '-v', '-v']) session = config.getsessionclass()(config, py.std.sys.stdout) session.main() @@ -38,17 +38,17 @@ def test_session_parsing(self): from py.__.test.terminal.terminal import TerminalSession from py.__.test.tkinter.reportsession import ReportSession - config, args = py.test.config._reparse(['--session=terminal']) + config = py.test.config._reparse(['--session=terminal']) assert issubclass(config.getsessionclass(), TerminalSession) - config, args = py.test.config._reparse(['--session=tkinter']) + config = py.test.config._reparse(['--session=tkinter']) assert issubclass(config.getsessionclass(), ReportSession) - config, args = py.test.config._reparse(['--tkinter']) + config = py.test.config._reparse(['--tkinter']) assert issubclass(config.getsessionclass(), ReportSession) class TestKeywordSelection: def test_select_simple(self): for keyword in ['test_one', 'est_on']: - config, args = py.test.config._reparse([datadir/'filetest.py', + config = py.test.config._reparse([datadir/'filetest.py', '-k', keyword]) session = config.getsessionclass()(config, py.std.sys.stdout) session.main() @@ -78,7 +78,7 @@ for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', 'TestClass test_2', 'xxx TestClass test_2',): f = py.std.StringIO.StringIO() - config, args = py.test.config._reparse([o, '-k', keyword]) + config = py.test.config._reparse([o, '-k', keyword]) session = config.getsessionclass()(config, f) session.main() print "keyword", repr(keyword) @@ -100,7 +100,7 @@ def mainsession(self, *args): from py.__.test.terminal.terminal import TerminalSession self.file = py.std.StringIO.StringIO() - config, args = py.test.config._reparse(list(args)) + config = py.test.config._reparse(list(args)) session = TerminalSession(config, file=self.file) session.main() return session @@ -345,7 +345,7 @@ assert 1 == 0 """)) print py.std.sys.executable - config, args = py.test.config._reparse( + config = py.test.config._reparse( ['--exec=' + py.std.sys.executable, o]) assert config.option._remote @@ -367,7 +367,7 @@ assert 1 == 0 """)) print py.std.sys.executable - config, args = py.test.config._reparse(['--looponfailing', str(o)]) + config = py.test.config._reparse(['--looponfailing', str(o)]) assert config.option._remote cls = config.getsessionclass() out = py.std.Queue.Queue() Modified: py/branch/conftest/py/test/tkinter/backend.py ============================================================================== --- py/branch/conftest/py/test/tkinter/backend.py (original) +++ py/branch/conftest/py/test/tkinter/backend.py Thu Jan 18 07:18:23 2007 @@ -229,11 +229,11 @@ from py.__.test.tkinter.reportsession import ReportSession from py.__.test.terminal.remote import getfailureitems - config, testfiles = py.test.config._reparse(args) + config = py.test.config._reparse(args) if tests: cols = getfailureitems(tests) else: - cols = testfiles + cols = config.remaining session = ReportSession(config = config, channel=channel) session.shouldclose = channel.isclosed session.main() Modified: py/branch/conftest/py/test/tkinter/testing/test_backend.py ============================================================================== --- py/branch/conftest/py/test/tkinter/testing/test_backend.py (original) +++ py/branch/conftest/py/test/tkinter/testing/test_backend.py Thu Jan 18 07:18:23 2007 @@ -178,7 +178,7 @@ assert l[0] is None def test_start_tests(self): - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([]) self.backend.start_tests(config = config, args = [str(datadir / 'filetest.py')], tests = []) Modified: py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py ============================================================================== --- py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py (original) +++ py/branch/conftest/py/test/tkinter/testing/test_capture_out_err.py Thu Jan 18 07:18:23 2007 @@ -5,10 +5,10 @@ datadir = py.magic.autopath().dirpath('data') def test_capture_out_err(): - config, args = py.test.config._reparse([]) + config = py.test.config._reparse([datadir/'filetest.py']) backend = ReportBackend() backend.start_tests(config = config, - args = [str(datadir / 'filetest.py')], + args = config.remaining, tests = []) while backend.running: backend.update() From hpk at codespeak.net Thu Jan 18 07:25:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 07:25:52 +0100 (CET) Subject: [py-svn] r36901 - py/branch/conftest/py/test Message-ID: <20070118062552.1FB231006E@code0.codespeak.net> Author: hpk Date: Thu Jan 18 07:25:51 2007 New Revision: 36901 Modified: py/branch/conftest/py/test/cmdline.py Log: remove dead code Modified: py/branch/conftest/py/test/cmdline.py ============================================================================== --- py/branch/conftest/py/test/cmdline.py (original) +++ py/branch/conftest/py/test/cmdline.py Thu Jan 18 07:25:51 2007 @@ -12,7 +12,6 @@ args = args.split(" ") config = py.test.config config.parse(args) - args = config.remaining sessionclass = config.getsessionclass() # ok, some option checks From hpk at codespeak.net Thu Jan 18 08:00:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 08:00:50 +0100 (CET) Subject: [py-svn] r36902 - in py/dist/py: . apigen apigen/testing doc doc/example/pytest test test/rsession test/rsession/testing test/terminal test/testing test/testing/data test/tkinter test/tkinter/testing Message-ID: <20070118070050.255831006E@code0.codespeak.net> Author: hpk Date: Thu Jan 18 08:00:43 2007 New Revision: 36902 Added: py/dist/py/test/conftesthandle.py - copied unchanged from r36901, py/branch/conftest/py/test/conftesthandle.py py/dist/py/test/testing/setupdata.py - copied unchanged from r36901, py/branch/conftest/py/test/testing/setupdata.py py/dist/py/test/testing/test_conftesthandle.py - copied unchanged from r36901, py/branch/conftest/py/test/testing/test_conftesthandle.py py/dist/py/test/todo-cleanup.txt - copied unchanged from r36901, py/branch/conftest/py/test/todo-cleanup.txt Removed: py/dist/py/test/testing/data/ Modified: py/dist/py/__init__.py py/dist/py/apigen/conftest.py py/dist/py/apigen/testing/test_apigen_functional.py py/dist/py/conftest.py py/dist/py/doc/conftest.py py/dist/py/doc/example/pytest/test_failures.py py/dist/py/doc/test.txt py/dist/py/doc/test_conftest.py py/dist/py/test/cmdline.py py/dist/py/test/collect.py py/dist/py/test/config.py py/dist/py/test/defaultconftest.py py/dist/py/test/item.py py/dist/py/test/rsession/conftest.py py/dist/py/test/rsession/rest.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_config.py py/dist/py/test/rsession/testing/test_executor.py py/dist/py/test/rsession/testing/test_lsession.py py/dist/py/test/rsession/testing/test_master.py py/dist/py/test/rsession/testing/test_reporter.py py/dist/py/test/rsession/testing/test_rest.py py/dist/py/test/rsession/testing/test_rsession.py py/dist/py/test/rsession/testing/test_slave.py py/dist/py/test/rsession/testing/test_web.py py/dist/py/test/rsession/testing/test_webjs.py py/dist/py/test/session.py py/dist/py/test/terminal/remote.py py/dist/py/test/terminal/terminal.py py/dist/py/test/testing/test_collect.py py/dist/py/test/testing/test_config.py py/dist/py/test/testing/test_session.py py/dist/py/test/tkinter/backend.py py/dist/py/test/tkinter/reportsession.py py/dist/py/test/tkinter/testing/test_backend.py py/dist/py/test/tkinter/testing/test_capture_out_err.py Log: * svn merge -r 36833:36901 http://codespeak.net/svn/py/branch/conftest/py * merging in the first bunch of cleanups from the conftest branch, main visible changes: * implemented and localized conftest handling code (in py/test/conftesthandle.py) * removed redundancies while setting up config and session objects * refactored config objects methods and attributes, you now use 'py.test.config' instead of 'py.test.Config' as you are now dealing with an instance and not a class anymore. py.test.config is a singleton-per-process (tests have the _reparse() method to get to additional config instances in a someone safe manner) * cleaned up documentation, added some docstrings to py.test objects Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Thu Jan 18 08:00:43 2007 @@ -32,7 +32,7 @@ 'test.compat.TestCase' : ('./test/compat.py', 'TestCase'), # configuration/initialization related test api - 'test.Config' : ('./test/config.py', 'Config'), + 'test.config' : ('./test/config.py', 'config'), 'test.ensuretemp' : ('./test/config.py', 'ensuretemp'), 'test.cmdline.main' : ('./test/cmdline.py', 'main'), Modified: py/dist/py/apigen/conftest.py ============================================================================== --- py/dist/py/apigen/conftest.py (original) +++ py/dist/py/apigen/conftest.py Thu Jan 18 08:00:43 2007 @@ -1,7 +1,7 @@ import py -Option = py.test.Config.Option -option = py.test.Config.addoptions("apigen test options", +Option = py.test.config.Option +option = py.test.config.addoptions("apigen test options", Option('', '--webcheck', action="store_true", dest="webcheck", default=False, help="run XHTML validation tests" Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Thu Jan 18 08:00:43 2007 @@ -7,6 +7,7 @@ from test_apigen_example import setup_fs_project def test_apigen_functional(): + py.test.skip("needs fixing with respect to rev 36800 and probably other problems") fs_root, package_name = setup_fs_project( 'test_apigen_functional') tempdir = py.test.ensuretemp('test_apigen_functional_results') Modified: py/dist/py/conftest.py ============================================================================== --- py/dist/py/conftest.py (original) +++ py/dist/py/conftest.py Thu Jan 18 08:00:43 2007 @@ -17,9 +17,9 @@ nomagic = False import py -Option = py.test.Config.Option +Option = py.test.config.Option -option = py.test.Config.addoptions("execnet options", +option = py.test.config.addoptions("execnet options", Option('-S', '', action="store", dest="sshtarget", default=None, help=("target to run tests requiring ssh, e.g. " Modified: py/dist/py/doc/conftest.py ============================================================================== --- py/dist/py/doc/conftest.py (original) +++ py/dist/py/doc/conftest.py Thu Jan 18 08:00:43 2007 @@ -2,8 +2,8 @@ import py from py.__.misc import rest -Option = py.test.Config.Option -option = py.test.Config.addoptions("documentation check options", +Option = py.test.config.Option +option = py.test.config.addoptions("documentation check options", Option('-R', '--checkremote', action="store_true", dest="checkremote", default=False, help="check remote links in ReST files" Modified: py/dist/py/doc/example/pytest/test_failures.py ============================================================================== --- py/dist/py/doc/example/pytest/test_failures.py (original) +++ py/dist/py/doc/example/pytest/test_failures.py Thu Jan 18 08:00:43 2007 @@ -3,9 +3,9 @@ failure_demo = py.magic.autopath().dirpath('failure_demo.py') def test_failure_demo_fails_properly(): - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([failure_demo]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([failure_demo]) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 21 l = session.getitemoutcomepairs(py.test.Item.Passed) Modified: py/dist/py/doc/test.txt ============================================================================== --- py/dist/py/doc/test.txt (original) +++ py/dist/py/doc/test.txt Thu Jan 18 08:00:43 2007 @@ -358,6 +358,7 @@ .. _`basicpicture`: + Collecting and running tests / implementation remarks ====================================================== @@ -396,7 +397,7 @@ ------------------------------------------ The collecting process is iterative, i.e. the session -traverses the *collector tree*. Here is an example of such +traverses and generates a *collector tree*. Here is an example of such a tree, generated with the command ``py.test --collectonly py/xmlobj``:: @@ -421,38 +422,6 @@ .. _`collector API`: -collector API invoked by sessions ---------------------------------- - -Apart from initialization the default session object invokes -a very uniform API on collectors and test items: - -*colitem.run()* - - 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. The session - object guards its calls to ``colitem.run()`` in its - ``session.runtraced(colitem)`` method, including - catching of stdout. - -*colitem.join(name)* - - return a child item from the given name. Usually the - session feeds the join method with each name obtained - from ``colitem.run()``. If the return value is None - it means the ``colitem`` was not able to resolve - with the given name. - -*colitem.parent* - - attribute pointing to the parent collector. - -*colitem.name* - - name of this sub item. This is the name that is - passed to ``colitem.join()`` above. - test items are collectors as well --------------------------------- @@ -477,7 +446,8 @@ on a path object (which returns the parent directory as a path object). -* insert this base directory into sys.path as its first item +* insert this base directory into the sys.path list + as its first element * import the root package @@ -485,10 +455,11 @@ at ``path`` ... * if the imported root package has a __package__ object - then call its ``getimportname(path)`` + then call ``__package__.getimportname(path)`` * otherwise use the relative path of the module path to - the base dir and turn slashes into dots. + 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 @@ -504,48 +475,16 @@ and test classes and methods. Test functions and methods are prefixed ``test`` by default. Test classes must start with a capitalized ``Test`` prefix. - -Reporting hooks of the session object -------------------------------------- - -Part of the default session API deals with reporting -test outcomes and collection details. These methods -are reponsible for representing the testing process -to the user or other programs: - -*session.header()* - - invoked once by ``session.run()`` before the whole - test session starts. -*session.footer()* - - invoked once by ``session.run()`` after the collection - and running process finished. - - -*session.start(colitem)* - - invoked before each ``colitem.run()`` invocation - - -*session.finish(colitem, outcome)* - - invoked after each ``colitem.run()`` invocation +Customizing the testing process +=============================== +writing conftest.py files +----------------------------------- -XXX the names of these session objects are likely to change -soon because the session object now has too many names that -aren't easily distinguishable regarding their purposes. It is -also likely that collectors/test items will become more -self-responsible for presenting outcomes in textual ways. -Currently, session object have to know too much about the -representation of failures/successes to the user which makes -it harder than neccessary to write custom test items. +XXX -Customizing the testing process -=============================== customizing the collecting and running process ----------------------------------------------- @@ -666,33 +605,17 @@ machines accessible through SSH, ``py.test`` can distribute tests across the machines. It does not require any particular installation on the remote machine sides as it uses `py.execnet`_ -mechanisms to distribute execution. - -*Warning*: support for distributed testing is in alpha state -and its mechanics and configuration options may change without -prior notice. - -Benefits --------- - -The main benefit for using distributed testing is speed. There -is some runtime overhead required to setup the test environment on the -remote machines, but if the project has a lot of tests and -several machines available, the speed up should be significant -in most cases. The more machines available, the better results -you will get. - -Automatically distributing the tests can be also be useful if you -need to use remote resouces which you do not have locally (perhaps -a server has much more RAM or disk available). -Of course, you could also just ssh into the remote server, -rsync or checkout your source code and run py.test -but that is obviously more cumbersome for the development -process. - -The distributed testing part of ``py.test`` also provides -a web server interface which tells you about the progress -of your distributed test run interactively. +mechanisms to distribute execution. Using distributed testing +can speed up your development process considerably and it +may also be useful where you need to use a remote server +that has more resources (e.g. RAM/diskspace) than your +local machine. + +*WARNING*: support for distributed testing is experimental, +its mechanics and configuration options may change without +prior notice. Particularly, not all reporting features +of the in-process py.test have been integrated into +the distributed testing approach. Requirements ------------ @@ -702,23 +625,23 @@ * ssh client * python -Remote requirements: +requirements for remote machines: * ssh daemon running * ssh keys setup to allow login without a password * python (2.3 or 2.4 should work) -* unix like machine (``os.fork``) +* unix like machine (reliance on ``os.fork``) -How it works ------------- +Hot to use it +----------------------- When you issue ``py.test --session=R`` then your computer becomes -the distributor of tests ("master") and will start distributing tests -to several machines which need to be specified in a ``conftest.py`` -file. +the distributor of tests ("master") and will start collecting +and distributing tests to several machines. The machines +need to be specified in a ``conftest.py`` file. -At start up, the master connects to each node using `py.execnet`_ and -its SSHGateways and *rsyncs* all specified python packages to all nodes. +At start up, the master connects to each node using `py.execnet.SshGateway`_ +and *rsyncs* all specified python packages to all nodes. Then the master collects all of the tests and immediately sends test item descriptions to its connected nodes. Each node has a local queue of tests to run and begins to execute the tests, following the setup and teardown @@ -730,6 +653,7 @@ from the testing nodes: command line, rest output and ajaxy web based. .. _`py.execnet`: execnet.html +.. _`py.execnet.SshGateway`: execnet.html Differences from local tests ---------------------------- @@ -745,19 +669,21 @@ The options that you need to specify in that conftest.py file are: -* `disthosts` - a list of ssh addresses (including a specific path if it +* `dist_hosts` - a list of ssh addresses (including a specific path if it should be different than the default: ``$HOME/pytestcache-hostname``) -* `distrsync_roots` - a list of packages to copy to the remote machines. +* `dist_rsync_roots` - a list of packages to copy to the remote machines. * `dist_remotepython` - the remote python to run. * `SessionOptions` - containing some specific tuning options Sample configuration:: - disthosts = ['localhost', 'user at someserver:/tmp/somedir'] - distrsync_roots = ['pypy', 'py'] + dist_hosts = ['localhost', 'user at someserver:/tmp/somedir'] + dist_rsync_roots = ['pypy', 'py'] dist_remotepython = 'python2.4' - class SessionOptions: - nice_level = 10 + dist_nicelevel = 10 + dist_boxing = True + dist_maxwait = 100 + dist_taskspernode = 10 Running server is done by ``-w`` command line option or ``--startserver`` (the former might change at some point due to conflicts). @@ -767,8 +693,8 @@ * `nice_level` - Level of nice under which tests are run * `runner_policy` - (for `LSession` only) - contains policy for the test boxig. `"plain_runner"` means no boxing, `"box_runner"` means boxing. -* `waittime` - Default waiting time for remote host to finish tests (after - that period we consider node as dead, default to 100s). +* `waittime` - Default maximum waiting time for remote host to execute + one test. * `max_tasks_per_node` - Maximum number of tasks which can be send to one node. * `import_pypy` - Flag to control pypy importing for js regeneration (defaults to False) Modified: py/dist/py/doc/test_conftest.py ============================================================================== --- py/dist/py/doc/test_conftest.py (original) +++ py/dist/py/doc/test_conftest.py Thu Jan 18 08:00:43 2007 @@ -28,9 +28,9 @@ end """)) - config, args = py.test.Config.parse([str(xtxt)]) + config = py.test.config._reparse([xtxt]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([xtxt]) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 l = session.getitemoutcomepairs(py.test.Item.Passed) @@ -46,9 +46,9 @@ .. _`blah`: javascript:some_function() """)) - config, args = py.test.Config.parse([str(xtxt)]) + config = py.test.config._reparse([xtxt]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([xtxt]) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 l = session.getitemoutcomepairs(py.test.Item.Passed) Modified: py/dist/py/test/cmdline.py ============================================================================== --- py/dist/py/test/cmdline.py (original) +++ py/dist/py/test/cmdline.py Thu Jan 18 08:00:43 2007 @@ -10,7 +10,8 @@ args = py.std.sys.argv[1:] elif isinstance(args, basestring): args = args.split(" ") - config, args = py.test.Config.parse(args) + config = py.test.config + config.parse(args) sessionclass = config.getsessionclass() # ok, some option checks @@ -31,17 +32,8 @@ session = sessionclass(config) - if config.option.runbrowser and not config.option.startserver: - print "Cannot point browser when not starting server" - config.option.startserver = True - try: - if config.getinitialvalue('startserver'): - py.std.warnings.warn("Startserver flag in config is deprecated, use commandline option instead") - except ValueError: - pass - try: - failures = session.main(args) + failures = session.main() if failures: raise SystemExit, 1 except KeyboardInterrupt: Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Thu Jan 18 08:00:43 2007 @@ -29,7 +29,7 @@ def configproperty(name): def fget(self): #print "retrieving %r property from %s" %(name, self.fspath) - return py.test.Config.getvalue(name, self.fspath) + return py.test.config.getvalue(name, self.fspath) return property(fget) def getfscollector(fspath): @@ -43,7 +43,7 @@ pkgpath = fspath else: pkgpath = fspath.dirpath() - Directory = py.test.Config.getvalue('Directory', pkgpath) + Directory = py.test.config.getvalue('Directory', pkgpath) current = Directory(pkgpath) #print "pkgpath", pkgpath names = filter(None, fspath.relto(pkgpath).split(fspath.sep)) @@ -55,6 +55,14 @@ return current class Collector(object): + """ Collector instances are iteratively generated + (through their run() and join() methods) + and form a tree. attributes:: + + parent: attribute pointing to the parent collector + (or None if it is the root collector) + name: basename of this collector object + """ def __init__(self, name, parent=None): self.name = name self.parent = parent @@ -113,6 +121,25 @@ #print "cmp", s1, s2 return cmp(s1, s2) + + def run(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. The session + object guards its calls to ``colitem.run()`` in its + ``session.runtraced(colitem)`` method, including + catching of stdout. + """ + raise NotImplementedError("abstract") + + def join(self, name): + """ return a child item for the given name. Usually the + session feeds the join method with each name obtained + from ``colitem.run()``. If the return value is None + it means the ``colitem`` was not able to resolve + with the given name. + """ + def obj(): def fget(self): try: @@ -287,7 +314,7 @@ elif p.ext == '.txt': res = self.DoctestFile(p, parent=self) elif p.check(dir=1): - Directory = py.test.Config.getvalue('Directory', p) + Directory = py.test.config.getvalue('Directory', p) res = Directory(p, parent=self) name2items[name] = res return res @@ -364,7 +391,7 @@ return res def startcapture(self): - if not self.option.nocapture and not self.option.usepdb: + if not self.option.nocapture: assert not hasattr(self, '_capture') #self._capture = py.io.OutErrCapture() # XXX integrate this into py.io / refactor @@ -454,7 +481,7 @@ Collector.Function.__get__(self)) # XXX for python 2.2 Function = property(Function) -class Generator(Collector, PyCollectorMixin): +class Generator(PyCollectorMixin, Collector): def run(self): self._prepare() itemlist = self._name2items Modified: py/dist/py/test/config.py ============================================================================== --- py/dist/py/test/config.py (original) +++ py/dist/py/test/config.py Thu Jan 18 08:00:43 2007 @@ -1,16 +1,9 @@ from __future__ import generators import py -optparse = py.compat.optparse - -defaultconfig = py.magic.autopath().dirpath('defaultconftest.py') -dummy = object() - -# -# configuration file handling -# -configbasename = 'conftest.py' +from conftesthandle import Conftest +optparse = py.compat.optparse # XXX move to Config class basetemp = None @@ -22,113 +15,90 @@ if basetemp is None: basetemp = py.path.local.make_numbered_dir(prefix='pytest-') return basetemp.ensure(string, dir=dir) - + +class CmdOptions(object): + """ pure container instance for holding cmdline options + as attributes. + """ + def __repr__(self): + return "" %(self.__dict__,) + class Config(object): """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option - _configs_cache = {} + conftest = Conftest() def __init__(self): - self.option = optparse.Values() - self._parser = optparse.OptionParser(usage="usage: %prog [options] [query] [filenames of tests]") - self._initialconfigmodules = [] - - # class level attributes - def _reset(cls): - cls._config = cls() - _reset = classmethod(_reset) - - def getvalue(cls, name, path=None, default=dummy, trydefaultconfig=True): - """ return 'name' value looked up from the first conftest file - found up the path (including the path itself). - """ - configpaths = guessconfigpaths(path) - if trydefaultconfig: - configpaths.append(defaultconfig) - for p in configpaths: - try: - mod = cls._configs_cache[p] - except (KeyError, IndexError): - mod = importconfig(p) - cls._configs_cache[p] = mod - try: - return getattr(mod, name) - except AttributeError: - pass - if default is not dummy: - return default - raise ValueError("config value not found: %r, path=%r" % (name, path)) - getvalue = classmethod(getvalue) - - def parse(cls, args): - """ return Config object and remaining arguments from parsing - command line arguments. + self.option = CmdOptions() + self._parser = optparse.OptionParser( + usage="usage: %prog [options] [query] [filenames of tests]") + self._parsed = False + + def parse(self, args): + """ parse cmdline arguments into this config object. + Note that this can only be called once per testing process. """ - configpaths = guessconfigpaths(*getanchorpaths(args)) - config = bootstrapconfig(configpaths) - config._origargs = args - cmdlineoption, remaining = config._parser.parse_args(args) - for name, value in vars(cmdlineoption).items(): - setattr(config.option, name, value) - fixoptions(config.option) + assert not self._parsed, ( + "can only parse cmdline args once per Config object") + self._parsed = True + self.conftest.setinitial(args) + self.conftest.lget('adddefaultoptions')() + args = [str(x) for x in args] + self._origargs = args + cmdlineoption, remaining = self._parser.parse_args(args) + self.option.__dict__.update(vars(cmdlineoption)) + fixoptions(self.option) # XXX fixing should be moved to sessions if not remaining: remaining.append(py.std.os.getcwd()) - config.remaining = remaining - return config, remaining - parse = classmethod(parse) + self.remaining = remaining - def addoptions(cls, groupname, *specs): + def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. This function gets invoked during testing session initialization. """ - parser = cls._config._parser - optgroup = optparse.OptionGroup(parser, groupname) + optgroup = optparse.OptionGroup(self._parser, groupname) optgroup.add_options(specs) - parser.add_option_group(optgroup) + self._parser.add_option_group(optgroup) for opt in specs: if hasattr(opt, 'default') and opt.dest: - setattr(cls._config.option, opt.dest, opt.default) - return cls._config.option - addoptions = classmethod(addoptions) - - # instance methods dealing with initial config module handling - - def loadconfig(self, configpath): - """ load configpath as an initial config module. """ - mod = importconfig(configpath) - self._initialconfigmodules.append(mod) - return mod - - def getinitialvalue(self, name, default=dummy): - """ return first value found in initial config modules. """ - for confmodule in self._initialconfigmodules: - if hasattr(confmodule, name): - return getattr(confmodule, name) - if default is not dummy: - return default - raise ValueError("initial config value not found: %r" % name) + setattr(self.option, opt.dest, opt.default) + return self.option + + def getvalue(self, name, path=None): + """ return 'name' value looked up from the first conftest file + found up the path (including the path itself). + if path is None, lookup the value in the initial + conftest modules found during command line parsing. + """ + return self.conftest.rget(name, path) def getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. """ - # we need to import all the sessions here - from py.__.test.session import Session - from py.__.test.rsession.rsession import RSession, LSession - from py.__.test.terminal.terminal import TerminalSession - from py.__.test.tkinter.reportsession import ReportSession - sessions = {'RSession':RSession, 'LSession':LSession, - 'TerminalSession':TerminalSession, - 'TkinterSession':ReportSession} - - name = self.option.session - name += 'Session' - try: - return self.getinitialvalue(name) - except ValueError: - return sessions[name] + sessionname = self.option.session + 'Session' + try: + return self.conftest.lget(sessionname) + except KeyError: + pass + sessionimportpaths = self.conftest.lget('sessionimportpaths') + importpath = sessionimportpaths[sessionname] + mod = __import__(importpath, None, None, ['__doc__']) + return getattr(mod, sessionname) + + def _reparse(self, args): + """ this is used from tests that want to re-invoke parse(). """ + global config + oldconfig = py.test.config + try: + config = py.test.config = Config() + config.parse(args) + return config + finally: + config = py.test.config = oldconfig -Config._reset() +# this is the one per-process instance of py.test configuration +config = Config() # # helpers @@ -153,6 +123,11 @@ else: option.executable = py.std.sys.executable + if option.usepdb: + if not option.nocapture: + print "--usepdb currently implies --nocapture" + option.nocapture = True + # make information available about wether we should/will be remote option._remote = remote or option.looponfailing option._fromremote = False @@ -167,75 +142,7 @@ name = name.capitalize() option.session = name -def bootstrapconfig(configpaths): - """ return 'current' config object, after initializing - it with the given configpaths. - """ - # XXX Config._config holds all options that are added from - # conftest's py.test.Config.addoptions() invocations. This "global state" - # manipulation is unfortunate but how else could we allow - # applications to add cmdline options and provide them - # access to the resulting config values? - - config = Config._config - # trigger loading conftest files which might add options! - config.loadconfig(defaultconfig).adddefaultoptions() - for configpath in configpaths: - config.loadconfig(configpath) - # each time we make a config object (which assembled cmdline - # options through py.test.addoptions() invocations) - # we want to reset and clean up the global state - Config._reset() - return config - -_config_paths_cache = {} - -def guessconfigpaths(*paths): - """ return test configuration paths from skimming the args. """ - key = tuple(paths) - try: - return _config_paths_cache[key] - except KeyError: - pass - d = {} - l = [] - for anchor in paths: - if anchor: - for p in anchor.parts(): - x = p.join(configbasename) - if x not in d and x.check(file=1): - d[x] = True - l.append(x) - l.reverse() - _config_paths_cache[key] = l - return l - -def getanchorpaths(args): - """ yield "anchors" from skimming the args for existing files/dirs. """ - current = py.path.local() - l = [] - for arg in args: - anchor = current.join(arg, abs=1) - if anchor.check(): - l.append(anchor) - if not l: - l = [current] - return l - -def importconfig(configpath): - if not configpath.dirpath('__init__.py').check(file=1): - # HACK: we don't want a "globally" imported conftest.py, - # prone to conflicts and subtle problems - modname = str(configpath).replace('.', configpath.sep) - return configpath.pyimport(modname=modname) - else: - return configpath.pyimport() + if option.runbrowser and not option.startserver: + print "Cannot point browser when not starting server" + option.startserver = True - -#XXX was needed for extracting defaults, not needed anymore? -#def flattenoptions(parser): -# for group in parser.option_groups: -# for y in group.option_list: -# yield y -# for x in parser.option_list: -# yield x Modified: py/dist/py/test/defaultconftest.py ============================================================================== --- py/dist/py/test/defaultconftest.py (original) +++ py/dist/py/test/defaultconftest.py Thu Jan 18 08:00:43 2007 @@ -10,9 +10,17 @@ additionalinfo = None +Option = py.test.config.Option + +sessionimportpaths = { + 'RSession': 'py.__.test.rsession.rsession', + 'LSession': 'py.__.test.rsession.rsession', + 'TerminalSession': 'py.__.test.terminal.terminal', + 'TkinterSession': 'py.__.test.tkinter.reportsession', +} + def adddefaultoptions(): - Option = py.test.Config.Option - py.test.Config.addoptions('general options', + py.test.config.addoptions('general options', Option('-v', '--verbose', action="count", dest="verbose", default=0, help="increase verbosity."), @@ -53,7 +61,8 @@ help="generate api documentation while testing (requires" "argument pointing to a script)."), ) - py.test.Config.addoptions('test-session related options', + + py.test.config.addoptions('test-session related options', Option('', '--tkinter', action="store_true", dest="tkinter", default=False, help="use tkinter test session frontend."), Modified: py/dist/py/test/item.py ============================================================================== --- py/dist/py/test/item.py (original) +++ py/dist/py/test/item.py Thu Jan 18 08:00:43 2007 @@ -31,7 +31,7 @@ class Item(py.test.collect.Collector): def startcapture(self): - if not self.option.nocapture and not self.option.usepdb: + if not self.option.nocapture: # XXX refactor integrate capturing #self._capture = py.io.OutErrCapture() from py.__.misc.simplecapture import SimpleOutErrCapture Modified: py/dist/py/test/rsession/conftest.py ============================================================================== --- py/dist/py/test/rsession/conftest.py (original) +++ py/dist/py/test/rsession/conftest.py Thu Jan 18 08:00:43 2007 @@ -1,7 +1,7 @@ import py -Option = py.test.Config.Option +Option = py.test.config.Option -option = py.test.Config.addoptions("boxing test options", +option = py.test.config.addoptions("boxing test options", Option('', '--skip-kill-test', action='store_true', default=False, dest='skip_kill_test', help='skip a certain test that checks for os.kill results, this ' Modified: py/dist/py/test/rsession/rest.py ============================================================================== --- py/dist/py/test/rsession/rest.py (original) +++ py/dist/py/test/rsession/rest.py Thu Jan 18 08:00:43 2007 @@ -19,7 +19,7 @@ def get_linkwriter(self): if self.linkwriter is None: - self.linkwriter = self.config.getinitialvalue('linkwriter') + self.linkwriter = self.config.getvalue('linkwriter') return self.linkwriter def report_unknown(self, what): Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Thu Jan 18 08:00:43 2007 @@ -45,8 +45,8 @@ def getvalue(self, opt): try: - return getattr(self.config.getinitialvalue('SessionOptions'), opt) - except (ValueError, AttributeError): + return getattr(self.config.getvalue('SessionOptions'), opt) + except (KeyError, AttributeError): try: return self.defaults[opt] except KeyError: @@ -56,8 +56,8 @@ self.config = config # copy to remote session options try: - ses_opt = self.config.getinitialvalue('SessionOptions').__dict__ - except ValueError: + ses_opt = self.config.getvalue('SessionOptions').__dict__ + except KeyError: ses_opt = self.defaults for key in self.defaults: try: @@ -184,10 +184,9 @@ class RSession(AbstractSession): """ Remote version of session """ - def main(self, args, reporter=None): + def main(self, reporter=None): """ main loop for running tests. """ - if not args: - args = [py.path.local()] + args = self.config.remaining session_options.bind_config(self.config) sshhosts, remotepython, rsync_roots = self.read_distributed_config() @@ -216,14 +215,14 @@ def read_distributed_config(self): try: - rsync_roots = self.config.getinitialvalue("distrsync_roots") + rsync_roots = self.config.getvalue("distrsync_roots") except: rsync_roots = None # all files and directories in the pkgdir sshhosts = [HostInfo(i) for i in - self.config.getinitialvalue("disthosts")] + self.config.getvalue("disthosts")] parse_directories(sshhosts) try: - remotepython = self.config.getinitialvalue("dist_remotepython") + remotepython = self.config.getvalue("dist_remotepython") except: remotepython = None return sshhosts, remotepython, rsync_roots @@ -246,11 +245,9 @@ class LSession(AbstractSession): """ Local version of session """ - def main(self, args, reporter=None, runner=None): + def main(self, reporter=None, runner=None): # check out if used options makes any sense - - if not args: - args = [py.path.local()] + args = self.config.remaining sshhosts = [HostInfo('localhost')] # this is just an info to reporter Modified: py/dist/py/test/rsession/testing/test_config.py ============================================================================== --- py/dist/py/test/rsession/testing/test_config.py (original) +++ py/dist/py/test/rsession/testing/test_config.py Thu Jan 18 08:00:43 2007 @@ -14,11 +14,11 @@ """) tmp2 = py.test.ensuretemp("xxx") args = [str(tmpdir)] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) session_options.bind_config(config) assert session_options.max_tasks_per_node == 5 assert remote_options.nice_level == 10 - config, args = py.test.Config.parse([str(tmp2)]) + config = py.test.config._reparse([str(tmp2)]) session_options.bind_config(config) assert session_options.max_tasks_per_node == \ SessionOptions.defaults['max_tasks_per_node'] Modified: py/dist/py/test/rsession/testing/test_executor.py ============================================================================== --- py/dist/py/test/rsession/testing/test_executor.py (original) +++ py/dist/py/test/rsession/testing/test_executor.py Thu Jan 18 08:00:43 2007 @@ -100,7 +100,7 @@ outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert outcome.passed - assert outcome.stdout == "samfing\n" + assert outcome.stdout.find("samfing") != -1 def test_box_executor_stdout_error(): rootcol = py.test.collect.Directory(rootdir) @@ -109,7 +109,7 @@ outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert not outcome.passed - assert outcome.stdout == "samfing elz\n" + assert outcome.stdout.find("samfing elz") != -1 def test_cont_executor(): rootcol = py.test.collect.Directory(rootdir) @@ -120,4 +120,4 @@ outcome_repr = cont() outcome = ReprOutcome(outcome_repr) assert not outcome.passed - assert outcome.stdout == "samfing elz\n" + assert outcome.stdout.find("samfing elz") != -1 Modified: py/dist/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_lsession.py (original) +++ py/dist/py/test/rsession/testing/test_lsession.py Thu Jan 18 08:00:43 2007 @@ -32,10 +32,10 @@ # os.kill(os.getpid(), 11) """)) args = [str(tmpdir.join(dirname))] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=runner) + lsession.main(reporter=allevents.append, runner=runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -85,11 +85,11 @@ pdb.post_mortem = some_fun args = [str(tmpdir.join(subdir)), '--pdb'] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] try: - lsession.main(args, reporter=allevents.append, runner=plain_runner) + lsession.main(reporter=allevents.append, runner=plain_runner) except SystemExit: pass else: @@ -116,12 +116,12 @@ pass """)) args = [str(tmpdir.join(subdir)), '-x'] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) assert config.option.exitfirst lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -146,11 +146,11 @@ pass """)) args = [str(tmpdir.join("sub3")), '-k', 'test_one'] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -174,7 +174,7 @@ """)) args = [str(tmpdir.join("sub4"))] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] allruns = [] @@ -182,7 +182,7 @@ allruns.append(item) item.passed = True return item - lsession.main(args, reporter=allevents.append, runner=dummy_runner) + lsession.main(reporter=allevents.append, runner=dummy_runner) assert len(allruns) == 4 testevents = [x for x in allevents @@ -205,10 +205,10 @@ """)) args = [str(tmpdir.join("sub5"))] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 0 @@ -231,10 +231,10 @@ assert [0] == x """)) args = [str(tmpdir.join("sub6"))] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=box_runner) + lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] failevents = [i for i in testevents if i.outcome.excinfo] @@ -252,10 +252,10 @@ print 3 """)) args = [str(tmpdir.join("sub7"))] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) lsession = LSession(config) allevents = [] - lsession.main(args, reporter=allevents.append, runner=plain_runner) + lsession.main(reporter=allevents.append, runner=plain_runner) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 1 Modified: py/dist/py/test/rsession/testing/test_master.py ============================================================================== --- py/dist/py/test/rsession/testing/test_master.py (original) +++ py/dist/py/test/rsession/testing/test_master.py Thu Jan 18 08:00:43 2007 @@ -18,7 +18,7 @@ def setup_module(mod): # bind an empty config - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) config.option.max_tasks_per_node = 10 session_options.bind_config(config) #assert not remote_options.exitfirst Modified: py/dist/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/dist/py/test/rsession/testing/test_reporter.py (original) +++ py/dist/py/test/rsession/testing/test_reporter.py Thu Jan 18 08:00:43 2007 @@ -61,7 +61,7 @@ return outcomes def report_received_item_outcome(self): - config, args = py.test.Config.parse(["some_sub"]) + config = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) item = rootcol.getitembynames(funcpass_spec) @@ -81,7 +81,7 @@ return out def _test_module(self): - config, args = py.test.Config.parse(["some_sub"]) + config = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) funcitem = rootcol.getitembynames(funcpass_spec) @@ -119,7 +119,7 @@ """)) def boxfun(): - config, args = py.test.Config.parse([str(tmpdir)]) + config = py.test.config._reparse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) hosts = [HostInfo('localhost')] r = self.reporter(config, hosts) @@ -138,7 +138,7 @@ sadsadsa """)) def boxfun(): - config, args = py.test.Config.parse([str(tmpdir)]) + config = py.test.config._reparse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) host = HostInfo('localhost') r = self.reporter(config, [host]) @@ -157,7 +157,7 @@ tmpdir = py.test.ensuretemp("stilltogo") tmpdir.ensure("__init__.py") cap = py.io.OutErrCapture() - config, args = py.test.Config.parse([str(tmpdir)]) + config = py.test.config._reparse([str(tmpdir)]) hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) r.report(report.TestStarted(hosts)) @@ -170,7 +170,7 @@ host1: READY (still 2 to go) host2: READY (still 1 to go) host3: READY""" - assert out.startswith(expected) + assert out.find(expected) != -1 class TestLocalReporter(AbstractTestReporter): reporter = LocalReporter @@ -191,7 +191,7 @@ repmod/test_one.py[1] repmod/test_three.py[0] - FAILED TO LOAD MODULE repmod/test_two.py[0] - skipped (reason)""" - assert received == expected + assert received.find(expected) != -1 class TestRemoteReporter(AbstractTestReporter): reporter = RemoteReporter @@ -207,7 +207,7 @@ localhost: FAILED py test rsession testing test_slave.py funcpass localhost: PASSED py test rsession testing test_slave.py funcpass """ - assert val == expected + assert val.find(expected) != -1 def test_module(self): val = self._test_module() @@ -217,10 +217,10 @@ localhost: FAILED py test rsession testing test_slave.py funcpass localhost: PASSED py test rsession testing test_slave.py funcpass """ - assert val == expected + assert val.find(expected) != -1 def test_full_module(self): #py.test.skip("XXX rewrite test to not rely on exact formatting") val = self._test_full_module() - assert val == 'FAILED TO LOAD MODULE: repmod/test_three.py\n'\ - '\nSkipped (reason) repmod/test_two.py\n\n' + assert val.find('FAILED TO LOAD MODULE: repmod/test_three.py\n'\ + '\nSkipped (reason) repmod/test_two.py') != -1 Modified: py/dist/py/test/rsession/testing/test_rest.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rest.py (original) +++ py/dist/py/test/rsession/testing/test_rest.py Thu Jan 18 08:00:43 2007 @@ -27,7 +27,7 @@ class TestRestUnits(object): def setup_method(self, method): - config, args = py.test.Config.parse(["some_sub"]) + config = py.test.config._reparse(["some_sub"]) config.option.verbose = False self.config = config hosts = [HostInfo('localhost')] Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Thu Jan 18 08:00:43 2007 @@ -91,10 +91,10 @@ pass """)) args = [str(tmpdir.join("sub")), "-x"] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) rsession = RSession(config) allevents = [] - rsession.main(args, reporter=allevents.append) + rsession.main(reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) == 3 @@ -121,10 +121,10 @@ assert __file__ != '%s' """ % str(tmpdir.join(subdir)))) args = [str(tmpdir.join(subdir))] - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) rsession = RSession(config, optimise_localhost=False) allevents = [] - rsession.main(args, reporter=allevents.append) + rsession.main(reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) @@ -154,7 +154,7 @@ setup_events = [] teardown_events = [] - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) nodes = init_hosts(setup_events.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) @@ -181,7 +181,7 @@ parse_directories(hosts) allevents = [] - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) @@ -214,7 +214,7 @@ assert len(skipped) == len(nodes) assert len(events) == 4 * len(nodes) # one of passed for each node has non-empty stdout - passed_stdout = [i for i in passed if i.outcome.stdout == 'samfing\n'] + passed_stdout = [i for i in passed if i.outcome.stdout.find('samfing') != -1] assert len(passed_stdout) == len(nodes), passed def test_config_pass(self): @@ -223,7 +223,7 @@ allevents = [] hosts = [HostInfo('localhost')] parse_directories(hosts) - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) d = remote_options.d.copy() d['custom'] = 'custom' @@ -264,11 +264,11 @@ assert os.nice(0) == 10 """) - config, args = py.test.Config.parse([str(tmpdir)]) + config = py.test.config._reparse([tmpdir]) config.option.nice_level = 10 rsession = RSession(config) allevents = [] - rsession.main(args, reporter=allevents.append) + rsession.main(reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] passevents = [x for x in testevents if x.outcome.passed] Modified: py/dist/py/test/rsession/testing/test_slave.py ============================================================================== --- py/dist/py/test/rsession/testing/test_slave.py (original) +++ py/dist/py/test/rsession/testing/test_slave.py Thu Jan 18 08:00:43 2007 @@ -13,7 +13,7 @@ def setup_module(module): from py.__.test.rsession.rsession import session_options module.rootdir = py.path.local(py.__file__).dirpath().dirpath() - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) session_options.bind_config(config) # ---------------------------------------------------------------------- Modified: py/dist/py/test/rsession/testing/test_web.py ============================================================================== --- py/dist/py/test/rsession/testing/test_web.py (original) +++ py/dist/py/test/rsession/testing/test_web.py Thu Jan 18 08:00:43 2007 @@ -13,7 +13,7 @@ py.test.skip("PyPy not found") def setup_module(mod): - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) from py.__.test.rsession.rsession import session_options session_options.bind_config(config) session_options.import_pypy = True Modified: py/dist/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/dist/py/test/rsession/testing/test_webjs.py (original) +++ py/dist/py/test/rsession/testing/test_webjs.py Thu Jan 18 08:00:43 2007 @@ -15,7 +15,7 @@ mod.dom = dom dom.window = dom.Window(html) dom.document = dom.window.document - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) from py.__.test.rsession.rsession import session_options session_options.bind_config(config) session_options.import_pypy = True Modified: py/dist/py/test/session.py ============================================================================== --- py/dist/py/test/session.py (original) +++ py/dist/py/test/session.py Thu Jan 18 08:00:43 2007 @@ -13,20 +13,21 @@ return False def header(self, colitems): - """ setup any neccessary resources. """ + """ setup any neccessary resources ahead of the test run. """ if not self.config.option.nomagic: py.magic.invoke(assertion=1) def footer(self, colitems): - """ teardown any resources we know about. """ + """ teardown any resources after a test run. """ py.test.Function.state.teardown_all() if not self.config.option.nomagic: py.magic.revoke(assertion=1) def start(self, colitem): - pass + """ hook invoked before each colitem.run() invocation. """ def finish(self, colitem, outcome): + """ hook invoked after each colitem.run() invocation. """ self._memo.append((colitem, outcome)) def startiteration(self, colitem, subitems): @@ -35,9 +36,9 @@ def getitemoutcomepairs(self, cls): return [x for x in self._memo if isinstance(x[1], cls)] - def main(self, args): + def main(self): """ main loop for running tests. """ - colitems = self._map2colitems(args) + colitems = self._map2colitems(self.config.remaining) try: self.header(colitems) try: Modified: py/dist/py/test/terminal/remote.py ============================================================================== --- py/dist/py/test/terminal/remote.py (original) +++ py/dist/py/test/terminal/remote.py Thu Jan 18 08:00:43 2007 @@ -53,7 +53,7 @@ def failure_slave(channel): """ we run this on the other side. """ args, failures = channel.receive() - config, args = py.test.Config.parse(args) + config = py.test.config._reparse(args) # making this session definitely non-remote config.option.executable = py.std.sys.executable config.option.looponfailing = False @@ -66,7 +66,7 @@ #print "processing", cols session = config.getsessionclass()(config) session.shouldclose = channel.isclosed - failures = session.main(cols) + failures = session.main() channel.send(failures) def getfailureitems(failures): Modified: py/dist/py/test/terminal/terminal.py ============================================================================== --- py/dist/py/test/terminal/terminal.py (original) +++ py/dist/py/test/terminal/terminal.py Thu Jan 18 08:00:43 2007 @@ -28,12 +28,12 @@ self._started = {} self._opencollectors = [] - def main(self, args): + def main(self): if self.config.option._remote: from py.__.test.terminal import remote return remote.main(self.config, self._file, self.config._origargs) else: - return super(TerminalSession, self).main(args) + return super(TerminalSession, self).main() # --------------------- # PROGRESS information @@ -145,7 +145,8 @@ for x in colitems: self.out.line("test target: %s" %(x.fspath,)) - for i,x in py.builtin.enumerate(self.config._initialconfigmodules): + conftestmodules = self.config.conftest.getconftestmodules(None) + for i,x in py.builtin.enumerate(conftestmodules): self.out.line("initial conf %d: %s" %(i, x.__file__)) #for i, x in py.builtin.enumerate(py.test.config.configpaths): Modified: py/dist/py/test/testing/test_collect.py ============================================================================== --- py/dist/py/test/testing/test_collect.py (original) +++ py/dist/py/test/testing/test_collect.py Thu Jan 18 08:00:43 2007 @@ -1,13 +1,13 @@ from __future__ import generators import py -datadir = py.magic.autopath().dirpath('data') +from setupdata import setupdatadir -tmpdir = py.test.ensuretemp('test_collect') +def setup_module(mod): + mod.datadir = setupdatadir() + mod.tmpdir = py.test.ensuretemp('test_collect') def test_failing_import_execfile(): - fn = datadir / 'failingimport.py' - dest = tmpdir.join('failing_import.py') - fn.copy(dest) + dest = datadir / 'failingimport.py' col = py.test.collect.Module(dest) py.test.raises(ImportError, col.run) py.test.raises(ImportError, col.run) @@ -213,20 +213,20 @@ # test that running a session works from the directories old = o.chdir() try: - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 2 finally: old.chdir() # test that running the file directly works - config, args = py.test.Config.parse([str(checkfile)]) + config = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 2 @@ -260,20 +260,20 @@ # test that running a session works from the directories old = o.chdir() try: - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 finally: old.chdir() # test that running the file directly works - config, args = py.test.Config.parse([str(checkfile)]) + config = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() session = config.getsessionclass()(config, out) - session.main(args) + session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 Modified: py/dist/py/test/testing/test_config.py ============================================================================== --- py/dist/py/test/testing/test_config.py (original) +++ py/dist/py/test/testing/test_config.py Thu Jan 18 08:00:43 2007 @@ -1,26 +1,5 @@ from __future__ import generators import py -from py.__.test import config - -class MyClass: - def getoptions(self): - yield config.Option('-v', action="count", dest="verbose", help="verbose") - -def xtest_verbose(): - obj = MyClass() - args = config.parseargs(['-v', 'hello'], obj) - assert args == ['hello'] - assert hasattr(obj, 'option') - assert hasattr(obj.option, 'verbose') - assert obj.option.verbose - -def xtest_verbose_default(): - obj = MyClass() - args = config.parseargs(['hello'], obj) - assert args, ['hello'] - assert hasattr(obj, 'option') - assert hasattr(obj.option, 'verbose') - assert not obj.option.verbose def test_tmpdir(): d1 = py.test.ensuretemp('hello') @@ -28,14 +7,14 @@ assert d1 == d2 assert d1.check(dir=1) -def test_config_options(): +def test_config_cmdline_options(): 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.test.Config.Option - option = py.test.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 @@ -45,35 +24,30 @@ """)) old = o.chdir() try: - config, args = py.test.Config.parse(['-g', '17']) - assert config.option.gdest == 17 + config = py.test.config._reparse(['-g', '17']) finally: old.chdir() - -#def test_config_order(): -# from py.__.test import config -# o = py.test.ensuretemp('configorder') -# o.ensure('conftest.py').write('x=1 ; import py ; py._x = [x]') -# o.ensure('a/conftest.py').write('x=2 ; import py ; py._x.append(x)') -# o.ensure('a/b/c/conftest.py').write('x=3 ; import py ; py._x.append(x)') -# cfg = config.Config() -# cfg.readconfiguration(o) -# assert cfg.getfirst('x') == 1 -# assert py._x == [1] -# -# cfg = config.Config() -# cfg.readconfiguration(o.join('a/b/c')) -# assert cfg.getfirst('x') == 1 -# assert py._x == [1,2,3] -# -#def test_getconfigvalue(): -# from py.__.test import config -# cfg = config.Config() -# o = py.test.ensuretemp('configtest') -# o.ensure('conftest.py').write('x=1') -# assert cfg.getconfigvalue(o, 'x') == 1 -# py.test.raises(ValueError, "cfg.getconfigvalue(o, 'y')") -# + assert config.option.gdest == 17 + +def test_parsing_again_fails(): + 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.test.ensuretemp('testconfigget') + o.ensure("conftest.py").write("x=1") + o.ensure("sub", "conftest.py").write("x=2 ; y = 3") + config = py.test.config._reparse([str(o)]) + assert config.getvalue("x") == 1 + assert config.getvalue("x", o.join('sub')) == 2 + 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.test.raises(KeyError, 'config.getvalue("y", o)') + def test_siblingconftest_fails_maybe(): from py.__.test import config Modified: py/dist/py/test/testing/test_session.py ============================================================================== --- py/dist/py/test/testing/test_session.py (original) +++ py/dist/py/test/testing/test_session.py Thu Jan 18 08:00:43 2007 @@ -1,32 +1,35 @@ import py -datadir = py.magic.autopath().dirpath('data') -from cStringIO import StringIO +from setupdata import setupdatadir -tmpdir = py.test.ensuretemp('test_drive') +def setup_module(mod): + mod.datadir = setupdatadir() + mod.tmpdir = py.test.ensuretemp(mod.__name__) class TestDefaultSession: def test_simple(self): - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([datadir/'filetest.py']) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l def test_simple_verbose(self): - config, args = py.test.Config.parse(['--verbose']) + config = py.test.config._reparse([datadir/'filetest.py', + '--verbose']) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l def test_simple_verbose_verbose(self): - config, args = py.test.Config.parse(['-v', '-v']) + config = py.test.config._reparse([datadir/'filetest.py', + '-v', '-v']) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 l = session.getitemoutcomepairs(py.test.Item.Passed) @@ -35,19 +38,20 @@ def test_session_parsing(self): from py.__.test.terminal.terminal import TerminalSession from py.__.test.tkinter.reportsession import ReportSession - config, args = py.test.Config.parse(['--session=terminal']) + config = py.test.config._reparse(['--session=terminal']) assert issubclass(config.getsessionclass(), TerminalSession) - config, args = py.test.Config.parse(['--session=tkinter']) + config = py.test.config._reparse(['--session=tkinter']) assert issubclass(config.getsessionclass(), ReportSession) - config, args = py.test.Config.parse(['--tkinter']) + config = py.test.config._reparse(['--tkinter']) assert issubclass(config.getsessionclass(), ReportSession) class TestKeywordSelection: def test_select_simple(self): for keyword in ['test_one', 'est_on']: - config, args = py.test.Config.parse(['-k', keyword]) + config = py.test.config._reparse([datadir/'filetest.py', + '-k', keyword]) session = config.getsessionclass()(config, py.std.sys.stdout) - session.main([datadir / 'filetest.py']) + session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 item = l[0][0] @@ -73,10 +77,10 @@ """)) for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', 'TestClass test_2', 'xxx TestClass test_2',): - f = StringIO() - config, args = py.test.Config.parse(['-k', keyword]) + f = py.std.StringIO.StringIO() + config = py.test.config._reparse([o, '-k', keyword]) session = config.getsessionclass()(config, f) - session.main([o]) + session.main() print "keyword", repr(keyword) l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 @@ -93,33 +97,23 @@ def teardown_class(cls): (datadir / 'syntax_error.py').remove() - def setup_method(self, method): + def mainsession(self, *args): from py.__.test.terminal.terminal import TerminalSession - self.file = StringIO() - config, args = py.test.Config.parse([]) - self.session = TerminalSession(config, file=self.file) - #print >>f, "session %r is setup for %r" %(self.session, method) - #f.flush() - #print "session is setup", self.session - - #def teardown_method(self, method): - #print >>f, "session %r finished method %r" % (self.session, method) - #f.flush() + self.file = py.std.StringIO.StringIO() + config = py.test.config._reparse(list(args)) + session = TerminalSession(config, file=self.file) + session.main() + return session def test_terminal(self): - self.session.main([datadir / 'filetest.py']) + session = self.mainsession(datadir / 'filetest.py') out = self.file.getvalue() - #print "memo" - #print self.session._memo - #print "out" - #print out - l = self.session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 assert out.find('2 failed') != -1 def test_syntax_error_module(self): - session = self.session - session.main([str(datadir / 'syntax_error.py')]) + session = self.mainsession(datadir / 'syntax_error.py') l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 out = self.file.getvalue() @@ -127,18 +121,18 @@ assert out.find(str('not python')) != -1 def test_exit_first_problem(self): - session = self.session - session.config.option.exitfirst = True - session.main([str(datadir / 'filetest.py')]) + session = self.mainsession("--exitfirst", + datadir / 'filetest.py') + assert session.config.option.exitfirst l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l def test_collectonly(self): - session = self.session - session.config.option.collectonly = True - session.main([str(datadir / 'filetest.py')]) + session = self.mainsession("--collectonly", + datadir / 'filetest.py') + assert session.config.option.collectonly out = self.file.getvalue() #print out l = session.getitemoutcomepairs(py.test.Item.Failed) @@ -162,11 +156,7 @@ f() f() """)) - #print "hello" - session = self.session - #session.config.option.nocapture = True - print "calling main", o - session.main([str(o)]) + session = self.mainsession(o) print "back from main", o out = self.file.getvalue() #print out @@ -180,8 +170,7 @@ def test_1(): yield None """)) - session = self.session - session.main([o]) + session = self.mainsession(o) out = self.file.getvalue() #print out i = out.find('TypeError') @@ -209,8 +198,7 @@ def finishcapture(self): self._testmycapture = self._mycapture.reset() """)) - session = self.session - session.main([o]) + session = self.mainsession(o) l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 item = l[0][0] @@ -236,8 +224,7 @@ def test_raises_doesnt(): py.test.raises(ValueError, int, "3") """)) - session = self.session - session.main([o]) + session = self.mainsession(o) out = self.file.getvalue() if not out.find("DID NOT RAISE") != -1: print out @@ -266,8 +253,7 @@ assert self.reslist == [1,2,1,2,3] """)) - session = self.session - session.main([o]) + session = self.mainsession(o) l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 l = session.getitemoutcomepairs(py.test.Item.Passed) @@ -290,16 +276,14 @@ import does_not_work a = 1 """)) - session = self.session - session.main([o]) + session = self.mainsession(o) l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 item, outcome = l[0] assert str(outcome.excinfo).find('does_not_work') != -1 def test_safe_repr(self): - session = self.session - session.main([str(datadir / 'brokenrepr.py')]) + session = self.mainsession(datadir/'brokenrepr.py') out = self.file.getvalue() print 'Output of simulated "py.test brokenrepr.py":' print out @@ -321,8 +305,7 @@ 'c']) """) tfile.write(source) - session = self.session - session.main([o]) + session = self.mainsession(o) out = self.file.getvalue() print 'Output of simulated "py.test test_correct_line.py":' print out @@ -362,14 +345,14 @@ assert 1 == 0 """)) print py.std.sys.executable - config, args = py.test.Config.parse( + config = py.test.config._reparse( ['--exec=' + py.std.sys.executable, - str(o)]) + o]) assert config.option._remote cls = config.getsessionclass() out = [] # out = py.std.Queue.Queue() session = cls(config, out.append) - session.main([o]) + session.main() for s in out: if s.find('1 failed') != -1: break @@ -384,13 +367,13 @@ assert 1 == 0 """)) print py.std.sys.executable - config, args = py.test.Config.parse(['--looponfailing', str(o)]) + config = py.test.config._reparse(['--looponfailing', str(o)]) assert config.option._remote cls = config.getsessionclass() out = py.std.Queue.Queue() session = cls(config, out.put) pool = py._thread.WorkerPool() - reply = pool.dispatch(session.main, [str(o)]) + reply = pool.dispatch(session.main) while 1: s = out.get() if s.find('1 failed') != -1: Modified: py/dist/py/test/tkinter/backend.py ============================================================================== --- py/dist/py/test/tkinter/backend.py (original) +++ py/dist/py/test/tkinter/backend.py Thu Jan 18 08:00:43 2007 @@ -229,12 +229,12 @@ from py.__.test.tkinter.reportsession import ReportSession from py.__.test.terminal.remote import getfailureitems - config, testfiles = py.test.Config.parse(args) + config = py.test.config._reparse(args) if tests: cols = getfailureitems(tests) else: - cols = testfiles + cols = config.remaining session = ReportSession(config = config, channel=channel) session.shouldclose = channel.isclosed - session.main(cols) + session.main() Modified: py/dist/py/test/tkinter/reportsession.py ============================================================================== --- py/dist/py/test/tkinter/reportsession.py (original) +++ py/dist/py/test/tkinter/reportsession.py Thu Jan 18 08:00:43 2007 @@ -44,4 +44,5 @@ def sendreport(self, report): self.channel.send(report.to_channel()) +TkinterSession = ReportSession Modified: py/dist/py/test/tkinter/testing/test_backend.py ============================================================================== --- py/dist/py/test/tkinter/testing/test_backend.py (original) +++ py/dist/py/test/tkinter/testing/test_backend.py Thu Jan 18 08:00:43 2007 @@ -178,7 +178,7 @@ assert l[0] is None def test_start_tests(self): - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([]) self.backend.start_tests(config = config, args = [str(datadir / 'filetest.py')], tests = []) Modified: py/dist/py/test/tkinter/testing/test_capture_out_err.py ============================================================================== --- py/dist/py/test/tkinter/testing/test_capture_out_err.py (original) +++ py/dist/py/test/tkinter/testing/test_capture_out_err.py Thu Jan 18 08:00:43 2007 @@ -5,10 +5,10 @@ datadir = py.magic.autopath().dirpath('data') def test_capture_out_err(): - config, args = py.test.Config.parse([]) + config = py.test.config._reparse([datadir/'filetest.py']) backend = ReportBackend() backend.start_tests(config = config, - args = [str(datadir / 'filetest.py')], + args = config.remaining, tests = []) while backend.running: backend.update() From hpk at codespeak.net Thu Jan 18 08:49:22 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 08:49:22 +0100 (CET) Subject: [py-svn] r36906 - in py/dist/py: apigen apigen/testing test/rsession Message-ID: <20070118074922.750661007A@code0.codespeak.net> Author: hpk Date: Thu Jan 18 08:49:21 2007 New Revision: 36906 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_functional.py py/dist/py/test/rsession/rsession.py Log: fixing the rsession/apigen interaction to to pass the apigen_functional test again. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Thu Jan 18 08:49:21 2007 @@ -297,6 +297,7 @@ reloutputpath = "source/%s.html" % (relfspath,) outputpath = outputbase.join(reloutputpath) linker.set_link(str(fspath), reloutputpath) + print "setting source link for", fspath passed.append((fspath, outputpath)) return passed Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Thu Jan 18 08:49:21 2007 @@ -7,7 +7,6 @@ from test_apigen_example import setup_fs_project def test_apigen_functional(): - py.test.skip("needs fixing with respect to rev 36800 and probably other problems") fs_root, package_name = setup_fs_project( 'test_apigen_functional') tempdir = py.test.ensuretemp('test_apigen_functional_results') @@ -16,7 +15,7 @@ output = py.process.cmdexec( 'APIGEN_TARGET=%s py.test --session=L --apigen=%s/apigen.py %s' % ( tempdir, parentdir, pkgdir)) - assert output.endswith('=======\n') # no traceback in the end + assert output.lower().find('traceback') == -1 # just some quick content checks apidir = tempdir.join('api') Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Thu Jan 18 08:49:21 2007 @@ -278,16 +278,15 @@ if not self.config.option.nomagic: py.magic.revoke(assertion=1) - self.write_docs() + self.write_docs(pkgdir) return retval - def write_docs(self): + def write_docs(self, pkgdir): if self.config.option.apigen: from py.__.apigen.tracer.docstorage import DocStorageAccessor apigen = py.path.local(self.config.option.apigen).pyimport() - pkgpath = py.path.local(py.__file__).dirpath() try: - apigen.build(pkgpath, DocStorageAccessor(self.docstorage)) + apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) except (ValueError, AttributeError): raise NotImplementedError("Provided script does not seem " "to contain build function") From hpk at codespeak.net Thu Jan 18 08:50:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 08:50:43 +0100 (CET) Subject: [py-svn] r36907 - py/branch/conftest Message-ID: <20070118075043.A679210075@code0.codespeak.net> Author: hpk Date: Thu Jan 18 08:50:42 2007 New Revision: 36907 Removed: py/branch/conftest/ Log: removing the conftest branch, probably opening a new one for more option/config cleanups soon From hpk at codespeak.net Thu Jan 18 09:15:57 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 09:15:57 +0100 (CET) Subject: [py-svn] r36908 - py/dist/py/apigen Message-ID: <20070118081557.5E0A51008E@code0.codespeak.net> Author: hpk Date: Thu Jan 18 09:15:55 2007 New Revision: 36908 Added: py/dist/py/apigen/todo-apigen.txt Log: some api todos i can currently think of Added: py/dist/py/apigen/todo-apigen.txt ============================================================================== --- (empty file) +++ py/dist/py/apigen/todo-apigen.txt Thu Jan 18 09:15:55 2007 @@ -0,0 +1,34 @@ + +* format docstrings more nicely (with tests) + +* have the API function view be as informative as possible + without having to go to the "single method" view + (do we even need a single method view?), for example: + + browsing the class views (and clicking on methods) + should always make it obvious which class is being + viewed. method views (when navigating there through + the class view) should also have the source there + +* have class-level attributes be displayed + +* use "inherited" doc strings, i.e. for + class A: + def meth(self): + "doc1" + class B(A): + def meth(self): + pass + + B.meth should display the A.meth docstring, probably + with special formatting (italics or so). + +* factor out some common code in the build_* functions + +* refactor the apigen/rsession interaction to become + cleaner (e.g. apigen's get_documentable_items should + be separately tested and the caller should not need + to guess what it will get, i think) + +* XXX list more here + From hpk at codespeak.net Thu Jan 18 09:16:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 09:16:53 +0100 (CET) Subject: [py-svn] r36909 - py/dist/py/apigen Message-ID: <20070118081653.0F7701008E@code0.codespeak.net> Author: hpk Date: Thu Jan 18 09:16:52 2007 New Revision: 36909 Modified: py/dist/py/apigen/todo-apigen.txt Log: documentation of course Modified: py/dist/py/apigen/todo-apigen.txt ============================================================================== --- py/dist/py/apigen/todo-apigen.txt (original) +++ py/dist/py/apigen/todo-apigen.txt Thu Jan 18 09:16:52 2007 @@ -30,5 +30,8 @@ be separately tested and the caller should not need to guess what it will get, i think) +* look out for and streamline all apigen/source-viewer + documentation into one document + * XXX list more here From guido at codespeak.net Thu Jan 18 12:24:41 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 12:24:41 +0100 (CET) Subject: [py-svn] r36933 - in py/dist/py/apigen: . testing Message-ID: <20070118112441.CDA7E1009A@code0.codespeak.net> Author: guido Date: Thu Jan 18 12:24:40 2007 New Revision: 36933 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py Log: Added links to parent namespace from function pages. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Thu Jan 18 12:24:40 2007 @@ -220,36 +220,35 @@ if len(path) > 1: parent_dotted_name = '.'.join(path[:-1]) navitems.append(indent * 2 * u'\xa0') - try: - obj = dsa.get_obj(parent_dotted_name) - except KeyError: - navitems.append(parent_dotted_name) - navitems.append(H.br()) - indent += 1 + navitems.append(H.NavigationItem( + H.a(parent_dotted_name, + href=linker.get_lazyhref( + parent_dotted_name)))) + navitems.append(H.br()) + indent += 1 + navitems.append(indent * 2 * u'\xa0') + navitems.append(H.NavigationItem( + H.a(dotted_name.split('.')[-1], + href=linker.get_lazyhref(dotted_name)))) + navitems.append(H.br()) + indent += 1 + for cls_dotted_name in dsa.get_class_names(): + if not cls_dotted_name.startswith('%s.' % (dotted_name,)): + continue navitems.append(indent * 2 * u'\xa0') navitems.append(H.NavigationItem( - H.a(dotted_name, - href=linker.get_lazyhref(dotted_name)))) - else: - navitems.append(H.NavigationItem( - H.a(parent_dotted_name, + H.a(cls_dotted_name, href=linker.get_lazyhref( - parent_dotted_name)))) - navitems.append(H.br()) - indent += 1 + cls_dotted_name)))) + for func_dotted_name in dsa.get_function_names(): + if not func_dotted_name.startswith('%s.' % (dotted_name,)): + continue navitems.append(indent * 2 * u'\xa0') navitems.append(H.NavigationItem( - H.a(dotted_name.split('.')[-1], - href=linker.get_lazyhref(dotted_name)))) + H.a(func_dotted_name, + href=linker.get_lazyhref( + func_dotted_name)))) navitems.append(H.br()) - indent += 1 - for funcname in dsa.get_class_methods(dotted_name): - func_dotted_name = '%s.%s' % (dotted_name, funcname) - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(funcname, - href=linker.get_lazyhref(func_dotted_name)))) - navitems.append(H.br()) return H.Navigation(*navitems) re = py.std.re Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Thu Jan 18 12:24:40 2007 @@ -83,6 +83,9 @@ def test_build_function_api_pages(self): base = py.test.ensuretemp('build_function_api_pages') prepare_source_pages(self.linker, self.fs_root, base) + prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', 'pkg.SomeOtherClass']) data = prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) build_function_api_pages(self.linker, data, self.project, base) @@ -226,3 +229,17 @@ in html) _checkhtml(html) + def test_build_function_api_pages_nav(self): + base = py.test.ensuretemp('build_function_api_pages_nav') + prepare_source_pages(self.linker, self.fs_root, base) + prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', 'pkg.SomeOtherClass']) + data = prepare_function_api_pages(self.linker, self.dsa, + base, ['pkg.sub.func']) + build_function_api_pages(self.linker, data, self.project, base) + funcfile = base.join('api/pkg.sub.func.html') + html = funcfile.read() + assert '' in html + + From fijal at codespeak.net Thu Jan 18 13:09:50 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 13:09:50 +0100 (CET) Subject: [py-svn] r36934 - py/dist/py/test Message-ID: <20070118120950.AFD3C100A0@code0.codespeak.net> Author: fijal Date: Thu Jan 18 13:09:48 2007 New Revision: 36934 Added: py/dist/py/test/session-options.txt Log: Added a bit of info about options Added: py/dist/py/test/session-options.txt ============================================================================== --- (empty file) +++ py/dist/py/test/session-options.txt Thu Jan 18 13:09:48 2007 @@ -0,0 +1,23 @@ +RSession options: + +class SessionOptions: + nice_level - how to nice remote prcesses (RSession) + boxing_policy - run with boxes or not (LSession) + max_tasks_per_node (RSession) + waittime - how long to wait till considering node dead (not fully working, + RSession) + import_pypy - try to import pypy for regenerating web js (RSession, LSession) + +user provided options: + +startserver - run web server for displaying output (RSession, LSession), + implies LSession +runbrowser - use webbrowser - implies startserver +rest - implies LSession +apigen - implies LSession + +other conftest options: + +dist_rsyncroots - RSession +disthosts - RSession +dist_remotepython - RSession From fijal at codespeak.net Thu Jan 18 13:10:47 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 13:10:47 +0100 (CET) Subject: [py-svn] r36935 - py/dist/py/test Message-ID: <20070118121047.DE85D100A4@code0.codespeak.net> Author: fijal Date: Thu Jan 18 13:10:46 2007 New Revision: 36935 Modified: py/dist/py/test/session-options.txt Log: Added misc. Modified: py/dist/py/test/session-options.txt ============================================================================== --- py/dist/py/test/session-options.txt (original) +++ py/dist/py/test/session-options.txt Thu Jan 18 13:10:46 2007 @@ -21,3 +21,8 @@ dist_rsyncroots - RSession disthosts - RSession dist_remotepython - RSession + +various stuff: + +we_are_remote - flag to indicate whether we are in remote process, not + available for user. \ No newline at end of file From fijal at codespeak.net Thu Jan 18 13:12:14 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 13:12:14 +0100 (CET) Subject: [py-svn] r36936 - py/dist/py/test Message-ID: <20070118121214.D7267100A0@code0.codespeak.net> Author: fijal Date: Thu Jan 18 13:12:13 2007 New Revision: 36936 Modified: py/dist/py/test/session-options.txt Log: contradictory options. Modified: py/dist/py/test/session-options.txt ============================================================================== --- py/dist/py/test/session-options.txt (original) +++ py/dist/py/test/session-options.txt Thu Jan 18 13:12:13 2007 @@ -25,4 +25,9 @@ various stuff: we_are_remote - flag to indicate whether we are in remote process, not - available for user. \ No newline at end of file + available for user. + +options that does not work in L/RSession: + +looponfailing +exec From fijal at codespeak.net Thu Jan 18 14:22:56 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 14:22:56 +0100 (CET) Subject: [py-svn] r36937 - py/branch/config Message-ID: <20070118132256.BEBA6100A0@code0.codespeak.net> Author: fijal Date: Thu Jan 18 14:22:54 2007 New Revision: 36937 Added: py/branch/config/ - copied from r36936, py/dist/ Log: Create a branch for further config cleanups. From guido at codespeak.net Thu Jan 18 14:57:44 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 14:57:44 +0100 (CET) Subject: [py-svn] r36938 - py/dist/py/apigen/tracer/testing Message-ID: <20070118135744.5398B100AA@code0.codespeak.net> Author: guido Date: Thu Jan 18 14:57:41 2007 New Revision: 36938 Modified: py/dist/py/apigen/tracer/testing/test_docgen.py Log: Test to display borken behaviour in tracer/description code that handles protected/private methods. Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Thu Jan 18 14:57:41 2007 @@ -333,3 +333,31 @@ assert len(ds.descs['B'].fields['meth'].call_sites) == 1 assert len(ds.descs['C'].fields['meth'].call_sites) == 1 return ds + +def test_private_methods(): + py.test.skip('XXX borken') + class Foo(object): + def foo(self): + pass + def _foo(self): + pass + def __foo(self): + pass + def trigger__foo(self): + self.__foo() + def __foo__(self): + pass + + ds = DocStorage().from_dict({'Foo': Foo}) + dsa = DocStorageAccessor(ds) + t = Tracer(ds) + t.start_tracing() + f = Foo() + f.foo() + f._foo() + f.trigger__foo() + f.__foo__() + t.end_tracing() + assert sorted(ds.descs['Foo'].fields.keys()) == ['__foo__', '_foo', 'foo', + 'trigger_foo'] + From guido at codespeak.net Thu Jan 18 15:04:45 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 15:04:45 +0100 (CET) Subject: [py-svn] r36939 - in py/dist/py/apigen/tracer: . testing Message-ID: <20070118140445.7838F100B2@code0.codespeak.net> Author: guido Date: Thu Jan 18 15:04:44 2007 New Revision: 36939 Modified: py/dist/py/apigen/tracer/description.py py/dist/py/apigen/tracer/testing/test_docgen.py Log: Isolated some code that checks whether a variable is private/protected. Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Thu Jan 18 15:04:44 2007 @@ -8,6 +8,9 @@ MAX_CALL_SITES = 20 +def is_private(name): + return name.startswith('_') and not name.startswith('__') + class CallFrame(object): def __init__(self, frame): self.filename = frame.code.raw.co_filename @@ -256,8 +259,7 @@ def getfields(self): # return fields of values that has been used - l = [i for i, v in self.fields.iteritems() if (not i.startswith('_') - or i.startswith('__'))] + l = [i for i, v in self.fields.iteritems() if not is_private(i)] return l def getbases(self): Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Thu Jan 18 15:04:44 2007 @@ -334,8 +334,8 @@ assert len(ds.descs['C'].fields['meth'].call_sites) == 1 return ds -def test_private_methods(): - py.test.skip('XXX borken') +def test_is_private(): + # XXX implicit test, but so are the rest :| class Foo(object): def foo(self): pass @@ -358,6 +358,6 @@ f.trigger__foo() f.__foo__() t.end_tracing() - assert sorted(ds.descs['Foo'].fields.keys()) == ['__foo__', '_foo', 'foo', - 'trigger_foo'] + assert sorted(ds.descs['Foo'].getfields()) == ['__foo__', 'foo', + 'trigger__foo'] From fijal at codespeak.net Thu Jan 18 15:27:19 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 15:27:19 +0100 (CET) Subject: [py-svn] r36940 - py/branch/config/py/doc Message-ID: <20070118142719.0077C100B5@code0.codespeak.net> Author: fijal Date: Thu Jan 18 15:27:17 2007 New Revision: 36940 Modified: py/branch/config/py/doc/test.txt Log: Factored out how I would like options to look like. Modified: py/branch/config/py/doc/test.txt ============================================================================== --- py/branch/config/py/doc/test.txt (original) +++ py/branch/config/py/doc/test.txt Thu Jan 18 15:27:17 2007 @@ -673,7 +673,14 @@ should be different than the default: ``$HOME/pytestcache-hostname``) * `dist_rsync_roots` - a list of packages to copy to the remote machines. * `dist_remotepython` - the remote python to run. -* `SessionOptions` - containing some specific tuning options +* `dist_nicelevel` - Level of nice under which tests are run, defaults to + nice level of process. +* `dist_boxing` - If set, will imply use of LSession as default session, + with boxing enabled. +* `dist_taskspernode` - Maximum number of tasks which can be send to one node, + defaults to 15. +* `dist_import_pypy` - Flag to control pypy importing for js regeneration + (defaults to False) Sample configuration:: @@ -688,17 +695,6 @@ Running server is done by ``-w`` command line option or ``--startserver`` (the former might change at some point due to conflicts). -Possible `SessionOptions` arguments: - -* `nice_level` - Level of nice under which tests are run -* `runner_policy` - (for `LSession` only) - contains policy for the test boxig. - `"plain_runner"` means no boxing, `"box_runner"` means boxing. -* `waittime` - Default maximum waiting time for remote host to execute - one test. -* `max_tasks_per_node` - Maximum number of tasks which can be send to one node. -* `import_pypy` - Flag to control pypy importing for js regeneration (defaults - to False) - Development Notes ----------------- From fijal at codespeak.net Thu Jan 18 15:59:10 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 15:59:10 +0100 (CET) Subject: [py-svn] r36941 - in py/branch/config/py/test: . testing Message-ID: <20070118145910.290F0100B5@code0.codespeak.net> Author: fijal Date: Thu Jan 18 15:59:08 2007 New Revision: 36941 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/testing/test_config.py Log: Add a hack which allows to temporarily overwrite the config value. Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Thu Jan 18 15:59:08 2007 @@ -33,6 +33,7 @@ self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") self._parsed = False + self.overwrite_dict = {} def parse(self, args): """ parse cmdline arguments into this config object. @@ -64,13 +65,21 @@ setattr(self.option, opt.dest, opt.default) return self.option + def overwrite(self, name, value): + """ Overwrite value for an option. Very usefull when testing + """ + self.overwrite_dict[name] = value + def getvalue(self, name, path=None): """ return 'name' value looked up from the first conftest file found up the path (including the path itself). if path is None, lookup the value in the initial conftest modules found during command line parsing. """ - return self.conftest.rget(name, path) + try: + return self.overwrite_dict[name] + except KeyError: + return self.conftest.rget(name, path) def getsessionclass(self): """ return Session class determined from cmdline options @@ -89,7 +98,9 @@ def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ global config - oldconfig = py.test.config + oldconfig = py.test.config + # we should not keep this values forever + self.overwrite_dict = {} try: config = py.test.config = Config() config.parse(args) @@ -145,4 +156,4 @@ if option.runbrowser and not option.startserver: print "Cannot point browser when not starting server" option.startserver = True - + # XXX: Shouldn't we fix the session here? Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Thu Jan 18 15:59:08 2007 @@ -72,3 +72,14 @@ print py.process.cmdexec("py.test") finally: old.chdir() + +def test_config_overwrite(): + o = py.test.ensuretemp('testconfigget') + o.ensure("conftest.py").write("x=1") + config = py.test.config._reparse([str(o)]) + assert config.getvalue('x') == 1 + config.overwrite('x', 2) + assert config.getvalue('x') == 2 + config = py.test.config._reparse([str(o)]) + assert config.getvalue('x') == 1 + From hpk at codespeak.net Thu Jan 18 16:08:08 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 16:08:08 +0100 (CET) Subject: [py-svn] r36942 - in py/branch/config/py/test: . testing Message-ID: <20070118150808.8D4DB100A0@code0.codespeak.net> Author: hpk Date: Thu Jan 18 16:08:06 2007 New Revision: 36942 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/testing/test_config.py Log: group things used for testing and not use non-underscore names (otherwise they are consdiered public and are possibly seen from apigen through py.test.config!) Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Thu Jan 18 16:08:06 2007 @@ -33,7 +33,7 @@ self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") self._parsed = False - self.overwrite_dict = {} + self._overwrite_dict = {} def parse(self, args): """ parse cmdline arguments into this config object. @@ -65,11 +65,6 @@ setattr(self.option, opt.dest, opt.default) return self.option - def overwrite(self, name, value): - """ Overwrite value for an option. Very usefull when testing - """ - self.overwrite_dict[name] = value - def getvalue(self, name, path=None): """ return 'name' value looked up from the first conftest file found up the path (including the path itself). @@ -77,7 +72,7 @@ conftest modules found during command line parsing. """ try: - return self.overwrite_dict[name] + return self._overwrite_dict[name] except KeyError: return self.conftest.rget(name, path) @@ -99,8 +94,6 @@ """ this is used from tests that want to re-invoke parse(). """ global config oldconfig = py.test.config - # we should not keep this values forever - self.overwrite_dict = {} try: config = py.test.config = Config() config.parse(args) @@ -108,6 +101,12 @@ finally: config = py.test.config = oldconfig + def _overwrite(self, name, value): + """ this is used from tests to overwrite values irrespectives of conftests. + """ + self._overwrite_dict[name] = value + + # this is the one per-process instance of py.test configuration config = Config() Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Thu Jan 18 16:08:06 2007 @@ -78,7 +78,7 @@ o.ensure("conftest.py").write("x=1") config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 - config.overwrite('x', 2) + config._overwrite('x', 2) assert config.getvalue('x') == 2 config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 From fijal at codespeak.net Thu Jan 18 16:09:36 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 16:09:36 +0100 (CET) Subject: [py-svn] r36943 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118150936.21352100A5@code0.codespeak.net> Author: fijal Date: Thu Jan 18 16:09:30 2007 New Revision: 36943 Modified: py/branch/config/py/test/rsession/box.py py/branch/config/py/test/rsession/hostmanage.py py/branch/config/py/test/rsession/local.py py/branch/config/py/test/rsession/master.py py/branch/config/py/test/rsession/outcome.py py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/testing/test_boxing.py py/branch/config/py/test/rsession/testing/test_config.py py/branch/config/py/test/rsession/testing/test_executor.py py/branch/config/py/test/rsession/testing/test_master.py py/branch/config/py/test/rsession/testing/test_rsession.py py/branch/config/py/test/rsession/testing/test_slave.py py/branch/config/py/test/rsession/testing/test_web.py py/branch/config/py/test/rsession/testing/test_webjs.py py/branch/config/py/test/rsession/web.py Log: at least LSession should work now. Did not touched yet the remote options stuff, but some simplifications were done. Modified: py/branch/config/py/test/rsession/box.py ============================================================================== --- py/branch/config/py/test/rsession/box.py (original) +++ py/branch/config/py/test/rsession/box.py Thu Jan 18 16:09:30 2007 @@ -35,8 +35,7 @@ self.PYTESTSTDOUT = tempdir.join('stdout') self.PYTESTSTDERR = tempdir.join('stderr') - from py.__.test.rsession.rsession import remote_options - nice_level = remote_options.nice_level + nice_level = py.test.config.getvalue('dist_nicelevel') pid = os.fork() if pid: if not continuation: Modified: py/branch/config/py/test/rsession/hostmanage.py ============================================================================== --- py/branch/config/py/test/rsession/hostmanage.py (original) +++ py/branch/config/py/test/rsession/hostmanage.py Thu Jan 18 16:09:30 2007 @@ -142,8 +142,6 @@ for channel in channels: channel.send(None) - from py.__.test.rsession.rsession import session_options - clean = exitfirst while not clean: clean = True @@ -155,7 +153,7 @@ for channel in channels: try: - report.wrapcall(reporter, channel.waitclose, int(session_options.waittime)) + report.wrapcall(reporter, channel.waitclose) except KeyboardInterrupt, SystemExit: raise except: Modified: py/branch/config/py/test/rsession/local.py ============================================================================== --- py/branch/config/py/test/rsession/local.py (original) +++ py/branch/config/py/test/rsession/local.py Thu Jan 18 16:09:30 2007 @@ -33,11 +33,6 @@ outcome.stdout, outcome.stderr = finishcapture(session) return outcome -RunnerPolicy = { - 'plain_runner':plain_runner, - 'box_runner':box_runner -} - def benchmark_runner(item, session, reporter): raise NotImplementedError() Modified: py/branch/config/py/test/rsession/master.py ============================================================================== --- py/branch/config/py/test/rsession/master.py (original) +++ py/branch/config/py/test/rsession/master.py Thu Jan 18 16:09:30 2007 @@ -56,9 +56,7 @@ waiter = lambda: py.std.time.sleep(0.1), max_tasks_per_node=None): if not max_tasks_per_node: - from py.__.test.rsession.rsession import session_options - - max_tasks_per_node = session_options.max_tasks_per_node + max_tasks_per_node = py.test.config.getvalue("dist_taskspernode") all_tests = {} while 1: try: Modified: py/branch/config/py/test/rsession/outcome.py ============================================================================== --- py/branch/config/py/test/rsession/outcome.py (original) +++ py/branch/config/py/test/rsession/outcome.py Thu Jan 18 16:09:30 2007 @@ -32,11 +32,12 @@ relline = lineno - tb_entry.frame.code.firstlineno path = str(tb_entry.path) try: - from py.__.test.rsession.rsession import remote_options - if remote_options.tbstyle == 'long': + if py.test.config.option.tbstyle == 'long': source = str(tb_entry.getsource()) else: source = str(tb_entry.getsource()).split("\n")[relline] + # XXX: Bare except. What can getsource() raise anyway? + # SyntaxError, AttributeError, IndentationError for sure, check it except: source = "" return (relline, lineno, source, path) Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Thu Jan 18 16:09:30 2007 @@ -14,71 +14,9 @@ from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ - box_runner, RunnerPolicy + box_runner from py.__.test.rsession.reporter import LocalReporter, RemoteReporter -class RemoteOptions(object): - def __init__(self, d): - self.d = d - - def __getattr__(self, attr): - if attr == 'd': - return self.__dict__['d'] - return self.d[attr] - - def __setitem__(self, item, val): - self.d[item] = val - -# XXX: Must be initialised somehow -remote_options = RemoteOptions({'we_are_remote':False}) - -class SessionOptions: - defaults = { - 'max_tasks_per_node' : 15, - 'runner_policy' : 'plain_runner', - 'nice_level' : 0, - 'waittime' : 100.0, - 'import_pypy' : False, - } - - config = None - - def getvalue(self, opt): - try: - return getattr(self.config.getvalue('SessionOptions'), opt) - except (KeyError, AttributeError): - try: - return self.defaults[opt] - except KeyError: - raise AttributeError("Option %s undeclared" % opt) - - def bind_config(self, config): - self.config = config - # copy to remote session options - try: - ses_opt = self.config.getvalue('SessionOptions').__dict__ - except KeyError: - ses_opt = self.defaults - for key in self.defaults: - try: - val = ses_opt[key] - except KeyError: - val = self.defaults[key] - remote_options[key] = val - # copy to remote all options - for item, val in config.option.__dict__.items(): - remote_options[item] = val - - def __repr__(self): - return "" % self.config - - def __getattr__(self, attr): - if self.config is None: - raise AttributeError("Need to set up config first") - return self.getvalue(attr) - -session_options = SessionOptions() - class AbstractSession(object): """ An abstract session executes collectors/items through a runner. @@ -188,7 +126,6 @@ """ main loop for running tests. """ args = self.config.remaining - session_options.bind_config(self.config) sshhosts, remotepython, rsync_roots = self.read_distributed_config() reporter, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) @@ -221,10 +158,7 @@ sshhosts = [HostInfo(i) for i in self.config.getvalue("disthosts")] parse_directories(sshhosts) - try: - remotepython = self.config.getvalue("dist_remotepython") - except: - remotepython = None + remotepython = self.config.getvalue("dist_remotepython") return sshhosts, remotepython, rsync_roots def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): @@ -254,7 +188,6 @@ if not self.config.option.nomagic: py.magic.invoke(assertion=1) - session_options.bind_config(self.config) reporter, startserverflag = self.init_reporter(reporter, sshhosts, LocalReporter, args[0]) reporter, checkfun = self.wrap_reporter(reporter) @@ -310,4 +243,7 @@ self.tracer = Tracer(self.docstorage) return apigen_runner else: - return RunnerPolicy[session_options.runner_policy] + if self.config.getvalue('dist_boxing'): + return box_runner + return plain_runner + Modified: py/branch/config/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_boxing.py (original) +++ py/branch/config/py/test/rsession/testing/test_boxing.py Thu Jan 18 16:09:30 2007 @@ -11,10 +11,6 @@ from py.__.test.rsession.testing import example2 from py.__.test.rsession.conftest import option -def setup_module(mod): - from py.__.test.rsession.rsession import remote_options - remote_options['nice_level'] = 0 - def test_basic_boxing(): # XXX: because we do not have option transfer ## if not hasattr(option, 'nocapture') or not option.nocapture: Modified: py/branch/config/py/test/rsession/testing/test_config.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_config.py (original) +++ py/branch/config/py/test/rsession/testing/test_config.py Thu Jan 18 16:09:30 2007 @@ -1,24 +1,6 @@ -""" test session config options +""" empty module for now, will probably have some content soon """ import py -from py.__.test.rsession.rsession import session_options, SessionOptions,\ - remote_options -def test_session_opts(): - tmpdir = py.test.ensuretemp("sessionopts") - tmpdir.ensure("conftest.py").write("""class SessionOptions: - max_tasks_per_node = 5 - nice_level = 10 - """) - tmp2 = py.test.ensuretemp("xxx") - args = [str(tmpdir)] - config = py.test.config._reparse(args) - session_options.bind_config(config) - assert session_options.max_tasks_per_node == 5 - assert remote_options.nice_level == 10 - config = py.test.config._reparse([str(tmp2)]) - session_options.bind_config(config) - assert session_options.max_tasks_per_node == \ - SessionOptions.defaults['max_tasks_per_node'] Modified: py/branch/config/py/test/rsession/testing/test_executor.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_executor.py (original) +++ py/branch/config/py/test/rsession/testing/test_executor.py Thu Jan 18 16:09:30 2007 @@ -10,8 +10,6 @@ def setup_module(mod): mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() - from py.__.test.rsession.rsession import remote_options - remote_options['nice_level'] = 0 def XXXtest_executor_passing_function(): ex = Executor(example1.f1) Modified: py/branch/config/py/test/rsession/testing/test_master.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_master.py (original) +++ py/branch/config/py/test/rsession/testing/test_master.py Thu Jan 18 16:09:30 2007 @@ -13,15 +13,12 @@ from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec from py.__.test.rsession import report -from py.__.test.rsession.rsession import session_options, remote_options from py.__.test.rsession.hostmanage import HostInfo def setup_module(mod): # bind an empty config config = py.test.config._reparse([]) - config.option.max_tasks_per_node = 10 - session_options.bind_config(config) - #assert not remote_options.exitfirst + config.overwrite('dist_taskspernode', 10) mod.pkgdir = py.path.local(py.__file__).dirpath() class DummyGateway(object): Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 16:09:30 2007 @@ -5,7 +5,7 @@ import py from py.__.test.rsession import report from py.__.test.rsession.rsession import RSession, parse_directories,\ - session_options, remote_options, parse_directories + parse_directories from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\ HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ @@ -155,7 +155,6 @@ teardown_events = [] config = py.test.config._reparse([]) - session_options.bind_config(config) nodes = init_hosts(setup_events.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) teardown_hosts(teardown_events.append, @@ -182,7 +181,6 @@ allevents = [] config = py.test.config._reparse([]) - session_options.bind_config(config) nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) @@ -224,7 +222,6 @@ hosts = [HostInfo('localhost')] parse_directories(hosts) config = py.test.config._reparse([]) - session_options.bind_config(config) d = remote_options.d.copy() d['custom'] = 'custom' nodes = init_hosts(allevents.append, hosts, pkgdir, Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Thu Jan 18 16:09:30 2007 @@ -11,10 +11,7 @@ py.test.skip("rsession is unsupported on Windows.") def setup_module(module): - from py.__.test.rsession.rsession import session_options module.rootdir = py.path.local(py.__file__).dirpath().dirpath() - config = py.test.config._reparse([]) - session_options.bind_config(config) # ---------------------------------------------------------------------- # inlined testing functions used below @@ -35,8 +32,7 @@ asddsa def funcoption(): - from py.__.test.rsession.rsession import remote_options - assert remote_options.we_are_remote + assert py.test.config.getvalue('we_are_remote') def funcoptioncustom(): from py.__.test.rsession.rsession import remote_options Modified: py/branch/config/py/test/rsession/testing/test_web.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_web.py (original) +++ py/branch/config/py/test/rsession/testing/test_web.py Thu Jan 18 16:09:30 2007 @@ -14,9 +14,7 @@ def setup_module(mod): config = py.test.config._reparse([]) - from py.__.test.rsession.rsession import session_options - session_options.bind_config(config) - session_options.import_pypy = True + config.overwrite('dist_import_pypy', True) from py.__.test.rsession.web import TestHandler as _TestHandler from py.__.test.rsession.web import MultiQueue mod._TestHandler = _TestHandler Modified: py/branch/config/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_webjs.py (original) +++ py/branch/config/py/test/rsession/testing/test_webjs.py Thu Jan 18 16:09:30 2007 @@ -7,6 +7,8 @@ from pypy.translator.js.tester import schedule_callbacks here = py.magic.autopath().dirpath() +py.test.skip("will fix later, multiple issues") + def setup_module(mod): # load HTML into window object html = here.join('../webdata/index.html').read() Modified: py/branch/config/py/test/rsession/web.py ============================================================================== --- py/branch/config/py/test/rsession/web.py (original) +++ py/branch/config/py/test/rsession/web.py Thu Jan 18 16:09:30 2007 @@ -14,7 +14,7 @@ import socket import py -from py.__.test.rsession.rsession import RSession, session_options +from py.__.test.rsession.rsession import RSession from py.__.test.rsession import report from py.__.test import collect from py.__.test.rsession.webdata import json @@ -25,7 +25,7 @@ try: try: - if not session_options.import_pypy: + if not py.test.config.getvalue('dist_import_pypy'): raise ImportError except AttributeError: pass From hpk at codespeak.net Thu Jan 18 16:19:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 16:19:49 +0100 (CET) Subject: [py-svn] r36944 - py/branch/config/py/test/tkinter/testing Message-ID: <20070118151949.5BD12100A5@code0.codespeak.net> Author: hpk Date: Thu Jan 18 16:19:47 2007 New Revision: 36944 Modified: py/branch/config/py/test/tkinter/testing/test_backend.py Log: fix non-uniform way of invocation Modified: py/branch/config/py/test/tkinter/testing/test_backend.py ============================================================================== --- py/branch/config/py/test/tkinter/testing/test_backend.py (original) +++ py/branch/config/py/test/tkinter/testing/test_backend.py Thu Jan 18 16:19:47 2007 @@ -178,9 +178,9 @@ assert l[0] is None def test_start_tests(self): - config = py.test.config._reparse([]) + config = py.test.config._reparse([datadir/'filetest.py']) self.backend.start_tests(config = config, - args = [str(datadir / 'filetest.py')], + args = config.remaining, tests = []) while self.backend.running: self.backend.update() From hpk at codespeak.net Thu Jan 18 16:22:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 16:22:07 +0100 (CET) Subject: [py-svn] r36945 - py/dist/py Message-ID: <20070118152207.A2FF4100A5@code0.codespeak.net> Author: hpk Date: Thu Jan 18 16:22:05 2007 New Revision: 36945 Modified: py/dist/py/LICENSE Log: before i forget add a least a few obvious names Modified: py/dist/py/LICENSE ============================================================================== --- py/dist/py/LICENSE (original) +++ py/dist/py/LICENSE Thu Jan 18 16:22:05 2007 @@ -8,6 +8,9 @@ Holger Krekel merlinux GmbH, Germany Armin Rigo + Carl Friedrich Bolz + Maciek Fijalkowski + Guido Wesdorp Jan Balster Contributors include:: From hpk at codespeak.net Thu Jan 18 16:43:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 16:43:42 +0100 (CET) Subject: [py-svn] r36946 - py/branch/config/py/test Message-ID: <20070118154342.D5D0C100A5@code0.codespeak.net> Author: hpk Date: Thu Jan 18 16:43:39 2007 New Revision: 36946 Modified: py/branch/config/py/test/defaultconftest.py Log: adding some default options for distributed testing Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Thu Jan 18 16:43:39 2007 @@ -19,6 +19,14 @@ 'TkinterSession': 'py.__.test.tkinter.reportsession', } +dist_nicelevel = 10 +#dist_hosts: needs to be provided by user +#dist_rsync_roots: needs to be provided by a user??? +#dist_remotepython: could be "python" by default +dist_boxing = 1 +dist_taskspernode = 15 +# dist_import_pypy: XXX i'd like to not have this option at all + def adddefaultoptions(): py.test.config.addoptions('general options', Option('-v', '--verbose', From hpk at codespeak.net Thu Jan 18 16:47:06 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 16:47:06 +0100 (CET) Subject: [py-svn] r36947 - py/branch/config/py/test/rsession/testing Message-ID: <20070118154706.5AB1C100A5@code0.codespeak.net> Author: hpk Date: Thu Jan 18 16:47:02 2007 New Revision: 36947 Modified: py/branch/config/py/test/rsession/testing/test_master.py py/branch/config/py/test/rsession/testing/test_web.py Log: fix the tests use my 36945 change Modified: py/branch/config/py/test/rsession/testing/test_master.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_master.py (original) +++ py/branch/config/py/test/rsession/testing/test_master.py Thu Jan 18 16:47:02 2007 @@ -18,7 +18,7 @@ def setup_module(mod): # bind an empty config config = py.test.config._reparse([]) - config.overwrite('dist_taskspernode', 10) + config._overwrite('dist_taskspernode', 10) mod.pkgdir = py.path.local(py.__file__).dirpath() class DummyGateway(object): Modified: py/branch/config/py/test/rsession/testing/test_web.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_web.py (original) +++ py/branch/config/py/test/rsession/testing/test_web.py Thu Jan 18 16:47:02 2007 @@ -14,7 +14,7 @@ def setup_module(mod): config = py.test.config._reparse([]) - config.overwrite('dist_import_pypy', True) + config._overwrite('dist_import_pypy', True) from py.__.test.rsession.web import TestHandler as _TestHandler from py.__.test.rsession.web import MultiQueue mod._TestHandler = _TestHandler From hpk at codespeak.net Thu Jan 18 16:54:57 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 16:54:57 +0100 (CET) Subject: [py-svn] r36948 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118155457.26E1B100A9@code0.codespeak.net> Author: hpk Date: Thu Jan 18 16:54:55 2007 New Revision: 36948 Removed: py/branch/config/py/test/rsession/conftest.py Modified: py/branch/config/py/test/rsession/testing/test_boxing.py Log: remove the skip_kill_test option, because it's not clear why exactly it is there and if it is still needed. Deleted: /py/branch/config/py/test/rsession/conftest.py ============================================================================== --- /py/branch/config/py/test/rsession/conftest.py Thu Jan 18 16:54:55 2007 +++ (empty file) @@ -1,10 +0,0 @@ -import py -Option = py.test.config.Option - -option = py.test.config.addoptions("boxing test options", - Option('', '--skip-kill-test', action='store_true', default=False, - dest='skip_kill_test', - help='skip a certain test that checks for os.kill results, this ' - 'should be used when kill() is not allowed for the current ' - 'user'), -) Modified: py/branch/config/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_boxing.py (original) +++ py/branch/config/py/test/rsession/testing/test_boxing.py Thu Jan 18 16:54:55 2007 @@ -73,8 +73,6 @@ assert b.retval == 2 def test_box_killer(): - if option.skip_kill_test: - py.test.skip('skipping kill test') class A: pass info = A() From guido at codespeak.net Thu Jan 18 16:56:11 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 16:56:11 +0100 (CET) Subject: [py-svn] r36949 - in py/dist/py/apigen: . testing Message-ID: <20070118155611.1536C100AD@code0.codespeak.net> Author: guido Date: Thu Jan 18 16:56:09 2007 New Revision: 36949 Added: py/dist/py/apigen/testing/test_htmlgen.py Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/linker.py py/dist/py/apigen/testing/test_apigen_example.py Log: Cleaned up the navigation code, there's now a single, relatively generic (and not even too many edge cases handled! ;) build_navigation function for all API pages. Note that certain nav-related tests don't pass yet (missing sibling functions in function view, for instance): those will be fixed shortly. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Thu Jan 18 16:56:09 2007 @@ -3,6 +3,7 @@ from py.__.apigen.layout import LayoutPage from py.__.apigen.source import browser as source_browser from py.__.apigen.source import html as source_html +from py.__.apigen.tracer.description import is_private html = py.xml.html raw = py.xml.raw @@ -41,7 +42,7 @@ class Navigation(html.div): style = html.Style(min_height='99%', float='left', margin_top='1.2em') - class NavigationItem(html.span): + class NavigationItem(html.div): pass class BaseDescription(html.a): @@ -142,9 +143,12 @@ H.NamespaceDef(namespace_dotted_name), ) for dotted_name in item_dotted_names: + itemname = dotted_name.split('.')[-1] + if is_private(itemname): + continue snippet.append( H.NamespaceItem( - H.a(dotted_name.split('.')[-1], + H.a(itemname, href=linker.get_lazyhref(dotted_name) ) ) @@ -152,103 +156,42 @@ return snippet # XXX nav functions need cleanup!! -def build_namespace_navigation(linker, dsa, dotted_name, item_dotted_names): - navitems = [] - path = dotted_name.split('.') - indent = 0 - if len(path) > 1: - parent_dotted_name = '.'.join(path[:-1]) - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(parent_dotted_name, - href=linker.get_lazyhref( - parent_dotted_name)))) - navitems.append(H.br()) - indent += 1 - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(dotted_name.split('.')[-1], - href=linker.get_lazyhref(dotted_name)))) - navitems.append(H.br()) - indent += 1 - for item_dotted_name in item_dotted_names: - navitems.append(indent * 2 * u'\xa0') - itemname = item_dotted_name.split('.')[-1] - navitems.append(H.NavigationItem( - H.a(itemname, - href=linker.get_lazyhref(item_dotted_name)))) - navitems.append(H.br()) - return H.Navigation(*navitems) +def build_navitem_html(linker, name, dotted_name, indent): + href = linker.get_lazyhref(dotted_name) + return H.NavigationItem((indent * 2 * u'\xa0'), H.a(name, href=href)) -def build_class_navigation(linker, dsa, dotted_name): +def build_api_navigation(linker, dsa, dotted_name, item_dotted_names): navitems = [] - path = dotted_name.split('.') - indent = 0 - if len(path) > 1: - parent_dotted_name = '.'.join(path[:-1]) - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(parent_dotted_name, - href=linker.get_lazyhref( - parent_dotted_name)))) - navitems.append(H.br()) - indent += 1 - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(dotted_name.split('.')[-1], - href=linker.get_lazyhref(dotted_name)))) - navitems.append(H.br()) - indent += 1 - for funcname in dsa.get_class_methods(dotted_name): - func_dotted_name = '%s.%s' % (dotted_name, funcname) - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(funcname, - href=linker.get_lazyhref(func_dotted_name)))) - navitems.append(H.br()) - return H.Navigation(*navitems) -def build_method_navigation(linker, dotted_name): - # import dotted name - # build nav from self + parent + parent's items - return H.Navigation('navigation') + # top namespace, index.html + module_name = dsa.get_module_name().split('/')[-1] + navitems.append(build_navitem_html(linker, module_name, '', 0)) -def build_function_navigation(linker, dsa, dotted_name): - navitems = [] + # build html for each item in path + indent = 1 path = dotted_name.split('.') - indent = 0 if len(path) > 1: - parent_dotted_name = '.'.join(path[:-1]) - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(parent_dotted_name, - href=linker.get_lazyhref( - parent_dotted_name)))) - navitems.append(H.br()) - indent += 1 - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(dotted_name.split('.')[-1], - href=linker.get_lazyhref(dotted_name)))) - navitems.append(H.br()) - indent += 1 - for cls_dotted_name in dsa.get_class_names(): - if not cls_dotted_name.startswith('%s.' % (dotted_name,)): - continue - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(cls_dotted_name, - href=linker.get_lazyhref( - cls_dotted_name)))) - for func_dotted_name in dsa.get_function_names(): - if not func_dotted_name.startswith('%s.' % (dotted_name,)): + for i in xrange(len(path)): + if i < 1: continue - navitems.append(indent * 2 * u'\xa0') - navitems.append(H.NavigationItem( - H.a(func_dotted_name, - href=linker.get_lazyhref( - func_dotted_name)))) - navitems.append(H.br()) + name = path[i] + parent_dotted_name = '.'.join(path[:i]) + navitems.append(build_navitem_html(linker, name, + parent_dotted_name, indent)) + indent += 1 + + # now build sub items + if dotted_name != '': + name = dotted_name.split('.')[-1] + navitems.append(build_navitem_html(linker, name, dotted_name, indent)) + indent += 1 + for item_dotted_name in item_dotted_names: + print 'item_dotted_name:', item_dotted_name + itemname = item_dotted_name.split('.')[-1] + if is_private(itemname): + continue + navitems.append(build_navitem_html(linker, itemname, + item_dotted_name, indent)) return H.Navigation(*navitems) re = py.std.re @@ -296,7 +239,6 @@ reloutputpath = "source/%s.html" % (relfspath,) outputpath = outputbase.join(reloutputpath) linker.set_link(str(fspath), reloutputpath) - print "setting source link for", fspath passed.append((fspath, outputpath)) return passed @@ -324,8 +266,10 @@ passed = [] methodsdata = [] for dotted_name in classes_dotted_names: + parent_dotted_name = dotted_name.rsplit('.', 1)[0] tag = build_class_view(base, linker, dsa, dotted_name) - nav = build_class_navigation(linker, dsa, dotted_name) + nav = build_api_navigation(linker, dsa, parent_dotted_name, + [dotted_name]) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -345,12 +289,13 @@ targetpath = outputbase.ensure(reltargetpath) targetpath.write(content.encode("utf8")) -def prepare_method_api_pages(linker, dsa, base, methods_dotted_names): +def prepare_method_api_pages(linker, dsa, base, method_dotted_names): passed = [] - for dotted_name in methods_dotted_names: + for dotted_name in method_dotted_names: + parent_dotted_name = dotted_name.rsplit('.', 1)[0] tag = build_full_method_view(base, linker, dsa, dotted_name) - nav = build_class_navigation(linker, dsa, - '.'.join(dotted_name.split('.')[:-1])) + nav = build_api_navigation(linker, dsa, parent_dotted_name, + [dotted_name]) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -365,12 +310,14 @@ targetpath.ensure() targetpath.write(content.encode("utf8")) -def prepare_function_api_pages(linker, dsa, base, methods_dotted_names): +def prepare_function_api_pages(linker, dsa, base, method_dotted_names): passed = [] - for dotted_name in methods_dotted_names: + for dotted_name in method_dotted_names: # XXX should we create a build_full_function_view instead? + parent_dotted_name = dotted_name.rsplit('.', 1)[0] tag = build_full_method_view(base, linker, dsa, dotted_name) - nav = build_function_navigation(linker, dsa, dotted_name) + nav = build_api_navigation(linker, dsa, parent_dotted_name, + [dotted_name]) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -389,32 +336,44 @@ ret = {} class_names = dsa.get_class_names() for dn in dotted_names: - if not '.' in dn: - continue - namespace, itemname = dn.rsplit('.', 1) - if namespace in class_names: - continue - ret.setdefault(namespace, []).append(dn) + path = dn.split('.') + for i in xrange(len(path)): + ns = '.'.join(path[:i]) + itempath = '.'.join(path[:i + 1]) + if ns not in ret: + ret[ns] = [] + if itempath not in ret[ns]: + ret[ns].append(itempath) return ret def prepare_namespace_api_pages(linker, dsa, base, all_dotted_names): passed = [] + module_name = dsa.get_module_name().split('/')[-1] + namespace_tree = create_namespace_tree(dsa, all_dotted_names) - for dotted_name, subitem_dotted_names in namespace_tree.iteritems(): + names = namespace_tree.keys() + names.sort() + for dotted_name in names: + subitem_dotted_names = namespace_tree[dotted_name] tag = build_namespace_view(base, linker, dsa, dotted_name, subitem_dotted_names) - nav = build_namespace_navigation(linker, dsa, dotted_name, - subitem_dotted_names) - reltargetpath = "api/%s.html" % (dotted_name,) + nav = build_api_navigation(linker, dsa, dotted_name, + subitem_dotted_names) + if dotted_name == '': + reltargetpath = 'api/index.html' + else: + reltargetpath = 'api/%s.html' % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) return passed def build_namespace_api_pages(linker, data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: - page = wrap_page(project, 'index of namespace %s' % (dotted_name,), + if dotted_name == '': + dotted_name = 'project root' + page = wrap_page(project, 'index of %s namespace' % (dotted_name,), tag, nav, outputbase) - content = linker.call_withbase(reltargetpath, page.unicode) + content = linker.call_withbase(reltargetpath, page.unicode) targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) Modified: py/dist/py/apigen/linker.py ============================================================================== --- py/dist/py/apigen/linker.py (original) +++ py/dist/py/apigen/linker.py Thu Jan 18 16:56:09 2007 @@ -20,13 +20,16 @@ self._linkid2target = {} def get_lazyhref(self, linkid): + if linkid == '_thread.ThreadOut._get': + raise '_get' 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 - def get_target(self, linkid): + def get_target(self, linkid): linktarget = self._linkid2target[linkid] if self.fromlocation is not None: linktarget = relpath(self.fromlocation, linktarget) Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Thu Jan 18 16:56:09 2007 @@ -85,7 +85,7 @@ prepare_source_pages(self.linker, self.fs_root, base) prepare_namespace_api_pages(self.linker, self.dsa, base, ['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', 'pkg.SomeOtherClass']) + 'pkg.SomeClass', 'pkg.SomeSubClass']) data = prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) build_function_api_pages(self.linker, data, self.project, base) @@ -181,8 +181,10 @@ ['pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - assert namespace_tree == {'pkg.sub': ['pkg.sub.func'], - 'pkg': ['pkg.SomeClass', 'pkg.SomeSubClass']} + assert namespace_tree == {'': ['pkg'], + 'pkg.sub': ['pkg.sub.func'], + 'pkg': ['pkg.sub', 'pkg.SomeClass', + 'pkg.SomeSubClass']} def test_build_namespace_api_pages_index(self): base = py.test.ensuretemp('build_namespace_api_pages') @@ -234,12 +236,51 @@ prepare_source_pages(self.linker, self.fs_root, base) prepare_namespace_api_pages(self.linker, self.dsa, base, ['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', 'pkg.SomeOtherClass']) + 'pkg.SomeClass', 'pkg.SomeSubClass']) data = prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) build_function_api_pages(self.linker, data, self.project, base) funcfile = base.join('api/pkg.sub.func.html') html = funcfile.read() + assert '' in html + assert '' in html assert '' in html - + assert '' in html + + def test_build_function_navigation(self): + base = py.test.ensuretemp('build_function_navigation') + prepare_source_pages(self.linker, self.fs_root, base) + prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', 'pkg.SomeSubClass']) + prepare_function_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func']) + nav = build_navigation(self.linker, self.dsa, 'pkg.sub', + ['pkg.sub.func']) + html = nav.unicode(indent=0) + assert (u'' + u'
\xa0\xa0sub
' + u'
\xa0\xa0\xa0\xa0' + u'sub
' + u'
\xa0\xa0\xa0\xa0\xa0\xa0' + u'func
' + ) in html + + def test_build_root_namespace_view(self): + base = py.test.ensuretemp('build_root_namespace_view') + prepare_source_pages(self.linker, self.fs_root, base) + data = prepare_namespace_api_pages(self.linker, self.dsa, base, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) + prepare_function_api_pages(self.linker, self.dsa, base, + ['pkg.sub.func']) + prepare_class_api_pages(self.linker, self.dsa, base, + ['pkg.SomeClass', 'pkg.SomeSubClass']) + build_namespace_api_pages(self.linker, data, self.project, base) + rootfile = base.join('api/index.html') + assert rootfile.check() + html = rootfile.read() + assert '' in html + _checkhtml(html) Added: py/dist/py/apigen/testing/test_htmlgen.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/testing/test_htmlgen.py Thu Jan 18 16:56:09 2007 @@ -0,0 +1,11 @@ +from py.__.apigen.htmlgen import build_navitem_html +from py.__.apigen.linker import Linker + +def test_build_navitem_html(): + l = Linker() + l.set_link('spam.eggs.foo', 'foo.html') + h = build_navitem_html(l, 'foo', 'spam.eggs.foo', 0) + assert unicode(h) == u'' + h = build_navitem_html(l, 'bar', 'spam.eggs.foo', 1) + assert unicode(h) == u'
\xa0\xa0bar
' + From guido at codespeak.net Thu Jan 18 16:56:31 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 16:56:31 +0100 (CET) Subject: [py-svn] r36950 - py/dist/py/apigen Message-ID: <20070118155631.DCD38100B2@code0.codespeak.net> Author: guido Date: Thu Jan 18 16:56:31 2007 New Revision: 36950 Modified: py/dist/py/apigen/linker.py Log: Oops, debug print. Modified: py/dist/py/apigen/linker.py ============================================================================== --- py/dist/py/apigen/linker.py (original) +++ py/dist/py/apigen/linker.py Thu Jan 18 16:56:31 2007 @@ -20,8 +20,6 @@ self._linkid2target = {} def get_lazyhref(self, linkid): - if linkid == '_thread.ThreadOut._get': - raise '_get' return LazyHref(self, linkid) def set_link(self, linkid, target): From hpk at codespeak.net Thu Jan 18 17:02:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 17:02:49 +0100 (CET) Subject: [py-svn] r36951 - py/branch/config/py/doc Message-ID: <20070118160249.C84F3100AD@code0.codespeak.net> Author: hpk Date: Thu Jan 18 17:02:48 2007 New Revision: 36951 Modified: py/branch/config/py/doc/test.txt Log: streamline description of dist-options a bit (maciej: i think we should avoid at least to advertise 'import_pypy', it's really only used internally, isn't it?) Modified: py/branch/config/py/doc/test.txt ============================================================================== --- py/branch/config/py/doc/test.txt (original) +++ py/branch/config/py/doc/test.txt Thu Jan 18 17:02:48 2007 @@ -669,18 +669,14 @@ The options that you need to specify in that conftest.py file are: -* `dist_hosts` - a list of ssh addresses (including a specific path if it - should be different than the default: ``$HOME/pytestcache-hostname``) +* **`dist_hosts`**: a required list of ssh addresses (which each may + include a path, default path is: ``$HOME/pytestcache-HOSTNAME``) * `dist_rsync_roots` - a list of packages to copy to the remote machines. -* `dist_remotepython` - the remote python to run. -* `dist_nicelevel` - Level of nice under which tests are run, defaults to - nice level of process. -* `dist_boxing` - If set, will imply use of LSession as default session, - with boxing enabled. -* `dist_taskspernode` - Maximum number of tasks which can be send to one node, - defaults to 15. -* `dist_import_pypy` - Flag to control pypy importing for js regeneration - (defaults to False) +* `dist_remotepython` - the remote python executable to run. +* `dist_nicelevel` - process priority of remote nodes. +* `dist_boxing` - will run each single test in a separate process + (allowing to survive segfaults for example) +* `dist_taskspernode` - Maximum number of tasks being queued to remote nodes Sample configuration:: From fijal at codespeak.net Thu Jan 18 17:05:07 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 17:05:07 +0100 (CET) Subject: [py-svn] r36952 - py/branch/config/py/test Message-ID: <20070118160507.B8DC5100AF@code0.codespeak.net> Author: fijal Date: Thu Jan 18 17:05:06 2007 New Revision: 36952 Modified: py/branch/config/py/test/defaultconftest.py Log: Add my version of this (forgotten to check in with 36943) Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Thu Jan 18 17:05:06 2007 @@ -19,13 +19,13 @@ 'TkinterSession': 'py.__.test.tkinter.reportsession', } -dist_nicelevel = 10 -#dist_hosts: needs to be provided by user -#dist_rsync_roots: needs to be provided by a user??? -#dist_remotepython: could be "python" by default -dist_boxing = 1 dist_taskspernode = 15 -# dist_import_pypy: XXX i'd like to not have this option at all +dist_boxing = False # I would go for default "False" now +#dist_rsync_roots: might be provided by user +#dist_hosts: needs to be provided by user +dist_import_pypy = False +dist_nicelevel = py.std.os.nice(0) # I like this attempt so nice py.test works +dist_remotepython = py.std.sys.executable # closer to execnet'ss default def adddefaultoptions(): py.test.config.addoptions('general options', From fijal at codespeak.net Thu Jan 18 17:07:55 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 17:07:55 +0100 (CET) Subject: [py-svn] r36953 - py/branch/config/py/test/rsession/testing Message-ID: <20070118160755.60D32100AF@code0.codespeak.net> Author: fijal Date: Thu Jan 18 17:07:53 2007 New Revision: 36953 Added: py/branch/config/py/test/rsession/testing/test_rconfig.py - copied unchanged from r36951, py/branch/config/py/test/rsession/testing/test_config.py Removed: py/branch/config/py/test/rsession/testing/test_config.py Log: Change name, because I don't want to loose edit history. Deleted: /py/branch/config/py/test/rsession/testing/test_config.py ============================================================================== --- /py/branch/config/py/test/rsession/testing/test_config.py Thu Jan 18 17:07:53 2007 +++ (empty file) @@ -1,6 +0,0 @@ - -""" empty module for now, will probably have some content soon -""" - -import py - From fijal at codespeak.net Thu Jan 18 17:25:08 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 17:25:08 +0100 (CET) Subject: [py-svn] r36954 - py/branch/config/py/test/rsession/testing Message-ID: <20070118162508.D8ACF100B0@code0.codespeak.net> Author: fijal Date: Thu Jan 18 17:25:06 2007 New Revision: 36954 Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py Log: Add a test for rconfig module Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rconfig.py (original) +++ py/branch/config/py/test/rsession/testing/test_rconfig.py Thu Jan 18 17:25:06 2007 @@ -1,6 +1,36 @@ -""" empty module for now, will probably have some content soon +""" testing of remote config """ import py +from py.__.test.rsession.rconfig import RConfig, make_repr +def test_basic_rconfig(): + tmp = py.test.ensuretemp("rconfig") + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write(py.code.Source(""" + x = 1 + """)) + config = py.test.config._reparse([str(tmp)]) + repr = make_repr(config, to_send=['x']) + rconfig = RConfig(repr) + assert rconfig.getvalue('x') == 1 + +def test_option_rconfig(): + tmp = py.test.ensuretemp("rconfigopt") + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write(py.code.Source(""" + import py + 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.test.config._reparse([str(tmp)]) + repr = make_repr(config, to_send=['x']) + rconfig = RConfig(repr) + assert rconfig.option.g == 42 + config = py.test.config._reparse([str(tmp), "-g", "11"]) + repr = make_repr(config, to_send=['x']) + rconfig = RConfig(repr) + assert rconfig.option.g == 11 From hpk at codespeak.net Thu Jan 18 17:44:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 17:44:23 +0100 (CET) Subject: [py-svn] r36955 - in py/branch/config/py/test: . rsession rsession/testing Message-ID: <20070118164423.7CA54100AD@code0.codespeak.net> Author: hpk Date: Thu Jan 18 17:44:21 2007 New Revision: 36955 Modified: py/branch/config/py/test/defaultconftest.py py/branch/config/py/test/rsession/testing/test_web.py py/branch/config/py/test/rsession/web.py Log: some mods to defaultconfest dist options and use "_dist_import_pypy", i.e. a leading underscore at least Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Thu Jan 18 17:44:21 2007 @@ -19,13 +19,13 @@ 'TkinterSession': 'py.__.test.tkinter.reportsession', } -dist_taskspernode = 15 -dist_boxing = False # I would go for default "False" now -#dist_rsync_roots: might be provided by user #dist_hosts: needs to be provided by user -dist_import_pypy = False +#dist_rsync_roots: might be provided by user, defaults to??? +dist_remotepython = "python" +dist_taskspernode = 15 +dist_boxing = False dist_nicelevel = py.std.os.nice(0) # I like this attempt so nice py.test works -dist_remotepython = py.std.sys.executable # closer to execnet'ss default +_dist_import_pypy = False # used for regenerating JS application def adddefaultoptions(): py.test.config.addoptions('general options', Modified: py/branch/config/py/test/rsession/testing/test_web.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_web.py (original) +++ py/branch/config/py/test/rsession/testing/test_web.py Thu Jan 18 17:44:21 2007 @@ -14,7 +14,7 @@ def setup_module(mod): config = py.test.config._reparse([]) - config._overwrite('dist_import_pypy', True) + config._overwrite('_dist_import_pypy', True) from py.__.test.rsession.web import TestHandler as _TestHandler from py.__.test.rsession.web import MultiQueue mod._TestHandler = _TestHandler Modified: py/branch/config/py/test/rsession/web.py ============================================================================== --- py/branch/config/py/test/rsession/web.py (original) +++ py/branch/config/py/test/rsession/web.py Thu Jan 18 17:44:21 2007 @@ -25,7 +25,7 @@ try: try: - if not py.test.config.getvalue('dist_import_pypy'): + if not py.test.config.getvalue('_dist_import_pypy'): raise ImportError except AttributeError: pass From fijal at codespeak.net Thu Jan 18 17:48:13 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 17:48:13 +0100 (CET) Subject: [py-svn] r36956 - py/branch/config/py/test Message-ID: <20070118164813.9FB5A100B4@code0.codespeak.net> Author: fijal Date: Thu Jan 18 17:48:12 2007 New Revision: 36956 Modified: py/branch/config/py/test/defaultconftest.py Log: Explanation + possible windows fix Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Thu Jan 18 17:48:12 2007 @@ -20,11 +20,15 @@ } #dist_hosts: needs to be provided by user -#dist_rsync_roots: might be provided by user, defaults to??? +#dist_rsync_roots: might be provided by user, if not present or None, +# whole pkgdir will be rsynced dist_remotepython = "python" dist_taskspernode = 15 dist_boxing = False -dist_nicelevel = py.std.os.nice(0) # I like this attempt so nice py.test works +if hasattr(py.std.os, 'nice'): + dist_nicelevel = py.std.os.nice(0) # nice py.test works +else: + dist_nicelevel = 0 _dist_import_pypy = False # used for regenerating JS application def adddefaultoptions(): From fijal at codespeak.net Thu Jan 18 17:55:52 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 17:55:52 +0100 (CET) Subject: [py-svn] r36957 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118165552.07776100AD@code0.codespeak.net> Author: fijal Date: Thu Jan 18 17:55:50 2007 New Revision: 36957 Added: py/branch/config/py/test/rsession/rconfig.py Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py Log: First passing test Added: py/branch/config/py/test/rsession/rconfig.py ============================================================================== --- (empty file) +++ py/branch/config/py/test/rsession/rconfig.py Thu Jan 18 17:55:50 2007 @@ -0,0 +1,16 @@ + +""" File which provides remote config, with similiar interface to +config, but in a marshallable way +""" + +DEFAULT_LIST = ['dist_nicelevel'] + +def make_repr(config, to_send=DEFAULT_LIST): + d = dict([(name, config.getvalue(name)) for name in to_send]) + return to_send, d + +def parse_repr(config, repr): + defaultconftest = config.conftest.getconftestmodules(None)[0] + names, d = repr + for name in names: + setattr(defaultconftest, name, d[name]) Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rconfig.py (original) +++ py/branch/config/py/test/rsession/testing/test_rconfig.py Thu Jan 18 17:55:50 2007 @@ -3,7 +3,12 @@ """ import py -from py.__.test.rsession.rconfig import RConfig, make_repr +from py.__.test.rsession.rconfig import make_repr, parse_repr + +def setup_module(mod): + t = py.test.ensuretemp("rconfigclear") + t.ensure("conftest.py") + mod.empty_mod = t def test_basic_rconfig(): tmp = py.test.ensuretemp("rconfig") @@ -13,10 +18,14 @@ """)) config = py.test.config._reparse([str(tmp)]) repr = make_repr(config, to_send=['x']) - rconfig = RConfig(repr) - assert rconfig.getvalue('x') == 1 + config = py.test.config._reparse([str(empty_mod)]) + py.test.raises(KeyError, "config.getvalue('x')") + parse_repr(config, repr) + # warning, this will leave garbage in our defaultconftest + assert config.getvalue('x') == 1 def test_option_rconfig(): + py.test.skip("finish that one") tmp = py.test.ensuretemp("rconfigopt") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write(py.code.Source(""" From hpk at codespeak.net Thu Jan 18 18:01:40 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 18:01:40 +0100 (CET) Subject: [py-svn] r36958 - py/branch/config/py/test/rsession/testing Message-ID: <20070118170140.4B4BD1009F@code0.codespeak.net> Author: hpk Date: Thu Jan 18 18:01:38 2007 New Revision: 36958 Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py Log: you don't need to str() on reparse Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rconfig.py (original) +++ py/branch/config/py/test/rsession/testing/test_rconfig.py Thu Jan 18 18:01:38 2007 @@ -16,9 +16,9 @@ tmp.ensure("conftest.py").write(py.code.Source(""" x = 1 """)) - config = py.test.config._reparse([str(tmp)]) + config = py.test.config._reparse([tmp]) repr = make_repr(config, to_send=['x']) - config = py.test.config._reparse([str(empty_mod)]) + config = py.test.config._reparse([empty_mod]) py.test.raises(KeyError, "config.getvalue('x')") parse_repr(config, repr) # warning, this will leave garbage in our defaultconftest @@ -35,11 +35,11 @@ Option('-g', '--glong', action="store", default=42, type="int", dest="gdest", help="g value.")) """)) - config = py.test.config._reparse([str(tmp)]) + config = py.test.config._reparse([tmp]) repr = make_repr(config, to_send=['x']) rconfig = RConfig(repr) assert rconfig.option.g == 42 - config = py.test.config._reparse([str(tmp), "-g", "11"]) + config = py.test.config._reparse([tmp, "-g", "11"]) repr = make_repr(config, to_send=['x']) rconfig = RConfig(repr) assert rconfig.option.g == 11 From fijal at codespeak.net Thu Jan 18 18:04:38 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 18:04:38 +0100 (CET) Subject: [py-svn] r36959 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118170438.4490E100A0@code0.codespeak.net> Author: fijal Date: Thu Jan 18 18:04:36 2007 New Revision: 36959 Modified: py/branch/config/py/test/rsession/rconfig.py py/branch/config/py/test/rsession/testing/test_rconfig.py Log: Another test passes. Modified: py/branch/config/py/test/rsession/rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/rconfig.py (original) +++ py/branch/config/py/test/rsession/rconfig.py Thu Jan 18 18:04:36 2007 @@ -4,13 +4,25 @@ """ DEFAULT_LIST = ['dist_nicelevel'] +import marshal def make_repr(config, to_send=DEFAULT_LIST): d = dict([(name, config.getvalue(name)) for name in to_send]) - return to_send, d + opts = {} + for name in config.option.__dict__.iterkeys(): + if not name.startswith("_"): + try: + val = getattr(config.option, name) + marshal.dumps(val) + opts[name] = val + except ValueError: + pass + return d, opts def parse_repr(config, repr): defaultconftest = config.conftest.getconftestmodules(None)[0] - names, d = repr - for name in names: + d, opts = repr + for name in d.keys(): setattr(defaultconftest, name, d[name]) + for name in opts.keys(): + setattr(config.option, name, opts[name]) Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rconfig.py (original) +++ py/branch/config/py/test/rsession/testing/test_rconfig.py Thu Jan 18 18:04:36 2007 @@ -25,7 +25,6 @@ assert config.getvalue('x') == 1 def test_option_rconfig(): - py.test.skip("finish that one") tmp = py.test.ensuretemp("rconfigopt") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write(py.code.Source(""" @@ -35,11 +34,10 @@ Option('-g', '--glong', action="store", default=42, type="int", dest="gdest", help="g value.")) """)) - config = py.test.config._reparse([tmp]) - repr = make_repr(config, to_send=['x']) - rconfig = RConfig(repr) - assert rconfig.option.g == 42 config = py.test.config._reparse([tmp, "-g", "11"]) - repr = make_repr(config, to_send=['x']) - rconfig = RConfig(repr) - assert rconfig.option.g == 11 + assert config.option.gdest == 11 + repr = make_repr(config, to_send=[]) + config = py.test.config._reparse([str(empty_mod)]) + py.test.raises(AttributeError, "config.option.gdest") + parse_repr(config, repr) + assert config.option.gdest == 11 From hpk at codespeak.net Thu Jan 18 18:30:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 18:30:16 +0100 (CET) Subject: [py-svn] r36960 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118173016.D605D100A4@code0.codespeak.net> Author: hpk Date: Thu Jan 18 18:30:14 2007 New Revision: 36960 Modified: py/branch/config/py/test/rsession/rconfig.py py/branch/config/py/test/rsession/testing/test_rconfig.py Log: use very explicit names, small try-bodies and fix marshal-issues also for conftest names (added a test for that) Modified: py/branch/config/py/test/rsession/rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/rconfig.py (original) +++ py/branch/config/py/test/rsession/rconfig.py Thu Jan 18 18:30:14 2007 @@ -6,23 +6,29 @@ DEFAULT_LIST = ['dist_nicelevel'] import marshal -def make_repr(config, to_send=DEFAULT_LIST): - d = dict([(name, config.getvalue(name)) for name in to_send]) - opts = {} - for name in config.option.__dict__.iterkeys(): +def checkmarshal(name, value): + try: + marshal.dumps(value) + except ValueError: + raise ValueError("%s=%r is not marshallable" %(name, value)) + +def make_repr(config, conftestnames=DEFAULT_LIST): + conftestdict = {} + for name in conftestnames: + value = config.getvalue(name) + checkmarshal(name, value) + conftestdict[name] = value + cmdlineopts = {} + for name, value in vars(config.option).items(): if not name.startswith("_"): - try: - val = getattr(config.option, name) - marshal.dumps(val) - opts[name] = val - except ValueError: - pass - return d, opts + checkmarshal(name, value) + cmdlineopts[name] = value + return conftestdict, cmdlineopts def parse_repr(config, repr): defaultconftest = config.conftest.getconftestmodules(None)[0] - d, opts = repr - for name in d.keys(): - setattr(defaultconftest, name, d[name]) - for name in opts.keys(): - setattr(config.option, name, opts[name]) + conftestdict, cmdlineopts = repr + for name, val in conftestdict.items(): + setattr(defaultconftest, name, val) + for name, val in cmdlineopts.items(): + setattr(config.option, name, val) Modified: py/branch/config/py/test/rsession/testing/test_rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rconfig.py (original) +++ py/branch/config/py/test/rsession/testing/test_rconfig.py Thu Jan 18 18:30:14 2007 @@ -17,13 +17,25 @@ x = 1 """)) config = py.test.config._reparse([tmp]) - repr = make_repr(config, to_send=['x']) + repr = make_repr(config, conftestnames=['x']) config = py.test.config._reparse([empty_mod]) py.test.raises(KeyError, "config.getvalue('x')") parse_repr(config, repr) # warning, this will leave garbage in our defaultconftest assert config.getvalue('x') == 1 +def test_rconfig_not_marshallable(): + tmp = py.test.ensuretemp("rconfignotmarshal") + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write(py.code.Source(""" + class a: pass + """)) + config = py.test.config._reparse([tmp]) + py.test.raises(ValueError, "make_repr(config, conftestnames=['a'])") + + config.option.hello = lambda x: None + py.test.raises(ValueError, "make_repr(config, conftestnames=[])") + def test_option_rconfig(): tmp = py.test.ensuretemp("rconfigopt") tmp.ensure("__init__.py") @@ -36,7 +48,7 @@ """)) config = py.test.config._reparse([tmp, "-g", "11"]) assert config.option.gdest == 11 - repr = make_repr(config, to_send=[]) + repr = make_repr(config, conftestnames=[]) config = py.test.config._reparse([str(empty_mod)]) py.test.raises(AttributeError, "config.option.gdest") parse_repr(config, repr) From hpk at codespeak.net Thu Jan 18 18:43:17 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 18:43:17 +0100 (CET) Subject: [py-svn] r36961 - py/branch/config/py/test/rsession Message-ID: <20070118174317.5DD07100A5@code0.codespeak.net> Author: hpk Date: Thu Jan 18 18:43:16 2007 New Revision: 36961 Modified: py/branch/config/py/test/rsession/rconfig.py Log: go for the correct way of being defensive about what config.option might be (maybe an inheritance hierarchy), thanks fijal :) Modified: py/branch/config/py/test/rsession/rconfig.py ============================================================================== --- py/branch/config/py/test/rsession/rconfig.py (original) +++ py/branch/config/py/test/rsession/rconfig.py Thu Jan 18 18:43:16 2007 @@ -19,8 +19,9 @@ checkmarshal(name, value) conftestdict[name] = value cmdlineopts = {} - for name, value in vars(config.option).items(): + for name in dir(config.option): if not name.startswith("_"): + value = getattr(config.option, name) checkmarshal(name, value) cmdlineopts[name] = value return conftestdict, cmdlineopts From fijal at codespeak.net Thu Jan 18 19:06:20 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 19:06:20 +0100 (CET) Subject: [py-svn] r36962 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118180620.77D28100AF@code0.codespeak.net> Author: fijal Date: Thu Jan 18 19:06:13 2007 New Revision: 36962 Modified: py/branch/config/py/test/rsession/box.py py/branch/config/py/test/rsession/executor.py py/branch/config/py/test/rsession/hostmanage.py py/branch/config/py/test/rsession/local.py py/branch/config/py/test/rsession/master.py py/branch/config/py/test/rsession/outcome.py py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/slave.py py/branch/config/py/test/rsession/testing/test_boxing.py py/branch/config/py/test/rsession/testing/test_executor.py py/branch/config/py/test/rsession/testing/test_master.py py/branch/config/py/test/rsession/testing/test_outcome.py py/branch/config/py/test/rsession/testing/test_rsession.py py/branch/config/py/test/rsession/testing/test_slave.py Log: Huge refactoring. Got rid of remote_options and session_options, two tests still fail, so it's intermediate checkin. Modified: py/branch/config/py/test/rsession/box.py ============================================================================== --- py/branch/config/py/test/rsession/box.py (original) +++ py/branch/config/py/test/rsession/box.py Thu Jan 18 19:06:13 2007 @@ -18,12 +18,14 @@ class FileBox(object): count = 0 - def __init__(self, fun, args=None, kwargs=None): + def __init__(self, fun, args=None, kwargs=None, config=None): if args is None: args = [] if kwargs is None: kwargs = {} self.fun = fun + self.config = config + assert self.config self.args = args self.kwargs = kwargs @@ -35,7 +37,7 @@ self.PYTESTSTDOUT = tempdir.join('stdout') self.PYTESTSTDERR = tempdir.join('stderr') - nice_level = py.test.config.getvalue('dist_nicelevel') + nice_level = self.config.getvalue('dist_nicelevel') pid = os.fork() if pid: if not continuation: @@ -47,10 +49,12 @@ outcome = self.children(nice_level) except: excinfo = py.code.ExceptionInfo() - print "Internal box error" + x = open("/tmp/traceback", "w") + print >>x, "Internal box error" for i in excinfo.traceback: - print str(i)[2:-1] - print excinfo + print >>x, str(i)[2:-1] + print >>x, excinfo + x.close() os._exit(1) os.close(1) os.close(2) Modified: py/branch/config/py/test/rsession/executor.py ============================================================================== --- py/branch/config/py/test/rsession/executor.py (original) +++ py/branch/config/py/test/rsession/executor.py Thu Jan 18 19:06:13 2007 @@ -12,10 +12,12 @@ """ wraps = False - def __init__(self, item, usepdb=False, reporter=None): + def __init__(self, item, usepdb=False, reporter=None, config=None): self.item = item self.usepdb = usepdb self.reporter = reporter + self.config = config + assert self.config def execute(self): try: @@ -36,7 +38,8 @@ if self.usepdb: if self.reporter is not None: self.reporter(report.ImmediateFailure(self.item, - ReprOutcome(outcome.make_repr()))) + ReprOutcome(outcome.make_repr + (self.config.option.tbstyle)))) import pdb pdb.post_mortem(excinfo._excinfo[2]) # XXX hmm, we probably will not like to continue from that @@ -54,8 +57,8 @@ def execute(self): def fun(): outcome = RunExecutor.execute(self) - return outcome.make_repr() - b = Box(fun) + return outcome.make_repr(self.config.option.tbstyle) + b = Box(fun, config=self.config) pid = b.run() assert pid if b.retval is not None: @@ -76,9 +79,9 @@ def execute(self): def fun(): outcome = RunExecutor.execute(self) - return outcome.make_repr() + return outcome.make_repr(self.config.option.tbstyle) - b = Box(fun) + b = Box(fun, config=self.config) parent, pid = b.run(continuation=True) def cont(): Modified: py/branch/config/py/test/rsession/hostmanage.py ============================================================================== --- py/branch/config/py/test/rsession/hostmanage.py (original) +++ py/branch/config/py/test/rsession/hostmanage.py Thu Jan 18 19:06:13 2007 @@ -84,7 +84,7 @@ if remote_python is None: gw = py.execnet.PopenGateway() else: - gw = py.execnet.PopenGateway(remotepython=remote_python) + gw = py.execnet.PopenGateway(python=remote_python) host.relpath = str(pkgdir.dirpath()) host.gw = gw gw.host = host @@ -92,7 +92,7 @@ def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None, remote_python=None, \ - remote_options={}, optimise_localhost=True,\ + config=None, optimise_localhost=True,\ do_sync=True, done_dict=None): if done_dict is None: done_dict = {} @@ -126,13 +126,13 @@ rsync.send(pkgdir.dirpath()) # hosts ready - return setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict) + return setup_nodes(hosts, pkgdir, config, reporter, done_dict) -def setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict): +def setup_nodes(hosts, pkgdir, config, reporter, done_dict): nodes = [] for host in hosts: ch = setup_slave(host.gw, os.path.join(host.relpath,\ - pkgdir.basename), remote_options) + pkgdir.basename), config) nodes.append(MasterNode(ch, reporter, done_dict)) return nodes Modified: py/branch/config/py/test/rsession/local.py ============================================================================== --- py/branch/config/py/test/rsession/local.py (original) +++ py/branch/config/py/test/rsession/local.py Thu Jan 18 19:06:13 2007 @@ -21,15 +21,15 @@ return "", "" def box_runner(item, session, reporter): - r = BoxExecutor(item) + r = BoxExecutor(item, config=session.config) return ReprOutcome(r.execute()) def plain_runner(item, session, reporter): # box executor is doing stdout/err catching for us, let's do it here startcapture(session) - r = RunExecutor(item, usepdb=session.config.option.usepdb, reporter=reporter) + r = RunExecutor(item, usepdb=session.config.option.usepdb, reporter=reporter, config=session.config) outcome = r.execute() - outcome = ReprOutcome(outcome.make_repr()) + outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) outcome.stdout, outcome.stderr = finishcapture(session) return outcome Modified: py/branch/config/py/test/rsession/master.py ============================================================================== --- py/branch/config/py/test/rsession/master.py (original) +++ py/branch/config/py/test/rsession/master.py Thu Jan 18 19:06:13 2007 @@ -4,6 +4,7 @@ import py from py.__.test.rsession.outcome import ReprOutcome from py.__.test.rsession import report +from py.__.test.rsession.rconfig import make_repr class MasterNode(object): def __init__(self, channel, reporter, done_dict): @@ -74,12 +75,12 @@ waiter() return all_tests -def setup_slave(gateway, pkgpath, options): +def setup_slave(gateway, pkgpath, config): from py.__.test.rsession import slave import os ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()"))) #if hasattr(gateway, 'sshaddress'): # assert not os.path.isabs(pkgpath) ch.send(str(pkgpath)) - ch.send(options) + ch.send(make_repr(config)) return ch Modified: py/branch/config/py/test/rsession/outcome.py ============================================================================== --- py/branch/config/py/test/rsession/outcome.py (original) +++ py/branch/config/py/test/rsession/outcome.py Thu Jan 18 19:06:13 2007 @@ -20,31 +20,32 @@ self.signal = 0 assert bool(self.passed) + bool(excinfo) + bool(skipped) == 1 - def make_excinfo_repr(self): + def make_excinfo_repr(self, tbstyle): if self.excinfo is None: return None excinfo = self.excinfo - tb_info = [self.traceback_entry_repr(x) for x in excinfo.traceback] + tb_info = [self.traceback_entry_repr(x, tbstyle) + for x in excinfo.traceback] return (excinfo.type.__name__, str(excinfo.value), tb_info) - def traceback_entry_repr(self, tb_entry): + def traceback_entry_repr(self, tb_entry, tb_style): lineno = tb_entry.lineno relline = lineno - tb_entry.frame.code.firstlineno path = str(tb_entry.path) - try: - if py.test.config.option.tbstyle == 'long': - source = str(tb_entry.getsource()) - else: - source = str(tb_entry.getsource()).split("\n")[relline] + #try: + if tb_style == 'long': + source = str(tb_entry.getsource()) + else: + source = str(tb_entry.getsource()).split("\n")[relline] # XXX: Bare except. What can getsource() raise anyway? # SyntaxError, AttributeError, IndentationError for sure, check it - except: - source = "" + #except: + # source = "" return (relline, lineno, source, path) - def make_repr(self): + def make_repr(self, tbstyle="long"): return (self.passed, self.setupfailure, - self.make_excinfo_repr(), + self.make_excinfo_repr(tbstyle), self.skipped, self.is_critical, 0, "", "") class TracebackEntryRepr(object): Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Thu Jan 18 19:06:13 2007 @@ -16,6 +16,7 @@ from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner from py.__.test.rsession.reporter import LocalReporter, RemoteReporter +from py.__.test.rsession.rconfig import make_repr class AbstractSession(object): """ @@ -136,7 +137,7 @@ pkgdir = self.getpkgdir(args[0]) done_dict = {} nodes = init_hosts(reporter, sshhosts, pkgdir, - rsync_roots, remotepython, remote_options=remote_options.d, + rsync_roots, remotepython, config=self.config, optimise_localhost=self.optimise_localhost, done_dict=done_dict) reporter(report.RsyncFinished()) Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Thu Jan 18 19:06:13 2007 @@ -7,11 +7,13 @@ from py.__.test.rsession.outcome import Outcome class Info: + # XXX: Another nasty hack to get rid off pid = None class SlaveNode(object): - def __init__(self, rootcollector, executor=AsyncExecutor): + def __init__(self, rootcollector, config, executor=AsyncExecutor): self.rootcollector = rootcollector + self.config = config self.executor = executor def execute(self, itemspec): @@ -19,7 +21,7 @@ #if isinstance(item, py.test.Function): # ex = Executor(item.obj, setup=item.setup) #else: - ex = self.executor(item) + ex = self.executor(item, config=self.config) if self.executor is AsyncExecutor: cont, pid = ex.execute() else: @@ -35,9 +37,9 @@ if self.executor.wraps: return outcome else: - return outcome.make_repr() + return outcome.make_repr(self.config.option.tbstyle) -def slave_main(receive, send, path, info = None): +def slave_main(receive, send, path, config, info = None): import os assert os.path.exists(path) path = os.path.abspath(path) @@ -47,7 +49,7 @@ if node is not None: return node col = py.test.collect.Directory(str(py.path.local(path).join(item[0]))) - node = nodes[item[0]] = SlaveNode(col) + node = nodes[item[0]] = SlaveNode(col, config) return node while 1: nextitem = receive() @@ -62,8 +64,7 @@ excinfo = py.code.ExceptionInfo() send(Outcome(excinfo=excinfo, is_critical=True).make_repr()) else: - from py.__.test.rsession.rsession import remote_options - if not res[0] and not res[3] and remote_options.exitfirst: + if not res[0] and not res[3] and config.option.exitfirst: # we're finished, but need to eat what we can send(res) break @@ -87,27 +88,27 @@ import os, sys from Queue import Queue pkgdir = channel.receive() # path is ready - options = channel.receive() # options stuff, should be dictionary + config_repr = channel.receive() # options stuff, should be dictionary basedir = os.path.dirname(pkgdir) pkgname = os.path.basename(pkgdir) # setup defaults... sys.path.insert(0, basedir) import py - options['we_are_remote'] = True - from py.__.test.rsession.rsession import RemoteOptions + config = py.test.config._reparse([]) + config._overwrite('we_are_remote', True) + from py.__.test.rsession.rconfig import parse_repr + parse_repr(config, config_repr) from py.__.test.rsession.slave import Info Info.pid = 0 - from py.__.test.rsession import rsession - rsession.remote_options = RemoteOptions(options) # XXX the following assumes that py lib is there, a bit # much of an assumtion - if not rsession.remote_options.nomagic: + if not config.option.nomagic: py.magic.invoke(assertion=1) mod = __import__(pkgname) assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir) from py.__.test.rsession.slave import slave_main queue = Queue() channel.setcallback(callback_gen(queue)) - slave_main(queue.get, channel.send, basedir) - if not rsession.remote_options.nomagic: + slave_main(queue.get, channel.send, basedir, config) + if not config.option.nomagic: py.magic.revoke(assertion=1) Modified: py/branch/config/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_boxing.py (original) +++ py/branch/config/py/test/rsession/testing/test_boxing.py Thu Jan 18 19:06:13 2007 @@ -9,13 +9,16 @@ from py.__.test.rsession.box import Box from py.__.test.rsession.testing import example2 -from py.__.test.rsession.conftest import option + +def setup_module(mod): + mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() + mod.config = py.test.config._reparse([mod.rootdir]) def test_basic_boxing(): # XXX: because we do not have option transfer ## if not hasattr(option, 'nocapture') or not option.nocapture: ## py.test.skip("Interacts with pylib i/o skipping which is bad actually") - b = Box(example2.boxf1) + b = Box(example2.boxf1, config=config) b.run() assert b.stdoutrepr == "some out\n" assert b.stderrrepr == "some err\n" @@ -24,7 +27,7 @@ assert b.retval == 1 def test_boxing_on_fds(): - b = Box(example2.boxf2) + b = Box(example2.boxf2, config=config) b.run() assert b.stdoutrepr == "someout" assert b.stderrrepr == "someerr" @@ -33,13 +36,13 @@ assert b.retval == 2 def test_boxing_signal(): - b = Box(example2.boxseg) + b = Box(example2.boxseg, config=config) b.run() assert b.signal == 11 assert b.retval is None def test_boxing_huge_data(): - b = Box(example2.boxhuge) + b = Box(example2.boxhuge, config=config) b.run() assert b.stdoutrepr assert b.exitstat == 0 @@ -49,7 +52,7 @@ def test_box_seq(): # we run many boxes with huge data, just one after another for i in xrange(100): - b = Box(example2.boxhuge) + b = Box(example2.boxhuge, config=config) b.run() assert b.stdoutrepr assert b.exitstat == 0 @@ -58,13 +61,13 @@ def test_box_in_a_box(): def boxfun(): - b = Box(example2.boxf2) + b = Box(example2.boxf2, config=config) b.run() print b.stdoutrepr print >>sys.stderr, b.stderrrepr return b.retval - b = Box(boxfun) + b = Box(boxfun, config=config) b.run() assert b.stdoutrepr == "someout\n" assert b.stderrrepr == "someerr\n" @@ -81,7 +84,7 @@ def box_fun(): time.sleep(10) # we don't want to last forever here - b = Box(box_fun) + b = Box(box_fun, config=config) par, pid = b.run(continuation=True) os.kill(pid, 15) par(pid) Modified: py/branch/config/py/test/rsession/testing/test_executor.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_executor.py (original) +++ py/branch/config/py/test/rsession/testing/test_executor.py Thu Jan 18 19:06:13 2007 @@ -10,6 +10,7 @@ def setup_module(mod): mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() + mod.config = py.test.config._reparse([mod.rootdir]) def XXXtest_executor_passing_function(): ex = Executor(example1.f1) @@ -59,32 +60,32 @@ py.test.skip("hello") def test_run_executor(): - ex = RunExecutor(ItemTestPassing("pass")) + ex = RunExecutor(ItemTestPassing("pass"), config=config) outcome = ex.execute() assert outcome.passed - ex = RunExecutor(ItemTestFailing("fail")) + ex = RunExecutor(ItemTestFailing("fail"), config=config) outcome = ex.execute() assert not outcome.passed - ex = RunExecutor(ItemTestSkipping("skip")) + ex = RunExecutor(ItemTestSkipping("skip"), config=config) outcome = ex.execute() assert outcome.skipped assert not outcome.passed assert not outcome.excinfo def test_box_executor(): - ex = BoxExecutor(ItemTestPassing("pass")) + ex = BoxExecutor(ItemTestPassing("pass"), config=config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert outcome.passed - ex = BoxExecutor(ItemTestFailing("fail")) + ex = BoxExecutor(ItemTestFailing("fail"), config=config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert not outcome.passed - ex = BoxExecutor(ItemTestSkipping("skip")) + ex = BoxExecutor(ItemTestSkipping("skip"), config=config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert outcome.skipped @@ -94,7 +95,7 @@ def test_box_executor_stdout(): rootcol = py.test.collect.Directory(rootdir) item = rootcol.getitembynames(funcprint_spec) - ex = BoxExecutor(item) + ex = BoxExecutor(item, config=config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert outcome.passed @@ -103,7 +104,7 @@ def test_box_executor_stdout_error(): rootcol = py.test.collect.Directory(rootdir) item = rootcol.getitembynames(funcprintfail_spec) - ex = BoxExecutor(item) + ex = BoxExecutor(item, config=config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert not outcome.passed @@ -112,7 +113,7 @@ def test_cont_executor(): rootcol = py.test.collect.Directory(rootdir) item = rootcol.getitembynames(funcprintfail_spec) - ex = AsyncExecutor(item) + ex = AsyncExecutor(item, config=config) cont, pid = ex.execute() assert pid outcome_repr = cont() Modified: py/branch/config/py/test/rsession/testing/test_master.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_master.py (original) +++ py/branch/config/py/test/rsession/testing/test_master.py Thu Jan 18 19:06:13 2007 @@ -89,7 +89,8 @@ def test_slave_setup(): gw = py.execnet.PopenGateway() - channel = setup_slave(gw, pkgdir, remote_options.d) + config = py.test.config._reparse([]) + channel = setup_slave(gw, pkgdir, config) channel.send(funcpass_spec) output = ReprOutcome(channel.receive()) assert output.passed @@ -111,7 +112,8 @@ gw = py.execnet.PopenGateway() gw.host = HostInfo("localhost") gw.host.gw = gw - channel = setup_slave(gw, pkgdir, remote_options.d) + config = py.test.config._reparse([]) + channel = setup_slave(gw, pkgdir, config) mn = MasterNode(channel, simple_report, {}) return mn Modified: py/branch/config/py/test/rsession/testing/test_outcome.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_outcome.py (original) +++ py/branch/config/py/test/rsession/testing/test_outcome.py Thu Jan 18 19:06:13 2007 @@ -28,7 +28,7 @@ except: outcome = Outcome(excinfo=py.code.ExceptionInfo()) - repr = outcome.make_excinfo_repr() + repr = outcome.make_excinfo_repr("long") assert marshal.dumps(repr) excinfo = ExcInfoRepr(repr) Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 19:06:13 2007 @@ -11,6 +11,7 @@ from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ funcoptioncustom_spec, funcoption_spec +from py.__.test.rsession.rconfig import make_repr def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() @@ -156,7 +157,8 @@ config = py.test.config._reparse([]) nodes = init_hosts(setup_events.append, hosts, pkgdir, - rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) + rsync_roots=["py"], optimise_localhost=False, + config=config) teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -182,7 +184,8 @@ config = py.test.config._reparse([]) nodes = init_hosts(allevents.append, hosts, pkgdir, - rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) + rsync_roots=["py"], optimise_localhost=False, + config=config) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping @@ -222,10 +225,9 @@ hosts = [HostInfo('localhost')] parse_directories(hosts) config = py.test.config._reparse([]) - d = remote_options.d.copy() - d['custom'] = 'custom' + config._overwrite('custom', 'custom') nodes = init_hosts(allevents.append, hosts, pkgdir, - rsync_roots=["py"], remote_options=d, + rsync_roots=["py"], config=config, optimise_localhost=False) rootcol = py.test.collect.Directory(pkgdir.dirpath()) Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Thu Jan 18 19:06:13 2007 @@ -2,6 +2,7 @@ """ Testing the slave side node code (in a local way). """ from py.__.test.rsession.slave import SlaveNode, slave_main from py.__.test.rsession.outcome import ReprOutcome +from py.__.test.rsession.rconfig import make_repr import py, sys modlevel = [] @@ -35,8 +36,7 @@ assert py.test.config.getvalue('we_are_remote') def funcoptioncustom(): - from py.__.test.rsession.rsession import remote_options - assert remote_options.custom == "custom" + assert py.test.config.getvalue("custom") def funchang(): import time @@ -59,7 +59,8 @@ def gettestnode(): rootcol = py.test.collect.Directory(rootdir) - node = SlaveNode(rootcol, executor=RunExecutor) + config = py.test.config._reparse([rootdir]) + node = SlaveNode(rootcol, config, executor=RunExecutor) return node def test_slave_run_passing(): @@ -112,7 +113,8 @@ funcpass_spec, funcfail_spec ] - slave_main(q.pop, res.append, str(rootdir)) + config = py.test.config._reparse([]) + slave_main(q.pop, res.append, str(rootdir), config) assert len(res) == 2 res_repr = [ReprOutcome(r) for r in res] assert not res_repr[0].passed and res_repr[1].passed @@ -124,6 +126,7 @@ def test_slave_setup_fails_on_import_error(): from py.__.test.rsession.slave import setup tmp = py.test.ensuretemp("slavesetup") + config = py.test.config._reparse([tmp]) class C: def __init__(self): self.count = 0 @@ -132,8 +135,7 @@ if self.count == 0: retval = str(tmp) elif self.count == 1: - from py.__.test.rsession.rsession import remote_options - retval = remote_options.d + retval = make_repr(config) else: raise NotImplementedError("more data") self.count += 1 @@ -152,13 +154,13 @@ from py.__.test.rsession.slave import setup from Queue import Queue q = Queue() + config = py.test.config._reparse([tmp]) class C: res = [] def __init__(self): - from py.__.test.rsession.rsession import remote_options self.q = [str(tmp), - remote_options.d, + make_repr(config), funchang_spec, 42, funcpass_spec] @@ -186,6 +188,7 @@ def test_slave_setup_fails_on_missing_pkg(): from py.__.test.rsession.slave import setup tmp = py.test.ensuretemp("slavesetup2") + config = py.test.config._reparse([tmp]) x = tmp.ensure("sometestpackage", "__init__.py") class C: def __init__(self): @@ -195,8 +198,7 @@ if self.count == 0: retval = str(x.dirpath()) elif self.count == 1: - from py.__.test.rsession.rsession import remote_options - retval = remote_options.d + retval = make_repr(config) else: raise NotImplementedError("more data") self.count += 1 From fijal at codespeak.net Thu Jan 18 19:21:49 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 19:21:49 +0100 (CET) Subject: [py-svn] r36963 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118182149.F3CA8100AF@code0.codespeak.net> Author: fijal Date: Thu Jan 18 19:21:43 2007 New Revision: 36963 Modified: py/branch/config/py/test/rsession/master.py py/branch/config/py/test/rsession/slave.py py/branch/config/py/test/rsession/testing/test_rsession.py Log: Fix remaining tests and make strange hack with parse/reparse. Modified: py/branch/config/py/test/rsession/master.py ============================================================================== --- py/branch/config/py/test/rsession/master.py (original) +++ py/branch/config/py/test/rsession/master.py Thu Jan 18 19:21:43 2007 @@ -78,7 +78,7 @@ def setup_slave(gateway, pkgpath, config): from py.__.test.rsession import slave import os - ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()"))) + ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup(True)"))) #if hasattr(gateway, 'sshaddress'): # assert not os.path.isabs(pkgpath) ch.send(str(pkgpath)) Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Thu Jan 18 19:21:43 2007 @@ -73,7 +73,7 @@ while nextitem is not None: nextitem = receive() -def setup(): +def setup(really_parse=False): def callback_gen(queue): from py.__.test.rsession.slave import Info def callback(item): @@ -94,10 +94,14 @@ # setup defaults... sys.path.insert(0, basedir) import py - config = py.test.config._reparse([]) - config._overwrite('we_are_remote', True) + if really_parse: + py.test.config.parse([basedir]) + config = py.test.config + else: + config = py.test.config._reparse([basedir]) from py.__.test.rsession.rconfig import parse_repr parse_repr(config, config_repr) + config._overwrite('we_are_remote', True) from py.__.test.rsession.slave import Info Info.pid = 0 # XXX the following assumes that py lib is there, a bit Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 19:21:43 2007 @@ -226,6 +226,9 @@ parse_directories(hosts) config = py.test.config._reparse([]) config._overwrite('custom', 'custom') + # we need to overwrite default list to serialize + from py.__.test.rsession.rconfig import DEFAULT_LIST + DEFAULT_LIST.append("custom") nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], config=config, optimise_localhost=False) @@ -248,6 +251,7 @@ assert len(passed) == 2 * len(nodes) assert len(skipped) == 0 assert len(events) == len(passed) + DEFAULT_LIST.remove("custom") def test_nice_level(self): """ Tests if nice level behaviour is ok @@ -257,14 +261,16 @@ parse_directories(hosts) tmpdir = py.test.ensuretemp("nice") tmpdir.ensure("__init__.py") - tmpdir.ensure("conftest.py").write("""disthosts = ['localhost']""") + tmpdir.ensure("conftest.py").write(py.code.Source(""" + disthosts = ['localhost'] + dist_nicelevel = 10 + """)) tmpdir.ensure("test_one.py").write("""def test_nice(): import os assert os.nice(0) == 10 """) config = py.test.config._reparse([tmpdir]) - config.option.nice_level = 10 rsession = RSession(config) allevents = [] rsession.main(reporter=allevents.append) From hpk at codespeak.net Thu Jan 18 19:30:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 19:30:07 +0100 (CET) Subject: [py-svn] r36964 - in py/branch/config/py/test: . rsession rsession/testing testing Message-ID: <20070118183007.B4BF2100AF@code0.codespeak.net> Author: hpk Date: Thu Jan 18 19:30:02 2007 New Revision: 36964 Removed: py/branch/config/py/test/rsession/rconfig.py py/branch/config/py/test/rsession/testing/test_rconfig.py Modified: py/branch/config/py/test/config.py py/branch/config/py/test/rsession/master.py py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/slave.py py/branch/config/py/test/rsession/testing/test_slave.py py/branch/config/py/test/testing/test_config.py Log: move rconfig functionality to general Config object, with docstrings, and trying to change rsession accordingly. note that it's called "make_repr" and "merge_repr" the latter to make it obvious that we are changing the instance with the given repr. Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Thu Jan 18 19:30:02 2007 @@ -106,6 +106,42 @@ """ self._overwrite_dict[name] = value + def make_repr(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 + return conftestdict, cmdlineopts + + def merge_repr(self, repr): + """ merge in the conftest and cmdline option values + found in the given representation (produced + by make_repr above). + + The repr-contained conftest values are + stored on the default conftest module (last + priority) and the cmdline options on self.option. + """ + defaultconftest = self.conftest.getconftestmodules(None)[0] + conftestdict, cmdlineopts = repr + for name, val in conftestdict.items(): + setattr(defaultconftest, name, val) + for name, val in cmdlineopts.items(): + setattr(self.option, name, val) # this is the one per-process instance of py.test configuration config = Config() @@ -114,6 +150,12 @@ # helpers # +def checkmarshal(name, value): + try: + py.std.marshal.dumps(value) + except ValueError: + raise ValueError("%s=%r is not marshallable" %(name, value)) + def fixoptions(option): """ sanity checks and making option values canonical. """ if option.looponfailing and option.usepdb: Modified: py/branch/config/py/test/rsession/master.py ============================================================================== --- py/branch/config/py/test/rsession/master.py (original) +++ py/branch/config/py/test/rsession/master.py Thu Jan 18 19:30:02 2007 @@ -4,7 +4,6 @@ import py from py.__.test.rsession.outcome import ReprOutcome from py.__.test.rsession import report -from py.__.test.rsession.rconfig import make_repr class MasterNode(object): def __init__(self, channel, reporter, done_dict): @@ -82,5 +81,7 @@ #if hasattr(gateway, 'sshaddress'): # assert not os.path.isabs(pkgpath) ch.send(str(pkgpath)) - ch.send(make_repr(config)) + ch.send(config.make_repr(conftestnames=defaultconftestnames)) return ch + +defaultconftestnames = ['dist_nicelevel'] Deleted: /py/branch/config/py/test/rsession/rconfig.py ============================================================================== --- /py/branch/config/py/test/rsession/rconfig.py Thu Jan 18 19:30:02 2007 +++ (empty file) @@ -1,35 +0,0 @@ - -""" File which provides remote config, with similiar interface to -config, but in a marshallable way -""" - -DEFAULT_LIST = ['dist_nicelevel'] -import marshal - -def checkmarshal(name, value): - try: - marshal.dumps(value) - except ValueError: - raise ValueError("%s=%r is not marshallable" %(name, value)) - -def make_repr(config, conftestnames=DEFAULT_LIST): - conftestdict = {} - for name in conftestnames: - value = config.getvalue(name) - checkmarshal(name, value) - conftestdict[name] = value - cmdlineopts = {} - for name in dir(config.option): - if not name.startswith("_"): - value = getattr(config.option, name) - checkmarshal(name, value) - cmdlineopts[name] = value - return conftestdict, cmdlineopts - -def parse_repr(config, repr): - defaultconftest = config.conftest.getconftestmodules(None)[0] - conftestdict, cmdlineopts = repr - for name, val in conftestdict.items(): - setattr(defaultconftest, name, val) - for name, val in cmdlineopts.items(): - setattr(config.option, name, val) Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Thu Jan 18 19:30:02 2007 @@ -16,7 +16,6 @@ from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner from py.__.test.rsession.reporter import LocalReporter, RemoteReporter -from py.__.test.rsession.rconfig import make_repr class AbstractSession(object): """ Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Thu Jan 18 19:30:02 2007 @@ -99,8 +99,7 @@ config = py.test.config else: config = py.test.config._reparse([basedir]) - from py.__.test.rsession.rconfig import parse_repr - parse_repr(config, config_repr) + config.merge_repr(config_repr) config._overwrite('we_are_remote', True) from py.__.test.rsession.slave import Info Info.pid = 0 Deleted: /py/branch/config/py/test/rsession/testing/test_rconfig.py ============================================================================== --- /py/branch/config/py/test/rsession/testing/test_rconfig.py Thu Jan 18 19:30:02 2007 +++ (empty file) @@ -1,55 +0,0 @@ - -""" testing of remote config -""" - -import py -from py.__.test.rsession.rconfig import make_repr, parse_repr - -def setup_module(mod): - t = py.test.ensuretemp("rconfigclear") - t.ensure("conftest.py") - mod.empty_mod = t - -def test_basic_rconfig(): - tmp = py.test.ensuretemp("rconfig") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write(py.code.Source(""" - x = 1 - """)) - config = py.test.config._reparse([tmp]) - repr = make_repr(config, conftestnames=['x']) - config = py.test.config._reparse([empty_mod]) - py.test.raises(KeyError, "config.getvalue('x')") - parse_repr(config, repr) - # warning, this will leave garbage in our defaultconftest - assert config.getvalue('x') == 1 - -def test_rconfig_not_marshallable(): - tmp = py.test.ensuretemp("rconfignotmarshal") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write(py.code.Source(""" - class a: pass - """)) - config = py.test.config._reparse([tmp]) - py.test.raises(ValueError, "make_repr(config, conftestnames=['a'])") - - config.option.hello = lambda x: None - py.test.raises(ValueError, "make_repr(config, conftestnames=[])") - -def test_option_rconfig(): - tmp = py.test.ensuretemp("rconfigopt") - tmp.ensure("__init__.py") - tmp.ensure("conftest.py").write(py.code.Source(""" - import py - 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.test.config._reparse([tmp, "-g", "11"]) - assert config.option.gdest == 11 - repr = make_repr(config, conftestnames=[]) - config = py.test.config._reparse([str(empty_mod)]) - py.test.raises(AttributeError, "config.option.gdest") - parse_repr(config, repr) - assert config.option.gdest == 11 Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Thu Jan 18 19:30:02 2007 @@ -2,7 +2,6 @@ """ Testing the slave side node code (in a local way). """ from py.__.test.rsession.slave import SlaveNode, slave_main from py.__.test.rsession.outcome import ReprOutcome -from py.__.test.rsession.rconfig import make_repr import py, sys modlevel = [] @@ -135,7 +134,7 @@ if self.count == 0: retval = str(tmp) elif self.count == 1: - retval = make_repr(config) + retval = config.make_repr(conftestnames=['dist_nicelevel']) else: raise NotImplementedError("more data") self.count += 1 @@ -160,7 +159,7 @@ res = [] def __init__(self): self.q = [str(tmp), - make_repr(config), + config.make_repr(conftestnames=['dist_nicelevel']), funchang_spec, 42, funcpass_spec] @@ -198,7 +197,7 @@ if self.count == 0: retval = str(x.dirpath()) elif self.count == 1: - retval = make_repr(config) + retval = config.make_repr(conftestnames=['dist_nicelevel']) else: raise NotImplementedError("more data") self.count += 1 Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Thu Jan 18 19:30:02 2007 @@ -83,3 +83,47 @@ config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 +def test_config_make_and_merge_repr(): + tmp = py.test.ensuretemp("reprconfig1") + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write("x=1") + config = py.test.config._reparse([tmp]) + repr = config.make_repr(conftestnames=['x']) + config.option.verbose = 42 + repr2 = config.make_repr(conftestnames=[], optnames=['verbose']) + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(KeyError, "config.getvalue('x')") + config.merge_repr(repr) + assert config.getvalue('x') == 1 + config.merge_repr(repr2) + assert config.option.verbose == 42 + +def test_config_marshability(): + tmp = py.test.ensuretemp("configmarshal") + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write("a = object()") + config = py.test.config._reparse([tmp]) + py.test.raises(ValueError, "config.make_repr(conftestnames=['a'])") + + config.option.hello = lambda x: None + py.test.raises(ValueError, "config.make_repr(conftestnames=[])") + config.make_repr(conftestnames=[], optnames=[]) + +def test_config_rconfig(): + tmp = py.test.ensuretemp("rconfigopt") + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write(py.code.Source(""" + import py + 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.test.config._reparse([tmp, "-g", "11"]) + assert config.option.gdest == 11 + repr = config.make_repr(conftestnames=[]) + config = py.test.config._reparse([tmp.dirpath()]) + py.test.raises(AttributeError, "config.option.gdest") + config.merge_repr(repr) + assert config.option.gdest == 11 + From fijal at codespeak.net Thu Jan 18 19:35:06 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 19:35:06 +0100 (CET) Subject: [py-svn] r36965 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118183506.D2D4B100B0@code0.codespeak.net> Author: fijal Date: Thu Jan 18 19:35:00 2007 New Revision: 36965 Modified: py/branch/config/py/test/rsession/master.py py/branch/config/py/test/rsession/testing/test_rsession.py Log: Fix the test. Modified: py/branch/config/py/test/rsession/master.py ============================================================================== --- py/branch/config/py/test/rsession/master.py (original) +++ py/branch/config/py/test/rsession/master.py Thu Jan 18 19:35:00 2007 @@ -81,7 +81,7 @@ #if hasattr(gateway, 'sshaddress'): # assert not os.path.isabs(pkgpath) ch.send(str(pkgpath)) - ch.send(config.make_repr(conftestnames=defaultconftestnames)) + ch.send(config.make_repr(defaultconftestnames)) return ch defaultconftestnames = ['dist_nicelevel'] Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 19:35:00 2007 @@ -227,8 +227,8 @@ config = py.test.config._reparse([]) config._overwrite('custom', 'custom') # we need to overwrite default list to serialize - from py.__.test.rsession.rconfig import DEFAULT_LIST - DEFAULT_LIST.append("custom") + from py.__.test.rsession.master import defaultconftestnames + defaultconftestnames.append("custom") nodes = init_hosts(allevents.append, hosts, pkgdir, rsync_roots=["py"], config=config, optimise_localhost=False) @@ -251,7 +251,7 @@ assert len(passed) == 2 * len(nodes) assert len(skipped) == 0 assert len(events) == len(passed) - DEFAULT_LIST.remove("custom") + defaultconftestnames.remove("custom") def test_nice_level(self): """ Tests if nice level behaviour is ok From fijal at codespeak.net Thu Jan 18 19:36:05 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 19:36:05 +0100 (CET) Subject: [py-svn] r36966 - py/branch/config/py/test/rsession/testing Message-ID: <20070118183605.200D0100B0@code0.codespeak.net> Author: fijal Date: Thu Jan 18 19:36:02 2007 New Revision: 36966 Modified: py/branch/config/py/test/rsession/testing/test_rsession.py Log: Add try: finally: Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 19:36:02 2007 @@ -229,29 +229,31 @@ # we need to overwrite default list to serialize from py.__.test.rsession.master import defaultconftestnames defaultconftestnames.append("custom") - nodes = init_hosts(allevents.append, hosts, pkgdir, - rsync_roots=["py"], config=config, - optimise_localhost=False) - - rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol.getitembynames(funcoption_spec) - itempassaswell = rootcol.getitembynames(funcoptioncustom_spec) - - for node in nodes: - node.send(itempass) - node.send(itempassaswell) - - teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) - events = [i for i in allevents - if isinstance(i, report.ReceivedItemOutcome)] - passed = [i for i in events - if i.outcome.passed] - skipped = [i for i in events - if i.outcome.skipped] - assert len(passed) == 2 * len(nodes) - assert len(skipped) == 0 - assert len(events) == len(passed) - defaultconftestnames.remove("custom") + try: + nodes = init_hosts(allevents.append, hosts, pkgdir, + rsync_roots=["py"], config=config, + optimise_localhost=False) + + rootcol = py.test.collect.Directory(pkgdir.dirpath()) + itempass = rootcol.getitembynames(funcoption_spec) + itempassaswell = rootcol.getitembynames(funcoptioncustom_spec) + + for node in nodes: + node.send(itempass) + node.send(itempassaswell) + + teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) + events = [i for i in allevents + if isinstance(i, report.ReceivedItemOutcome)] + passed = [i for i in events + if i.outcome.passed] + skipped = [i for i in events + if i.outcome.skipped] + assert len(passed) == 2 * len(nodes) + assert len(skipped) == 0 + assert len(events) == len(passed) + finally: + defaultconftestnames.remove("custom") def test_nice_level(self): """ Tests if nice level behaviour is ok From fijal at codespeak.net Thu Jan 18 19:49:26 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 19:49:26 +0100 (CET) Subject: [py-svn] r36967 - py/branch/config/py/test/rsession Message-ID: <20070118184926.3AB1D100B8@code0.codespeak.net> Author: fijal Date: Thu Jan 18 19:49:24 2007 New Revision: 36967 Modified: py/branch/config/py/test/rsession/master.py py/branch/config/py/test/rsession/slave.py Log: (hpk, fijal) - kill really_parse, in favor of not parsing command line arguments on remote side. Modified: py/branch/config/py/test/rsession/master.py ============================================================================== --- py/branch/config/py/test/rsession/master.py (original) +++ py/branch/config/py/test/rsession/master.py Thu Jan 18 19:49:24 2007 @@ -77,7 +77,7 @@ def setup_slave(gateway, pkgpath, config): from py.__.test.rsession import slave import os - ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup(True)"))) + ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()"))) #if hasattr(gateway, 'sshaddress'): # assert not os.path.isabs(pkgpath) ch.send(str(pkgpath)) Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Thu Jan 18 19:49:24 2007 @@ -73,7 +73,7 @@ while nextitem is not None: nextitem = receive() -def setup(really_parse=False): +def setup(): def callback_gen(queue): from py.__.test.rsession.slave import Info def callback(item): @@ -94,11 +94,12 @@ # setup defaults... sys.path.insert(0, basedir) import py - if really_parse: - py.test.config.parse([basedir]) - config = py.test.config + config = py.test.config + if config._parsed: + config = config._reparse([basedir]) else: - config = py.test.config._reparse([basedir]) + config.conftest.setinitial([basedir]) + config.conftest.lget('adddefaultoptions')() config.merge_repr(config_repr) config._overwrite('we_are_remote', True) from py.__.test.rsession.slave import Info From hpk at codespeak.net Thu Jan 18 20:00:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 20:00:25 +0100 (CET) Subject: [py-svn] r36968 - py/branch/config/py/test/rsession/testing Message-ID: <20070118190025.5620B100AD@code0.codespeak.net> Author: hpk Date: Thu Jan 18 20:00:21 2007 New Revision: 36968 Modified: py/branch/config/py/test/rsession/testing/test_rsession.py py/branch/config/py/test/rsession/testing/test_slave.py Log: fixing some imports Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 20:00:21 2007 @@ -11,7 +11,6 @@ from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ funcoptioncustom_spec, funcoption_spec -from py.__.test.rsession.rconfig import make_repr def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Thu Jan 18 20:00:21 2007 @@ -1,6 +1,6 @@ """ Testing the slave side node code (in a local way). """ -from py.__.test.rsession.slave import SlaveNode, slave_main +from py.__.test.rsession.slave import SlaveNode, slave_main, setup from py.__.test.rsession.outcome import ReprOutcome import py, sys @@ -123,7 +123,6 @@ node.run("py doc log.txt".split()) def test_slave_setup_fails_on_import_error(): - from py.__.test.rsession.slave import setup tmp = py.test.ensuretemp("slavesetup") config = py.test.config._reparse([tmp]) class C: @@ -150,9 +149,7 @@ def test_slave_setup_exit(): tmp = py.test.ensuretemp("slaveexit") tmp.ensure("__init__.py") - from py.__.test.rsession.slave import setup - from Queue import Queue - q = Queue() + q = py.std.Queue.Queue() config = py.test.config._reparse([tmp]) class C: @@ -185,7 +182,6 @@ py.test.fail("Did not exit") def test_slave_setup_fails_on_missing_pkg(): - from py.__.test.rsession.slave import setup tmp = py.test.ensuretemp("slavesetup2") config = py.test.config._reparse([tmp]) x = tmp.ensure("sometestpackage", "__init__.py") From fijal at codespeak.net Thu Jan 18 20:10:49 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 20:10:49 +0100 (CET) Subject: [py-svn] r36969 - py/branch/config/py/test/rsession Message-ID: <20070118191049.7A031100B7@code0.codespeak.net> Author: fijal Date: Thu Jan 18 20:10:36 2007 New Revision: 36969 Modified: py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/slave.py Log: Move teardown hosts out of finally (needs further refactoring) Kill adddefaultoptions call for now. Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Thu Jan 18 20:10:36 2007 @@ -140,10 +140,10 @@ optimise_localhost=self.optimise_localhost, done_dict=done_dict) reporter(report.RsyncFinished()) - try: - self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) - finally: - teardown_hosts(reporter, [node.channel for node in nodes], nodes, + #try: + self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) + #finally: + teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) reporter(report.Nodes(nodes)) retval = reporter(report.TestFinished()) Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Thu Jan 18 20:10:36 2007 @@ -99,7 +99,7 @@ config = config._reparse([basedir]) else: config.conftest.setinitial([basedir]) - config.conftest.lget('adddefaultoptions')() + #config.conftest.lget('adddefaultoptions')() config.merge_repr(config_repr) config._overwrite('we_are_remote', True) from py.__.test.rsession.slave import Info From hpk at codespeak.net Thu Jan 18 20:33:18 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 18 Jan 2007 20:33:18 +0100 (CET) Subject: [py-svn] r36970 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118193318.8C4B11006F@code0.codespeak.net> Author: hpk Date: Thu Jan 18 20:33:16 2007 New Revision: 36970 Modified: py/branch/config/py/test/rsession/slave.py py/branch/config/py/test/rsession/testing/test_rsession.py py/branch/config/py/test/rsession/testing/test_slave.py Log: some little cleanups - getting rid of we_are_remote which is not used anywhere as far as i can see. maciej: please check that i didn't mess anything up, but for me the same tests as before pass Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Thu Jan 18 20:33:16 2007 @@ -86,9 +86,8 @@ return callback import os, sys - from Queue import Queue pkgdir = channel.receive() # path is ready - config_repr = channel.receive() # options stuff, should be dictionary + config_repr = channel.receive() basedir = os.path.dirname(pkgdir) pkgname = os.path.basename(pkgdir) # setup defaults... @@ -101,17 +100,14 @@ config.conftest.setinitial([basedir]) #config.conftest.lget('adddefaultoptions')() config.merge_repr(config_repr) - config._overwrite('we_are_remote', True) from py.__.test.rsession.slave import Info Info.pid = 0 - # XXX the following assumes that py lib is there, a bit - # much of an assumtion if not config.option.nomagic: py.magic.invoke(assertion=1) mod = __import__(pkgname) assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir) from py.__.test.rsession.slave import slave_main - queue = Queue() + queue = py.std.Queue.Queue() channel.setcallback(callback_gen(queue)) slave_main(queue.get, channel.send, basedir, config) if not config.option.nomagic: Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 20:33:16 2007 @@ -10,7 +10,7 @@ HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ - funcoptioncustom_spec, funcoption_spec + funcoptioncustom_spec def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() @@ -234,12 +234,10 @@ optimise_localhost=False) rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol.getitembynames(funcoption_spec) - itempassaswell = rootcol.getitembynames(funcoptioncustom_spec) + itempass = rootcol.getitembynames(funcoptioncustom_spec) for node in nodes: node.send(itempass) - node.send(itempassaswell) teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) events = [i for i in allevents @@ -248,7 +246,7 @@ if i.outcome.passed] skipped = [i for i in events if i.outcome.skipped] - assert len(passed) == 2 * len(nodes) + assert len(passed) == 1 * len(nodes) assert len(skipped) == 0 assert len(events) == len(passed) finally: Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Thu Jan 18 20:33:16 2007 @@ -31,9 +31,6 @@ print "samfing elz" asddsa -def funcoption(): - assert py.test.config.getvalue('we_are_remote') - def funcoptioncustom(): assert py.test.config.getvalue("custom") @@ -47,7 +44,6 @@ funcskip_spec = (BASE + "funcskip").split("/") funcprint_spec = (BASE + "funcprint").split("/") funcprintfail_spec = (BASE + "funcprintfail").split("/") -funcoption_spec = (BASE + "funcoption").split("/") funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/") funchang_spec = (BASE + "funchang").split("/") mod_spec = BASE[:-1].split("/") From guido at codespeak.net Thu Jan 18 21:35:23 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 21:35:23 +0100 (CET) Subject: [py-svn] r36972 - py/dist/py/test/rsession Message-ID: <20070118203523.5350010036@code0.codespeak.net> Author: guido Date: Thu Jan 18 21:35:14 2007 New Revision: 36972 Modified: py/dist/py/test/rsession/rsession.py Log: Capturing stdout while running tests, and printing message to stderr. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Thu Jan 18 21:35:14 2007 @@ -285,11 +285,22 @@ if self.config.option.apigen: from py.__.apigen.tracer.docstorage import DocStorageAccessor apigen = py.path.local(self.config.option.apigen).pyimport() + print >>sys.stderr, 'building documentation' + capture = py.io.OutErrCapture() try: - apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) - except (ValueError, AttributeError): - raise NotImplementedError("Provided script does not seem " - "to contain build function") + try: + apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) + except (ValueError, AttributeError): + #import traceback + #exc, e, tb = sys.exc_info() + #print '%s - %s' % (exc, e) + #print ''.join(traceback.format_tb(tb)) + #del tb + #print '-' * 79 + raise NotImplementedError("Provided script does not seem " + "to contain build function") + finally: + capture.reset() def init_runner(self, pkgdir): if self.config.option.apigen: From guido at codespeak.net Thu Jan 18 22:12:59 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 22:12:59 +0100 (CET) Subject: [py-svn] r36973 - in py/dist/py/apigen: . testing Message-ID: <20070118211259.16FEA10061@code0.codespeak.net> Author: guido Date: Thu Jan 18 22:12:52 2007 New Revision: 36973 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_htmlgen.py Log: More navigation cleanup, fixed the tests, added 'class="selected"' to selected item. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Thu Jan 18 22:12:52 2007 @@ -156,42 +156,53 @@ return snippet # XXX nav functions need cleanup!! -def build_navitem_html(linker, name, dotted_name, indent): +def build_navitem_html(linker, name, dotted_name, indent, selected): href = linker.get_lazyhref(dotted_name) - return H.NavigationItem((indent * 2 * u'\xa0'), H.a(name, href=href)) + navitem = H.NavigationItem((indent * 2 * u'\xa0'), H.a(name, href=href)) + if selected: + navitem.attr.class_ = 'selected' + return navitem -def build_api_navigation(linker, dsa, dotted_name, item_dotted_names): +def build_api_navigation(linker, dsa, dotted_name, item_dotted_names, + selection): navitems = [] # top namespace, index.html module_name = dsa.get_module_name().split('/')[-1] - navitems.append(build_navitem_html(linker, module_name, '', 0)) + navitems.append(build_navitem_html(linker, module_name, '', 0, + (selection == ''))) - # build html for each item in path indent = 1 - path = dotted_name.split('.') - if len(path) > 1: - for i in xrange(len(path)): - if i < 1: - continue - name = path[i] - parent_dotted_name = '.'.join(path[:i]) - navitems.append(build_navitem_html(linker, name, - parent_dotted_name, indent)) - indent += 1 - - # now build sub items if dotted_name != '': + # build html for each item in path to dotted_name item + path = dotted_name.split('.') + if len(path) > 1: + for i in xrange(len(path)): + if i < 1: + continue + name = path[i] + parent_dotted_name = '.'.join(path[:i]) + selected = (selection == '.'.join(path[:i+1])) + navitems.append(build_navitem_html(linker, name, + parent_dotted_name, indent, + selected)) + indent += 1 + + # build dotted_name item itself name = dotted_name.split('.')[-1] - navitems.append(build_navitem_html(linker, name, dotted_name, indent)) + selected = (name == selection) + navitems.append(build_navitem_html(linker, name, dotted_name, indent, + selected)) indent += 1 + + # build sub items of dotted_name item for item_dotted_name in item_dotted_names: - print 'item_dotted_name:', item_dotted_name itemname = item_dotted_name.split('.')[-1] if is_private(itemname): continue + selected = (item_dotted_name == selection) navitems.append(build_navitem_html(linker, itemname, - item_dotted_name, indent)) + item_dotted_name, indent, selected)) return H.Navigation(*navitems) re = py.std.re @@ -267,9 +278,12 @@ methodsdata = [] for dotted_name in classes_dotted_names: parent_dotted_name = dotted_name.rsplit('.', 1)[0] + sibling_dotted_names = [dn for dn in classes_dotted_names if + dn.startswith(parent_dotted_name) and + dn.count('.') == dotted_name.count('.')] tag = build_class_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, - [dotted_name]) + sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -295,7 +309,7 @@ parent_dotted_name = dotted_name.rsplit('.', 1)[0] tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, - [dotted_name]) + method_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -317,7 +331,7 @@ parent_dotted_name = dotted_name.rsplit('.', 1)[0] tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, - [dotted_name]) + method_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -358,7 +372,7 @@ tag = build_namespace_view(base, linker, dsa, dotted_name, subitem_dotted_names) nav = build_api_navigation(linker, dsa, dotted_name, - subitem_dotted_names) + subitem_dotted_names, dotted_name) if dotted_name == '': reltargetpath = 'api/index.html' else: Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Thu Jan 18 22:12:52 2007 @@ -132,8 +132,14 @@ clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() - assert html.find( - 'href="pkg.SomeClass.__init__.html">__init__') > -1 + print html + assert 'href="index.html"' in html + assert 'href="pkg.html"' in html + assert 'href="pkg.SomeClass.html"' in html + assert 'href="pkg.SomeSubClass.html"' in html + assert not 'href="pkg.sub.func.html"' in html + # not entirely sure about this one... + assert not 'href="pkg.sub.html"' in html _checkhtml(html) def test_build_class_api_pages_base_link(self): @@ -255,14 +261,14 @@ 'pkg.SomeClass', 'pkg.SomeSubClass']) prepare_function_api_pages(self.linker, self.dsa, base, ['pkg.sub.func']) - nav = build_navigation(self.linker, self.dsa, 'pkg.sub', - ['pkg.sub.func']) + nav = build_api_navigation(self.linker, self.dsa, 'pkg.sub', + ['pkg.sub.func'], 'pkg.sub.func') html = nav.unicode(indent=0) assert (u'' u'
\xa0\xa0sub
' u'
\xa0\xa0\xa0\xa0' u'sub
' - u'
\xa0\xa0\xa0\xa0\xa0\xa0' + u'
\xa0\xa0\xa0\xa0\xa0\xa0' u'func
' ) in html Modified: py/dist/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/dist/py/apigen/testing/test_htmlgen.py (original) +++ py/dist/py/apigen/testing/test_htmlgen.py Thu Jan 18 22:12:52 2007 @@ -4,8 +4,9 @@ def test_build_navitem_html(): l = Linker() l.set_link('spam.eggs.foo', 'foo.html') - h = build_navitem_html(l, 'foo', 'spam.eggs.foo', 0) + h = build_navitem_html(l, 'foo', 'spam.eggs.foo', 0, False) assert unicode(h) == u'' - h = build_navitem_html(l, 'bar', 'spam.eggs.foo', 1) - assert unicode(h) == u'
\xa0\xa0bar
' + h = build_navitem_html(l, 'bar', 'spam.eggs.foo', 1, True) + assert unicode(h) == (u'
\xa0\xa0' + u'bar
') From fijal at codespeak.net Thu Jan 18 22:32:07 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 18 Jan 2007 22:32:07 +0100 (CET) Subject: [py-svn] r36974 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070118213207.6037610069@code0.codespeak.net> Author: fijal Date: Thu Jan 18 22:32:04 2007 New Revision: 36974 Modified: py/branch/config/py/test/rsession/rsync_remote.py py/branch/config/py/test/rsession/testing/test_rsession.py Log: Fix the test to actually perform RSync of pylib itself and a test if that was true. Modified: py/branch/config/py/test/rsession/rsync_remote.py ============================================================================== --- py/branch/config/py/test/rsession/rsync_remote.py (original) +++ py/branch/config/py/test/rsession/rsync_remote.py Thu Jan 18 22:32:04 2007 @@ -76,7 +76,7 @@ assert _type == "link" path = os.path.join(destdir, relpath) try: - os.unlink(path) + remove(path) except OSError: pass Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Thu Jan 18 22:32:04 2007 @@ -105,7 +105,7 @@ tmpdir = py.test.ensuretemp("example_distribution") tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" disthosts = [%r] - distrsync_roots = ["%s"] + distrsync_roots = ["%s", "py"] """ % ('localhost', subdir))) tmpdir.ensure(subdir, "__init__.py") tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" @@ -119,7 +119,11 @@ pass def test_5(): assert __file__ != '%s' - """ % str(tmpdir.join(subdir)))) + def test_6(): + import py + assert py.__file__ != '%s' + """ % (str(tmpdir.join(subdir)), py.__file__))) + tmpdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) args = [str(tmpdir.join(subdir))] config = py.test.config._reparse(args) rsession = RSession(config, optimise_localhost=False) @@ -131,8 +135,8 @@ passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] skippedevents = [i for i in testevents if i.outcome.skipped] - assert len(testevents) == 5 - assert len(passevents) == 2 + assert len(testevents) == 6 + assert len(passevents) == 3 assert len(failevents) == 3 tb = failevents[0].outcome.excinfo.traceback assert tb[0].path.find("test_one") != -1 From guido at codespeak.net Thu Jan 18 23:02:34 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 23:02:34 +0100 (CET) Subject: [py-svn] r36975 - py/dist/py/apigen/rest/testing Message-ID: <20070118220234.4941A10069@code0.codespeak.net> Author: guido Date: Thu Jan 18 23:02:33 2007 New Revision: 36975 Modified: py/dist/py/apigen/rest/testing/test_rest.py Log: Skipping rest generation if docutils is not installed. Modified: py/dist/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/dist/py/apigen/rest/testing/test_rest.py (original) +++ py/dist/py/apigen/rest/testing/test_rest.py Thu Jan 18 23:02:33 2007 @@ -178,7 +178,12 @@ def check_rest(self, tempdir): from py.__.misc import rest for path in tempdir.listdir('*.txt'): - rest.process(path) + try: + rest.process(path) + except ImportError: + py.test.skip('skipping rest generation because docutils is ' + 'not installed (this is a partial skip, the rest ' + 'of the test was successful)') for path in tempdir.listdir('*.txt'): for item, arg1, arg2, arg3 in genlinkchecks(path): item(arg1, arg2, arg3) From guido at codespeak.net Thu Jan 18 23:03:01 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 18 Jan 2007 23:03:01 +0100 (CET) Subject: [py-svn] r36976 - in py/dist/py/apigen: . testing Message-ID: <20070118220301.2C2411006E@code0.codespeak.net> Author: guido Date: Thu Jan 18 23:03:00 2007 New Revision: 36976 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/style.css py/dist/py/apigen/testing/test_apigen_example.py Log: More bugs in nav, applying style for nav links. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Thu Jan 18 23:03:00 2007 @@ -173,30 +173,20 @@ (selection == ''))) indent = 1 + path = dotted_name.split('.') if dotted_name != '': # build html for each item in path to dotted_name item - path = dotted_name.split('.') - if len(path) > 1: - for i in xrange(len(path)): - if i < 1: - continue - name = path[i] - parent_dotted_name = '.'.join(path[:i]) - selected = (selection == '.'.join(path[:i+1])) - navitems.append(build_navitem_html(linker, name, - parent_dotted_name, indent, - selected)) - indent += 1 - - # build dotted_name item itself - name = dotted_name.split('.')[-1] - selected = (name == selection) - navitems.append(build_navitem_html(linker, name, dotted_name, indent, - selected)) - indent += 1 + for i in xrange(len(path)): + name = path[i] + item_dotted_name = '.'.join(path[:i+1]) + selected = (selection == item_dotted_name) + navitems.append(build_navitem_html(linker, name, + item_dotted_name, indent, + selected)) + indent += 1 # build sub items of dotted_name item - for item_dotted_name in item_dotted_names: + for item_dotted_name in sorted(item_dotted_names): itemname = item_dotted_name.split('.')[-1] if is_private(itemname): continue @@ -304,6 +294,8 @@ targetpath.write(content.encode("utf8")) def prepare_method_api_pages(linker, dsa, base, method_dotted_names): + # XXX note that even though these pages are still built, there's no nav + # pointing to them anymore... passed = [] for dotted_name in method_dotted_names: parent_dotted_name = dotted_name.rsplit('.', 1)[0] @@ -329,9 +321,12 @@ for dotted_name in method_dotted_names: # XXX should we create a build_full_function_view instead? parent_dotted_name = dotted_name.rsplit('.', 1)[0] + sibling_dotted_names = [dn for dn in method_dotted_names if + dn.startswith(parent_dotted_name) and + dn.count('.') == dotted_name.count('.')] tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, - method_dotted_names, dotted_name) + sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) Modified: py/dist/py/apigen/style.css ============================================================================== --- py/dist/py/apigen/style.css (original) +++ py/dist/py/apigen/style.css Thu Jan 18 23:03:00 2007 @@ -28,6 +28,15 @@ border-width: 0px; } +#menubar a { + text-decoration: none; + color: blue; +} + +#menubar div.selected a { + color: purple; +} + .project_title { font-size: 2em; font-weight: bold; Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Thu Jan 18 23:03:00 2007 @@ -264,8 +264,9 @@ nav = build_api_navigation(self.linker, self.dsa, 'pkg.sub', ['pkg.sub.func'], 'pkg.sub.func') html = nav.unicode(indent=0) + print html.encode('UTF-8') assert (u'' - u'
\xa0\xa0sub
' + u'
\xa0\xa0pkg
' u'
\xa0\xa0\xa0\xa0' u'sub
' u'
\xa0\xa0\xa0\xa0\xa0\xa0' From guido at codespeak.net Fri Jan 19 12:01:16 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 19 Jan 2007 12:01:16 +0100 (CET) Subject: [py-svn] r36982 - in py/dist/py/apigen: . testing Message-ID: <20070119110116.BC51D10072@code0.codespeak.net> Author: guido Date: Fri Jan 19 12:01:14 2007 New Revision: 36982 Modified: py/dist/py/apigen/apigen.py py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_apigen_functional.py Log: Passing namespace_tree around to the prepare_* methods to allow building of consistent navigation (showing the same siblings every level). Modified: py/dist/py/apigen/apigen.py ============================================================================== --- py/dist/py/apigen/apigen.py (original) +++ py/dist/py/apigen/apigen.py Fri Jan 19 12:01:14 2007 @@ -35,13 +35,17 @@ targetdir.ensure(dir=True) all_names = dsa._get_names(filter=lambda x, y: True) - ns_data = htmlgen.prepare_namespace_api_pages(l, dsa, targetdir, all_names) + namespace_tree = htmlgen.create_namespace_tree(dsa, all_names) + ns_data = htmlgen.prepare_namespace_api_pages(l, dsa, targetdir, + namespace_tree) class_names = dsa.get_class_names() class_data, method_data = htmlgen.prepare_class_api_pages(l, dsa, targetdir, + namespace_tree, class_names) function_names = dsa.get_function_names() func_data = htmlgen.prepare_function_api_pages(l, dsa, targetdir, + namespace_tree, function_names) source_data = htmlgen.prepare_source_pages(l, pkgdir, targetdir) Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 19 12:01:14 2007 @@ -263,14 +263,16 @@ content = page.unicode() #linker.call_withbase(reltargetpath, page.unicode) outputpath.write(content.encode("utf8")) -def prepare_class_api_pages(linker, dsa, base, classes_dotted_names): +def prepare_class_api_pages(linker, dsa, base, namespace_tree, + classes_dotted_names): passed = [] methodsdata = [] for dotted_name in classes_dotted_names: parent_dotted_name = dotted_name.rsplit('.', 1)[0] - sibling_dotted_names = [dn for dn in classes_dotted_names if - dn.startswith(parent_dotted_name) and - dn.count('.') == dotted_name.count('.')] + try: + sibling_dotted_names = namespace_tree[parent_dotted_name] + except KeyError: + sibling_dotted_names = [] # no siblings (built-in module or sth) tag = build_class_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, sibling_dotted_names, dotted_name) @@ -281,6 +283,7 @@ method_name in dsa.get_class_methods(dotted_name)] methodsdata += prepare_method_api_pages(linker, dsa, base, + namespace_tree, method_dotted_names) return passed, methodsdata @@ -293,15 +296,18 @@ targetpath = outputbase.ensure(reltargetpath) targetpath.write(content.encode("utf8")) -def prepare_method_api_pages(linker, dsa, base, method_dotted_names): +def prepare_method_api_pages(linker, dsa, base, namespace_tree, + method_dotted_names): # XXX note that even though these pages are still built, there's no nav # pointing to them anymore... passed = [] for dotted_name in method_dotted_names: parent_dotted_name = dotted_name.rsplit('.', 1)[0] + module_dotted_name = dotted_name.rsplit('.', 2)[0] + sibling_dotted_names = namespace_tree[module_dotted_name] tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, - method_dotted_names, dotted_name) + sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -316,14 +322,13 @@ targetpath.ensure() targetpath.write(content.encode("utf8")) -def prepare_function_api_pages(linker, dsa, base, method_dotted_names): +def prepare_function_api_pages(linker, dsa, base, namespace_tree, + method_dotted_names): passed = [] for dotted_name in method_dotted_names: # XXX should we create a build_full_function_view instead? parent_dotted_name = dotted_name.rsplit('.', 1)[0] - sibling_dotted_names = [dn for dn in method_dotted_names if - dn.startswith(parent_dotted_name) and - dn.count('.') == dotted_name.count('.')] + sibling_dotted_names = namespace_tree[parent_dotted_name] tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, sibling_dotted_names, dotted_name) @@ -355,11 +360,10 @@ ret[ns].append(itempath) return ret -def prepare_namespace_api_pages(linker, dsa, base, all_dotted_names): +def prepare_namespace_api_pages(linker, dsa, base, namespace_tree): passed = [] module_name = dsa.get_module_name().split('/')[-1] - namespace_tree = create_namespace_tree(dsa, all_dotted_names) names = namespace_tree.keys() names.sort() for dotted_name in names: Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Fri Jan 19 12:01:14 2007 @@ -83,11 +83,15 @@ def test_build_function_api_pages(self): base = py.test.ensuretemp('build_function_api_pages') prepare_source_pages(self.linker, self.fs_root, base) + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', 'pkg.SomeSubClass']) + namespace_tree) data = prepare_function_api_pages(self.linker, self.dsa, - base, ['pkg.sub.func']) + base, namespace_tree, + ['pkg.sub.func']) build_function_api_pages(self.linker, data, self.project, base) funcfile = base.join('api/pkg.sub.func.html') assert funcfile.check() @@ -104,13 +108,17 @@ def test_build_class_api_pages(self): base = py.test.ensuretemp('build_class_api_pages') + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.SomeClass', + 'pkg.SomeSubClass', + 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, ['pkg.SomeClass']) + base, namespace_tree, + ['pkg.SomeClass', + 'pkg.SomeSubClass']) prepare_source_pages(self.linker, self.fs_root, base) prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree) build_class_api_pages(self.linker, data, self.project, base) build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') @@ -120,13 +128,16 @@ def test_build_class_api_pages_nav_links(self): base = py.test.ensuretemp('build_class_api_pages_nav_links') + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.SomeClass', + 'pkg.SomeSubClass', + 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, ['pkg.SomeSubClass', 'pkg.SomeClass']) + base, namespace_tree, + ['pkg.SomeSubClass', 'pkg.SomeClass']) prepare_source_pages(self.linker, self.fs_root, base) prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree) build_class_api_pages(self.linker, data, self.project, base) build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') @@ -137,20 +148,22 @@ assert 'href="pkg.html"' in html assert 'href="pkg.SomeClass.html"' in html assert 'href="pkg.SomeSubClass.html"' in html + assert 'href="pkg.sub.html"' in html assert not 'href="pkg.sub.func.html"' in html - # not entirely sure about this one... - assert not 'href="pkg.sub.html"' in html _checkhtml(html) def test_build_class_api_pages_base_link(self): base = py.test.ensuretemp('build_class_api_pages_base_link') + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.SomeClass', + 'pkg.SomeSubClass', + 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, ['pkg.SomeSubClass', 'pkg.SomeClass']) + base, namespace_tree, + ['pkg.SomeSubClass', 'pkg.SomeClass']) prepare_source_pages(self.linker, self.fs_root, base) prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree) build_class_api_pages(self.linker, data, self.project, base) build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeSubClass.html') @@ -168,13 +181,16 @@ def test_api_source_links(self): base = py.test.ensuretemp('api_source_links') + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.SomeClass', + 'pkg.SomeSubClass', + 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, ['pkg.SomeSubClass', 'pkg.SomeClass']) + base, namespace_tree, + ['pkg.SomeSubClass', 'pkg.SomeClass']) sourcedata = prepare_source_pages(self.linker, self.fs_root, base) prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree) build_class_api_pages(self.linker, data, self.project, base) build_method_api_pages(self.linker, methodsdata, self.project, base) build_source_pages(self.linker, sourcedata, self.project, base) @@ -194,14 +210,17 @@ def test_build_namespace_api_pages_index(self): base = py.test.ensuretemp('build_namespace_api_pages') + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) data = prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree) prepare_class_api_pages(self.linker, self.dsa, base, + namespace_tree, ['pkg.SomeClass', 'pkg.SomeSubClass']) prepare_function_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func']) + namespace_tree, ['pkg.sub.func']) prepare_source_pages(self.linker, self.fs_root, base) build_namespace_api_pages(self.linker, data, self.project, base) pkgfile = base.join('api/pkg.html') @@ -211,14 +230,17 @@ def test_build_namespace_api_pages_subnamespace(self): base = py.test.ensuretemp('build_namespace_api_pages') + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) data = prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree) prepare_class_api_pages(self.linker, self.dsa, base, + namespace_tree, ['pkg.SomeClass', 'pkg.SomeSubClass']) prepare_function_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func']) + namespace_tree, ['pkg.sub.func']) prepare_source_pages(self.linker, self.fs_root, base) build_namespace_api_pages(self.linker, data, self.project, base) subfile = base.join('api/pkg.sub.html') @@ -240,11 +262,15 @@ def test_build_function_api_pages_nav(self): base = py.test.ensuretemp('build_function_api_pages_nav') prepare_source_pages(self.linker, self.fs_root, base) + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', 'pkg.SomeSubClass']) + namespace_tree) data = prepare_function_api_pages(self.linker, self.dsa, - base, ['pkg.sub.func']) + base, namespace_tree, + ['pkg.sub.func']) build_function_api_pages(self.linker, data, self.project, base) funcfile = base.join('api/pkg.sub.func.html') html = funcfile.read() @@ -256,11 +282,14 @@ def test_build_function_navigation(self): base = py.test.ensuretemp('build_function_navigation') prepare_source_pages(self.linker, self.fs_root, base) + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', 'pkg.SomeSubClass']) + namespace_tree) prepare_function_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func']) + namespace_tree, ['pkg.sub.func']) nav = build_api_navigation(self.linker, self.dsa, 'pkg.sub', ['pkg.sub.func'], 'pkg.sub.func') html = nav.unicode(indent=0) @@ -276,13 +305,16 @@ def test_build_root_namespace_view(self): base = py.test.ensuretemp('build_root_namespace_view') prepare_source_pages(self.linker, self.fs_root, base) + namespace_tree = create_namespace_tree(self.dsa, + ['pkg.sub', 'pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) data = prepare_namespace_api_pages(self.linker, self.dsa, base, - ['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree) prepare_function_api_pages(self.linker, self.dsa, base, - ['pkg.sub.func']) + namespace_tree, ['pkg.sub.func']) prepare_class_api_pages(self.linker, self.dsa, base, + namespace_tree, ['pkg.SomeClass', 'pkg.SomeSubClass']) build_namespace_api_pages(self.linker, data, self.project, base) rootfile = base.join('api/index.html') Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Fri Jan 19 12:01:14 2007 @@ -15,32 +15,35 @@ output = py.process.cmdexec( 'APIGEN_TARGET=%s py.test --session=L --apigen=%s/apigen.py %s' % ( tempdir, parentdir, pkgdir)) - assert output.lower().find('traceback') == -1 + assert output.lower().find('traceback') == -1 # just some quick content checks apidir = tempdir.join('api') assert apidir.check(dir=True) someclass_api = apidir.join('pkg.SomeClass.html') assert someclass_api.check(file=True) - assert someclass_api.read().find( - 'SomeClass') > -1 - someclass_init_api = apidir.join('pkg.SomeClass.__init__.html') - assert someclass_init_api.check(file=True) - assert someclass_init_api.read().find( - '__init__') > -1 + html = someclass_api.read() + assert 'SomeClass' in html + # XXX not linking to method files anymore + #someclass_init_api = apidir.join('pkg.SomeClass.__init__.html') + #assert someclass_init_api.check(file=True) + #assert someclass_init_api.read().find( + # '__init__') > -1 namespace_api = apidir.join('pkg.html') assert namespace_api.check(file=True) - assert namespace_api.read().find( - 'SomeClass') > -1 + html = namespace_api.read() + assert 'SomeClass' in html sourcedir = tempdir.join('source') assert sourcedir.check(dir=True) someclass_source = sourcedir.join('someclass.py.html') assert someclass_source.check(file=True) - assert someclass_source.read().find( - '
sources for someclass.py
') > -1 + html = someclass_source.read() + assert '
sources for someclass.py
' in html # XXX later... #index = sourcedir.join('index.html') #assert index.check(file=True) + #html = index.read() + #assert 'pkg' in html From fijal at codespeak.net Fri Jan 19 12:19:03 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 12:19:03 +0100 (CET) Subject: [py-svn] r36985 - py/branch/config/py/test Message-ID: <20070119111903.B97FC1006E@code0.codespeak.net> Author: fijal Date: Fri Jan 19 12:19:01 2007 New Revision: 36985 Added: py/branch/config/py/test/rsession-cleanup.txt Log: Document how I see cleanup of rsession. Added: py/branch/config/py/test/rsession-cleanup.txt ============================================================================== --- (empty file) +++ py/branch/config/py/test/rsession-cleanup.txt Fri Jan 19 12:19:01 2007 @@ -0,0 +1,42 @@ +Quick overview to make myself sure what I'm talking about. + +exit cases: + +1. normal exit (tests finished) +2. -x and a failing test seen at sending side +3. C-c pressed, or SystemExit raised + +exit conditions: + +- normal cleanup (exit) +- cleanup of remote nodes (rsession only) - possibly kill child processes + and close gateway nicely +- cleanup of web server (only regarding to web interface) - probably later + +Normal exit: + +easy :), just works + +-x and a failing test: + +works, but with a hack + +C-c hard case: + +we should have different layers of C-c pressed, to make sure that we can +interrupt teardown and still exit cleanly, that's why I don't like the idea +of having finally: beacause exceptions are ignored inside finally. + +We should kill Info.pid hack, which is quite awfull usecase of global +variable. + +So: + + first strike of C-c should go to hosts_teardown and start sending nodes info + that they should kill boxes. + + second strike should exit this teardown, print some message like "abandoned" + and eventually try to kill web server. + + third strike should just exit program. + From fijal at codespeak.net Fri Jan 19 13:06:02 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 13:06:02 +0100 (CET) Subject: [py-svn] r36986 - in py/branch/config/py/test/rsession: . testing webdata Message-ID: <20070119120602.0A86A10071@code0.codespeak.net> Author: fijal Date: Fri Jan 19 13:05:58 2007 New Revision: 36986 Modified: py/branch/config/py/test/rsession/box.py py/branch/config/py/test/rsession/executor.py py/branch/config/py/test/rsession/master.py py/branch/config/py/test/rsession/slave.py py/branch/config/py/test/rsession/testing/test_master.py py/branch/config/py/test/rsession/testing/test_slave.py py/branch/config/py/test/rsession/testing/test_webjs.py py/branch/config/py/test/rsession/webdata/source.js Log: Improve the behaviour off cleanup and channel closing, at least on slave side. Some cleanups, got rid of some hacks (in favor of other hacks, but posix is a bit hackish anyway) Modified: py/branch/config/py/test/rsession/box.py ============================================================================== --- py/branch/config/py/test/rsession/box.py (original) +++ py/branch/config/py/test/rsession/box.py Fri Jan 19 13:05:58 2007 @@ -86,8 +86,8 @@ retvalf.close() os._exit(0) - def parent(self, pid): - pid, exitstat = os.waitpid(pid, 0) + def parent(self, pid, waiter=os.waitpid): + pid, exitstat = waiter(pid, 0) self.signal = exitstat & 0x7f self.exitstat = exitstat & 0xff00 Modified: py/branch/config/py/test/rsession/executor.py ============================================================================== --- py/branch/config/py/test/rsession/executor.py (original) +++ py/branch/config/py/test/rsession/executor.py Fri Jan 19 13:05:58 2007 @@ -84,8 +84,8 @@ b = Box(fun, config=self.config) parent, pid = b.run(continuation=True) - def cont(): - parent(pid) + def cont(waiter=os.waitpid): + parent(pid, waiter=waiter) if b.retval is not None: passed, setupfailure, excinfo, skipped,\ critical, _, _, _ = b.retval Modified: py/branch/config/py/test/rsession/master.py ============================================================================== --- py/branch/config/py/test/rsession/master.py (original) +++ py/branch/config/py/test/rsession/master.py Fri Jan 19 13:05:58 2007 @@ -35,7 +35,7 @@ self.reporter(report.SendItem(self.channel, item)) def itemgen(colitems, reporter, keyword, reporterror): - for x in colitems: + for x in colitems: for y in x.tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword): yield y Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Fri Jan 19 13:05:58 2007 @@ -5,16 +5,51 @@ import py from py.__.test.rsession.executor import RunExecutor, BoxExecutor, AsyncExecutor from py.__.test.rsession.outcome import Outcome +import thread +import os -class Info: - # XXX: Another nasty hack to get rid off - pid = None +class PidInfo(object): + """ Pure container class to store information of actually running + pid + """ + def __init__(self): + self.pid = 0 + self.lock = thread.allocate_lock() + + def set_pid(self, pid): + self.lock.acquire() + try: + self.pid = pid + finally: + self.lock.release() + + def kill(self): + self.lock.acquire() + try: + if self.pid: + os.kill(self.pid, 15) + self.pid = 0 + finally: + self.lock.release() + + def waitandclear(self, pid, num): + """ This is an obscure hack to keep locking properly, adhere to posix semantics + and try to clean it as much as possible, not clean at all + """ + self.lock.acquire() + try: + retval = os.waitpid(self.pid, 0) + self.pid = 0 + return retval + finally: + self.lock.release() class SlaveNode(object): - def __init__(self, rootcollector, config, executor=AsyncExecutor): + def __init__(self, rootcollector, config, pidinfo, executor=AsyncExecutor): self.rootcollector = rootcollector self.config = config self.executor = executor + self.pidinfo = pidinfo def execute(self, itemspec): item = self.rootcollector.getitembynames(itemspec) @@ -24,11 +59,11 @@ ex = self.executor(item, config=self.config) if self.executor is AsyncExecutor: cont, pid = ex.execute() + self.pidinfo.set_pid(pid) else: # for tests only return ex.execute() - Info.pid = pid - return cont() + return cont(self.pidinfo.waitandclear) def run(self, itemspec): #outcome = self.execute(itemspec) @@ -39,7 +74,7 @@ else: return outcome.make_repr(self.config.option.tbstyle) -def slave_main(receive, send, path, config, info = None): +def slave_main(receive, send, path, config, pidinfo): import os assert os.path.exists(path) path = os.path.abspath(path) @@ -49,7 +84,7 @@ if node is not None: return node col = py.test.collect.Directory(str(py.path.local(path).join(item[0]))) - node = nodes[item[0]] = SlaveNode(col, config) + node = nodes[item[0]] = SlaveNode(col, config, pidinfo) return node while 1: nextitem = receive() @@ -72,15 +107,14 @@ while nextitem is not None: nextitem = receive() - + def setup(): - def callback_gen(queue): - from py.__.test.rsession.slave import Info + def callback_gen(channel, queue, info): def callback(item): if item == 42: # magic call-cleanup # XXX should kill a pid here - if Info.pid: - os.kill(Info.pid, 15) + info.kill() + channel.close() sys.exit(0) queue.put(item) return callback @@ -100,15 +134,15 @@ config.conftest.setinitial([basedir]) #config.conftest.lget('adddefaultoptions')() config.merge_repr(config_repr) - from py.__.test.rsession.slave import Info - Info.pid = 0 if not config.option.nomagic: py.magic.invoke(assertion=1) mod = __import__(pkgname) assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir) - from py.__.test.rsession.slave import slave_main + from py.__.test.rsession.slave import slave_main, PidInfo queue = py.std.Queue.Queue() - channel.setcallback(callback_gen(queue)) - slave_main(queue.get, channel.send, basedir, config) + pidinfo = PidInfo() + channel.setcallback(callback_gen(channel, queue, pidinfo)) + slave_main(queue.get, channel.send, basedir, config, pidinfo) if not config.option.nomagic: py.magic.revoke(assertion=1) + channel.close() Modified: py/branch/config/py/test/rsession/testing/test_master.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_master.py (original) +++ py/branch/config/py/test/rsession/testing/test_master.py Fri Jan 19 13:05:58 2007 @@ -11,7 +11,7 @@ from py.__.test.rsession.master import dispatch_loop, setup_slave, MasterNode, randomgen from py.__.test.rsession.outcome import ReprOutcome, Outcome -from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec +from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec, funchang_spec from py.__.test.rsession import report from py.__.test.rsession.hostmanage import HostInfo @@ -94,7 +94,7 @@ channel.send(funcpass_spec) output = ReprOutcome(channel.receive()) assert output.passed - channel.send(None) + channel.send(42) channel.waitclose(10) gw.exit() @@ -126,6 +126,34 @@ shouldstop = lambda : False dispatch_loop(master_nodes, itemgenerator, shouldstop) +def test_slave_running_interrupted(): + #def simple_report(event): + # if not isinstance(event, report.ReceivedItemOutcome): + # return + # item = event.item + # if item.code.name == 'funcpass': + # assert event.outcome.passed + # else: + # assert not event.outcome.passed + reports = [] + + def open_gw(): + gw = py.execnet.PopenGateway() + gw.host = HostInfo("localhost") + gw.host.gw = gw + config = py.test.config._reparse([]) + channel = setup_slave(gw, pkgdir, config) + mn = MasterNode(channel, reports.append, {}) + return mn, gw, channel + + mn, gw, channel = open_gw() + rootcol = py.test.collect.Directory(pkgdir.dirpath()) + funchang_item = rootcol.getitembynames(funchang_spec) + mn.send(funchang_item) + mn.send(StopIteration) + # XXX: We have to wait here a bit to make sure that it really did happen + channel.waitclose(2) + def test_randomgen(): d = {} gen = randomgen({1:True, 2:True, 3:True}, d) Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Fri Jan 19 13:05:58 2007 @@ -1,6 +1,6 @@ """ Testing the slave side node code (in a local way). """ -from py.__.test.rsession.slave import SlaveNode, slave_main, setup +from py.__.test.rsession.slave import SlaveNode, slave_main, setup, PidInfo from py.__.test.rsession.outcome import ReprOutcome import py, sys @@ -55,7 +55,8 @@ def gettestnode(): rootcol = py.test.collect.Directory(rootdir) config = py.test.config._reparse([rootdir]) - node = SlaveNode(rootcol, config, executor=RunExecutor) + pidinfo = PidInfo() + node = SlaveNode(rootcol, config, pidinfo, executor=RunExecutor) return node def test_slave_run_passing(): @@ -109,7 +110,8 @@ funcfail_spec ] config = py.test.config._reparse([]) - slave_main(q.pop, res.append, str(rootdir), config) + pidinfo = PidInfo() + slave_main(q.pop, res.append, str(rootdir), config, pidinfo) assert len(res) == 2 res_repr = [ReprOutcome(r) for r in res] assert not res_repr[0].passed and res_repr[1].passed @@ -134,6 +136,10 @@ raise NotImplementedError("more data") self.count += 1 return retval + + def close(self): + pass + try: exec py.code.Source(setup, "setup()").compile() in { 'channel': C()} @@ -168,6 +174,9 @@ callback(self.q.pop()) f() #thread.start_new_thread(f, ()) + + def close(self): + pass send = res.append try: Modified: py/branch/config/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_webjs.py (original) +++ py/branch/config/py/test/rsession/testing/test_webjs.py Fri Jan 19 13:05:58 2007 @@ -7,8 +7,6 @@ from pypy.translator.js.tester import schedule_callbacks here = py.magic.autopath().dirpath() -py.test.skip("will fix later, multiple issues") - def setup_module(mod): # load HTML into window object html = here.join('../webdata/index.html').read() @@ -18,9 +16,7 @@ dom.window = dom.Window(html) dom.document = dom.window.document config = py.test.config._reparse([]) - from py.__.test.rsession.rsession import session_options - session_options.bind_config(config) - session_options.import_pypy = True + config._overwrite('_dist_import_pypy', True) from py.__.test.rsession import webjs from py.__.test.rsession.web import exported_methods mod.webjs = webjs Modified: py/branch/config/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From fijal at codespeak.net Fri Jan 19 13:25:19 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 13:25:19 +0100 (CET) Subject: [py-svn] r36988 - in py/branch/config/py/test/rsession: . webdata Message-ID: <20070119122519.56BC410070@code0.codespeak.net> Author: fijal Date: Fri Jan 19 13:25:05 2007 New Revision: 36988 Modified: py/branch/config/py/test/rsession/hostmanage.py py/branch/config/py/test/rsession/report.py py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/webdata/source.js py/branch/config/py/test/rsession/webjs.py Log: Improve the C-c support, also keep web interface informed about crashes/interrupts. Modified: py/branch/config/py/test/rsession/hostmanage.py ============================================================================== --- py/branch/config/py/test/rsession/hostmanage.py (original) +++ py/branch/config/py/test/rsession/hostmanage.py Fri Jan 19 13:25:05 2007 @@ -149,8 +149,13 @@ if node.pending: clean = False waiter() - + teardown_gateways(reporter, channels) +def kill_channels(channels): + for channel in channels: + channel.send(42) + +def teardown_gateways(reporter, channels): for channel in channels: try: report.wrapcall(reporter, channel.waitclose) Modified: py/branch/config/py/test/rsession/report.py ============================================================================== --- py/branch/config/py/test/rsession/report.py (original) +++ py/branch/config/py/test/rsession/report.py Fri Jan 19 13:25:05 2007 @@ -124,3 +124,9 @@ def __init__(self, hostid, result): self.hostid = hostid self.result = result + +class InterruptedExecution(ReportEvent): + pass + +class CrashedExecution(ReportEvent): + pass Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Fri Jan 19 13:25:05 2007 @@ -11,7 +11,8 @@ from py.__.test.rsession import report from py.__.test.rsession.master import \ setup_slave, MasterNode, dispatch_loop, itemgen, randomgen -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo +from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo,\ + teardown_gateways, kill_channels from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner @@ -135,20 +136,35 @@ pkgdir = self.getpkgdir(args[0]) done_dict = {} - nodes = init_hosts(reporter, sshhosts, pkgdir, - rsync_roots, remotepython, config=self.config, - optimise_localhost=self.optimise_localhost, done_dict=done_dict) - reporter(report.RsyncFinished()) - - #try: - self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) - #finally: - teardown_hosts(reporter, [node.channel for node in nodes], nodes, + try: + nodes = init_hosts(reporter, sshhosts, pkgdir, + rsync_roots, remotepython, config=self.config, + optimise_localhost=self.optimise_localhost, done_dict=done_dict) + reporter(report.RsyncFinished()) + try: + self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) + except (KeyboardInterrupt, SystemExit): + print >>sys.stderr, "C-c pressed waiting for gateways to teardown..." + channels = [node.channel for node in nodes] + kill_channels(channels) + teardown_gateways(reporter, channels) + print >>sys.stderr, "... Done" + raise + + teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) - reporter(report.Nodes(nodes)) - retval = reporter(report.TestFinished()) - self.kill_server(startserverflag) - return retval + reporter(report.Nodes(nodes)) + retval = reporter(report.TestFinished()) + self.kill_server(startserverflag) + return retval + except (KeyboardInterrupt, SystemExit): + reporter(report.InterruptedExecution()) + self.kill_server(startserverflag) + raise + except: + reporter(report.CrashedExecution()) + self.kill_server(startserverflag) + raise def read_distributed_config(self): try: Modified: py/branch/config/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/branch/config/py/test/rsession/webjs.py ============================================================================== --- py/branch/config/py/test/rsession/webjs.py (original) +++ py/branch/config/py/test/rsession/webjs.py Fri Jan 19 13:25:05 2007 @@ -62,6 +62,16 @@ info = dom.document.getElementById("info") info.style.visibility = "hidden" +def show_interrupt(): + glob.finished = True + dom.document.title = "Py.test [interrupted]" + dom.document.getElementById("Tests").childNodes[0].nodeValue = "Tests [interrupted]" + +def show_crash(): + glob.finished = True + dom.document.title = "Py.test [crashed]" + dom.document.getElementById("Tests").childNodes[0].nodeValue = "Tests [crashed]" + SCROLL_LINES = 50 def scroll_down_if_needed(mbox): @@ -175,6 +185,7 @@ add_received_item_outcome(msg, module_part) elif msg['type'] == 'TestFinished': text = "FINISHED %s run, %s failures, %s skipped" % (msg['run'], msg['fails'], msg['skips']) + glob.finished = True dom.document.title = "Py.test %s" % text dom.document.getElementById("Tests").childNodes[0].nodeValue = \ "Tests [%s]" % text @@ -202,6 +213,10 @@ module_part.appendChild(tr) elif msg['type'] == 'RsyncFinished': glob.rsync_done = True + elif msg['type'] == 'InterruptedExecution': + show_interrupt() + elif msg['type'] == 'CrashedExecution': + show_crash() if glob.data_empty: mbox = dom.document.getElementById('messagebox') scroll_down_if_needed(mbox) @@ -262,6 +277,8 @@ glob.host = "" def update_rsync(): + if glob.finished: + return elem = dom.document.getElementById("Tests") if glob.rsync_done is True: elem.childNodes[0].nodeValue = "Tests" @@ -299,6 +316,7 @@ exported_methods.show_all_statuses(id, comeback) def main(): + glob.finished = False exported_methods.show_hosts(host_init) exported_methods.show_sessid(sessid_comeback) From hpk at codespeak.net Fri Jan 19 14:27:39 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 14:27:39 +0100 (CET) Subject: [py-svn] r36990 - py/dist/py/apigen Message-ID: <20070119132739.39CFC10072@code0.codespeak.net> Author: hpk Date: Fri Jan 19 14:27:37 2007 New Revision: 36990 Modified: py/dist/py/apigen/todo-apigen.txt Log: made two notes about conceptually improving the dependency/test situation Modified: py/dist/py/apigen/todo-apigen.txt ============================================================================== --- py/dist/py/apigen/todo-apigen.txt (original) +++ py/dist/py/apigen/todo-apigen.txt Fri Jan 19 14:27:37 2007 @@ -33,5 +33,36 @@ * look out for and streamline all apigen/source-viewer documentation into one document -* XXX list more here + +* consider automating dependencies: + + e.g. something like: queue_render(page, fspath, linker, ...) + would defer the rendering until later. + then a loop does: + + maxlength = len(queue) + while queue: + page, fspath, linker, ... = queue.get() + # fill outputpath/link here or even earlier + if all_links_resolve(page, linker): + render it and write to filesystem + maxlength = len(queue) + else: + queue.append(...) + maxlength -= 1 + if maxlength <= 0: + print "ERROR: seems i can't make progress" + print "unresolved links follow: " + ... + print "unresolved pages/fspaths:" + ... + XXX maybe: print "filling linker with dummy hrefs, and rendering anyway" + ... + raise ... + +* also we might have a support function for tests that + fills the linker with "dummy hrefs" for certain types + like source links + +* XXX list more here From fijal at codespeak.net Fri Jan 19 14:54:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 14:54:28 +0100 (CET) Subject: [py-svn] r36993 - in py/branch/config/py/test/rsession: . webdata Message-ID: <20070119135428.08FEF10070@code0.codespeak.net> Author: fijal Date: Fri Jan 19 14:54:25 2007 New Revision: 36993 Modified: py/branch/config/py/test/rsession/web.py py/branch/config/py/test/rsession/webdata/index.html py/branch/config/py/test/rsession/webdata/source.js py/branch/config/py/test/rsession/webjs.py Log: Add possibility to scroll or not to scroll to web frontend. This checkin makes me believe that there is lot of stuff to do in JS backend. Modified: py/branch/config/py/test/rsession/web.py ============================================================================== --- py/branch/config/py/test/rsession/web.py (original) +++ py/branch/config/py/test/rsession/web.py Fri Jan 19 14:54:25 2007 @@ -21,7 +21,7 @@ DATADIR = py.path.local(__file__).dirpath("webdata") FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", - "show_host", "hide_host", "hide_messagebox"] + "show_host", "hide_host", "hide_messagebox", "opt_scroll"] try: try: Modified: py/branch/config/py/test/rsession/webdata/index.html ============================================================================== --- py/branch/config/py/test/rsession/webdata/index.html (original) +++ py/branch/config/py/test/rsession/webdata/index.html Fri Jan 19 14:54:25 2007 @@ -97,6 +97,12 @@ End of Traceback + + Options + + + Scroll with tests arriving + Modified: py/branch/config/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/branch/config/py/test/rsession/webjs.py ============================================================================== --- py/branch/config/py/test/rsession/webjs.py (original) +++ py/branch/config/py/test/rsession/webjs.py Fri Jan 19 14:54:25 2007 @@ -36,6 +36,14 @@ glob = Globals() +class Options(object): + """ Store global options + """ + def __init__(self): + self.scroll = True + +opts = Options() + def comeback(msglist): if len(msglist) == 0: return @@ -74,9 +82,17 @@ SCROLL_LINES = 50 +def opt_scroll(): + if opts.scroll: + opts.scroll = False + else: + opts.scroll = True + def scroll_down_if_needed(mbox): - if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES: - mbox.parentNode.scrollIntoView() + if not opts.scroll: + return + #if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES: + mbox.parentNode.scrollIntoView() def hide_messagebox(): mbox = dom.document.getElementById("messagebox") @@ -311,6 +327,16 @@ for key in host_dict.keys(): glob.host_pending[key] = [] +def key_pressed(key): + if key.charCode == ord('s'): + scroll_box = dom.document.getElementById("opt_scroll") + if opts.scroll: + scroll_box.removeAttribute("checked") + opts.scroll = False + else: + scroll_box.setAttribute("checked", "true") + opts.scroll = True + def sessid_comeback(id): glob.sessid = id exported_methods.show_all_statuses(id, comeback) @@ -319,4 +345,5 @@ glob.finished = False exported_methods.show_hosts(host_init) exported_methods.show_sessid(sessid_comeback) - + dom.document.onkeypress = key_pressed + dom.document.getElementById("opt_scroll").setAttribute("checked", "True") From fijal at codespeak.net Fri Jan 19 15:08:20 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 15:08:20 +0100 (CET) Subject: [py-svn] r36994 - in py/branch/config/py/test: . terminal Message-ID: <20070119140820.D5BE61006E@code0.codespeak.net> Author: fijal Date: Fri Jan 19 15:08:16 2007 New Revision: 36994 Modified: py/branch/config/py/test/collect.py py/branch/config/py/test/config.py py/branch/config/py/test/terminal/terminal.py Log: removal of some trailing white-spaces. Modified: py/branch/config/py/test/collect.py ============================================================================== --- py/branch/config/py/test/collect.py (original) +++ py/branch/config/py/test/collect.py Fri Jan 19 15:08:16 2007 @@ -342,15 +342,15 @@ d[name] = res return d - def makeitem(self, name, obj, usefilters=True): + 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) + 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) + return self.Generator(name, parent=self) else: - return self.Function(name, parent=self) + return self.Function(name, parent=self) def _prepare(self): if not hasattr(self, '_name2items'): Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Fri Jan 19 15:08:16 2007 @@ -49,7 +49,7 @@ cmdlineoption, remaining = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) fixoptions(self.option) # XXX fixing should be moved to sessions - if not remaining: + if not remaining: remaining.append(py.std.os.getcwd()) self.remaining = remaining Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Fri Jan 19 15:08:16 2007 @@ -146,7 +146,7 @@ self.out.line("test target: %s" %(x.fspath,)) conftestmodules = self.config.conftest.getconftestmodules(None) - for i,x in py.builtin.enumerate(conftestmodules): + for i,x in py.builtin.enumerate(conftestmodules): self.out.line("initial conf %d: %s" %(i, x.__file__)) #for i, x in py.builtin.enumerate(py.test.config.configpaths): From fijal at codespeak.net Fri Jan 19 15:16:04 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 15:16:04 +0100 (CET) Subject: [py-svn] r36995 - py/branch/config/py/test/rsession/testing Message-ID: <20070119141604.C7F6410070@code0.codespeak.net> Author: fijal Date: Fri Jan 19 15:16:03 2007 New Revision: 36995 Modified: py/branch/config/py/test/rsession/testing/test_slave.py Log: Add a test for PidInfo. Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Fri Jan 19 15:16:03 2007 @@ -221,3 +221,17 @@ else: py.test.fail("missing exception") + +def test_pidinfo(): + pidinfo = PidInfo() + pid = os.fork() + if pid: + pidinfo.set_pid(pid) + pidinfo.waitandclear(pid, 0) + else: + import time, sys + time.sleep(.3) + os._exit(0) + # check if this really exits + py.test.raises(OSError, "os.waitpid(pid, 0)") + From fijal at codespeak.net Fri Jan 19 15:19:58 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 15:19:58 +0100 (CET) Subject: [py-svn] r36996 - py/branch/config/py/test/rsession/testing Message-ID: <20070119141958.A725A10072@code0.codespeak.net> Author: fijal Date: Fri Jan 19 15:19:57 2007 New Revision: 36996 Modified: py/branch/config/py/test/rsession/testing/test_slave.py Log: Skip test if platform does not support os.fork Modified: py/branch/config/py/test/rsession/testing/test_slave.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_slave.py (original) +++ py/branch/config/py/test/rsession/testing/test_slave.py Fri Jan 19 15:19:57 2007 @@ -223,6 +223,8 @@ def test_pidinfo(): + if not hasattr(os, 'fork') or not hasattr(os, 'waitpid'): + py.test.skip("Platform does not support fork") pidinfo = PidInfo() pid = os.fork() if pid: From fijal at codespeak.net Fri Jan 19 16:00:22 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 16:00:22 +0100 (CET) Subject: [py-svn] r36998 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070119150022.5ADA410079@code0.codespeak.net> Author: fijal Date: Fri Jan 19 16:00:20 2007 New Revision: 36998 Modified: py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/rsession/testing/test_reporter.py Log: Improve reporting a bit, make test not relying on concrete numbers. Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Fri Jan 19 16:00:20 2007 @@ -10,6 +10,9 @@ from py.__.test.terminal.out import getout from py.__.test.rsession import report from py.__.test.rsession import outcome +from py.__.misc.terminal_helper import ansi_print, terminal_width + +import sys class AbstractReporter(object): def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)): @@ -224,20 +227,22 @@ def report_ReceivedItemOutcome(self, event): host = event.host + itempath = " ".join(event.item.listnames()[1:]) if event.outcome.passed: status = "PASSED " self.passed[host] += 1 + print "%10s: %s %s" %(host.hostname[:10], status, itempath) elif event.outcome.skipped: status = "SKIPPED" self.skipped_tests_outcome.append(event) self.skipped[host] += 1 + print "%10s: %s %s" %(host.hostname[:10], status, itempath) else: - status = "FAILED " self.failed[host] += 1 self.failed_tests_outcome.append(event) - # we'll take care of them later - itempath = " ".join(event.item.listnames()[1:]) - print "%10s: %s %s" %(host.hostname[:10], status, itempath) + sys.stdout.write("%10s: " % host.hostname[:10]) + ansi_print("FAILED", esc=31, newline=False, file=sys.stdout) + print " " + itempath def report_Nodes(self, event): self.nodes = event.nodes Modified: py/branch/config/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_reporter.py (original) +++ py/branch/config/py/test/rsession/testing/test_reporter.py Fri Jan 19 16:00:20 2007 @@ -181,8 +181,9 @@ def test_module(self): #py.test.skip("XXX rewrite test to not rely on exact formatting") - assert self._test_module().endswith("test_slave.py[9] FsF."),\ - self._test_module() + output = self._test_module() + assert output.find("test_slave") != -1 + assert output.endswith("FsF."), output def test_full_module(self): #py.test.skip("XXX rewrite test to not rely on exact formatting") From hpk at codespeak.net Fri Jan 19 16:06:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 16:06:13 +0100 (CET) Subject: [py-svn] r36999 - in py/branch/config/py/test: . rsession Message-ID: <20070119150613.716A910079@code0.codespeak.net> Author: hpk Date: Fri Jan 19 16:06:11 2007 New Revision: 36999 Added: py/branch/config/py/test/rsession/cleanup-rsession.txt - copied unchanged from r36998, py/branch/config/py/test/rsession-cleanup.txt Removed: py/branch/config/py/test/rsession-cleanup.txt Log: move cleanup doc out of the way of completion (and into a more proper directory) Deleted: /py/branch/config/py/test/rsession-cleanup.txt ============================================================================== --- /py/branch/config/py/test/rsession-cleanup.txt Fri Jan 19 16:06:11 2007 +++ (empty file) @@ -1,42 +0,0 @@ -Quick overview to make myself sure what I'm talking about. - -exit cases: - -1. normal exit (tests finished) -2. -x and a failing test seen at sending side -3. C-c pressed, or SystemExit raised - -exit conditions: - -- normal cleanup (exit) -- cleanup of remote nodes (rsession only) - possibly kill child processes - and close gateway nicely -- cleanup of web server (only regarding to web interface) - probably later - -Normal exit: - -easy :), just works - --x and a failing test: - -works, but with a hack - -C-c hard case: - -we should have different layers of C-c pressed, to make sure that we can -interrupt teardown and still exit cleanly, that's why I don't like the idea -of having finally: beacause exceptions are ignored inside finally. - -We should kill Info.pid hack, which is quite awfull usecase of global -variable. - -So: - - first strike of C-c should go to hosts_teardown and start sending nodes info - that they should kill boxes. - - second strike should exit this teardown, print some message like "abandoned" - and eventually try to kill web server. - - third strike should just exit program. - From fijal at codespeak.net Fri Jan 19 16:07:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 16:07:11 +0100 (CET) Subject: [py-svn] r37001 - py/branch/config/py/test/rsession Message-ID: <20070119150711.7094E1007F@code0.codespeak.net> Author: fijal Date: Fri Jan 19 16:07:09 2007 New Revision: 37001 Modified: py/branch/config/py/test/rsession/reporter.py Log: Make it even bolder. Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Fri Jan 19 16:07:09 2007 @@ -241,7 +241,7 @@ self.failed[host] += 1 self.failed_tests_outcome.append(event) sys.stdout.write("%10s: " % host.hostname[:10]) - ansi_print("FAILED", esc=31, newline=False, file=sys.stdout) + ansi_print("FAILED", esc=(31,1), newline=False, file=sys.stdout) print " " + itempath def report_Nodes(self, event): From hpk at codespeak.net Fri Jan 19 16:08:32 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 16:08:32 +0100 (CET) Subject: [py-svn] r37002 - py/branch/config/py/test Message-ID: <20070119150832.44A241007E@code0.codespeak.net> Author: hpk Date: Fri Jan 19 16:08:31 2007 New Revision: 37002 Modified: py/branch/config/py/test/cmdline.py Log: remove unsued/untested use case of main() invocation Modified: py/branch/config/py/test/cmdline.py ============================================================================== --- py/branch/config/py/test/cmdline.py (original) +++ py/branch/config/py/test/cmdline.py Fri Jan 19 16:08:31 2007 @@ -8,8 +8,6 @@ warn_about_missing_assertion() if args is None: args = py.std.sys.argv[1:] - elif isinstance(args, basestring): - args = args.split(" ") config = py.test.config config.parse(args) sessionclass = config.getsessionclass() From cfbolz at codespeak.net Fri Jan 19 16:25:00 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 16:25:00 +0100 (CET) Subject: [py-svn] r37003 - py/dist/py/code/testing Message-ID: <20070119152500.4FD6610078@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 16:24:58 2007 New Revision: 37003 Modified: py/dist/py/code/testing/test_source.py Log: a skipped test showing a fundamental problem with the heuristic in getstatementrange. Should be fixed, but probably only after the release. Modified: py/dist/py/code/testing/test_source.py ============================================================================== --- py/dist/py/code/testing/test_source.py (original) +++ py/dist/py/code/testing/test_source.py Fri Jan 19 16:24:58 2007 @@ -173,6 +173,22 @@ assert len(source) == 6 assert source.getstatementrange(2) == (1, 4) + def test_getstatementrange_bug2(self): + py.test.skip("fix me") + source = Source("""\ + assert ( + 33 + == + [ + X(3, + b=1, c=2 + ), + ] + ) + """) + assert len(source) == 9 + assert source.getstatementrange(5) == (0, 9) + def test_compile_and_getsource(self): co = self.source.compile() exec co From cfbolz at codespeak.net Fri Jan 19 16:35:58 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 16:35:58 +0100 (CET) Subject: [py-svn] r37005 - py/dist/py/code/testing Message-ID: <20070119153558.BDA6C10078@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 16:35:41 2007 New Revision: 37005 Modified: py/dist/py/code/testing/test_source.py Log: add issue number Modified: py/dist/py/code/testing/test_source.py ============================================================================== --- py/dist/py/code/testing/test_source.py (original) +++ py/dist/py/code/testing/test_source.py Fri Jan 19 16:35:41 2007 @@ -174,7 +174,7 @@ assert source.getstatementrange(2) == (1, 4) def test_getstatementrange_bug2(self): - py.test.skip("fix me") + py.test.skip("fix me (issue19)") source = Source("""\ assert ( 33 From hpk at codespeak.net Fri Jan 19 16:50:36 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 16:50:36 +0100 (CET) Subject: [py-svn] r37009 - in py/branch/config/py/test: . testing Message-ID: <20070119155036.9144510077@code0.codespeak.net> Author: hpk Date: Fri Jan 19 16:50:32 2007 New Revision: 37009 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/defaultconftest.py py/branch/config/py/test/testing/test_config.py Log: adding a config method to determine the session name Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Fri Jan 19 16:50:32 2007 @@ -90,6 +90,19 @@ mod = __import__(importpath, None, None, ['__doc__']) return getattr(mod, sessionname) + def _getsessionname(self): + """ return session name as determined from options. """ + name = 'Session' + if self.option.dist: + name = 'RSession' + else: + optnames = 'startserver runbrowser apigen restreport'.split() + for opt in optnames: + if getattr(self.option, opt, False): + name = 'LSession' + break + return name + def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ global config Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Fri Jan 19 16:50:32 2007 @@ -78,6 +78,9 @@ Option('', '--tkinter', action="store_true", dest="tkinter", default=False, help="use tkinter test session frontend."), + Option('', '--dist', + action="store_true", dest="dist", default=False, + help="use ad-hoc distributed testing (requires conftest settings)"), Option('', '--looponfailing', action="store_true", dest="looponfailing", default=False, help="loop on failing test set."), Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Fri Jan 19 16:50:32 2007 @@ -127,3 +127,39 @@ config.merge_repr(repr) assert config.option.gdest == 11 +class TestSessionAndOptions: + def setup_class(cls): + cls.tmproot = py.test.ensuretemp(cls.__name__) + + def setup_method(self, method): + self.tmpdir = self.tmproot.mkdir(method.__name__) + + def XXXtest_sessionclass_conftest_override(self): + self.tmpdir.join("conftest.py").write(py.code.Source(""" + from py.__.test.session import Session as orig + class Session(orig): + x = 42 + """)) + config = py.test.config._reparse([self.tmpdir]) + cls = config._getsessionclass() + assert cls.x == 42 + config.option.dist = True + config.option.startserver = True + cls = config._getsessionclass() + assert cls.x == 42 + + def test_sessionname_default(self): + config = py.test.config._reparse([]) + assert config._getsessionname() == 'Session' + + def test_sessionname_dist(self): + config = py.test.config._reparse(['--dist']) + assert config._getsessionname() == 'RSession' + + def test_implied_lsession(self): + optnames = 'startserver runbrowser apigen=x rest'.split() + for x in optnames: + config = py.test.config._reparse(['--%s' % x]) + assert config._getsessionname() == 'LSession' + config = py.test.config._reparse(['--dist', '--%s' % x]) + assert config._getsessionname() == 'RSession' # XXX is this right? From cfbolz at codespeak.net Fri Jan 19 17:04:27 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 17:04:27 +0100 (CET) Subject: [py-svn] r37010 - py/dist/py/rest Message-ID: <20070119160427.73B6610070@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 17:04:25 2007 New Revision: 37010 Modified: py/dist/py/rest/convert.py Log: quote args, so that spaces in the paths are no problem Modified: py/dist/py/rest/convert.py ============================================================================== --- py/dist/py/rest/convert.py (original) +++ py/dist/py/rest/convert.py Fri Jan 19 17:04:25 2007 @@ -16,9 +16,9 @@ raise SystemExit("neither ps2eps nor ps2epsi found") try: eps = ps.new(ext=".eps") - py.process.cmdexec("ps2epsi %s %s" % (ps, eps)) + py.process.cmdexec('ps2epsi "%s" "%s"' % (ps, eps)) except ExecutionFailed: - py.process.cmdexec("ps2eps -l -f %s" % ps) + py.process.cmdexec('ps2eps -l -f "%s"' % ps) def ps2pdf(ps, compat_level="1.2"): if not py.path.local.sysfind("gs"): @@ -36,12 +36,12 @@ # XXX write a pure python version if not py.path.local.sysfind("epstopdf"): raise SystemExit("ERROR: epstopdf not found") - py.process.cmdexec("epstopdf %s" % eps) + py.process.cmdexec('epstopdf "%s"' % eps) def dvi2eps(dvi, dest=None): if dest is None: dest = eps.new(ext=".eps") - command = 'dvips -q -E -n 1 -D 600 -p 1 -o %s %s' % (dest, dvi) + command = 'dvips -q -E -n 1 -D 600 -p 1 -o "%s" "%s"' % (dest, dvi) if not py.path.local.sysfind("dvips"): raise SystemExit("ERROR: dvips not found") py.process.cmdexec(command) @@ -52,7 +52,7 @@ result = fn.new(ext=new_extension) print result arg = "-T%s" % (format_to_dotargument[new_extension], ) - py.std.os.system("dot %s %s > %s" % (arg, fn, result)) + py.std.os.system('dot "%s" "%s" > "%s"' % (arg, fn, result)) if new_extension == "eps": ps = result.new(ext="ps") result.move(ps) @@ -115,7 +115,7 @@ def gen_dvi(self): origdir = py.path.local() self.temp.chdir() - py.process.cmdexec('latex %s' % (self.latex)) + py.process.cmdexec('latex "%s"' % (self.latex)) origdir.chdir() def gen_png(self): @@ -148,7 +148,9 @@ sx = int((x2 - x1) * self.scale * self.upscale) sy = int((y2 - y1) * self.scale * self.upscale) res = 72 * self.scale * self.upscale - command = 'gs -q -g%dx%d -r%dx%d -sDEVICE=%s -sOutputFile=%s -dNOPAUSE -dBATCH %s' % (sx, sy, res, res, self.output_format, self.png, ps) + command = ('gs -q -g%dx%d -r%dx%d -sDEVICE=%s -sOutputFile="%s" ' + '-dNOPAUSE -dBATCH "%s"') % ( + sx, sy, res, res, self.output_format, self.png, ps) py.process.cmdexec(command) def scale_image(self): From cfbolz at codespeak.net Fri Jan 19 17:06:31 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 17:06:31 +0100 (CET) Subject: [py-svn] r37011 - py/dist/py/misc/testing Message-ID: <20070119160631.D977E10070@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 17:06:28 2007 New Revision: 37011 Modified: py/dist/py/misc/testing/test_svnlook.py Log: add quoting Modified: py/dist/py/misc/testing/test_svnlook.py ============================================================================== --- py/dist/py/misc/testing/test_svnlook.py (original) +++ py/dist/py/misc/testing/test_svnlook.py Fri Jan 19 17:06:28 2007 @@ -10,8 +10,8 @@ def test_svnlook(): tempdir = py.test.ensuretemp("svnlook") repo = tempdir.join("repo") - py.process.cmdexec("svnadmin create --fs-type fsfs %s" % repo) - py.process.cmdexec("svnadmin load %s < \"%s\"" %(repo, + py.process.cmdexec('svnadmin create --fs-type fsfs "%s"' % repo) + py.process.cmdexec('svnadmin load "%s" < "%s"' %(repo, data.join("svnlookrepo.dump"))) author = svnlook.author(repo, 1) From cfbolz at codespeak.net Fri Jan 19 17:10:57 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 17:10:57 +0100 (CET) Subject: [py-svn] r37012 - in py/dist/py: apigen/testing rest Message-ID: <20070119161057.C18A610077@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 17:10:55 2007 New Revision: 37012 Modified: py/dist/py/apigen/testing/test_apigen_functional.py py/dist/py/rest/latex.py Log: more quoting Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Fri Jan 19 17:10:55 2007 @@ -13,7 +13,7 @@ parentdir = py.magic.autopath().dirpath().dirpath() pkgdir = fs_root.join('pkg') output = py.process.cmdexec( - 'APIGEN_TARGET=%s py.test --session=L --apigen=%s/apigen.py %s' % ( + 'APIGEN_TARGET="%s" py.test --session=L --apigen="%s/apigen.py" "%s"' % ( tempdir, parentdir, pkgdir)) assert output.lower().find('traceback') == -1 Modified: py/dist/py/rest/latex.py ============================================================================== --- py/dist/py/rest/latex.py (original) +++ py/dist/py/rest/latex.py Fri Jan 19 17:10:55 2007 @@ -131,7 +131,7 @@ i = 0 while i < 10: # there should never be as many as five reruns, but to be sure try: - latexoutput = py.process.cmdexec("pdflatex %s" % (tex, )) + latexoutput = py.process.cmdexec('pdflatex "%s"' % (tex, )) except ExecutionFailed, e: print "ERROR: pdflatex execution failed" print "pdflatex stdout:" From hpk at codespeak.net Fri Jan 19 17:14:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 17:14:16 +0100 (CET) Subject: [py-svn] r37013 - in py/branch/config/py/test: . testing tkinter Message-ID: <20070119161416.D425710078@code0.codespeak.net> Author: hpk Date: Fri Jan 19 17:14:11 2007 New Revision: 37013 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/defaultconftest.py py/branch/config/py/test/testing/test_config.py py/branch/config/py/test/tkinter/reportsession.py Log: somewhat simpler lookup mechanism for sessions Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Fri Jan 19 17:14:11 2007 @@ -81,20 +81,26 @@ and looked up in initial config modules. """ sessionname = self.option.session + 'Session' - try: - return self.conftest.lget(sessionname) - except KeyError: - pass sessionimportpaths = self.conftest.lget('sessionimportpaths') importpath = sessionimportpaths[sessionname] mod = __import__(importpath, None, None, ['__doc__']) return getattr(mod, sessionname) + def _lookupsession(self, name): + """ return session class for the given name. """ + res = self.conftest.rget(name) + if isinstance(res, str): + mod = __import__(res, None, None, '__doc__') + res = getattr(mod, name) + return res + def _getsessionname(self): """ return session name as determined from options. """ - name = 'Session' + name = 'TerminalSession' if self.option.dist: name = 'RSession' + elif self.option.tkinter: + name = 'TkinterSession' else: optnames = 'startserver runbrowser apigen restreport'.split() for opt in optnames: Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Fri Jan 19 17:14:11 2007 @@ -19,6 +19,11 @@ 'TkinterSession': 'py.__.test.tkinter.reportsession', } +TkinterSession = 'py.__.test.tkinter.reportsession' +TerminalSession = 'py.__.test.terminal.terminal' +RSession = 'py.__.test.rsession.rsession' +LSession = 'py.__.test.rsession.rsession' + #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 Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Fri Jan 19 17:14:11 2007 @@ -149,17 +149,33 @@ assert cls.x == 42 def test_sessionname_default(self): - config = py.test.config._reparse([]) - assert config._getsessionname() == 'Session' + config = py.test.config._reparse([self.tmpdir]) + assert config._getsessionname() == 'TerminalSession' def test_sessionname_dist(self): - config = py.test.config._reparse(['--dist']) + config = py.test.config._reparse([self.tmpdir, '--dist']) assert config._getsessionname() == 'RSession' def test_implied_lsession(self): optnames = 'startserver runbrowser apigen=x rest'.split() for x in optnames: - config = py.test.config._reparse(['--%s' % x]) + config = py.test.config._reparse([self.tmpdir, '--%s' % x]) assert config._getsessionname() == 'LSession' - config = py.test.config._reparse(['--dist', '--%s' % x]) + config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x]) assert config._getsessionname() == 'RSession' # XXX is this right? + + def test_tkintersession(self): + config = py.test.config._reparse([self.tmpdir, '--tkinter']) + assert config._getsessionname() == 'TkinterSession' + + def test_sessionname_lookup(self): + config = py.test.config._reparse([self.tmpdir]) + for name in 'TerminalSession LSession RSession TkinterSession'.split(): + cls = config._lookupsession(name) + assert cls.__name__ == name + + def XXXtest_sessionname_lookup_custom(self): + config = py.test.config._reparse([]) + for name in 'TerminalSession LSession RSession TkinterSession'.split(): + cls = config._lookupsession(name) + assert py.std.inspect.isclass(cls) Modified: py/branch/config/py/test/tkinter/reportsession.py ============================================================================== --- py/branch/config/py/test/tkinter/reportsession.py (original) +++ py/branch/config/py/test/tkinter/reportsession.py Fri Jan 19 17:14:11 2007 @@ -3,7 +3,7 @@ from util import TestReport from py.__.test.session import Session -class ReportSession(Session): +class TkinterSession(Session): def __init__(self, config = None, channel = None): super(ReportSession, self).__init__(config) @@ -44,5 +44,5 @@ def sendreport(self, report): self.channel.send(report.to_channel()) -TkinterSession = ReportSession +ReportSession = TkinterSession From guido at codespeak.net Fri Jan 19 17:24:03 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 19 Jan 2007 17:24:03 +0100 (CET) Subject: [py-svn] r37014 - in py/dist/py/apigen: . testing Message-ID: <20070119162403.A539E10078@code0.codespeak.net> Author: guido Date: Fri Jan 19 17:23:59 2007 New Revision: 37014 Modified: py/dist/py/apigen/apigen.py py/dist/py/apigen/htmlgen.py py/dist/py/apigen/linker.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_htmlgen.py Log: Cleanup in the tests: I'm using a Linker class in the tests now that just returns a certain string instead of raising a KeyError in get_target(), which makes that a lot of the cruft (first pass stuff that was basically only there to satisfy the linker) could be removed. Added source navigation. Modified: py/dist/py/apigen/apigen.py ============================================================================== --- py/dist/py/apigen/apigen.py (original) +++ py/dist/py/apigen/apigen.py Fri Jan 19 17:23:59 2007 @@ -35,7 +35,7 @@ targetdir.ensure(dir=True) all_names = dsa._get_names(filter=lambda x, y: True) - namespace_tree = htmlgen.create_namespace_tree(dsa, all_names) + namespace_tree = htmlgen.create_namespace_tree(all_names) ns_data = htmlgen.prepare_namespace_api_pages(l, dsa, targetdir, namespace_tree) class_names = dsa.get_class_names() @@ -53,5 +53,5 @@ htmlgen.build_class_api_pages(l, class_data, proj, targetdir) htmlgen.build_method_api_pages(l, method_data, proj, targetdir) htmlgen.build_function_api_pages(l, func_data, proj, targetdir) - htmlgen.build_source_pages(l, source_data, proj, targetdir) + htmlgen.build_source_pages(l, source_data, proj, pkgdir, targetdir) Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 19 17:23:59 2007 @@ -66,6 +66,21 @@ # XXX copy and modify formatargspec to produce html return H.em(inspect.formatargspec(*inspect.getargspec(func))) +def create_namespace_tree(dotted_names): + """ creates a tree (in dict form) from a set of dotted names + """ + ret = {} + for dn in dotted_names: + path = dn.split('.') + for i in xrange(len(path)): + ns = '.'.join(path[:i]) + itempath = '.'.join(path[:i + 1]) + if ns not in ret: + ret[ns] = [] + if itempath not in ret[ns]: + ret[ns].append(itempath) + return ret + def build_full_method_view(base, linker, dsa, dotted_name): """ build the html for a class method """ func = dsa.get_obj(dotted_name) @@ -156,8 +171,8 @@ return snippet # XXX nav functions need cleanup!! -def build_navitem_html(linker, name, dotted_name, indent, selected): - href = linker.get_lazyhref(dotted_name) +def build_navitem_html(linker, name, linkid, indent, selected): + href = linker.get_lazyhref(linkid) navitem = H.NavigationItem((indent * 2 * u'\xa0'), H.a(name, href=href)) if selected: navitem.attr.class_ = 'selected' @@ -195,41 +210,104 @@ item_dotted_name, indent, selected)) return H.Navigation(*navitems) +def source_dirs_files(fspath): + """ returns a tuple (dirs, files) for fspath + + dirs are all the subdirs, files are the files which are interesting + in building source documentation for a Python code tree (basically all + normal files excluding .pyc and .pyo ones) + + all files and dirs that have a name starting with . are considered + hidden + """ + dirs = [] + files = [] + for child in fspath.listdir(): + if child.basename.startswith('.'): + continue + if child.check(dir=True): + dirs.append(child) + elif child.check(file=True): + if child.ext in ['.pyc', '.pyo']: + continue + files.append(child) + return dirs, files + +def build_source_navigation(linker, projroot, fspath, outputpath): + nav = H.Navigation() + relpath = fspath.relto(projroot) + path = relpath.split('/') + indent = 0 + # build links to parents + for i in xrange(len(path)): + dirpath = '/'.join(path[:i]) + abspath = projroot.join(dirpath).strpath + if i == 0: + text = 'root' + else: + text = path[i-1] + nav.append(build_navitem_html(linker, text, abspath, indent, False)) + indent += 1 + # build siblings or children and self + if fspath.check(dir=True): + # we're a dir, build ourselves and our children + dirpath = fspath + nav.append(build_navitem_html(linker, dirpath.basename, + dirpath.strpath, indent, True)) + indent += 1 + elif fspath.strpath == projroot.strpath: + dirpath = fspath + else: + # we're a file, build our parent's children only + dirpath = fspath.dirpath() + diritems, fileitems = source_dirs_files(dirpath) + for dir in sorted(diritems): + nav.append(build_navitem_html(linker, dir.basename, dir.strpath, + indent, False)) + for file in sorted(fileitems): + selected = (fspath.check(file=True) and + file.basename == fspath.basename) + nav.append(build_navitem_html(linker, file.basename, file.strpath, + indent, selected)) + return nav + re = py.std.re _reg_body = re.compile(r']*>(.*)', re.S) -def build_source_python_page(linker, fspath): +def build_source_python_page(linker, projroot, fspath, outputpath): mod = source_browser.parse_path(fspath) # XXX let's cheat a bit here... there should be a different function using # the linker, and returning a proper py.xml.html element, at some point html = source_html.create_html(mod) snippet = _reg_body.search(html).group(1) tag = H.SourceDef(raw(snippet)) - nav = H.Navigation('navigation') + nav = build_source_navigation(linker, projroot, fspath, outputpath) return tag, nav -def build_source_dir_page(linker, fspath): +def build_source_dir_page(linker, projroot, fspath, outputpath): tag = H.DirList() for path in fspath.listdir(): if path.ext in ['.pyc', '.pyo'] or path.basename.startswith('.'): continue tag.append(H.DirListItem(H.a(path.basename, href=linker.get_lazyhref(str(path))))) - nav = H.Navigation('navigation') + nav = build_source_navigation(linker, projroot, fspath, outputpath) return tag, nav -def build_source_nonpython_page(linker, fspath): +def build_source_nonpython_page(linker, projroot, fspath, outputpath): try: tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8')) except UnicodeError: tag = H.NonPythonSource('no source available (binary file?)') - nav = H.Navigation('navigation') + nav = build_source_navigation(linker, projroot, fspath, outputpath) return tag, nav # the build_*_pages methods all do two-pass page generation (first pass builds # the link db, second uses it) def prepare_source_pages(linker, base, outputbase): passed = [] - for fspath in base.visit(): + for fspath in [base] + list(base.visit()): + if fspath.ext in ['.pyc', '.pyo']: + continue relfspath = fspath.relto(base) if relfspath.find('/.') > -1: # skip hidden dirs and files @@ -243,24 +321,35 @@ passed.append((fspath, outputpath)) return passed -def build_source_pages(linker, data, project, outputbase): +def build_source_pages(linker, data, project, projroot, outputbase): """ build syntax-colored source views """ for fspath, outputpath in data: if fspath.check(ext='.py'): try: - tag, nav = build_source_python_page(linker, fspath) + tag, nav = build_source_python_page(linker, projroot, fspath, + outputpath) except (KeyboardInterrupt, SystemError): raise except: # XXX strange stuff going wrong at times... need to fix - tag, nav = build_source_nonpython_page(linker, fspath) + exc, e, tb = py.std.sys.exc_info() + print '%s - %s' % (exc, e) + print + print ''.join(py.std.traceback.format_tb(tb)) + print '-' * 79 + del tb + tag, nav = build_source_nonpython_page(linker, projroot, + fspath, outputpath) elif fspath.check(dir=True): - tag, nav = build_source_dir_page(linker, fspath) + tag, nav = build_source_dir_page(linker, projroot, fspath, + outputpath) else: - tag, nav = build_source_nonpython_page(linker, fspath) + tag, nav = build_source_nonpython_page(linker, projroot, fspath, + outputpath) page = wrap_page(project, 'sources for %s' % (fspath.basename,), tag, nav, outputbase) outputpath.ensure() - content = page.unicode() #linker.call_withbase(reltargetpath, page.unicode) + reltargetpath = outputpath.relto(outputbase) + content = linker.call_withbase(reltargetpath, page.unicode) outputpath.write(content.encode("utf8")) def prepare_class_api_pages(linker, dsa, base, namespace_tree, @@ -346,20 +435,6 @@ targetpath.ensure() targetpath.write(content.encode("utf8")) -def create_namespace_tree(dsa, dotted_names): - ret = {} - class_names = dsa.get_class_names() - for dn in dotted_names: - path = dn.split('.') - for i in xrange(len(path)): - ns = '.'.join(path[:i]) - itempath = '.'.join(path[:i + 1]) - if ns not in ret: - ret[ns] = [] - if itempath not in ret[ns]: - ret[ns].append(itempath) - return ret - def prepare_namespace_api_pages(linker, dsa, base, namespace_tree): passed = [] module_name = dsa.get_module_name().split('/')[-1] Modified: py/dist/py/apigen/linker.py ============================================================================== Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Fri Jan 19 17:23:59 2007 @@ -70,11 +70,16 @@ cls.project = Project() def setup_method(self, meth): - self.linker = Linker() + class LinkerForTests(Linker): + def get_target(self, linkid): + try: + return super(LinkerForTests, self).get_target(linkid) + except KeyError: + return 'unknown_link_%s' % (linkid,) + self.linker = LinkerForTests() def test_build_full_method_view(self): base = py.test.ensuretemp('build_full_method_view') - prepare_source_pages(self.linker, self.fs_root, base) snippet = build_full_method_view(base, self.linker, self.dsa, 'pkg.sub.func') html = snippet.unicode() @@ -82,13 +87,9 @@ def test_build_function_api_pages(self): base = py.test.ensuretemp('build_function_api_pages') - prepare_source_pages(self.linker, self.fs_root, base) - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.sub', 'pkg.sub.func', + namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) data = prepare_function_api_pages(self.linker, self.dsa, base, namespace_tree, ['pkg.sub.func']) @@ -100,7 +101,6 @@ def test_build_class_view(self): base = py.test.ensuretemp('build_class_view') - prepare_source_pages(self.linker, self.fs_root, base) snippet = build_class_view(base, self.linker, self.dsa, 'pkg.SomeClass') html = snippet.unicode() @@ -108,19 +108,14 @@ def test_build_class_api_pages(self): base = py.test.ensuretemp('build_class_api_pages') - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.SomeClass', + namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, namespace_tree, ['pkg.SomeClass', 'pkg.SomeSubClass']) - prepare_source_pages(self.linker, self.fs_root, base) - prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) build_class_api_pages(self.linker, data, self.project, base) - build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() @@ -128,79 +123,84 @@ def test_build_class_api_pages_nav_links(self): base = py.test.ensuretemp('build_class_api_pages_nav_links') - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.SomeClass', + namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, namespace_tree, ['pkg.SomeSubClass', 'pkg.SomeClass']) - prepare_source_pages(self.linker, self.fs_root, base) - prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) + # fake some stuff that would be built from other methods + self.linker.set_link('', 'api/index.html') + self.linker.set_link('pkg', 'api/pkg.html') build_class_api_pages(self.linker, data, self.project, base) - build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() print html - assert 'href="index.html"' in html - assert 'href="pkg.html"' in html - assert 'href="pkg.SomeClass.html"' in html - assert 'href="pkg.SomeSubClass.html"' in html - assert 'href="pkg.sub.html"' in html + # note that the root and the first level both have the same name (pkg) + # in our example project (XXX fixme!!) + assert 'href="index.html">pkg' in html + assert 'href="pkg.html">pkg' in html + assert 'href="pkg.SomeClass.html">SomeClass' in html + assert 'href="pkg.SomeSubClass.html">SomeSubClass' in html assert not 'href="pkg.sub.func.html"' in html + py.test.skip('WOP from here') + assert 'href="pkg.sub.html">sub' in html _checkhtml(html) def test_build_class_api_pages_base_link(self): base = py.test.ensuretemp('build_class_api_pages_base_link') - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.SomeClass', + namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, namespace_tree, ['pkg.SomeSubClass', 'pkg.SomeClass']) - prepare_source_pages(self.linker, self.fs_root, base) - prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) build_class_api_pages(self.linker, data, self.project, base) - build_method_api_pages(self.linker, methodsdata, self.project, base) clsfile = base.join('api/pkg.SomeSubClass.html') assert clsfile.check() html = clsfile.read() - assert html.find('href="pkg.SomeClass.html">pkg.SomeClass') > -1 + assert 'href="pkg.SomeClass.html">pkg.SomeClass' in html _checkhtml(html) def test_build_source_pages(self): base = py.test.ensuretemp('build_source_pages') data = prepare_source_pages(self.linker, self.fs_root, base) - build_source_pages(self.linker, data, self.project, base) + build_source_pages(self.linker, data, self.project, self.fs_root, base) somesource = base.join('source/pkg/func.py.html').read() _checkhtml(somesource) + def test_build_source_pages_nav(self): + base = py.test.ensuretemp('build_source_pages_nav') + data = prepare_source_pages(self.linker, self.fs_root, base) + build_source_pages(self.linker, data, self.project, self.fs_root, base) + funcsource = base.join('source/pkg/func.py.html') + assert funcsource.check(file=True) + html = funcsource.read() + print html + #assert 'py' in html + assert 'pkg' in html + assert 'someclass.py' in html + assert 'somesubclass.py' in html + def test_api_source_links(self): base = py.test.ensuretemp('api_source_links') - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.SomeClass', + namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, base, namespace_tree, ['pkg.SomeSubClass', 'pkg.SomeClass']) sourcedata = prepare_source_pages(self.linker, self.fs_root, base) - prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) build_class_api_pages(self.linker, data, self.project, base) - build_method_api_pages(self.linker, methodsdata, self.project, base) - build_source_pages(self.linker, sourcedata, self.project, base) + build_source_pages(self.linker, sourcedata, self.project, self.fs_root, + base) funchtml = base.join('api/pkg.SomeClass.html').read() assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) def test_create_namespace_tree(self): - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.sub.func', + namespace_tree = create_namespace_tree(['pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) assert namespace_tree == {'': ['pkg'], @@ -210,8 +210,7 @@ def test_build_namespace_api_pages_index(self): base = py.test.ensuretemp('build_namespace_api_pages') - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.sub', 'pkg.sub.func', + namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) data = prepare_namespace_api_pages(self.linker, self.dsa, base, @@ -230,18 +229,11 @@ def test_build_namespace_api_pages_subnamespace(self): base = py.test.ensuretemp('build_namespace_api_pages') - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.sub', 'pkg.sub.func', + namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) data = prepare_namespace_api_pages(self.linker, self.dsa, base, namespace_tree) - prepare_class_api_pages(self.linker, self.dsa, base, - namespace_tree, - ['pkg.SomeClass', 'pkg.SomeSubClass']) - prepare_function_api_pages(self.linker, self.dsa, base, - namespace_tree, ['pkg.sub.func']) - prepare_source_pages(self.linker, self.fs_root, base) build_namespace_api_pages(self.linker, data, self.project, base) subfile = base.join('api/pkg.sub.html') assert subfile.check() @@ -251,29 +243,30 @@ def test_build_source_dir_page(self): base = py.test.ensuretemp('build_source_dir_page') data = prepare_source_pages(self.linker, self.fs_root, base) - build_source_pages(self.linker, data, self.project, base) + build_source_pages(self.linker, data, self.project, self.fs_root, base) pkgindex = base.join('source/pkg/index.html') assert pkgindex.check(file=True) html = pkgindex.read() - assert ('someclass.py' - in html) + print html + assert 'someclass.py' in html _checkhtml(html) def test_build_function_api_pages_nav(self): base = py.test.ensuretemp('build_function_api_pages_nav') prepare_source_pages(self.linker, self.fs_root, base) - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.sub', 'pkg.sub.func', + namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) data = prepare_function_api_pages(self.linker, self.dsa, base, namespace_tree, ['pkg.sub.func']) + self.linker.set_link('', 'api/index.html') + self.linker.set_link('pkg', 'api/pkg.html') + self.linker.set_link('pkg.sub', 'api/pkg.sub.html') build_function_api_pages(self.linker, data, self.project, base) funcfile = base.join('api/pkg.sub.func.html') html = funcfile.read() + print html assert '' in html assert '' in html assert '' in html @@ -282,8 +275,7 @@ def test_build_function_navigation(self): base = py.test.ensuretemp('build_function_navigation') prepare_source_pages(self.linker, self.fs_root, base) - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.sub', 'pkg.sub.func', + namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) prepare_namespace_api_pages(self.linker, self.dsa, base, @@ -304,18 +296,11 @@ def test_build_root_namespace_view(self): base = py.test.ensuretemp('build_root_namespace_view') - prepare_source_pages(self.linker, self.fs_root, base) - namespace_tree = create_namespace_tree(self.dsa, - ['pkg.sub', 'pkg.sub.func', + namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) data = prepare_namespace_api_pages(self.linker, self.dsa, base, namespace_tree) - prepare_function_api_pages(self.linker, self.dsa, base, - namespace_tree, ['pkg.sub.func']) - prepare_class_api_pages(self.linker, self.dsa, base, - namespace_tree, - ['pkg.SomeClass', 'pkg.SomeSubClass']) build_namespace_api_pages(self.linker, data, self.project, base) rootfile = base.join('api/index.html') assert rootfile.check() @@ -323,3 +308,17 @@ assert '' in html _checkhtml(html) + def test_build_source_navigation_root(self): + base = py.test.ensuretemp('test_build_source_navigation') + prepare_source_pages(self.linker, self.fs_root, base) + nav = build_source_navigation(self.linker, self.fs_root, + self.fs_root.join('pkg'), base) + html = nav.unicode(indent=0) + print html.encode('UTF-8') + # XXX fix the double slash!! + assert 'href="source//index.html">root' in html + assert 'href="source/pkg/index.html">pkg' in html + assert 'href="source/pkg/func.py.html">func.py' in html + assert 'href="source/pkg/someclass.py.html">someclass.py' in html + assert 'href="source/pkg/somesubclass.py.html">somesubclass.py' in html + Modified: py/dist/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/dist/py/apigen/testing/test_htmlgen.py (original) +++ py/dist/py/apigen/testing/test_htmlgen.py Fri Jan 19 17:23:59 2007 @@ -1,12 +1,38 @@ -from py.__.apigen.htmlgen import build_navitem_html +import py +from py.__.apigen import htmlgen from py.__.apigen.linker import Linker +def test_create_namespace_tree(): + tree = htmlgen.create_namespace_tree(['foo.bar.baz']) + assert tree == {'': ['foo'], + 'foo': ['foo.bar'], + 'foo.bar': ['foo.bar.baz']} + tree = htmlgen.create_namespace_tree(['foo.bar.baz', 'foo.bar.qux']) + assert tree == {'': ['foo'], + 'foo': ['foo.bar'], + 'foo.bar': ['foo.bar.baz', 'foo.bar.qux']} + def test_build_navitem_html(): l = Linker() l.set_link('spam.eggs.foo', 'foo.html') - h = build_navitem_html(l, 'foo', 'spam.eggs.foo', 0, False) + h = htmlgen.build_navitem_html(l, 'foo', 'spam.eggs.foo', 0, False) assert unicode(h) == u'' - h = build_navitem_html(l, 'bar', 'spam.eggs.foo', 1, True) + h = htmlgen.build_navitem_html(l, 'bar', 'spam.eggs.foo', 1, True) assert unicode(h) == (u'
\xa0\xa0' u'bar
') +def test_source_dirs_files(): + temp = py.test.ensuretemp('test_source_dirs_files') + temp.join('dir').ensure(dir=True) + temp.join('dir/file1.py').ensure(file=True) + temp.join('dir/file2.pyc').ensure(file=True) + temp.join('dir/file3.c').ensure(file=True) + temp.join('dir/.hidden_file').ensure(file=True) + temp.join('dir/sub').ensure(dir=True) + temp.join('dir/.hidden_dir').ensure(dir=True) + dirs, files = htmlgen.source_dirs_files(temp.join('dir')) + dirnames = sorted([d.basename for d in dirs]) + filenames = sorted([f.basename for f in files]) + assert dirnames == ['sub'] + assert filenames == ['file1.py', 'file3.c'] + From fijal at codespeak.net Fri Jan 19 17:24:38 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 17:24:38 +0100 (CET) Subject: [py-svn] r37015 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070119162438.2DE8E10079@code0.codespeak.net> Author: fijal Date: Fri Jan 19 17:24:32 2007 New Revision: 37015 Modified: py/branch/config/py/test/rsession/hostmanage.py py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/testing/test_rsession.py Log: Major cleanup of hostmanage. Got rid of global functions in favor of some better-structured classes. Modified: py/branch/config/py/test/rsession/hostmanage.py ============================================================================== --- py/branch/config/py/test/rsession/hostmanage.py (original) +++ py/branch/config/py/test/rsession/hostmanage.py Fri Jan 19 17:24:32 2007 @@ -60,107 +60,143 @@ class DummyGateway(object): pass -def prepare_gateway(sshosts, optimise_localhost, - remote_python, pkgdir, real_create=True): - hosts = [] - for host in sshosts: - if host.hostname != 'localhost' or not optimise_localhost: - if real_create: - # for tests we want to use somtehing different - if host.hostname == 'localhost' and optimise_localhost is False: - from py.__.execnet.register import PopenCmdGateway - gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'") - if not host.relpath.startswith("/"): - host.relpath = os.environ['HOME'] + '/' + host.relpath - else: - if remote_python is None: - gw = py.execnet.SshGateway(host.hostname) - else: - gw = py.execnet.SshGateway(host.hostname, - remotepython=remote_python) - else: - gw = DummyGateway() - else: - if remote_python is None: - gw = py.execnet.PopenGateway() - else: - gw = py.execnet.PopenGateway(python=remote_python) - host.relpath = str(pkgdir.dirpath()) - host.gw = gw - gw.host = host - return sshosts - -def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None, - remote_python=None, \ - config=None, optimise_localhost=True,\ - do_sync=True, done_dict=None): - if done_dict is None: - done_dict = {} - assert pkgdir.join("__init__.py").check(), ( +class HostOptions(object): + """ Dummy container for host options, not to keep that + as different function parameters, mostly related to + tests only + """ + def __init__(self, rsync_roots=None, remote_python="python", + optimise_localhost=True, do_sync=True, + create_gateways=True): + self.rsync_roots = rsync_roots + self.remote_python = remote_python + self.optimise_localhost = optimise_localhost + self.do_sync = do_sync + self.create_gateways = create_gateways + +class HostManager(object): + def __init__(self, sshhosts, config, pkgdir, options=HostOptions()): + self.sshhosts = sshhosts + self.pkgdir = pkgdir + self.config = config + self.options = options + if not options.create_gateways: + self.prepare_gateways = self.prepare_dummy_gateways + assert pkgdir.join("__init__.py").check(), ( "%s probably wrong" %(pkgdir,)) - exc_info = [None] - hosts = prepare_gateway(sshhosts, optimise_localhost, - remote_python, pkgdir, real_create=do_sync) - - # rsyncing - rsynced = {} - - if do_sync: - rsync = HostRSync(rsync_roots) - for host in hosts: - #for num, host, gw, remoterootpath in hosts: - remoterootpath = host.relpath - if (host.hostname, remoterootpath) in rsynced or\ - (host.hostname == 'localhost' and optimise_localhost): - reporter(report.HostReady(host)) - continue - rsynced[(host.hostname, host.relpath)] = True - def done(host=host): - reporter(report.HostReady(host)) - reporter(report.HostRSyncing(host)) - if do_sync: - rsync.add_target(host.gw, remoterootpath, done) - if not do_sync: - return # for testing only - rsync.send(pkgdir.dirpath()) - - # hosts ready - return setup_nodes(hosts, pkgdir, config, reporter, done_dict) - -def setup_nodes(hosts, pkgdir, config, reporter, done_dict): - nodes = [] - for host in hosts: - ch = setup_slave(host.gw, os.path.join(host.relpath,\ - pkgdir.basename), config) - nodes.append(MasterNode(ch, reporter, done_dict)) + def prepare_dummy_gateways(self): + for host in self.sshhosts: + gw = DummyGateway() + host.gw = gw + gw.host = host + return self.sshhosts + + def prepare_ssh_gateway(self, host): + if self.options.remote_python is None: + gw = py.execnet.SshGateway(host.hostname) + else: + gw = py.execnet.SshGateway(host.hostname, + remotepython=self.options.remote_python) + return gw + + def prepare_popen_rsync_gateway(self, host): + """ Popen gateway, but with forced rsync + """ + from py.__.execnet.register import PopenCmdGateway + gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'") + if not host.relpath.startswith("/"): + host.relpath = os.environ['HOME'] + '/' + host.relpath + return gw + + def prepare_popen_gateway(self, host): + if self.options.remote_python is None: + gw = py.execnet.PopenGateway() + else: + gw = py.execnet.PopenGateway(python=self.options.remote_python) + host.relpath = str(self.pkgdir.dirpath()) + return gw + + def prepare_gateways(self): + for host in self.sshhosts: + if host.hostname == 'localhost': + if not self.options.optimise_localhost: + gw = self.prepare_popen_rsync_gateway(host) + else: + gw = self.prepare_popen_gateway(host) + else: + gw = self.prepare_ssh_gateway(host) + host.gw = gw + gw.host = host + return self.sshhosts + + def need_rsync(self, rsynced, hostname, remoterootpath): + if (hostname, remoterootpath) in rsynced: + return False + if hostname == 'localhost' and self.options.optimise_localhost: + return False + return True + + def init_hosts(self, reporter, done_dict={}): + if done_dict is None: + done_dict = {} + + hosts = self.prepare_gateways() + + # rsyncing + rsynced = {} + + if self.options.do_sync: + rsync = HostRSync(self.options.rsync_roots) + for host in hosts: + if not self.need_rsync(rsynced, host.hostname, host.relpath): + reporter(report.HostReady(host)) + continue + rsynced[(host.hostname, host.relpath)] = True + def done(host=host): + reporter(report.HostReady(host)) + reporter(report.HostRSyncing(host)) + if self.options.do_sync: + rsync.add_target(host.gw, host.relpath, done) + if not self.options.do_sync: + return # for testing only + rsync.send(self.pkgdir.dirpath()) + # hosts ready + return self.setup_nodes(reporter, done_dict) + + def setup_nodes(self, reporter, done_dict): + nodes = [] + for host in self.sshhosts: + ch = setup_slave(host.gw, os.path.join(host.relpath,\ + self.pkgdir.basename), self.config) + nodes.append(MasterNode(ch, reporter, done_dict)) - return nodes + return nodes -def teardown_hosts(reporter, channels, nodes, waiter=lambda : time.sleep(.1), - exitfirst=False): - for channel in channels: - channel.send(None) + def teardown_hosts(self, reporter, channels, nodes, + waiter=lambda : time.sleep(.1), exitfirst=False): + for channel in channels: + channel.send(None) - clean = exitfirst - while not clean: - clean = True - for node in nodes: - if node.pending: - clean = False - waiter() - teardown_gateways(reporter, channels) - -def kill_channels(channels): - for channel in channels: - channel.send(42) - -def teardown_gateways(reporter, channels): - for channel in channels: - try: - report.wrapcall(reporter, channel.waitclose) - except KeyboardInterrupt, SystemExit: - raise - except: - pass - channel.gateway.exit() + clean = exitfirst + while not clean: + clean = True + for node in nodes: + if node.pending: + clean = False + waiter() + self.teardown_gateways(reporter, channels) + + def kill_channels(self, channels): + for channel in channels: + channel.send(42) + + def teardown_gateways(self, reporter, channels): + for channel in channels: + try: + report.wrapcall(reporter, channel.waitclose) + except KeyboardInterrupt, SystemExit: + raise + except: + pass + channel.gateway.exit() Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Fri Jan 19 17:24:32 2007 @@ -11,8 +11,7 @@ from py.__.test.rsession import report from py.__.test.rsession.master import \ setup_slave, MasterNode, dispatch_loop, itemgen, randomgen -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo,\ - teardown_gateways, kill_channels +from py.__.test.rsession.hostmanage import HostInfo, HostOptions, HostManager from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner @@ -136,22 +135,25 @@ pkgdir = self.getpkgdir(args[0]) done_dict = {} + hostopts = HostOptions(rsync_roots=rsync_roots, + remote_python=remotepython, + optimise_localhost=self.optimise_localhost) + hostmanager = HostManager(sshhosts, self.config, pkgdir, hostopts) try: - nodes = init_hosts(reporter, sshhosts, pkgdir, - rsync_roots, remotepython, config=self.config, - optimise_localhost=self.optimise_localhost, done_dict=done_dict) + nodes = hostmanager.init_hosts(reporter, done_dict) reporter(report.RsyncFinished()) try: self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) except (KeyboardInterrupt, SystemExit): print >>sys.stderr, "C-c pressed waiting for gateways to teardown..." channels = [node.channel for node in nodes] - kill_channels(channels) - teardown_gateways(reporter, channels) + hostmanager.kill_channels(channels) + hostmanager.teardown_gateways(reporter, channels) print >>sys.stderr, "... Done" raise - teardown_hosts(reporter, [node.channel for node in nodes], nodes, + channels = [node.channel for node in nodes] + hostmanager.teardown_hosts(reporter, channels, nodes, exitfirst=self.config.option.exitfirst) reporter(report.Nodes(nodes)) retval = reporter(report.TestFinished()) Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Fri Jan 19 17:24:32 2007 @@ -6,7 +6,7 @@ from py.__.test.rsession import report from py.__.test.rsession.rsession import RSession, parse_directories,\ parse_directories -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\ +from py.__.test.rsession.hostmanage import HostOptions, HostManager,\ HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ @@ -18,7 +18,8 @@ def test_setup_non_existing_hosts(): setup_events = [] hosts = [HostInfo("alskdjalsdkjasldkajlsd")] - cmd = "init_hosts(setup_events.append, hosts, pkgdir)" + hm = HostManager(hosts, None, pkgdir) + cmd = "hm.init_hosts(setup_events.append)" py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) #assert setup_events @@ -159,10 +160,10 @@ teardown_events = [] config = py.test.config._reparse([]) - nodes = init_hosts(setup_events.append, hosts, pkgdir, - rsync_roots=["py"], optimise_localhost=False, - config=config) - teardown_hosts(teardown_events.append, + opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(setup_events.append) + hm.teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) count_rsyn_calls = [i for i in setup_events @@ -186,9 +187,9 @@ allevents = [] config = py.test.config._reparse([]) - nodes = init_hosts(allevents.append, hosts, pkgdir, - rsync_roots=["py"], optimise_localhost=False, - config=config) + opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(allevents.append) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping @@ -206,7 +207,7 @@ node.send(itemskip) node.send(itemprint) - teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) + hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) events = [i for i in allevents if isinstance(i, report.ReceivedItemOutcome)] @@ -233,9 +234,9 @@ from py.__.test.rsession.master import defaultconftestnames defaultconftestnames.append("custom") try: - nodes = init_hosts(allevents.append, hosts, pkgdir, - rsync_roots=["py"], config=config, - optimise_localhost=False) + opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(allevents.append) rootcol = py.test.collect.Directory(pkgdir.dirpath()) itempass = rootcol.getitembynames(funcoptioncustom_spec) @@ -243,7 +244,7 @@ for node in nodes: node.send(itempass) - teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) + hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) events = [i for i in allevents if isinstance(i, report.ReceivedItemOutcome)] passed = [i for i in events @@ -305,7 +306,10 @@ hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] hosts = [HostInfo(i) for i in hostnames] parse_directories(hosts) - init_hosts(testevents.append, hosts, pkgdir, do_sync=False) + config = py.test.config._reparse([]) + opts = HostOptions(do_sync=False, create_gateways=False) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(testevents.append) events = [i for i in testevents if isinstance(i, report.HostRSyncing)] assert len(events) == 4 assert events[0].host.hostname == 'h1' From cfbolz at codespeak.net Fri Jan 19 17:29:35 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 17:29:35 +0100 (CET) Subject: [py-svn] r37016 - py/dist/py/rest Message-ID: <20070119162935.66DF310078@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 17:29:33 2007 New Revision: 37016 Modified: py/dist/py/rest/latex.py Log: another missing quote Modified: py/dist/py/rest/latex.py ============================================================================== --- py/dist/py/rest/latex.py (original) +++ py/dist/py/rest/latex.py Fri Jan 19 17:29:33 2007 @@ -122,7 +122,7 @@ if stylesheet is not None: sty = path.join(stylesheet) if sty.check(): - options.append("--stylesheet=%s" % (sty, )) + options.append('--stylesheet="%s"' % (sty, )) options.append(f.new(basename=tex)) options = map(str, options) if rest_options is not None: From fijal at codespeak.net Fri Jan 19 17:33:18 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 17:33:18 +0100 (CET) Subject: [py-svn] r37018 - py/branch/config/py/test/testing Message-ID: <20070119163318.7F84710078@code0.codespeak.net> Author: fijal Date: Fri Jan 19 17:33:14 2007 New Revision: 37018 Modified: py/branch/config/py/test/testing/test_config.py Log: Little clarification Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Fri Jan 19 17:33:14 2007 @@ -161,8 +161,10 @@ for x in optnames: config = py.test.config._reparse([self.tmpdir, '--%s' % x]) assert config._getsessionname() == 'LSession' + + for x in 'startserver runbrowser rest'.split(): config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x]) - assert config._getsessionname() == 'RSession' # XXX is this right? + assert config._getsessionname() == 'RSession' def test_tkintersession(self): config = py.test.config._reparse([self.tmpdir, '--tkinter']) From hpk at codespeak.net Fri Jan 19 17:34:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 17:34:26 +0100 (CET) Subject: [py-svn] r37019 - py/branch/config/py/test/testing Message-ID: <20070119163426.234B21007A@code0.codespeak.net> Author: hpk Date: Fri Jan 19 17:34:24 2007 New Revision: 37019 Modified: py/branch/config/py/test/testing/test_config.py Log: check that custom session names are correctly looked up Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Fri Jan 19 17:34:24 2007 @@ -176,8 +176,15 @@ cls = config._lookupsession(name) assert cls.__name__ == name - def XXXtest_sessionname_lookup_custom(self): - config = py.test.config._reparse([]) - for name in 'TerminalSession LSession RSession TkinterSession'.split(): - cls = config._lookupsession(name) - assert py.std.inspect.isclass(cls) + def test_sessionname_lookup_custom(self): + self.tmpdir.join("conftest.py").write(py.code.Source(""" + TerminalSession = 0 + TkinterSession = 1 + RSession = 2 + LSession = 3 + """)) + config = py.test.config._reparse([self.tmpdir]) + assert config._lookupsession('TerminalSession') == 0 + assert config._lookupsession('TkinterSession') == 1 + assert config._lookupsession('RSession') == 2 + assert config._lookupsession('LSession') == 3 From hpk at codespeak.net Fri Jan 19 17:48:18 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 17:48:18 +0100 (CET) Subject: [py-svn] r37020 - in py/branch/config/py: apigen/testing test test/testing Message-ID: <20070119164818.55FA61007A@code0.codespeak.net> Author: hpk Date: Fri Jan 19 17:48:15 2007 New Revision: 37020 Modified: py/branch/config/py/apigen/testing/test_apigen_functional.py py/branch/config/py/test/cmdline.py py/branch/config/py/test/config.py py/branch/config/py/test/defaultconftest.py py/branch/config/py/test/testing/test_session.py Log: switching in the new way of getting to a sessionclass, removing code in cmdline.py and elsewhere accordingly Modified: py/branch/config/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/branch/config/py/apigen/testing/test_apigen_functional.py (original) +++ py/branch/config/py/apigen/testing/test_apigen_functional.py Fri Jan 19 17:48:15 2007 @@ -13,7 +13,7 @@ parentdir = py.magic.autopath().dirpath().dirpath() pkgdir = fs_root.join('pkg') output = py.process.cmdexec( - 'APIGEN_TARGET=%s py.test --session=L --apigen=%s/apigen.py %s' % ( + 'APIGEN_TARGET=%s py.test --apigen=%s/apigen.py %s' % ( tempdir, parentdir, pkgdir)) assert output.lower().find('traceback') == -1 Modified: py/branch/config/py/test/cmdline.py ============================================================================== --- py/branch/config/py/test/cmdline.py (original) +++ py/branch/config/py/test/cmdline.py Fri Jan 19 17:48:15 2007 @@ -11,23 +11,6 @@ config = py.test.config config.parse(args) sessionclass = config.getsessionclass() - - # ok, some option checks - if config.option.startserver or config.option.runbrowser: - from py.__.test.rsession.rsession import AbstractSession, LSession - if not issubclass(sessionclass, AbstractSession): - print "Cannot use web server without (R|L)Session, using lsession" - sessionclass = LSession - if config.option.apigen: - from py.__.test.rsession.rsession import AbstractSession, LSession - if not issubclass(sessionclass, AbstractSession): - sessionclass = LSession - print "Cannot generate API without (R|L)Session, using lsession" - if config.option.restreport: - from py.__.test.rsession.rsession import AbstractSession, LSession - if not issubclass(sessionclass, AbstractSession): - sessionclass = LSession - session = sessionclass(config) try: Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Fri Jan 19 17:48:15 2007 @@ -80,11 +80,8 @@ """ return Session class determined from cmdline options and looked up in initial config modules. """ - sessionname = self.option.session + 'Session' - sessionimportpaths = self.conftest.lget('sessionimportpaths') - importpath = sessionimportpaths[sessionname] - mod = __import__(importpath, None, None, ['__doc__']) - return getattr(mod, sessionname) + name = self._getsessionname() + return self._lookupsession(name) def _lookupsession(self, name): """ return session class for the given name. """ @@ -203,16 +200,6 @@ option._remote = remote or option.looponfailing option._fromremote = False - # setting a correct frontend session - if option.session: - name = option.session - elif option.tkinter: - name = 'tkinter' - else: - name = 'terminal' - name = name.capitalize() - option.session = name - if option.runbrowser and not option.startserver: print "Cannot point browser when not starting server" option.startserver = True Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Fri Jan 19 17:48:15 2007 @@ -10,20 +10,17 @@ additionalinfo = None -Option = py.test.config.Option - -sessionimportpaths = { - 'RSession': 'py.__.test.rsession.rsession', - 'LSession': 'py.__.test.rsession.rsession', - 'TerminalSession': 'py.__.test.terminal.terminal', - 'TkinterSession': 'py.__.test.tkinter.reportsession', -} +# =================================================== +# Default Sessions TkinterSession = 'py.__.test.tkinter.reportsession' TerminalSession = 'py.__.test.terminal.terminal' RSession = 'py.__.test.rsession.rsession' LSession = 'py.__.test.rsession.rsession' +# =================================================== +# Distributed testing specific options + #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 @@ -36,6 +33,9 @@ dist_nicelevel = 0 _dist_import_pypy = False # used for regenerating JS application +# =================================================== + +Option = py.test.config.Option def adddefaultoptions(): py.test.config.addoptions('general options', Option('-v', '--verbose', @@ -79,7 +79,7 @@ "argument pointing to a script)."), ) - py.test.config.addoptions('test-session related options', + py.test.config.addoptions('options implying subprocesses (warning: beta)', Option('', '--tkinter', action="store_true", dest="tkinter", default=False, help="use tkinter test session frontend."), @@ -89,9 +89,6 @@ Option('', '--looponfailing', action="store_true", dest="looponfailing", default=False, help="loop on failing test set."), - Option('', '--session', - action="store", dest="session", default=None, - help="use given sessionclass, default is terminal."), Option('', '--exec', action="store", dest="executable", default=None, help="python executable to run the tests with."), Modified: py/branch/config/py/test/testing/test_session.py ============================================================================== --- py/branch/config/py/test/testing/test_session.py (original) +++ py/branch/config/py/test/testing/test_session.py Fri Jan 19 17:48:15 2007 @@ -35,16 +35,6 @@ l = session.getitemoutcomepairs(py.test.Item.Passed) assert not l - def test_session_parsing(self): - from py.__.test.terminal.terminal import TerminalSession - from py.__.test.tkinter.reportsession import ReportSession - config = py.test.config._reparse(['--session=terminal']) - assert issubclass(config.getsessionclass(), TerminalSession) - config = py.test.config._reparse(['--session=tkinter']) - assert issubclass(config.getsessionclass(), ReportSession) - config = py.test.config._reparse(['--tkinter']) - assert issubclass(config.getsessionclass(), ReportSession) - class TestKeywordSelection: def test_select_simple(self): for keyword in ['test_one', 'est_on']: From cfbolz at codespeak.net Fri Jan 19 17:48:43 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 17:48:43 +0100 (CET) Subject: [py-svn] r37021 - py/dist/py/path/svn Message-ID: <20070119164843.950D21007D@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 17:48:41 2007 New Revision: 37021 Modified: py/dist/py/path/svn/wccommand.py Log: my svn client has yet another error message Modified: py/dist/py/path/svn/wccommand.py ============================================================================== --- py/dist/py/path/svn/wccommand.py (original) +++ py/dist/py/path/svn/wccommand.py Fri Jan 19 17:48:41 2007 @@ -94,7 +94,8 @@ if strerr.find('file not found') != -1: raise py.error.ENOENT(self) if (strerr.find('file exists') != -1 or - strerr.find('file already exists') != -1): + strerr.find('file already exists') != -1 or + strerr.find("can't create directory") != -1): raise py.error.EEXIST(self) raise return out From hpk at codespeak.net Fri Jan 19 17:49:47 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 17:49:47 +0100 (CET) Subject: [py-svn] r37022 - py/branch/config/py/test/testing Message-ID: <20070119164947.0A79F1007D@code0.codespeak.net> Author: hpk Date: Fri Jan 19 17:49:44 2007 New Revision: 37022 Modified: py/branch/config/py/test/testing/test_config.py Log: this test is not really needed because all single bits are tested Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Fri Jan 19 17:49:44 2007 @@ -134,20 +134,6 @@ def setup_method(self, method): self.tmpdir = self.tmproot.mkdir(method.__name__) - def XXXtest_sessionclass_conftest_override(self): - self.tmpdir.join("conftest.py").write(py.code.Source(""" - from py.__.test.session import Session as orig - class Session(orig): - x = 42 - """)) - config = py.test.config._reparse([self.tmpdir]) - cls = config._getsessionclass() - assert cls.x == 42 - config.option.dist = True - config.option.startserver = True - cls = config._getsessionclass() - assert cls.x == 42 - def test_sessionname_default(self): config = py.test.config._reparse([self.tmpdir]) assert config._getsessionname() == 'TerminalSession' From arigo at codespeak.net Fri Jan 19 17:59:50 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Fri, 19 Jan 2007 17:59:50 +0100 (CET) Subject: [py-svn] r37024 - py/dist/py/test Message-ID: <20070119165950.B01CF10072@code0.codespeak.net> Author: arigo Date: Fri Jan 19 17:59:48 2007 New Revision: 37024 Modified: py/dist/py/test/config.py Log: This should be a right-to-left, overridable lookup. Modified: py/dist/py/test/config.py ============================================================================== --- py/dist/py/test/config.py (original) +++ py/dist/py/test/config.py Fri Jan 19 17:59:48 2007 @@ -78,7 +78,7 @@ """ sessionname = self.option.session + 'Session' try: - return self.conftest.lget(sessionname) + return self.conftest.rget(sessionname) except KeyError: pass sessionimportpaths = self.conftest.lget('sessionimportpaths') From hpk at codespeak.net Fri Jan 19 17:59:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 19 Jan 2007 17:59:52 +0100 (CET) Subject: [py-svn] r37025 - in py/branch/config/py/test: . testing Message-ID: <20070119165952.19A0B10072@code0.codespeak.net> Author: hpk Date: Fri Jan 19 17:59:50 2007 New Revision: 37025 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/conftesthandle.py py/branch/config/py/test/testing/test_conftesthandle.py Log: remove lget and its last usage (which was a bit random) Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Fri Jan 19 17:59:50 2007 @@ -43,7 +43,7 @@ "can only parse cmdline args once per Config object") self._parsed = True self.conftest.setinitial(args) - self.conftest.lget('adddefaultoptions')() + self.conftest.rget('adddefaultoptions')() args = [str(x) for x in args] self._origargs = args cmdlineoption, remaining = self._parser.parse_args(args) Modified: py/branch/config/py/test/conftesthandle.py ============================================================================== --- py/branch/config/py/test/conftesthandle.py (original) +++ py/branch/config/py/test/conftesthandle.py Fri Jan 19 17:59:50 2007 @@ -51,9 +51,10 @@ # affect us by always returning a copy of the actual list return clist[:] - def lget(self, name, path=None): - modules = self.getconftestmodules(path) - return self._get(name, modules) + # XXX no real use case, may probably go + #def lget(self, name, path=None): + # modules = self.getconftestmodules(path) + # return self._get(name, modules) def rget(self, name, path=None): modules = self.getconftestmodules(path) Modified: py/branch/config/py/test/testing/test_conftesthandle.py ============================================================================== --- py/branch/config/py/test/testing/test_conftesthandle.py (original) +++ py/branch/config/py/test/testing/test_conftesthandle.py Fri Jan 19 17:59:50 2007 @@ -25,40 +25,40 @@ def test_default_Module_setting_is_visible_always(self): for path in self.basedir.parts(): conftest = Conftest(path) - assert conftest.lget("Module") == py.test.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.test.collect.Directory + #assert conftest.lget('Directory') == py.test.collect.Directory def test_value_access_not_existing(self): conftest = Conftest(self.basedir) py.test.raises(KeyError, "conftest.rget('a')") - py.test.raises(KeyError, "conftest.lget('a')") + #py.test.raises(KeyError, "conftest.lget('a')") def test_value_access_by_path(self): conftest = Conftest(self.basedir) assert conftest.rget("a", self.basedir.join('adir')) == 1 - assert conftest.lget("a", self.basedir.join('adir')) == 1 + #assert conftest.lget("a", self.basedir.join('adir')) == 1 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.test.raises(KeyError, - 'conftest.lget("b", self.basedir.join("a"))' - ) + #assert conftest.lget("a", self.basedir.join('adir', 'b')) == 1 + #assert conftest.lget("b", self.basedir.join('adir', 'b')) == 2 + #assert py.test.raises(KeyError, + # 'conftest.lget("b", self.basedir.join("a"))' + #) def test_value_access_with_init_one_conftest(self): conftest = Conftest(self.basedir.join('adir')) assert conftest.rget("a") == 1 - assert conftest.lget("a") == 1 + #assert conftest.lget("a") == 1 def test_value_access_with_init_two_conftests(self): conftest = Conftest(self.basedir.join("adir", "b")) conftest.rget("a") == 1.5 - conftest.lget("a") == 1 - conftest.lget("b") == 1 + #conftest.lget("a") == 1 + #conftest.lget("b") == 1 class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): def setup_class(cls): From fijal at codespeak.net Fri Jan 19 18:32:35 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 18:32:35 +0100 (CET) Subject: [py-svn] r37027 - py/branch/config/py/misc Message-ID: <20070119173235.5460010077@code0.codespeak.net> Author: fijal Date: Fri Jan 19 18:32:33 2007 New Revision: 37027 Modified: py/branch/config/py/misc/terminal_helper.py Log: Add a bit more complicated terminal width guesser (any clue how to test it???) Modified: py/branch/config/py/misc/terminal_helper.py ============================================================================== --- py/branch/config/py/misc/terminal_helper.py (original) +++ py/branch/config/py/misc/terminal_helper.py Fri Jan 19 18:32:33 2007 @@ -1,6 +1,19 @@ import sys, os -terminal_width = int(os.environ.get('COLUMNS', 80))-1 +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: From cfbolz at codespeak.net Fri Jan 19 18:33:00 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 18:33:00 +0100 (CET) Subject: [py-svn] r37028 - py/dist/py/c-extension/greenlet Message-ID: <20070119173300.8B5C810078@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 18:32:58 2007 New Revision: 37028 Modified: py/dist/py/c-extension/greenlet/greenlet.c Log: fix module names Modified: py/dist/py/c-extension/greenlet/greenlet.c ============================================================================== --- py/dist/py/c-extension/greenlet/greenlet.c (original) +++ py/dist/py/c-extension/greenlet/greenlet.c Fri Jan 19 18:32:58 2007 @@ -908,7 +908,7 @@ return; if (PyType_Ready(&PyGreen_Type) < 0) return; - PyExc_GreenletError = PyErr_NewException("greenlet.error", NULL, NULL); + PyExc_GreenletError = PyErr_NewException("py.magic.greenlet.error", NULL, NULL); if (PyExc_GreenletError == NULL) return; greenleterror_doc = PyString_FromString("internal greenlet error"); @@ -919,7 +919,7 @@ Py_DECREF(greenleterror_doc); if (error == -1) return; - PyExc_GreenletExit = PyErr_NewException("greenlet.GreenletExit", + PyExc_GreenletExit = PyErr_NewException("py.magic.greenlet.GreenletExit", NULL, NULL); if (PyExc_GreenletExit == NULL) return; From fijal at codespeak.net Fri Jan 19 18:33:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 18:33:28 +0100 (CET) Subject: [py-svn] r37029 - py/branch/config/py/test/terminal Message-ID: <20070119173328.EFE091007E@code0.codespeak.net> Author: fijal Date: Fri Jan 19 18:33:27 2007 New Revision: 37029 Modified: py/branch/config/py/test/terminal/out.py Log: This should work out now out-of-the-box, but must be ported to new interface, to allow dynamic adaption to terminal size changes. Modified: py/branch/config/py/test/terminal/out.py ============================================================================== --- py/branch/config/py/test/terminal/out.py (original) +++ py/branch/config/py/test/terminal/out.py Fri Jan 19 18:33:27 2007 @@ -35,17 +35,8 @@ class TerminalOut(Out): tty = True - def __init__(self, file): - super(TerminalOut, self).__init__(file) - try: - import termios,fcntl,struct - call = fcntl.ioctl(0,termios.TIOCGWINSZ,"\000"*8) - height,width = struct.unpack( "hhhh", call ) [:2] - self.fullwidth = width - except (SystemExit, KeyboardInterrupt), e: - raise - except: - pass + #def __init__(self, file): + # super(TerminalOut, self).__init__(file) def write(self, s): self.file.write(str(s)) From fijal at codespeak.net Fri Jan 19 18:43:12 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 18:43:12 +0100 (CET) Subject: [py-svn] r37030 - in py/branch/config/py/test/rsession: . testing Message-ID: <20070119174312.231E210077@code0.codespeak.net> Author: fijal Date: Fri Jan 19 18:42:52 2007 New Revision: 37030 Modified: py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/rsession/testing/test_reporter.py Log: Improve a bit rsession command line interface. I'm not sure if colors shouldn't come as an option. Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Fri Jan 19 18:42:52 2007 @@ -10,7 +10,7 @@ from py.__.test.terminal.out import getout from py.__.test.rsession import report from py.__.test.rsession import outcome -from py.__.misc.terminal_helper import ansi_print, terminal_width +from py.__.misc.terminal_helper import ansi_print, get_terminal_width import sys @@ -227,22 +227,31 @@ def report_ReceivedItemOutcome(self, event): host = event.host - itempath = " ".join(event.item.listnames()[1:]) if event.outcome.passed: - status = "PASSED " self.passed[host] += 1 - print "%10s: %s %s" %(host.hostname[:10], status, itempath) + sys.stdout.write("%10s: PASSED " % host.hostname[:10]) elif event.outcome.skipped: - status = "SKIPPED" self.skipped_tests_outcome.append(event) self.skipped[host] += 1 - print "%10s: %s %s" %(host.hostname[:10], status, itempath) + sys.stdout.write("%10s: SKIPPED " % host.hostname[:10]) else: self.failed[host] += 1 self.failed_tests_outcome.append(event) sys.stdout.write("%10s: " % host.hostname[:10]) ansi_print("FAILED", esc=(31,1), newline=False, file=sys.stdout) - print " " + itempath + 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 Modified: py/branch/config/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_reporter.py (original) +++ py/branch/config/py/test/rsession/testing/test_reporter.py Fri Jan 19 18:42:52 2007 @@ -166,11 +166,12 @@ r.report(report.HostReady(hosts[2])) out, err = cap.reset() assert not err - expected = """================= Test started, hosts: host1, host2, host3 ================== - host1: READY (still 2 to go) + expected1 = "Test started, hosts: host1, host2, host3" + expected2 = """host1: READY (still 2 to go) host2: READY (still 1 to go) host3: READY""" - assert out.find(expected) != -1 + assert out.find(expected1) != -1 + assert out.find(expected2) != -1 class TestLocalReporter(AbstractTestReporter): reporter = LocalReporter @@ -203,20 +204,20 @@ def test_report_received_item_outcome(self): #py.test.skip("XXX rewrite test to not rely on exact formatting") val = self.report_received_item_outcome() - expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass - localhost: SKIPPED py test rsession testing test_slave.py funcpass - localhost: FAILED py test rsession testing test_slave.py funcpass - localhost: PASSED py test rsession testing test_slave.py funcpass + expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass + localhost: SKIPPED py.test.rsession.testing.test_slave.py funcpass + localhost: FAILED py.test.rsession.testing.test_slave.py funcpass + localhost: PASSED py.test.rsession.testing.test_slave.py funcpass """ assert val.find(expected) != -1 def test_module(self): val = self._test_module() print val - expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass - localhost: SKIPPED py test rsession testing test_slave.py funcpass - localhost: FAILED py test rsession testing test_slave.py funcpass - localhost: PASSED py test rsession testing test_slave.py funcpass + expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass + localhost: SKIPPED py.test.rsession.testing.test_slave.py funcpass + localhost: FAILED py.test.rsession.testing.test_slave.py funcpass + localhost: PASSED py.test.rsession.testing.test_slave.py funcpass """ assert val.find(expected) != -1 From fijal at codespeak.net Fri Jan 19 18:47:14 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 18:47:14 +0100 (CET) Subject: [py-svn] r37031 - py/branch/config/py/test/terminal Message-ID: <20070119174714.E280C10077@code0.codespeak.net> Author: fijal Date: Fri Jan 19 18:47:12 2007 New Revision: 37031 Modified: py/branch/config/py/test/terminal/out.py Log: Don't change semantics if we don't have to. (this is a quick hack, I'll tackle this today's later) Modified: py/branch/config/py/test/terminal/out.py ============================================================================== --- py/branch/config/py/test/terminal/out.py (original) +++ py/branch/config/py/test/terminal/out.py Fri Jan 19 18:47:12 2007 @@ -35,8 +35,9 @@ class TerminalOut(Out): tty = True - #def __init__(self, file): - # super(TerminalOut, self).__init__(file) + def __init__(self, file): + super(TerminalOut, self).__init__(file) + self.fullwidth = terminal_helper.get_terminal_width() def write(self, s): self.file.write(str(s)) From fijal at codespeak.net Fri Jan 19 18:50:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 18:50:28 +0100 (CET) Subject: [py-svn] r37032 - py/branch/config/py/test/terminal Message-ID: <20070119175028.98F201007D@code0.codespeak.net> Author: fijal Date: Fri Jan 19 18:50:17 2007 New Revision: 37032 Modified: py/branch/config/py/test/terminal/out.py Log: Change the behaviour to always call get_terminal_width, this should make TerminalSession adapt to terminal size changes. Modified: py/branch/config/py/test/terminal/out.py ============================================================================== --- py/branch/config/py/test/terminal/out.py (original) +++ py/branch/config/py/test/terminal/out.py Fri Jan 19 18:50:17 2007 @@ -10,8 +10,9 @@ def __init__(self, file): self.file = py.io.dupfile(file) - def sep(self, sepchar, title=None): - fullwidth = self.fullwidth + 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: @@ -37,7 +38,10 @@ tty = True def __init__(self, file): super(TerminalOut, self).__init__(file) - self.fullwidth = terminal_helper.get_terminal_width() + + def sep(self, sepchar, title=None): + super(TerminalOut, self).sep(sepchar, title, + terminal_helper.get_terminal_width()) def write(self, s): self.file.write(str(s)) From cfbolz at codespeak.net Fri Jan 19 18:55:49 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 18:55:49 +0100 (CET) Subject: [py-svn] r37033 - py/dist/py/test/rsession/testing Message-ID: <20070119175549.235141007D@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 18:55:47 2007 New Revision: 37033 Modified: py/dist/py/test/rsession/testing/test_webjs.py Log: do all the imports there, to skip if e.g. pypy is found, but not ctypes Modified: py/dist/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/dist/py/test/rsession/testing/test_webjs.py (original) +++ py/dist/py/test/rsession/testing/test_webjs.py Fri Jan 19 18:55:47 2007 @@ -1,10 +1,17 @@ import py try: import pypy + from pypy.translator.js.modules import dom + from py.__.test.rsession.rsession import session_options + from py.__.test.rsession.rsession import session_options + from py.__.test.rsession import webjs + from py.__.test.rsession.web import exported_methods + from pypy.translator.js.tester import schedule_callbacks except ImportError: py.test.skip('PyPy not found') -from pypy.translator.js.tester import schedule_callbacks + from py.__.test.rsession import webjs + from py.__.test.rsession.web import exported_methods here = py.magic.autopath().dirpath() def setup_module(mod): From fijal at codespeak.net Fri Jan 19 18:58:15 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 19 Jan 2007 18:58:15 +0100 (CET) Subject: [py-svn] r37034 - py/branch/config/py/misc/testing Message-ID: <20070119175815.E9CFC1007E@code0.codespeak.net> Author: fijal Date: Fri Jan 19 18:58:13 2007 New Revision: 37034 Added: py/branch/config/py/misc/testing/test_terminal.py Log: Rather stupid test for get_terminal_width Added: py/branch/config/py/misc/testing/test_terminal.py ============================================================================== --- (empty file) +++ py/branch/config/py/misc/testing/test_terminal.py Fri Jan 19 18:58:13 2007 @@ -0,0 +1,21 @@ + +import os +from py.__.misc.terminal_helper import get_terminal_width + +def test_terminal_width(): + """ Dummy test for get_terminal_width + """ + assert get_terminal_width() + try: + def f(*args): + raise ValueError + import fcntl + ioctl = fcntl.ioctl + fcntl.ioctl = f + cols = os.environ.get('COLUMNS', None) + os.environ['COLUMNS'] = '42' + assert get_terminal_width() == 41 + finally: + fcntl.ioctl = ioctl + if cols: + os.environ['COLUMNS'] = cols From cfbolz at codespeak.net Fri Jan 19 19:08:21 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 19:08:21 +0100 (CET) Subject: [py-svn] r37035 - py/dist/py/apigen/source/testing Message-ID: <20070119180821.9133010082@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 19:08:19 2007 New Revision: 37035 Modified: py/dist/py/apigen/source/testing/test_html.py Log: no genexps in 2.3 Modified: py/dist/py/apigen/source/testing/test_html.py ============================================================================== --- py/dist/py/apigen/source/testing/test_html.py (original) +++ py/dist/py/apigen/source/testing/test_html.py Fri Jan 19 19:08:19 2007 @@ -77,7 +77,7 @@ if doc is None: doc = HTMLDocument() l = doc.prepare_line(line) - return ''.join(unicode(i) for i in l) + return ''.join([unicode(i) for i in l]) def test_prepare_line_basic(self): result = self.prepare_line(['see if this works']) From cfbolz at codespeak.net Fri Jan 19 22:53:47 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 22:53:47 +0100 (CET) Subject: [py-svn] r37037 - py/dist/py/apigen/tracer Message-ID: <20070119215347.367BD10071@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 22:53:45 2007 New Revision: 37037 Modified: py/dist/py/apigen/tracer/permastore.py Log: func_name is not writeable before 2.4 Modified: py/dist/py/apigen/tracer/permastore.py ============================================================================== --- py/dist/py/apigen/tracer/permastore.py (original) +++ py/dist/py/apigen/tracer/permastore.py Fri Jan 19 22:53:45 2007 @@ -1,3 +1,4 @@ +import py class DescPlaceholder(object): pass @@ -96,8 +97,10 @@ # pass for field in PermaDocStorage.function_fields: - def f(self, name, field=field): + d = {"field": field} + func_name = "get_function_%s" % (field, ) + exec py.code.Source(""" + def %s(self, name, field=field): return getattr(self.names[name], field) - func_name = 'get_function_%s' % field - f.func_name = func_name - setattr(PermaDocStorage, func_name, f) +""" % (func_name, )).compile() in d + setattr(PermaDocStorage, func_name, d[func_name]) From cfbolz at codespeak.net Fri Jan 19 22:54:50 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 22:54:50 +0100 (CET) Subject: [py-svn] r37038 - py/dist/py/rest Message-ID: <20070119215450.43D2C10071@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 22:54:49 2007 New Revision: 37038 Modified: py/dist/py/rest/latex.py Log: move docutils import into function where it is needed Modified: py/dist/py/rest/latex.py ============================================================================== --- py/dist/py/rest/latex.py (original) +++ py/dist/py/rest/latex.py Fri Jan 19 22:54:49 2007 @@ -1,6 +1,5 @@ import py -from docutils.core import publish_cmdline from py.__.process.cmdexec import ExecutionFailed font_to_package = {"times": "times", "helvetica": "times", @@ -107,6 +106,7 @@ old.chdir() def process_rest_file(restfile, stylesheet=None, debug=False, rest_options=None): + from docutils.core import publish_cmdline if not py.path.local.sysfind("pdflatex"): raise SystemExit("ERROR: pdflatex not found") old = py.path.local() From cfbolz at codespeak.net Fri Jan 19 22:56:20 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 22:56:20 +0100 (CET) Subject: [py-svn] r37039 - py/dist/py/rest/testing Message-ID: <20070119215620.AD7EB10071@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 22:56:19 2007 New Revision: 37039 Modified: py/dist/py/rest/testing/test_rst2pdf.py Log: directly check for the docutils Modified: py/dist/py/rest/testing/test_rst2pdf.py ============================================================================== --- py/dist/py/rest/testing/test_rst2pdf.py (original) +++ py/dist/py/rest/testing/test_rst2pdf.py Fri Jan 19 22:56:19 2007 @@ -1,8 +1,9 @@ from __future__ import generators import py +from py.__.rest.latex import process_configfile, process_rest_file try: - from py.__.rest.latex import process_configfile, process_rest_file + import docutils except ImportError: py.test.skip("docutils not present") From cfbolz at codespeak.net Fri Jan 19 22:59:48 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 22:59:48 +0100 (CET) Subject: [py-svn] r37040 - py/dist/py/test/rsession/testing Message-ID: <20070119215948.7FFE010072@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 22:59:47 2007 New Revision: 37040 Modified: py/dist/py/test/rsession/testing/test_rsession.py Log: 2.3 compatibility Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Fri Jan 19 22:59:47 2007 @@ -283,7 +283,8 @@ def test_sophisticated_parse(self): sshhosts = ['a at h1:/tmp', 'h2:tmp', 'h3'] dirs = parse_directories(sshhosts) - assert sorted(dirs.values()) == ['/tmp', 'pytestcache', 'tmp'] + assert py.magic.builtin.sorted( + dirs.values()) == ['/tmp', 'pytestcache', 'tmp'] def test_parse_multiple_hosts(self): hosts = ['h1', 'h1', 'h1:/tmp'] From cfbolz at codespeak.net Fri Jan 19 23:30:01 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 23:30:01 +0100 (CET) Subject: [py-svn] r37041 - py/dist/py/doc Message-ID: <20070119223001.13DF610070@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 23:30:00 2007 New Revision: 37041 Modified: py/dist/py/doc/TODO.txt Log: bin scripts are documented now. I also sorted the issues a bit Modified: py/dist/py/doc/TODO.txt ============================================================================== --- py/dist/py/doc/TODO.txt (original) +++ py/dist/py/doc/TODO.txt Fri Jan 19 23:30:00 2007 @@ -6,7 +6,7 @@ py/bin ----------- -* document and review py/bin scripts abit +* review py/bin scripts abit py.test py.rest py.lookup @@ -109,6 +109,7 @@ * no lines longer than 80 characters * review the pylib issue tracker + (cfbolz: done: what has a 0.8.0 or a 0.9.0 tag should be looked at again) py.test From cfbolz at codespeak.net Fri Jan 19 23:30:44 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Fri, 19 Jan 2007 23:30:44 +0100 (CET) Subject: [py-svn] r37042 - in py/dist/py/apigen: . rest rest/testing source testing tracer tracer/testing Message-ID: <20070119223044.32B0510079@code0.codespeak.net> Author: cfbolz Date: Fri Jan 19 23:30:40 2007 New Revision: 37042 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/rest/genrest.py py/dist/py/apigen/rest/testing/test_rest.py py/dist/py/apigen/source/browser.py py/dist/py/apigen/testing/test_htmlgen.py py/dist/py/apigen/tracer/description.py py/dist/py/apigen/tracer/docstorage.py py/dist/py/apigen/tracer/model.py py/dist/py/apigen/tracer/testing/test_docgen.py py/dist/py/apigen/tracer/testing/test_model.py py/dist/py/apigen/tracer/testing/test_package.py Log: clear apigen of 2.4isms. works now on 2.3! Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Fri Jan 19 23:30:40 2007 @@ -4,10 +4,13 @@ from py.__.apigen.source import browser as source_browser from py.__.apigen.source import html as source_html from py.__.apigen.tracer.description import is_private +from py.__.apigen.rest.genrest import split_of_last_part +sorted = py.builtin.sorted html = py.xml.html raw = py.xml.raw + class H(html): class Description(html.div): style = html.Style(margin_left='10em') @@ -201,7 +204,7 @@ indent += 1 # build sub items of dotted_name item - for item_dotted_name in sorted(item_dotted_names): + for item_dotted_name in py.builtin.sorted(item_dotted_names): itemname = item_dotted_name.split('.')[-1] if is_private(itemname): continue @@ -357,7 +360,7 @@ passed = [] methodsdata = [] for dotted_name in classes_dotted_names: - parent_dotted_name = dotted_name.rsplit('.', 1)[0] + parent_dotted_name, _ = split_of_last_part(dotted_name) try: sibling_dotted_names = namespace_tree[parent_dotted_name] except KeyError: @@ -391,8 +394,8 @@ # pointing to them anymore... passed = [] for dotted_name in method_dotted_names: - parent_dotted_name = dotted_name.rsplit('.', 1)[0] - module_dotted_name = dotted_name.rsplit('.', 2)[0] + parent_dotted_name, _ = split_of_last_part(dotted_name) + module_dotted_name, _ = split_of_last_part(parent_dotted_name) sibling_dotted_names = namespace_tree[module_dotted_name] tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, @@ -416,7 +419,7 @@ passed = [] for dotted_name in method_dotted_names: # XXX should we create a build_full_function_view instead? - parent_dotted_name = dotted_name.rsplit('.', 1)[0] + parent_dotted_name, _ = split_of_last_part(dotted_name) sibling_dotted_names = namespace_tree[parent_dotted_name] tag = build_full_method_view(base, linker, dsa, dotted_name) nav = build_api_navigation(linker, dsa, parent_dotted_name, Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Fri Jan 19 23:30:40 2007 @@ -12,6 +12,10 @@ from py.__.apigen.tracer import model from py.__.rest.transform import RestTransformer +def split_of_last_part(name): + name = name.split(".") + return ".".join(name[:-1]), name[-1] + class AbstractLinkWriter(object): """ Class implementing writing links to source code. There should exist various classes for that, different for Trac, @@ -324,7 +328,7 @@ ret = [] for name in self.dsa.get_class_names(): if '.' in name: - module, classname = name.rsplit('.', 1) + module, classname = split_of_last_part(name) if module in visited: continue visited.append(module) @@ -337,7 +341,7 @@ for name in self.dsa.get_class_names(): classname = name if '.' in name: - classmodule, classname = name.rsplit('.', 1) + classmodule, classname = split_of_last_part(name) if classmodule != module: continue elif module != '': @@ -351,7 +355,7 @@ for name in self.dsa.get_function_names(): funcname = name if '.' in name: - funcpath, funcname = name.rsplit('.', 1) + funcpath, funcname = split_of_last_part(name) if funcpath != module: continue elif module != '': Modified: py/dist/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/dist/py/apigen/rest/testing/test_rest.py (original) +++ py/dist/py/apigen/rest/testing/test_rest.py Fri Jan 19 23:30:40 2007 @@ -20,6 +20,8 @@ from py.__.rest.transform import HTMLHandler # XXX: UUuuuuuuuuuuuuuuuuuuuuuuu, dangerous import +sorted = py.builtin.sorted + def _nl(s): """normalize newlines (converting to \n)""" s = s.replace('\r\n', '\n') Modified: py/dist/py/apigen/source/browser.py ============================================================================== --- py/dist/py/apigen/source/browser.py (original) +++ py/dist/py/apigen/source/browser.py Fri Jan 19 23:30:40 2007 @@ -39,7 +39,7 @@ return all def get_endline(start, lst): - l = reversed(lst) + l = lst[::-1] for i in l: if i.lineno: return i.lineno Modified: py/dist/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/dist/py/apigen/testing/test_htmlgen.py (original) +++ py/dist/py/apigen/testing/test_htmlgen.py Fri Jan 19 23:30:40 2007 @@ -31,8 +31,8 @@ temp.join('dir/sub').ensure(dir=True) temp.join('dir/.hidden_dir').ensure(dir=True) dirs, files = htmlgen.source_dirs_files(temp.join('dir')) - dirnames = sorted([d.basename for d in dirs]) - filenames = sorted([f.basename for f in files]) + dirnames = py.builtin.sorted([d.basename for d in dirs]) + filenames = py.builtin.sorted([f.basename for f in files]) assert dirnames == ['sub'] assert filenames == ['file1.py', 'file3.c'] Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Fri Jan 19 23:30:40 2007 @@ -8,6 +8,12 @@ MAX_CALL_SITES = 20 +try: + set +except NameError: + from sets import Set as set + + def is_private(name): return name.startswith('_') and not name.startswith('__') Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Fri Jan 19 23:30:40 2007 @@ -12,6 +12,8 @@ from py.__.apigen.tracer import model +sorted = py.builtin.sorted + class DocStorage(object): """ Class storing info about API """ Modified: py/dist/py/apigen/tracer/model.py ============================================================================== --- py/dist/py/apigen/tracer/model.py (original) +++ py/dist/py/apigen/tracer/model.py Fri Jan 19 23:30:40 2007 @@ -7,6 +7,12 @@ import types +try: + set +except NameError: + from sets import Set as set + + # __extend__ and pairtype? class SomeObject(object): typedef = types.ObjectType Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Fri Jan 19 23:30:40 2007 @@ -21,6 +21,12 @@ # sys.path.insert(0, str(data_path)) # XXX: Perma doc storage disabled a bit +try: + set +except NameError: + from sets import Set as set + +sorted = py.builtin.sorted def fun(a, b, c): "Some docstring" Modified: py/dist/py/apigen/tracer/testing/test_model.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_model.py (original) +++ py/dist/py/apigen/tracer/testing/test_model.py Fri Jan 19 23:30:40 2007 @@ -105,7 +105,7 @@ pass g = guess_type(A).unionof(guess_type(A())) - l = sorted(list(g.striter())) + l = py.builtin.sorted(list(g.striter())) assert l[4] == "AnyOf(" assert isinstance(l[0], SomeClass) assert l[3] == ", " Modified: py/dist/py/apigen/tracer/testing/test_package.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_package.py (original) +++ py/dist/py/apigen/tracer/testing/test_package.py Fri Jan 19 23:30:40 2007 @@ -24,8 +24,9 @@ def test_init(self): ds = self.ds assert len(ds.descs) == 4 - assert sorted(ds.descs.keys()) == ["notpak.notmod.notclass", "notpak.notmod.notclass.__init__", \ - "pak.mod.one", "pak.mod.two"] + assert py.builtin.sorted(ds.descs.keys()) == [ + "notpak.notmod.notclass", "notpak.notmod.notclass.__init__", + "pak.mod.one", "pak.mod.two"] def test_simple_call(self): ds = self.ds From cfbolz at codespeak.net Sat Jan 20 00:23:16 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sat, 20 Jan 2007 00:23:16 +0100 (CET) Subject: [py-svn] r37043 - py/dist/py/c-extension/greenlet Message-ID: <20070119232316.2C30310077@code0.codespeak.net> Author: cfbolz Date: Sat Jan 20 00:23:15 2007 New Revision: 37043 Modified: py/dist/py/c-extension/greenlet/greenlet.c Log: try to make greenlets work with 2.5 (new style exceptions). armin, could you take a look? Modified: py/dist/py/c-extension/greenlet/greenlet.c ============================================================================== --- py/dist/py/c-extension/greenlet/greenlet.c (original) +++ py/dist/py/c-extension/greenlet/greenlet.c Sat Jan 20 00:23:15 2007 @@ -894,7 +894,9 @@ { PyObject* m; PyObject* greenletexit_doc; + PyObject* greenletexit_dict; PyObject* greenleterror_doc; + PyObject* greenleterror_dict; int error; char** p; _PyGreen_switchstack = g_switchstack; @@ -908,30 +910,50 @@ return; if (PyType_Ready(&PyGreen_Type) < 0) return; - PyExc_GreenletError = PyErr_NewException("py.magic.greenlet.error", NULL, NULL); - if (PyExc_GreenletError == NULL) + + greenleterror_dict = PyDict_New(); + if (greenleterror_dict == NULL) return; greenleterror_doc = PyString_FromString("internal greenlet error"); - if (greenleterror_doc == NULL) + if (greenleterror_doc == NULL) { + Py_DECREF(greenleterror_dict); return; - error = PyObject_SetAttrString( - PyExc_GreenletError, "__doc__", greenleterror_doc); + } + + error = PyDict_SetItemString(greenleterror_dict, "__doc__", greenleterror_doc); Py_DECREF(greenleterror_doc); - if (error == -1) + if (error == -1) { + Py_DECREF(greenleterror_dict); return; - PyExc_GreenletExit = PyErr_NewException("py.magic.greenlet.GreenletExit", - NULL, NULL); - if (PyExc_GreenletExit == NULL) + } + + PyExc_GreenletError = PyErr_NewException("py.magic.greenlet.error", NULL, greenleterror_dict); + Py_DECREF(greenleterror_dict); + if (PyExc_GreenletError == NULL) + return; + + greenletexit_dict = PyDict_New(); + if (greenletexit_dict == NULL) return; greenletexit_doc = PyString_FromString("greenlet.GreenletExit\n\ This special exception does not propagate to the parent greenlet; it\n\ can be used to kill a single greenlet.\n"); - if (greenletexit_doc == NULL) + if (greenletexit_doc == NULL) { + Py_DECREF(greenletexit_dict); return; - error = PyObject_SetAttrString( - PyExc_GreenletExit, "__doc__", greenletexit_doc); + } + + error = PyDict_SetItemString(greenletexit_dict, "__doc__", greenletexit_doc); Py_DECREF(greenletexit_doc); - if (error == -1) + if (error == -1) { + Py_DECREF(greenletexit_dict); + return; + } + + PyExc_GreenletExit = PyErr_NewException("py.magic.greenlet.GreenletExit", + NULL, greenletexit_dict); + Py_DECREF(greenletexit_dict); + if (PyExc_GreenletExit == NULL) return; ts_current = green_create_main(); From cfbolz at codespeak.net Sat Jan 20 00:25:44 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sat, 20 Jan 2007 00:25:44 +0100 (CET) Subject: [py-svn] r37044 - in py/dist/py/test/rsession: . testing Message-ID: <20070119232544.6BBF810077@code0.codespeak.net> Author: cfbolz Date: Sat Jan 20 00:25:42 2007 New Revision: 37044 Modified: py/dist/py/test/rsession/testing/test_webjs.py py/dist/py/test/rsession/web.py Log: more fixes to the 2.3-pypy combination Modified: py/dist/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/dist/py/test/rsession/testing/test_webjs.py (original) +++ py/dist/py/test/rsession/testing/test_webjs.py Sat Jan 20 00:25:42 2007 @@ -1,17 +1,17 @@ import py + try: import pypy from pypy.translator.js.modules import dom - from py.__.test.rsession.rsession import session_options - from py.__.test.rsession.rsession import session_options - from py.__.test.rsession import webjs - from py.__.test.rsession.web import exported_methods from pypy.translator.js.tester import schedule_callbacks -except ImportError: + from py.__.test.rsession.rsession import session_options + dom.Window # check whether dom was properly imported or is just a + # leftover in sys.modules +except (ImportError, AttributeError): py.test.skip('PyPy not found') - from py.__.test.rsession import webjs - from py.__.test.rsession.web import exported_methods +from py.__.test.rsession import webjs +from py.__.test.rsession.web import exported_methods here = py.magic.autopath().dirpath() def setup_module(mod): Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Sat Jan 20 00:25:42 2007 @@ -37,7 +37,7 @@ commproxy.USE_MOCHIKIT = False IMPORTED_PYPY = True -except ImportError: +except (ImportError, NameError): class BasicExternal(object): pass From hpk at codespeak.net Sat Jan 20 02:24:06 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 20 Jan 2007 02:24:06 +0100 (CET) Subject: [py-svn] r37048 - py/dist/py/test/rsession/testing Message-ID: <20070120012406.32BF410077@code0.codespeak.net> Author: hpk Date: Sat Jan 20 02:24:05 2007 New Revision: 37048 Modified: py/dist/py/test/rsession/testing/test_rsession.py Log: test is not executed, but fix anyway Modified: py/dist/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/dist/py/test/rsession/testing/test_rsession.py (original) +++ py/dist/py/test/rsession/testing/test_rsession.py Sat Jan 20 02:24:05 2007 @@ -283,7 +283,7 @@ def test_sophisticated_parse(self): sshhosts = ['a at h1:/tmp', 'h2:tmp', 'h3'] dirs = parse_directories(sshhosts) - assert py.magic.builtin.sorted( + assert py.builtin.sorted( dirs.values()) == ['/tmp', 'pytestcache', 'tmp'] def test_parse_multiple_hosts(self): From hpk at codespeak.net Sat Jan 20 12:52:00 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 20 Jan 2007 12:52:00 +0100 (CET) Subject: [py-svn] r37055 - in py/branch/config/py/test: . terminal testing Message-ID: <20070120115200.1CADF10078@code0.codespeak.net> Author: hpk Date: Sat Jan 20 12:51:54 2007 New Revision: 37055 Modified: py/branch/config/py/test/cmdline.py py/branch/config/py/test/config.py py/branch/config/py/test/defaultconftest.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/testing/test_collect.py py/branch/config/py/test/testing/test_config.py py/branch/config/py/test/testing/test_session.py Log: reintroducing --session to lookup custom session objects in conftests.py there is no other way to override a session object from conftests. Modified: py/branch/config/py/test/cmdline.py ============================================================================== --- py/branch/config/py/test/cmdline.py (original) +++ py/branch/config/py/test/cmdline.py Sat Jan 20 12:51:54 2007 @@ -10,9 +10,7 @@ args = py.std.sys.argv[1:] config = py.test.config config.parse(args) - sessionclass = config.getsessionclass() - session = sessionclass(config) - + session = config.initsession() try: failures = session.main() if failures: Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Sat Jan 20 12:51:54 2007 @@ -76,23 +76,27 @@ except KeyError: return self.conftest.rget(name, path) - def getsessionclass(self): + def initsession(self): + """ return an initialized session object. """ + cls = self._getsessionclass() + session = cls(config) + #session.fixoptions() + return session + + def _getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. """ - name = self._getsessionname() - return self._lookupsession(name) - - def _lookupsession(self, name): - """ return session class for the given name. """ - res = self.conftest.rget(name) - if isinstance(res, str): - mod = __import__(res, None, None, '__doc__') - res = getattr(mod, name) - return res + if self.option.session is not None: + return self.conftest.rget(self.option.session) + else: + name = self._getsessionname() + importpath = globals()[name] + mod = __import__(importpath, None, None, '__doc__') + return getattr(mod, name) def _getsessionname(self): - """ return session name as determined from options. """ + """ return default session name as determined from options. """ name = 'TerminalSession' if self.option.dist: name = 'RSession' @@ -162,6 +166,13 @@ # this is the one per-process instance of py.test configuration config = Config() +# default import paths for sessions + +TkinterSession = 'py.__.test.tkinter.reportsession' +TerminalSession = 'py.__.test.terminal.terminal' +RSession = 'py.__.test.rsession.rsession' +LSession = 'py.__.test.rsession.rsession' + # # helpers # Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Sat Jan 20 12:51:54 2007 @@ -10,13 +10,6 @@ additionalinfo = None -# =================================================== -# Default Sessions - -TkinterSession = 'py.__.test.tkinter.reportsession' -TerminalSession = 'py.__.test.terminal.terminal' -RSession = 'py.__.test.rsession.rsession' -LSession = 'py.__.test.rsession.rsession' # =================================================== # Distributed testing specific options @@ -73,13 +66,9 @@ Option('', '--traceconfig', action="store_true", dest="traceconfig", default=False, help="trace considerations of conftest.py files."), - Option('', '--apigen', - action="store", dest="apigen", - help="generate api documentation while testing (requires" - "argument pointing to a script)."), ) - py.test.config.addoptions('options implying subprocesses (warning: beta)', + py.test.config.addoptions('EXPERIMENTAL options implying subprocesses', Option('', '--tkinter', action="store_true", dest="tkinter", default=False, help="use tkinter test session frontend."), @@ -103,5 +92,12 @@ Option('-r', '--rest', action='store_true', dest="restreport", default=False, help="restructured text output reporting."), + Option('', '--apigen', + action="store", dest="apigen", + help="generate api documentation while testing (requires" + "argument pointing to a script)."), + Option('', '--session', + action="store", dest="session", default=None, + help="lookup sessioname in conftest.py files and use it."), ) Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Sat Jan 20 12:51:54 2007 @@ -64,7 +64,7 @@ else: cols = args #print "processing", cols - session = config.getsessionclass()(config) + session = config._getsessionclass()(config) session.shouldclose = channel.isclosed failures = session.main() channel.send(failures) Modified: py/branch/config/py/test/testing/test_collect.py ============================================================================== --- py/branch/config/py/test/testing/test_collect.py (original) +++ py/branch/config/py/test/testing/test_collect.py Sat Jan 20 12:51:54 2007 @@ -215,7 +215,7 @@ try: config = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() - session = config.getsessionclass()(config, out) + session = config._getsessionclass()(config, out) session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 2 @@ -225,7 +225,7 @@ # test that running the file directly works config = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() - session = config.getsessionclass()(config, out) + session = config._getsessionclass()(config, out) session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 2 @@ -262,7 +262,7 @@ try: config = py.test.config._reparse([]) out = py.std.cStringIO.StringIO() - session = config.getsessionclass()(config, out) + session = config._getsessionclass()(config, out) session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 @@ -272,7 +272,7 @@ # test that running the file directly works config = py.test.config._reparse([str(checkfile)]) out = py.std.cStringIO.StringIO() - session = config.getsessionclass()(config, out) + session = config._getsessionclass()(config, out) session.main() l = session.getitemoutcomepairs(py.test.Item.Passed) assert len(l) == 1 Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Sat Jan 20 12:51:54 2007 @@ -155,22 +155,15 @@ def test_tkintersession(self): config = py.test.config._reparse([self.tmpdir, '--tkinter']) assert config._getsessionname() == 'TkinterSession' - - def test_sessionname_lookup(self): - config = py.test.config._reparse([self.tmpdir]) - for name in 'TerminalSession LSession RSession TkinterSession'.split(): - cls = config._lookupsession(name) - assert cls.__name__ == name + config = py.test.config._reparse([self.tmpdir, '--dist']) def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" - TerminalSession = 0 - TkinterSession = 1 - RSession = 2 - LSession = 3 + class MySession: + def __init__(self, config): + self.config = config """)) - config = py.test.config._reparse([self.tmpdir]) - assert config._lookupsession('TerminalSession') == 0 - assert config._lookupsession('TkinterSession') == 1 - assert config._lookupsession('RSession') == 2 - assert config._lookupsession('LSession') == 3 + config = py.test.config._reparse(["--session=MySession", self.tmpdir]) + session = config.initsession() + assert session.__class__.__name__ == 'MySession' + Modified: py/branch/config/py/test/testing/test_session.py ============================================================================== --- py/branch/config/py/test/testing/test_session.py (original) +++ py/branch/config/py/test/testing/test_session.py Sat Jan 20 12:51:54 2007 @@ -8,7 +8,7 @@ class TestDefaultSession: def test_simple(self): config = py.test.config._reparse([datadir/'filetest.py']) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config._getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 @@ -18,7 +18,7 @@ def test_simple_verbose(self): config = py.test.config._reparse([datadir/'filetest.py', '--verbose']) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config._getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 @@ -28,7 +28,7 @@ def test_simple_verbose_verbose(self): config = py.test.config._reparse([datadir/'filetest.py', '-v', '-v']) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config._getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 2 @@ -40,7 +40,7 @@ for keyword in ['test_one', 'est_on']: config = py.test.config._reparse([datadir/'filetest.py', '-k', keyword]) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config._getsessionclass()(config, py.std.sys.stdout) session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 1 @@ -69,7 +69,7 @@ 'TestClass test_2', 'xxx TestClass test_2',): f = py.std.StringIO.StringIO() config = py.test.config._reparse([o, '-k', keyword]) - session = config.getsessionclass()(config, f) + session = config._getsessionclass()(config, f) session.main() print "keyword", repr(keyword) l = session.getitemoutcomepairs(py.test.Item.Passed) @@ -339,7 +339,7 @@ ['--exec=' + py.std.sys.executable, o]) assert config.option._remote - cls = config.getsessionclass() + cls = config._getsessionclass() out = [] # out = py.std.Queue.Queue() session = cls(config, out.append) session.main() @@ -359,7 +359,7 @@ print py.std.sys.executable config = py.test.config._reparse(['--looponfailing', str(o)]) assert config.option._remote - cls = config.getsessionclass() + cls = config._getsessionclass() out = py.std.Queue.Queue() session = cls(config, out.put) pool = py._thread.WorkerPool() From cfbolz at codespeak.net Sat Jan 20 13:11:03 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sat, 20 Jan 2007 13:11:03 +0100 (CET) Subject: [py-svn] r37056 - in py/dist/py: . builtin builtin/testing Message-ID: <20070120121103.035EA1007E@code0.codespeak.net> Author: cfbolz Date: Sat Jan 20 13:11:01 2007 New Revision: 37056 Added: py/dist/py/builtin/set.py py/dist/py/builtin/testing/test_set.py Modified: py/dist/py/__init__.py Log: add set and frozenset to builtin Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Sat Jan 20 13:11:01 2007 @@ -85,6 +85,8 @@ 'builtin.reversed' : ('./builtin/reversed.py', 'reversed'), 'builtin.sorted' : ('./builtin/sorted.py', 'sorted'), 'builtin.BaseException' : ('./builtin/exception.py', 'BaseException'), + 'builtin.set' : ('./builtin/set.py', 'set'), + 'builtin.frozenset' : ('./builtin/set.py', 'frozenset'), # gateways into remote contexts 'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'), Added: py/dist/py/builtin/set.py ============================================================================== --- (empty file) +++ py/dist/py/builtin/set.py Sat Jan 20 13:11:01 2007 @@ -0,0 +1,587 @@ +"""Classes to represent arbitrary sets (including sets of sets). + +This module implements sets using dictionaries whose values are +ignored. The usual operations (union, intersection, deletion, etc.) +are provided as both methods and operators. + +Important: sets are not sequences! While they support 'x in s', +'len(s)', and 'for x in s', none of those operations are unique for +sequences; for example, mappings support all three as well. The +characteristic operation for sequences is subscripting with small +integers: s[i], for i in range(len(s)). Sets don't support +subscripting at all. Also, sequences allow multiple occurrences and +their elements have a definite order; sets on the other hand don't +record multiple occurrences and don't remember the order of element +insertion (which is why they don't support s[i]). + +The following classes are provided: + +BaseSet -- All the operations common to both mutable and immutable + sets. This is an abstract class, not meant to be directly + instantiated. + +Set -- Mutable sets, subclass of BaseSet; not hashable. + +ImmutableSet -- Immutable sets, subclass of BaseSet; hashable. + + +_TemporarilyImmutableSet -- A wrapper around a Set, hashable, + giving the same hash value as the immutable set equivalent + would have. Do not use this class directly. + +Only hashable objects can be added to a Set. In particular, you cannot +really add a Set as an element to another Set; if you try, what is +actually added is an ImmutableSet built from it (it compares equal to +the one you tried adding). + +When you ask if `x in y' where x is a Set and y is a Set or +ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and +what's tested is actually `z in y'. + +""" + +# Code history: +# +# - Greg V. Wilson wrote the first version, using a different approach +# to the mutable/immutable problem, and inheriting from dict. +# +# - Alex Martelli modified Greg's version to implement the current +# Set/ImmutableSet approach, and make the data an attribute. +# +# - Guido van Rossum rewrote much of the code, made some API changes, +# and cleaned up the docstrings. +# +# - Raymond Hettinger added a number of speedups and other +# improvements. + +from __future__ import generators +try: + from itertools import ifilter, ifilterfalse +except ImportError: + # Code to make the module run under Py2.2 + def ifilter(predicate, iterable): + if predicate is None: + def predicate(x): + return x + for x in iterable: + if predicate(x): + yield x + def ifilterfalse(predicate, iterable): + if predicate is None: + def predicate(x): + return x + for x in iterable: + if not predicate(x): + yield x + try: + True, False + except NameError: + True, False = (0==0, 0!=0) + +__all__ = ['BaseSet', 'Set', 'ImmutableSet'] + +class BaseSet(object): + """Common base class for mutable and immutable sets.""" + + __slots__ = ['_data'] + + # Constructor + + def __init__(self): + """This is an abstract class.""" + # Don't call this from a concrete subclass! + if self.__class__ is BaseSet: + raise TypeError, ("BaseSet is an abstract class. " + "Use Set or ImmutableSet.") + + # Standard protocols: __len__, __repr__, __str__, __iter__ + + def __len__(self): + """Return the number of elements of a set.""" + return len(self._data) + + def __repr__(self): + """Return string representation of a set. + + This looks like 'Set([])'. + """ + return self._repr() + + # __str__ is the same as __repr__ + __str__ = __repr__ + + def _repr(self, sorted=False): + elements = self._data.keys() + if sorted: + elements.sort() + return '%s(%r)' % (self.__class__.__name__, elements) + + def __iter__(self): + """Return an iterator over the elements or a set. + + This is the keys iterator for the underlying dict. + """ + return self._data.iterkeys() + + # Three-way comparison is not supported. However, because __eq__ is + # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and + # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this + # case). + + def __cmp__(self, other): + raise TypeError, "can't compare sets using cmp()" + + # Equality comparisons using the underlying dicts. Mixed-type comparisons + # are allowed here, where Set == z for non-Set z always returns False, + # and Set != z always True. This allows expressions like "x in y" to + # give the expected result when y is a sequence of mixed types, not + # raising a pointless TypeError just because y contains a Set, or x is + # a Set and y contain's a non-set ("in" invokes only __eq__). + # Subtle: it would be nicer if __eq__ and __ne__ could return + # NotImplemented instead of True or False. Then the other comparand + # would get a chance to determine the result, and if the other comparand + # also returned NotImplemented then it would fall back to object address + # comparison (which would always return False for __eq__ and always + # True for __ne__). However, that doesn't work, because this type + # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented, + # Python tries __cmp__ next, and the __cmp__ here then raises TypeError. + + def __eq__(self, other): + if isinstance(other, BaseSet): + return self._data == other._data + else: + return False + + def __ne__(self, other): + if isinstance(other, BaseSet): + return self._data != other._data + else: + return True + + # Copying operations + + def copy(self): + """Return a shallow copy of a set.""" + result = self.__class__() + result._data.update(self._data) + return result + + __copy__ = copy # For the copy module + + def __deepcopy__(self, memo): + """Return a deep copy of a set; used by copy module.""" + # This pre-creates the result and inserts it in the memo + # early, in case the deep copy recurses into another reference + # to this same set. A set can't be an element of itself, but + # it can certainly contain an object that has a reference to + # itself. + from copy import deepcopy + result = self.__class__() + memo[id(self)] = result + data = result._data + value = True + for elt in self: + data[deepcopy(elt, memo)] = value + return result + + # Standard set operations: union, intersection, both differences. + # Each has an operator version (e.g. __or__, invoked with |) and a + # method version (e.g. union). + # Subtle: Each pair requires distinct code so that the outcome is + # correct when the type of other isn't suitable. For example, if + # we did "union = __or__" instead, then Set().union(3) would return + # NotImplemented instead of raising TypeError (albeit that *why* it + # raises TypeError as-is is also a bit subtle). + + def __or__(self, other): + """Return the union of two sets as a new set. + + (I.e. all elements that are in either set.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.union(other) + + def union(self, other): + """Return the union of two sets as a new set. + + (I.e. all elements that are in either set.) + """ + result = self.__class__(self) + result._update(other) + return result + + def __and__(self, other): + """Return the intersection of two sets as a new set. + + (I.e. all elements that are in both sets.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.intersection(other) + + def intersection(self, other): + """Return the intersection of two sets as a new set. + + (I.e. all elements that are in both sets.) + """ + if not isinstance(other, BaseSet): + other = Set(other) + if len(self) <= len(other): + little, big = self, other + else: + little, big = other, self + common = ifilter(big._data.has_key, little) + return self.__class__(common) + + def __xor__(self, other): + """Return the symmetric difference of two sets as a new set. + + (I.e. all elements that are in exactly one of the sets.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.symmetric_difference(other) + + def symmetric_difference(self, other): + """Return the symmetric difference of two sets as a new set. + + (I.e. all elements that are in exactly one of the sets.) + """ + result = self.__class__() + data = result._data + value = True + selfdata = self._data + try: + otherdata = other._data + except AttributeError: + otherdata = Set(other)._data + for elt in ifilterfalse(otherdata.has_key, selfdata): + data[elt] = value + for elt in ifilterfalse(selfdata.has_key, otherdata): + data[elt] = value + return result + + def __sub__(self, other): + """Return the difference of two sets as a new Set. + + (I.e. all elements that are in this set and not in the other.) + """ + if not isinstance(other, BaseSet): + return NotImplemented + return self.difference(other) + + def difference(self, other): + """Return the difference of two sets as a new Set. + + (I.e. all elements that are in this set and not in the other.) + """ + result = self.__class__() + data = result._data + try: + otherdata = other._data + except AttributeError: + otherdata = Set(other)._data + value = True + for elt in ifilterfalse(otherdata.has_key, self): + data[elt] = value + return result + + # Membership test + + def __contains__(self, element): + """Report whether an element is a member of a set. + + (Called in response to the expression `element in self'.) + """ + try: + return element in self._data + except TypeError: + transform = getattr(element, "__as_temporarily_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + return transform() in self._data + + # Subset and superset test + + def issubset(self, other): + """Report whether another set contains this set.""" + self._binary_sanity_check(other) + if len(self) > len(other): # Fast check for obvious cases + return False + for elt in ifilterfalse(other._data.has_key, self): + return False + return True + + def issuperset(self, other): + """Report whether this set contains another set.""" + self._binary_sanity_check(other) + if len(self) < len(other): # Fast check for obvious cases + return False + for elt in ifilterfalse(self._data.has_key, other): + return False + return True + + # Inequality comparisons using the is-subset relation. + __le__ = issubset + __ge__ = issuperset + + def __lt__(self, other): + self._binary_sanity_check(other) + return len(self) < len(other) and self.issubset(other) + + def __gt__(self, other): + self._binary_sanity_check(other) + return len(self) > len(other) and self.issuperset(other) + + # Assorted helpers + + def _binary_sanity_check(self, other): + # Check that the other argument to a binary operation is also + # a set, raising a TypeError otherwise. + if not isinstance(other, BaseSet): + raise TypeError, "Binary operation only permitted between sets" + + def _compute_hash(self): + # Calculate hash code for a set by xor'ing the hash codes of + # the elements. This ensures that the hash code does not depend + # on the order in which elements are added to the set. This is + # not called __hash__ because a BaseSet should not be hashable; + # only an ImmutableSet is hashable. + result = 0 + for elt in self: + result ^= hash(elt) + return result + + def _update(self, iterable): + # The main loop for update() and the subclass __init__() methods. + data = self._data + + # Use the fast update() method when a dictionary is available. + if isinstance(iterable, BaseSet): + data.update(iterable._data) + return + + value = True + + if type(iterable) in (list, tuple, xrange): + # Optimized: we know that __iter__() and next() can't + # raise TypeError, so we can move 'try:' out of the loop. + it = iter(iterable) + while True: + try: + for element in it: + data[element] = value + return + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + data[transform()] = value + else: + # Safe: only catch TypeError where intended + for element in iterable: + try: + data[element] = value + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + data[transform()] = value + + +class ImmutableSet(BaseSet): + """Immutable set class.""" + + __slots__ = ['_hashcode'] + + # BaseSet + hashing + + def __init__(self, iterable=None): + """Construct an immutable set from an optional iterable.""" + self._hashcode = None + self._data = {} + if iterable is not None: + self._update(iterable) + + def __hash__(self): + if self._hashcode is None: + self._hashcode = self._compute_hash() + return self._hashcode + + def __getstate__(self): + return self._data, self._hashcode + + def __setstate__(self, state): + self._data, self._hashcode = state + +class Set(BaseSet): + """ Mutable set class.""" + + __slots__ = [] + + # BaseSet + operations requiring mutability; no hashing + + def __init__(self, iterable=None): + """Construct a set from an optional iterable.""" + self._data = {} + if iterable is not None: + self._update(iterable) + + def __getstate__(self): + # getstate's results are ignored if it is not + return self._data, + + def __setstate__(self, data): + self._data, = data + + def __hash__(self): + """A Set cannot be hashed.""" + # We inherit object.__hash__, so we must deny this explicitly + raise TypeError, "Can't hash a Set, only an ImmutableSet." + + # In-place union, intersection, differences. + # Subtle: The xyz_update() functions deliberately return None, + # as do all mutating operations on built-in container types. + # The __xyz__ spellings have to return self, though. + + def __ior__(self, other): + """Update a set with the union of itself and another.""" + self._binary_sanity_check(other) + self._data.update(other._data) + return self + + def union_update(self, other): + """Update a set with the union of itself and another.""" + self._update(other) + + def __iand__(self, other): + """Update a set with the intersection of itself and another.""" + self._binary_sanity_check(other) + self._data = (self & other)._data + return self + + def intersection_update(self, other): + """Update a set with the intersection of itself and another.""" + if isinstance(other, BaseSet): + self &= other + else: + self._data = (self.intersection(other))._data + + def __ixor__(self, other): + """Update a set with the symmetric difference of itself and another.""" + self._binary_sanity_check(other) + self.symmetric_difference_update(other) + return self + + def symmetric_difference_update(self, other): + """Update a set with the symmetric difference of itself and another.""" + data = self._data + value = True + if not isinstance(other, BaseSet): + other = Set(other) + if self is other: + self.clear() + for elt in other: + if elt in data: + del data[elt] + else: + data[elt] = value + + def __isub__(self, other): + """Remove all elements of another set from this set.""" + self._binary_sanity_check(other) + self.difference_update(other) + return self + + def difference_update(self, other): + """Remove all elements of another set from this set.""" + data = self._data + if not isinstance(other, BaseSet): + other = Set(other) + if self is other: + self.clear() + for elt in ifilter(data.has_key, other): + del data[elt] + + # Python dict-like mass mutations: update, clear + + def update(self, iterable): + """Add all values from an iterable (such as a list or file).""" + self._update(iterable) + + def clear(self): + """Remove all elements from this set.""" + self._data.clear() + + # Single-element mutations: add, remove, discard + + def add(self, element): + """Add an element to a set. + + This has no effect if the element is already present. + """ + try: + self._data[element] = True + except TypeError: + transform = getattr(element, "__as_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + self._data[transform()] = True + + def remove(self, element): + """Remove an element from a set; it must be a member. + + If the element is not a member, raise a KeyError. + """ + try: + del self._data[element] + except TypeError: + transform = getattr(element, "__as_temporarily_immutable__", None) + if transform is None: + raise # re-raise the TypeError exception we caught + del self._data[transform()] + + def discard(self, element): + """Remove an element from a set if it is a member. + + If the element is not a member, do nothing. + """ + try: + self.remove(element) + except KeyError: + pass + + def pop(self): + """Remove and return an arbitrary set element.""" + return self._data.popitem()[0] + + def __as_immutable__(self): + # Return a copy of self as an immutable set + return ImmutableSet(self) + + def __as_temporarily_immutable__(self): + # Return self wrapped in a temporarily immutable set + return _TemporarilyImmutableSet(self) + + +class _TemporarilyImmutableSet(BaseSet): + # Wrap a mutable set as if it was temporarily immutable. + # This only supplies hashing and equality comparisons. + + def __init__(self, set): + self._set = set + self._data = set._data # Needed by ImmutableSet.__eq__() + + def __hash__(self): + return self._set._compute_hash() + + +try: + set, frozenset = set, frozenset +except NameError: + try: + from sets import Set as set, ImmutableSet as frozenset + except ImportError: + set = Set + frozenset = ImmutableSet Added: py/dist/py/builtin/testing/test_set.py ============================================================================== --- (empty file) +++ py/dist/py/builtin/testing/test_set.py Sat Jan 20 13:11:01 2007 @@ -0,0 +1,14 @@ +# some small tests to see whether sets are there and work + +from py.builtin import set, frozenset + +def test_simple(): + s = set([1, 2, 3, 4]) + assert s == set([3, 4, 2, 1]) + s1 = s.union(set([5, 6])) + assert 5 in s1 + assert 1 in s1 + +def test_frozenset(): + s = set([frozenset([0, 1]), frozenset([1, 0])]) + assert len(s) == 1 From cfbolz at codespeak.net Sat Jan 20 13:13:25 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sat, 20 Jan 2007 13:13:25 +0100 (CET) Subject: [py-svn] r37057 - py/dist/py/doc Message-ID: <20070120121325.230331007E@code0.codespeak.net> Author: cfbolz Date: Sat Jan 20 13:13:22 2007 New Revision: 37057 Modified: py/dist/py/doc/misc.txt Log: mention set in docs Modified: py/dist/py/doc/misc.txt ============================================================================== --- py/dist/py/doc/misc.txt (original) +++ py/dist/py/doc/misc.txt Sat Jan 20 13:13:22 2007 @@ -203,5 +203,7 @@ * reversed * sorted * BaseException + * set and frozenset (using either the builtin, if available, or the sets + module) ``py.builtin.BaseException`` is just ``Exception`` before Python 2.5. From cfbolz at codespeak.net Sat Jan 20 13:18:28 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sat, 20 Jan 2007 13:18:28 +0100 (CET) Subject: [py-svn] r37058 - in py/dist/py/apigen/tracer: . testing Message-ID: <20070120121828.AC72E1007E@code0.codespeak.net> Author: cfbolz Date: Sat Jan 20 13:18:23 2007 New Revision: 37058 Modified: py/dist/py/apigen/tracer/description.py py/dist/py/apigen/tracer/model.py py/dist/py/apigen/tracer/testing/test_docgen.py Log: use py.builtin.set instead of the import Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Sat Jan 20 13:18:23 2007 @@ -8,11 +8,7 @@ MAX_CALL_SITES = 20 -try: - set -except NameError: - from sets import Set as set - +set = py.builtin.set def is_private(name): return name.startswith('_') and not name.startswith('__') Modified: py/dist/py/apigen/tracer/model.py ============================================================================== --- py/dist/py/apigen/tracer/model.py (original) +++ py/dist/py/apigen/tracer/model.py Sat Jan 20 13:18:23 2007 @@ -5,12 +5,10 @@ # we implement all the types which are in the types.*, naming # scheme after pypy's +import py import types -try: - set -except NameError: - from sets import Set as set +set = py.builtin.set # __extend__ and pairtype? Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Sat Jan 20 13:18:23 2007 @@ -21,12 +21,9 @@ # sys.path.insert(0, str(data_path)) # XXX: Perma doc storage disabled a bit -try: - set -except NameError: - from sets import Set as set sorted = py.builtin.sorted +set = py.builtin.set def fun(a, b, c): "Some docstring" From hpk at codespeak.net Sat Jan 20 16:29:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 20 Jan 2007 16:29:27 +0100 (CET) Subject: [py-svn] r37061 - in py/branch/config/py/test: . terminal testing Message-ID: <20070120152927.E80941007B@code0.codespeak.net> Author: hpk Date: Sat Jan 20 16:29:25 2007 New Revision: 37061 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/terminal/terminal.py py/branch/config/py/test/testing/test_config.py py/branch/config/py/test/testing/test_session.py Log: refactoring/fixing the remote terminal approach (used for --exec and --looponfailing) to use the new configiguration. Removes lots of strange code. XXX there are still some recursive testing issues. Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Sat Jan 20 16:29:25 2007 @@ -108,6 +108,11 @@ if getattr(self.option, opt, False): name = 'LSession' break + else: + if self.option.looponfailing: + name = 'RemoteTerminalSession' + elif self.option.executable: + name = 'RemoteTerminalSession' return name def _reparse(self, args): @@ -170,6 +175,8 @@ TkinterSession = 'py.__.test.tkinter.reportsession' TerminalSession = 'py.__.test.terminal.terminal' +TerminalSession = 'py.__.test.terminal.terminal' +RemoteTerminalSession = 'py.__.test.terminal.remote' RSession = 'py.__.test.rsession.rsession' LSession = 'py.__.test.rsession.rsession' @@ -192,15 +199,15 @@ # setting a correct executable remote = False - if option.executable is not None: - remote = True - exe = py.path.local(py.std.os.path.expanduser(option.executable)) - if not exe.check(): - exe = py.path.local.sysfind(option.executable) - assert exe.check() - option.executable = exe - else: - option.executable = py.std.sys.executable + #if option.executable is not None: + # remote = True + # exe = py.path.local(py.std.os.path.expanduser(option.executable)) + # if not exe.check(): + # exe = py.path.local.sysfind(option.executable) + # assert exe.check() + # option.executable = exe + #else: + # option.executable = py.std.sys.executable if option.usepdb: if not option.nocapture: Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Sat Jan 20 16:29:25 2007 @@ -29,46 +29,6 @@ changed = True return changed -class FailingCollector(py.test.collect.Collector): - def __init__(self, faileditems): - self._faileditems = faileditems - - def __iter__(self): - for x in self._faileditems: - yield x - -def waitfinish(channel): - try: - while 1: - try: - channel.waitclose(0.1) - except (IOError, py.error.Error): - continue - else: - return channel.receive() - finally: - #print "closing down channel and gateway" - channel.gateway.exit() - -def failure_slave(channel): - """ we run this on the other side. """ - args, failures = channel.receive() - config = py.test.config._reparse(args) - # making this session definitely non-remote - config.option.executable = py.std.sys.executable - config.option.looponfailing = False - config.option._remote = False - config.option._fromremote = True - if failures: - cols = getfailureitems(failures) - else: - cols = args - #print "processing", cols - session = config._getsessionclass()(config) - session.shouldclose = channel.isclosed - failures = session.main() - channel.send(failures) - def getfailureitems(failures): l = [] for rootpath, names in failures: @@ -91,15 +51,6 @@ l.append(current) return l -def failure_master(executable, out, args, failures): - gw = py.execnet.PopenGateway(executable) - channel = gw.remote_exec(""" - from py.__.test.terminal.remote import failure_slave - failure_slave(channel) - """, stdout=out, stderr=out) - channel.send((args, failures)) - return waitfinish(channel) - def generalize(p1, p2): general = p1 for x, y in zip(p1.parts(), p2.parts()): @@ -118,34 +69,80 @@ p = p.dirpath() return p -def main(config, file, args): - """ testing process and output happens at a remote place. """ - assert file - if hasattr(file, 'write'): - def out(data): - file.write(data) - file.flush() - else: - out = file - failures = [] - args = list(args) - rootdir = getrootdir(config.remaining) - #print "rootdir", rootdir - wasfailing = False - while 1: - if config.option.looponfailing and (failures or not wasfailing): - while not checkpyfilechange(rootdir): - py.std.time.sleep(0.4) - wasfailing = len(failures) - failures = failure_master(config.option.executable, out, args, failures) - if not config.option.looponfailing: - break - print "#" * 60 - print "# looponfailing: mode: %d failures remaining" % 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 +class RemoteTerminalSession(object): + def __init__(self, config, file=None): + self.config = config + self._setexecutable() + if file is None: + file = py.std.sys.stdout + self._file = file + self.out = getout(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 = getrootdir(self.config.remaining) + wasfailing = False + failures = [] + while 1: + if self.config.option.looponfailing and (failures or not wasfailing): + while not checkpyfilechange(rootdir): + py.std.time.sleep(0.4) + wasfailing = len(failures) + failures = self.run_remote_session(failures) + if not self.config.option.looponfailing: + break + print "#" * 60 + print "# looponfailing: mode: %d failures remaining" % 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 run_remote_session(self, failures): + print "* opening PopenGateway: ", self.executable + gw = py.execnet.PopenGateway(self.executable) + channel = gw.remote_exec(""" + from py.__.test.terminal.remote import slaverun_TerminalSession + slaverun_TerminalSession(channel) + """, stdout=self.out, stderr=self.out) + print "* triggered slave terminal session ->" + repr = self.config.make_repr(conftestnames=[]) + args = self.config.remaining + channel.send((repr, args, failures)) + print "* send start info" + ack = channel.receive() + assert ack is None + failures = channel.receive() + channel.waitclose(0.1) + return failures + +def slaverun_TerminalSession(channel): + """ we run this on the other side. """ + repr, args, failures = channel.receive() + config = py.test.config + config.remaining = args + config.conftest.setinitial(args) + config.merge_repr(repr) + config.option.looponfailing = False + config.option.usepdb = False + config.option.executable = None + if failures: + cols = getfailureitems(failures) + else: + cols = args + session = config.initsession() + session.shouldclose = channel.isclosed + channel.send(None) + failures = session.main() + channel.send(failures) Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Sat Jan 20 16:29:25 2007 @@ -25,16 +25,8 @@ file = py.std.sys.stdout self._file = file self.out = getout(file) - self._started = {} self._opencollectors = [] - def main(self): - if self.config.option._remote: - from py.__.test.terminal import remote - return remote.main(self.config, self._file, self.config._origargs) - else: - return super(TerminalSession, self).main() - # --------------------- # PROGRESS information # --------------------- @@ -128,10 +120,10 @@ for name in 'looponfailing', 'exitfirst', 'nomagic': if getattr(option, name): modes.append(name) - if option._fromremote: - modes.insert(0, 'child process') - else: - modes.insert(0, 'inprocess') + #if self._isremoteoption._fromremote: + # modes.insert(0, 'child process') + #else: + # modes.insert(0, 'inprocess') mode = "/".join(modes) self.out.line("testing-mode: %s" % mode) self.out.line("executable: %s (%s)" % Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Sat Jan 20 16:29:25 2007 @@ -152,6 +152,14 @@ config = py.test.config._reparse([self.tmpdir, '--dist', '--%s' % x]) assert config._getsessionname() == 'RSession' + def test_implied_remote_terminal_session(self): + config = py.test.config._reparse([self.tmpdir, '--looponfailing']) + assert config._getsessionname() == 'RemoteTerminalSession' + config = py.test.config._reparse([self.tmpdir, '--exec=x']) + assert config._getsessionname() == 'RemoteTerminalSession' + config = py.test.config._reparse([self.tmpdir, '--dist', '--exec=x']) + assert config._getsessionname() == 'RSession' + def test_tkintersession(self): config = py.test.config._reparse([self.tmpdir, '--tkinter']) assert config._getsessionname() == 'TkinterSession' Modified: py/branch/config/py/test/testing/test_session.py ============================================================================== --- py/branch/config/py/test/testing/test_session.py (original) +++ py/branch/config/py/test/testing/test_session.py Sat Jan 20 16:29:25 2007 @@ -328,6 +328,7 @@ assert rootdir == one.dirpath() def test_exec(self): + py.test.skip("xxx work in progress") o = tmpdir.ensure('remote', dir=1) tfile = o.join('test_exec.py') tfile.write(py.code.Source(""" @@ -338,7 +339,6 @@ config = py.test.config._reparse( ['--exec=' + py.std.sys.executable, o]) - assert config.option._remote cls = config._getsessionclass() out = [] # out = py.std.Queue.Queue() session = cls(config, out.append) @@ -350,6 +350,7 @@ py.test.fail("did not see test_1 failure") def test_looponfailing(self): + py.test.skip("xxx work in progress") o = tmpdir.ensure('looponfailing', dir=1) tfile = o.join('test_looponfailing.py') tfile.write(py.code.Source(""" From arigo at codespeak.net Sat Jan 20 17:00:33 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 20 Jan 2007 17:00:33 +0100 (CET) Subject: [py-svn] r37063 - py/branch/config/py/test/rsession Message-ID: <20070120160033.427F41007B@code0.codespeak.net> Author: arigo Date: Sat Jan 20 17:00:31 2007 New Revision: 37063 Modified: py/branch/config/py/test/rsession/rsession.py Log: webbrowser.open() may block until the browser finishes or not, so better start it from a background thread. An case where it blocks is if you define the $BROWSER environment variable. Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Sat Jan 20 17:00:31 2007 @@ -61,8 +61,10 @@ reporter = exported_methods.report start_server() if self.config.option.runbrowser: - import webbrowser - webbrowser.open("http://localhost:8000") + import webbrowser, thread + # webbrowser.open() may block until the browser finishes or not + url = "http://localhost:8000" + thread.start_new_thread(webbrowser.open, (url,)) elif reporter is None: if restflag: from py.__.test.rsession.rest import RestReporter From arigo at codespeak.net Sat Jan 20 17:15:12 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sat, 20 Jan 2007 17:15:12 +0100 (CET) Subject: [py-svn] r37065 - in py/branch/config/py/test: . rsession Message-ID: <20070120161512.352DE1007B@code0.codespeak.net> Author: arigo Date: Sat Jan 20 17:15:10 2007 New Revision: 37065 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/web.py Log: Choose a random free port when using the --runbrowser option, but stick to the fixed 8000 when using --startserver only. Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Sat Jan 20 17:15:10 2007 @@ -219,6 +219,6 @@ option._fromremote = False if option.runbrowser and not option.startserver: - print "Cannot point browser when not starting server" + #print "Cannot point browser when not starting server" option.startserver = True # XXX: Shouldn't we fix the session here? Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Sat Jan 20 17:15:10 2007 @@ -57,13 +57,19 @@ if startserverflag and reporter is None: from py.__.test.rsession.web import start_server, exported_methods + if self.config.option.runbrowser: + from socket import INADDR_ANY + port = INADDR_ANY # pick a random port when starting browser + else: + port = 8000 # stick to a fixed port otherwise reporter = exported_methods.report - start_server() + httpd = start_server(server_address = ('', port)) + port = httpd.server_port if self.config.option.runbrowser: import webbrowser, thread # webbrowser.open() may block until the browser finishes or not - url = "http://localhost:8000" + url = "http://localhost:%d" % (port,) thread.start_new_thread(webbrowser.open, (url,)) elif reporter is None: if restflag: Modified: py/branch/config/py/test/rsession/web.py ============================================================================== --- py/branch/config/py/test/rsession/web.py (original) +++ py/branch/config/py/test/rsession/web.py Sat Jan 20 17:15:10 2007 @@ -416,10 +416,10 @@ if start_new: thread.start_new_thread(httpd.serve_forever, ()) - print "Server started, listening on %s" % (server_address,) + print "Server started, listening on port %d" % (httpd.server_port,) return httpd else: - print "Server started, listening on %s" % (server_address,) + print "Server started, listening on port %d" % (httpd.server_port,) httpd.serve_forever() def kill_server(): From guido at codespeak.net Sat Jan 20 17:37:40 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 20 Jan 2007 17:37:40 +0100 (CET) Subject: [py-svn] r37066 - in py/dist/py/apigen: . testing Message-ID: <20070120163740.764581007E@code0.codespeak.net> Author: guido Date: Sat Jan 20 17:37:38 2007 New Revision: 37066 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py Log: Fixed stylesheet links. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Sat Jan 20 17:37:38 2007 @@ -5,6 +5,7 @@ from py.__.apigen.source import html as source_html from py.__.apigen.tracer.description import is_private from py.__.apigen.rest.genrest import split_of_last_part +from py.__.apigen.linker import relpath sorted = py.builtin.sorted html = py.xml.html @@ -234,7 +235,7 @@ if child.ext in ['.pyc', '.pyo']: continue files.append(child) - return dirs, files + return sorted(dirs), sorted(files) def build_source_navigation(linker, projroot, fspath, outputpath): nav = H.Navigation() @@ -264,10 +265,10 @@ # we're a file, build our parent's children only dirpath = fspath.dirpath() diritems, fileitems = source_dirs_files(dirpath) - for dir in sorted(diritems): + for dir in diritems: nav.append(build_navitem_html(linker, dir.basename, dir.strpath, indent, False)) - for file in sorted(fileitems): + for file in fileitems: selected = (fspath.check(file=True) and file.basename == fspath.basename) nav.append(build_navitem_html(linker, file.basename, file.strpath, @@ -316,7 +317,9 @@ # skip hidden dirs and files continue elif fspath.check(dir=True): - reloutputpath = 'source/%s/index.html' % (relfspath,) + if relfspath != '': + relfspath += '/' + reloutputpath = 'source/%sindex.html' % (relfspath,) else: reloutputpath = "source/%s.html" % (relfspath,) outputpath = outputbase.join(reloutputpath) @@ -348,8 +351,10 @@ else: tag, nav = build_source_nonpython_page(linker, projroot, fspath, outputpath) + stylesheeturl = relpath('%s/' % (outputpath.dirpath(),), + '%s' % (outputbase.join('style.css'),)) page = wrap_page(project, 'sources for %s' % (fspath.basename,), - tag, nav, outputbase) + tag, nav, outputbase, stylesheeturl) outputpath.ensure() reltargetpath = outputpath.relto(outputbase) content = linker.call_withbase(reltargetpath, page.unicode) @@ -382,10 +387,12 @@ def build_class_api_pages(linker, data, project, outputbase): """ build the full api pages for a set of classes """ for dotted_name, tag, nav, reltargetpath in data: + targetpath = outputbase.ensure(reltargetpath) + stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), + outputbase.join('style.css').strpath) page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav, outputbase) + tag, nav, outputbase, stylesheeturl) content = linker.call_withbase(reltargetpath, page.unicode) - targetpath = outputbase.ensure(reltargetpath) targetpath.write(content.encode("utf8")) def prepare_method_api_pages(linker, dsa, base, namespace_tree, @@ -407,10 +414,12 @@ def build_method_api_pages(linker, data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: + targetpath = outputbase.join(reltargetpath) + stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), + outputbase.join('style.css').strpath) page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav, outputbase) + tag, nav, outputbase, stylesheeturl) content = linker.call_withbase(reltargetpath, page.unicode) - targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) @@ -431,10 +440,12 @@ def build_function_api_pages(linker, data, project, outputbase): for dotted_name, tag, nav, reltargetpath in data: + targetpath = outputbase.join(reltargetpath) + stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), + outputbase.join('style.css').strpath) page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav, outputbase) + tag, nav, outputbase, stylesheeturl) content = linker.call_withbase(reltargetpath, page.unicode) - targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) @@ -462,16 +473,18 @@ for dotted_name, tag, nav, reltargetpath in data: if dotted_name == '': dotted_name = 'project root' + targetpath = outputbase.join(reltargetpath) + stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), + outputbase.join('style.css').strpath) page = wrap_page(project, 'index of %s namespace' % (dotted_name,), - tag, nav, outputbase) + tag, nav, outputbase, stylesheeturl) content = linker.call_withbase(reltargetpath, page.unicode) - targetpath = outputbase.join(reltargetpath) targetpath.ensure() targetpath.write(content.encode("utf8")) -def wrap_page(project, title, contentel, navel, outputpath): +def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', - stylesheeturl='style.css',) + stylesheeturl=stylesheeturl) page.set_content(contentel) here = py.magic.autopath().dirpath() style = here.join('style.css').read() Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Sat Jan 20 17:37:38 2007 @@ -139,6 +139,7 @@ print html # note that the root and the first level both have the same name (pkg) # in our example project (XXX fixme!!) + assert 'href="../style.css"' in html assert 'href="index.html">pkg' in html assert 'href="pkg.html">pkg' in html assert 'href="pkg.SomeClass.html">SomeClass' in html @@ -160,6 +161,8 @@ clsfile = base.join('api/pkg.SomeSubClass.html') assert clsfile.check() html = clsfile.read() + print html + assert 'href="../style.css"' in html assert 'href="pkg.SomeClass.html">pkg.SomeClass' in html _checkhtml(html) @@ -178,7 +181,8 @@ assert funcsource.check(file=True) html = funcsource.read() print html - #assert 'py' in html + assert 'href="../../style.css"' in html + assert 'root' in html assert 'pkg' in html assert 'someclass.py' in html assert 'somesubclass.py' in html @@ -315,8 +319,7 @@ self.fs_root.join('pkg'), base) html = nav.unicode(indent=0) print html.encode('UTF-8') - # XXX fix the double slash!! - assert 'href="source//index.html">root' in html + assert 'href="source/index.html">root' in html assert 'href="source/pkg/index.html">pkg' in html assert 'href="source/pkg/func.py.html">func.py' in html assert 'href="source/pkg/someclass.py.html">someclass.py' in html From hpk at codespeak.net Sat Jan 20 21:43:55 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 20 Jan 2007 21:43:55 +0100 (CET) Subject: [py-svn] r37074 - in py/branch/config/py: . doc doc/example/pytest test test/terminal test/testing test/tkinter Message-ID: <20070120204355.A06101007E@code0.codespeak.net> Author: hpk Date: Sat Jan 20 21:43:52 2007 New Revision: 37074 Modified: py/branch/config/py/__init__.py py/branch/config/py/doc/example/pytest/test_failures.py py/branch/config/py/doc/test_conftest.py py/branch/config/py/test/collect.py py/branch/config/py/test/config.py py/branch/config/py/test/item.py py/branch/config/py/test/session.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/testing/test_collect.py py/branch/config/py/test/testing/test_config.py py/branch/config/py/test/testing/test_doctest.py py/branch/config/py/test/testing/test_session.py py/branch/config/py/test/tkinter/backend.py Log: * config.initsession() deals with getting at a session class and initiliazing it * config._getcollector(path) gets a collector pointing to path * added lots of tests * thus remove the strange getfscollector and map2colitems logic * mark some tkinter tests as skipped * fixing things all around Modified: py/branch/config/py/__init__.py ============================================================================== --- py/branch/config/py/__init__.py (original) +++ py/branch/config/py/__init__.py Sat Jan 20 21:43:52 2007 @@ -32,7 +32,7 @@ 'test.compat.TestCase' : ('./test/compat.py', 'TestCase'), # configuration/initialization related test api - 'test.config' : ('./test/config.py', 'config'), + 'test.config' : ('./test/config.py', 'config_per_process'), 'test.ensuretemp' : ('./test/config.py', 'ensuretemp'), 'test.cmdline.main' : ('./test/cmdline.py', 'main'), Modified: py/branch/config/py/doc/example/pytest/test_failures.py ============================================================================== --- py/branch/config/py/doc/example/pytest/test_failures.py (original) +++ py/branch/config/py/doc/example/pytest/test_failures.py Sat Jan 20 21:43:52 2007 @@ -4,7 +4,7 @@ def test_failure_demo_fails_properly(): config = py.test.config._reparse([failure_demo]) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config.initsession() session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 21 Modified: py/branch/config/py/doc/test_conftest.py ============================================================================== --- py/branch/config/py/doc/test_conftest.py (original) +++ py/branch/config/py/doc/test_conftest.py Sat Jan 20 21:43:52 2007 @@ -7,7 +7,6 @@ def test_doctest_basic(): # XXX get rid of the next line: py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) - tmpdir.ensure('__init__.py') xtxt = tmpdir.join('x.txt') xtxt.write(py.code.Source(""" @@ -29,7 +28,7 @@ end """)) config = py.test.config._reparse([xtxt]) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config.initsession() session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 @@ -47,7 +46,7 @@ .. _`blah`: javascript:some_function() """)) config = py.test.config._reparse([xtxt]) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config.initsession() session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 Modified: py/branch/config/py/test/collect.py ============================================================================== --- py/branch/config/py/test/collect.py (original) +++ py/branch/config/py/test/collect.py Sat Jan 20 21:43:52 2007 @@ -29,31 +29,9 @@ def configproperty(name): def fget(self): #print "retrieving %r property from %s" %(name, self.fspath) - return py.test.config.getvalue(name, self.fspath) + return self.config.getvalue(name, self.fspath) return property(fget) -def getfscollector(fspath): - if isinstance(fspath, str): - fspath = py.path.local(fspath) - if not fspath.check(): - raise py.error.ENOENT(fspath) - pkgpath = fspath.pypkgpath() - if pkgpath is None: - if fspath.check(dir=1): - pkgpath = fspath - else: - pkgpath = fspath.dirpath() - Directory = py.test.config.getvalue('Directory', pkgpath) - current = Directory(pkgpath) - #print "pkgpath", pkgpath - names = filter(None, fspath.relto(pkgpath).split(fspath.sep)) - for name in names: - current = current.join(name) - assert current, "joining %r resulted in None!" % (names,) - top = current.listchain()[0] - #top._config = config - return current - class Collector(object): """ Collector instances are iteratively generated (through their run() and join() methods) @@ -63,10 +41,10 @@ (or None if it is the root collector) name: basename of this collector object """ - def __init__(self, name, parent=None): + def __init__(self, name, parent=None): self.name = name self.parent = parent - self.option = getattr(parent, 'option', None) + self.config = getattr(parent, 'config', py.test.config) self.fspath = getattr(parent, 'fspath', None) Module = configproperty('Module') @@ -391,7 +369,7 @@ return res def startcapture(self): - if not self.option.nocapture: + if not self.config.option.nocapture: assert not hasattr(self, '_capture') #self._capture = py.io.OutErrCapture() # XXX integrate this into py.io / refactor Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Sat Jan 20 21:43:52 2007 @@ -45,7 +45,6 @@ self.conftest.setinitial(args) self.conftest.rget('adddefaultoptions')() args = [str(x) for x in args] - self._origargs = args cmdlineoption, remaining = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) fixoptions(self.option) # XXX fixing should be moved to sessions @@ -53,6 +52,31 @@ remaining.append(py.std.os.getcwd()) self.remaining = remaining + def getcolitems(self): + """ return initial collectors. """ + colitems = [] + if self.remaining: + for path in self.remaining: + path = py.path.local(path) + colitems.append(self._getcollector(path)) + return colitems + + def _getcollector(self, path): + assert path.check(), "%s: path does not exist" %(path,) + pkgpath = path.pypkgpath() + if pkgpath is None: + pkgpath = path.check(file=1) and path.dirpath() or path + print pkgpath + col = self.conftest.rget("Directory", pkgpath)(pkgpath) + col.config = self + for name in path.relto(pkgpath).split(path.sep): + if name: + print "joining", name, "to", col + col = col.join(name) + assert col is not None, ( + "joining %r to collector %s resulted in None" %(name, col)) + return col + def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. This function gets invoked during testing session initialization. @@ -79,7 +103,7 @@ def initsession(self): """ return an initialized session object. """ cls = self._getsessionclass() - session = cls(config) + session = cls(self) #session.fixoptions() return session @@ -117,14 +141,14 @@ def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ - global config + global config_per_process oldconfig = py.test.config try: - config = py.test.config = Config() - config.parse(args) - return config + config_per_process = py.test.config = Config() + config_per_process.parse(args) + return config_per_process finally: - config = py.test.config = oldconfig + config_per_process = py.test.config = oldconfig def _overwrite(self, name, value): """ this is used from tests to overwrite values irrespectives of conftests. @@ -169,7 +193,7 @@ setattr(self.option, name, val) # this is the one per-process instance of py.test configuration -config = Config() +config_per_process = Config() # default import paths for sessions Modified: py/branch/config/py/test/item.py ============================================================================== --- py/branch/config/py/test/item.py (original) +++ py/branch/config/py/test/item.py Sat Jan 20 21:43:52 2007 @@ -31,7 +31,7 @@ class Item(py.test.collect.Collector): def startcapture(self): - if not self.option.nocapture: + if not self.config.option.nocapture: # XXX refactor integrate capturing #self._capture = py.io.OutErrCapture() from py.__.misc.simplecapture import SimpleOutErrCapture Modified: py/branch/config/py/test/session.py ============================================================================== --- py/branch/config/py/test/session.py (original) +++ py/branch/config/py/test/session.py Sat Jan 20 21:43:52 2007 @@ -38,7 +38,7 @@ def main(self): """ main loop for running tests. """ - colitems = self._map2colitems(self.config.remaining) + colitems = self.config.getcolitems() try: self.header(colitems) try: @@ -128,21 +128,6 @@ return True return False - - def _map2colitems(items): - # first convert all path objects into collectors - from py.__.test.collect import getfscollector - colitems = [] - for item in items: - if isinstance(item, (list, tuple)): - colitems.extend(Session._map2colitems(item)) - elif not isinstance(item, py.test.collect.Collector): - colitems.append(getfscollector(item)) - else: - colitems.append(item) - return colitems - _map2colitems = staticmethod(_map2colitems) - class Exit(Exception): """ for immediate program exits without tracebacks and reporter/summary. """ def __init__(self, msg="unknown reason", item=None): Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Sat Jan 20 21:43:52 2007 @@ -115,20 +115,21 @@ from py.__.test.terminal.remote import slaverun_TerminalSession slaverun_TerminalSession(channel) """, stdout=self.out, stderr=self.out) - print "* triggered slave terminal session ->" + print "MASTER: triggered slave terminal session ->" repr = self.config.make_repr(conftestnames=[]) - args = self.config.remaining - channel.send((repr, args, failures)) - print "* send start info" - ack = channel.receive() - assert ack is None - failures = channel.receive() - channel.waitclose(0.1) - return failures + channel.send((repr, self.config.remaining, failures)) + print "MASTER: send start info" + try: + return channel.receive() + except channel.RemoteError, e: + print e + return [] def slaverun_TerminalSession(channel): """ we run this on the other side. """ + print "SLAVE: starting" repr, args, failures = channel.receive() + print "SLAVE: received configuration" config = py.test.config config.remaining = args config.conftest.setinitial(args) @@ -143,6 +144,6 @@ cols = args session = config.initsession() session.shouldclose = channel.isclosed - channel.send(None) + print "SLAVE: received configuration" failures = session.main() channel.send(failures) Modified: py/branch/config/py/test/testing/test_collect.py ============================================================================== --- py/branch/config/py/test/testing/test_collect.py (original) +++ py/branch/config/py/test/testing/test_collect.py Sat Jan 20 21:43:52 2007 @@ -12,18 +12,6 @@ py.test.raises(ImportError, col.run) py.test.raises(ImportError, col.run) -def XXXtest_finds_root(): - fn = datadir / 'filetest.py' - col = py.test.collect.Module(fn) - root, namelist = col.fromroot() - assert isinstance(root, py.test.collect.Directory) - cur = root - for x in namelist: - cur = cur.join(x) - assert cur.name == col.name - assert cur.parent == col.parent - assert cur.fspath == cur.fspath - def test_collect_listnames_and_back(): col1 = py.test.collect.Directory(datadir.dirpath()) col2 = col1.join(datadir.basename) @@ -203,10 +191,10 @@ assert 23 == 23 """) - from py.__.test.collect import getfscollector for x in (o, checkfile, checkfile.dirpath()): + config = py.test.config._reparse([x]) #print "checking that %s returns custom items" % (x,) - col = getfscollector(x) + col = config._getcollector(x) assert len(list(col.tryiter(py.test.Item))) == 2 #assert items[1].__class__.__name__ == 'MyFunction' @@ -250,10 +238,10 @@ """) checkfile = o.ensure('somedir', 'moredir', 'check_something.txt') - from py.__.test.collect import getfscollector for x in (o, checkfile, checkfile.dirpath()): print "checking that %s returns custom items" % (x,) - col = getfscollector(x) + config = py.test.config._reparse([x]) + col = config._getcollector(x) assert len(list(col.tryiter(py.test.Item))) == 1 #assert items[1].__class__.__name__ == 'MyFunction' Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Sat Jan 20 21:43:52 2007 @@ -132,7 +132,7 @@ cls.tmproot = py.test.ensuretemp(cls.__name__) def setup_method(self, method): - self.tmpdir = self.tmproot.mkdir(method.__name__) + self.tmpdir = self.tmproot.ensure(method.__name__, dir=1) def test_sessionname_default(self): config = py.test.config._reparse([self.tmpdir]) @@ -175,3 +175,80 @@ session = config.initsession() assert session.__class__.__name__ == 'MySession' + def test_initsession(self): + config = py.test.config._reparse([self.tmpdir]) + session = config.initsession() + assert session.config is config + +class TestConfigColitems: + def setup_class(cls): + 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.test.config._reparse([self.tmpdir]) + colitems = config.getcolitems() + assert len(colitems) == 1 + col = colitems[0] + assert isinstance(col, py.test.collect.Directory) + for col in col.listchain(): + assert col.config is config + + def test_getcolitems_twodirs(self): + config = py.test.config._reparse([self.tmpdir, self.tmpdir]) + colitems = config.getcolitems() + assert len(colitems) == 2 + col1, col2 = colitems + assert col1.name == col2.name + assert col1.parent == col2.parent + + def test_getcolitems_curdir_and_subdir(self): + a = self.tmpdir.ensure("a", dir=1) + config = py.test.config._reparse([self.tmpdir, a]) + colitems = config.getcolitems() + assert len(colitems) == 2 + col1, col2 = colitems + assert col1.name == self.tmpdir.basename + assert col2.name == 'a' + for col in colitems: + for subcol in col.listchain(): + assert col.config is config + + def test__getcol_global_file(self): + x = self.tmpdir.ensure("x.py") + config = py.test.config._reparse([x]) + col = config._getcollector(x) + 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 + for col in col.listchain(): + assert col.config is config + + def test__getcol_global_dir(self): + x = self.tmpdir.ensure("a", dir=1) + config = py.test.config._reparse([x]) + col = config._getcollector(x) + assert isinstance(col, py.test.collect.Directory) + print col.listchain() + assert col.name == 'a' + assert col.parent is None + assert col.config is config + + def test__getcol_pkgfile(self): + x = self.tmpdir.ensure("x.py") + self.tmpdir.ensure("__init__.py") + config = py.test.config._reparse([x]) + col = config._getcollector(x) + 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 + for col in col.listchain(): + assert col.config is config + + + + Modified: py/branch/config/py/test/testing/test_doctest.py ============================================================================== --- py/branch/config/py/test/testing/test_doctest.py (original) +++ py/branch/config/py/test/testing/test_doctest.py Sat Jan 20 21:43:52 2007 @@ -32,10 +32,10 @@ >>> i-1 4 """)) - from py.__.test.collect import getfscollector for x in (o, checkfile): #print "checking that %s returns custom items" % (x,) - col = getfscollector(x) + config = py.test.config._reparse([x]) + col = config._getcollector(x) items = list(col.tryiter(py.test.Item)) assert len(items) == 1 assert isinstance(items[0], DoctestText) Modified: py/branch/config/py/test/testing/test_session.py ============================================================================== --- py/branch/config/py/test/testing/test_session.py (original) +++ py/branch/config/py/test/testing/test_session.py Sat Jan 20 21:43:52 2007 @@ -5,35 +5,20 @@ mod.datadir = setupdatadir() mod.tmpdir = py.test.ensuretemp(mod.__name__) -class TestDefaultSession: - def test_simple(self): - config = py.test.config._reparse([datadir/'filetest.py']) - session = config._getsessionclass()(config, py.std.sys.stdout) - session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 2 - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert not l - - def test_simple_verbose(self): - config = py.test.config._reparse([datadir/'filetest.py', - '--verbose']) - session = config._getsessionclass()(config, py.std.sys.stdout) - session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 2 - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert not l - - def test_simple_verbose_verbose(self): - config = py.test.config._reparse([datadir/'filetest.py', - '-v', '-v']) - session = config._getsessionclass()(config, py.std.sys.stdout) - session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 2 - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert not l +def test_default_session_options(): + for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], + ['--tb=long'], ['--fulltrace'], ['--nomagic'], + ['--traceconfig'], ['-v'], ['-v', '-v']): + yield runfiletest, opts + +def runfiletest(opts): + config = py.test.config._reparse(opts + [datadir/'filetest.py']) + session = config.initsession() + session.main() + l = session.getitemoutcomepairs(py.test.Item.Failed) + assert len(l) == 2 + l = session.getitemoutcomepairs(py.test.Item.Passed) + assert not l class TestKeywordSelection: def test_select_simple(self): Modified: py/branch/config/py/test/tkinter/backend.py ============================================================================== --- py/branch/config/py/test/tkinter/backend.py (original) +++ py/branch/config/py/test/tkinter/backend.py Sat Jan 20 21:43:52 2007 @@ -184,6 +184,7 @@ self.queue.put(item) def start_tests(self, config = None, args = [], tests = []): + py.test.skip("XXX fix this or remove --tkinter") if self.running: return if config is None: From hpk at codespeak.net Sat Jan 20 21:51:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 20 Jan 2007 21:51:19 +0100 (CET) Subject: [py-svn] r37075 - py/branch/config/py/test Message-ID: <20070120205119.DC32E10069@code0.codespeak.net> Author: hpk Date: Sat Jan 20 21:51:18 2007 New Revision: 37075 Modified: py/branch/config/py/test/config.py Log: remove two debug prints Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Sat Jan 20 21:51:18 2007 @@ -66,12 +66,10 @@ pkgpath = path.pypkgpath() if pkgpath is None: pkgpath = path.check(file=1) and path.dirpath() or path - print pkgpath col = self.conftest.rget("Directory", pkgpath)(pkgpath) col.config = self for name in path.relto(pkgpath).split(path.sep): if name: - print "joining", name, "to", col col = col.join(name) assert col is not None, ( "joining %r to collector %s resulted in None" %(name, col)) From hpk at codespeak.net Sat Jan 20 22:19:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 20 Jan 2007 22:19:44 +0100 (CET) Subject: [py-svn] r37076 - in py/branch/config/py/test: . terminal testing Message-ID: <20070120211944.70DF810078@code0.codespeak.net> Author: hpk Date: Sat Jan 20 22:19:42 2007 New Revision: 37076 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/terminal/out.py py/branch/config/py/test/testing/test_session.py Log: unskip exec and looponfailing tests, fixes for that Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Sat Jan 20 22:19:42 2007 @@ -237,9 +237,6 @@ option.nocapture = True # make information available about wether we should/will be remote - option._remote = remote or option.looponfailing - option._fromremote = False - if option.runbrowser and not option.startserver: #print "Cannot point browser when not starting server" option.startserver = True Modified: py/branch/config/py/test/terminal/out.py ============================================================================== --- py/branch/config/py/test/terminal/out.py (original) +++ py/branch/config/py/test/terminal/out.py Sat Jan 20 22:19:42 2007 @@ -80,8 +80,10 @@ # if file is None: file = py.std.sys.stdout - elif hasattr(file, 'send'): # likely a channel like thing + 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: Modified: py/branch/config/py/test/testing/test_session.py ============================================================================== --- py/branch/config/py/test/testing/test_session.py (original) +++ py/branch/config/py/test/testing/test_session.py Sat Jan 20 22:19:42 2007 @@ -313,7 +313,6 @@ assert rootdir == one.dirpath() def test_exec(self): - py.test.skip("xxx work in progress") o = tmpdir.ensure('remote', dir=1) tfile = o.join('test_exec.py') tfile.write(py.code.Source(""" @@ -335,7 +334,6 @@ py.test.fail("did not see test_1 failure") def test_looponfailing(self): - py.test.skip("xxx work in progress") o = tmpdir.ensure('looponfailing', dir=1) tfile = o.join('test_looponfailing.py') tfile.write(py.code.Source(""" @@ -344,16 +342,16 @@ """)) print py.std.sys.executable config = py.test.config._reparse(['--looponfailing', str(o)]) - assert config.option._remote 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() + s = out.get(timeout=1.0) if s.find('1 failed') != -1: break + print s else: py.test.fail("did not see test_1 failure") # XXX we would like to have a cleaner way to finish From hpk at codespeak.net Sat Jan 20 22:26:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 20 Jan 2007 22:26:53 +0100 (CET) Subject: [py-svn] r37077 - py/branch/config/py/test Message-ID: <20070120212653.E923C10078@code0.codespeak.net> Author: hpk Date: Sat Jan 20 22:26:52 2007 New Revision: 37077 Modified: py/branch/config/py/test/config.py Log: cleaning up fixoptions() a bit Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Sat Jan 20 22:26:52 2007 @@ -214,30 +214,22 @@ def fixoptions(option): """ sanity checks and making option values canonical. """ - if option.looponfailing and option.usepdb: - raise ValueError, "--looponfailing together with --pdb not supported yet." - if option.executable and option.usepdb: - raise ValueError, "--exec together with --pdb not supported yet." - - # setting a correct executable - remote = False - #if option.executable is not None: - # remote = True - # exe = py.path.local(py.std.os.path.expanduser(option.executable)) - # if not exe.check(): - # exe = py.path.local.sysfind(option.executable) - # assert exe.check() - # option.executable = exe - #else: - # option.executable = py.std.sys.executable + # implied options if option.usepdb: if not option.nocapture: - print "--usepdb currently implies --nocapture" + print "--usepdb implies --nocapture" option.nocapture = True - - # make information available about wether we should/will be remote if option.runbrowser and not option.startserver: - #print "Cannot point browser when not starting server" + print "--runbrowser implies --startserver" option.startserver = True - # XXX: Shouldn't we fix the session here? + + # conflicting options + if option.looponfailing and option.usepdb: + raise ValueError, "--looponfailing together with --pdb not supported yet." + if option.looponfailing and option.dist: + raise ValueError, "--looponfailing together with --dist not supported yet." + if option.executable and option.usepdb: + raise ValueError, "--exec together with --pdb not supported yet." + + From guido at codespeak.net Sun Jan 21 17:17:07 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 21 Jan 2007 17:17:07 +0100 (CET) Subject: [py-svn] r37096 - in py/dist/py/apigen: . testing Message-ID: <20070121161707.818921007A@code0.codespeak.net> Author: guido Date: Sun Jan 21 17:17:05 2007 New Revision: 37096 Modified: py/dist/py/apigen/apigen.py py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_htmlgen.py Log: Somewhat larger cleanup/refactoring of both the htmlgen code (stuff is now in classes, which is a start in cleaning up there) and the tests (factored some code out to helper functions). Modified: py/dist/py/apigen/apigen.py ============================================================================== --- py/dist/py/apigen/apigen.py (original) +++ py/dist/py/apigen/apigen.py Sun Jan 21 17:17:05 2007 @@ -36,22 +36,20 @@ all_names = dsa._get_names(filter=lambda x, y: True) namespace_tree = htmlgen.create_namespace_tree(all_names) - ns_data = htmlgen.prepare_namespace_api_pages(l, dsa, targetdir, - namespace_tree) + apb = htmlgen.ApiPageBuilder(targetdir, l, dsa) + spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir) + + ns_data = apb.prepare_namespace_pages(namespace_tree) class_names = dsa.get_class_names() - class_data, method_data = htmlgen.prepare_class_api_pages(l, dsa, - targetdir, - namespace_tree, - class_names) + class_data, method_data = apb.prepare_class_pages(namespace_tree, + class_names) function_names = dsa.get_function_names() - func_data = htmlgen.prepare_function_api_pages(l, dsa, targetdir, - namespace_tree, - function_names) - source_data = htmlgen.prepare_source_pages(l, pkgdir, targetdir) + func_data = apb.prepare_function_pages(namespace_tree, function_names) + source_data = spb.prepare_pages(pkgdir) - htmlgen.build_namespace_api_pages(l, ns_data, proj, targetdir) - htmlgen.build_class_api_pages(l, class_data, proj, targetdir) - htmlgen.build_method_api_pages(l, method_data, proj, targetdir) - htmlgen.build_function_api_pages(l, func_data, proj, targetdir) - htmlgen.build_source_pages(l, source_data, proj, pkgdir, targetdir) + apb.build_namespace_pages(ns_data, proj) + apb.build_class_pages(class_data, proj) + apb.build_method_pages(method_data, proj) + apb.build_function_pages(func_data, proj) + spb.build_pages(source_data, proj, pkgdir) Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Sun Jan 21 17:17:05 2007 @@ -11,6 +11,20 @@ html = py.xml.html raw = py.xml.raw +def create_namespace_tree(dotted_names): + """ creates a tree (in dict form) from a set of dotted names + """ + ret = {} + for dn in dotted_names: + path = dn.split('.') + for i in xrange(len(path)): + ns = '.'.join(path[:i]) + itempath = '.'.join(path[:i + 1]) + if ns not in ret: + ret[ns] = [] + if itempath not in ret[ns]: + ret[ns].append(itempath) + return ret class H(html): class Description(html.div): @@ -70,111 +84,6 @@ # XXX copy and modify formatargspec to produce html return H.em(inspect.formatargspec(*inspect.getargspec(func))) -def create_namespace_tree(dotted_names): - """ creates a tree (in dict form) from a set of dotted names - """ - ret = {} - for dn in dotted_names: - path = dn.split('.') - for i in xrange(len(path)): - ns = '.'.join(path[:i]) - itempath = '.'.join(path[:i + 1]) - if ns not in ret: - ret[ns] = [] - if itempath not in ret[ns]: - ret[ns].append(itempath) - return ret - -def build_full_method_view(base, linker, dsa, dotted_name): - """ build the html for a class method """ - func = dsa.get_obj(dotted_name) - sourcefile = inspect.getsourcefile(func) - params = get_param_htmldesc(linker, func) - docstring = func.__doc__ - local_methodname = func.__name__ - if sourcefile is not None: - sourcelink = H.a('source', href=linker.get_lazyhref(sourcefile)) - else: - sourcelink = H.span('no source file available') - snippet = H.MethodDescription( - H.MethodDef(local_methodname, params, ":"), - sourcelink, - H.Docstring(docstring), - ) - return snippet - -def build_short_method_view(base, linker, dsa, dotted_name): - """ build the html for a class method """ - func = dsa.get_obj(dotted_name) - params = get_param_htmldesc(linker, func) - docstring = func.__doc__ - local_methodname = func.__name__ - snippet = H.MethodDescription( - H.MethodDef(local_methodname, params, ":"), - H.Docstring(docstring), - # XXX more info here... - ) - return snippet - -def build_class_view(base, linker, dsa, dotted_name): - """ build the html for a class """ - cls = dsa.get_obj(dotted_name) - try: - sourcefile = inspect.getsourcefile(cls) - except TypeError: - return H.div('builtin file') - if sourcefile is None: - return H.div('no source available') - if sourcefile[-1] in ['o', 'c']: - sourcefile = sourcefile[:-1] - docstring = cls.__doc__ - methods = dsa.get_class_methods(dotted_name) - basehtml = [] - bases = dsa.get_possible_base_classes(dotted_name) - for base in bases: - try: - obj = dsa.get_obj(base.name) - except KeyError: - basehtml.append(base.name) - else: - href = linker.get_lazyhref(base.name) - basehtml.append(H.BaseDescription(base.name, href=href)) - basehtml.append(',') - if basehtml: - basehtml.pop() - basehtml.append('):') - snippet = H.ClassDescription( - # XXX bases HTML - H.ClassDef('%s(' % (cls.__name__,), *basehtml), - H.a('source', href=linker.get_lazyhref(sourcefile)), - H.Docstring(docstring), - ) - snippet.append(H.h2('Functions:')) - for method in methods: - snippet += build_short_method_view(base, linker, dsa, - '%s.%s' % (dotted_name, method)) - return snippet - -def build_namespace_view(base, linker, dsa, namespace_dotted_name, - item_dotted_names): - """ build the html for a namespace (module) """ - snippet = H.NamespaceDescription( - H.NamespaceDef(namespace_dotted_name), - ) - for dotted_name in item_dotted_names: - itemname = dotted_name.split('.')[-1] - if is_private(itemname): - continue - snippet.append( - H.NamespaceItem( - H.a(itemname, - href=linker.get_lazyhref(dotted_name) - ) - ) - ) - return snippet - -# XXX nav functions need cleanup!! def build_navitem_html(linker, name, linkid, indent, selected): href = linker.get_lazyhref(linkid) navitem = H.NavigationItem((indent * 2 * u'\xa0'), H.a(name, href=href)) @@ -182,38 +91,6 @@ navitem.attr.class_ = 'selected' return navitem -def build_api_navigation(linker, dsa, dotted_name, item_dotted_names, - selection): - navitems = [] - - # top namespace, index.html - module_name = dsa.get_module_name().split('/')[-1] - navitems.append(build_navitem_html(linker, module_name, '', 0, - (selection == ''))) - - indent = 1 - path = dotted_name.split('.') - if dotted_name != '': - # build html for each item in path to dotted_name item - for i in xrange(len(path)): - name = path[i] - item_dotted_name = '.'.join(path[:i+1]) - selected = (selection == item_dotted_name) - navitems.append(build_navitem_html(linker, name, - item_dotted_name, indent, - selected)) - indent += 1 - - # build sub items of dotted_name item - for item_dotted_name in py.builtin.sorted(item_dotted_names): - itemname = item_dotted_name.split('.')[-1] - if is_private(itemname): - continue - selected = (item_dotted_name == selection) - navitems.append(build_navitem_html(linker, itemname, - item_dotted_name, indent, selected)) - return H.Navigation(*navitems) - def source_dirs_files(fspath): """ returns a tuple (dirs, files) for fspath @@ -237,250 +114,364 @@ files.append(child) return sorted(dirs), sorted(files) -def build_source_navigation(linker, projroot, fspath, outputpath): - nav = H.Navigation() - relpath = fspath.relto(projroot) - path = relpath.split('/') - indent = 0 - # build links to parents - for i in xrange(len(path)): - dirpath = '/'.join(path[:i]) - abspath = projroot.join(dirpath).strpath - if i == 0: - text = 'root' - else: - text = path[i-1] - nav.append(build_navitem_html(linker, text, abspath, indent, False)) - indent += 1 - # build siblings or children and self - if fspath.check(dir=True): - # we're a dir, build ourselves and our children - dirpath = fspath - nav.append(build_navitem_html(linker, dirpath.basename, - dirpath.strpath, indent, True)) - indent += 1 - elif fspath.strpath == projroot.strpath: - dirpath = fspath - else: - # we're a file, build our parent's children only - dirpath = fspath.dirpath() - diritems, fileitems = source_dirs_files(dirpath) - for dir in diritems: - nav.append(build_navitem_html(linker, dir.basename, dir.strpath, - indent, False)) - for file in fileitems: - selected = (fspath.check(file=True) and - file.basename == fspath.basename) - nav.append(build_navitem_html(linker, file.basename, file.strpath, - indent, selected)) - return nav - -re = py.std.re -_reg_body = re.compile(r']*>(.*)', re.S) -def build_source_python_page(linker, projroot, fspath, outputpath): - mod = source_browser.parse_path(fspath) - # XXX let's cheat a bit here... there should be a different function using - # the linker, and returning a proper py.xml.html element, at some point - html = source_html.create_html(mod) - snippet = _reg_body.search(html).group(1) - tag = H.SourceDef(raw(snippet)) - nav = build_source_navigation(linker, projroot, fspath, outputpath) - return tag, nav - -def build_source_dir_page(linker, projroot, fspath, outputpath): - tag = H.DirList() - for path in fspath.listdir(): - if path.ext in ['.pyc', '.pyo'] or path.basename.startswith('.'): - continue - tag.append(H.DirListItem(H.a(path.basename, - href=linker.get_lazyhref(str(path))))) - nav = build_source_navigation(linker, projroot, fspath, outputpath) - return tag, nav - -def build_source_nonpython_page(linker, projroot, fspath, outputpath): - try: - tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8')) - except UnicodeError: - tag = H.NonPythonSource('no source available (binary file?)') - nav = build_source_navigation(linker, projroot, fspath, outputpath) - return tag, nav - -# the build_*_pages methods all do two-pass page generation (first pass builds -# the link db, second uses it) -def prepare_source_pages(linker, base, outputbase): - passed = [] - for fspath in [base] + list(base.visit()): - if fspath.ext in ['.pyc', '.pyo']: - continue - relfspath = fspath.relto(base) - if relfspath.find('/.') > -1: - # skip hidden dirs and files - continue - elif fspath.check(dir=True): - if relfspath != '': - relfspath += '/' - reloutputpath = 'source/%sindex.html' % (relfspath,) - else: - reloutputpath = "source/%s.html" % (relfspath,) - outputpath = outputbase.join(reloutputpath) - linker.set_link(str(fspath), reloutputpath) - passed.append((fspath, outputpath)) - return passed - -def build_source_pages(linker, data, project, projroot, outputbase): - """ build syntax-colored source views """ - for fspath, outputpath in data: - if fspath.check(ext='.py'): - try: - tag, nav = build_source_python_page(linker, projroot, fspath, - outputpath) - except (KeyboardInterrupt, SystemError): - raise - except: # XXX strange stuff going wrong at times... need to fix - exc, e, tb = py.std.sys.exc_info() - print '%s - %s' % (exc, e) - print - print ''.join(py.std.traceback.format_tb(tb)) - print '-' * 79 - del tb - tag, nav = build_source_nonpython_page(linker, projroot, - fspath, outputpath) - elif fspath.check(dir=True): - tag, nav = build_source_dir_page(linker, projroot, fspath, - outputpath) - else: - tag, nav = build_source_nonpython_page(linker, projroot, fspath, - outputpath) - stylesheeturl = relpath('%s/' % (outputpath.dirpath(),), - '%s' % (outputbase.join('style.css'),)) - page = wrap_page(project, 'sources for %s' % (fspath.basename,), - tag, nav, outputbase, stylesheeturl) - outputpath.ensure() - reltargetpath = outputpath.relto(outputbase) - content = linker.call_withbase(reltargetpath, page.unicode) - outputpath.write(content.encode("utf8")) - -def prepare_class_api_pages(linker, dsa, base, namespace_tree, - classes_dotted_names): - passed = [] - methodsdata = [] - for dotted_name in classes_dotted_names: - parent_dotted_name, _ = split_of_last_part(dotted_name) - try: - sibling_dotted_names = namespace_tree[parent_dotted_name] - except KeyError: - sibling_dotted_names = [] # no siblings (built-in module or sth) - tag = build_class_view(base, linker, dsa, dotted_name) - nav = build_api_navigation(linker, dsa, parent_dotted_name, - sibling_dotted_names, dotted_name) - reltargetpath = "api/%s.html" % (dotted_name,) - linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) - method_dotted_names = ['%s.%s' % (dotted_name, method_name) for - method_name in - dsa.get_class_methods(dotted_name)] - methodsdata += prepare_method_api_pages(linker, dsa, base, - namespace_tree, - method_dotted_names) - return passed, methodsdata - -def build_class_api_pages(linker, data, project, outputbase): - """ build the full api pages for a set of classes """ - for dotted_name, tag, nav, reltargetpath in data: - targetpath = outputbase.ensure(reltargetpath) - stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), - outputbase.join('style.css').strpath) - page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav, outputbase, stylesheeturl) - content = linker.call_withbase(reltargetpath, page.unicode) - targetpath.write(content.encode("utf8")) - -def prepare_method_api_pages(linker, dsa, base, namespace_tree, - method_dotted_names): - # XXX note that even though these pages are still built, there's no nav - # pointing to them anymore... - passed = [] - for dotted_name in method_dotted_names: - parent_dotted_name, _ = split_of_last_part(dotted_name) - module_dotted_name, _ = split_of_last_part(parent_dotted_name) - sibling_dotted_names = namespace_tree[module_dotted_name] - tag = build_full_method_view(base, linker, dsa, dotted_name) - nav = build_api_navigation(linker, dsa, parent_dotted_name, - sibling_dotted_names, dotted_name) - reltargetpath = "api/%s.html" % (dotted_name,) - linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) - return passed - -def build_method_api_pages(linker, data, project, outputbase): - for dotted_name, tag, nav, reltargetpath in data: - targetpath = outputbase.join(reltargetpath) +class AbstractPageBuilder(object): + def write_page(self, title, reltargetpath, project, tag, nav): + targetpath = self.base.join(reltargetpath) stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), - outputbase.join('style.css').strpath) - page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav, outputbase, stylesheeturl) - content = linker.call_withbase(reltargetpath, page.unicode) + self.base.join('style.css').strpath) + page = wrap_page(project, title, + tag, nav, self.base, stylesheeturl) + content = self.linker.call_withbase(reltargetpath, page.unicode) targetpath.ensure() targetpath.write(content.encode("utf8")) -def prepare_function_api_pages(linker, dsa, base, namespace_tree, - method_dotted_names): - passed = [] - for dotted_name in method_dotted_names: - # XXX should we create a build_full_function_view instead? - parent_dotted_name, _ = split_of_last_part(dotted_name) - sibling_dotted_names = namespace_tree[parent_dotted_name] - tag = build_full_method_view(base, linker, dsa, dotted_name) - nav = build_api_navigation(linker, dsa, parent_dotted_name, - sibling_dotted_names, dotted_name) - reltargetpath = "api/%s.html" % (dotted_name,) - linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) - return passed - -def build_function_api_pages(linker, data, project, outputbase): - for dotted_name, tag, nav, reltargetpath in data: - targetpath = outputbase.join(reltargetpath) - stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), - outputbase.join('style.css').strpath) - page = wrap_page(project, 'api documentation for %s' % (dotted_name,), - tag, nav, outputbase, stylesheeturl) - content = linker.call_withbase(reltargetpath, page.unicode) - targetpath.ensure() - targetpath.write(content.encode("utf8")) +class SourcePageBuilder(AbstractPageBuilder): + """ builds the html for a source docs page """ + def __init__(self, base, linker, projroot): + self.base = base + self.linker = linker + self.projroot = projroot + + def build_navigation(self, fspath): + nav = H.Navigation() + relpath = fspath.relto(self.projroot) + path = relpath.split('/') + indent = 0 + # build links to parents + for i in xrange(len(path)): + dirpath = '/'.join(path[:i]) + abspath = self.projroot.join(dirpath).strpath + if i == 0: + text = 'root' + else: + text = path[i-1] + nav.append(build_navitem_html(self.linker, text, abspath, + indent, False)) + indent += 1 + # build siblings or children and self + if fspath.check(dir=True): + # we're a dir, build ourselves and our children + dirpath = fspath + nav.append(build_navitem_html(self.linker, dirpath.basename, + dirpath.strpath, indent, True)) + indent += 1 + elif fspath.strpath == self.projroot.strpath: + dirpath = fspath + else: + # we're a file, build our parent's children only + dirpath = fspath.dirpath() + diritems, fileitems = source_dirs_files(dirpath) + for dir in diritems: + nav.append(build_navitem_html(self.linker, dir.basename, + dir.strpath, indent, False)) + for file in fileitems: + selected = (fspath.check(file=True) and + file.basename == fspath.basename) + nav.append(build_navitem_html(self.linker, file.basename, + file.strpath, indent, selected)) + return nav + + re = py.std.re + _reg_body = re.compile(r']*>(.*)', re.S) + def build_python_page(self, fspath): + mod = source_browser.parse_path(fspath) + # XXX let's cheat a bit here... there should be a different function + # using the linker, and returning a proper py.xml.html element, + # at some point + html = source_html.create_html(mod) + snippet = self._reg_body.search(html).group(1) + tag = H.SourceDef(raw(snippet)) + nav = self.build_navigation(fspath) + return tag, nav + + def build_dir_page(self, fspath): + tag = H.DirList() + dirs, files = source_dirs_files(fspath) + tag.append(H.h2('directories')) + for path in dirs: + tag.append(H.DirListItem(H.a(path.basename, + href=self.linker.get_lazyhref(str(path))))) + tag.append(H.h2('files')) + for path in files: + tag.append(H.DirListItem(H.a(path.basename, + href=self.linker.get_lazyhref(str(path))))) + nav = self.build_navigation(fspath) + return tag, nav -def prepare_namespace_api_pages(linker, dsa, base, namespace_tree): - passed = [] - module_name = dsa.get_module_name().split('/')[-1] - - names = namespace_tree.keys() - names.sort() - for dotted_name in names: - subitem_dotted_names = namespace_tree[dotted_name] - tag = build_namespace_view(base, linker, dsa, dotted_name, - subitem_dotted_names) - nav = build_api_navigation(linker, dsa, dotted_name, - subitem_dotted_names, dotted_name) - if dotted_name == '': - reltargetpath = 'api/index.html' + def build_nonpython_page(self, fspath): + try: + tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8')) + except UnicodeError: + tag = H.NonPythonSource('no source available (binary file?)') + nav = self.build_navigation(fspath) + return tag, nav + + def prepare_pages(self, base): + passed = [] + for fspath in [base] + list(base.visit()): + if fspath.ext in ['.pyc', '.pyo']: + continue + relfspath = fspath.relto(base) + if relfspath.find('/.') > -1: + # skip hidden dirs and files + continue + elif fspath.check(dir=True): + if relfspath != '': + relfspath += '/' + reloutputpath = 'source/%sindex.html' % (relfspath,) + else: + reloutputpath = "source/%s.html" % (relfspath,) + outputpath = self.base.join(reloutputpath) + self.linker.set_link(str(fspath), reloutputpath) + passed.append((fspath, outputpath)) + return passed + + def build_pages(self, data, project, base): + """ build syntax-colored source views """ + for fspath, outputpath in data: + if fspath.check(ext='.py'): + try: + tag, nav = self.build_python_page(fspath) + except (KeyboardInterrupt, SystemError): + raise + except: # XXX strange stuff going wrong at times... need to fix + exc, e, tb = py.std.sys.exc_info() + print '%s - %s' % (exc, e) + print + print ''.join(py.std.traceback.format_tb(tb)) + print '-' * 79 + del tb + tag, nav = self.build_nonpython_page(fspath) + elif fspath.check(dir=True): + tag, nav = self.build_dir_page(fspath) + else: + tag, nav = self.build_nonpython_page(fspath) + title = 'sources for %s' % (fspath.basename,) + reltargetpath = outputpath.relto(self.base) + self.write_page(title, reltargetpath, project, tag, nav) + +class ApiPageBuilder(AbstractPageBuilder): + """ builds the html for an api docs page """ + def __init__(self, base, linker, dsa): + self.base = base + self.linker = linker + self.dsa = dsa + + def build_full_method_view(self, dotted_name): + """ build the html for a class method """ + func = self.dsa.get_obj(dotted_name) + sourcefile = inspect.getsourcefile(func) + params = get_param_htmldesc(self.linker, func) + docstring = func.__doc__ + local_methodname = func.__name__ + if sourcefile is not None: + sourcelink = H.a('source', + href=self.linker.get_lazyhref(sourcefile)) else: - reltargetpath = 'api/%s.html' % (dotted_name,) - linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) - return passed - -def build_namespace_api_pages(linker, data, project, outputbase): - for dotted_name, tag, nav, reltargetpath in data: - if dotted_name == '': - dotted_name = 'project root' - targetpath = outputbase.join(reltargetpath) - stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), - outputbase.join('style.css').strpath) - page = wrap_page(project, 'index of %s namespace' % (dotted_name,), - tag, nav, outputbase, stylesheeturl) - content = linker.call_withbase(reltargetpath, page.unicode) - targetpath.ensure() - targetpath.write(content.encode("utf8")) + sourcelink = H.span('no source file available') + snippet = H.MethodDescription( + H.MethodDef(local_methodname, params, ":"), + sourcelink, + H.Docstring(docstring), + ) + return snippet + + def build_short_method_view(self, dotted_name): + """ build the html for a class method """ + func = self.dsa.get_obj(dotted_name) + params = get_param_htmldesc(self.linker, func) + docstring = func.__doc__ + local_methodname = func.__name__ + snippet = H.MethodDescription( + H.MethodDef(local_methodname, params, ":"), + H.Docstring(docstring), + # XXX more info here... + ) + return snippet + + def build_class_view(self, dotted_name): + """ build the html for a class """ + cls = self.dsa.get_obj(dotted_name) + try: + sourcefile = inspect.getsourcefile(cls) + except TypeError: + return H.div('builtin file') + if sourcefile is None: + return H.div('no source available') + if sourcefile[-1] in ['o', 'c']: + sourcefile = sourcefile[:-1] + docstring = cls.__doc__ + methods = self.dsa.get_class_methods(dotted_name) + basehtml = [] + bases = self.dsa.get_possible_base_classes(dotted_name) + for base in bases: + try: + obj = self.dsa.get_obj(base.name) + except KeyError: + basehtml.append(base.name) + else: + href = self.linker.get_lazyhref(base.name) + basehtml.append(H.BaseDescription(base.name, href=href)) + basehtml.append(',') + if basehtml: + basehtml.pop() + basehtml.append('):') + snippet = H.ClassDescription( + # XXX bases HTML + H.ClassDef('%s(' % (cls.__name__,), *basehtml), + H.a('source', href=self.linker.get_lazyhref(sourcefile)), + H.Docstring(docstring), + ) + snippet.append(H.h2('Functions:')) + for method in methods: + snippet += self.build_short_method_view('%s.%s' % (dotted_name, + method)) + return snippet + + def build_namespace_view(self, namespace_dotted_name, item_dotted_names): + """ build the html for a namespace (module) """ + snippet = H.NamespaceDescription( + H.NamespaceDef(namespace_dotted_name), + ) + for dotted_name in item_dotted_names: + itemname = dotted_name.split('.')[-1] + if is_private(itemname): + continue + snippet.append( + H.NamespaceItem( + H.a(itemname, + href=self.linker.get_lazyhref(dotted_name) + ) + ) + ) + return snippet + + def prepare_class_pages(self, namespace_tree, classes_dotted_names): + passed = [] + methodsdata = [] + for dotted_name in classes_dotted_names: + parent_dotted_name, _ = split_of_last_part(dotted_name) + try: + sibling_dotted_names = namespace_tree[parent_dotted_name] + except KeyError: + # no siblings (built-in module or sth) + sibling_dotted_names = [] + tag = self.build_class_view(dotted_name) + nav = self.build_navigation(parent_dotted_name, + sibling_dotted_names, dotted_name) + reltargetpath = "api/%s.html" % (dotted_name,) + self.linker.set_link(dotted_name, reltargetpath) + passed.append((dotted_name, tag, nav, reltargetpath)) + method_dotted_names = ['%s.%s' % (dotted_name, method_name) for + method_name in + self.dsa.get_class_methods(dotted_name)] + methodsdata += self.prepare_method_pages(namespace_tree, + method_dotted_names) + return passed, methodsdata + + def build_class_pages(self, data, project): + """ build the full api pages for a set of classes """ + for dotted_name, tag, nav, reltargetpath in data: + title = 'api documentation for %s' % (dotted_name,) + self.write_page(title, reltargetpath, project, tag, nav) + + def prepare_method_pages(self, namespace_tree, method_dotted_names): + # XXX note that even though these pages are still built, there's no nav + # pointing to them anymore... + passed = [] + for dotted_name in method_dotted_names: + parent_dotted_name, _ = split_of_last_part(dotted_name) + module_dotted_name, _ = split_of_last_part(parent_dotted_name) + sibling_dotted_names = namespace_tree[module_dotted_name] + tag = self.build_full_method_view(dotted_name) + nav = self.build_navigation(parent_dotted_name, + sibling_dotted_names, dotted_name) + reltargetpath = "api/%s.html" % (dotted_name,) + self.linker.set_link(dotted_name, reltargetpath) + passed.append((dotted_name, tag, nav, reltargetpath)) + return passed + + def build_method_pages(self, data, project): + for dotted_name, tag, nav, reltargetpath in data: + title = 'api documentation for %s' % (dotted_name,) + self.write_page(title, reltargetpath, project, tag, nav) + + def prepare_function_pages(self, namespace_tree, method_dotted_names): + passed = [] + for dotted_name in method_dotted_names: + # XXX should we create a build_full_function_view instead? + parent_dotted_name, _ = split_of_last_part(dotted_name) + sibling_dotted_names = namespace_tree[parent_dotted_name] + tag = self.build_full_method_view(dotted_name) + nav = self.build_navigation(parent_dotted_name, + sibling_dotted_names, dotted_name) + reltargetpath = "api/%s.html" % (dotted_name,) + self.linker.set_link(dotted_name, reltargetpath) + passed.append((dotted_name, tag, nav, reltargetpath)) + return passed + + def build_function_pages(self, data, project): + for dotted_name, tag, nav, reltargetpath in data: + title = 'api documentation for %s' % (dotted_name,) + self.write_page(title, reltargetpath, project, tag, nav) + + def prepare_namespace_pages(self, namespace_tree): + passed = [] + module_name = self.dsa.get_module_name().split('/')[-1] + + names = namespace_tree.keys() + names.sort() + for dotted_name in names: + subitem_dotted_names = namespace_tree[dotted_name] + tag = self.build_namespace_view(dotted_name, subitem_dotted_names) + nav = self.build_navigation(dotted_name, subitem_dotted_names, + dotted_name) + if dotted_name == '': + reltargetpath = 'api/index.html' + else: + reltargetpath = 'api/%s.html' % (dotted_name,) + self.linker.set_link(dotted_name, reltargetpath) + passed.append((dotted_name, tag, nav, reltargetpath)) + return passed + + def build_namespace_pages(self, data, project): + for dotted_name, tag, nav, reltargetpath in data: + if dotted_name == '': + dotted_name = 'project root' + title = 'index of %s namespace' % (dotted_name,) + self.write_page(title, reltargetpath, project, tag, nav) + + def build_navigation(self, dotted_name, item_dotted_names, selection): + navitems = [] + + # top namespace, index.html + module_name = self.dsa.get_module_name().split('/')[-1] + navitems.append(build_navitem_html(self.linker, module_name, '', 0, + (selection == ''))) + + indent = 1 + path = dotted_name.split('.') + if dotted_name != '': + # build html for each item in path to dotted_name item + for i in xrange(len(path)): + name = path[i] + item_dotted_name = '.'.join(path[:i+1]) + selected = (selection == item_dotted_name) + navitems.append(build_navitem_html(self.linker, name, + item_dotted_name, indent, + selected)) + indent += 1 + + # build sub items of dotted_name item + for item_dotted_name in py.builtin.sorted(item_dotted_names): + itemname = item_dotted_name.split('.')[-1] + if is_private(itemname): + continue + selected = (item_dotted_name == selection) + navitems.append(build_navitem_html(self.linker, itemname, + item_dotted_name, indent, + selected)) + return H.Navigation(*navitems) def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Sun Jan 21 17:17:05 2007 @@ -7,6 +7,17 @@ from py.__.test.web import webcheck from py.__.apigen.conftest import option +def run_string_sequence_test(data, seq): + currpos = -1 + for s in seq: + newpos = data.find(s) + if currpos >= newpos: + if newpos == -1: + message = 'not found' + else: + message = 'unexpected position: %s' % (newpos,) + py.test.fail('string %r: %s' % (s, message)) + def setup_fs_project(tempname='apigen_example'): temp = py.test.ensuretemp(tempname) temp.ensure("pkg/func.py").write(py.code.Source("""\ @@ -49,7 +60,9 @@ return dsa def _checkhtml(htmlstring): - assert isinstance(htmlstring, (unicode, str)) + if isinstance(htmlstring, unicode): + htmlstring = htmlstring.encode('UTF-8', 'replace') + assert isinstance(htmlstring, str) if option.webcheck: webcheck.check_html(htmlstring) else: @@ -63,7 +76,7 @@ #"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n""" + unicode(h) #_checkhtml(newstring) -class TestExampleProject(object): +class AbstractBuilderTest(object): def setup_class(cls): cls.fs_root, cls.pkg_name = setup_fs_project() cls.dsa = get_dsa(cls.fs_root, cls.pkg_name) @@ -76,218 +89,164 @@ return super(LinkerForTests, self).get_target(linkid) except KeyError: return 'unknown_link_%s' % (linkid,) - self.linker = LinkerForTests() + self.base = base = py.test.ensuretemp('%s_%s' % ( + self.__class__.__name__, meth.im_func.func_name)) + self.linker = linker = LinkerForTests() + self.apb = ApiPageBuilder(base, linker, self.dsa) + self.spb = SourcePageBuilder(base, linker, self.fs_root) +class TestApiPageBuilder(AbstractBuilderTest): def test_build_full_method_view(self): - base = py.test.ensuretemp('build_full_method_view') - snippet = build_full_method_view(base, self.linker, self.dsa, - 'pkg.sub.func') + snippet = self.apb.build_full_method_view('pkg.sub.func') html = snippet.unicode() _checkhtmlsnippet(html) - def test_build_function_api_pages(self): - base = py.test.ensuretemp('build_function_api_pages') + def test_build_function_pages(self): namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - data = prepare_function_api_pages(self.linker, self.dsa, - base, namespace_tree, - ['pkg.sub.func']) - build_function_api_pages(self.linker, data, self.project, base) - funcfile = base.join('api/pkg.sub.func.html') + data = self.apb.prepare_function_pages(namespace_tree, + ['pkg.sub.func']) + self.apb.build_function_pages(data, self.project) + funcfile = self.base.join('api/pkg.sub.func.html') assert funcfile.check() html = funcfile.read() _checkhtml(html) def test_build_class_view(self): - base = py.test.ensuretemp('build_class_view') - snippet = build_class_view(base, self.linker, self.dsa, - 'pkg.SomeClass') + snippet = self.apb.build_class_view('pkg.SomeClass') html = snippet.unicode() _checkhtmlsnippet(html) - def test_build_class_api_pages(self): - base = py.test.ensuretemp('build_class_api_pages') + def test_build_class_pages(self): namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) - data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, namespace_tree, - ['pkg.SomeClass', - 'pkg.SomeSubClass']) - build_class_api_pages(self.linker, data, self.project, base) - clsfile = base.join('api/pkg.SomeClass.html') + data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + ['pkg.SomeClass', + 'pkg.SomeSubClass']) + self.apb.build_class_pages(data, self.project) + clsfile = self.base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() _checkhtml(html) - def test_build_class_api_pages_nav_links(self): - base = py.test.ensuretemp('build_class_api_pages_nav_links') + def test_build_class_pages_nav_links(self): namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) - data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, namespace_tree, - ['pkg.SomeSubClass', 'pkg.SomeClass']) + data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + ['pkg.SomeSubClass', + 'pkg.SomeClass']) # fake some stuff that would be built from other methods self.linker.set_link('', 'api/index.html') self.linker.set_link('pkg', 'api/pkg.html') - build_class_api_pages(self.linker, data, self.project, base) - clsfile = base.join('api/pkg.SomeClass.html') + self.apb.build_class_pages(data, self.project) + clsfile = self.base.join('api/pkg.SomeClass.html') assert clsfile.check() html = clsfile.read() print html # note that the root and the first level both have the same name (pkg) # in our example project (XXX fixme!!) - assert 'href="../style.css"' in html - assert 'href="index.html">pkg' in html - assert 'href="pkg.html">pkg' in html - assert 'href="pkg.SomeClass.html">SomeClass' in html - assert 'href="pkg.SomeSubClass.html">SomeSubClass' in html + run_string_sequence_test(html, [ + 'href="../style.css"', + 'href="index.html">pkg', + 'href="pkg.html">pkg', + 'href="pkg.SomeClass.html">SomeClass', + 'href="pkg.SomeSubClass.html">SomeSubClass', + ]) assert not 'href="pkg.sub.func.html"' in html py.test.skip('WOP from here') assert 'href="pkg.sub.html">sub' in html _checkhtml(html) - def test_build_class_api_pages_base_link(self): - base = py.test.ensuretemp('build_class_api_pages_base_link') + def test_build_class_pages_base_link(self): namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) - data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, namespace_tree, - ['pkg.SomeSubClass', 'pkg.SomeClass']) - build_class_api_pages(self.linker, data, self.project, base) - clsfile = base.join('api/pkg.SomeSubClass.html') + data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + ['pkg.SomeSubClass', + 'pkg.SomeClass']) + self.apb.build_class_pages(data, self.project) + clsfile = self.base.join('api/pkg.SomeSubClass.html') assert clsfile.check() html = clsfile.read() print html - assert 'href="../style.css"' in html - assert 'href="pkg.SomeClass.html">pkg.SomeClass' in html + run_string_sequence_test(html, [ + 'href="../style.css"', + 'href="pkg.SomeClass.html">pkg.SomeClass', + ]) _checkhtml(html) - def test_build_source_pages(self): - base = py.test.ensuretemp('build_source_pages') - data = prepare_source_pages(self.linker, self.fs_root, base) - build_source_pages(self.linker, data, self.project, self.fs_root, base) - somesource = base.join('source/pkg/func.py.html').read() - _checkhtml(somesource) - - def test_build_source_pages_nav(self): - base = py.test.ensuretemp('build_source_pages_nav') - data = prepare_source_pages(self.linker, self.fs_root, base) - build_source_pages(self.linker, data, self.project, self.fs_root, base) - funcsource = base.join('source/pkg/func.py.html') - assert funcsource.check(file=True) - html = funcsource.read() - print html - assert 'href="../../style.css"' in html - assert 'root' in html - assert 'pkg' in html - assert 'someclass.py' in html - assert 'somesubclass.py' in html - - def test_api_source_links(self): - base = py.test.ensuretemp('api_source_links') + def test_source_links(self): namespace_tree = create_namespace_tree(['pkg.SomeClass', 'pkg.SomeSubClass', 'pkg.sub.func']) - data, methodsdata = prepare_class_api_pages(self.linker, self.dsa, - base, namespace_tree, - ['pkg.SomeSubClass', 'pkg.SomeClass']) - sourcedata = prepare_source_pages(self.linker, self.fs_root, base) - build_class_api_pages(self.linker, data, self.project, base) - build_source_pages(self.linker, sourcedata, self.project, self.fs_root, - base) - funchtml = base.join('api/pkg.SomeClass.html').read() + data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + ['pkg.SomeSubClass', + 'pkg.SomeClass']) + sourcedata = self.spb.prepare_pages(self.fs_root) + self.apb.build_class_pages(data, self.project) + self.spb.build_pages(sourcedata, self.project, self.fs_root) + funchtml = self.base.join('api/pkg.SomeClass.html').read() assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) - def test_create_namespace_tree(self): - namespace_tree = create_namespace_tree(['pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) - assert namespace_tree == {'': ['pkg'], - 'pkg.sub': ['pkg.sub.func'], - 'pkg': ['pkg.sub', 'pkg.SomeClass', - 'pkg.SomeSubClass']} - - def test_build_namespace_api_pages_index(self): - base = py.test.ensuretemp('build_namespace_api_pages') + def test_build_namespace_pages_index(self): namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - data = prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) - prepare_class_api_pages(self.linker, self.dsa, base, - namespace_tree, - ['pkg.SomeClass', 'pkg.SomeSubClass']) - prepare_function_api_pages(self.linker, self.dsa, base, - namespace_tree, ['pkg.sub.func']) - prepare_source_pages(self.linker, self.fs_root, base) - build_namespace_api_pages(self.linker, data, self.project, base) - pkgfile = base.join('api/pkg.html') + data = self.apb.prepare_namespace_pages(namespace_tree) + self.apb.prepare_class_pages(namespace_tree, + ['pkg.SomeClass', + 'pkg.SomeSubClass']) + self.apb.prepare_function_pages(namespace_tree, ['pkg.sub.func']) + self.apb.build_namespace_pages(data, self.project) + pkgfile = self.base.join('api/pkg.html') assert pkgfile.check() html = pkgfile.read() _checkhtml(html) - def test_build_namespace_api_pages_subnamespace(self): - base = py.test.ensuretemp('build_namespace_api_pages') + def test_build_namespace_pages_subnamespace(self): namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - data = prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) - build_namespace_api_pages(self.linker, data, self.project, base) - subfile = base.join('api/pkg.sub.html') + data = self.apb.prepare_namespace_pages(namespace_tree) + self.apb.build_namespace_pages(data, self.project) + subfile = self.base.join('api/pkg.sub.html') assert subfile.check() html = subfile.read() _checkhtml(html) - def test_build_source_dir_page(self): - base = py.test.ensuretemp('build_source_dir_page') - data = prepare_source_pages(self.linker, self.fs_root, base) - build_source_pages(self.linker, data, self.project, self.fs_root, base) - pkgindex = base.join('source/pkg/index.html') - assert pkgindex.check(file=True) - html = pkgindex.read() - print html - assert 'someclass.py' in html - _checkhtml(html) - def test_build_function_api_pages_nav(self): - base = py.test.ensuretemp('build_function_api_pages_nav') - prepare_source_pages(self.linker, self.fs_root, base) namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - data = prepare_function_api_pages(self.linker, self.dsa, - base, namespace_tree, - ['pkg.sub.func']) + data = self.apb.prepare_function_pages(namespace_tree, + ['pkg.sub.func']) self.linker.set_link('', 'api/index.html') self.linker.set_link('pkg', 'api/pkg.html') self.linker.set_link('pkg.sub', 'api/pkg.sub.html') - build_function_api_pages(self.linker, data, self.project, base) - funcfile = base.join('api/pkg.sub.func.html') + self.apb.build_function_pages(data, self.project) + funcfile = self.base.join('api/pkg.sub.func.html') html = funcfile.read() print html - assert '' in html - assert '' in html - assert '' in html - assert '' in html + run_string_sequence_test(html, [ + '', + '', + '', + '', + ]) + _checkhtml(html) def test_build_function_navigation(self): - base = py.test.ensuretemp('build_function_navigation') - prepare_source_pages(self.linker, self.fs_root, base) namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) - prepare_function_api_pages(self.linker, self.dsa, base, - namespace_tree, ['pkg.sub.func']) - nav = build_api_navigation(self.linker, self.dsa, 'pkg.sub', - ['pkg.sub.func'], 'pkg.sub.func') + self.apb.prepare_namespace_pages(namespace_tree) + self.apb.prepare_function_pages(namespace_tree, ['pkg.sub.func']) + nav = self.apb.build_navigation('pkg.sub', ['pkg.sub.func'], + 'pkg.sub.func') html = nav.unicode(indent=0) print html.encode('UTF-8') assert (u'' @@ -299,29 +258,67 @@ ) in html def test_build_root_namespace_view(self): - base = py.test.ensuretemp('build_root_namespace_view') namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', 'pkg.SomeClass', 'pkg.SomeSubClass']) - data = prepare_namespace_api_pages(self.linker, self.dsa, base, - namespace_tree) - build_namespace_api_pages(self.linker, data, self.project, base) - rootfile = base.join('api/index.html') + data = self.apb.prepare_namespace_pages(namespace_tree) + self.apb.build_namespace_pages(data, self.project) + rootfile = self.base.join('api/index.html') assert rootfile.check() html = rootfile.read() assert '' in html _checkhtml(html) - def test_build_source_navigation_root(self): - base = py.test.ensuretemp('test_build_source_navigation') - prepare_source_pages(self.linker, self.fs_root, base) - nav = build_source_navigation(self.linker, self.fs_root, - self.fs_root.join('pkg'), base) +class TestSourcePageBuilder(AbstractBuilderTest): + def test_build_pages(self): + data = self.spb.prepare_pages(self.fs_root) + self.spb.build_pages(data, self.project, self.fs_root) + somesource = self.base.join('source/pkg/func.py.html').read() + _checkhtml(somesource) + + def test_build_pages_nav(self): + data = self.spb.prepare_pages(self.fs_root) + self.spb.build_pages(data, self.project, self.fs_root) + funcsource = self.base.join('source/pkg/func.py.html') + assert funcsource.check(file=True) + html = funcsource.read() + print html + run_string_sequence_test(html, [ + 'href="../../style.css"', + 'root', + 'pkg', + 'someclass.py', + 'somesubclass.py', + ]) + + def test_build_dir_page(self): + data = self.spb.prepare_pages(self.fs_root) + self.spb.build_pages(data, self.project, self.fs_root) + pkgindex = self.base.join('source/pkg/index.html') + assert pkgindex.check(file=True) + html = pkgindex.read() + print html + run_string_sequence_test(html, [ + 'href="../../style.css"', + 'root', + 'pkg', + 'func.py', + 'someclass.py', + 'somesubclass.py', + '

directories

', + '

files

']) + _checkhtml(html) + + def test_build_navigation_root(self): + self.spb.prepare_pages(self.fs_root) + nav = self.spb.build_navigation(self.fs_root.join('pkg')) html = nav.unicode(indent=0) print html.encode('UTF-8') - assert 'href="source/index.html">root' in html - assert 'href="source/pkg/index.html">pkg' in html - assert 'href="source/pkg/func.py.html">func.py' in html - assert 'href="source/pkg/someclass.py.html">someclass.py' in html - assert 'href="source/pkg/somesubclass.py.html">somesubclass.py' in html + run_string_sequence_test(html, [ + 'href="source/index.html">root', + 'href="source/pkg/index.html">pkg', + 'href="source/pkg/func.py.html">func.py', + 'href="source/pkg/someclass.py.html">someclass.py', + 'href="source/pkg/somesubclass.py.html">somesubclass.py', + ]) Modified: py/dist/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/dist/py/apigen/testing/test_htmlgen.py (original) +++ py/dist/py/apigen/testing/test_htmlgen.py Sun Jan 21 17:17:05 2007 @@ -11,6 +11,13 @@ assert tree == {'': ['foo'], 'foo': ['foo.bar'], 'foo.bar': ['foo.bar.baz', 'foo.bar.qux']} + tree = htmlgen.create_namespace_tree(['pkg.sub.func', + 'pkg.SomeClass', + 'pkg.SomeSubClass']) + assert tree == {'': ['pkg'], + 'pkg.sub': ['pkg.sub.func'], + 'pkg': ['pkg.sub', 'pkg.SomeClass', + 'pkg.SomeSubClass']} def test_build_navitem_html(): l = Linker() From hpk at codespeak.net Mon Jan 22 00:39:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 22 Jan 2007 00:39:28 +0100 (CET) Subject: [py-svn] r37110 - in py/branch/config/py/test: . rsession terminal tkinter tkinter/testing Message-ID: <20070121233928.B33D510077@code0.codespeak.net> Author: hpk Date: Mon Jan 22 00:39:24 2007 New Revision: 37110 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/rsession/rsession.py py/branch/config/py/test/rsession/slave.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/tkinter/backend.py py/branch/config/py/test/tkinter/testing/test_backend.py py/branch/config/py/test/tkinter/testing/test_capture_out_err.py Log: renaming some variables for more clarity Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Mon Jan 22 00:39:24 2007 @@ -27,36 +27,36 @@ """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option conftest = Conftest() + _initialized = False def __init__(self): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - self._parsed = False self._overwrite_dict = {} 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._parsed, ( + assert not self._initialized, ( "can only parse cmdline args once per Config object") - self._parsed = True + self._initialized = True self.conftest.setinitial(args) self.conftest.rget('adddefaultoptions')() args = [str(x) for x in args] - cmdlineoption, remaining = self._parser.parse_args(args) + cmdlineoption, args = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) fixoptions(self.option) # XXX fixing should be moved to sessions - if not remaining: - remaining.append(py.std.os.getcwd()) - self.remaining = remaining + if not args: + args.append(py.std.os.getcwd()) + self.args = args def getcolitems(self): """ return initial collectors. """ colitems = [] - if self.remaining: - for path in self.remaining: + if self.args: + for path in self.args: path = py.path.local(path) colitems.append(self._getcollector(path)) return colitems Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Mon Jan 22 00:39:24 2007 @@ -132,7 +132,7 @@ """ def main(self, reporter=None): """ main loop for running tests. """ - args = self.config.remaining + args = self.config.args sshhosts, remotepython, rsync_roots = self.read_distributed_config() reporter, startserverflag = self.init_reporter(reporter, @@ -207,7 +207,7 @@ """ def main(self, reporter=None, runner=None): # check out if used options makes any sense - args = self.config.remaining + args = self.config.args sshhosts = [HostInfo('localhost')] # this is just an info to reporter Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Mon Jan 22 00:39:24 2007 @@ -128,7 +128,7 @@ sys.path.insert(0, basedir) import py config = py.test.config - if config._parsed: + if config._initialized: config = config._reparse([basedir]) else: config.conftest.setinitial([basedir]) Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Mon Jan 22 00:39:24 2007 @@ -88,7 +88,7 @@ self.executable = executable def main(self): - rootdir = getrootdir(self.config.remaining) + rootdir = getrootdir(self.config.args) wasfailing = False failures = [] while 1: @@ -100,7 +100,7 @@ if not self.config.option.looponfailing: break print "#" * 60 - print "# looponfailing: mode: %d failures remaining" % len(failures) + print "# looponfailing: mode: %d failures args" % len(failures) for root, names in failures: name = "/".join(names) # XXX print "Failure at: %r" % (name,) @@ -117,7 +117,7 @@ """, stdout=self.out, stderr=self.out) print "MASTER: triggered slave terminal session ->" repr = self.config.make_repr(conftestnames=[]) - channel.send((repr, self.config.remaining, failures)) + channel.send((repr, self.config.args, failures)) print "MASTER: send start info" try: return channel.receive() @@ -131,7 +131,7 @@ repr, args, failures = channel.receive() print "SLAVE: received configuration" config = py.test.config - config.remaining = args + config.args = args config.conftest.setinitial(args) config.merge_repr(repr) config.option.looponfailing = False Modified: py/branch/config/py/test/tkinter/backend.py ============================================================================== --- py/branch/config/py/test/tkinter/backend.py (original) +++ py/branch/config/py/test/tkinter/backend.py Mon Jan 22 00:39:24 2007 @@ -234,7 +234,7 @@ if tests: cols = getfailureitems(tests) else: - cols = config.remaining + cols = config.args session = ReportSession(config = config, channel=channel) session.shouldclose = channel.isclosed session.main() Modified: py/branch/config/py/test/tkinter/testing/test_backend.py ============================================================================== --- py/branch/config/py/test/tkinter/testing/test_backend.py (original) +++ py/branch/config/py/test/tkinter/testing/test_backend.py Mon Jan 22 00:39:24 2007 @@ -180,7 +180,7 @@ def test_start_tests(self): config = py.test.config._reparse([datadir/'filetest.py']) self.backend.start_tests(config = config, - args = config.remaining, + args = config.args, tests = []) while self.backend.running: self.backend.update() Modified: py/branch/config/py/test/tkinter/testing/test_capture_out_err.py ============================================================================== --- py/branch/config/py/test/tkinter/testing/test_capture_out_err.py (original) +++ py/branch/config/py/test/tkinter/testing/test_capture_out_err.py Mon Jan 22 00:39:24 2007 @@ -8,7 +8,7 @@ config = py.test.config._reparse([datadir/'filetest.py']) backend = ReportBackend() backend.start_tests(config = config, - args = config.remaining, + args = config.args, tests = []) while backend.running: backend.update() From hpk at codespeak.net Mon Jan 22 01:07:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 22 Jan 2007 01:07:11 +0100 (CET) Subject: [py-svn] r37112 - in py/branch/config/py/test: . testing Message-ID: <20070122000711.8752D1007E@code0.codespeak.net> Author: hpk Date: Mon Jan 22 01:07:09 2007 New Revision: 37112 Modified: py/branch/config/py/test/collect.py py/branch/config/py/test/config.py py/branch/config/py/test/testing/test_config.py Log: * moving towards a common rootdir/root collector * adding an xxxed test Modified: py/branch/config/py/test/collect.py ============================================================================== --- py/branch/config/py/test/collect.py (original) +++ py/branch/config/py/test/collect.py Mon Jan 22 01:07:09 2007 @@ -165,9 +165,10 @@ namelist = namelist.split("/") cur = self for name in namelist: - next = cur.join(name) - assert next is not None, (cur, name, namelist) - cur = next + if name: + next = cur.join(name) + assert next is not None, (cur, name, namelist) + cur = next return cur def haskeyword(self, keyword): Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Mon Jan 22 01:07:09 2007 @@ -63,17 +63,18 @@ def _getcollector(self, path): assert path.check(), "%s: path does not exist" %(path,) + rootcol = self._getrootcollector(path) + return rootcol.getitembynames( + path.relto(rootcol.fspath).split(path.sep)) + + def _getrootcollector(self, path): pkgpath = path.pypkgpath() if pkgpath is None: pkgpath = path.check(file=1) and path.dirpath() or path col = self.conftest.rget("Directory", pkgpath)(pkgpath) col.config = self - for name in path.relto(pkgpath).split(path.sep): - if name: - col = col.join(name) - assert col is not None, ( - "joining %r to collector %s resulted in None" %(name, col)) return col + def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Mon Jan 22 01:07:09 2007 @@ -83,6 +83,32 @@ config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 +def XXXtest_config_init_direct(): + tmp = py.test.ensuretemp("reprconfig1") + tmp.ensure("__init__.py") + tmp.ensure("conftest.py").write("x=1 ; y=2") + hello = tmp.ensure("test_hello.py") + config = py.test.config._reparse([hello]) + repr = config.repr_direct(conftestnames='x,y') + config2 = py.test.config._reparse([tmp.dirpath()]) + config2.init_direct(repr, basedir=tmp.dirpath()) + for x,y in zip(config.args, config2.args): + assert x == y + py.test.raises(AssertionError, "config2.init_direct(repr, basedir=None)") + config3 = py.test.config._reparse([tmp.dirpath()]) + col = config3._getcollector(hello) + config3.init_direct(repr, basedir=tmp.dirpath(), + collectspecs=[col.gettrail()]) + assert config3.getvalue('x') == 1 + assert config3.getvalue('y') == 2 + cols = config.getcolitems() + assert len(cols) == 1 + col = cols[0] + assert col.name == 'test_hello.py' + assert col.parent.name == 'reprconfig1' + assert col.parent.parent.name == tmp.dirpath().basename + assert col.parent.parent.parent is None + def test_config_make_and_merge_repr(): tmp = py.test.ensuretemp("reprconfig1") tmp.ensure("__init__.py") From hpk at codespeak.net Mon Jan 22 02:20:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 22 Jan 2007 02:20:31 +0100 (CET) Subject: [py-svn] r37114 - in py/branch/config/py/test: . terminal testing Message-ID: <20070122012031.E6A781007A@code0.codespeak.net> Author: hpk Date: Mon Jan 22 02:20:30 2007 New Revision: 37114 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/testing/test_config.py Log: streamlining initial construction of collectors (to unify topdir/pkgdir/collector handling in rsession/terminal/normal) still work in-progress with a skipped test Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Mon Jan 22 02:20:30 2007 @@ -49,23 +49,33 @@ self.option.__dict__.update(vars(cmdlineoption)) fixoptions(self.option) # XXX fixing should be moved to sessions if not args: - args.append(py.std.os.getcwd()) + args.append(py.std.os.getcwd()) + args = [py.path.local(x) for x in args] + self.topdir = reduce(py.path.local.common, args) self.args = args + def initdirect(self, topdir, repr, coltrails=None): + assert not self._initialized + self._initialized = True + self.merge_repr(repr) + self.topdir = topdir + self.args = coltrails or [] + def getcolitems(self): """ return initial collectors. """ - colitems = [] - if self.args: - for path in self.args: - path = py.path.local(path) - colitems.append(self._getcollector(path)) - return colitems + return [self._getcollector(path) for path in self.args] def _getcollector(self, path): - assert path.check(), "%s: path does not exist" %(path,) - rootcol = self._getrootcollector(path) - return rootcol.getitembynames( - path.relto(rootcol.fspath).split(path.sep)) + if isinstance(path, tuple): + 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): pkgpath = path.pypkgpath() @@ -74,7 +84,6 @@ col = self.conftest.rget("Directory", pkgpath)(pkgpath) col.config = self return col - def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Mon Jan 22 02:20:30 2007 @@ -117,7 +117,7 @@ """, stdout=self.out, stderr=self.out) print "MASTER: triggered slave terminal session ->" repr = self.config.make_repr(conftestnames=[]) - channel.send((repr, self.config.args, failures)) + channel.send((repr, [str(x) for x in self.config.args], failures)) print "MASTER: send start info" try: return channel.receive() Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Mon Jan 22 02:20:30 2007 @@ -83,29 +83,41 @@ config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 -def XXXtest_config_init_direct(): - tmp = py.test.ensuretemp("reprconfig1") +def test_topdir(): + tmp = py.test.ensuretemp("topdir") + config = py.test.config._reparse([tmp]) + assert config.topdir == tmp + config = py.test.config._reparse([tmp.join('hello'), + tmp.join("world")]) + assert config.topdir == tmp + +def test_config_init_direct(): + py.test.skip("XXX partially not implemented") + tmp = py.test.ensuretemp("initdirect") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1 ; y=2") hello = tmp.ensure("test_hello.py") config = py.test.config._reparse([hello]) - repr = config.repr_direct(conftestnames='x,y') + repr = config.make_repr(conftestnames=['x', 'y']) config2 = py.test.config._reparse([tmp.dirpath()]) - config2.init_direct(repr, basedir=tmp.dirpath()) - for x,y in zip(config.args, config2.args): - assert x == y - py.test.raises(AssertionError, "config2.init_direct(repr, basedir=None)") + config2._initialized = False # we have to do that from tests + config2.initdirect(topdir=tmp.dirpath(), repr=repr) + for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): + assert col1.fspath == col2.fspath + py.test.raises(AssertionError, "config2.initdirect(None, None)") config3 = py.test.config._reparse([tmp.dirpath()]) col = config3._getcollector(hello) - config3.init_direct(repr, basedir=tmp.dirpath(), - collectspecs=[col.gettrail()]) + # XXX write repr_colspecs(topdir, cols), + # and have it consumable by initdirect() + config3.initdirect(topdir=tmp.dirpath(), repr=repr, + collectspecs=[col.listnames()]) assert config3.getvalue('x') == 1 assert config3.getvalue('y') == 2 cols = config.getcolitems() assert len(cols) == 1 col = cols[0] assert col.name == 'test_hello.py' - assert col.parent.name == 'reprconfig1' + assert col.parent.name == tmp.basename assert col.parent.parent.name == tmp.dirpath().basename assert col.parent.parent.parent is None From fijal at codespeak.net Mon Jan 22 13:48:35 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 13:48:35 +0100 (CET) Subject: [py-svn] r37127 - py/branch/config/py/test/rsession Message-ID: <20070122124835.BC0E110088@code0.codespeak.net> Author: fijal Date: Mon Jan 22 13:48:33 2007 New Revision: 37127 Modified: py/branch/config/py/test/rsession/rsession.py Log: Add a few docstrings. Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Mon Jan 22 13:48:33 2007 @@ -52,6 +52,10 @@ getpkgdir = staticmethod(getpkgdir) def init_reporter(self, reporter, sshhosts, reporter_class, arg=""): + """ This initialises so called `reporter` class, which will + handle all event presenting to user. Does not get called + if main received custom reporter + """ startserverflag = self.config.option.startserver restflag = self.config.option.restreport @@ -96,6 +100,8 @@ reporterror = staticmethod(reporterror) def kill_server(self, startserverflag): + """ Kill web server + """ if startserverflag: from py.__.test.rsession.web import kill_server kill_server() @@ -118,6 +124,8 @@ return new_reporter, checkfun def parse_directories(sshhosts): + """ Parse sshadresses of hosts to have distinct hostname/hostdir + """ directories = {} for host in sshhosts: m = re.match("^(.*?):(.*)$", host.hostname) @@ -177,6 +185,8 @@ raise def read_distributed_config(self): + """ Read from conftest file the configuration of distributed testing + """ try: rsync_roots = self.config.getvalue("distrsync_roots") except: From fijal at codespeak.net Mon Jan 22 13:58:31 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 13:58:31 +0100 (CET) Subject: [py-svn] r37128 - py/branch/config/py/execnet Message-ID: <20070122125831.A617910088@code0.codespeak.net> Author: fijal Date: Mon Jan 22 13:58:29 2007 New Revision: 37128 Removed: py/branch/config/py/execnet/rsync.py Log: Seems like an old file, will rather move rsync from rsession here. Deleted: /py/branch/config/py/execnet/rsync.py ============================================================================== --- /py/branch/config/py/execnet/rsync.py Mon Jan 22 13:58:29 2007 +++ (empty file) @@ -1,162 +0,0 @@ -import py, md5 - - -def rsync(gw, sourcedir, destdir, **options): - for name in options: - assert name in ('delete',) - - channel = gw.remote_exec(""" - import os, stat, shutil, md5 - destdir, options = channel.receive() - modifiedfiles = [] - - def remove(path): - assert path.startswith(destdir) - try: - os.unlink(path) - except OSError: - # assume it's a dir - shutil.rmtree(path) - - def receive_directory_structure(path, relcomponents): - #print "receive directory structure", path - try: - st = os.lstat(path) - except OSError: - st = None - msg = channel.receive() - if isinstance(msg, list): - if st and not stat.S_ISDIR(st.st_mode): - os.unlink(path) - st = None - if not st: - os.mkdir(path) - entrynames = {} - for entryname in msg: - receive_directory_structure(os.path.join(path, entryname), - relcomponents + [entryname]) - entrynames[entryname] = True - if options.get('delete'): - for othername in os.listdir(path): - if othername not in entrynames: - otherpath = os.path.join(path, othername) - remove(otherpath) - else: - if st and stat.S_ISREG(st.st_mode): - f = file(path, 'rb') - data = f.read() - f.close() - mychecksum = md5.md5(data).digest() - else: - if st: - remove(path) - mychecksum = None - if mychecksum != msg: - channel.send(relcomponents) - modifiedfiles.append(path) - receive_directory_structure(destdir, []) - channel.send(None) # end marker - for path in modifiedfiles: - data = channel.receive() - f = open(path, 'wb') - f.write(data) - f.close() - """) - - channel.send((str(destdir), options)) - - def send_directory_structure(path): - if path.check(dir=1): - subpaths = path.listdir() - print "sending directory structure", path - channel.send([p.basename for p in subpaths]) - for p in subpaths: - send_directory_structure(p) - elif path.check(file=1): - data = path.read() - checksum = md5.md5(data).digest() - channel.send(checksum) - else: - raise ValueError, "cannot sync %r" % (path,) - send_directory_structure(sourcedir) - while True: - modified_rel_path = channel.receive() - if modified_rel_path is None: - break - modifiedpath = sourcedir.join(*modified_rel_path) - data = modifiedpath.read() - channel.send(data) - channel.waitclose() - -def copy(gw, source, dest): - channel = gw.remote_exec(""" - import md5 - localfilename = channel.receive() - try: - f = file(localfilename, 'rb') - existingdata = f.read() - f.close() - except (IOError, OSError): - mycrc = None - else: - mycrc = md5.md5(existingdata).digest() - remotecrc = channel.receive() - if remotecrc == mycrc: - channel.send(None) - else: - channel.send(localfilename) - newdata = channel.receive() - f = file(localfilename, 'wb') - f.write(newdata) - f.close() - """) - channel.send(str(dest)) - f = file(str(source), 'rb') - localdata = f.read() - f.close() - channel.send(md5.md5(localdata).digest()) - status = channel.receive() - if status is not None: - assert status == str(dest) # for now - channel.send(localdata) - channel.waitclose() - - -def setup_module(mod): - mod.gw = py.execnet.PopenGateway() - -def teardown_module(mod): - mod.gw.exit() - - -def test_filecopy(): - dir = py.test.ensuretemp('filecopy') - source = dir.ensure('source') - dest = dir.join('dest') - source.write('hello world') - copy(gw, source, dest) - assert dest.check(file=1) - assert dest.read() == 'hello world' - source.write('something else') - copy(gw, source, dest) - assert dest.check(file=1) - assert dest.read() == 'something else' - -def test_dirsync(): - base = py.test.ensuretemp('dirsync') - dest = base.join('dest') - source = base.mkdir('source') - - for s in ('content1', 'content2'): - source.ensure('subdir', 'file1').write(s) - rsync(gw, source, dest) - assert dest.join('subdir').check(dir=1) - assert dest.join('subdir', 'file1').check(file=1) - assert dest.join('subdir', 'file1').read() == s - - source.join('subdir').remove('file1') - rsync(gw, source, dest) - assert dest.join('subdir', 'file1').check(file=1) - rsync(gw, source, dest, delete=True) - assert not dest.join('subdir', 'file1').check() - From fijal at codespeak.net Mon Jan 22 14:04:05 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 14:04:05 +0100 (CET) Subject: [py-svn] r37129 - in py/branch/config/py: . execnet execnet/testing test/rsession test/rsession/testing Message-ID: <20070122130405.EFF4810088@code0.codespeak.net> Author: fijal Date: Mon Jan 22 14:04:00 2007 New Revision: 37129 Added: py/branch/config/py/execnet/rsync.py - copied unchanged from r37125, py/branch/config/py/test/rsession/rsync.py py/branch/config/py/execnet/rsync_remote.py - copied unchanged from r37125, py/branch/config/py/test/rsession/rsync_remote.py py/branch/config/py/execnet/testing/test_rsync.py - copied unchanged from r37125, py/branch/config/py/test/rsession/testing/test_rsync.py Removed: py/branch/config/py/test/rsession/rsync.py py/branch/config/py/test/rsession/rsync_remote.py py/branch/config/py/test/rsession/testing/test_rsync.py Modified: py/branch/config/py/__init__.py py/branch/config/py/test/rsession/hostmanage.py Log: Move rsync to execnet and expose it as py.execnet.RSync Modified: py/branch/config/py/__init__.py ============================================================================== --- py/branch/config/py/__init__.py (original) +++ py/branch/config/py/__init__.py Mon Jan 22 14:04:00 2007 @@ -91,6 +91,9 @@ 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), + # execnet scripts + 'execnet.RSync' : ('./execnet/rsync.py', 'RSync'), + # input-output helping 'io.dupfile' : ('./io/dupfile.py', 'dupfile'), 'io.FDCapture' : ('./io/capture.py', 'FDCapture'), Modified: py/branch/config/py/test/rsession/hostmanage.py ============================================================================== --- py/branch/config/py/test/rsession/hostmanage.py (original) +++ py/branch/config/py/test/rsession/hostmanage.py Mon Jan 22 14:04:00 2007 @@ -5,7 +5,6 @@ from py.__.test.rsession.master import \ setup_slave, MasterNode from py.__.test.rsession import report -from py.__.test.rsession.rsync import RSync class HostInfo(object): """ Class trying to store all necessary attributes @@ -39,11 +38,11 @@ def __ne__(self, other): return not self == other -class HostRSync(RSync): +class HostRSync(py.execnet.RSync): """ An rsync wrapper which filters out *~, .svn/ and *.pyc """ def __init__(self, rsync_roots): - RSync.__init__(self, delete=True) + py.execnet.RSync.__init__(self, delete=True) self.rsync_roots = rsync_roots def filter(self, path): Deleted: /py/branch/config/py/test/rsession/rsync.py ============================================================================== --- /py/branch/config/py/test/rsession/rsync.py Mon Jan 22 14:04:00 2007 +++ (empty file) @@ -1,137 +0,0 @@ -import py, os, stat, md5 -from Queue import Queue - - -class RSync(object): - - def __init__(self, callback=None, **options): - for name in options: - assert name in ('delete') - self.options = options - self.callback = callback - self.channels = {} - self.receivequeue = Queue() - self.links = [] - - def filter(self, path): - return True - - def add_target(self, gateway, destdir, finishedcallback=None): - def itemcallback(req): - self.receivequeue.put((channel, req)) - channel = gateway.remote_exec(REMOTE_SOURCE) - channel.setcallback(itemcallback, endmarker = None) - channel.send((str(destdir), self.options)) - self.channels[channel] = finishedcallback - - def send(self, sourcedir): - self.sourcedir = str(sourcedir) - # normalize a trailing '/' away - self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x')) - # send directory structure and file timestamps/sizes - self._send_directory_structure(self.sourcedir) - - # paths and to_send are only used for doing - # progress-related callbacks - self.paths = {} - self.to_send = {} - - # send modified file to clients - while self.channels: - channel, req = self.receivequeue.get() - if req is None: - # end-of-channel - if channel in self.channels: - # too early! we must have got an error - channel.waitclose() - # or else we raise one - raise IOError('connection unexpectedly closed: %s ' % ( - channel.gateway,)) - else: - command, data = req - if command == "links": - for link in self.links: - channel.send(link) - # completion marker, this host is done - channel.send(42) - elif command == "done": - finishedcallback = self.channels.pop(channel) - if finishedcallback: - finishedcallback() - elif command == "ack": - if self.callback: - self.callback("ack", self.paths[data], channel) - elif command == "list_done": - # sum up all to send - if self.callback: - s = sum([self.paths[i] for i in self.to_send[channel]]) - self.callback("list", s, channel) - elif command == "send": - modified_rel_path, checksum = data - modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) - f = open(modifiedpath, 'rb') - data = f.read() - - # provide info to progress callback function - modified_rel_path = "/".join(modified_rel_path) - self.paths[modified_rel_path] = len(data) - if channel not in self.to_send: - self.to_send[channel] = [] - self.to_send[channel].append(modified_rel_path) - - f.close() - if checksum is not None and checksum == md5.md5(data).digest(): - data = None # not really modified - else: - # ! there is a reason for the interning: - # sharing multiple copies of the file's data - data = intern(data) - print '%s <= %s' % ( - channel.gateway._getremoteaddress(), - modified_rel_path) - channel.send(data) - del data - else: - assert "Unknown command %s" % command - - def _broadcast(self, msg): - for channel in self.channels: - channel.send(msg) - - def _send_link(self, basename, linkpoint): - self.links.append(("link", basename, linkpoint)) - - def _send_directory_structure(self, path): - st = os.lstat(path) - if stat.S_ISREG(st.st_mode): - # regular file: send a timestamp/size pair - self._broadcast((st.st_mtime, st.st_size)) - elif stat.S_ISDIR(st.st_mode): - # dir: send a list of entries - names = [] - subpaths = [] - for name in os.listdir(path): - p = os.path.join(path, name) - if self.filter(p): - names.append(name) - subpaths.append(p) - self._broadcast(names) - for p in subpaths: - self._send_directory_structure(p) - elif stat.S_ISLNK(st.st_mode): - linkpoint = os.readlink(path) - basename = path[len(self.sourcedir) + 1:] - if not linkpoint.startswith(os.sep): - # relative link, just send it - self._send_link(basename, linkpoint) - elif linkpoint.startswith(self.sourcedir): - self._send_link(basename, linkpoint[len(self.sourcedir) + 1:]) - else: - self._send_link(basename, linkpoint) - self._broadcast(None) - else: - raise ValueError, "cannot sync %r" % (path,) - -REMOTE_SOURCE = py.path.local(__file__).dirpath().\ - join('rsync_remote.py').open().read() + "\nf()" - Deleted: /py/branch/config/py/test/rsession/rsync_remote.py ============================================================================== --- /py/branch/config/py/test/rsession/rsync_remote.py Mon Jan 22 14:04:00 2007 +++ (empty file) @@ -1,86 +0,0 @@ - -def f(): - import os, stat, shutil, md5 - destdir, options = channel.receive() - modifiedfiles = [] - - def remove(path): - assert path.startswith(destdir) - try: - os.unlink(path) - except OSError: - # assume it's a dir - shutil.rmtree(path) - - def receive_directory_structure(path, relcomponents): - try: - st = os.lstat(path) - except OSError: - st = None - msg = channel.receive() - if isinstance(msg, list): - if st and not stat.S_ISDIR(st.st_mode): - os.unlink(path) - st = None - if not st: - os.makedirs(path) - entrynames = {} - for entryname in msg: - receive_directory_structure(os.path.join(path, entryname), - relcomponents + [entryname]) - entrynames[entryname] = True - if options.get('delete'): - for othername in os.listdir(path): - if othername not in entrynames: - otherpath = os.path.join(path, othername) - remove(otherpath) - elif msg is not None: - checksum = None - if st: - if stat.S_ISREG(st.st_mode): - msg_mtime, msg_size = msg - if msg_size != st.st_size: - pass - elif msg_mtime != st.st_mtime: - f = open(path, 'rb') - checksum = md5.md5(f.read()).digest() - f.close() - else: - return # already fine - else: - remove(path) - channel.send(("send", (relcomponents, checksum))) - modifiedfiles.append((path, msg)) - receive_directory_structure(destdir, []) - - STRICT_CHECK = False # seems most useful this way for py.test - channel.send(("list_done", None)) - - for path, (time, size) in modifiedfiles: - data = channel.receive() - channel.send(("ack", path[len(destdir) + 1:])) - if data is not None: - if STRICT_CHECK and len(data) != size: - raise IOError('file modified during rsync: %r' % (path,)) - f = open(path, 'wb') - f.write(data) - f.close() - os.utime(path, (time, time)) - del data - channel.send(("links", None)) - - msg = channel.receive() - while msg is not 42: - # we get symlink - _type, relpath, linkpoint = msg - assert _type == "link" - path = os.path.join(destdir, relpath) - try: - remove(path) - except OSError: - pass - - os.symlink(os.path.join(destdir, linkpoint), path) - msg = channel.receive() - channel.send(("done", None)) - Deleted: /py/branch/config/py/test/rsession/testing/test_rsync.py ============================================================================== --- /py/branch/config/py/test/rsession/testing/test_rsync.py Mon Jan 22 14:04:00 2007 +++ (empty file) @@ -1,80 +0,0 @@ -import py -from py.__.test.rsession.rsync import RSync - - -def setup_module(mod): - mod.gw = py.execnet.PopenGateway() - mod.gw2 = py.execnet.PopenGateway() - -def teardown_module(mod): - mod.gw.exit() - mod.gw2.exit() - - -def test_dirsync(): - base = py.test.ensuretemp('dirsync') - dest = base.join('dest') - dest2 = base.join('dest2') - source = base.mkdir('source') - - for s in ('content1', 'content2-a-bit-longer'): - source.ensure('subdir', 'file1').write(s) - rsync = RSync() - rsync.add_target(gw, dest) - rsync.add_target(gw2, dest2) - rsync.send(source) - assert dest.join('subdir').check(dir=1) - assert dest.join('subdir', 'file1').check(file=1) - assert dest.join('subdir', 'file1').read() == s - assert dest2.join('subdir').check(dir=1) - assert dest2.join('subdir', 'file1').check(file=1) - assert dest2.join('subdir', 'file1').read() == s - - source.join('subdir').remove('file1') - rsync = RSync() - rsync.add_target(gw2, dest2) - rsync.add_target(gw, dest) - rsync.send(source) - assert dest.join('subdir', 'file1').check(file=1) - assert dest2.join('subdir', 'file1').check(file=1) - rsync = RSync(delete=True) - rsync.add_target(gw2, dest2) - rsync.add_target(gw, dest) - rsync.send(source) - assert not dest.join('subdir', 'file1').check() - assert not dest2.join('subdir', 'file1').check() - -def test_symlink_rsync(): - if py.std.sys.platform == 'win32': - py.test.skip("symlinks are unsupported on Windows.") - base = py.test.ensuretemp('symlinkrsync') - dest = base.join('dest') - source = base.join('source') - source.ensure("existant") - source.join("rellink").mksymlinkto(source.join("existant"), absolute=0) - source.join('abslink').mksymlinkto(source.join("existant")) - - rsync = RSync() - rsync.add_target(gw, dest) - rsync.send(source) - - assert dest.join('rellink').readlink() == dest.join("existant") - assert dest.join('abslink').readlink() == dest.join("existant") - -def test_callback(): - base = py.test.ensuretemp('callback') - dest = base.join("dest") - source = base.join("source") - source.ensure("existant").write("a" * 100) - source.ensure("existant2").write("a" * 10) - total = {} - def callback(cmd, lgt, channel): - total[(cmd, lgt)] = True - - rsync = RSync(callback=callback) - #rsync = RSync() - rsync.add_target(gw, dest) - rsync.send(source) - - assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True} - From fijal at codespeak.net Mon Jan 22 14:10:43 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 14:10:43 +0100 (CET) Subject: [py-svn] r37130 - py/branch/config/py/execnet Message-ID: <20070122131043.4E9A11008A@code0.codespeak.net> Author: fijal Date: Mon Jan 22 14:10:41 2007 New Revision: 37130 Modified: py/branch/config/py/execnet/rsync.py Log: add docstrings. Modified: py/branch/config/py/execnet/rsync.py ============================================================================== --- py/branch/config/py/execnet/rsync.py (original) +++ py/branch/config/py/execnet/rsync.py Mon Jan 22 14:10:41 2007 @@ -3,7 +3,19 @@ class RSync(object): + """ This is an example usage of py.execnet - a sample RSync + protocol, which can perform syncing 1-to-n. + Sample usage: you instantiate this class, eventually providing a + callback when rsyncing is done, than add some targets + (gateway + destdir) by running add_target and finally + invoking send() which will send provided source tree remotely. + + There is limited support for symlinks, which means that symlinks + pointing to the sourcetree will be send "as is" while external + symlinks will be just copied (regardless of existance of such + a path on remote side) + """ def __init__(self, callback=None, **options): for name in options: assert name in ('delete') @@ -17,6 +29,8 @@ return True def add_target(self, gateway, destdir, finishedcallback=None): + """ Adds a target for to-be-send data + """ def itemcallback(req): self.receivequeue.put((channel, req)) channel = gateway.remote_exec(REMOTE_SOURCE) @@ -25,6 +39,8 @@ self.channels[channel] = finishedcallback def send(self, sourcedir): + """ Sends a sourcedir to previously prepared targets + """ self.sourcedir = str(sourcedir) # normalize a trailing '/' away self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x')) From fijal at codespeak.net Mon Jan 22 14:11:14 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 14:11:14 +0100 (CET) Subject: [py-svn] r37131 - py/branch/config/py/execnet/testing Message-ID: <20070122131114.996221008B@code0.codespeak.net> Author: fijal Date: Mon Jan 22 14:11:13 2007 New Revision: 37131 Modified: py/branch/config/py/execnet/testing/test_rsync.py Log: Fix a test Modified: py/branch/config/py/execnet/testing/test_rsync.py ============================================================================== --- py/branch/config/py/execnet/testing/test_rsync.py (original) +++ py/branch/config/py/execnet/testing/test_rsync.py Mon Jan 22 14:11:13 2007 @@ -1,5 +1,5 @@ import py -from py.__.test.rsession.rsync import RSync +from py.execnet import RSync def setup_module(mod): From fijal at codespeak.net Mon Jan 22 14:12:12 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 14:12:12 +0100 (CET) Subject: [py-svn] r37132 - py/branch/config/py/doc Message-ID: <20070122131212.5B90E1008A@code0.codespeak.net> Author: fijal Date: Mon Jan 22 14:12:11 2007 New Revision: 37132 Modified: py/branch/config/py/doc/TODO.txt Log: Remove done issues. Modified: py/branch/config/py/doc/TODO.txt ============================================================================== --- py/branch/config/py/doc/TODO.txt (original) +++ py/branch/config/py/doc/TODO.txt Mon Jan 22 14:12:11 2007 @@ -95,11 +95,7 @@ distributed testing ---------------------- -* the main rsession methods should have docstrings - explaining the purpose - -* move RSync class to py.execnet.RSync, document its usage and - features in the class and method docstrings +Should be done. code quality ----------------- From fijal at codespeak.net Mon Jan 22 14:15:54 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 14:15:54 +0100 (CET) Subject: [py-svn] r37133 - py/branch/config/py/doc Message-ID: <20070122131554.1C5B71008A@code0.codespeak.net> Author: fijal Date: Mon Jan 22 14:15:53 2007 New Revision: 37133 Modified: py/branch/config/py/doc/TODO.txt Log: another update what has been done. Modified: py/branch/config/py/doc/TODO.txt ============================================================================== --- py/branch/config/py/doc/TODO.txt (original) +++ py/branch/config/py/doc/TODO.txt Mon Jan 22 14:15:53 2007 @@ -87,8 +87,6 @@ - running "py.test --looponfailing" - running "py.test" distributed on some hosts -* tests should not create any tempdirectories in the source code base - * try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) From guido at codespeak.net Mon Jan 22 16:07:56 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 22 Jan 2007 16:07:56 +0100 (CET) Subject: [py-svn] r37145 - in py/dist/py/apigen: . testing Message-ID: <20070122150756.E88571008A@code0.codespeak.net> Author: guido Date: Mon Jan 22 16:07:54 2007 New Revision: 37145 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_apigen_functional.py Log: Displaying arguments in a nicer fashion, displaying argument and return value types, renamed the main namespace in the tests to 'main' to avoid confusion. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Mon Jan 22 16:07:54 2007 @@ -11,21 +11,7 @@ html = py.xml.html raw = py.xml.raw -def create_namespace_tree(dotted_names): - """ creates a tree (in dict form) from a set of dotted names - """ - ret = {} - for dn in dotted_names: - path = dn.split('.') - for i in xrange(len(path)): - ns = '.'.join(path[:i]) - itempath = '.'.join(path[:i + 1]) - if ns not in ret: - ret[ns] = [] - if itempath not in ret[ns]: - ret[ns].append(itempath) - return ret - +# HTML related stuff class H(html): class Description(html.div): style = html.Style(margin_left='10em') @@ -51,6 +37,12 @@ class MethodDef(html.h2): pass + class FunctionDescription(Description): + pass + + class FunctionDef(html.h2): + pass + class ParameterDescription(html.div): pass @@ -78,6 +70,14 @@ class DirListItem(html.div): pass + class ValueDescList(html.ul): + def __init__(self, *args, **kwargs): + super(H.ValueDescList, self).__init__(*args, **kwargs) + self.insert(0, html.div('where:')) + + class ValueDescItem(html.li): + pass + def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect @@ -91,6 +91,7 @@ navitem.attr.class_ = 'selected' return navitem +# some helper functionality def source_dirs_files(fspath): """ returns a tuple (dirs, files) for fspath @@ -114,6 +115,32 @@ files.append(child) return sorted(dirs), sorted(files) +def create_namespace_tree(dotted_names): + """ creates a tree (in dict form) from a set of dotted names + """ + ret = {} + for dn in dotted_names: + path = dn.split('.') + for i in xrange(len(path)): + ns = '.'.join(path[:i]) + itempath = '.'.join(path[:i + 1]) + if ns not in ret: + ret[ns] = [] + if itempath not in ret[ns]: + ret[ns].append(itempath) + return ret + +def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl): + page = LayoutPage(project, title, nav=navel, encoding='UTF-8', + stylesheeturl=stylesheeturl) + page.set_content(contentel) + here = py.magic.autopath().dirpath() + style = here.join('style.css').read() + outputpath.join('style.css').write(style) + return page + +# the PageBuilder classes take care of producing the docs (using the stuff +# above) class AbstractPageBuilder(object): def write_page(self, title, reltargetpath, project, tag, nav): targetpath = self.base.join(reltargetpath) @@ -257,36 +284,38 @@ self.linker = linker self.dsa = dsa - def build_full_method_view(self, dotted_name): + def build_callable_view(self, dotted_name): """ build the html for a class method """ + # XXX we may want to have seperate func = self.dsa.get_obj(dotted_name) sourcefile = inspect.getsourcefile(func) - params = get_param_htmldesc(self.linker, func) docstring = func.__doc__ local_methodname = func.__name__ + argdesc = get_param_htmldesc(self.linker, func) + valuedesc = self.build_callable_value_description(dotted_name) + + callable_source = self.dsa.get_function_source(dotted_name) + if callable_source: + csource = H.div(H.br(), + H.div('source:'), + H.SourceDef(H.pre(callable_source))) + else: + csource = H.SourceDef('could not get source') + if sourcefile is not None: - sourcelink = H.a('source', + sourcelink = H.a('view source file', href=self.linker.get_lazyhref(sourcefile)) else: sourcelink = H.span('no source file available') - snippet = H.MethodDescription( - H.MethodDef(local_methodname, params, ":"), - sourcelink, - H.Docstring(docstring), - ) - return snippet - def build_short_method_view(self, dotted_name): - """ build the html for a class method """ - func = self.dsa.get_obj(dotted_name) - params = get_param_htmldesc(self.linker, func) - docstring = func.__doc__ - local_methodname = func.__name__ - snippet = H.MethodDescription( - H.MethodDef(local_methodname, params, ":"), + snippet = H.FunctionDescription( + H.FunctionDef(local_methodname, argdesc), + valuedesc, H.Docstring(docstring), - # XXX more info here... + csource, + sourcelink, ) + return snippet def build_class_view(self, dotted_name): @@ -324,8 +353,8 @@ ) snippet.append(H.h2('Functions:')) for method in methods: - snippet += self.build_short_method_view('%s.%s' % (dotted_name, - method)) + snippet += self.build_callable_view('%s.%s' % (dotted_name, + method)) return snippet def build_namespace_view(self, namespace_dotted_name, item_dotted_names): @@ -383,7 +412,7 @@ parent_dotted_name, _ = split_of_last_part(dotted_name) module_dotted_name, _ = split_of_last_part(parent_dotted_name) sibling_dotted_names = namespace_tree[module_dotted_name] - tag = self.build_full_method_view(dotted_name) + tag = self.build_callable_view(dotted_name) nav = self.build_navigation(parent_dotted_name, sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) @@ -399,10 +428,10 @@ def prepare_function_pages(self, namespace_tree, method_dotted_names): passed = [] for dotted_name in method_dotted_names: - # XXX should we create a build_full_function_view instead? + # XXX should we create a build_function_view instead? parent_dotted_name, _ = split_of_last_part(dotted_name) sibling_dotted_names = namespace_tree[parent_dotted_name] - tag = self.build_full_method_view(dotted_name) + tag = self.build_callable_view(dotted_name) nav = self.build_navigation(parent_dotted_name, sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) @@ -437,7 +466,7 @@ def build_namespace_pages(self, data, project): for dotted_name, tag, nav, reltargetpath in data: if dotted_name == '': - dotted_name = 'project root' + dotted_name = self.dsa.get_module_name().split('/')[-1] title = 'index of %s namespace' % (dotted_name,) self.write_page(title, reltargetpath, project, tag, nav) @@ -473,12 +502,43 @@ selected)) return H.Navigation(*navitems) -def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl): - page = LayoutPage(project, title, nav=navel, encoding='UTF-8', - stylesheeturl=stylesheeturl) - page.set_content(contentel) - here = py.magic.autopath().dirpath() - style = here.join('style.css').read() - outputpath.join('style.css').write(style) - return page + def build_callable_value_description(self, dotted_name): + args, retval = self.dsa.get_function_signature(dotted_name) + valuedesc = H.ValueDescList() + for name, _type in args + [('return value', retval)]: + l = self.process_type_link(_type) + items = [] + next = "%s :: " % name + for item in l: + if isinstance(item, str): + next += item + else: + if next: + items.append(next) + next = "" + items.append(item) + if next: + items.append(next) + valuedesc.append(H.ValueDescItem(*items)) + return valuedesc + + def process_type_link(self, _type): + # now we do simple type dispatching and provide a link in this case + lst = [] + data = self.dsa.get_type_desc(_type) + if not data: + for i in _type.striter(): + if isinstance(i, str): + lst.append(i) + else: + lst += self.process_type_link(i) + return lst + name, _desc_type, is_degenerated = data + if not is_degenerated: + linktarget = self.linker.get_lazyhref(name) + lst.append(H.a(str(_type), href=linktarget)) + else: + # we should provide here some way of linking to sourcegen directly + lst.append(name) + return lst Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Mon Jan 22 16:07:54 2007 @@ -3,6 +3,7 @@ from py.__.apigen.linker import Linker from py.__.apigen.htmlgen import * from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor +from py.__.apigen.tracer.tracer import Tracer from py.__.apigen.project import Project from py.__.test.web import webcheck from py.__.apigen.conftest import option @@ -17,9 +18,10 @@ else: message = 'unexpected position: %s' % (newpos,) py.test.fail('string %r: %s' % (s, message)) + currpos = newpos -def setup_fs_project(tempname='apigen_example'): - temp = py.test.ensuretemp(tempname) +def setup_fs_project(): + temp = py.test.ensuretemp('apigen_example') temp.ensure("pkg/func.py").write(py.code.Source("""\ def func(arg1): "docstring" @@ -44,9 +46,9 @@ temp.ensure("pkg/__init__.py").write(py.code.Source("""\ from py.initpkg import initpkg initpkg(__name__, exportdefs = { - 'pkg.sub.func': ("./func.py", "func"), - 'pkg.SomeClass': ('./someclass.py', 'SomeClass'), - 'pkg.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), + 'main.sub.func': ("./func.py", "func"), + 'main.SomeClass': ('./someclass.py', 'SomeClass'), + 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), }) """)) return temp, 'pkg' @@ -57,7 +59,7 @@ ds = DocStorage() ds.from_pkg(pkg) dsa = DocStorageAccessor(ds) - return dsa + return ds, dsa def _checkhtml(htmlstring): if isinstance(htmlstring, unicode): @@ -79,7 +81,7 @@ class AbstractBuilderTest(object): def setup_class(cls): cls.fs_root, cls.pkg_name = setup_fs_project() - cls.dsa = get_dsa(cls.fs_root, cls.pkg_name) + cls.ds, cls.dsa = get_dsa(cls.fs_root, cls.pkg_name) cls.project = Project() def setup_method(self, meth): @@ -96,177 +98,194 @@ self.spb = SourcePageBuilder(base, linker, self.fs_root) class TestApiPageBuilder(AbstractBuilderTest): - def test_build_full_method_view(self): - snippet = self.apb.build_full_method_view('pkg.sub.func') + def test_build_callable_view(self): + ds, dsa = get_dsa(self.fs_root, self.pkg_name) + t = Tracer(ds) + t.start_tracing() + pkg = __import__(self.pkg_name) + pkg.main.sub.func(10) + pkg.main.sub.func(pkg.main.SomeClass(10)) + t.end_tracing() + apb = ApiPageBuilder(self.base, self.linker, dsa) + snippet = apb.build_callable_view('main.sub.func') html = snippet.unicode() + print html + run_string_sequence_test(html, [ + 'arg1 :: AnyOf(', + 'href="', + 'Class SomeClass', + 'Int>', + 'return value :: <None>', + 'source:', + 'def func(arg1):', + ]) _checkhtmlsnippet(html) def test_build_function_pages(self): - namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass']) data = self.apb.prepare_function_pages(namespace_tree, - ['pkg.sub.func']) + ['main.sub.func']) self.apb.build_function_pages(data, self.project) - funcfile = self.base.join('api/pkg.sub.func.html') + funcfile = self.base.join('api/main.sub.func.html') assert funcfile.check() html = funcfile.read() _checkhtml(html) def test_build_class_view(self): - snippet = self.apb.build_class_view('pkg.SomeClass') + snippet = self.apb.build_class_view('main.SomeClass') html = snippet.unicode() _checkhtmlsnippet(html) def test_build_class_pages(self): - namespace_tree = create_namespace_tree(['pkg.SomeClass', - 'pkg.SomeSubClass', - 'pkg.sub.func']) + namespace_tree = create_namespace_tree(['main.SomeClass', + 'main.SomeSubClass', + 'main.sub.func']) data, methodsdata = self.apb.prepare_class_pages(namespace_tree, - ['pkg.SomeClass', - 'pkg.SomeSubClass']) + ['main.SomeClass', + 'main.SomeSubClass']) self.apb.build_class_pages(data, self.project) - clsfile = self.base.join('api/pkg.SomeClass.html') + clsfile = self.base.join('api/main.SomeClass.html') assert clsfile.check() html = clsfile.read() _checkhtml(html) def test_build_class_pages_nav_links(self): - namespace_tree = create_namespace_tree(['pkg.SomeClass', - 'pkg.SomeSubClass', - 'pkg.sub.func']) + namespace_tree = create_namespace_tree(['main.SomeClass', + 'main.SomeSubClass', + 'main.sub.func']) data, methodsdata = self.apb.prepare_class_pages(namespace_tree, - ['pkg.SomeSubClass', - 'pkg.SomeClass']) + ['main.SomeSubClass', + 'main.SomeClass']) # fake some stuff that would be built from other methods self.linker.set_link('', 'api/index.html') - self.linker.set_link('pkg', 'api/pkg.html') + self.linker.set_link('main', 'api/main.html') self.apb.build_class_pages(data, self.project) - clsfile = self.base.join('api/pkg.SomeClass.html') + clsfile = self.base.join('api/main.SomeClass.html') assert clsfile.check() html = clsfile.read() print html - # note that the root and the first level both have the same name (pkg) - # in our example project (XXX fixme!!) run_string_sequence_test(html, [ 'href="../style.css"', 'href="index.html">pkg', - 'href="pkg.html">pkg', - 'href="pkg.SomeClass.html">SomeClass', - 'href="pkg.SomeSubClass.html">SomeSubClass', + 'href="main.html">main', + 'href="main.SomeClass.html">SomeClass', + 'href="main.SomeSubClass.html">SomeSubClass', ]) - assert not 'href="pkg.sub.func.html"' in html + assert not 'href="main.sub.func.html"' in html py.test.skip('WOP from here') - assert 'href="pkg.sub.html">sub' in html + assert 'href="main.sub.html">sub' in html _checkhtml(html) def test_build_class_pages_base_link(self): - namespace_tree = create_namespace_tree(['pkg.SomeClass', - 'pkg.SomeSubClass', - 'pkg.sub.func']) + namespace_tree = create_namespace_tree(['main.SomeClass', + 'main.SomeSubClass', + 'main.sub.func']) data, methodsdata = self.apb.prepare_class_pages(namespace_tree, - ['pkg.SomeSubClass', - 'pkg.SomeClass']) + ['main.SomeSubClass', + 'main.SomeClass']) self.apb.build_class_pages(data, self.project) - clsfile = self.base.join('api/pkg.SomeSubClass.html') + clsfile = self.base.join('api/main.SomeSubClass.html') assert clsfile.check() html = clsfile.read() print html run_string_sequence_test(html, [ 'href="../style.css"', - 'href="pkg.SomeClass.html">pkg.SomeClass', + 'href="main.SomeClass.html">main.SomeClass', ]) _checkhtml(html) def test_source_links(self): - namespace_tree = create_namespace_tree(['pkg.SomeClass', - 'pkg.SomeSubClass', - 'pkg.sub.func']) + namespace_tree = create_namespace_tree(['main.SomeClass', + 'main.SomeSubClass', + 'main.sub.func']) data, methodsdata = self.apb.prepare_class_pages(namespace_tree, - ['pkg.SomeSubClass', - 'pkg.SomeClass']) + ['main.SomeSubClass', + 'main.SomeClass']) sourcedata = self.spb.prepare_pages(self.fs_root) self.apb.build_class_pages(data, self.project) self.spb.build_pages(sourcedata, self.project, self.fs_root) - funchtml = self.base.join('api/pkg.SomeClass.html').read() + funchtml = self.base.join('api/main.SomeClass.html').read() assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) def test_build_namespace_pages_index(self): - namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass']) data = self.apb.prepare_namespace_pages(namespace_tree) self.apb.prepare_class_pages(namespace_tree, - ['pkg.SomeClass', - 'pkg.SomeSubClass']) - self.apb.prepare_function_pages(namespace_tree, ['pkg.sub.func']) + ['main.SomeClass', + 'main.SomeSubClass']) + self.apb.prepare_function_pages(namespace_tree, ['main.sub.func']) self.apb.build_namespace_pages(data, self.project) - pkgfile = self.base.join('api/pkg.html') + pkgfile = self.base.join('api/index.html') assert pkgfile.check() html = pkgfile.read() + assert 'index of project pkg namespace' _checkhtml(html) def test_build_namespace_pages_subnamespace(self): - namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass']) data = self.apb.prepare_namespace_pages(namespace_tree) self.apb.build_namespace_pages(data, self.project) - subfile = self.base.join('api/pkg.sub.html') + subfile = self.base.join('api/main.sub.html') assert subfile.check() html = subfile.read() _checkhtml(html) def test_build_function_api_pages_nav(self): - namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass']) data = self.apb.prepare_function_pages(namespace_tree, - ['pkg.sub.func']) + ['main.sub.func']) self.linker.set_link('', 'api/index.html') - self.linker.set_link('pkg', 'api/pkg.html') - self.linker.set_link('pkg.sub', 'api/pkg.sub.html') + self.linker.set_link('main', 'api/main.html') + self.linker.set_link('main.sub', 'api/main.sub.html') self.apb.build_function_pages(data, self.project) - funcfile = self.base.join('api/pkg.sub.func.html') + funcfile = self.base.join('api/main.sub.func.html') html = funcfile.read() print html run_string_sequence_test(html, [ '', - '', - '', - '', + '', + '', + '', ]) _checkhtml(html) def test_build_function_navigation(self): - namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass']) self.apb.prepare_namespace_pages(namespace_tree) - self.apb.prepare_function_pages(namespace_tree, ['pkg.sub.func']) - nav = self.apb.build_navigation('pkg.sub', ['pkg.sub.func'], - 'pkg.sub.func') + self.apb.prepare_function_pages(namespace_tree, ['main.sub.func']) + nav = self.apb.build_navigation('main.sub', ['main.sub.func'], + 'main.sub.func') html = nav.unicode(indent=0) print html.encode('UTF-8') assert (u'' - u'
\xa0\xa0pkg
' + u'
\xa0\xa0main
' u'
\xa0\xa0\xa0\xa0' - u'sub
' + u'sub' u'
\xa0\xa0\xa0\xa0\xa0\xa0' - u'func
' + u'func' ) in html def test_build_root_namespace_view(self): - namespace_tree = create_namespace_tree(['pkg.sub', 'pkg.sub.func', - 'pkg.SomeClass', - 'pkg.SomeSubClass']) + namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass']) data = self.apb.prepare_namespace_pages(namespace_tree) self.apb.build_namespace_pages(data, self.project) rootfile = self.base.join('api/index.html') assert rootfile.check() html = rootfile.read() - assert '' in html + assert '' in html _checkhtml(html) class TestSourcePageBuilder(AbstractBuilderTest): Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Mon Jan 22 16:07:54 2007 @@ -4,46 +4,106 @@ """ import py -from test_apigen_example import setup_fs_project + +def setup_fs_project(): + temp = py.test.ensuretemp('apigen_functional') + temp.ensure("pkg/func.py").write(py.code.Source("""\ + def func(arg1): + "docstring" + + def func_2(arg1, arg2): + return arg1(arg2) + """)) + temp.ensure('pkg/sometestclass.py').write(py.code.Source("""\ + class SomeTestClass(object): + " docstring sometestclass " + def __init__(self, somevar): + self.somevar = somevar + + def get_somevar(self): + " get_somevar docstring " + return self.somevar + """)) + temp.ensure('pkg/sometestsubclass.py').write(py.code.Source("""\ + from sometestclass import SomeTestClass + class SomeTestSubClass(SomeTestClass): + " docstring sometestsubclass " + def get_somevar(self): + return self.somevar + 1 + """)) + temp.ensure("pkg/__init__.py").write(py.code.Source("""\ + from py.initpkg import initpkg + initpkg(__name__, exportdefs = { + 'main.sub.func': ("./func.py", "func"), + 'main.func': ("./func.py", "func_2"), + 'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'), + 'main.SomeTestSubClass': ('./sometestsubclass.py', 'SomeTestSubClass'), + }) + """)) + temp.ensure('pkg/test/test_pkg.py').write(py.code.Source("""\ + import py + py.std.sys.path.insert(0, + py.magic.autopath().dirpath().dirpath().dirpath().strpath) + import pkg + + # this mainly exists to provide some data to the tracer + def test_pkg(): + s = pkg.main.SomeTestClass(10) + assert s.get_somevar() == 10 + s = pkg.main.SomeTestClass('10') + assert s.get_somevar() == '10' + s = pkg.main.SomeTestSubClass(10) + assert s.get_somevar() == 11 + s = pkg.main.SomeTestSubClass('10') + py.test.raises(TypeError, 's.get_somevar()') + assert pkg.main.sub.func(10) is None + assert pkg.main.sub.func(20) is None + s = pkg.main.func(pkg.main.SomeTestClass, 10) + assert isinstance(s, pkg.main.SomeTestClass) + """)) + return temp, 'pkg' def test_apigen_functional(): - fs_root, package_name = setup_fs_project( - 'test_apigen_functional') + fs_root, package_name = setup_fs_project() tempdir = py.test.ensuretemp('test_apigen_functional_results') parentdir = py.magic.autopath().dirpath().dirpath() pkgdir = fs_root.join('pkg') - output = py.process.cmdexec( - 'APIGEN_TARGET="%s" py.test --session=L --apigen="%s/apigen.py" "%s"' % ( - tempdir, parentdir, pkgdir)) + try: + output = py.process.cmdexec('APIGEN_TARGET="%s" py.test --session=L ' + '--apigen="%s/apigen.py" "%s"' % ( + tempdir, parentdir, pkgdir)) + except py.error.Error, e: + print e.out + raise assert output.lower().find('traceback') == -1 # just some quick content checks apidir = tempdir.join('api') assert apidir.check(dir=True) - someclass_api = apidir.join('pkg.SomeClass.html') - assert someclass_api.check(file=True) - html = someclass_api.read() - assert 'SomeClass' in html + sometestclass_api = apidir.join('main.SomeTestClass.html') + assert sometestclass_api.check(file=True) + html = sometestclass_api.read() + assert 'SomeTestClass' in html # XXX not linking to method files anymore - #someclass_init_api = apidir.join('pkg.SomeClass.__init__.html') - #assert someclass_init_api.check(file=True) - #assert someclass_init_api.read().find( - # '__init__') > -1 - namespace_api = apidir.join('pkg.html') + #sometestclass_init_api = apidir.join('main.SomeTestClass.__init__.html') + #assert sometestclass_init_api.check(file=True) + #assert sometestclass_init_api.read().find( + # '__init__') > -1 + namespace_api = apidir.join('main.html') assert namespace_api.check(file=True) html = namespace_api.read() - assert 'SomeClass' in html + assert 'SomeTestClass' in html sourcedir = tempdir.join('source') assert sourcedir.check(dir=True) - someclass_source = sourcedir.join('someclass.py.html') - assert someclass_source.check(file=True) - html = someclass_source.read() - assert '
sources for someclass.py
' in html + sometestclass_source = sourcedir.join('sometestclass.py.html') + assert sometestclass_source.check(file=True) + html = sometestclass_source.read() + assert '
sources for sometestclass.py
' in html # XXX later... #index = sourcedir.join('index.html') #assert index.check(file=True) #html = index.read() - #assert 'pkg' in html + #assert 'main' in html From guido at codespeak.net Mon Jan 22 19:23:08 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 22 Jan 2007 19:23:08 +0100 (CET) Subject: [py-svn] r37155 - in py/dist/py/apigen: . testing Message-ID: <20070122182308.C99051007C@code0.codespeak.net> Author: guido Date: Mon Jan 22 19:23:07 2007 New Revision: 37155 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/testing/test_apigen_functional.py Log: Some improvements in function HTML, small cleanup in tests (there's now a single create_namespace_tree call done for all the apigen tests). Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Mon Jan 22 19:23:07 2007 @@ -288,32 +288,28 @@ """ build the html for a class method """ # XXX we may want to have seperate func = self.dsa.get_obj(dotted_name) - sourcefile = inspect.getsourcefile(func) docstring = func.__doc__ - local_methodname = func.__name__ + localname = func.__name__ argdesc = get_param_htmldesc(self.linker, func) valuedesc = self.build_callable_value_description(dotted_name) - + + sourcefile = inspect.getsourcefile(func) callable_source = self.dsa.get_function_source(dotted_name) - if callable_source: + # i assume they're both either available or unavailable(XXX ?) + if sourcefile and callable_source: csource = H.div(H.br(), - H.div('source:'), + H.a('source:', + href=self.linker.get_lazyhref(sourcefile)), + H.br(), H.SourceDef(H.pre(callable_source))) else: - csource = H.SourceDef('could not get source') - - if sourcefile is not None: - sourcelink = H.a('view source file', - href=self.linker.get_lazyhref(sourcefile)) - else: - sourcelink = H.span('no source file available') + csource = H.SourceDef('could not get source file') snippet = H.FunctionDescription( - H.FunctionDef(local_methodname, argdesc), + H.FunctionDef(localname, argdesc), valuedesc, - H.Docstring(docstring), + H.Docstring(docstring or H.em('no docstring available')), csource, - sourcelink, ) return snippet @@ -321,14 +317,20 @@ def build_class_view(self, dotted_name): """ build the html for a class """ cls = self.dsa.get_obj(dotted_name) + # XXX is this a safe check? try: sourcefile = inspect.getsourcefile(cls) except TypeError: - return H.div('builtin file') - if sourcefile is None: - return H.div('no source available') - if sourcefile[-1] in ['o', 'c']: - sourcefile = sourcefile[:-1] + sourcelink = 'builtin file, no source available' + else: + if sourcefile is None: + sourcelink = H.div('no source available') + else: + if sourcefile[-1] in ['o', 'c']: + sourcefile = sourcefile[:-1] + sourcelink = H.div(H.a('source', + href=self.linker.get_lazyhref(sourcefile))) + docstring = cls.__doc__ methods = self.dsa.get_class_methods(dotted_name) basehtml = [] @@ -345,11 +347,15 @@ if basehtml: basehtml.pop() basehtml.append('):') + if not hasattr(cls, '__name__'): + clsname = 'instance of %s' % (cls.__class__.__name__,) + else: + clsname = cls.__name__ snippet = H.ClassDescription( # XXX bases HTML - H.ClassDef('%s(' % (cls.__name__,), *basehtml), - H.a('source', href=self.linker.get_lazyhref(sourcefile)), - H.Docstring(docstring), + H.ClassDef('%s(' % (clsname,), *basehtml), + sourcelink, + H.Docstring(docstring or H.em('no docstring available')), ) snippet.append(H.h2('Functions:')) for method in methods: @@ -359,6 +365,7 @@ def build_namespace_view(self, namespace_dotted_name, item_dotted_names): """ build the html for a namespace (module) """ + print 'building namespace for', namespace_dotted_name snippet = H.NamespaceDescription( H.NamespaceDef(namespace_dotted_name), ) Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Mon Jan 22 19:23:07 2007 @@ -35,6 +35,7 @@ def get_somevar(self): " get_somevar docstring " return self.somevar + SomeInstance = SomeClass(10) """)) temp.ensure('pkg/somesubclass.py').write(py.code.Source("""\ from someclass import SomeClass @@ -43,12 +44,21 @@ def get_somevar(self): return self.somevar + 1 """)) + temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ + def foo(): + return 'bar' + def baz(qux): + return qux + """)) temp.ensure("pkg/__init__.py").write(py.code.Source("""\ from py.initpkg import initpkg initpkg(__name__, exportdefs = { 'main.sub.func': ("./func.py", "func"), 'main.SomeClass': ('./someclass.py', 'SomeClass'), + 'main.SomeInstance': ('./someclass.py', 'SomeInstance'), + 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), + 'other': ('./somenamespace.py', '*'), }) """)) return temp, 'pkg' @@ -96,6 +106,13 @@ self.linker = linker = LinkerForTests() self.apb = ApiPageBuilder(base, linker, self.dsa) self.spb = SourcePageBuilder(base, linker, self.fs_root) + self.namespace_tree = create_namespace_tree(['main.sub', + 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance', + 'other.foo', + 'other.bar']) class TestApiPageBuilder(AbstractBuilderTest): def test_build_callable_view(self): @@ -122,10 +139,7 @@ _checkhtmlsnippet(html) def test_build_function_pages(self): - namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', - 'main.SomeClass', - 'main.SomeSubClass']) - data = self.apb.prepare_function_pages(namespace_tree, + data = self.apb.prepare_function_pages(self.namespace_tree, ['main.sub.func']) self.apb.build_function_pages(data, self.project) funcfile = self.base.join('api/main.sub.func.html') @@ -139,10 +153,7 @@ _checkhtmlsnippet(html) def test_build_class_pages(self): - namespace_tree = create_namespace_tree(['main.SomeClass', - 'main.SomeSubClass', - 'main.sub.func']) - data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, ['main.SomeClass', 'main.SomeSubClass']) self.apb.build_class_pages(data, self.project) @@ -151,11 +162,22 @@ html = clsfile.read() _checkhtml(html) + def test_build_class_pages_instance(self): + data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance']) + self.apb.build_class_pages(data, self.project) + clsfile = self.base.join('api/main.SomeInstance.html') + assert clsfile.check() + html = clsfile.read() + print html + run_string_sequence_test(html, [ + 'instance of SomeClass()', + ]) + def test_build_class_pages_nav_links(self): - namespace_tree = create_namespace_tree(['main.SomeClass', - 'main.SomeSubClass', - 'main.sub.func']) - data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, ['main.SomeSubClass', 'main.SomeClass']) # fake some stuff that would be built from other methods @@ -179,10 +201,7 @@ _checkhtml(html) def test_build_class_pages_base_link(self): - namespace_tree = create_namespace_tree(['main.SomeClass', - 'main.SomeSubClass', - 'main.sub.func']) - data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, ['main.SomeSubClass', 'main.SomeClass']) self.apb.build_class_pages(data, self.project) @@ -197,10 +216,7 @@ _checkhtml(html) def test_source_links(self): - namespace_tree = create_namespace_tree(['main.SomeClass', - 'main.SomeSubClass', - 'main.sub.func']) - data, methodsdata = self.apb.prepare_class_pages(namespace_tree, + data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, ['main.SomeSubClass', 'main.SomeClass']) sourcedata = self.spb.prepare_pages(self.fs_root) @@ -210,15 +226,28 @@ assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) + def test_build_namespace_pages(self): + data = self.apb.prepare_namespace_pages(self.namespace_tree) + self.apb.build_namespace_pages(data, self.project) + mainfile = self.base.join('api/main.html') + assert mainfile.check() + html = mainfile.read() + print html + run_string_sequence_test(html, [ + 'index of main namespace', + ]) + otherfile = self.base.join('api/other.html') + assert otherfile.check() + otherhtml = otherfile.read() + print otherhtml + run_string_sequence_test(otherhtml, [ + 'index of other namespace', + ]) + _checkhtml(html) + _checkhtml(otherhtml) + def test_build_namespace_pages_index(self): - namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', - 'main.SomeClass', - 'main.SomeSubClass']) - data = self.apb.prepare_namespace_pages(namespace_tree) - self.apb.prepare_class_pages(namespace_tree, - ['main.SomeClass', - 'main.SomeSubClass']) - self.apb.prepare_function_pages(namespace_tree, ['main.sub.func']) + data = self.apb.prepare_namespace_pages(self.namespace_tree) self.apb.build_namespace_pages(data, self.project) pkgfile = self.base.join('api/index.html') assert pkgfile.check() @@ -227,10 +256,7 @@ _checkhtml(html) def test_build_namespace_pages_subnamespace(self): - namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', - 'main.SomeClass', - 'main.SomeSubClass']) - data = self.apb.prepare_namespace_pages(namespace_tree) + data = self.apb.prepare_namespace_pages(self.namespace_tree) self.apb.build_namespace_pages(data, self.project) subfile = self.base.join('api/main.sub.html') assert subfile.check() @@ -238,10 +264,7 @@ _checkhtml(html) def test_build_function_api_pages_nav(self): - namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', - 'main.SomeClass', - 'main.SomeSubClass']) - data = self.apb.prepare_function_pages(namespace_tree, + data = self.apb.prepare_function_pages(self.namespace_tree, ['main.sub.func']) self.linker.set_link('', 'api/index.html') self.linker.set_link('main', 'api/main.html') @@ -259,11 +282,8 @@ _checkhtml(html) def test_build_function_navigation(self): - namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', - 'main.SomeClass', - 'main.SomeSubClass']) - self.apb.prepare_namespace_pages(namespace_tree) - self.apb.prepare_function_pages(namespace_tree, ['main.sub.func']) + self.apb.prepare_namespace_pages(self.namespace_tree) + self.apb.prepare_function_pages(self.namespace_tree, ['main.sub.func']) nav = self.apb.build_navigation('main.sub', ['main.sub.func'], 'main.sub.func') html = nav.unicode(indent=0) @@ -277,10 +297,7 @@ ) in html def test_build_root_namespace_view(self): - namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', - 'main.SomeClass', - 'main.SomeSubClass']) - data = self.apb.prepare_namespace_pages(namespace_tree) + data = self.apb.prepare_namespace_pages(self.namespace_tree) self.apb.build_namespace_pages(data, self.project) rootfile = self.base.join('api/index.html') assert rootfile.check() Modified: py/dist/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_functional.py (original) +++ py/dist/py/apigen/testing/test_apigen_functional.py Mon Jan 22 19:23:07 2007 @@ -31,13 +31,20 @@ def get_somevar(self): return self.somevar + 1 """)) + temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ + def foo(): + return 'bar' + def baz(qux): + return qux + """)) temp.ensure("pkg/__init__.py").write(py.code.Source("""\ from py.initpkg import initpkg initpkg(__name__, exportdefs = { 'main.sub.func': ("./func.py", "func"), 'main.func': ("./func.py", "func_2"), 'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'), - 'main.SomeTestSubClass': ('./sometestsubclass.py', 'SomeTestSubClass'), + 'main.SomeTestSubClass': ('./sometestsubclass.py', + 'SomeTestSubClass'), }) """)) temp.ensure('pkg/test/test_pkg.py').write(py.code.Source("""\ From fijal at codespeak.net Mon Jan 22 21:21:12 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 21:21:12 +0100 (CET) Subject: [py-svn] r37159 - in py/branch/config/py: code test/rsession test/terminal Message-ID: <20070122202112.51D3710089@code0.codespeak.net> Author: fijal Date: Mon Jan 22 21:21:10 2007 New Revision: 37159 Added: py/branch/config/py/code/representation.py Modified: py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/terminal/terminal.py Log: First attempt to share some reporting code between terminal session and rsession. Added: py/branch/config/py/code/representation.py ============================================================================== --- (empty file) +++ py/branch/config/py/code/representation.py Mon Jan 22 21:21:10 2007 @@ -0,0 +1,34 @@ + +""" This file intends to gather all methods of representing +failures/tracebacks etc. which should be used among +all terminal-based reporters. This methods should be general, +to allow further use outside the pylib +""" + +# XXX: Probably it all should go as methods of proper +# objects + +class Presenter(object): + """ Class used for presentation of various objects, + sharing common output style + """ + def __init__(self, out): + """ out is a file-like object (we can write to it) + """ + assert hasattr(out, 'write') + self.out = out + + def repr_source(self, source, marker=">", marker_location=-1): + 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 = marker + " " + else: + prefix = " " + self.out.line(prefix + source[i]) + Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Mon Jan 22 21:21:10 2007 @@ -11,6 +11,7 @@ from py.__.test.rsession import report from py.__.test.rsession import outcome from py.__.misc.terminal_helper import ansi_print, get_terminal_width +from py.__.code.representation import Presenter import sys @@ -22,6 +23,7 @@ self.failed_tests_outcome = [] self.skipped_tests_outcome = [] self.out = getout(py.std.sys.stdout) + self.presenter = Presenter(self.out) 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]) @@ -154,20 +156,14 @@ for index, entry in py.builtin.enumerate(traceback): self.out.sep('-') self.out.line("%s: %s" % (entry.path, entry.lineno)) - self.repr_source(entry.relline, str(entry.source)) + self.presenter.repr_source(entry.source, marker_location= + entry.relline) elif self.config.option.tbstyle == 'short': for index, entry in py.builtin.enumerate(traceback): self.out.line("%s: %s" % (entry.path, entry.lineno)) self.out.line(entry.source) self.out.line("%s: %s" % (excinfo.typename, excinfo.value)) - def repr_source(self, relline, source): - for num, line in enumerate(source.split("\n")): - if num == relline: - self.out.line(">>>>" + line) - else: - self.out.line(" " + line) - def skips(self): texts = {} for event in self.skipped_tests_outcome: Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Mon Jan 22 21:21:10 2007 @@ -4,6 +4,7 @@ Item = py.test.Item from py.__.test.terminal.out import getout import py.__.code.safe_repr +from py.__.code.representation import Presenter def getrelpath(source, dest): base = source.common(dest) @@ -26,6 +27,7 @@ self._file = file self.out = getout(file) self._opencollectors = [] + self.presenter = Presenter(self.out) # --------------------- # PROGRESS information @@ -292,10 +294,10 @@ firstsourceline = entry.getfirstlinesource() marker_location = entry.lineno - firstsourceline if entry == last: - self.repr_source(source, 'E', marker_location) + self.presenter.repr_source(source, 'E', marker_location) self.repr_failure_explanation(excinfo, source) else: - self.repr_source(source, '>', marker_location) + self.presenter.repr_source(source, '>', marker_location) self.out.line("") self.out.line("[%s:%d]" %(entry.frame.code.path, entry.lineno+1)) self.repr_locals(entry) @@ -337,11 +339,11 @@ source = [] if entry == last: if source: - self.repr_source(source, 'E') + self.presenter.repr_source(source, 'E') self.repr_failure_explanation(excinfo, source) else: if source: - self.repr_source(source, ' ') + self.presenter.repr_source(source, ' ') self.repr_locals(entry) # trailing info @@ -378,26 +380,12 @@ self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath)) def getentrysource(self, entry): - try: - source = entry.getsource() + try: + source = entry.getsource() except py.error.ENOENT: source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) return source.deindent() - def repr_source(self, source, marker=">", marker_location=-1): - 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 = marker + " " - else: - prefix = " " - self.out.line(prefix + source[i]) - def repr_failure_explanation(self, excinfo, source): try: s = str(source.getstatement(len(source)-1)) From fijal at codespeak.net Mon Jan 22 21:41:00 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 21:41:00 +0100 (CET) Subject: [py-svn] r37160 - py/branch/config/py/test/rsession Message-ID: <20070122204100.314E910089@code0.codespeak.net> Author: fijal Date: Mon Jan 22 21:40:59 2007 New Revision: 37160 Removed: py/branch/config/py/test/rsession/cleanup-rsession.txt Log: This was implemented, that file might go away. Deleted: /py/branch/config/py/test/rsession/cleanup-rsession.txt ============================================================================== --- /py/branch/config/py/test/rsession/cleanup-rsession.txt Mon Jan 22 21:40:59 2007 +++ (empty file) @@ -1,42 +0,0 @@ -Quick overview to make myself sure what I'm talking about. - -exit cases: - -1. normal exit (tests finished) -2. -x and a failing test seen at sending side -3. C-c pressed, or SystemExit raised - -exit conditions: - -- normal cleanup (exit) -- cleanup of remote nodes (rsession only) - possibly kill child processes - and close gateway nicely -- cleanup of web server (only regarding to web interface) - probably later - -Normal exit: - -easy :), just works - --x and a failing test: - -works, but with a hack - -C-c hard case: - -we should have different layers of C-c pressed, to make sure that we can -interrupt teardown and still exit cleanly, that's why I don't like the idea -of having finally: beacause exceptions are ignored inside finally. - -We should kill Info.pid hack, which is quite awfull usecase of global -variable. - -So: - - first strike of C-c should go to hosts_teardown and start sending nodes info - that they should kill boxes. - - second strike should exit this teardown, print some message like "abandoned" - and eventually try to kill web server. - - third strike should just exit program. - From fijal at codespeak.net Mon Jan 22 21:42:42 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 22 Jan 2007 21:42:42 +0100 (CET) Subject: [py-svn] r37161 - py/branch/config/py/test/rsession Message-ID: <20070122204242.755CB10089@code0.codespeak.net> Author: fijal Date: Mon Jan 22 21:42:41 2007 New Revision: 37161 Modified: py/branch/config/py/test/rsession/reporter.py Log: Typo in docstring. Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Mon Jan 22 21:42:41 2007 @@ -2,7 +2,7 @@ """ reporter - different reporter for different purposes ;-) Still lacks: - 1. Hanging nodes are not good done + 1. Hanging nodes are not done well """ import py From fijal at codespeak.net Tue Jan 23 00:27:51 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 00:27:51 +0100 (CET) Subject: [py-svn] r37163 - in py/branch/config/py: code test/rsession test/terminal Message-ID: <20070122232751.9F99510092@code0.codespeak.net> Author: fijal Date: Tue Jan 23 00:27:49 2007 New Revision: 37163 Modified: py/branch/config/py/code/representation.py py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/terminal/terminal.py Log: Move another method to a common place. Modified: py/branch/config/py/code/representation.py ============================================================================== --- py/branch/config/py/code/representation.py (original) +++ py/branch/config/py/code/representation.py Tue Jan 23 00:27:49 2007 @@ -19,6 +19,9 @@ self.out = out def repr_source(self, source, marker=">", marker_location=-1): + """ This one represents piece of source with possible + marker at requested position + """ if marker_location < 0: marker_location += len(source) if marker_location < 0: @@ -32,3 +35,18 @@ prefix = " " self.out.line(prefix + source[i]) + def repr_item_info(self, item): + """ This method represents py.test.Item info (path and module) + """ + root = item.fspath + modpath = item.getmodpath() + try: + fn, lineno = item.getpathlineno() + except TypeError: + assert isinstance(item.parent, py.test.collect.Generator) + # a generative test yielded a non-callable + fn, lineno = item.parent.getpathlineno() + if root == fn: + self.out.sep("_", "entrypoint: %s" %(modpath)) + else: + self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath)) Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Tue Jan 23 00:27:49 2007 @@ -112,6 +112,7 @@ host = self.gethost(event) self.out.sep('_', "%s on %s" % (" ".join(event.item.listnames()), host)) + self.presenter.repr_item_info(event.item) if event.outcome.signal: self.repr_signal(event.item, event.outcome) else: @@ -156,7 +157,8 @@ for index, entry in py.builtin.enumerate(traceback): self.out.sep('-') self.out.line("%s: %s" % (entry.path, entry.lineno)) - self.presenter.repr_source(entry.source, marker_location= + source = entry.source.split("\n") + self.presenter.repr_source(source, marker_location= entry.relline) elif self.config.option.tbstyle == 'short': for index, entry in py.builtin.enumerate(traceback): Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Tue Jan 23 00:27:49 2007 @@ -284,9 +284,9 @@ last = traceback[-1] first = traceback[0] for index, entry in py.builtin.enumerate(traceback): - if entry == first: + if entry == first: if item: - self.repr_failure_info(item, entry) + self.presenter.repr_item_info(item) self.out.line() else: self.out.line("") @@ -304,8 +304,6 @@ # trailing info if entry == last: - #if item: - # self.repr_failure_info(item, entry) self.repr_out_err(item) self.out.sep("_") else: @@ -348,8 +346,6 @@ # trailing info if entry == last: - #if item: - # self.repr_failure_info(item, entry) self.repr_out_err(item) self.out.sep("_") else: @@ -361,24 +357,6 @@ # the following is only used by the combination '--pdb --tb=no' repr_failure_tbno = repr_failure_tbshort - def repr_failure_info(self, item, entry): - root = item.fspath - modpath = item.getmodpath() - try: - fn, lineno = item.getpathlineno() - except TypeError: - assert isinstance(item.parent, py.test.collect.Generator) - # a generative test yielded a non-callable - fn, lineno = item.parent.getpathlineno() - # hum, the following overloads traceback output - #if fn != entry.frame.code.path or \ - # entry.frame.code.firstlineno != lineno: - # self.out.line("testcode: %s:%d" % (fn, lineno+1)) - if root == fn: - self.out.sep("_", "entrypoint: %s" %(modpath)) - else: - self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath)) - def getentrysource(self, entry): try: source = entry.getsource() From cfbolz at codespeak.net Tue Jan 23 10:32:02 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 23 Jan 2007 10:32:02 +0100 (CET) Subject: [py-svn] r37169 - py/dist/py/rest Message-ID: <20070123093202.BECD61008B@code0.codespeak.net> Author: cfbolz Date: Tue Jan 23 10:32:01 2007 New Revision: 37169 Modified: py/dist/py/rest/rst.py Log: typo Modified: py/dist/py/rest/rst.py ============================================================================== --- py/dist/py/rest/rst.py (original) +++ py/dist/py/rest/rst.py Tue Jan 23 10:32:01 2007 @@ -111,7 +111,7 @@ self.links = {} def render_links(self, check=False): - """render the link attachents of the document""" + """render the link attachments of the document""" assert not check, "Link checking not implemented" if not self.links: return "" From fijal at codespeak.net Tue Jan 23 10:49:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 10:49:11 +0100 (CET) Subject: [py-svn] r37170 - py/branch/config/py/test Message-ID: <20070123094911.F20C81008B@code0.codespeak.net> Author: fijal Date: Tue Jan 23 10:49:10 2007 New Revision: 37170 Modified: py/branch/config/py/test/defaultconftest.py Log: Those options does not require subprocess Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Tue Jan 23 10:49:10 2007 @@ -66,21 +66,6 @@ Option('', '--traceconfig', action="store_true", dest="traceconfig", default=False, help="trace considerations of conftest.py files."), - ) - - py.test.config.addoptions('EXPERIMENTAL options implying subprocesses', - Option('', '--tkinter', - action="store_true", dest="tkinter", default=False, - help="use tkinter test session frontend."), - Option('', '--dist', - action="store_true", dest="dist", default=False, - help="use ad-hoc distributed testing (requires conftest settings)"), - Option('', '--looponfailing', - action="store_true", dest="looponfailing", default=False, - help="loop on failing test set."), - Option('', '--exec', - action="store", dest="executable", default=None, - help="python executable to run the tests with."), Option('-w', '--startserver', action="store_true", dest="startserver", default=False, help="start HTTP server listening on localhost:8000 for test." @@ -100,4 +85,19 @@ action="store", dest="session", default=None, help="lookup sessioname in conftest.py files and use it."), ) + + py.test.config.addoptions('EXPERIMENTAL options implying subprocesses', + Option('', '--tkinter', + action="store_true", dest="tkinter", default=False, + help="use tkinter test session frontend."), + Option('', '--dist', + action="store_true", dest="dist", default=False, + help="use ad-hoc distributed testing (requires conftest settings)"), + Option('', '--looponfailing', + action="store_true", dest="looponfailing", default=False, + help="loop on failing test set."), + Option('', '--exec', + action="store", dest="executable", default=None, + help="python executable to run the tests with."), + ) From fijal at codespeak.net Tue Jan 23 11:25:39 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 11:25:39 +0100 (CET) Subject: [py-svn] r37171 - py/branch/config/py/test Message-ID: <20070123102539.3F50E10089@code0.codespeak.net> Author: fijal Date: Tue Jan 23 11:25:37 2007 New Revision: 37171 Modified: py/branch/config/py/test/defaultconftest.py Log: Add an option for boxing, XXX should be also --no-box to override conftest decision. Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Tue Jan 23 11:25:37 2007 @@ -93,6 +93,9 @@ Option('', '--dist', action="store_true", dest="dist", default=False, help="use ad-hoc distributed testing (requires conftest settings)"), + Option('', '--box', + action="store_true", dest="boxing", + help="use boxing (running each test in external process)"), Option('', '--looponfailing', action="store_true", dest="looponfailing", default=False, help="loop on failing test set."), From fijal at codespeak.net Tue Jan 23 11:26:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 11:26:11 +0100 (CET) Subject: [py-svn] r37172 - in py/branch/config/py/test: . testing Message-ID: <20070123102611.8FB7A1008A@code0.codespeak.net> Author: fijal Date: Tue Jan 23 11:26:10 2007 New Revision: 37172 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/testing/test_config.py Log: Boxing implies lsession Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Tue Jan 23 11:26:10 2007 @@ -135,18 +135,23 @@ elif self.option.tkinter: name = 'TkinterSession' else: - optnames = 'startserver runbrowser apigen restreport'.split() + optnames = 'startserver runbrowser apigen restreport boxing'.split() for opt in optnames: if getattr(self.option, opt, False): name = 'LSession' break else: + if self.getvalue('dist_boxing'): + name = 'LSession' if self.option.looponfailing: name = 'RemoteTerminalSession' elif self.option.executable: name = 'RemoteTerminalSession' return name + def is_boxed(self): + return self.getvalue("dist_boxing") or self.config.option.boxing + def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ global config_per_process Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Tue Jan 23 11:26:10 2007 @@ -181,8 +181,8 @@ assert config._getsessionname() == 'RSession' def test_implied_lsession(self): - optnames = 'startserver runbrowser apigen=x rest'.split() - for x in optnames: + optnames = 'startserver runbrowser apigen=x rest box'.split() + for x in optnames: config = py.test.config._reparse([self.tmpdir, '--%s' % x]) assert config._getsessionname() == 'LSession' From fijal at codespeak.net Tue Jan 23 11:26:33 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 11:26:33 +0100 (CET) Subject: [py-svn] r37173 - py/branch/config/py/test/rsession Message-ID: <20070123102633.998B51008E@code0.codespeak.net> Author: fijal Date: Tue Jan 23 11:26:32 2007 New Revision: 37173 Modified: py/branch/config/py/test/rsession/rsession.py Log: Adhere to --box option. Modified: py/branch/config/py/test/rsession/rsession.py ============================================================================== --- py/branch/config/py/test/rsession/rsession.py (original) +++ py/branch/config/py/test/rsession/rsession.py Tue Jan 23 11:26:32 2007 @@ -279,7 +279,8 @@ self.tracer = Tracer(self.docstorage) return apigen_runner else: - if self.config.getvalue('dist_boxing'): + if (self.config.getvalue('dist_boxing') or self.config.option.boxing)\ + and not self.config.option.nocapture: return box_runner return plain_runner From fijal at codespeak.net Tue Jan 23 11:26:54 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 11:26:54 +0100 (CET) Subject: [py-svn] r37174 - py/branch/config/py/test/testing Message-ID: <20070123102654.3A9F510089@code0.codespeak.net> Author: fijal Date: Tue Jan 23 11:26:53 2007 New Revision: 37174 Modified: py/branch/config/py/test/testing/test_collect.py Log: Skip those tests if boxing is enabled Modified: py/branch/config/py/test/testing/test_collect.py ============================================================================== --- py/branch/config/py/test/testing/test_collect.py (original) +++ py/branch/config/py/test/testing/test_collect.py Tue Jan 23 11:26:53 2007 @@ -266,6 +266,8 @@ assert len(l) == 1 def test_order_of_execution_generator_same_codeline(): + if py.test.config.is_boxed(): + py.test.skip("Does not work with boxing") test_list = [] expected_list = range(6) @@ -283,6 +285,8 @@ def test_order_of_execution_generator_different_codeline(): + if py.test.config.is_boxed(): + py.test.skip("Does not work with boxing") test_list = [] expected_list = range(3) From fijal at codespeak.net Tue Jan 23 11:27:45 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 11:27:45 +0100 (CET) Subject: [py-svn] r37175 - py/branch/config/py/test/rsession/testing Message-ID: <20070123102745.4E6221008A@code0.codespeak.net> Author: fijal Date: Tue Jan 23 11:27:44 2007 New Revision: 37175 Modified: py/branch/config/py/test/rsession/testing/test_lsession.py Log: - Make test safer, so pdb is not overwritten (boxing jumps over it, but --pdb forces boxing to disappear) - Write down a test for runner chooser. Modified: py/branch/config/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_lsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_lsession.py Tue Jan 23 11:27:44 2007 @@ -5,7 +5,7 @@ import py from py.__.test.rsession.rsession import LSession from py.__.test.rsession import report -from py.__.test.rsession.local import box_runner, plain_runner +from py.__.test.rsession.local import box_runner, plain_runner, apigen_runner def setup_module(mod): mod.tmp = py.test.ensuretemp("lsession_module") @@ -82,22 +82,26 @@ l = [] def some_fun(*args): l.append(args) - - pdb.post_mortem = some_fun - args = [str(tmpdir.join(subdir)), '--pdb'] - config = py.test.config._reparse(args) - lsession = LSession(config) - allevents = [] + try: - lsession.main(reporter=allevents.append, runner=plain_runner) - except SystemExit: - pass - else: - py.test.fail("Didn't raise system exit") - failure_events = [event for event in allevents if isinstance(event, - report.ImmediateFailure)] - assert len(failure_events) == 1 - assert len(l) == 1 + post_mortem = pdb.post_mortem + pdb.post_mortem = some_fun + args = [str(tmpdir.join(subdir)), '--pdb'] + config = py.test.config._reparse(args) + lsession = LSession(config) + allevents = [] + try: + lsession.main(reporter=allevents.append, runner=plain_runner) + except SystemExit: + pass + else: + py.test.fail("Didn't raise system exit") + failure_events = [event for event in allevents if isinstance(event, + report.ImmediateFailure)] + assert len(failure_events) == 1 + assert len(l) == 1 + finally: + pdb.post_mortem = post_mortem def test_minus_x(self): if not hasattr(py.std.os, 'fork'): @@ -262,3 +266,31 @@ assert testevents[0].outcome.passed assert testevents[0].outcome.stderr == "" assert testevents[0].outcome.stdout == "1\n2\n3\n" + + def test_runner_selection(self): + tmpdir = py.test.ensuretemp("lsession_runner_selection") + tmpdir.ensure("apigen.py").write(py.code.Source(""" + def get_documentable_items(*args): + return {} + """)) + opt_mapping = { + '': plain_runner, + '--box': box_runner, + '--apigen=%s/apigen.py' % str(tmpdir): apigen_runner, + } + pkgdir = tmpdir.dirpath() + for opt in opt_mapping.keys(): + if opt: + all = opt + " " + str(tmpdir) + else: + all = str(tmpdir) + config = py.test.config._reparse(all.split(" ")) + lsession = LSession(config) + assert lsession.init_runner(pkgdir) is opt_mapping[opt] + #tmpdir.dirpath().ensure("conftest.py").write(py.code.Source(""" + #dist_boxing=True + #""")) + #config = py.test.config._reparse([str(tmpdir)]) + #lsession = LSession(config) + #assert lsession.init_runner(pkgdir) is box_runner + # XXX check why it fails From fijal at codespeak.net Tue Jan 23 11:29:23 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 11:29:23 +0100 (CET) Subject: [py-svn] r37176 - in py/branch/config/py: code test/rsession test/terminal Message-ID: <20070123102923.A435E10089@code0.codespeak.net> Author: fijal Date: Tue Jan 23 11:29:21 2007 New Revision: 37176 Modified: py/branch/config/py/code/representation.py py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/terminal/terminal.py Log: Move one method from terminal outside to the representation.py. Now rsession should use this one as well. Modified: py/branch/config/py/code/representation.py ============================================================================== --- py/branch/config/py/code/representation.py (original) +++ py/branch/config/py/code/representation.py Tue Jan 23 11:29:21 2007 @@ -12,16 +12,20 @@ """ Class used for presentation of various objects, sharing common output style """ - def __init__(self, out): + def __init__(self, out, config): """ out is a file-like object (we can write to it) """ assert hasattr(out, 'write') self.out = out + self.config = config def repr_source(self, source, marker=">", marker_location=-1): """ This one represents piece of source with possible marker at requested position """ + if isinstance(source, str): + # why the hell, string is iterable? + source = source.split("\n") if marker_location < 0: marker_location += len(source) if marker_location < 0: @@ -50,3 +54,44 @@ self.out.sep("_", "entrypoint: %s" %(modpath)) else: self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath)) + + 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()))) + # get the real exception information out + lines = excinfo.exconly(tryshort=True).split('\n') + self.out.line('>' + indent[:-1] + lines.pop(0)) + for x in lines: + self.out.line(indent + x) + return + + # XXX reinstate the following with a --magic option? + # the following line gets user-supplied messages (e.g. + # for "assert 0, 'custom message'") + msg = getattr(getattr(excinfo, 'value', ''), 'msg', '') + info = None + if not msg: + special = excinfo.errisinstance((SyntaxError, SystemExit, KeyboardInterrupt)) + if not self.config.option.nomagic and not special: + try: + info = excinfo.traceback[-1].reinterpret() # very detailed info + except KeyboardInterrupt: + raise + except: + if self.config.option.verbose >= 1: + self.out.line("[reinterpretation traceback]") + py.std.traceback.print_exc(file=py.std.sys.stdout) + else: + self.out.line("[reinterpretation failed, increase " + "verbosity to see details]") + # print reinterpreted info if any + if info: + lines = info.split('\n') + self.out.line('>' + indent[:-1] + lines.pop(0)) + for x in lines: + self.out.line(indent + x) Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Tue Jan 23 11:29:21 2007 @@ -23,7 +23,7 @@ self.failed_tests_outcome = [] self.skipped_tests_outcome = [] self.out = getout(py.std.sys.stdout) - self.presenter = Presenter(self.out) + self.presenter = Presenter(self.out, config) 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]) @@ -125,7 +125,7 @@ def gethost(self, event): return event.host.hostname - def repr_failure(self, item, outcome): + def repr_failure(self, item, outcome): excinfo = outcome.excinfo traceback = excinfo.traceback #if item and not self.config.option.fulltrace: Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Tue Jan 23 11:29:21 2007 @@ -27,7 +27,7 @@ self._file = file self.out = getout(file) self._opencollectors = [] - self.presenter = Presenter(self.out) + self.presenter = Presenter(self.out, config) # --------------------- # PROGRESS information @@ -295,7 +295,7 @@ marker_location = entry.lineno - firstsourceline if entry == last: self.presenter.repr_source(source, 'E', marker_location) - self.repr_failure_explanation(excinfo, source) + self.presenter.repr_failure_explanation(excinfo, source) else: self.presenter.repr_source(source, '>', marker_location) self.out.line("") @@ -338,7 +338,7 @@ if entry == last: if source: self.presenter.repr_source(source, 'E') - self.repr_failure_explanation(excinfo, source) + self.presenter.repr_failure_explanation(excinfo, source) else: if source: self.presenter.repr_source(source, ' ') @@ -364,47 +364,6 @@ source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) return source.deindent() - 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()))) - # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - self.out.line('>' + indent[:-1] + lines.pop(0)) - for x in lines: - self.out.line(indent + x) - return - - # XXX reinstate the following with a --magic option? - # the following line gets user-supplied messages (e.g. - # for "assert 0, 'custom message'") - msg = getattr(getattr(excinfo, 'value', ''), 'msg', '') - info = None - if not msg: - special = excinfo.errisinstance((SyntaxError, SystemExit, KeyboardInterrupt)) - if not self.config.option.nomagic and not special: - try: - info = excinfo.traceback[-1].reinterpret() # very detailed info - except KeyboardInterrupt: - raise - except: - if self.config.option.verbose >= 1: - self.out.line("[reinterpretation traceback]") - py.std.traceback.print_exc(file=py.std.sys.stdout) - else: - self.out.line("[reinterpretation failed, increase " - "verbosity to see details]") - # print reinterpreted info if any - if info: - lines = info.split('\n') - self.out.line('>' + indent[:-1] + lines.pop(0)) - for x in lines: - self.out.line(indent + x) - def repr_out_err(self, colitem): for parent in colitem.listchain(): for name, obj in zip(['out', 'err'], parent.getouterr()): From fijal at codespeak.net Tue Jan 23 11:38:50 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 11:38:50 +0100 (CET) Subject: [py-svn] r37178 - py/branch/config/py/test/rsession Message-ID: <20070123103850.8DD3910089@code0.codespeak.net> Author: fijal Date: Tue Jan 23 11:38:39 2007 New Revision: 37178 Modified: py/branch/config/py/test/rsession/rest.py Log: Make it a bit more working. Modified: py/branch/config/py/test/rsession/rest.py ============================================================================== --- py/branch/config/py/test/rsession/rest.py (original) +++ py/branch/config/py/test/rsession/rest.py Tue Jan 23 11:38:39 2007 @@ -26,18 +26,23 @@ if self.config.option.verbose: self.add_rest(Paragraph("Unknown report: %s" % what)) + def gethost(self, item): + if item.channel: + return item.channel.gateway.host + return self.hosts[0] + def report_SendItem(self, item): - address = item.channel.gateway.host.hostname + address = self.gethost(item) if self.config.option.verbose: self.add_rest(Paragraph('sending item %s to %s' % (item.item, address))) def report_HostRSyncing(self, item): - self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.hostname[:10], + self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10], item.remoterootpath))) def report_HostReady(self, item): - self.add_rest(LiteralBlock('%10s: READY' % (item.hostname[:10],))) + self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],))) def report_TestStarted(self, event): txt = "Running tests on hosts: %s" % ", ".join(event.hosts) @@ -86,7 +91,7 @@ self.out.write(self.rest.render_links()) def report_ReceivedItemOutcome(self, event): - host = event.channel.gateway.host + host = self.gethost(event) if event.outcome.passed: status = [Strong("PASSED")] self.passed[host] += 1 From guido at codespeak.net Tue Jan 23 11:42:46 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 23 Jan 2007 11:42:46 +0100 (CET) Subject: [py-svn] r37180 - py/dist/py/test/rsession Message-ID: <20070123104246.CFB3410089@code0.codespeak.net> Author: guido Date: Tue Jan 23 11:42:45 2007 New Revision: 37180 Modified: py/dist/py/test/rsession/rest.py Log: Using RelLinkWriter when no 'linkwriter' config option is set. Modified: py/dist/py/test/rsession/rest.py ============================================================================== --- py/dist/py/test/rsession/rest.py (original) +++ py/dist/py/test/rsession/rest.py Tue Jan 23 11:42:45 2007 @@ -19,7 +19,12 @@ def get_linkwriter(self): if self.linkwriter is None: - self.linkwriter = self.config.getvalue('linkwriter') + try: + self.linkwriter = self.config.getvalue('linkwriter') + except KeyError: + print >>sys.stderr, ('no linkwriter configured, using default ' + 'one') + self.linkwriter = RelLinkWriter() return self.linkwriter def report_unknown(self, what): From guido at codespeak.net Tue Jan 23 13:14:05 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 23 Jan 2007 13:14:05 +0100 (CET) Subject: [py-svn] r37184 - py/dist/py/apigen Message-ID: <20070123121405.F17E410089@code0.codespeak.net> Author: guido Date: Tue Jan 23 13:14:03 2007 New Revision: 37184 Modified: py/dist/py/apigen/apigen.py Log: Outputting to a more sane default directory: the parent of the pkgdir. Modified: py/dist/py/apigen/apigen.py ============================================================================== --- py/dist/py/apigen/apigen.py (original) +++ py/dist/py/apigen/apigen.py Tue Jan 23 13:14:03 2007 @@ -31,7 +31,7 @@ if 'APIGEN_TARGET' in os.environ: targetdir = py.path.local(os.environ['APIGEN_TARGET']) else: - targetdir = py.path.local('/tmp/py_apigen_output') + targetdir = pkgdir.dirpath().join('apigen') targetdir.ensure(dir=True) all_names = dsa._get_names(filter=lambda x, y: True) From hpk at codespeak.net Tue Jan 23 14:53:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 14:53:19 +0100 (CET) Subject: [py-svn] r37193 - in py/branch/config/py/test: . rsession terminal testing Message-ID: <20070123135319.42B2810075@code0.codespeak.net> Author: hpk Date: Tue Jan 23 14:53:17 2007 New Revision: 37193 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/testing/test_config.py py/branch/config/py/test/testing/test_conftesthandle.py py/branch/config/py/test/testing/test_session.py Log: introduced config.topdir, tests and use it from terminal/remote at least. also fix fijal's "--box" option handling a bit, but i am not sure it should stay this way at all. Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Tue Jan 23 14:53:17 2007 @@ -50,8 +50,7 @@ fixoptions(self.option) # XXX fixing should be moved to sessions if not args: args.append(py.std.os.getcwd()) - args = [py.path.local(x) for x in args] - self.topdir = reduce(py.path.local.common, args) + self.topdir = gettopdir(args) self.args = args def initdirect(self, topdir, repr, coltrails=None): @@ -150,7 +149,8 @@ return name def is_boxed(self): - return self.getvalue("dist_boxing") or self.config.option.boxing + # XXX probably not a good idea to have this special function ... + return self.option.boxing or self.getvalue("dist_boxing") def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ @@ -248,3 +248,16 @@ raise ValueError, "--exec together with --pdb not supported yet." +def gettopdir(args): + """ return the top directory for the given paths. + if the common base dir resides in a python package + parent directory of the root package is returned. + """ + args = [py.path.local(arg) for arg in args] + p = reduce(py.path.local.common, args) + assert p, "cannot determine common basedir of %s" %(args,) + pkgdir = p.pypkgpath() + if pkgdir is None: + return p + else: + return pkgdir.dirpath() Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Tue Jan 23 14:53:17 2007 @@ -51,6 +51,7 @@ 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) Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Tue Jan 23 14:53:17 2007 @@ -51,24 +51,6 @@ l.append(current) return l -def generalize(p1, p2): - general = p1 - for x, y in zip(p1.parts(), p2.parts()): - if x != y: - break - general = x - return general - -def getrootdir(args): - tops = [] - for arg in args: - p = py.path.local(arg) - tops.append(p.pypkgpath() or p) - p = reduce(generalize, tops) - if p.check(file=1): - p = p.dirpath() - return p - class RemoteTerminalSession(object): def __init__(self, config, file=None): self.config = config @@ -88,7 +70,7 @@ self.executable = executable def main(self): - rootdir = getrootdir(self.config.args) + rootdir = self.config.topdir wasfailing = False failures = [] while 1: Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Tue Jan 23 14:53:17 2007 @@ -1,6 +1,8 @@ from __future__ import generators import py +from py.__.test.config import gettopdir + def test_tmpdir(): d1 = py.test.ensuretemp('hello') d2 = py.test.ensuretemp('hello') @@ -83,13 +85,20 @@ config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 -def test_topdir(): +def test_gettopdir(): tmp = py.test.ensuretemp("topdir") - config = py.test.config._reparse([tmp]) - assert config.topdir == tmp - config = py.test.config._reparse([tmp.join('hello'), - tmp.join("world")]) - assert config.topdir == tmp + assert gettopdir([tmp]) == tmp + topdir =gettopdir([tmp.join("hello"), tmp.join("world")]) + assert topdir == tmp + +def test_gettopdir_pypkg(): + 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') + Z = tmp.ensure('Z', dir=1) + assert gettopdir([c]) == a + assert gettopdir([c, Z]) == tmp def test_config_init_direct(): py.test.skip("XXX partially not implemented") @@ -218,6 +227,24 @@ session = config.initsession() assert session.config is config + def test_boxing_options(self): + # XXX config.is_boxed() is probably not a good idea + tmpdir = self.tmpdir + config = py.test.config._reparse([tmpdir]) + assert not config.option.boxing + assert not config.is_boxed() + + #tmpdir.join("conftest.py").write("dist_boxing=True\n") + #config = py.test.config._reparse([tmpdir]) + #assert config.is_boxed() + + tmpdir.join("conftest.py").write("dist_boxing=False\n") + config = py.test.config._reparse([tmpdir]) + assert not config.is_boxed() + config = py.test.config._reparse([tmpdir, '--box']) + assert config.is_boxed() + + class TestConfigColitems: def setup_class(cls): cls.tmproot = py.test.ensuretemp(cls.__name__) Modified: py/branch/config/py/test/testing/test_conftesthandle.py ============================================================================== --- py/branch/config/py/test/testing/test_conftesthandle.py (original) +++ py/branch/config/py/test/testing/test_conftesthandle.py Tue Jan 23 14:53:17 2007 @@ -11,6 +11,11 @@ d.ensure("adir/conftest.py").write("a=1 ; Directory = 3") d.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") + def test_basic_init(self): + conftest = Conftest() + conftest.setinitial([self.basedir.join("adir")]) + assert conftest.rget("a") == 1 + def test_immediate_initialiation_and_incremental_are_the_same(self): conftest = Conftest() snap0 = len(conftest._path2confmods) Modified: py/branch/config/py/test/testing/test_session.py ============================================================================== --- py/branch/config/py/test/testing/test_session.py (original) +++ py/branch/config/py/test/testing/test_session.py Tue Jan 23 14:53:17 2007 @@ -293,9 +293,8 @@ assert expected_output in out -from py.__.test.terminal.remote import getrootdir class TestRemote: - def test_rootdir_is_package(self): + def XXXtest_rootdir_is_package(self): d = tmpdir.ensure('rootdirtest1', dir=1) d.ensure('__init__.py') x1 = d.ensure('subdir', '__init__.py') @@ -307,7 +306,7 @@ assert getrootdir([x3,x2]) == d assert getrootdir([x2,x3]) == d - def test_rootdir_is_not_package(self): + def XXXtest_rootdir_is_not_package(self): one = tmpdir.ensure('rootdirtest1', 'hello') rootdir = getrootdir([one]) assert rootdir == one.dirpath() From fijal at codespeak.net Tue Jan 23 15:51:21 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 15:51:21 +0100 (CET) Subject: [py-svn] r37195 - py/branch/config/py/test/rsession/testing Message-ID: <20070123145121.59F0610069@code0.codespeak.net> Author: fijal Date: Tue Jan 23 15:51:19 2007 New Revision: 37195 Modified: py/branch/config/py/test/rsession/testing/test_rest.py Log: Skip those tests, as I need deeper look at them. Modified: py/branch/config/py/test/rsession/testing/test_rest.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rest.py (original) +++ py/branch/config/py/test/rsession/testing/test_rest.py Tue Jan 23 15:51:19 2007 @@ -10,6 +10,8 @@ from py.__.rest.rst import * from py.__.test.rsession.hostmanage import HostInfo +py.test.skip("This tests are not really testing, needs rewrite") + class RestTestReporter(RestReporter): def __init__(self, *args, **kwargs): if args: From guido at codespeak.net Tue Jan 23 15:55:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 23 Jan 2007 15:55:53 +0100 (CET) Subject: [py-svn] r37196 - py/dist/py/doc Message-ID: <20070123145553.F41C310069@code0.codespeak.net> Author: guido Date: Tue Jan 23 15:55:52 2007 New Revision: 37196 Modified: py/dist/py/doc/conftest.py py/dist/py/doc/test_conftest.py Log: Added textrole for links to apigen documentation. Modified: py/dist/py/doc/conftest.py ============================================================================== --- py/dist/py/doc/conftest.py (original) +++ py/dist/py/doc/conftest.py Tue Jan 23 15:55:52 2007 @@ -1,6 +1,7 @@ from __future__ import generators import py from py.__.misc import rest +from py.__.rest import directive Option = py.test.config.Option option = py.test.config.addoptions("documentation check options", @@ -213,3 +214,27 @@ if p.check(file=1): return self.ReSTChecker(p, parent=self) Directory = DocDirectory + +def resolve_linkrole(name, text): + if name == 'api': + if text == 'py': + return 'py', '../../apigen/api/index.html' + assert text.startswith('py.'), ( + 'api link "%s" does not point to the py package') % (text,) + dotted_name = text + if dotted_name.find('(') > -1: + dotted_name = dotted_name[:text.find('(')] + dotted_name = '.'.join(dotted_name.split('.')[1:]) # remove pkg root + return text, '../../apigen/api/%s.html' % (dotted_name,) + elif name == 'source': + assert text.startswith('py/'), ('source link "%s" does not point ' + 'to the py package') % (text,) + relpath = '/'.join(text.split('/')[1:]) + if relpath.endswith('/') or not relpath: + relpath += 'index.html' + else: + relpath += '.html' + return text, '../../apigen/source/%s' % (relpath,) +directive.register_linkrole('api', resolve_linkrole) +directive.register_linkrole('source', resolve_linkrole) + Modified: py/dist/py/doc/test_conftest.py ============================================================================== --- py/dist/py/doc/test_conftest.py (original) +++ py/dist/py/doc/test_conftest.py Tue Jan 23 15:55:52 2007 @@ -55,3 +55,20 @@ l2 = session.getitemoutcomepairs(py.test.Item.Skipped) assert len(l+l2) == 3 +def test_resolve_linkrole(): + from py.__.doc.conftest import resolve_linkrole + assert resolve_linkrole('api', 'py.foo.bar') == ( + 'py.foo.bar', '../../apigen/api/foo.bar.html') + assert resolve_linkrole('api', 'py.foo.bar()') == ( + 'py.foo.bar()', '../../apigen/api/foo.bar.html') + assert resolve_linkrole('api', 'py') == ( + 'py', '../../apigen/api/index.html') + py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') + assert resolve_linkrole('source', 'py/foo/bar.py') == ( + 'py/foo/bar.py', '../../apigen/source/foo/bar.py.html') + assert resolve_linkrole('source', 'py/foo/') == ( + 'py/foo/', '../../apigen/source/foo/index.html') + assert resolve_linkrole('source', 'py/') == ( + 'py/', '../../apigen/source/index.html') + py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') + From fijal at codespeak.net Tue Jan 23 15:59:51 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 15:59:51 +0100 (CET) Subject: [py-svn] r37197 - py/branch/config/py/code Message-ID: <20070123145951.0E39010069@code0.codespeak.net> Author: fijal Date: Tue Jan 23 15:59:49 2007 New Revision: 37197 Modified: py/branch/config/py/code/representation.py Log: kill some dead code, which seems to be dead for at least a year by now. Modified: py/branch/config/py/code/representation.py ============================================================================== --- py/branch/config/py/code/representation.py (original) +++ py/branch/config/py/code/representation.py Tue Jan 23 15:59:49 2007 @@ -69,29 +69,3 @@ for x in lines: self.out.line(indent + x) return - - # XXX reinstate the following with a --magic option? - # the following line gets user-supplied messages (e.g. - # for "assert 0, 'custom message'") - msg = getattr(getattr(excinfo, 'value', ''), 'msg', '') - info = None - if not msg: - special = excinfo.errisinstance((SyntaxError, SystemExit, KeyboardInterrupt)) - if not self.config.option.nomagic and not special: - try: - info = excinfo.traceback[-1].reinterpret() # very detailed info - except KeyboardInterrupt: - raise - except: - if self.config.option.verbose >= 1: - self.out.line("[reinterpretation traceback]") - py.std.traceback.print_exc(file=py.std.sys.stdout) - else: - self.out.line("[reinterpretation failed, increase " - "verbosity to see details]") - # print reinterpreted info if any - if info: - lines = info.split('\n') - self.out.line('>' + indent[:-1] + lines.pop(0)) - for x in lines: - self.out.line(indent + x) From guido at codespeak.net Tue Jan 23 16:14:44 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 23 Jan 2007 16:14:44 +0100 (CET) Subject: [py-svn] r37200 - py/dist/py/doc Message-ID: <20070123151444.C29BD10077@code0.codespeak.net> Author: guido Date: Tue Jan 23 16:14:40 2007 New Revision: 37200 Modified: py/dist/py/doc/misc.txt Log: Added some API links for demonstration purposes. Modified: py/dist/py/doc/misc.txt ============================================================================== --- py/dist/py/doc/misc.txt (original) +++ py/dist/py/doc/misc.txt Tue Jan 23 16:14:40 2007 @@ -17,8 +17,8 @@ also ensures that there remains a focus on getting short paths to objects. -The ``py.std`` hook -------------------- +The :api:`py.std` hook +---------------------- Of course, no matter what, everybody will continue to use the python standard library because it is a very usable code base. @@ -27,7 +27,7 @@ statements. For example, to get to the print-exception functionality of the standard library you can write:: - py.std.traceback.print_exc() + py.std.traceback.print_exc() without having to do anything else than the usual ``import py`` at the beginning. Note that not having imports for the @@ -45,7 +45,7 @@ Automagically accessing sub packages doesn't work (yet?) -------------------------------------------------------- -If you use the ``py.std`` hook you currently cannot magically +If you use the :api:`py.std` hook you currently cannot magically import nested packages which otherwise need explicit imports of their sub-packages. For example, the suversion bindings require you to do something like:: @@ -55,7 +55,7 @@ If you just do the naive thing with the py lib, i.e. write ``py.std.svn.client`` it will not work unless you previously imported it already. The py lib currently doesn't try to -magically make this work. The ``py.std`` hook really is +magically make this work. The :api:`py.std` hook really is intended for Python standard modules which very seldomly (if at all) provide such nested packages. @@ -86,9 +86,9 @@ ====================================================== Currently, the py lib offers two ways to interact with -system executables. ``py.process.cmdexec()`` invokes +system executables. :api:`py.process.cmdexec()` invokes the shell in order to execute a string. The other -one, ``localpath.sysexec()`` lets you directly execute a binary. +one, :api:`py.path.local.sysexec()` lets you directly execute a binary. Both approaches will raise an exception in case of a return- code other than 0 and otherwise return the stdout-output @@ -150,7 +150,7 @@ Currently, the ``PATH`` environment variable based search on unix platforms is supported:: - py.path.local.sysfind('svn') + py.path.local.sysfind('svn') which returns the first path whose ``basename`` matches ``svn``. In principle, `sysfind` deploys platform specific algorithms @@ -177,12 +177,12 @@ The py-lib contains some helpers that make writing scripts that work on various Python versions easier. -``py.compat`` -------------- +:api:`py.compat` +---------------- -``py.compat`` provides fixed versions (currently from Python 2.4.4) of various -newer modules to be able to use them in various Python versions. Currently these -are: +:api:`py.compat` provides fixed versions (currently from Python 2.4.4) of +various newer modules to be able to use them in various Python versions. +Currently these are: * doctest * optparse @@ -192,10 +192,10 @@ They are used by replacing the normal ``import ...`` byr ``from py.compat import ...``. -``py.builtin`` --------------- +:api:`py.builtin` +----------------- -``py.builtin`` provides various builtins that were added in later Python +:api:`py.builtin` provides various builtins that were added in later Python versions. If the used Python version used does not provide these builtins, they are pure-Python reimplementations. These currently are: @@ -206,4 +206,4 @@ * set and frozenset (using either the builtin, if available, or the sets module) -``py.builtin.BaseException`` is just ``Exception`` before Python 2.5. +:api:`py.builtin.BaseException` is just ``Exception`` before Python 2.5. From cfbolz at codespeak.net Tue Jan 23 16:29:55 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 23 Jan 2007 16:29:55 +0100 (CET) Subject: [py-svn] r37203 - py/dist/py/doc Message-ID: <20070123152955.BBCB91006F@code0.codespeak.net> Author: cfbolz Date: Tue Jan 23 16:29:54 2007 New Revision: 37203 Modified: py/dist/py/doc/confrest.py Log: factor the content-getting out into a method (so I can override it in a slightly hackish way somewhere else). Modified: py/dist/py/doc/confrest.py ============================================================================== --- py/dist/py/doc/confrest.py (original) +++ py/dist/py/doc/confrest.py Tue Jan 23 16:29:54 2007 @@ -90,9 +90,13 @@ href="http://codespeak.net")) Page = PyPage + + def get_content(self, txtpath, encoding): + return unicode(txtpath.read(), encoding) + def process(self, txtpath): encoding = self.encoding - content = unicode(txtpath.read(), encoding) + content = self.get_content(txtpath, encoding) stylesheet = self.stylesheet if not stylesheet.startswith('http') and \ not txtpath.dirpath(stylesheet).check(): From fijal at codespeak.net Tue Jan 23 16:31:15 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 16:31:15 +0100 (CET) Subject: [py-svn] r37204 - in py/branch/config/py: code test/rsession Message-ID: <20070123153115.895A51006F@code0.codespeak.net> Author: fijal Date: Tue Jan 23 16:31:13 2007 New Revision: 37204 Modified: py/branch/config/py/code/representation.py py/branch/config/py/test/rsession/outcome.py py/branch/config/py/test/rsession/reporter.py Log: Add another part of code, which is shared between rsession/terminal. (actually one line in lsession, but now at least looks a bit better ;-) Modified: py/branch/config/py/code/representation.py ============================================================================== --- py/branch/config/py/code/representation.py (original) +++ py/branch/config/py/code/representation.py Tue Jan 23 16:31:13 2007 @@ -50,13 +50,13 @@ assert isinstance(item.parent, py.test.collect.Generator) # a generative test yielded a non-callable fn, lineno = item.parent.getpathlineno() - if root == fn: + if root == fn: self.out.sep("_", "entrypoint: %s" %(modpath)) else: self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath)) - def repr_failure_explanation(self, excinfo, source): - try: + def repr_failure_explanation(self, excinfo, source): + try: s = str(source.getstatement(len(source)-1)) except KeyboardInterrupt: raise @@ -64,8 +64,7 @@ s = str(source[-1]) indent = " " * (4 + (len(s) - len(s.lstrip()))) # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - self.out.line('>' + indent[:-1] + lines.pop(0)) - for x in lines: - self.out.line(indent + x) - return + lines = excinfo.exconly(tryshort=True).split('\n') + self.out.line('>' + indent[:-1] + lines.pop(0)) + for x in lines: + self.out.line(indent + x) Modified: py/branch/config/py/test/rsession/outcome.py ============================================================================== --- py/branch/config/py/test/rsession/outcome.py (original) +++ py/branch/config/py/test/rsession/outcome.py Tue Jan 23 16:31:13 2007 @@ -67,6 +67,11 @@ for x in "typename value traceback".split()] return "" %(" ".join(l),) + def exconly(self, tryshort=False): + """ Somehow crippled version of original one + """ + return "%s: %s" % (self.typename, self.value) + class ReprOutcome(object): def __init__(self, repr_tuple): (self.passed, self.setupfailure, excinfo, self.skipped, Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Tue Jan 23 16:31:13 2007 @@ -165,7 +165,7 @@ for index, entry in py.builtin.enumerate(traceback): self.out.line("%s: %s" % (entry.path, entry.lineno)) self.out.line(entry.source) - self.out.line("%s: %s" % (excinfo.typename, excinfo.value)) + self.presenter.repr_failure_explanation(excinfo, entry.source) def skips(self): texts = {} From cfbolz at codespeak.net Tue Jan 23 19:36:06 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 23 Jan 2007 19:36:06 +0100 (CET) Subject: [py-svn] r37226 - in py/dist/py/rest: . testing Message-ID: <20070123183606.3546E10078@code0.codespeak.net> Author: cfbolz Date: Tue Jan 23 19:36:04 2007 New Revision: 37226 Modified: py/dist/py/rest/rst.py py/dist/py/rest/testing/test_rst.py Log: make AbstractNode.join take *args Modified: py/dist/py/rest/rst.py ============================================================================== --- py/dist/py/rest/rst.py (original) +++ py/dist/py/rest/rst.py Tue Jan 23 19:36:04 2007 @@ -65,12 +65,13 @@ for arg in kwargs: setattr(self, arg, kwargs[arg]) - def join(self, child): - """ adds a child node + def join(self, *children): + """ add child nodes returns a reference to self """ - self._add(child) + for child in children: + self._add(child) return self def add(self, child): @@ -78,7 +79,6 @@ returns a reference to the child """ - self._add(child) return child def _add(self, child): Modified: py/dist/py/rest/testing/test_rst.py ============================================================================== --- py/dist/py/rest/testing/test_rst.py (original) +++ py/dist/py/rest/testing/test_rst.py Tue Jan 23 19:36:04 2007 @@ -92,6 +92,11 @@ txt = Paragraph(Text("dupa"), Text("dupa")).text() assert txt == "dupa dupa" +def test_text_join(): + txt = Paragraph(Text("worse things")) + txt = txt.join(Text("happen at sea"), Text("you know")) + assert txt.text() == "worse things happen at sea you know" + def test_paragraph_basic(): txt = Paragraph(Text('spam')).text() assert txt == 'spam' From hpk at codespeak.net Tue Jan 23 19:59:02 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 19:59:02 +0100 (CET) Subject: [py-svn] r37227 - py/branch/config/py/test Message-ID: <20070123185902.D0B8910077@code0.codespeak.net> Author: hpk Date: Tue Jan 23 19:59:01 2007 New Revision: 37227 Modified: py/branch/config/py/test/defaultconftest.py Log: thinking about and putting some options into the "experimental" section. Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Tue Jan 23 19:59:01 2007 @@ -66,6 +66,24 @@ Option('', '--traceconfig', action="store_true", dest="traceconfig", default=False, help="trace considerations of conftest.py files."), + ) + + py.test.config.addoptions('EXPERIMENTAL options', + Option('', '--looponfailing', + action="store_true", dest="looponfailing", default=False, + help="loop on failing test set."), + Option('', '--exec', + action="store", dest="executable", default=None, + help="python executable to run the tests with."), + Option('-d', '--dist', + action="store_true", dest="dist", default=False, + help="use ad-hoc distributed testing (requires conftest settings)"), + Option('', '--tkinter', + action="store_true", dest="tkinter", default=False, + help="use tkinter test session frontend."), + Option('', '--box', + action="store_true", dest="boxing", + help="use boxing (running each test in external process)"), Option('-w', '--startserver', action="store_true", dest="startserver", default=False, help="start HTTP server listening on localhost:8000 for test." @@ -74,7 +92,7 @@ action="store_true", dest="runbrowser", default=False, help="run browser to point to your freshly started web server." ), - Option('-r', '--rest', + Option('', '--rest', action='store_true', dest="restreport", default=False, help="restructured text output reporting."), Option('', '--apigen', @@ -83,24 +101,6 @@ "argument pointing to a script)."), Option('', '--session', action="store", dest="session", default=None, - help="lookup sessioname in conftest.py files and use it."), - ) - - py.test.config.addoptions('EXPERIMENTAL options implying subprocesses', - Option('', '--tkinter', - action="store_true", dest="tkinter", default=False, - help="use tkinter test session frontend."), - Option('', '--dist', - action="store_true", dest="dist", default=False, - help="use ad-hoc distributed testing (requires conftest settings)"), - Option('', '--box', - action="store_true", dest="boxing", - help="use boxing (running each test in external process)"), - Option('', '--looponfailing', - action="store_true", dest="looponfailing", default=False, - help="loop on failing test set."), - Option('', '--exec', - action="store", dest="executable", default=None, - help="python executable to run the tests with."), + help="lookup given sessioname in conftest.py files and use it."), ) From hpk at codespeak.net Tue Jan 23 20:26:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 20:26:27 +0100 (CET) Subject: [py-svn] r37228 - py/branch/config/py/test Message-ID: <20070123192627.AE1C31006F@code0.codespeak.net> Author: hpk Date: Tue Jan 23 20:26:25 2007 New Revision: 37228 Modified: py/branch/config/py/test/defaultconftest.py Log: streamlining options and docs some more Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Tue Jan 23 20:26:25 2007 @@ -77,21 +77,21 @@ help="python executable to run the tests with."), Option('-d', '--dist', action="store_true", dest="dist", default=False, - help="use ad-hoc distributed testing (requires conftest settings)"), + help="ad-hoc distribute tests across machines (requires conftest settings)"), + Option('-w', '--startserver', + action="store_true", dest="startserver", default=False, + help="starts local web server for displaying test progress.", + ), + Option('-r', '--runbrowser', + action="store_true", dest="runbrowser", default=False, + help="run browser (implies --startserver)." + ), Option('', '--tkinter', action="store_true", dest="tkinter", default=False, help="use tkinter test session frontend."), Option('', '--box', action="store_true", dest="boxing", help="use boxing (running each test in external process)"), - Option('-w', '--startserver', - action="store_true", dest="startserver", default=False, - help="start HTTP server listening on localhost:8000 for test." - ), - Option('', '--runbrowser', - action="store_true", dest="runbrowser", default=False, - help="run browser to point to your freshly started web server." - ), Option('', '--rest', action='store_true', dest="restreport", default=False, help="restructured text output reporting."), From hpk at codespeak.net Tue Jan 23 20:38:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 20:38:28 +0100 (CET) Subject: [py-svn] r37229 - py/branch/config/py/test Message-ID: <20070123193828.1E2D91006E@code0.codespeak.net> Author: hpk Date: Tue Jan 23 20:38:27 2007 New Revision: 37229 Modified: py/branch/config/py/test/collect.py py/branch/config/py/test/session.py Log: not sure why this code was duplicated at all? (seems so easy to avoid it) Modified: py/branch/config/py/test/collect.py ============================================================================== --- py/branch/config/py/test/collect.py (original) +++ py/branch/config/py/test/collect.py Tue Jan 23 20:38:27 2007 @@ -190,8 +190,10 @@ newl.append(x.name) return ".".join(newl) - # XXX: Copied from session def skipbykeyword(self, keyword): + """ raise Skipped() exception if the given keyword + matches for this collector. + """ if not keyword: return chain = self.listchain() Modified: py/branch/config/py/test/session.py ============================================================================== --- py/branch/config/py/test/session.py (original) +++ py/branch/config/py/test/session.py Tue Jan 23 20:38:27 2007 @@ -91,7 +91,7 @@ if self.config.option.collectonly and isinstance(colitem, py.test.Item): return if isinstance(colitem, py.test.Item): - self.skipbykeyword(colitem) + colitem.skipbykeyword(self.config.option.keyword) res = colitem.run() if res is None: return py.test.Item.Passed() @@ -110,24 +110,6 @@ finish() return res - def skipbykeyword(self, colitem): - keyword = self.config.option.keyword - if not keyword: - return - chain = colitem.listchain() - for key in filter(None, keyword.split()): - eor = key[:1] == '-' - if eor: - key = key[1:] - if not (eor ^ self._matchonekeyword(key, chain)): - py.test.skip("test not selected by keyword %r" %(keyword,)) - - def _matchonekeyword(self, key, chain): - for subitem in chain: - if subitem.haskeyword(key): - return True - return False - class Exit(Exception): """ for immediate program exits without tracebacks and reporter/summary. """ def __init__(self, msg="unknown reason", item=None): From hpk at codespeak.net Tue Jan 23 20:44:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 20:44:23 +0100 (CET) Subject: [py-svn] r37230 - py/branch/config/py/test Message-ID: <20070123194423.3D1911006E@code0.codespeak.net> Author: hpk Date: Tue Jan 23 20:44:22 2007 New Revision: 37230 Modified: py/branch/config/py/test/config.py Log: typo-level fixes Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Tue Jan 23 20:44:22 2007 @@ -233,19 +233,20 @@ # implied options if option.usepdb: if not option.nocapture: - print "--usepdb implies --nocapture" + #print "--pdb implies --nocapture" option.nocapture = True + if option.runbrowser and not option.startserver: - print "--runbrowser implies --startserver" + #print "--runbrowser implies --startserver" option.startserver = True # conflicting options if option.looponfailing and option.usepdb: - raise ValueError, "--looponfailing together with --pdb not supported yet." + raise ValueError, "--looponfailing together with --pdb not supported." if option.looponfailing and option.dist: - raise ValueError, "--looponfailing together with --dist not supported yet." + raise ValueError, "--looponfailing together with --dist not supported." if option.executable and option.usepdb: - raise ValueError, "--exec together with --pdb not supported yet." + raise ValueError, "--exec together with --pdb not supported." def gettopdir(args): From hpk at codespeak.net Tue Jan 23 21:53:57 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 21:53:57 +0100 (CET) Subject: [py-svn] r37232 - in py/branch/config/py/path/local: . testing Message-ID: <20070123205357.780871006F@code0.codespeak.net> Author: hpk Date: Tue Jan 23 21:53:55 2007 New Revision: 37232 Modified: py/branch/config/py/path/local/local.py py/branch/config/py/path/local/testing/test_local.py Log: join() should take care to stick to normalized paths (uncovered bug by config works and fix for it) Modified: py/branch/config/py/path/local/local.py ============================================================================== --- py/branch/config/py/path/local/local.py (original) +++ py/branch/config/py/path/local/local.py Tue Jan 23 21:53:55 2007 @@ -189,7 +189,7 @@ strpath += sep strpath += arg obj = self.new() - obj.strpath = strpath + obj.strpath = os.path.normpath(strpath) return obj def __eq__(self, other): Modified: py/branch/config/py/path/local/testing/test_local.py ============================================================================== --- py/branch/config/py/path/local/testing/test_local.py (original) +++ py/branch/config/py/path/local/testing/test_local.py Tue Jan 23 21:53:55 2007 @@ -17,6 +17,13 @@ class TestLocalPath(LocalSetup, CommonFSTests): + def test_join_normpath(self): + assert self.tmpdir.join(".") == self.tmpdir + p = self.tmpdir.join("../%s" % self.tmpdir.basename) + assert p == self.tmpdir + p = self.tmpdir.join("..//%s/" % self.tmpdir.basename) + assert p == self.tmpdir + def test_gethash(self): import md5 import sha From hpk at codespeak.net Tue Jan 23 22:25:17 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 22:25:17 +0100 (CET) Subject: [py-svn] r37235 - in py/branch/config/py/test: . testing Message-ID: <20070123212517.F250F10077@code0.codespeak.net> Author: hpk Date: Tue Jan 23 22:25:16 2007 New Revision: 37235 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/testing/test_config.py Log: added get_collector_trail method to get a representation of a collector relative to the topdir. Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Tue Jan 23 22:25:16 2007 @@ -205,6 +205,19 @@ 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 + 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 self.topdir == chain[0].fspath: + relpath = '.' + return relpath, tuple([x.name for x in chain[1:]]) + # this is the one per-process instance of py.test configuration config_per_process = Config() Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Tue Jan 23 22:25:16 2007 @@ -314,6 +314,26 @@ 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.test.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.listchain() == col.listchain() + + def test_get_collector_trail_topdir_and_beyond(self): + config = py.test.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 From hpk at codespeak.net Tue Jan 23 22:27:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 22:27:01 +0100 (CET) Subject: [py-svn] r37236 - py/branch/config/py/test Message-ID: <20070123212701.DB09110078@code0.codespeak.net> Author: hpk Date: Tue Jan 23 22:27:00 2007 New Revision: 37236 Modified: py/branch/config/py/test/defaultconftest.py Log: add '-f' as a shortcut for --looponfailing Modified: py/branch/config/py/test/defaultconftest.py ============================================================================== --- py/branch/config/py/test/defaultconftest.py (original) +++ py/branch/config/py/test/defaultconftest.py Tue Jan 23 22:27:00 2007 @@ -69,7 +69,7 @@ ) py.test.config.addoptions('EXPERIMENTAL options', - Option('', '--looponfailing', + Option('-f', '--looponfailing', action="store_true", dest="looponfailing", default=False, help="loop on failing test set."), Option('', '--exec', From hpk at codespeak.net Tue Jan 23 23:09:58 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 23 Jan 2007 23:09:58 +0100 (CET) Subject: [py-svn] r37238 - py/branch/config/py/test Message-ID: <20070123220958.C3E7110077@code0.codespeak.net> Author: hpk Date: Tue Jan 23 23:09:57 2007 New Revision: 37238 Modified: py/branch/config/py/test/config.py Log: why do it complicated? Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Tue Jan 23 23:09:57 2007 @@ -212,10 +212,7 @@ starting from a different topdir). """ chain = collector.listchain() - relpath = chain[0].fspath.relto(self.topdir) - if not relpath: - if self.topdir == chain[0].fspath: - relpath = '.' + relpath = chain[0].fspath.relto(self.topdir) or '.' return relpath, tuple([x.name for x in chain[1:]]) # this is the one per-process instance of py.test configuration From fijal at codespeak.net Tue Jan 23 23:38:47 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 23:38:47 +0100 (CET) Subject: [py-svn] r37239 - in py/branch/config/py: code test/terminal Message-ID: <20070123223847.9DC5B10077@code0.codespeak.net> Author: fijal Date: Tue Jan 23 23:38:45 2007 New Revision: 37239 Modified: py/branch/config/py/code/representation.py py/branch/config/py/test/terminal/terminal.py Log: Move minor function from terminal to representation Modified: py/branch/config/py/code/representation.py ============================================================================== --- py/branch/config/py/code/representation.py (original) +++ py/branch/config/py/code/representation.py Tue Jan 23 23:38:45 2007 @@ -68,3 +68,11 @@ self.out.line('>' + 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 = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) + return source.deindent() + Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Tue Jan 23 23:38:45 2007 @@ -290,7 +290,7 @@ self.out.line() else: self.out.line("") - source = self.getentrysource(entry) + source = self.presenter.getentrysource(entry) firstsourceline = entry.getfirstlinesource() marker_location = entry.lineno - firstsourceline if entry == last: @@ -300,7 +300,7 @@ self.presenter.repr_source(source, '>', marker_location) self.out.line("") self.out.line("[%s:%d]" %(entry.frame.code.path, entry.lineno+1)) - self.repr_locals(entry) + #self.repr_locals(entry) # trailing info if entry == last: @@ -357,13 +357,6 @@ # the following is only used by the combination '--pdb --tb=no' repr_failure_tbno = repr_failure_tbshort - def getentrysource(self, entry): - try: - source = entry.getsource() - except py.error.ENOENT: - source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) - return source.deindent() - def repr_out_err(self, colitem): for parent in colitem.listchain(): for name, obj in zip(['out', 'err'], parent.getouterr()): From fijal at codespeak.net Tue Jan 23 23:39:20 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 23:39:20 +0100 (CET) Subject: [py-svn] r37240 - in py/branch/config/py: code test Message-ID: <20070123223920.E36BE1007C@code0.codespeak.net> Author: fijal Date: Tue Jan 23 23:39:19 2007 New Revision: 37240 Added: py/branch/config/py/test/representation.py - copied unchanged from r37239, py/branch/config/py/code/representation.py Removed: py/branch/config/py/code/representation.py Log: Move this file to test directory, because it depends on ie. py.test.Item Deleted: /py/branch/config/py/code/representation.py ============================================================================== --- /py/branch/config/py/code/representation.py Tue Jan 23 23:39:19 2007 +++ (empty file) @@ -1,78 +0,0 @@ - -""" This file intends to gather all methods of representing -failures/tracebacks etc. which should be used among -all terminal-based reporters. This methods should be general, -to allow further use outside the pylib -""" - -# XXX: Probably it all should go as methods of proper -# objects - -class Presenter(object): - """ Class used for presentation of various objects, - sharing common output style - """ - def __init__(self, out, config): - """ out is a file-like object (we can write to it) - """ - assert hasattr(out, 'write') - self.out = out - self.config = config - - def repr_source(self, source, marker=">", marker_location=-1): - """ This one represents piece of source with possible - marker at requested position - """ - if isinstance(source, str): - # why the hell, string is iterable? - 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 = marker + " " - else: - prefix = " " - self.out.line(prefix + source[i]) - - def repr_item_info(self, item): - """ This method represents py.test.Item info (path and module) - """ - root = item.fspath - modpath = item.getmodpath() - try: - fn, lineno = item.getpathlineno() - except TypeError: - assert isinstance(item.parent, py.test.collect.Generator) - # a generative test yielded a non-callable - fn, lineno = item.parent.getpathlineno() - if root == fn: - self.out.sep("_", "entrypoint: %s" %(modpath)) - else: - self.out.sep("_", "entrypoint: %s %s" %(root.basename, modpath)) - - 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()))) - # get the real exception information out - lines = excinfo.exconly(tryshort=True).split('\n') - self.out.line('>' + 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 = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) - return source.deindent() - From fijal at codespeak.net Tue Jan 23 23:43:59 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 23:43:59 +0100 (CET) Subject: [py-svn] r37241 - in py/branch/config/py/test: rsession terminal Message-ID: <20070123224359.78BB810078@code0.codespeak.net> Author: fijal Date: Tue Jan 23 23:43:58 2007 New Revision: 37241 Modified: py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/terminal/terminal.py Log: Fix imports. Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Tue Jan 23 23:43:58 2007 @@ -11,7 +11,7 @@ from py.__.test.rsession import report from py.__.test.rsession import outcome from py.__.misc.terminal_helper import ansi_print, get_terminal_width -from py.__.code.representation import Presenter +from py.__.test.representation import Presenter import sys Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Tue Jan 23 23:43:58 2007 @@ -4,7 +4,7 @@ Item = py.test.Item from py.__.test.terminal.out import getout import py.__.code.safe_repr -from py.__.code.representation import Presenter +from py.__.test.representation import Presenter def getrelpath(source, dest): base = source.common(dest) From fijal at codespeak.net Tue Jan 23 23:53:30 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 23 Jan 2007 23:53:30 +0100 (CET) Subject: [py-svn] r37242 - py/branch/config/py/test/testing Message-ID: <20070123225330.0E17A10077@code0.codespeak.net> Author: fijal Date: Tue Jan 23 23:53:28 2007 New Revision: 37242 Added: py/branch/config/py/test/testing/test_repr.py Log: Add a file for testing representation.py Added: py/branch/config/py/test/testing/test_repr.py ============================================================================== --- (empty file) +++ py/branch/config/py/test/testing/test_repr.py Tue Jan 23 23:53:28 2007 @@ -0,0 +1,22 @@ + +import py +from py.__.test.representation import Presenter +from py.__.test.terminal.out import getout +from StringIO import StringIO + +def test_repr_source(): + source = py.code.Source(""" + def f(x): + pass + """).strip() + config = py.test.config._reparse([]) + s = StringIO() + out = getout(s) + p = Presenter(out, config) + p.repr_source(source, "|", 0) + lines = s.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 + From hpk at codespeak.net Wed Jan 24 00:06:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 00:06:25 +0100 (CET) Subject: [py-svn] r37243 - in py/branch/config/py/test: . terminal testing Message-ID: <20070123230625.BA69A10077@code0.codespeak.net> Author: hpk Date: Wed Jan 24 00:06:24 2007 New Revision: 37243 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/session.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/testing/test_config.py Log: small step-wise cleanups Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Wed Jan 24 00:06:24 2007 @@ -26,13 +26,13 @@ class Config(object): """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option - conftest = Conftest() - _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 self._overwrite_dict = {} def parse(self, args): @@ -212,7 +212,13 @@ starting from a different topdir). """ chain = collector.listchain() - relpath = chain[0].fspath.relto(self.topdir) or '.' + relpath = chain[0].fspath.relto(self.topdir) + if not relpath: + if chain[0].fspath == self.topdir: + relpath = "." + else: + raise ValueError("%r not relative to %s" + %(chain[0], self.topdir)) return relpath, tuple([x.name for x in chain[1:]]) # this is the one per-process instance of py.test configuration Modified: py/branch/config/py/test/session.py ============================================================================== --- py/branch/config/py/test/session.py (original) +++ py/branch/config/py/test/session.py Wed Jan 24 00:06:24 2007 @@ -43,7 +43,6 @@ self.header(colitems) try: for colitem in colitems: - colitem.option = self.config.option self.runtraced(colitem) except KeyboardInterrupt: raise @@ -53,9 +52,6 @@ self.footer(colitems) except Exit, ex: pass - # return [(fspath as string, [names as string])] - return [(str(item.listchain()[0].fspath), item.listnames()) - for item, outcome in self.getitemoutcomepairs(py.test.Item.Failed)] def runtraced(self, colitem): if self.shouldclose(): Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Wed Jan 24 00:06:24 2007 @@ -120,12 +120,14 @@ config.option.usepdb = False config.option.executable = None + session = config.initsession() + session.shouldclose = channel.isclosed if failures: cols = getfailureitems(failures) else: cols = args - session = config.initsession() - session.shouldclose = channel.isclosed - print "SLAVE: received configuration" - failures = session.main() + print "SLAVE: starting session.main()" + session.main() + failures = session.getitemoutcomepairs(py.test.Item.Failed) + failures = [config.get_collector_trail(item) for item,_ in failures] channel.send(failures) Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Wed Jan 24 00:06:24 2007 @@ -337,3 +337,6 @@ col2 = config._getcollector(trail) assert col2.fspath == config.topdir assert len(col2.listchain()) == 1 + col3 = config._getcollector(config.topdir.dirpath()) + py.test.raises(ValueError, + "config.get_collector_trail(col3)") From guido at codespeak.net Wed Jan 24 10:47:41 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 10:47:41 +0100 (CET) Subject: [py-svn] r37252 - py/dist/py/doc Message-ID: <20070124094741.1EC1A1006E@code0.codespeak.net> Author: guido Date: Wed Jan 24 10:47:39 2007 New Revision: 37252 Modified: py/dist/py/doc/misc.txt Log: Added some source links. Modified: py/dist/py/doc/misc.txt ============================================================================== --- py/dist/py/doc/misc.txt (original) +++ py/dist/py/doc/misc.txt Wed Jan 24 10:47:39 2007 @@ -85,6 +85,11 @@ Support for interaction with system utilities/binaries ====================================================== +sources: + + * :source:`py/process/` + * :source:`py/path/local/` + Currently, the py lib offers two ways to interact with system executables. :api:`py.process.cmdexec()` invokes the shell in order to execute a string. The other @@ -174,6 +179,11 @@ Python version compatibility helpers ===================================== +sources: + + * :source:`py/compat/` + * :source:`py/builtin/` + The py-lib contains some helpers that make writing scripts that work on various Python versions easier. From guido at codespeak.net Wed Jan 24 11:24:54 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 11:24:54 +0100 (CET) Subject: [py-svn] r37253 - py/dist/py/doc Message-ID: <20070124102454.B3D7B10077@code0.codespeak.net> Author: guido Date: Wed Jan 24 11:24:52 2007 New Revision: 37253 Modified: py/dist/py/doc/TODO.txt Log: Adjustments after discussions with fijal. Modified: py/dist/py/doc/TODO.txt ============================================================================== --- py/dist/py/doc/TODO.txt (original) +++ py/dist/py/doc/TODO.txt Wed Jan 24 11:24:52 2007 @@ -37,11 +37,13 @@ py.path.extpy -> py.path._extpy py.log -> py._log (pypy!) -* review py.io and write py.io.dupfile docstring +* review py.io and write py.io.dupfile docstring (XXX guido) * re-consider what to do with read and write methods of py.path classes (since there are places that check for file-ness by doing hasattr(... 'write')) + (XXX guido talk to holger) + packaging ------------------------------------- @@ -70,6 +72,8 @@ collect tracing information and call the apigen script to produce api and source code documentation + (done) + * deploy the above "py.test --apigen" run on codespeak regularly, determine directory locations and URL namespace design. @@ -77,30 +81,44 @@ with help code from py.__.rest.directive.... make sure that the txt files in py/documentation/ use it + (done XXX functions/methods) + testing ----------- -* windows tests (rev 36514 passes without errors, many skips) +* windows tests (rev 36514 passes without errors, many skips) (XXX guido) + * these should all work on 0.9 and on the py lib and pypy: - running "py.test -s" - running "py.test --pdb" - running "py.test --looponfailing" - running "py.test" distributed on some hosts -* tests should not create any tempdirectories in the source code base +* fix --box on config branch (XXX guido - find tests that depend on each other) + +* tests should not create any tempdirectories in the source code base + + + (done) * try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) + (done) + distributed testing ---------------------- * the main rsession methods should have docstrings explaining the purpose + (done) + * move RSync class to py.execnet.RSync, document its usage and features in the class and method docstrings + (done) + code quality ----------------- @@ -120,6 +138,8 @@ * document py.test's conftest.py approach (somewhat done) +* py.test fails to parse strangely formatted code after assertion failure + Missing docstrings ------------------ From guido at codespeak.net Wed Jan 24 13:29:27 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 13:29:27 +0100 (CET) Subject: [py-svn] r37254 - in py/dist/py/apigen/tracer: . testing testing/package/submodule Message-ID: <20070124122927.AC0C610078@code0.codespeak.net> Author: guido Date: Wed Jan 24 13:29:25 2007 New Revision: 37254 Modified: py/dist/py/apigen/tracer/docstorage.py py/dist/py/apigen/tracer/testing/package/submodule/__init__.py py/dist/py/apigen/tracer/testing/test_docgen.py py/dist/py/apigen/tracer/testing/test_package.py Log: Fixed (at least as far as I can tell) support for * entries in initpkg exportdefs. Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Wed Jan 24 13:29:25 2007 @@ -6,6 +6,7 @@ import py import sys import types +import inspect from py.__.apigen.tracer.description import FunctionDesc, ClassDesc, \ MethodDesc, Desc @@ -49,8 +50,9 @@ def find_desc(self, code, locals): try: # argh, very fragile specialcasing - return self.desc_cache[(code.raw, locals[code.raw.co_varnames[0]].__class__)] - except (KeyError, IndexError, AttributeError): + return self.desc_cache[(code.raw, + locals[code.raw.co_varnames[0]].__class__)] + except (KeyError, IndexError, AttributeError): # XXX hrmph return self.desc_cache.get(code.raw, None) #for desc in self.descs.values(): # if desc.has_code(frame.code.raw): @@ -115,17 +117,44 @@ def from_pkg(self, module, keep_frames=False): self.module = module - keys = module.__package__.exportdefs.keys() + defs = module.__package__.exportdefs d = {} - for key in keys: + for key, value in defs.iteritems(): chain = key.split('.') base = module for elem in chain: base = getattr(base, elem) - d[key] = base + if value[1] == '*': + d.update(self.get_star_import_tree(base, key)) + else: + d[key] = base self.from_dict(d, keep_frames) # XXX return self + + def get_star_import_tree(self, module, parentname): + """ deal with '*' entries in an initpkg situation """ + ret = {} + pkgpath = module.__package__.getpath() + for objname in dir(module): + if objname.startswith('_'): + continue # also skip __*__ attributes + obj = getattr(module, objname) + if (isinstance(obj, types.ClassType) or + isinstance(obj, types.ObjectType)): + try: + sourcefile_object = py.path.local( + inspect.getsourcefile(obj)) + except TypeError: + continue + else: + if not sourcefile_object.relto(pkgpath): + # not in this package + continue + dotted_name = '%s.%s' % (parentname, objname) + print 'adding dotted name', dotted_name + ret[dotted_name] = obj + return ret def from_module(self, func): raise NotImplementedError("From module") Modified: py/dist/py/apigen/tracer/testing/package/submodule/__init__.py ============================================================================== --- py/dist/py/apigen/tracer/testing/package/submodule/__init__.py (original) +++ py/dist/py/apigen/tracer/testing/package/submodule/__init__.py Wed Jan 24 13:29:25 2007 @@ -8,4 +8,5 @@ 'pak.mod.one': ('./pak/mod.py', 'one'), 'pak.mod.two': ('./pak/mod.py', 'nottwo'), 'notpak.notmod.notclass': ('./pak/mod.py', 'cls'), + 'somenamespace': ('./pak/mod.py', '*'), }) Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Wed Jan 24 13:29:25 2007 @@ -364,3 +364,55 @@ assert sorted(ds.descs['Foo'].getfields()) == ['__foo__', 'foo', 'trigger__foo'] +def setup_fs_project(): + temp = py.test.ensuretemp('test_get_initpkg_star_items') + temp.ensure("pkg/func.py").write(py.code.Source("""\ + def func(arg1): + "docstring" + """)) + temp.ensure('pkg/someclass.py').write(py.code.Source("""\ + class SomeClass(object): + " docstring someclass " + def __init__(self, somevar): + self.somevar = somevar + + def get_somevar(self): + " get_somevar docstring " + return self.somevar + SomeInstance = SomeClass(10) + """)) + temp.ensure('pkg/somesubclass.py').write(py.code.Source("""\ + from someclass import SomeClass + class SomeSubClass(SomeClass): + " docstring somesubclass " + def get_somevar(self): + return self.somevar + 1 + """)) + temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ + def foo(): + return 'bar' + def baz(qux): + return qux + """)) + temp.ensure("pkg/__init__.py").write(py.code.Source("""\ + from py.initpkg import initpkg + initpkg(__name__, exportdefs = { + 'main.sub.func': ("./func.py", "func"), + 'main.SomeClass': ('./someclass.py', 'SomeClass'), + 'main.SomeInstance': ('./someclass.py', 'SomeInstance'), + 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), + 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), + 'other': ('./somenamespace.py', '*'), + }) + """)) + return temp, 'pkg' + +def test_get_initpkg_star_items(): + pkgdir, pkgname = setup_fs_project() + py.std.sys.path.insert(0, str(pkgdir)) + pkg = __import__(pkgname) + ds = DocStorage().from_pkg(pkg) + sit = ds.get_star_import_tree(pkg.other, 'pkg.other') + print sit + assert sorted(sit.keys()) == ['pkg.other.baz', 'pkg.other.foo'] + Modified: py/dist/py/apigen/tracer/testing/test_package.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_package.py (original) +++ py/dist/py/apigen/tracer/testing/test_package.py Wed Jan 24 13:29:25 2007 @@ -23,10 +23,13 @@ def test_init(self): ds = self.ds - assert len(ds.descs) == 4 + print sorted(ds.descs.keys()) + assert len(ds.descs) == 8 assert py.builtin.sorted(ds.descs.keys()) == [ - "notpak.notmod.notclass", "notpak.notmod.notclass.__init__", - "pak.mod.one", "pak.mod.two"] + 'notpak.notmod.notclass', 'notpak.notmod.notclass.__init__', + 'pak.mod.one', 'pak.mod.two', 'somenamespace.cls', + 'somenamespace.cls.__init__', 'somenamespace.nottwo', + 'somenamespace.one'] def test_simple_call(self): ds = self.ds From fijal at codespeak.net Wed Jan 24 13:43:20 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 13:43:20 +0100 (CET) Subject: [py-svn] r37255 - in py/branch/config/py: code test test/rsession test/terminal test/testing Message-ID: <20070124124320.2648310077@code0.codespeak.net> Author: fijal Date: Wed Jan 24 13:43:17 2007 New Revision: 37255 Modified: py/branch/config/py/code/traceback2.py py/branch/config/py/test/representation.py py/branch/config/py/test/rsession/outcome.py py/branch/config/py/test/rsession/reporter.py py/branch/config/py/test/terminal/terminal.py py/branch/config/py/test/testing/test_repr.py Log: Share the code of representing tracebacks between terminal.py and reporter.py, some tests are there in test/testing/session.py and test/rsession/testing/test_reporter.py Modified: py/branch/config/py/code/traceback2.py ============================================================================== --- py/branch/config/py/code/traceback2.py (original) +++ py/branch/config/py/code/traceback2.py Wed Jan 24 13:43:17 2007 @@ -23,6 +23,10 @@ return self.frame.code.path path = property(path, None, None, "path to the full source code") + def getlocals(self): + return self.frame.f_locals + locals = property(getlocals, None, None, "locals of underlaying frame") + def reinterpret(self): """Reinterpret the failing statement and returns a detailed information about what operations are performed.""" Modified: py/branch/config/py/test/representation.py ============================================================================== --- py/branch/config/py/test/representation.py (original) +++ py/branch/config/py/test/representation.py Wed Jan 24 13:43:17 2007 @@ -5,8 +5,7 @@ to allow further use outside the pylib """ -# XXX: Probably it all should go as methods of proper -# objects +import py class Presenter(object): """ Class used for presentation of various objects, @@ -76,3 +75,100 @@ source = py.code.Source("[failure to get at sourcelines from %r]\n" % entry) return source.deindent() + def repr_locals(self, f_locals): + if self.config.option.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 = py.__.code.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_failure_tblong(self, item, excinfo, traceback, out_err_reporter): + if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError): + recursionindex = traceback.recursionindex() + else: + recursionindex = None + last = traceback[-1] + first = traceback[0] + for index, entry in py.builtin.enumerate(traceback): + if entry == first: + if item: + self.repr_item_info(item) + self.out.line() + else: + self.out.line("") + source = self.getentrysource(entry) + firstsourceline = entry.getfirstlinesource() + marker_location = entry.lineno - firstsourceline + if entry == last: + self.repr_source(source, 'E', marker_location) + self.repr_failure_explanation(excinfo, source) + else: + self.repr_source(source, '>', marker_location) + self.out.line("") + self.out.line("[%s:%d]" %(entry.path, entry.lineno+1)) + self.repr_locals(entry.locals) + + # trailing info + if entry == last: + out_err_reporter() + self.out.sep("_") + else: + self.out.sep("_ ") + if index == recursionindex: + self.out.line("Recursion detected (same locals & position)") + self.out.sep("!") + break + + def repr_failure_tbshort(self, item, excinfo, traceback, out_err_reporter): + # print a Python-style short traceback + if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError): + recursionindex = traceback.recursionindex() + else: + recursionindex = None + last = traceback[-1] + first = traceback[0] + self.out.line() + for index, entry in py.builtin.enumerate(traceback): + code = entry.frame.code + self.out.line(' File "%s", line %d, in %s' % ( + code.raw.co_filename, entry.lineno+1, code.raw.co_name)) + try: + fullsource = entry.frame.code.fullsource + except py.error.ENOENT: + source = ["?"] + else: + try: + source = [fullsource[entry.lineno].lstrip()] + except IndexError: + source = [] + if entry == last: + if source: + self.repr_source(source, 'E') + self.repr_failure_explanation(excinfo, source) + else: + if source: + self.repr_source(source, ' ') + self.repr_locals(entry.frame.f_locals) + + # trailing info + if entry == last: + out_err_reporter() + self.out.sep("_") + else: + if index == recursionindex: + self.out.line("Recursion detected (same locals & position)") + self.out.sep("!") + break + + # the following is only used by the combination '--pdb --tb=no' + repr_failure_tbno = repr_failure_tbshort Modified: py/branch/config/py/test/rsession/outcome.py ============================================================================== --- py/branch/config/py/test/rsession/outcome.py (original) +++ py/branch/config/py/test/rsession/outcome.py Wed Jan 24 13:43:17 2007 @@ -26,7 +26,8 @@ excinfo = self.excinfo tb_info = [self.traceback_entry_repr(x, tbstyle) for x in excinfo.traceback] - return (excinfo.type.__name__, str(excinfo.value), tb_info) + rec_index = excinfo.traceback.recursionindex() + return (excinfo.type.__name__, str(excinfo.value), (tb_info, rec_index)) def traceback_entry_repr(self, tb_entry, tb_style): lineno = tb_entry.lineno @@ -53,14 +54,27 @@ relline, lineno, self.source, self.path = tbentry self.relline = int(relline) self.lineno = int(lineno) + self.locals = {} def __repr__(self): return "line %s in %s\n %s" %(self.lineno, self.path, self.source[100:]) + def getsource(self): + return py.code.Source(self.source).strip() + + def getfirstlinesource(self): + return self.lineno - self.relline + +class TracebackRepr(list): + def recursionindex(self): + return self.recursion_index + class ExcInfoRepr(object): def __init__(self, excinfo): - self.typename, self.value, tb = excinfo - self.traceback = [TracebackEntryRepr(x) for x in tb] + self.typename, self.value, tb_i = excinfo + tb, rec_index = tb_i + self.traceback = TracebackRepr([TracebackEntryRepr(x) for x in tb]) + self.traceback.recursion_index = rec_index def __repr__(self): l = ["%s=%s" %(x, getattr(self, x)) @@ -72,6 +86,14 @@ """ return "%s: %s" % (self.typename, self.value) + def errisinstance(self, exc_t): + if not isinstance(exc_t, tuple): + exc_t = (exc_t,) + for exc in exc_t: + if self.typename == str(exc).split('.')[-1]: + return True + return False + class ReprOutcome(object): def __init__(self, repr_tuple): (self.passed, self.setupfailure, excinfo, self.skipped, Modified: py/branch/config/py/test/rsession/reporter.py ============================================================================== --- py/branch/config/py/test/rsession/reporter.py (original) +++ py/branch/config/py/test/rsession/reporter.py Wed Jan 24 13:43:17 2007 @@ -113,8 +113,8 @@ host = self.gethost(event) self.out.sep('_', "%s on %s" % (" ".join(event.item.listnames()), host)) - self.presenter.repr_item_info(event.item) 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) @@ -134,38 +134,22 @@ if not traceback: self.out.line("empty traceback from item %r" % (item,)) return - #handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle) - self.repr_traceback(item, excinfo, traceback) + + 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) - 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_traceback(self, item, excinfo, traceback): - if self.config.option.tbstyle == 'long': - for index, entry in py.builtin.enumerate(traceback): - self.out.sep('-') - self.out.line("%s: %s" % (entry.path, entry.lineno)) - source = entry.source.split("\n") - self.presenter.repr_source(source, marker_location= - entry.relline) - elif self.config.option.tbstyle == 'short': - for index, entry in py.builtin.enumerate(traceback): - self.out.line("%s: %s" % (entry.path, entry.lineno)) - self.out.line(entry.source) - self.presenter.repr_failure_explanation(excinfo, entry.source) + self.repr_out_err(outcome) def skips(self): texts = {} Modified: py/branch/config/py/test/terminal/terminal.py ============================================================================== --- py/branch/config/py/test/terminal/terminal.py (original) +++ py/branch/config/py/test/terminal/terminal.py Wed Jan 24 13:43:17 2007 @@ -273,89 +273,8 @@ if not traceback: self.out.line("empty traceback from item %r" % (item,)) return - handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle) - handler(item, excinfo, traceback) - - def repr_failure_tblong(self, item, excinfo, traceback): - if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError): - recursionindex = traceback.recursionindex() - else: - recursionindex = None - last = traceback[-1] - first = traceback[0] - for index, entry in py.builtin.enumerate(traceback): - if entry == first: - if item: - self.presenter.repr_item_info(item) - self.out.line() - else: - self.out.line("") - source = self.presenter.getentrysource(entry) - firstsourceline = entry.getfirstlinesource() - marker_location = entry.lineno - firstsourceline - if entry == last: - self.presenter.repr_source(source, 'E', marker_location) - self.presenter.repr_failure_explanation(excinfo, source) - else: - self.presenter.repr_source(source, '>', marker_location) - self.out.line("") - self.out.line("[%s:%d]" %(entry.frame.code.path, entry.lineno+1)) - #self.repr_locals(entry) - - # trailing info - if entry == last: - self.repr_out_err(item) - self.out.sep("_") - else: - self.out.sep("_ ") - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break - - def repr_failure_tbshort(self, item, excinfo, traceback): - # print a Python-style short traceback - if not self.config.option.nomagic and excinfo.errisinstance(RuntimeError): - recursionindex = traceback.recursionindex() - else: - recursionindex = None - last = traceback[-1] - first = traceback[0] - self.out.line() - for index, entry in py.builtin.enumerate(traceback): - code = entry.frame.code - self.out.line(' File "%s", line %d, in %s' % ( - code.raw.co_filename, entry.lineno+1, code.raw.co_name)) - try: - fullsource = entry.frame.code.fullsource - except py.error.ENOENT: - source = ["?"] - else: - try: - source = [fullsource[entry.lineno].lstrip()] - except IndexError: - source = [] - if entry == last: - if source: - self.presenter.repr_source(source, 'E') - self.presenter.repr_failure_explanation(excinfo, source) - else: - if source: - self.presenter.repr_source(source, ' ') - self.repr_locals(entry) - - # trailing info - if entry == last: - self.repr_out_err(item) - self.out.sep("_") - else: - if index == recursionindex: - self.out.line("Recursion detected (same locals & position)") - self.out.sep("!") - break - - # the following is only used by the combination '--pdb --tb=no' - repr_failure_tbno = repr_failure_tbshort + handler = getattr(self.presenter, 'repr_failure_tb%s' % self.config.option.tbstyle) + handler(item, excinfo, traceback, lambda : self.repr_out_err(item)) def repr_out_err(self, colitem): for parent in colitem.listchain(): @@ -363,23 +282,6 @@ if obj: self.out.sep("- ", "%s: recorded std%s" % (parent.name, name)) self.out.line(obj) - - def repr_locals(self, entry): - if self.config.option.showlocals: - self.out.sep('- ', 'locals') - for name, value in entry.frame.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 = py.__.code.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_pythonversion(): v = py.std.sys.version_info Modified: py/branch/config/py/test/testing/test_repr.py ============================================================================== --- py/branch/config/py/test/testing/test_repr.py (original) +++ py/branch/config/py/test/testing/test_repr.py Wed Jan 24 13:43:17 2007 @@ -3,6 +3,7 @@ from py.__.test.representation import Presenter from py.__.test.terminal.out import getout from StringIO import StringIO +import sys def test_repr_source(): source = py.code.Source(""" @@ -20,3 +21,44 @@ assert lines[0].find("def f(x)") != -1 assert lines[1].find("pass") != -1 +def test_repr_failure_explanation(): + """ We check here if indentation is right + """ + def f(): + def g(): + 1/0 + try: + g() + except: + e = py.code.ExceptionInfo() + return e + config = py.test.config._reparse([]) + s = StringIO() + out = getout(s) + p = Presenter(out, config) + source = py.code.Source(f) + e = f() + p.repr_failure_explanation(e, source) + assert s.getvalue().startswith("> ") + +def test_repr_local(): + config = py.test.config._reparse(['--showlocals']) + s = StringIO() + out = getout(s) + p = Presenter(out, config) + p.repr_locals(locals()) + for key in locals().keys(): + assert s.getvalue().find(key) != -1 + +def test_repr_traceback_long(): + py.test.skip("unfinished") + config = py.test.config._reparse([]) + s = StringIO() + out = getout(s) + p = Presenter(out, config) + # errr... here we should + # a) prepare an item + # b) prepare excinfo + # c) prepare some traceback info, with few different ideas, + # like recursion detected etc. + # test it... From guido at codespeak.net Wed Jan 24 13:45:14 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 13:45:14 +0100 (CET) Subject: [py-svn] r37256 - py/dist/py/apigen Message-ID: <20070124124514.6D03610077@code0.codespeak.net> Author: guido Date: Wed Jan 24 13:45:13 2007 New Revision: 37256 Modified: py/dist/py/apigen/apigen.py py/dist/py/apigen/htmlgen.py Log: Some style fixes and small HTML tweaks, fixed source links pointing to outside the package (for methods that are defined on superclasses that live outside the package). Modified: py/dist/py/apigen/apigen.py ============================================================================== --- py/dist/py/apigen/apigen.py (original) +++ py/dist/py/apigen/apigen.py Wed Jan 24 13:45:13 2007 @@ -36,7 +36,7 @@ all_names = dsa._get_names(filter=lambda x, y: True) namespace_tree = htmlgen.create_namespace_tree(all_names) - apb = htmlgen.ApiPageBuilder(targetdir, l, dsa) + apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir) spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir) ns_data = apb.prepare_namespace_pages(namespace_tree) Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Wed Jan 24 13:45:13 2007 @@ -14,7 +14,7 @@ # HTML related stuff class H(html): class Description(html.div): - style = html.Style(margin_left='10em') + style = html.Style(margin_left='15em') class NamespaceDescription(Description): pass @@ -47,10 +47,11 @@ pass class Docstring(html.div): - style = html.Style(white_space='pre') + style = html.Style(white_space='pre', min_height='3em') class Navigation(html.div): - style = html.Style(min_height='99%', float='left', margin_top='1.2em') + style = html.Style(min_height='99%', float='left', margin_top='1.2em', + overflow='auto', width='15em', white_space='nowrap') class NavigationItem(html.div): pass @@ -62,10 +63,10 @@ pass class NonPythonSource(html.pre): - style = html.Style(margin_left='10em') + style = html.Style(margin_left='15em') class DirList(html.div): - style = html.Style(margin_left='10em') + style = html.Style(margin_left='15em') class DirListItem(html.div): pass @@ -279,10 +280,12 @@ class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ - def __init__(self, base, linker, dsa): + def __init__(self, base, linker, dsa, projroot): self.base = base self.linker = linker self.dsa = dsa + self.projroot = projroot + self.projpath = py.path.local(projroot) def build_callable_view(self, dotted_name): """ build the html for a class method """ @@ -295,13 +298,19 @@ sourcefile = inspect.getsourcefile(func) callable_source = self.dsa.get_function_source(dotted_name) + is_in_pkg = py.path.local(sourcefile).relto(self.projpath) # i assume they're both either available or unavailable(XXX ?) - if sourcefile and callable_source: + if is_in_pkg and sourcefile and callable_source: csource = H.div(H.br(), - H.a('source:', + H.a('origin: %s' % (sourcefile,), href=self.linker.get_lazyhref(sourcefile)), H.br(), H.SourceDef(H.pre(callable_source))) + elif not is_in_pkg and sourcefile and callable_source: + csource = H.div(H.br(), + H.em('origin: %s' % (sourcefile,)), + H.br(), + H.SourceDef(H.pre(callable_source))) else: csource = H.SourceDef('could not get source file') @@ -328,7 +337,7 @@ else: if sourcefile[-1] in ['o', 'c']: sourcefile = sourcefile[:-1] - sourcelink = H.div(H.a('source', + sourcelink = H.div(H.a('view source', href=self.linker.get_lazyhref(sourcefile))) docstring = cls.__doc__ @@ -354,20 +363,28 @@ snippet = H.ClassDescription( # XXX bases HTML H.ClassDef('%s(' % (clsname,), *basehtml), - sourcelink, H.Docstring(docstring or H.em('no docstring available')), + sourcelink, ) - snippet.append(H.h2('Functions:')) - for method in methods: - snippet += self.build_callable_view('%s.%s' % (dotted_name, - method)) + if methods: + snippet.append(H.h2('methods:')) + for method in methods: + snippet += self.build_callable_view('%s.%s' % (dotted_name, + method)) + # XXX properties return snippet def build_namespace_view(self, namespace_dotted_name, item_dotted_names): """ build the html for a namespace (module) """ - print 'building namespace for', namespace_dotted_name + try: + obj = self.dsa.get_obj(namespace_dotted_name) + except KeyError: + docstring = None + else: + docstring = obj.__doc__ snippet = H.NamespaceDescription( H.NamespaceDef(namespace_dotted_name), + H.Docstring(docstring or H.em('no docstring available')) ) for dotted_name in item_dotted_names: itemname = dotted_name.split('.')[-1] From fijal at codespeak.net Wed Jan 24 13:54:31 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 13:54:31 +0100 (CET) Subject: [py-svn] r37257 - in py/branch/config/py: code test test/rsession Message-ID: <20070124125431.E36FB10077@code0.codespeak.net> Author: fijal Date: Wed Jan 24 13:54:30 2007 New Revision: 37257 Modified: py/branch/config/py/code/traceback2.py py/branch/config/py/test/representation.py py/branch/config/py/test/rsession/outcome.py Log: Yeah, but it seems that short tracebacks was not tested at all, argh argh argh. Modified: py/branch/config/py/code/traceback2.py ============================================================================== --- py/branch/config/py/code/traceback2.py (original) +++ py/branch/config/py/code/traceback2.py Wed Jan 24 13:54:30 2007 @@ -82,6 +82,10 @@ line = "" return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line) + def name(self): + return self.frame.code.raw.co_name + name = property(name, None, None, "co_name of underlaying code") + class Traceback(list): """ Traceback objects encapsulate and offer higher level access to Traceback entries. Modified: py/branch/config/py/test/representation.py ============================================================================== --- py/branch/config/py/test/representation.py (original) +++ py/branch/config/py/test/representation.py Wed Jan 24 13:54:30 2007 @@ -138,17 +138,20 @@ last = traceback[-1] first = traceback[0] self.out.line() - for index, entry in py.builtin.enumerate(traceback): - code = entry.frame.code + for index, entry in py.builtin.enumerate(traceback): + path = entry.path.basename + firstsourceline = entry.getfirstlinesource() + relline = entry.lineno - firstsourceline self.out.line(' File "%s", line %d, in %s' % ( - code.raw.co_filename, entry.lineno+1, code.raw.co_name)) + path, entry.lineno+1, entry.name)) try: - fullsource = entry.frame.code.fullsource + source = entry.getsource().lines except py.error.ENOENT: source = ["?"] else: try: - source = [fullsource[entry.lineno].lstrip()] + if len(source) > 1: + source = source[relline] except IndexError: source = [] if entry == last: @@ -158,7 +161,7 @@ else: if source: self.repr_source(source, ' ') - self.repr_locals(entry.frame.f_locals) + self.repr_locals(entry.locals) # trailing info if entry == last: Modified: py/branch/config/py/test/rsession/outcome.py ============================================================================== --- py/branch/config/py/test/rsession/outcome.py (original) +++ py/branch/config/py/test/rsession/outcome.py Wed Jan 24 13:54:30 2007 @@ -38,11 +38,12 @@ source = str(tb_entry.getsource()) else: source = str(tb_entry.getsource()).split("\n")[relline] + name = tb_entry.frame.code.raw.co_name # XXX: Bare except. What can getsource() raise anyway? # SyntaxError, AttributeError, IndentationError for sure, check it #except: # source = "" - return (relline, lineno, source, path) + return (relline, lineno, source, path, name) def make_repr(self, tbstyle="long"): return (self.passed, self.setupfailure, @@ -51,8 +52,9 @@ class TracebackEntryRepr(object): def __init__(self, tbentry): - relline, lineno, self.source, self.path = tbentry + relline, lineno, self.source, self.path, self.name = tbentry self.relline = int(relline) + self.path = py.path.local(self.path) self.lineno = int(lineno) self.locals = {} From guido at codespeak.net Wed Jan 24 14:14:26 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 14:14:26 +0100 (CET) Subject: [py-svn] r37258 - in py/dist/py/apigen: . testing tracer tracer/testing tracer/testing/package/submodule Message-ID: <20070124131426.549EB10077@code0.codespeak.net> Author: guido Date: Wed Jan 24 14:14:23 2007 New Revision: 37258 Modified: py/dist/py/apigen/htmlgen.py py/dist/py/apigen/testing/test_apigen_example.py py/dist/py/apigen/tracer/docstorage.py py/dist/py/apigen/tracer/testing/package/submodule/__init__.py py/dist/py/apigen/tracer/testing/test_docgen.py py/dist/py/apigen/tracer/testing/test_package.py Log: Fixed HTML validation, fixed bug in get_star_import_tree() that made that too much stuff was reported as part of the namespace. Modified: py/dist/py/apigen/htmlgen.py ============================================================================== --- py/dist/py/apigen/htmlgen.py (original) +++ py/dist/py/apigen/htmlgen.py Wed Jan 24 14:14:23 2007 @@ -74,7 +74,6 @@ class ValueDescList(html.ul): def __init__(self, *args, **kwargs): super(H.ValueDescList, self).__init__(*args, **kwargs) - self.insert(0, html.div('where:')) class ValueDescItem(html.li): pass @@ -544,7 +543,7 @@ if next: items.append(next) valuedesc.append(H.ValueDescItem(*items)) - return valuedesc + return H.div(H.div('where:'), valuedesc) def process_type_link(self, _type): # now we do simple type dispatching and provide a link in this case Modified: py/dist/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/dist/py/apigen/testing/test_apigen_example.py (original) +++ py/dist/py/apigen/testing/test_apigen_example.py Wed Jan 24 14:14:23 2007 @@ -104,7 +104,7 @@ self.base = base = py.test.ensuretemp('%s_%s' % ( self.__class__.__name__, meth.im_func.func_name)) self.linker = linker = LinkerForTests() - self.apb = ApiPageBuilder(base, linker, self.dsa) + self.apb = ApiPageBuilder(base, linker, self.dsa, self.fs_root) self.spb = SourcePageBuilder(base, linker, self.fs_root) self.namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', @@ -123,7 +123,7 @@ pkg.main.sub.func(10) pkg.main.sub.func(pkg.main.SomeClass(10)) t.end_tracing() - apb = ApiPageBuilder(self.base, self.linker, dsa) + apb = ApiPageBuilder(self.base, self.linker, dsa, self.fs_root) snippet = apb.build_callable_view('main.sub.func') html = snippet.unicode() print html @@ -133,7 +133,7 @@ 'Class SomeClass', 'Int>', 'return value :: <None>', - 'source:', + 'origin: %s' % (self.fs_root.join('pkg/func.py'),), 'def func(arg1):', ]) _checkhtmlsnippet(html) Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Wed Jan 24 14:14:23 2007 @@ -132,9 +132,10 @@ # XXX return self - def get_star_import_tree(self, module, parentname): + def get_star_import_tree(self, module, modname): """ deal with '*' entries in an initpkg situation """ ret = {} + modpath = py.path.local(inspect.getsourcefile(module)) pkgpath = module.__package__.getpath() for objname in dir(module): if objname.startswith('_'): @@ -148,11 +149,10 @@ except TypeError: continue else: - if not sourcefile_object.relto(pkgpath): + if sourcefile_object.strpath != modpath.strpath: # not in this package continue - dotted_name = '%s.%s' % (parentname, objname) - print 'adding dotted name', dotted_name + dotted_name = '%s.%s' % (modname, objname) ret[dotted_name] = obj return ret @@ -331,3 +331,4 @@ def get_obj(self, name): return self.ds.descs[name].pyobj + Modified: py/dist/py/apigen/tracer/testing/package/submodule/__init__.py ============================================================================== --- py/dist/py/apigen/tracer/testing/package/submodule/__init__.py (original) +++ py/dist/py/apigen/tracer/testing/package/submodule/__init__.py Wed Jan 24 14:14:23 2007 @@ -8,5 +8,6 @@ 'pak.mod.one': ('./pak/mod.py', 'one'), 'pak.mod.two': ('./pak/mod.py', 'nottwo'), 'notpak.notmod.notclass': ('./pak/mod.py', 'cls'), - 'somenamespace': ('./pak/mod.py', '*'), + 'somenamespace': ('./pak/somenamespace.py', '*'), }) + Modified: py/dist/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_docgen.py (original) +++ py/dist/py/apigen/tracer/testing/test_docgen.py Wed Jan 24 14:14:23 2007 @@ -389,6 +389,8 @@ return self.somevar + 1 """)) temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ + from pkg.main.sub import func + def foo(): return 'bar' def baz(qux): Modified: py/dist/py/apigen/tracer/testing/test_package.py ============================================================================== --- py/dist/py/apigen/tracer/testing/test_package.py (original) +++ py/dist/py/apigen/tracer/testing/test_package.py Wed Jan 24 14:14:23 2007 @@ -24,12 +24,11 @@ def test_init(self): ds = self.ds print sorted(ds.descs.keys()) - assert len(ds.descs) == 8 + assert len(ds.descs) == 6 assert py.builtin.sorted(ds.descs.keys()) == [ 'notpak.notmod.notclass', 'notpak.notmod.notclass.__init__', - 'pak.mod.one', 'pak.mod.two', 'somenamespace.cls', - 'somenamespace.cls.__init__', 'somenamespace.nottwo', - 'somenamespace.one'] + 'pak.mod.one', 'pak.mod.two', 'somenamespace.bar', + 'somenamespace.foo'] def test_simple_call(self): ds = self.ds From guido at codespeak.net Wed Jan 24 14:26:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 14:26:53 +0100 (CET) Subject: [py-svn] r37260 - py/dist/py/apigen/tracer/testing/package/submodule/pak Message-ID: <20070124132653.A0B4210077@code0.codespeak.net> Author: guido Date: Wed Jan 24 14:26:52 2007 New Revision: 37260 Added: py/dist/py/apigen/tracer/testing/package/submodule/pak/somenamespace.py Log: Oops, missing file for tracer tests. Added: py/dist/py/apigen/tracer/testing/package/submodule/pak/somenamespace.py ============================================================================== --- (empty file) +++ py/dist/py/apigen/tracer/testing/package/submodule/pak/somenamespace.py Wed Jan 24 14:26:52 2007 @@ -0,0 +1,5 @@ +def foo(x): + return x + 1 + +def bar(x): + return x + 2 From hpk at codespeak.net Wed Jan 24 14:55:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 14:55:01 +0100 (CET) Subject: [py-svn] r37262 - py/branch/config/py/test/rsession/testing Message-ID: <20070124135501.1BC0610077@code0.codespeak.net> Author: hpk Date: Wed Jan 24 14:55:00 2007 New Revision: 37262 Modified: py/branch/config/py/test/rsession/testing/test_rsession.py Log: fix failing test Modified: py/branch/config/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/branch/config/py/test/rsession/testing/test_rsession.py (original) +++ py/branch/config/py/test/rsession/testing/test_rsession.py Wed Jan 24 14:55:00 2007 @@ -140,17 +140,17 @@ assert len(passevents) == 3 assert len(failevents) == 3 tb = failevents[0].outcome.excinfo.traceback - assert tb[0].path.find("test_one") != -1 + assert str(tb[0].path).find("test_one") != -1 assert tb[0].source.find("test_2") != -1 assert failevents[0].outcome.excinfo.typename == 'AssertionError' tb = failevents[1].outcome.excinfo.traceback - assert tb[0].path.find("test_one") != -1 + assert str(tb[0].path).find("test_one") != -1 assert tb[0].source.find("test_3") != -1 assert failevents[1].outcome.excinfo.typename == 'ValueError' assert failevents[1].outcome.excinfo.value == '23' tb = failevents[2].outcome.excinfo.traceback assert failevents[2].outcome.excinfo.typename == 'TypeError' - assert tb[0].path.find("executor") != -1 + assert str(tb[0].path).find("executor") != -1 assert tb[0].source.find("execute") != -1 def test_setup_teardown_ssh(self): From hpk at codespeak.net Wed Jan 24 14:59:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 14:59:23 +0100 (CET) Subject: [py-svn] r37263 - in py/branch/config/py/test: . rsession terminal testing Message-ID: <20070124135923.DEEA710077@code0.codespeak.net> Author: hpk Date: Wed Jan 24 14:59:20 2007 New Revision: 37263 Modified: py/branch/config/py/test/config.py py/branch/config/py/test/rsession/slave.py py/branch/config/py/test/terminal/remote.py py/branch/config/py/test/testing/test_config.py Log: * introduce config.initdirect() which should be the central place to perform initialization on remote nodes/sides. * fix --looponfailing (which works nicely now) * have Conftest() be an instance instead of a class-variable (avoid global state) * more thoroughly use the "config.topdir" concept (which is not neccessarily the pkgdir.dirpath()) * on merge_repr don't write values to global defaultconftest module (this gets tests failing that reuse defaultconftest module, but also we want to avoid global state in general) Modified: py/branch/config/py/test/config.py ============================================================================== --- py/branch/config/py/test/config.py (original) +++ py/branch/config/py/test/config.py Wed Jan 24 14:59:20 2007 @@ -56,13 +56,14 @@ def initdirect(self, topdir, repr, coltrails=None): assert not self._initialized self._initialized = True + self.topdir = py.path.local(topdir) self.merge_repr(repr) - self.topdir = topdir - self.args = coltrails or [] + self._coltrails = coltrails def getcolitems(self): """ return initial collectors. """ - return [self._getcollector(path) for path in self.args] + trails = getattr(self, '_coltrails', None) + return [self._getcollector(path) for path in (trails or self.args)] def _getcollector(self, path): if isinstance(path, tuple): @@ -187,7 +188,11 @@ value = getattr(self.option, name) checkmarshal(name, value) cmdlineopts[name] = value - return conftestdict, cmdlineopts + l = [] + for path in self.args: + path = py.path.local(path) + l.append(path.relto(self.topdir)) + return l, conftestdict, cmdlineopts def merge_repr(self, repr): """ merge in the conftest and cmdline option values @@ -198,10 +203,13 @@ stored on the default conftest module (last priority) and the cmdline options on self.option. """ - defaultconftest = self.conftest.getconftestmodules(None)[0] - conftestdict, cmdlineopts = repr - for name, val in conftestdict.items(): - setattr(defaultconftest, name, val) + class override: + def __init__(self, d): + self.__dict__.update(d) + 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) Modified: py/branch/config/py/test/rsession/slave.py ============================================================================== --- py/branch/config/py/test/rsession/slave.py (original) +++ py/branch/config/py/test/rsession/slave.py Wed Jan 24 14:59:20 2007 @@ -130,10 +130,10 @@ config = py.test.config if config._initialized: config = config._reparse([basedir]) + config.merge_repr(config_repr) else: - config.conftest.setinitial([basedir]) + config.initdirect(basedir, config_repr) #config.conftest.lget('adddefaultoptions')() - config.merge_repr(config_repr) if not config.option.nomagic: py.magic.invoke(assertion=1) mod = __import__(pkgname) Modified: py/branch/config/py/test/terminal/remote.py ============================================================================== --- py/branch/config/py/test/terminal/remote.py (original) +++ py/branch/config/py/test/terminal/remote.py Wed Jan 24 14:59:20 2007 @@ -99,7 +99,7 @@ """, stdout=self.out, stderr=self.out) print "MASTER: triggered slave terminal session ->" repr = self.config.make_repr(conftestnames=[]) - channel.send((repr, [str(x) for x in self.config.args], failures)) + channel.send((str(self.config.topdir), repr, failures)) print "MASTER: send start info" try: return channel.receive() @@ -110,22 +110,16 @@ def slaverun_TerminalSession(channel): """ we run this on the other side. """ print "SLAVE: starting" - repr, args, failures = channel.receive() + topdir, repr, failures = channel.receive() print "SLAVE: received configuration" config = py.test.config - config.args = args - config.conftest.setinitial(args) - config.merge_repr(repr) + config.initdirect(topdir, repr, failures) config.option.looponfailing = False config.option.usepdb = False config.option.executable = None session = config.initsession() session.shouldclose = channel.isclosed - if failures: - cols = getfailureitems(failures) - else: - cols = args print "SLAVE: starting session.main()" session.main() failures = session.getitemoutcomepairs(py.test.Item.Failed) Modified: py/branch/config/py/test/testing/test_config.py ============================================================================== --- py/branch/config/py/test/testing/test_config.py (original) +++ py/branch/config/py/test/testing/test_config.py Wed Jan 24 14:59:20 2007 @@ -100,8 +100,8 @@ assert gettopdir([c]) == a assert gettopdir([c, Z]) == tmp + def test_config_init_direct(): - py.test.skip("XXX partially not implemented") tmp = py.test.ensuretemp("initdirect") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1 ; y=2") @@ -114,12 +114,10 @@ for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): assert col1.fspath == col2.fspath py.test.raises(AssertionError, "config2.initdirect(None, None)") - config3 = py.test.config._reparse([tmp.dirpath()]) - col = config3._getcollector(hello) - # XXX write repr_colspecs(topdir, cols), - # and have it consumable by initdirect() + from py.__.test.config import Config + config3 = Config() config3.initdirect(topdir=tmp.dirpath(), repr=repr, - collectspecs=[col.listnames()]) + coltrails=[(tmp.basename, (hello.basename,))]) assert config3.getvalue('x') == 1 assert config3.getvalue('y') == 2 cols = config.getcolitems() @@ -127,8 +125,7 @@ col = cols[0] assert col.name == 'test_hello.py' assert col.parent.name == tmp.basename - assert col.parent.parent.name == tmp.dirpath().basename - assert col.parent.parent.parent is None + assert col.parent.parent is None def test_config_make_and_merge_repr(): tmp = py.test.ensuretemp("reprconfig1") From hpk at codespeak.net Wed Jan 24 15:24:02 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 15:24:02 +0100 (CET) Subject: [py-svn] r37264 - py/trunk Message-ID: <20070124142402.5677E10075@code0.codespeak.net> Author: hpk Date: Wed Jan 24 15:24:01 2007 New Revision: 37264 Added: py/trunk/ - copied from r37263, py/dist/ Log: create the new development trunk From guido at codespeak.net Wed Jan 24 17:06:38 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 17:06:38 +0100 (CET) Subject: [py-svn] r37266 - in py/trunk/py/apigen: . rest/testing testing tracer tracer/testing Message-ID: <20070124160638.7478910075@code0.codespeak.net> Author: guido Date: Wed Jan 24 17:06:35 2007 New Revision: 37266 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/rest/testing/test_rest.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/tracer/description.py py/trunk/py/apigen/tracer/testing/test_docgen.py py/trunk/py/apigen/tracer/testing/test_magic.py Log: Fixed a problem in the CallFrame code, it doesn't like py.code.Source objects (no test, sorry, didn't succeed in reproducing it from a test yet), some minor HTML changes. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Jan 24 17:06:35 2007 @@ -293,21 +293,21 @@ docstring = func.__doc__ localname = func.__name__ argdesc = get_param_htmldesc(self.linker, func) - valuedesc = self.build_callable_value_description(dotted_name) + valuedesc = self.build_callable_signature_description(dotted_name) sourcefile = inspect.getsourcefile(func) callable_source = self.dsa.get_function_source(dotted_name) - is_in_pkg = py.path.local(sourcefile).relto(self.projpath) # i assume they're both either available or unavailable(XXX ?) + is_in_pkg = self.is_in_pkg(sourcefile) if is_in_pkg and sourcefile and callable_source: csource = H.div(H.br(), - H.a('origin: %s' % (sourcefile,), + H.a('source: %s' % (sourcefile,), href=self.linker.get_lazyhref(sourcefile)), H.br(), H.SourceDef(H.pre(callable_source))) elif not is_in_pkg and sourcefile and callable_source: csource = H.div(H.br(), - H.em('origin: %s' % (sourcefile,)), + H.em('source: %s' % (sourcefile,)), H.br(), H.SourceDef(H.pre(callable_source))) else: @@ -525,25 +525,32 @@ selected)) return H.Navigation(*navitems) - def build_callable_value_description(self, dotted_name): + def build_callable_signature_description(self, dotted_name): args, retval = self.dsa.get_function_signature(dotted_name) valuedesc = H.ValueDescList() - for name, _type in args + [('return value', retval)]: - l = self.process_type_link(_type) - items = [] - next = "%s :: " % name - for item in l: - if isinstance(item, str): - next += item - else: - if next: - items.append(next) - next = "" - items.append(item) - if next: - items.append(next) - valuedesc.append(H.ValueDescItem(*items)) - return H.div(H.div('where:'), valuedesc) + for name, _type in args: + valuedesc.append(self.build_sig_value_description(name, _type)) + if retval: + retval = self.process_type_link(retval) + ret = H.div(H.div('where:'), valuedesc, H.div('return value:'), + retval or 'None') + return ret + + def build_sig_value_description(self, name, _type): + l = self.process_type_link(_type) + items = [] + next = "%s : " % name + for item in l: + if isinstance(item, str): + next += item + else: + if next: + items.append(next) + next = "" + items.append(item) + if next: + items.append(next) + return H.ValueDescItem(*items) def process_type_link(self, _type): # now we do simple type dispatching and provide a link in this case @@ -561,7 +568,11 @@ linktarget = self.linker.get_lazyhref(name) lst.append(H.a(str(_type), href=linktarget)) else: + raise IOError('do not think we ever get here?') # we should provide here some way of linking to sourcegen directly lst.append(name) return lst + def is_in_pkg(self, sourcefile): + return py.path.local(sourcefile).relto(self.projpath) + Modified: py/trunk/py/apigen/rest/testing/test_rest.py ============================================================================== --- py/trunk/py/apigen/rest/testing/test_rest.py (original) +++ py/trunk/py/apigen/rest/testing/test_rest.py Wed Jan 24 17:06:35 2007 @@ -404,6 +404,7 @@ assert -1 < source.find("x \:\: ") < call_point source = tempdir.join('method_B.a.txt').read() + py.test.skip('XXX needs to be fixed, clueless atm though') assert source.find('**origin** \: `A`_') > -1 self.check_rest(tempdir) 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 Wed Jan 24 17:06:35 2007 @@ -128,12 +128,13 @@ html = snippet.unicode() print html run_string_sequence_test(html, [ - 'arg1 :: AnyOf(', + 'arg1 : AnyOf(', 'href="', 'Class SomeClass', 'Int>', - 'return value :: <None>', - 'origin: %s' % (self.fs_root.join('pkg/func.py'),), + 'return value:', + '<None>', + 'source: %s' % (self.fs_root.join('pkg/func.py'),), 'def func(arg1):', ]) _checkhtmlsnippet(html) Modified: py/trunk/py/apigen/tracer/description.py ============================================================================== --- py/trunk/py/apigen/tracer/description.py (original) +++ py/trunk/py/apigen/tracer/description.py Wed Jan 24 17:06:35 2007 @@ -18,7 +18,20 @@ self.filename = frame.code.raw.co_filename self.lineno = frame.lineno self.firstlineno = frame.code.firstlineno - self.source = frame.code.source() + + fname = frame.code.raw.co_filename + if fname == '': + self.source = '' + elif hasattr(fname, '__source__'): + # is a py.code.Source object + self.source = str(fname.__source__) + # XXX should we do this? + # self.filename = fname.split('<')[1].split('>')[0] + else: + try: + self.source = frame.code.source() + except IOError: + raise IOError(self.filename) def _getval(self): return (self.filename, self.lineno) Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_docgen.py (original) +++ py/trunk/py/apigen/tracer/testing/test_docgen.py Wed Jan 24 17:06:35 2007 @@ -390,11 +390,15 @@ """)) temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ from pkg.main.sub import func + import py def foo(): return 'bar' + def baz(qux): return qux + + quux = py.code.Source('print "foo"') """)) temp.ensure("pkg/__init__.py").write(py.code.Source("""\ from py.initpkg import initpkg @@ -409,11 +413,15 @@ """)) return temp, 'pkg' -def test_get_initpkg_star_items(): +def setup_pkg_docstorage(): pkgdir, pkgname = setup_fs_project() py.std.sys.path.insert(0, str(pkgdir)) pkg = __import__(pkgname) ds = DocStorage().from_pkg(pkg) + return pkg, ds + +def test_get_initpkg_star_items(): + pkg, ds = setup_pkg_docstorage() sit = ds.get_star_import_tree(pkg.other, 'pkg.other') print sit assert sorted(sit.keys()) == ['pkg.other.baz', 'pkg.other.foo'] Modified: py/trunk/py/apigen/tracer/testing/test_magic.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_magic.py (original) +++ py/trunk/py/apigen/tracer/testing/test_magic.py Wed Jan 24 17:06:35 2007 @@ -3,7 +3,7 @@ """ import py -py.test.skip("These features has been disabled") +py.test.skip("These features have been disabled") from py.__.apigen.tracer.magic import trace, get_storage, stack_copier, \ DocStorageKeeper From hpk at codespeak.net Wed Jan 24 17:39:40 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:39:40 +0100 (CET) Subject: [py-svn] r37268 - py/trunk/py Message-ID: <20070124163940.D773810075@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:39:40 2007 New Revision: 37268 Added: py/trunk/py/__init__.py.merge.tmp - copied, changed from r37267, py/trunk/py/__init__.py Log: merging of file:///svn/py/branch/config/py/__init__.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37129 | fijal | 2007-01-22 14:04:00 +0100 (Mon, 22 Jan 2007) | 2 lines Move rsync to execnet and expose it as py.execnet.RSync ------------------------------------------------------------------------ r37074 | hpk | 2007-01-20 21:43:52 +0100 (Sat, 20 Jan 2007) | 9 lines * config.initsession() deals with getting at a session class and initiliazing it * config._getcollector(path) gets a collector pointing to path * added lots of tests * thus remove the strange getfscollector and map2colitems logic * mark some tkinter tests as skipped * fixing things all around ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/__init__.py.merge.tmp (from r37267, py/trunk/py/__init__.py) ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py.merge.tmp Wed Jan 24 17:39:40 2007 @@ -32,7 +32,7 @@ 'test.compat.TestCase' : ('./test/compat.py', 'TestCase'), # configuration/initialization related test api - 'test.config' : ('./test/config.py', 'config'), + 'test.config' : ('./test/config.py', 'config_per_process'), 'test.ensuretemp' : ('./test/config.py', 'ensuretemp'), 'test.cmdline.main' : ('./test/cmdline.py', 'main'), @@ -93,6 +93,9 @@ 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), + # execnet scripts + 'execnet.RSync' : ('./execnet/rsync.py', 'RSync'), + # input-output helping 'io.dupfile' : ('./io/dupfile.py', 'dupfile'), 'io.FDCapture' : ('./io/capture.py', 'FDCapture'), From hpk at codespeak.net Wed Jan 24 17:39:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:39:43 +0100 (CET) Subject: [py-svn] r37269 - py/trunk/py/test Message-ID: <20070124163943.06AE11007C@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:39:42 2007 New Revision: 37269 Added: py/trunk/py/test/config.py.merge.tmp - copied, changed from r37267, py/trunk/py/test/config.py Log: merging of file:///svn/py/branch/config/py/test/config.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37263 | hpk | 2007-01-24 14:59:20 +0100 (Wed, 24 Jan 2007) | 19 lines * introduce config.initdirect() which should be the central place to perform initialization on remote nodes/sides. * fix --looponfailing (which works nicely now) * have Conftest() be an instance instead of a class-variable (avoid global state) * more thoroughly use the "config.topdir" concept (which is not neccessarily the pkgdir.dirpath()) * on merge_repr don't write values to global defaultconftest module (this gets tests failing that reuse defaultconftest module, but also we want to avoid global state in general) ------------------------------------------------------------------------ r37243 | hpk | 2007-01-24 00:06:24 +0100 (Wed, 24 Jan 2007) | 2 lines small step-wise cleanups ------------------------------------------------------------------------ r37238 | hpk | 2007-01-23 23:09:57 +0100 (Tue, 23 Jan 2007) | 2 lines why do it complicated? ------------------------------------------------------------------------ r37235 | hpk | 2007-01-23 22:25:16 +0100 (Tue, 23 Jan 2007) | 5 lines added get_collector_trail method to get a representation of a collector relative to the topdir. ------------------------------------------------------------------------ r37230 | hpk | 2007-01-23 20:44:22 +0100 (Tue, 23 Jan 2007) | 2 lines typo-level fixes ------------------------------------------------------------------------ r37193 | hpk | 2007-01-23 14:53:17 +0100 (Tue, 23 Jan 2007) | 5 lines introduced config.topdir, tests and use it from terminal/remote at least. also fix fijal's "--box" option handling a bit, but i am not sure it should stay this way at all. ------------------------------------------------------------------------ r37172 | fijal | 2007-01-23 11:26:10 +0100 (Tue, 23 Jan 2007) | 2 lines Boxing implies lsession ------------------------------------------------------------------------ r37114 | hpk | 2007-01-22 02:20:30 +0100 (Mon, 22 Jan 2007) | 6 lines streamlining initial construction of collectors (to unify topdir/pkgdir/collector handling in rsession/terminal/normal) still work in-progress with a skipped test ------------------------------------------------------------------------ r37112 | hpk | 2007-01-22 01:07:09 +0100 (Mon, 22 Jan 2007) | 3 lines * moving towards a common rootdir/root collector * adding an xxxed test ------------------------------------------------------------------------ r37110 | hpk | 2007-01-22 00:39:24 +0100 (Mon, 22 Jan 2007) | 2 lines renaming some variables for more clarity ------------------------------------------------------------------------ r37077 | hpk | 2007-01-20 22:26:52 +0100 (Sat, 20 Jan 2007) | 2 lines cleaning up fixoptions() a bit ------------------------------------------------------------------------ r37076 | hpk | 2007-01-20 22:19:42 +0100 (Sat, 20 Jan 2007) | 2 lines unskip exec and looponfailing tests, fixes for that ------------------------------------------------------------------------ r37075 | hpk | 2007-01-20 21:51:18 +0100 (Sat, 20 Jan 2007) | 2 lines remove two debug prints ------------------------------------------------------------------------ r37074 | hpk | 2007-01-20 21:43:52 +0100 (Sat, 20 Jan 2007) | 9 lines * config.initsession() deals with getting at a session class and initiliazing it * config._getcollector(path) gets a collector pointing to path * added lots of tests * thus remove the strange getfscollector and map2colitems logic * mark some tkinter tests as skipped * fixing things all around ------------------------------------------------------------------------ r37065 | arigo | 2007-01-20 17:15:10 +0100 (Sat, 20 Jan 2007) | 3 lines Choose a random free port when using the --runbrowser option, but stick to the fixed 8000 when using --startserver only. ------------------------------------------------------------------------ r37061 | hpk | 2007-01-20 16:29:25 +0100 (Sat, 20 Jan 2007) | 8 lines refactoring/fixing the remote terminal approach (used for --exec and --looponfailing) to use the new configiguration. Removes lots of strange code. XXX there are still some recursive testing issues. ------------------------------------------------------------------------ r37055 | hpk | 2007-01-20 12:51:54 +0100 (Sat, 20 Jan 2007) | 4 lines reintroducing --session to lookup custom session objects in conftests.py there is no other way to override a session object from conftests. ------------------------------------------------------------------------ r37025 | hpk | 2007-01-19 17:59:50 +0100 (Fri, 19 Jan 2007) | 2 lines remove lget and its last usage (which was a bit random) ------------------------------------------------------------------------ r37020 | hpk | 2007-01-19 17:48:15 +0100 (Fri, 19 Jan 2007) | 3 lines switching in the new way of getting to a sessionclass, removing code in cmdline.py and elsewhere accordingly ------------------------------------------------------------------------ r37013 | hpk | 2007-01-19 17:14:11 +0100 (Fri, 19 Jan 2007) | 2 lines somewhat simpler lookup mechanism for sessions ------------------------------------------------------------------------ r37009 | hpk | 2007-01-19 16:50:32 +0100 (Fri, 19 Jan 2007) | 2 lines adding a config method to determine the session name ------------------------------------------------------------------------ r36994 | fijal | 2007-01-19 15:08:16 +0100 (Fri, 19 Jan 2007) | 2 lines removal of some trailing white-spaces. ------------------------------------------------------------------------ r36964 | hpk | 2007-01-18 19:30:02 +0100 (Thu, 18 Jan 2007) | 8 lines move rconfig functionality to general Config object, with docstrings, and trying to change rsession accordingly. note that it's called "make_repr" and "merge_repr" the latter to make it obvious that we are changing the instance with the given repr. ------------------------------------------------------------------------ r36942 | hpk | 2007-01-18 16:08:06 +0100 (Thu, 18 Jan 2007) | 4 lines group things used for testing and not use non-underscore names (otherwise they are consdiered public and are possibly seen from apigen through py.test.config!) ------------------------------------------------------------------------ r36941 | fijal | 2007-01-18 15:59:08 +0100 (Thu, 18 Jan 2007) | 2 lines Add a hack which allows to temporarily overwrite the config value. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/test/config.py.merge.tmp (from r37267, py/trunk/py/test/config.py) ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py.merge.tmp Wed Jan 24 17:39:42 2007 @@ -26,32 +26,65 @@ class Config(object): """ central hub for dealing with configuration/initialization data. """ Option = optparse.Option - conftest = Conftest() def __init__(self): self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - self._parsed = False + self.conftest = Conftest() + self._initialized = False + self._overwrite_dict = {} 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._parsed, ( + assert not self._initialized, ( "can only parse cmdline args once per Config object") - self._parsed = True + self._initialized = True self.conftest.setinitial(args) - self.conftest.lget('adddefaultoptions')() + self.conftest.rget('adddefaultoptions')() args = [str(x) for x in args] - self._origargs = args - cmdlineoption, remaining = self._parser.parse_args(args) + cmdlineoption, args = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) fixoptions(self.option) # XXX fixing should be moved to sessions - if not remaining: - remaining.append(py.std.os.getcwd()) - self.remaining = remaining - + if not args: + args.append(py.std.os.getcwd()) + self.topdir = gettopdir(args) + self.args = args + + def initdirect(self, topdir, repr, coltrails=None): + assert not self._initialized + self._initialized = True + self.topdir = py.path.local(topdir) + self.merge_repr(repr) + self._coltrails = coltrails + + def getcolitems(self): + """ return initial collectors. """ + trails = getattr(self, '_coltrails', None) + return [self._getcollector(path) for path in (trails or self.args)] + + def _getcollector(self, path): + if isinstance(path, tuple): + 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): + pkgpath = path.pypkgpath() + if pkgpath is None: + pkgpath = path.check(file=1) and path.dirpath() or path + col = self.conftest.rget("Directory", pkgpath)(pkgpath) + col.config = self + return col + def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. This function gets invoked during testing session initialization. @@ -70,79 +103,186 @@ if path is None, lookup the value in the initial conftest modules found during command line parsing. """ - return self.conftest.rget(name, path) + try: + return self._overwrite_dict[name] + except KeyError: + return self.conftest.rget(name, path) + + def initsession(self): + """ return an initialized session object. """ + cls = self._getsessionclass() + session = cls(self) + #session.fixoptions() + return session - def getsessionclass(self): + def _getsessionclass(self): """ return Session class determined from cmdline options and looked up in initial config modules. """ - sessionname = self.option.session + 'Session' - try: - return self.conftest.rget(sessionname) - except KeyError: - pass - sessionimportpaths = self.conftest.lget('sessionimportpaths') - importpath = sessionimportpaths[sessionname] - mod = __import__(importpath, None, None, ['__doc__']) - return getattr(mod, sessionname) + if self.option.session is not None: + return self.conftest.rget(self.option.session) + else: + name = self._getsessionname() + importpath = globals()[name] + mod = __import__(importpath, None, None, '__doc__') + return getattr(mod, name) + + def _getsessionname(self): + """ return default session name as determined from options. """ + name = 'TerminalSession' + if self.option.dist: + name = 'RSession' + elif self.option.tkinter: + name = 'TkinterSession' + else: + optnames = 'startserver runbrowser apigen restreport boxing'.split() + for opt in optnames: + if getattr(self.option, opt, False): + name = 'LSession' + break + else: + if self.getvalue('dist_boxing'): + name = 'LSession' + if self.option.looponfailing: + name = 'RemoteTerminalSession' + elif self.option.executable: + name = 'RemoteTerminalSession' + return name + + def is_boxed(self): + # XXX probably not a good idea to have this special function ... + return self.option.boxing or self.getvalue("dist_boxing") def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ - global config - oldconfig = py.test.config + global config_per_process + oldconfig = py.test.config try: - config = py.test.config = Config() - config.parse(args) - return config + config_per_process = py.test.config = Config() + config_per_process.parse(args) + return config_per_process finally: - config = py.test.config = oldconfig + config_per_process = py.test.config = oldconfig + + def _overwrite(self, name, value): + """ this is used from tests to overwrite values irrespectives of conftests. + """ + self._overwrite_dict[name] = value + + def make_repr(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 merge_repr(self, repr): + """ merge in the conftest and cmdline option values + found in the given representation (produced + by make_repr 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) + 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 + 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 %s" + %(chain[0], self.topdir)) + return relpath, tuple([x.name for x in chain[1:]]) # this is the one per-process instance of py.test configuration -config = Config() +config_per_process = Config() + +# default import paths for sessions + +TkinterSession = 'py.__.test.tkinter.reportsession' +TerminalSession = 'py.__.test.terminal.terminal' +TerminalSession = 'py.__.test.terminal.terminal' +RemoteTerminalSession = 'py.__.test.terminal.remote' +RSession = 'py.__.test.rsession.rsession' +LSession = 'py.__.test.rsession.rsession' # # helpers # +def checkmarshal(name, value): + try: + py.std.marshal.dumps(value) + except ValueError: + raise ValueError("%s=%r is not marshallable" %(name, value)) + def fixoptions(option): """ sanity checks and making option values canonical. """ - if option.looponfailing and option.usepdb: - raise ValueError, "--looponfailing together with --pdb not supported yet." - if option.executable and option.usepdb: - raise ValueError, "--exec together with --pdb not supported yet." - - # setting a correct executable - remote = False - if option.executable is not None: - remote = True - exe = py.path.local(py.std.os.path.expanduser(option.executable)) - if not exe.check(): - exe = py.path.local.sysfind(option.executable) - assert exe.check() - option.executable = exe - else: - option.executable = py.std.sys.executable + # implied options if option.usepdb: if not option.nocapture: - print "--usepdb currently implies --nocapture" + #print "--pdb implies --nocapture" option.nocapture = True - # make information available about wether we should/will be remote - option._remote = remote or option.looponfailing - option._fromremote = False - - # setting a correct frontend session - if option.session: - name = option.session - elif option.tkinter: - name = 'tkinter' - else: - name = 'terminal' - name = name.capitalize() - option.session = name - if option.runbrowser and not option.startserver: - print "Cannot point browser when not starting server" + #print "--runbrowser implies --startserver" option.startserver = 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." + + +def gettopdir(args): + """ return the top directory for the given paths. + if the common base dir resides in a python package + parent directory of the root package is returned. + """ + args = [py.path.local(arg) for arg in args] + p = reduce(py.path.local.common, args) + assert p, "cannot determine common basedir of %s" %(args,) + pkgdir = p.pypkgpath() + if pkgdir is None: + return p + else: + return pkgdir.dirpath() From hpk at codespeak.net Wed Jan 24 17:39:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:39:46 +0100 (CET) Subject: [py-svn] r37270 - py/trunk/py/doc Message-ID: <20070124163946.B6E7F10080@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:39:46 2007 New Revision: 37270 Added: py/trunk/py/doc/TODO.txt.merge.tmp - copied, changed from r37267, py/trunk/py/doc/TODO.txt Log: merging of file:///svn/py/branch/config/py/doc/TODO.txt revisions 36936 to 37267: ------------------------------------------------------------------------ r37133 | fijal | 2007-01-22 14:15:53 +0100 (Mon, 22 Jan 2007) | 2 lines another update what has been done. ------------------------------------------------------------------------ r37132 | fijal | 2007-01-22 14:12:11 +0100 (Mon, 22 Jan 2007) | 2 lines Remove done issues. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/doc/TODO.txt.merge.tmp (from r37267, py/trunk/py/doc/TODO.txt) ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt.merge.tmp Wed Jan 24 17:39:46 2007 @@ -94,7 +94,7 @@ - running "py.test --looponfailing" - running "py.test" distributed on some hosts -* fix --box on config branch (XXX guido - find tests that depend on each other) +* consider --box on trunk (XXX guido - find tests that depend on each other) * tests should not create any tempdirectories in the source code base @@ -109,15 +109,6 @@ distributed testing ---------------------- -* the main rsession methods should have docstrings - explaining the purpose - - (done) - -* move RSync class to py.execnet.RSync, document its usage and - features in the class and method docstrings - - (done) code quality ----------------- From hpk at codespeak.net Wed Jan 24 17:39:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:39:49 +0100 (CET) Subject: [py-svn] r37271 - py/trunk/py/doc Message-ID: <20070124163949.8835310088@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:39:49 2007 New Revision: 37271 Added: py/trunk/py/doc/test_conftest.py.merge.tmp - copied, changed from r37267, py/trunk/py/doc/test_conftest.py Log: merging of file:///svn/py/branch/config/py/doc/test_conftest.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37074 | hpk | 2007-01-20 21:43:52 +0100 (Sat, 20 Jan 2007) | 9 lines * config.initsession() deals with getting at a session class and initiliazing it * config._getcollector(path) gets a collector pointing to path * added lots of tests * thus remove the strange getfscollector and map2colitems logic * mark some tkinter tests as skipped * fixing things all around ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/doc/test_conftest.py.merge.tmp (from r37267, py/trunk/py/doc/test_conftest.py) ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py.merge.tmp Wed Jan 24 17:39:49 2007 @@ -7,7 +7,6 @@ def test_doctest_basic(): # XXX get rid of the next line: py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) - tmpdir.ensure('__init__.py') xtxt = tmpdir.join('x.txt') xtxt.write(py.code.Source(""" @@ -29,7 +28,7 @@ end """)) config = py.test.config._reparse([xtxt]) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config.initsession() session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 @@ -47,7 +46,7 @@ .. _`blah`: javascript:some_function() """)) config = py.test.config._reparse([xtxt]) - session = config.getsessionclass()(config, py.std.sys.stdout) + session = config.initsession() session.main() l = session.getitemoutcomepairs(py.test.Item.Failed) assert len(l) == 0 From hpk at codespeak.net Wed Jan 24 17:39:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:39:53 +0100 (CET) Subject: [py-svn] r37272 - py/trunk/py/test/rsession Message-ID: <20070124163953.CBC311008E@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:39:52 2007 New Revision: 37272 Added: py/trunk/py/test/rsession/rest.py.merge.tmp - copied, changed from r37267, py/trunk/py/test/rsession/rest.py Log: merging of file:///svn/py/branch/config/py/test/rsession/rest.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37178 | fijal | 2007-01-23 11:38:39 +0100 (Tue, 23 Jan 2007) | 2 lines Make it a bit more working. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/test/rsession/rest.py.merge.tmp (from r37267, py/trunk/py/test/rsession/rest.py) ============================================================================== --- py/trunk/py/test/rsession/rest.py (original) +++ py/trunk/py/test/rsession/rest.py.merge.tmp Wed Jan 24 17:39:52 2007 @@ -31,18 +31,23 @@ if self.config.option.verbose: self.add_rest(Paragraph("Unknown report: %s" % what)) + def gethost(self, item): + if item.channel: + return item.channel.gateway.host + return self.hosts[0] + def report_SendItem(self, item): - address = item.channel.gateway.host.hostname + address = self.gethost(item) if self.config.option.verbose: self.add_rest(Paragraph('sending item %s to %s' % (item.item, address))) def report_HostRSyncing(self, item): - self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.hostname[:10], + self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10], item.remoterootpath))) def report_HostReady(self, item): - self.add_rest(LiteralBlock('%10s: READY' % (item.hostname[:10],))) + self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],))) def report_TestStarted(self, event): txt = "Running tests on hosts: %s" % ", ".join(event.hosts) @@ -91,7 +96,7 @@ self.out.write(self.rest.render_links()) def report_ReceivedItemOutcome(self, event): - host = event.channel.gateway.host + host = self.gethost(event) if event.outcome.passed: status = [Strong("PASSED")] self.passed[host] += 1 From hpk at codespeak.net Wed Jan 24 17:39:55 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:39:55 +0100 (CET) Subject: [py-svn] r37273 - py/trunk/py/test/rsession Message-ID: <20070124163955.0A8B21008E@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:39:54 2007 New Revision: 37273 Added: py/trunk/py/test/rsession/rsession.py.merge.tmp - copied, changed from r37267, py/trunk/py/test/rsession/rsession.py Log: merging of file:///svn/py/branch/config/py/test/rsession/rsession.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37173 | fijal | 2007-01-23 11:26:32 +0100 (Tue, 23 Jan 2007) | 2 lines Adhere to --box option. ------------------------------------------------------------------------ r37127 | fijal | 2007-01-22 13:48:33 +0100 (Mon, 22 Jan 2007) | 2 lines Add a few docstrings. ------------------------------------------------------------------------ r37110 | hpk | 2007-01-22 00:39:24 +0100 (Mon, 22 Jan 2007) | 2 lines renaming some variables for more clarity ------------------------------------------------------------------------ r37065 | arigo | 2007-01-20 17:15:10 +0100 (Sat, 20 Jan 2007) | 3 lines Choose a random free port when using the --runbrowser option, but stick to the fixed 8000 when using --startserver only. ------------------------------------------------------------------------ r37063 | arigo | 2007-01-20 17:00:31 +0100 (Sat, 20 Jan 2007) | 5 lines webbrowser.open() may block until the browser finishes or not, so better start it from a background thread. An case where it blocks is if you define the $BROWSER environment variable. ------------------------------------------------------------------------ r37015 | fijal | 2007-01-19 17:24:32 +0100 (Fri, 19 Jan 2007) | 2 lines Major cleanup of hostmanage. Got rid of global functions in favor of some better-structured classes. ------------------------------------------------------------------------ r36988 | fijal | 2007-01-19 13:25:05 +0100 (Fri, 19 Jan 2007) | 2 lines Improve the C-c support, also keep web interface informed about crashes/interrupts. ------------------------------------------------------------------------ r36969 | fijal | 2007-01-18 20:10:36 +0100 (Thu, 18 Jan 2007) | 3 lines Move teardown hosts out of finally (needs further refactoring) Kill adddefaultoptions call for now. ------------------------------------------------------------------------ r36964 | hpk | 2007-01-18 19:30:02 +0100 (Thu, 18 Jan 2007) | 8 lines move rconfig functionality to general Config object, with docstrings, and trying to change rsession accordingly. note that it's called "make_repr" and "merge_repr" the latter to make it obvious that we are changing the instance with the given repr. ------------------------------------------------------------------------ r36962 | fijal | 2007-01-18 19:06:13 +0100 (Thu, 18 Jan 2007) | 3 lines Huge refactoring. Got rid of remote_options and session_options, two tests still fail, so it's intermediate checkin. ------------------------------------------------------------------------ r36943 | fijal | 2007-01-18 16:09:30 +0100 (Thu, 18 Jan 2007) | 3 lines at least LSession should work now. Did not touched yet the remote options stuff, but some simplifications were done. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/test/rsession/rsession.py.merge.tmp (from r37267, py/trunk/py/test/rsession/rsession.py) ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py.merge.tmp Wed Jan 24 17:39:54 2007 @@ -11,74 +11,12 @@ from py.__.test.rsession import report from py.__.test.rsession.master import \ setup_slave, MasterNode, dispatch_loop, itemgen, randomgen -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo +from py.__.test.rsession.hostmanage import HostInfo, HostOptions, HostManager from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ - box_runner, RunnerPolicy + box_runner from py.__.test.rsession.reporter import LocalReporter, RemoteReporter -class RemoteOptions(object): - def __init__(self, d): - self.d = d - - def __getattr__(self, attr): - if attr == 'd': - return self.__dict__['d'] - return self.d[attr] - - def __setitem__(self, item, val): - self.d[item] = val - -# XXX: Must be initialised somehow -remote_options = RemoteOptions({'we_are_remote':False}) - -class SessionOptions: - defaults = { - 'max_tasks_per_node' : 15, - 'runner_policy' : 'plain_runner', - 'nice_level' : 0, - 'waittime' : 100.0, - 'import_pypy' : False, - } - - config = None - - def getvalue(self, opt): - try: - return getattr(self.config.getvalue('SessionOptions'), opt) - except (KeyError, AttributeError): - try: - return self.defaults[opt] - except KeyError: - raise AttributeError("Option %s undeclared" % opt) - - def bind_config(self, config): - self.config = config - # copy to remote session options - try: - ses_opt = self.config.getvalue('SessionOptions').__dict__ - except KeyError: - ses_opt = self.defaults - for key in self.defaults: - try: - val = ses_opt[key] - except KeyError: - val = self.defaults[key] - remote_options[key] = val - # copy to remote all options - for item, val in config.option.__dict__.items(): - remote_options[item] = val - - def __repr__(self): - return "" % self.config - - def __getattr__(self, attr): - if self.config is None: - raise AttributeError("Need to set up config first") - return self.getvalue(attr) - -session_options = SessionOptions() - class AbstractSession(object): """ An abstract session executes collectors/items through a runner. @@ -114,17 +52,29 @@ getpkgdir = staticmethod(getpkgdir) def init_reporter(self, reporter, sshhosts, reporter_class, arg=""): + """ This initialises so called `reporter` class, which will + handle all event presenting to user. Does not get called + if main received custom reporter + """ startserverflag = self.config.option.startserver restflag = self.config.option.restreport if startserverflag and reporter is None: from py.__.test.rsession.web import start_server, exported_methods + if self.config.option.runbrowser: + from socket import INADDR_ANY + port = INADDR_ANY # pick a random port when starting browser + else: + port = 8000 # stick to a fixed port otherwise reporter = exported_methods.report - start_server() + httpd = start_server(server_address = ('', port)) + port = httpd.server_port if self.config.option.runbrowser: - import webbrowser - webbrowser.open("http://localhost:8000") + import webbrowser, thread + # webbrowser.open() may block until the browser finishes or not + url = "http://localhost:%d" % (port,) + thread.start_new_thread(webbrowser.open, (url,)) elif reporter is None: if restflag: from py.__.test.rsession.rest import RestReporter @@ -150,6 +100,8 @@ reporterror = staticmethod(reporterror) def kill_server(self, startserverflag): + """ Kill web server + """ if startserverflag: from py.__.test.rsession.web import kill_server kill_server() @@ -172,6 +124,8 @@ return new_reporter, checkfun def parse_directories(sshhosts): + """ Parse sshadresses of hosts to have distinct hostname/hostdir + """ directories = {} for host in sshhosts: m = re.match("^(.*?):(.*)$", host.hostname) @@ -186,9 +140,8 @@ """ def main(self, reporter=None): """ main loop for running tests. """ - args = self.config.remaining + args = self.config.args - session_options.bind_config(self.config) sshhosts, remotepython, rsync_roots = self.read_distributed_config() reporter, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) @@ -198,22 +151,42 @@ pkgdir = self.getpkgdir(args[0]) done_dict = {} - nodes = init_hosts(reporter, sshhosts, pkgdir, - rsync_roots, remotepython, remote_options=remote_options.d, - optimise_localhost=self.optimise_localhost, done_dict=done_dict) - reporter(report.RsyncFinished()) - + hostopts = HostOptions(rsync_roots=rsync_roots, + remote_python=remotepython, + optimise_localhost=self.optimise_localhost) + hostmanager = HostManager(sshhosts, self.config, pkgdir, hostopts) try: - self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) - finally: - teardown_hosts(reporter, [node.channel for node in nodes], nodes, + nodes = hostmanager.init_hosts(reporter, done_dict) + reporter(report.RsyncFinished()) + try: + self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) + except (KeyboardInterrupt, SystemExit): + print >>sys.stderr, "C-c pressed waiting for gateways to teardown..." + channels = [node.channel for node in nodes] + hostmanager.kill_channels(channels) + hostmanager.teardown_gateways(reporter, channels) + print >>sys.stderr, "... Done" + raise + + channels = [node.channel for node in nodes] + hostmanager.teardown_hosts(reporter, channels, nodes, exitfirst=self.config.option.exitfirst) - reporter(report.Nodes(nodes)) - retval = reporter(report.TestFinished()) - self.kill_server(startserverflag) - return retval + reporter(report.Nodes(nodes)) + retval = reporter(report.TestFinished()) + self.kill_server(startserverflag) + return retval + except (KeyboardInterrupt, SystemExit): + reporter(report.InterruptedExecution()) + self.kill_server(startserverflag) + raise + except: + reporter(report.CrashedExecution()) + self.kill_server(startserverflag) + raise def read_distributed_config(self): + """ Read from conftest file the configuration of distributed testing + """ try: rsync_roots = self.config.getvalue("distrsync_roots") except: @@ -221,10 +194,7 @@ sshhosts = [HostInfo(i) for i in self.config.getvalue("disthosts")] parse_directories(sshhosts) - try: - remotepython = self.config.getvalue("dist_remotepython") - except: - remotepython = None + remotepython = self.config.getvalue("dist_remotepython") return sshhosts, remotepython, rsync_roots def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): @@ -247,14 +217,13 @@ """ def main(self, reporter=None, runner=None): # check out if used options makes any sense - args = self.config.remaining + args = self.config.args sshhosts = [HostInfo('localhost')] # this is just an info to reporter if not self.config.option.nomagic: py.magic.invoke(assertion=1) - session_options.bind_config(self.config) reporter, startserverflag = self.init_reporter(reporter, sshhosts, LocalReporter, args[0]) reporter, checkfun = self.wrap_reporter(reporter) @@ -321,4 +290,8 @@ self.tracer = Tracer(self.docstorage) return apigen_runner else: - return RunnerPolicy[session_options.runner_policy] + if (self.config.getvalue('dist_boxing') or self.config.option.boxing)\ + and not self.config.option.nocapture: + return box_runner + return plain_runner + From hpk at codespeak.net Wed Jan 24 17:40:03 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:40:03 +0100 (CET) Subject: [py-svn] r37274 - py/trunk/py/test/rsession Message-ID: <20070124164003.3BF7610093@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:39:56 2007 New Revision: 37274 Added: py/trunk/py/test/rsession/web.py.merge.tmp - copied, changed from r37267, py/trunk/py/test/rsession/web.py Log: merging of file:///svn/py/branch/config/py/test/rsession/web.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37065 | arigo | 2007-01-20 17:15:10 +0100 (Sat, 20 Jan 2007) | 3 lines Choose a random free port when using the --runbrowser option, but stick to the fixed 8000 when using --startserver only. ------------------------------------------------------------------------ r36993 | fijal | 2007-01-19 14:54:25 +0100 (Fri, 19 Jan 2007) | 2 lines Add possibility to scroll or not to scroll to web frontend. This checkin makes me believe that there is lot of stuff to do in JS backend. ------------------------------------------------------------------------ r36955 | hpk | 2007-01-18 17:44:21 +0100 (Thu, 18 Jan 2007) | 4 lines some mods to defaultconfest dist options and use "_dist_import_pypy", i.e. a leading underscore at least ------------------------------------------------------------------------ r36943 | fijal | 2007-01-18 16:09:30 +0100 (Thu, 18 Jan 2007) | 3 lines at least LSession should work now. Did not touched yet the remote options stuff, but some simplifications were done. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/test/rsession/web.py.merge.tmp (from r37267, py/trunk/py/test/rsession/web.py) ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py.merge.tmp Wed Jan 24 17:39:56 2007 @@ -14,18 +14,18 @@ import socket import py -from py.__.test.rsession.rsession import RSession, session_options +from py.__.test.rsession.rsession import RSession from py.__.test.rsession import report from py.__.test import collect from py.__.test.rsession.webdata import json DATADIR = py.path.local(__file__).dirpath("webdata") FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", - "show_host", "hide_host", "hide_messagebox"] + "show_host", "hide_host", "hide_messagebox", "opt_scroll"] try: try: - if not session_options.import_pypy: + if not py.test.config.getvalue('_dist_import_pypy'): raise ImportError except AttributeError: pass @@ -416,10 +416,10 @@ if start_new: thread.start_new_thread(httpd.serve_forever, ()) - print "Server started, listening on %s" % (server_address,) + print "Server started, listening on port %d" % (httpd.server_port,) return httpd else: - print "Server started, listening on %s" % (server_address,) + print "Server started, listening on port %d" % (httpd.server_port,) httpd.serve_forever() def kill_server(): From hpk at codespeak.net Wed Jan 24 17:40:03 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:40:03 +0100 (CET) Subject: [py-svn] r37275 - py/trunk/py/apigen/testing Message-ID: <20070124164003.8242110093@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:40:01 2007 New Revision: 37275 Added: py/trunk/py/apigen/testing/test_apigen_functional.py.merge.tmp - copied, changed from r37267, py/trunk/py/apigen/testing/test_apigen_functional.py Log: merging of file:///svn/py/branch/config/py/apigen/testing/test_apigen_functional.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37020 | hpk | 2007-01-19 17:48:15 +0100 (Fri, 19 Jan 2007) | 3 lines switching in the new way of getting to a sessionclass, removing code in cmdline.py and elsewhere accordingly ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/apigen/testing/test_apigen_functional.py.merge.tmp (from r37267, py/trunk/py/apigen/testing/test_apigen_functional.py) ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py.merge.tmp Wed Jan 24 17:40:01 2007 @@ -76,7 +76,7 @@ parentdir = py.magic.autopath().dirpath().dirpath() pkgdir = fs_root.join('pkg') try: - output = py.process.cmdexec('APIGEN_TARGET="%s" py.test --session=L ' + output = py.process.cmdexec('APIGEN_TARGET="%s" py.test ' '--apigen="%s/apigen.py" "%s"' % ( tempdir, parentdir, pkgdir)) except py.error.Error, e: From hpk at codespeak.net Wed Jan 24 17:40:08 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:40:08 +0100 (CET) Subject: [py-svn] r37276 - py/trunk/py/test/rsession/testing Message-ID: <20070124164008.3B93F10098@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:40:04 2007 New Revision: 37276 Added: py/trunk/py/test/rsession/testing/test_rsession.py.merge.tmp - copied, changed from r37267, py/trunk/py/test/rsession/testing/test_rsession.py Log: merging of file:///svn/py/branch/config/py/test/rsession/testing/test_rsession.py revisions 36936 to 37267: ------------------------------------------------------------------------ r37262 | hpk | 2007-01-24 14:55:00 +0100 (Wed, 24 Jan 2007) | 2 lines fix failing test ------------------------------------------------------------------------ r37015 | fijal | 2007-01-19 17:24:32 +0100 (Fri, 19 Jan 2007) | 2 lines Major cleanup of hostmanage. Got rid of global functions in favor of some better-structured classes. ------------------------------------------------------------------------ r36974 | fijal | 2007-01-18 22:32:04 +0100 (Thu, 18 Jan 2007) | 2 lines Fix the test to actually perform RSync of pylib itself and a test if that was true. ------------------------------------------------------------------------ r36970 | hpk | 2007-01-18 20:33:16 +0100 (Thu, 18 Jan 2007) | 6 lines some little cleanups - getting rid of we_are_remote which is not used anywhere as far as i can see. maciej: please check that i didn't mess anything up, but for me the same tests as before pass ------------------------------------------------------------------------ r36968 | hpk | 2007-01-18 20:00:21 +0100 (Thu, 18 Jan 2007) | 2 lines fixing some imports ------------------------------------------------------------------------ r36966 | fijal | 2007-01-18 19:36:02 +0100 (Thu, 18 Jan 2007) | 2 lines Add try: finally: ------------------------------------------------------------------------ r36965 | fijal | 2007-01-18 19:35:00 +0100 (Thu, 18 Jan 2007) | 2 lines Fix the test. ------------------------------------------------------------------------ r36963 | fijal | 2007-01-18 19:21:43 +0100 (Thu, 18 Jan 2007) | 2 lines Fix remaining tests and make strange hack with parse/reparse. ------------------------------------------------------------------------ r36962 | fijal | 2007-01-18 19:06:13 +0100 (Thu, 18 Jan 2007) | 3 lines Huge refactoring. Got rid of remote_options and session_options, two tests still fail, so it's intermediate checkin. ------------------------------------------------------------------------ r36943 | fijal | 2007-01-18 16:09:30 +0100 (Thu, 18 Jan 2007) | 3 lines at least LSession should work now. Did not touched yet the remote options stuff, but some simplifications were done. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/test/rsession/testing/test_rsession.py.merge.tmp (from r37267, py/trunk/py/test/rsession/testing/test_rsession.py) ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py.merge.tmp Wed Jan 24 17:40:04 2007 @@ -5,12 +5,12 @@ import py from py.__.test.rsession import report from py.__.test.rsession.rsession import RSession, parse_directories,\ - session_options, remote_options, parse_directories -from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\ + parse_directories +from py.__.test.rsession.hostmanage import HostOptions, HostManager,\ HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ - funcoptioncustom_spec, funcoption_spec + funcoptioncustom_spec def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() @@ -18,7 +18,8 @@ def test_setup_non_existing_hosts(): setup_events = [] hosts = [HostInfo("alskdjalsdkjasldkajlsd")] - cmd = "init_hosts(setup_events.append, hosts, pkgdir)" + hm = HostManager(hosts, None, pkgdir) + cmd = "hm.init_hosts(setup_events.append)" py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) #assert setup_events @@ -105,7 +106,7 @@ tmpdir = py.test.ensuretemp("example_distribution") tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" disthosts = [%r] - distrsync_roots = ["%s"] + distrsync_roots = ["%s", "py"] """ % ('localhost', subdir))) tmpdir.ensure(subdir, "__init__.py") tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" @@ -119,7 +120,11 @@ pass def test_5(): assert __file__ != '%s' - """ % str(tmpdir.join(subdir)))) + def test_6(): + import py + assert py.__file__ != '%s' + """ % (str(tmpdir.join(subdir)), py.__file__))) + tmpdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) args = [str(tmpdir.join(subdir))] config = py.test.config._reparse(args) rsession = RSession(config, optimise_localhost=False) @@ -131,21 +136,21 @@ passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] skippedevents = [i for i in testevents if i.outcome.skipped] - assert len(testevents) == 5 - assert len(passevents) == 2 + assert len(testevents) == 6 + assert len(passevents) == 3 assert len(failevents) == 3 tb = failevents[0].outcome.excinfo.traceback - assert tb[0].path.find("test_one") != -1 + assert str(tb[0].path).find("test_one") != -1 assert tb[0].source.find("test_2") != -1 assert failevents[0].outcome.excinfo.typename == 'AssertionError' tb = failevents[1].outcome.excinfo.traceback - assert tb[0].path.find("test_one") != -1 + assert str(tb[0].path).find("test_one") != -1 assert tb[0].source.find("test_3") != -1 assert failevents[1].outcome.excinfo.typename == 'ValueError' assert failevents[1].outcome.excinfo.value == '23' tb = failevents[2].outcome.excinfo.traceback assert failevents[2].outcome.excinfo.typename == 'TypeError' - assert tb[0].path.find("executor") != -1 + assert str(tb[0].path).find("executor") != -1 assert tb[0].source.find("execute") != -1 def test_setup_teardown_ssh(self): @@ -155,10 +160,10 @@ teardown_events = [] config = py.test.config._reparse([]) - session_options.bind_config(config) - nodes = init_hosts(setup_events.append, hosts, pkgdir, - rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) - teardown_hosts(teardown_events.append, + opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(setup_events.append) + hm.teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) count_rsyn_calls = [i for i in setup_events @@ -182,9 +187,9 @@ allevents = [] config = py.test.config._reparse([]) - session_options.bind_config(config) - nodes = init_hosts(allevents.append, hosts, pkgdir, - rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) + opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(allevents.append) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping @@ -202,7 +207,7 @@ node.send(itemskip) node.send(itemprint) - teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) + hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) events = [i for i in allevents if isinstance(i, report.ReceivedItemOutcome)] @@ -224,31 +229,33 @@ hosts = [HostInfo('localhost')] parse_directories(hosts) config = py.test.config._reparse([]) - session_options.bind_config(config) - d = remote_options.d.copy() - d['custom'] = 'custom' - nodes = init_hosts(allevents.append, hosts, pkgdir, - rsync_roots=["py"], remote_options=d, - optimise_localhost=False) - - rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol.getitembynames(funcoption_spec) - itempassaswell = rootcol.getitembynames(funcoptioncustom_spec) - - for node in nodes: - node.send(itempass) - node.send(itempassaswell) - - teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) - events = [i for i in allevents - if isinstance(i, report.ReceivedItemOutcome)] - passed = [i for i in events - if i.outcome.passed] - skipped = [i for i in events - if i.outcome.skipped] - assert len(passed) == 2 * len(nodes) - assert len(skipped) == 0 - assert len(events) == len(passed) + config._overwrite('custom', 'custom') + # we need to overwrite default list to serialize + from py.__.test.rsession.master import defaultconftestnames + defaultconftestnames.append("custom") + try: + opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(allevents.append) + + rootcol = py.test.collect.Directory(pkgdir.dirpath()) + itempass = rootcol.getitembynames(funcoptioncustom_spec) + + for node in nodes: + node.send(itempass) + + hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) + events = [i for i in allevents + if isinstance(i, report.ReceivedItemOutcome)] + passed = [i for i in events + if i.outcome.passed] + skipped = [i for i in events + if i.outcome.skipped] + assert len(passed) == 1 * len(nodes) + assert len(skipped) == 0 + assert len(events) == len(passed) + finally: + defaultconftestnames.remove("custom") def test_nice_level(self): """ Tests if nice level behaviour is ok @@ -258,14 +265,16 @@ parse_directories(hosts) tmpdir = py.test.ensuretemp("nice") tmpdir.ensure("__init__.py") - tmpdir.ensure("conftest.py").write("""disthosts = ['localhost']""") + tmpdir.ensure("conftest.py").write(py.code.Source(""" + disthosts = ['localhost'] + dist_nicelevel = 10 + """)) tmpdir.ensure("test_one.py").write("""def test_nice(): import os assert os.nice(0) == 10 """) config = py.test.config._reparse([tmpdir]) - config.option.nice_level = 10 rsession = RSession(config) allevents = [] rsession.main(reporter=allevents.append) @@ -298,7 +307,10 @@ hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] hosts = [HostInfo(i) for i in hostnames] parse_directories(hosts) - init_hosts(testevents.append, hosts, pkgdir, do_sync=False) + config = py.test.config._reparse([]) + opts = HostOptions(do_sync=False, create_gateways=False) + hm = HostManager(hosts, config, pkgdir, opts) + nodes = hm.init_hosts(testevents.append) events = [i for i in testevents if isinstance(i, report.HostRSyncing)] assert len(events) == 4 assert events[0].host.hostname == 'h1' From hpk at codespeak.net Wed Jan 24 17:40:09 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:40:09 +0100 (CET) Subject: [py-svn] r37277 - py/trunk/py/test/rsession/testing Message-ID: <20070124164009.2D2F41008A@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:40:06 2007 New Revision: 37277 Added: py/trunk/py/test/rsession/testing/test_webjs.py.merge.tmp - copied, changed from r37267, py/trunk/py/test/rsession/testing/test_webjs.py Log: merging of file:///svn/py/branch/config/py/test/rsession/testing/test_webjs.py revisions 36936 to 37267: ------------------------------------------------------------------------ r36986 | fijal | 2007-01-19 13:05:58 +0100 (Fri, 19 Jan 2007) | 4 lines Improve the behaviour off cleanup and channel closing, at least on slave side. Some cleanups, got rid of some hacks (in favor of other hacks, but posix is a bit hackish anyway) ------------------------------------------------------------------------ r36943 | fijal | 2007-01-18 16:09:30 +0100 (Thu, 18 Jan 2007) | 3 lines at least LSession should work now. Did not touched yet the remote options stuff, but some simplifications were done. ------------------------------------------------------------------------ r36937 | fijal | 2007-01-18 14:22:54 +0100 (Thu, 18 Jan 2007) | 2 lines Create a branch for further config cleanups. ------------------------------------------------------------------------ Copied: py/trunk/py/test/rsession/testing/test_webjs.py.merge.tmp (from r37267, py/trunk/py/test/rsession/testing/test_webjs.py) ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py.merge.tmp Wed Jan 24 17:40:06 2007 @@ -23,9 +23,7 @@ dom.window = dom.Window(html) dom.document = dom.window.document config = py.test.config._reparse([]) - from py.__.test.rsession.rsession import session_options - session_options.bind_config(config) - session_options.import_pypy = True + config._overwrite('_dist_import_pypy', True) from py.__.test.rsession import webjs from py.__.test.rsession.web import exported_methods mod.webjs = webjs From hpk at codespeak.net Wed Jan 24 17:46:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 17:46:48 +0100 (CET) Subject: [py-svn] r37278 - in py/trunk/py: . apigen/testing code doc doc/example execnet misc misc/testing path/local test test/rsession test/rsession/testing test/rsession/webdata test/terminal test/testing test/tkinter Message-ID: <20070124164648.8ECBD10078@code0.codespeak.net> Author: hpk Date: Wed Jan 24 17:46:46 2007 New Revision: 37278 Added: py/trunk/py/__init__.py - copied unchanged from r37277, py/trunk/py/__init__.py.merge.tmp py/trunk/py/apigen/testing/test_apigen_functional.py - copied unchanged from r37277, py/trunk/py/apigen/testing/test_apigen_functional.py.merge.tmp py/trunk/py/code/traceback2.py - copied unchanged from r37277, py/branch/config/py/code/traceback2.py py/trunk/py/doc/TODO.txt - copied unchanged from r37277, py/trunk/py/doc/TODO.txt.merge.tmp py/trunk/py/doc/example/ - copied from r37277, py/branch/config/py/doc/example/ py/trunk/py/doc/test.txt - copied unchanged from r37277, py/branch/config/py/doc/test.txt py/trunk/py/doc/test_conftest.py - copied unchanged from r37277, py/trunk/py/doc/test_conftest.py.merge.tmp py/trunk/py/execnet/ - copied from r37277, py/branch/config/py/execnet/ py/trunk/py/misc/terminal_helper.py - copied unchanged from r37277, py/branch/config/py/misc/terminal_helper.py py/trunk/py/misc/testing/test_terminal.py - copied unchanged from r37277, py/branch/config/py/misc/testing/test_terminal.py py/trunk/py/path/local/ - copied from r37277, py/branch/config/py/path/local/ py/trunk/py/test/cmdline.py - copied unchanged from r37277, py/branch/config/py/test/cmdline.py py/trunk/py/test/collect.py - copied unchanged from r37277, py/branch/config/py/test/collect.py py/trunk/py/test/config.py - copied unchanged from r37277, py/trunk/py/test/config.py.merge.tmp py/trunk/py/test/conftesthandle.py - copied unchanged from r37277, py/branch/config/py/test/conftesthandle.py py/trunk/py/test/defaultconftest.py - copied unchanged from r37277, py/branch/config/py/test/defaultconftest.py py/trunk/py/test/item.py - copied unchanged from r37277, py/branch/config/py/test/item.py py/trunk/py/test/representation.py - copied unchanged from r37277, py/branch/config/py/test/representation.py py/trunk/py/test/rsession/box.py - copied unchanged from r37277, py/branch/config/py/test/rsession/box.py py/trunk/py/test/rsession/executor.py - copied unchanged from r37277, py/branch/config/py/test/rsession/executor.py py/trunk/py/test/rsession/hostmanage.py - copied unchanged from r37277, py/branch/config/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/local.py - copied unchanged from r37277, py/branch/config/py/test/rsession/local.py py/trunk/py/test/rsession/master.py - copied unchanged from r37277, py/branch/config/py/test/rsession/master.py py/trunk/py/test/rsession/outcome.py - copied unchanged from r37277, py/branch/config/py/test/rsession/outcome.py py/trunk/py/test/rsession/report.py - copied unchanged from r37277, py/branch/config/py/test/rsession/report.py py/trunk/py/test/rsession/reporter.py - copied unchanged from r37277, py/branch/config/py/test/rsession/reporter.py py/trunk/py/test/rsession/rest.py - copied unchanged from r37277, py/trunk/py/test/rsession/rest.py.merge.tmp py/trunk/py/test/rsession/rsession.py - copied unchanged from r37277, py/trunk/py/test/rsession/rsession.py.merge.tmp py/trunk/py/test/rsession/slave.py - copied unchanged from r37277, py/branch/config/py/test/rsession/slave.py py/trunk/py/test/rsession/testing/test_boxing.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_boxing.py py/trunk/py/test/rsession/testing/test_executor.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_executor.py py/trunk/py/test/rsession/testing/test_lsession.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_lsession.py py/trunk/py/test/rsession/testing/test_master.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_outcome.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_outcome.py py/trunk/py/test/rsession/testing/test_reporter.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rest.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_rest.py py/trunk/py/test/rsession/testing/test_rsession.py - copied unchanged from r37277, py/trunk/py/test/rsession/testing/test_rsession.py.merge.tmp py/trunk/py/test/rsession/testing/test_slave.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_slave.py py/trunk/py/test/rsession/testing/test_web.py - copied unchanged from r37277, py/branch/config/py/test/rsession/testing/test_web.py py/trunk/py/test/rsession/testing/test_webjs.py - copied unchanged from r37277, py/trunk/py/test/rsession/testing/test_webjs.py.merge.tmp py/trunk/py/test/rsession/web.py - copied unchanged from r37277, py/trunk/py/test/rsession/web.py.merge.tmp py/trunk/py/test/rsession/webdata/ - copied from r37277, py/branch/config/py/test/rsession/webdata/ py/trunk/py/test/rsession/webjs.py - copied unchanged from r37277, py/branch/config/py/test/rsession/webjs.py py/trunk/py/test/session.py - copied unchanged from r37277, py/branch/config/py/test/session.py py/trunk/py/test/terminal/ - copied from r37277, py/branch/config/py/test/terminal/ py/trunk/py/test/testing/ - copied from r37277, py/branch/config/py/test/testing/ py/trunk/py/test/tkinter/ - copied from r37277, py/branch/config/py/test/tkinter/ Removed: py/trunk/py/__init__.py.merge.tmp py/trunk/py/apigen/testing/test_apigen_functional.py.merge.tmp py/trunk/py/doc/TODO.txt.merge.tmp py/trunk/py/doc/test_conftest.py.merge.tmp py/trunk/py/test/config.py.merge.tmp py/trunk/py/test/rsession/conftest.py py/trunk/py/test/rsession/rest.py.merge.tmp py/trunk/py/test/rsession/rsession.py.merge.tmp py/trunk/py/test/rsession/rsync.py py/trunk/py/test/rsession/rsync_remote.py py/trunk/py/test/rsession/testing/test_config.py py/trunk/py/test/rsession/testing/test_rsession.py.merge.tmp py/trunk/py/test/rsession/testing/test_rsync.py py/trunk/py/test/rsession/testing/test_webjs.py.merge.tmp py/trunk/py/test/rsession/web.py.merge.tmp Log: move files from branch to trunk (and thus complete the merge of the config branch into the trunk) Deleted: /py/trunk/py/__init__.py.merge.tmp ============================================================================== --- /py/trunk/py/__init__.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,132 +0,0 @@ -"""\ -the py lib is a development support library featuring -py.test, an interactive testing tool which supports -unit-testing with practically no boilerplate. -""" -from initpkg import initpkg - -version = "0.8.80-alpha2" - -initpkg(__name__, - description = "py.test and the py lib", - revision = int('$LastChangedRevision$'.split(':')[1][:-1]), - lastchangedate = '$LastChangedDate$', - version = version, - url = "http://codespeak.net/py", - download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,), - license = "MIT license", - platforms = ['unix', 'linux', 'cygwin'], - author = "holger krekel & others", - author_email = "py-dev at codespeak.net", - long_description = globals()['__doc__'], - - exportdefs = { - '_dist.setup' : ('./misc/_dist.py', 'setup'), - - # helpers for use from test functions or collectors - '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.compat.TestCase' : ('./test/compat.py', 'TestCase'), - - # configuration/initialization related test api - 'test.config' : ('./test/config.py', 'config_per_process'), - 'test.ensuretemp' : ('./test/config.py', 'ensuretemp'), - 'test.cmdline.main' : ('./test/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.Item' : ('./test/item.py', 'Item'), - 'test.Function' : ('./test/item.py', 'Function'), - - # thread related API (still in early design phase) - '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), - '_thread.NamedThreadPool' : ('./thread/pool.py', 'NamedThreadPool'), - '_thread.ThreadOut' : ('./thread/io.py', 'ThreadOut'), - - # hook into the top-level standard library - 'std' : ('./misc/std.py', 'std'), - - 'process.cmdexec' : ('./process/cmdexec.py', 'cmdexec'), - - # path implementations - 'path.svnwc' : ('./path/svn/wccommand.py', 'SvnWCCommandPath'), - 'path.svnurl' : ('./path/svn/urlcommand.py', 'SvnCommandPath'), - 'path.local' : ('./path/local/local.py', 'LocalPath'), - 'path.extpy' : ('./path/extpy/extpy.py', 'Extpy'), - - # some nice slightly magic APIs - 'magic.greenlet' : ('./magic/greenlet.py', 'greenlet'), - 'magic.invoke' : ('./magic/invoke.py', 'invoke'), - 'magic.revoke' : ('./magic/invoke.py', 'revoke'), - 'magic.patch' : ('./magic/patch.py', 'patch'), - 'magic.revert' : ('./magic/patch.py', 'revert'), - 'magic.autopath' : ('./magic/autopath.py', 'autopath'), - 'magic.AssertionError' : ('./magic/assertion.py', 'AssertionError'), - - # python inspection/code-generation API - 'code.compile' : ('./code/source.py', 'compile_'), - 'code.Source' : ('./code/source.py', 'Source'), - 'code.Code' : ('./code/code.py', 'Code'), - 'code.Frame' : ('./code/frame.py', 'Frame'), - 'code.ExceptionInfo' : ('./code/excinfo.py', 'ExceptionInfo'), - 'code.Traceback' : ('./code/traceback2.py', 'Traceback'), - - # backports and additions of builtins - 'builtin.enumerate' : ('./builtin/enumerate.py', 'enumerate'), - 'builtin.reversed' : ('./builtin/reversed.py', 'reversed'), - 'builtin.sorted' : ('./builtin/sorted.py', 'sorted'), - 'builtin.BaseException' : ('./builtin/exception.py', 'BaseException'), - 'builtin.set' : ('./builtin/set.py', 'set'), - 'builtin.frozenset' : ('./builtin/set.py', 'frozenset'), - - # gateways into remote contexts - 'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'), - 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), - 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), - - # execnet scripts - 'execnet.RSync' : ('./execnet/rsync.py', 'RSync'), - - # input-output helping - 'io.dupfile' : ('./io/dupfile.py', 'dupfile'), - 'io.FDCapture' : ('./io/capture.py', 'FDCapture'), - 'io.OutErrCapture' : ('./io/capture.py', 'OutErrCapture'), - 'io.callcapture' : ('./io/capture.py', 'callcapture'), - - # error module, defining all errno's as Classes - 'error' : ('./misc/error.py', 'error'), - - # small and mean xml/html generation - 'xml.html' : ('./xmlobj/html.py', 'html'), - 'xml.Tag' : ('./xmlobj/xml.py', 'Tag'), - 'xml.raw' : ('./xmlobj/xml.py', 'raw'), - 'xml.Namespace' : ('./xmlobj/xml.py', 'Namespace'), - 'xml.escape' : ('./xmlobj/misc.py', 'escape'), - - # logging API ('producers' and 'consumers' connected via keywords) - 'log.Producer' : ('./log/producer.py', 'Producer'), - 'log.default' : ('./log/producer.py', 'default'), - 'log._getstate' : ('./log/producer.py', '_getstate'), - 'log._setstate' : ('./log/producer.py', '_setstate'), - 'log.setconsumer' : ('./log/consumer.py', 'setconsumer'), - 'log.Path' : ('./log/consumer.py', 'Path'), - 'log.STDOUT' : ('./log/consumer.py', 'STDOUT'), - 'log.STDERR' : ('./log/consumer.py', 'STDERR'), - 'log.Syslog' : ('./log/consumer.py', 'Syslog'), - 'log.get' : ('./log/logger.py', 'get'), - - # compatibility modules (taken from 2.4.4) - 'compat.doctest' : ('./compat/doctest.py', '*'), - 'compat.optparse' : ('./compat/optparse.py', '*'), - 'compat.textwrap' : ('./compat/textwrap.py', '*'), - 'compat.subprocess' : ('./compat/subprocess.py', '*'), -}) Deleted: /py/trunk/py/apigen/testing/test_apigen_functional.py.merge.tmp ============================================================================== --- /py/trunk/py/apigen/testing/test_apigen_functional.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,116 +0,0 @@ -""" functional test for apigen.py - - script to build api + source docs from py.test -""" - -import py - -def setup_fs_project(): - temp = py.test.ensuretemp('apigen_functional') - temp.ensure("pkg/func.py").write(py.code.Source("""\ - def func(arg1): - "docstring" - - def func_2(arg1, arg2): - return arg1(arg2) - """)) - temp.ensure('pkg/sometestclass.py').write(py.code.Source("""\ - class SomeTestClass(object): - " docstring sometestclass " - def __init__(self, somevar): - self.somevar = somevar - - def get_somevar(self): - " get_somevar docstring " - return self.somevar - """)) - temp.ensure('pkg/sometestsubclass.py').write(py.code.Source("""\ - from sometestclass import SomeTestClass - class SomeTestSubClass(SomeTestClass): - " docstring sometestsubclass " - def get_somevar(self): - return self.somevar + 1 - """)) - temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ - def foo(): - return 'bar' - def baz(qux): - return qux - """)) - temp.ensure("pkg/__init__.py").write(py.code.Source("""\ - from py.initpkg import initpkg - initpkg(__name__, exportdefs = { - 'main.sub.func': ("./func.py", "func"), - 'main.func': ("./func.py", "func_2"), - 'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'), - 'main.SomeTestSubClass': ('./sometestsubclass.py', - 'SomeTestSubClass'), - }) - """)) - temp.ensure('pkg/test/test_pkg.py').write(py.code.Source("""\ - import py - py.std.sys.path.insert(0, - py.magic.autopath().dirpath().dirpath().dirpath().strpath) - import pkg - - # this mainly exists to provide some data to the tracer - def test_pkg(): - s = pkg.main.SomeTestClass(10) - assert s.get_somevar() == 10 - s = pkg.main.SomeTestClass('10') - assert s.get_somevar() == '10' - s = pkg.main.SomeTestSubClass(10) - assert s.get_somevar() == 11 - s = pkg.main.SomeTestSubClass('10') - py.test.raises(TypeError, 's.get_somevar()') - assert pkg.main.sub.func(10) is None - assert pkg.main.sub.func(20) is None - s = pkg.main.func(pkg.main.SomeTestClass, 10) - assert isinstance(s, pkg.main.SomeTestClass) - """)) - return temp, 'pkg' - -def test_apigen_functional(): - fs_root, package_name = setup_fs_project() - tempdir = py.test.ensuretemp('test_apigen_functional_results') - parentdir = py.magic.autopath().dirpath().dirpath() - pkgdir = fs_root.join('pkg') - try: - output = py.process.cmdexec('APIGEN_TARGET="%s" py.test ' - '--apigen="%s/apigen.py" "%s"' % ( - tempdir, parentdir, pkgdir)) - except py.error.Error, e: - print e.out - raise - assert output.lower().find('traceback') == -1 - - # just some quick content checks - apidir = tempdir.join('api') - assert apidir.check(dir=True) - sometestclass_api = apidir.join('main.SomeTestClass.html') - assert sometestclass_api.check(file=True) - html = sometestclass_api.read() - assert 'SomeTestClass' in html - # XXX not linking to method files anymore - #sometestclass_init_api = apidir.join('main.SomeTestClass.__init__.html') - #assert sometestclass_init_api.check(file=True) - #assert sometestclass_init_api.read().find( - # '__init__') > -1 - namespace_api = apidir.join('main.html') - assert namespace_api.check(file=True) - html = namespace_api.read() - assert 'SomeTestClass' in html - - sourcedir = tempdir.join('source') - assert sourcedir.check(dir=True) - sometestclass_source = sourcedir.join('sometestclass.py.html') - assert sometestclass_source.check(file=True) - html = sometestclass_source.read() - assert '
sources for sometestclass.py
' in html - - # XXX later... - #index = sourcedir.join('index.html') - #assert index.check(file=True) - #html = index.read() - #assert 'main' in html - Deleted: /py/trunk/py/doc/TODO.txt.merge.tmp ============================================================================== --- /py/trunk/py/doc/TODO.txt.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,561 +0,0 @@ -Things to do before 0.9.0 -========================= - - - -py/bin ------------ - -* review py/bin scripts abit - py.test - py.rest - py.lookup - py.cleanup - py.countloc - -review all py lib documentation -------------------------------------- - -* (hpk, in-progress) rename py/documentation to py/doc - (check web page and pypy usage of it) - -streamline exported API -------------------------------------- - -* (hpk, should be done) move not-to-be-exported Gateway() methods to _ - methods. -* docstrings for all exported API - -* remove: - test.compat.TestCAse - -* check and likely remove already deprecated API - -* remove from public namespace: - XXX consider py.magic. invoke/revoke/patch/revert - -* make "_" namespace: - py.path.extpy -> py.path._extpy - py.log -> py._log (pypy!) - -* review py.io and write py.io.dupfile docstring (XXX guido) - -* re-consider what to do with read and write methods of py.path classes (since - there are places that check for file-ness by doing hasattr(... 'write')) - - (XXX guido talk to holger) - -packaging -------------------------------------- - -* debian and TAR/zip packages for py lib, - particularly look into C module issues (greenlet most importantly) - -* do something about c-extensions both on unix-ish - versus win32 systems - -* ensure compatibility with Python 2.3 - 2.5, - see what is missing for 2.2 - -* optional: support setuptools (eggs?) installs, and instally - from pypi (and register pylib there) - -* see if things work on Win32 (partially done) - -* refine and implement `releasescheme`_ - -.. _releasescheme: releasescheme.html - -APIGEN / source viewer -------------------------------------- - -* make py.test --apigen=PATH_TO_SCRIPT - collect tracing information and call the apigen - script to produce api and source code documentation - - (done) - -* deploy the above "py.test --apigen" run on codespeak - regularly, determine directory locations and URL namespace design. - -* integrate rest directive into py/documentation/conftest.py - with help code from py.__.rest.directive.... - make sure that the txt files in py/documentation/ use it - - (done XXX functions/methods) - -testing ------------ - -* windows tests (rev 36514 passes without errors, many skips) (XXX guido) - -* these should all work on 0.9 and on the py lib and pypy: - - running "py.test -s" - - running "py.test --pdb" - - running "py.test --looponfailing" - - running "py.test" distributed on some hosts - -* consider --box on trunk (XXX guido - find tests that depend on each other) - -* tests should not create any tempdirectories in the source code base - - - (done) - -* try to be as 2.2 compatible as possible - (use e.g. py.builtin.enumerate instead of "enumerate" directly) - - (done) - -distributed testing ----------------------- - - -code quality ------------------ - -* no function implementation longer than 30 lines - -* no lines longer than 80 characters - -* review the pylib issue tracker - (cfbolz: done: what has a 0.8.0 or a 0.9.0 tag should be looked at again) - - -py.test -------- - -* adjust py.test documentation to reflect new - collector/session architecture (mostly done) - -* document py.test's conftest.py approach (somewhat done) - -* py.test fails to parse strangely formatted code after assertion failure - -Missing docstrings ------------------- - -:: - - code.Traceback.recursionindex misses a docstring - code.Traceback.filter misses a docstring - code.Traceback.cut misses a docstring - code.Traceback.__getitem__ misses a docstring - code.Traceback.Entry misses a docstring - code.Traceback.Entry.ishidden misses a docstring - code.Traceback.Entry.getfirstlinesource misses a docstring - code.Traceback.Entry.__str__ misses a docstring - code.Traceback.Entry.__repr__ misses a docstring - code.Traceback.Entry.__init__ misses a docstring - code.Source.getblockend misses a docstring - code.Source.__str__ misses a docstring - code.Source.__len__ misses a docstring - code.Source.__init__ misses a docstring - code.Source.__getslice__ misses a docstring - code.Source.__getitem__ misses a docstring - code.Source.__eq__ misses a docstring - code.Frame.repr misses a docstring - code.Frame.is_true misses a docstring - code.Frame.getargs misses a docstring - code.Frame.exec_ misses a docstring - code.Frame.eval misses a docstring - code.Frame.__init__ misses a docstring - code.ExceptionInfo.exconly misses a docstring - code.ExceptionInfo.errisinstance misses a docstring - code.ExceptionInfo.__str__ misses a docstring - code.ExceptionInfo.__init__ misses a docstring - code.Code misses a docstring - code.Code.source misses a docstring - code.Code.getargs misses a docstring - code.Code.__init__ misses a docstring - code.Code.__eq__ misses a docstring - execnet.SshGateway misses a docstring - execnet.SshGateway.join misses a docstring - execnet.SshGateway.exit misses a docstring - execnet.SshGateway.__repr__ misses a docstring - execnet.SshGateway.__init__ misses a docstring - execnet.SshGateway.ThreadOut.write misses a docstring - execnet.SshGateway.ThreadOut.setwritefunc misses a docstring - execnet.SshGateway.ThreadOut.setdefaultwriter misses a docstring - execnet.SshGateway.ThreadOut.resetdefault misses a docstring - execnet.SshGateway.ThreadOut.isatty misses a docstring - execnet.SshGateway.ThreadOut.flush misses a docstring - execnet.SshGateway.ThreadOut.delwritefunc misses a docstring - execnet.SshGateway.ThreadOut.deinstall misses a docstring - execnet.SocketGateway misses a docstring - execnet.SocketGateway.join misses a docstring - execnet.SocketGateway.exit misses a docstring - execnet.SocketGateway.__repr__ misses a docstring - execnet.SocketGateway.__init__ misses a docstring - execnet.PopenGateway misses a docstring - execnet.PopenGateway.remote_bootstrap_gateway misses a docstring - execnet.PopenGateway.join misses a docstring - execnet.PopenGateway.exit misses a docstring - execnet.PopenGateway.__repr__ misses a docstring - execnet.PopenGateway.__init__ misses a docstring - initpkg misses a docstring - io.dupfile misses a docstring - log.setconsumer misses a docstring - log.get misses a docstring - log.Syslog misses a docstring - log.STDOUT misses a docstring - log.STDERR misses a docstring - log.Producer.set_consumer misses a docstring - log.Producer.get_consumer misses a docstring - log.Producer.__repr__ misses a docstring - log.Producer.__init__ misses a docstring - log.Producer.__getattr__ misses a docstring - log.Producer.__call__ misses a docstring - log.Producer.Message misses a docstring - log.Producer.Message.prefix misses a docstring - log.Producer.Message.content misses a docstring - log.Producer.Message.__str__ misses a docstring - log.Producer.Message.__init__ misses a docstring - log.Path misses a docstring - log.Path.__init__ misses a docstring - log.Path.__call__ misses a docstring - magic.View.__viewkey__ misses a docstring - magic.View.__repr__ misses a docstring - magic.View.__new__ misses a docstring - magic.View.__matchkey__ misses a docstring - magic.View.__getattr__ misses a docstring - magic.AssertionError misses a docstring - path.svnwc.visit misses a docstring - path.svnwc.mkdir misses a docstring - path.svnwc.dump misses a docstring - path.svnwc.check misses a docstring - path.svnwc.add misses a docstring - path.svnwc.__str__ misses a docstring - path.svnwc.__repr__ misses a docstring - path.svnwc.__new__ misses a docstring - path.svnwc.__iter__ misses a docstring - path.svnwc.__hash__ misses a docstring - path.svnwc.__eq__ misses a docstring - path.svnwc.__div__ misses a docstring - path.svnwc.__contains__ misses a docstring - path.svnwc.__cmp__ misses a docstring - path.svnwc.__add__ misses a docstring - path.svnwc.Checkers misses a docstring - path.svnurl.visit misses a docstring - path.svnurl.check misses a docstring - path.svnurl.__repr__ misses a docstring - path.svnurl.__new__ misses a docstring - path.svnurl.__ne__ misses a docstring - path.svnurl.__iter__ misses a docstring - path.svnurl.__hash__ misses a docstring - path.svnurl.__div__ misses a docstring - path.svnurl.__contains__ misses a docstring - path.svnurl.__cmp__ misses a docstring - path.svnurl.__add__ misses a docstring - path.svnurl.Checkers misses a docstring - path.local.visit misses a docstring - path.local.sysexec has an 'XXX' in its docstring - path.local.check misses a docstring - path.local.__repr__ misses a docstring - path.local.__iter__ misses a docstring - path.local.__hash__ misses a docstring - path.local.__eq__ misses a docstring - path.local.__div__ misses a docstring - path.local.__contains__ misses a docstring - path.local.__cmp__ misses a docstring - path.local.__add__ misses a docstring - path.local.Checkers misses a docstring - path.extpy.visit misses a docstring - path.extpy.samefile misses a docstring - path.extpy.relto misses a docstring - path.extpy.listobj misses a docstring - path.extpy.listdir misses a docstring - path.extpy.join misses a docstring - path.extpy.getpymodule misses a docstring - path.extpy.getfilelineno misses a docstring - path.extpy.dirpath misses a docstring - path.extpy.check misses a docstring - path.extpy.__str__ misses a docstring - path.extpy.__repr__ misses a docstring - path.extpy.__new__ misses a docstring - path.extpy.__iter__ misses a docstring - path.extpy.__hash__ misses a docstring - path.extpy.__contains__ misses a docstring - path.extpy.__cmp__ misses a docstring - path.extpy.__add__ misses a docstring - path.extpy.Checkers misses a docstring - test.rest.RestReporter misses a docstring - test.rest.RestReporter.summary misses a docstring - test.rest.RestReporter.skips misses a docstring - test.rest.RestReporter.repr_traceback misses a docstring - test.rest.RestReporter.repr_source misses a docstring - test.rest.RestReporter.repr_signal misses a docstring - test.rest.RestReporter.repr_failure misses a docstring - test.rest.RestReporter.report_unknown misses a docstring - test.rest.RestReporter.report_TestStarted misses a docstring - test.rest.RestReporter.report_TestFinished misses a docstring - test.rest.RestReporter.report_SkippedTryiter misses a docstring - test.rest.RestReporter.report_SendItem misses a docstring - test.rest.RestReporter.report_RsyncFinished misses a docstring - test.rest.RestReporter.report_ReceivedItemOutcome misses a docstring - test.rest.RestReporter.report_Nodes misses a docstring - test.rest.RestReporter.report_ItemStart misses a docstring - test.rest.RestReporter.report_ImmediateFailure misses a docstring - test.rest.RestReporter.report_HostReady misses a docstring - test.rest.RestReporter.report_HostRSyncing misses a docstring - test.rest.RestReporter.report_FailedTryiter misses a docstring - test.rest.RestReporter.report misses a docstring - test.rest.RestReporter.print_summary misses a docstring - test.rest.RestReporter.prepare_source misses a docstring - test.rest.RestReporter.hangs misses a docstring - test.rest.RestReporter.get_rootpath misses a docstring - test.rest.RestReporter.get_path_from_item misses a docstring - test.rest.RestReporter.get_linkwriter misses a docstring - test.rest.RestReporter.get_item_name misses a docstring - test.rest.RestReporter.get_host misses a docstring - test.rest.RestReporter.failures misses a docstring - test.rest.RestReporter.add_rest misses a docstring - test.rest.RestReporter.__init__ misses a docstring - test.rest.RelLinkWriter misses a docstring - test.rest.RelLinkWriter.get_link misses a docstring - test.rest.NoLinkWriter misses a docstring - test.rest.NoLinkWriter.get_link misses a docstring - test.rest.LinkWriter misses a docstring - test.rest.LinkWriter.get_link misses a docstring - test.rest.LinkWriter.__init__ misses a docstring - test.exit misses a docstring - test.deprecated_call misses a docstring - test.collect.Module misses a docstring - test.collect.Module.tryiter has an 'XXX' in its docstring - test.collect.Module.teardown misses a docstring - test.collect.Module.startcapture misses a docstring - test.collect.Module.skipbykeyword misses a docstring - test.collect.Module.setup misses a docstring - test.collect.Module.run misses a docstring - test.collect.Module.makeitem misses a docstring - test.collect.Module.listnames misses a docstring - test.collect.Module.join misses a docstring - test.collect.Module.haskeyword misses a docstring - test.collect.Module.getsortvalue misses a docstring - test.collect.Module.getpathlineno misses a docstring - test.collect.Module.getouterr misses a docstring - test.collect.Module.getitembynames misses a docstring - test.collect.Module.funcnamefilter misses a docstring - test.collect.Module.finishcapture misses a docstring - test.collect.Module.classnamefilter misses a docstring - test.collect.Module.buildname2items misses a docstring - test.collect.Module.__repr__ misses a docstring - test.collect.Module.__ne__ misses a docstring - test.collect.Module.__init__ misses a docstring - test.collect.Module.__hash__ misses a docstring - test.collect.Module.__eq__ misses a docstring - test.collect.Module.__cmp__ misses a docstring - test.collect.Module.Skipped misses a docstring - test.collect.Module.Passed misses a docstring - test.collect.Module.Outcome misses a docstring - test.collect.Module.Failed misses a docstring - test.collect.Module.ExceptionFailure misses a docstring - test.collect.Instance misses a docstring - test.collect.Instance.tryiter has an 'XXX' in its docstring - test.collect.Instance.teardown misses a docstring - test.collect.Instance.startcapture misses a docstring - test.collect.Instance.skipbykeyword misses a docstring - test.collect.Instance.setup misses a docstring - test.collect.Instance.run misses a docstring - test.collect.Instance.makeitem misses a docstring - test.collect.Instance.listnames misses a docstring - test.collect.Instance.join misses a docstring - test.collect.Instance.haskeyword misses a docstring - test.collect.Instance.getsortvalue misses a docstring - test.collect.Instance.getpathlineno misses a docstring - test.collect.Instance.getouterr misses a docstring - test.collect.Instance.getitembynames misses a docstring - test.collect.Instance.funcnamefilter misses a docstring - test.collect.Instance.finishcapture misses a docstring - test.collect.Instance.classnamefilter misses a docstring - test.collect.Instance.buildname2items misses a docstring - test.collect.Instance.__repr__ misses a docstring - test.collect.Instance.__ne__ misses a docstring - test.collect.Instance.__init__ misses a docstring - test.collect.Instance.__hash__ misses a docstring - test.collect.Instance.__eq__ misses a docstring - test.collect.Instance.__cmp__ misses a docstring - test.collect.Generator misses a docstring - test.collect.Generator.tryiter has an 'XXX' in its docstring - test.collect.Generator.teardown misses a docstring - test.collect.Generator.startcapture misses a docstring - test.collect.Generator.skipbykeyword misses a docstring - test.collect.Generator.setup misses a docstring - test.collect.Generator.run misses a docstring - test.collect.Generator.listnames misses a docstring - test.collect.Generator.join misses a docstring - test.collect.Generator.haskeyword misses a docstring - test.collect.Generator.getsortvalue misses a docstring - test.collect.Generator.getpathlineno misses a docstring - test.collect.Generator.getouterr misses a docstring - test.collect.Generator.getitembynames misses a docstring - test.collect.Generator.getcallargs misses a docstring - test.collect.Generator.finishcapture misses a docstring - test.collect.Generator.buildname2items misses a docstring - test.collect.Generator.__repr__ misses a docstring - test.collect.Generator.__ne__ misses a docstring - test.collect.Generator.__init__ misses a docstring - test.collect.Generator.__hash__ misses a docstring - test.collect.Generator.__eq__ misses a docstring - test.collect.Generator.__cmp__ misses a docstring - test.collect.DoctestFile misses a docstring - test.collect.DoctestFile.tryiter has an 'XXX' in its docstring - test.collect.DoctestFile.teardown misses a docstring - test.collect.DoctestFile.startcapture misses a docstring - test.collect.DoctestFile.skipbykeyword misses a docstring - test.collect.DoctestFile.setup misses a docstring - test.collect.DoctestFile.run misses a docstring - test.collect.DoctestFile.makeitem misses a docstring - test.collect.DoctestFile.listnames misses a docstring - test.collect.DoctestFile.join misses a docstring - test.collect.DoctestFile.haskeyword misses a docstring - test.collect.DoctestFile.getsortvalue misses a docstring - test.collect.DoctestFile.getpathlineno misses a docstring - test.collect.DoctestFile.getouterr misses a docstring - test.collect.DoctestFile.getitembynames misses a docstring - test.collect.DoctestFile.funcnamefilter misses a docstring - test.collect.DoctestFile.finishcapture misses a docstring - test.collect.DoctestFile.classnamefilter misses a docstring - test.collect.DoctestFile.buildname2items misses a docstring - test.collect.DoctestFile.__repr__ misses a docstring - test.collect.DoctestFile.__ne__ misses a docstring - test.collect.DoctestFile.__init__ misses a docstring - test.collect.DoctestFile.__hash__ misses a docstring - test.collect.DoctestFile.__eq__ misses a docstring - test.collect.DoctestFile.__cmp__ misses a docstring - test.collect.Directory misses a docstring - test.collect.Directory.tryiter has an 'XXX' in its docstring - test.collect.Directory.teardown misses a docstring - test.collect.Directory.startcapture misses a docstring - test.collect.Directory.skipbykeyword misses a docstring - test.collect.Directory.setup misses a docstring - test.collect.Directory.run misses a docstring - test.collect.Directory.recfilter misses a docstring - test.collect.Directory.makeitem misses a docstring - test.collect.Directory.listnames misses a docstring - test.collect.Directory.join misses a docstring - test.collect.Directory.haskeyword misses a docstring - test.collect.Directory.getsortvalue misses a docstring - test.collect.Directory.getpathlineno misses a docstring - test.collect.Directory.getouterr misses a docstring - test.collect.Directory.getitembynames misses a docstring - test.collect.Directory.finishcapture misses a docstring - test.collect.Directory.filefilter misses a docstring - test.collect.Directory.buildname2items misses a docstring - test.collect.Directory.__repr__ misses a docstring - test.collect.Directory.__ne__ misses a docstring - test.collect.Directory.__init__ misses a docstring - test.collect.Directory.__hash__ misses a docstring - test.collect.Directory.__eq__ misses a docstring - test.collect.Directory.__cmp__ misses a docstring - test.collect.Collector misses a docstring - test.collect.Collector.tryiter has an 'XXX' in its docstring - test.collect.Collector.teardown misses a docstring - test.collect.Collector.startcapture misses a docstring - test.collect.Collector.skipbykeyword misses a docstring - test.collect.Collector.setup misses a docstring - test.collect.Collector.run misses a docstring - test.collect.Collector.listnames misses a docstring - test.collect.Collector.join misses a docstring - test.collect.Collector.haskeyword misses a docstring - test.collect.Collector.getsortvalue misses a docstring - test.collect.Collector.getpathlineno misses a docstring - test.collect.Collector.getouterr misses a docstring - test.collect.Collector.getitembynames misses a docstring - test.collect.Collector.finishcapture misses a docstring - test.collect.Collector.buildname2items misses a docstring - test.collect.Collector.__repr__ misses a docstring - test.collect.Collector.__ne__ misses a docstring - test.collect.Collector.__init__ misses a docstring - test.collect.Collector.__hash__ misses a docstring - test.collect.Collector.__eq__ misses a docstring - test.collect.Collector.__cmp__ misses a docstring - test.collect.Class misses a docstring - test.collect.Class.tryiter has an 'XXX' in its docstring - test.collect.Class.teardown misses a docstring - test.collect.Class.startcapture misses a docstring - test.collect.Class.skipbykeyword misses a docstring - test.collect.Class.setup misses a docstring - test.collect.Class.run misses a docstring - test.collect.Class.makeitem misses a docstring - test.collect.Class.listnames misses a docstring - test.collect.Class.join misses a docstring - test.collect.Class.haskeyword misses a docstring - test.collect.Class.getsortvalue misses a docstring - test.collect.Class.getpathlineno misses a docstring - test.collect.Class.getouterr misses a docstring - test.collect.Class.getitembynames misses a docstring - test.collect.Class.funcnamefilter misses a docstring - test.collect.Class.finishcapture misses a docstring - test.collect.Class.classnamefilter misses a docstring - test.collect.Class.buildname2items misses a docstring - test.collect.Class.__repr__ misses a docstring - test.collect.Class.__ne__ misses a docstring - test.collect.Class.__init__ misses a docstring - test.collect.Class.__hash__ misses a docstring - test.collect.Class.__eq__ misses a docstring - test.collect.Class.__cmp__ misses a docstring - test.cmdline.main misses a docstring - test.Item misses a docstring - test.Item.tryiter has an 'XXX' in its docstring - test.Item.teardown misses a docstring - test.Item.startcapture misses a docstring - test.Item.skipbykeyword misses a docstring - test.Item.setup misses a docstring - test.Item.run misses a docstring - test.Item.listnames misses a docstring - test.Item.join misses a docstring - test.Item.haskeyword misses a docstring - test.Item.getsortvalue misses a docstring - test.Item.getpathlineno misses a docstring - test.Item.getouterr misses a docstring - test.Item.getitembynames misses a docstring - test.Item.finishcapture misses a docstring - test.Item.buildname2items misses a docstring - test.Item.__repr__ misses a docstring - test.Item.__ne__ misses a docstring - test.Item.__init__ misses a docstring - test.Item.__hash__ misses a docstring - test.Item.__eq__ misses a docstring - test.Item.__cmp__ misses a docstring - test.Function.tryiter has an 'XXX' in its docstring - test.Function.teardown misses a docstring - test.Function.startcapture misses a docstring - test.Function.skipbykeyword misses a docstring - test.Function.setup misses a docstring - test.Function.run misses a docstring - test.Function.listnames misses a docstring - test.Function.join misses a docstring - test.Function.haskeyword misses a docstring - test.Function.getsortvalue misses a docstring - test.Function.getpathlineno misses a docstring - test.Function.getouterr misses a docstring - test.Function.getitembynames misses a docstring - test.Function.finishcapture misses a docstring - test.Function.buildname2items misses a docstring - test.Function.__repr__ misses a docstring - test.Function.__ne__ misses a docstring - test.Function.__init__ misses a docstring - test.Function.__hash__ misses a docstring - test.Function.__eq__ misses a docstring - test.Function.__cmp__ misses a docstring - test.Config.__init__ misses a docstring - xml.raw.__init__ misses a docstring - xml.html misses a docstring - xml.html.__tagclass__ misses a docstring - xml.html.__tagclass__.unicode misses a docstring - xml.html.__tagclass__.__unicode__ misses a docstring - xml.html.__tagclass__.__repr__ misses a docstring - xml.html.__tagclass__.__init__ misses a docstring - xml.html.__tagclass__.Attr misses a docstring - xml.html.__tagclass__.Attr.__init__ misses a docstring - xml.html.__metaclass__ misses a docstring - xml.html.__metaclass__.__getattr__ misses a docstring - xml.html.Style misses a docstring - xml.html.Style.__init__ misses a docstring - xml.escape misses a docstring - xml.Tag misses a docstring - xml.Tag.unicode misses a docstring - xml.Tag.__unicode__ misses a docstring - xml.Tag.__repr__ misses a docstring - xml.Tag.__init__ misses a docstring - xml.Namespace misses a docstring Deleted: /py/trunk/py/doc/test_conftest.py.merge.tmp ============================================================================== --- /py/trunk/py/doc/test_conftest.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,73 +0,0 @@ - -import py - -def setup_module(mod): - mod.tmpdir = py.test.ensuretemp('docdoctest') - -def test_doctest_basic(): - # XXX get rid of the next line: - py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) - - xtxt = tmpdir.join('x.txt') - xtxt.write(py.code.Source(""" - .. - >>> from os.path import abspath - - hello world - - >>> assert abspath - >>> i=3 - >>> print i - 3 - - yes yes - - >>> i - 3 - - end - """)) - config = py.test.config._reparse([xtxt]) - session = config.initsession() - session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 0 - l = session.getitemoutcomepairs(py.test.Item.Passed) - l2 = session.getitemoutcomepairs(py.test.Item.Skipped) - assert len(l+l2) == 2 - -def test_js_ignore(): - py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) - tmpdir.ensure('__init__.py') - xtxt = tmpdir.join('x.txt') - xtxt.write(py.code.Source(""" - `blah`_ - - .. _`blah`: javascript:some_function() - """)) - config = py.test.config._reparse([xtxt]) - session = config.initsession() - session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 0 - l = session.getitemoutcomepairs(py.test.Item.Passed) - l2 = session.getitemoutcomepairs(py.test.Item.Skipped) - assert len(l+l2) == 3 - -def test_resolve_linkrole(): - from py.__.doc.conftest import resolve_linkrole - assert resolve_linkrole('api', 'py.foo.bar') == ( - 'py.foo.bar', '../../apigen/api/foo.bar.html') - assert resolve_linkrole('api', 'py.foo.bar()') == ( - 'py.foo.bar()', '../../apigen/api/foo.bar.html') - assert resolve_linkrole('api', 'py') == ( - 'py', '../../apigen/api/index.html') - py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') - assert resolve_linkrole('source', 'py/foo/bar.py') == ( - 'py/foo/bar.py', '../../apigen/source/foo/bar.py.html') - assert resolve_linkrole('source', 'py/foo/') == ( - 'py/foo/', '../../apigen/source/foo/index.html') - assert resolve_linkrole('source', 'py/') == ( - 'py/', '../../apigen/source/index.html') - py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') - Deleted: /py/trunk/py/test/config.py.merge.tmp ============================================================================== --- /py/trunk/py/test/config.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,288 +0,0 @@ -from __future__ import generators - -import py -from conftesthandle import Conftest - -optparse = py.compat.optparse - -# XXX move to Config class -basetemp = None -def ensuretemp(string, dir=1): - """ return temporary directory path with - the given string as the trailing part. - """ - global basetemp - if basetemp is None: - basetemp = py.path.local.make_numbered_dir(prefix='pytest-') - return basetemp.ensure(string, dir=dir) - -class CmdOptions(object): - """ pure container instance for holding cmdline options - as attributes. - """ - def __repr__(self): - return "" %(self.__dict__,) - -class Config(object): - """ central hub for dealing with configuration/initialization data. """ - Option = optparse.Option - - def __init__(self): - self.option = CmdOptions() - self._parser = optparse.OptionParser( - usage="usage: %prog [options] [query] [filenames of tests]") - self.conftest = Conftest() - self._initialized = False - self._overwrite_dict = {} - - 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") - self._initialized = True - self.conftest.setinitial(args) - self.conftest.rget('adddefaultoptions')() - args = [str(x) for x in args] - cmdlineoption, args = self._parser.parse_args(args) - self.option.__dict__.update(vars(cmdlineoption)) - fixoptions(self.option) # XXX fixing should be moved to sessions - if not args: - args.append(py.std.os.getcwd()) - self.topdir = gettopdir(args) - self.args = args - - def initdirect(self, topdir, repr, coltrails=None): - assert not self._initialized - self._initialized = True - self.topdir = py.path.local(topdir) - self.merge_repr(repr) - self._coltrails = coltrails - - def getcolitems(self): - """ return initial collectors. """ - trails = getattr(self, '_coltrails', None) - return [self._getcollector(path) for path in (trails or self.args)] - - def _getcollector(self, path): - if isinstance(path, tuple): - 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): - pkgpath = path.pypkgpath() - if pkgpath is None: - pkgpath = path.check(file=1) and path.dirpath() or path - col = self.conftest.rget("Directory", pkgpath)(pkgpath) - col.config = self - return col - - def addoptions(self, groupname, *specs): - """ add a named group of options to the current testing session. - This function gets invoked during testing session initialization. - """ - optgroup = optparse.OptionGroup(self._parser, groupname) - optgroup.add_options(specs) - self._parser.add_option_group(optgroup) - for opt in specs: - if hasattr(opt, 'default') and opt.dest: - setattr(self.option, opt.dest, opt.default) - return self.option - - def getvalue(self, name, path=None): - """ return 'name' value looked up from the first conftest file - found up the path (including the path itself). - if path is None, lookup the value in the initial - conftest modules found during command line parsing. - """ - try: - return self._overwrite_dict[name] - except KeyError: - return self.conftest.rget(name, path) - - def initsession(self): - """ return an initialized session object. """ - cls = self._getsessionclass() - session = cls(self) - #session.fixoptions() - return session - - def _getsessionclass(self): - """ return Session class determined from cmdline options - and looked up in initial config modules. - """ - if self.option.session is not None: - return self.conftest.rget(self.option.session) - else: - name = self._getsessionname() - importpath = globals()[name] - mod = __import__(importpath, None, None, '__doc__') - return getattr(mod, name) - - def _getsessionname(self): - """ return default session name as determined from options. """ - name = 'TerminalSession' - if self.option.dist: - name = 'RSession' - elif self.option.tkinter: - name = 'TkinterSession' - else: - optnames = 'startserver runbrowser apigen restreport boxing'.split() - for opt in optnames: - if getattr(self.option, opt, False): - name = 'LSession' - break - else: - if self.getvalue('dist_boxing'): - name = 'LSession' - if self.option.looponfailing: - name = 'RemoteTerminalSession' - elif self.option.executable: - name = 'RemoteTerminalSession' - return name - - def is_boxed(self): - # XXX probably not a good idea to have this special function ... - return self.option.boxing or self.getvalue("dist_boxing") - - def _reparse(self, args): - """ this is used from tests that want to re-invoke parse(). """ - global config_per_process - oldconfig = py.test.config - try: - config_per_process = py.test.config = Config() - config_per_process.parse(args) - return config_per_process - finally: - config_per_process = py.test.config = oldconfig - - def _overwrite(self, name, value): - """ this is used from tests to overwrite values irrespectives of conftests. - """ - self._overwrite_dict[name] = value - - def make_repr(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 merge_repr(self, repr): - """ merge in the conftest and cmdline option values - found in the given representation (produced - by make_repr 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) - 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 - 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 %s" - %(chain[0], self.topdir)) - return relpath, tuple([x.name for x in chain[1:]]) - -# this is the one per-process instance of py.test configuration -config_per_process = Config() - -# default import paths for sessions - -TkinterSession = 'py.__.test.tkinter.reportsession' -TerminalSession = 'py.__.test.terminal.terminal' -TerminalSession = 'py.__.test.terminal.terminal' -RemoteTerminalSession = 'py.__.test.terminal.remote' -RSession = 'py.__.test.rsession.rsession' -LSession = 'py.__.test.rsession.rsession' - -# -# helpers -# - -def checkmarshal(name, value): - try: - py.std.marshal.dumps(value) - except ValueError: - raise ValueError("%s=%r is not marshallable" %(name, value)) - -def fixoptions(option): - """ sanity checks and making option values canonical. """ - - # implied options - if option.usepdb: - if not option.nocapture: - #print "--pdb implies --nocapture" - option.nocapture = True - - if option.runbrowser and not option.startserver: - #print "--runbrowser implies --startserver" - option.startserver = 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." - - -def gettopdir(args): - """ return the top directory for the given paths. - if the common base dir resides in a python package - parent directory of the root package is returned. - """ - args = [py.path.local(arg) for arg in args] - p = reduce(py.path.local.common, args) - assert p, "cannot determine common basedir of %s" %(args,) - pkgdir = p.pypkgpath() - if pkgdir is None: - return p - else: - return pkgdir.dirpath() Deleted: /py/trunk/py/test/rsession/conftest.py ============================================================================== --- /py/trunk/py/test/rsession/conftest.py Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,10 +0,0 @@ -import py -Option = py.test.config.Option - -option = py.test.config.addoptions("boxing test options", - Option('', '--skip-kill-test', action='store_true', default=False, - dest='skip_kill_test', - help='skip a certain test that checks for os.kill results, this ' - 'should be used when kill() is not allowed for the current ' - 'user'), -) Deleted: /py/trunk/py/test/rsession/rest.py.merge.tmp ============================================================================== --- /py/trunk/py/test/rsession/rest.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,276 +0,0 @@ - -""" Rest reporting stuff -""" - -import py -import sys -from StringIO import StringIO -from py.__.test.rsession.reporter import AbstractReporter -from py.__.test.rsession import report -from py.__.rest.rst import * - -class RestReporter(AbstractReporter): - linkwriter = None - - def __init__(self, *args, **kwargs): - super(RestReporter, self).__init__(*args, **kwargs) - self.rest = Rest() - self.traceback_num = 0 - - def get_linkwriter(self): - if self.linkwriter is None: - try: - self.linkwriter = self.config.getvalue('linkwriter') - except KeyError: - print >>sys.stderr, ('no linkwriter configured, using default ' - 'one') - self.linkwriter = RelLinkWriter() - return self.linkwriter - - def report_unknown(self, what): - if self.config.option.verbose: - self.add_rest(Paragraph("Unknown report: %s" % what)) - - def gethost(self, item): - if item.channel: - return item.channel.gateway.host - return self.hosts[0] - - def report_SendItem(self, item): - address = self.gethost(item) - if self.config.option.verbose: - self.add_rest(Paragraph('sending item %s to %s' % (item.item, - address))) - - def report_HostRSyncing(self, item): - self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10], - item.remoterootpath))) - - def report_HostReady(self, item): - self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],))) - - def report_TestStarted(self, event): - txt = "Running tests on hosts: %s" % ", ".join(event.hosts) - self.add_rest(Title(txt, abovechar='=', belowchar='=')) - self.timestart = event.timestart - - def report_TestFinished(self, item): - self.timeend = item.timeend - self.summary() - return len(self.failed_tests_outcome) > 0 - - def report_ImmediateFailure(self, item): - pass - - def report_ItemStart(self, event): - item = event.item - if isinstance(item, py.test.collect.Module): - lgt = len(list(item.tryiter())) - lns = item.listnames()[1:] - name = "/".join(lns) - link = self.get_linkwriter().get_link(self.get_rootpath(item), - item.fspath) - if link: - name = Link(name, link) - txt = 'Testing module %s (%d items)' % (name, lgt) - self.add_rest(Title('Testing module', name, '(%d items)' % (lgt,), - belowchar='-')) - - def get_rootpath(self, item): - root = item.parent - while root.parent is not None: - root = root.parent - return root.fspath - - def print_summary(self, total, skipped_str, failed_str): - self.skips() - self.failures() - - txt = "%d tests run%s%s in %.2fs (rsync: %.2f)" % \ - (total, skipped_str, failed_str, self.timeend - self.timestart, - self.timersync - self.timestart) - self.add_rest(Title(txt, belowchar='-')) - - # since we're rendering each item, the links haven't been rendered - # yet - self.out.write(self.rest.render_links()) - - def report_ReceivedItemOutcome(self, event): - host = self.gethost(event) - if event.outcome.passed: - status = [Strong("PASSED")] - self.passed[host] += 1 - elif event.outcome.skipped: - status = [Strong("SKIPPED")] - self.skipped_tests_outcome.append(event) - self.skipped[host] += 1 - else: - status = [Strong("FAILED"), - InternalLink("traceback%d" % self.traceback_num)] - self.traceback_num += 1 - self.failed[host] += 1 - self.failed_tests_outcome.append(event) - # we'll take care of them later - itempath = self.get_path_from_item(event.item) - status.append(Text(itempath)) - hostname = host.hostname - self.add_rest(ListItem(Text("%10s:" % (hostname[:10],)), *status)) - - def skips(self): - # XXX hrmph, copied code - texts = {} - for event in self.skipped_tests_outcome: - colitem = event.item - if isinstance(event, report.ReceivedItemOutcome): - outcome = event.outcome - text = outcome.skipped - itemname = self.get_item_name(event, colitem) - elif isinstance(event, report.SkippedTryiter): - 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.add_rest(Title('Reasons for skipped tests:', belowchar='+')) - for text, items in texts.items(): - for item in items: - self.add_rest(ListItem('%s: %s' % (item, text))) - - def get_host(self, event): - return event.channel.gateway.host - - def failures(self): - self.traceback_num = 0 - tbstyle = self.config.option.tbstyle - if self.failed_tests_outcome: - self.add_rest(Title('Exceptions:', belowchar='+')) - for i, event in enumerate(self.failed_tests_outcome): - if i > 0: - self.add_rest(Transition()) - if isinstance(event, report.ReceivedItemOutcome): - host = self.get_host(event) - itempath = self.get_path_from_item(event.item) - root = self.get_rootpath(event.item) - link = self.get_linkwriter().get_link(root, event.item.fspath) - t = Title(belowchar='+') - if link: - t.add(Link(itempath, link)) - else: - t.add(Text(itempath)) - t.add(Text('on %s' % (host.hostname,))) - self.add_rest(t) - if event.outcome.signal: - self.repr_signal(event.item, event.outcome) - else: - self.repr_failure(event.item, event.outcome, tbstyle) - else: - itempath = self.get_path_from_item(event.item) - root = self.get_rootpath(event.item) - link = self.get_linkwriter().get_link(root, event.item.fspath) - t = Title(abovechar='+', belowchar='+') - if link: - t.add(Link(itempath, link)) - else: - t.add(Text(itempath)) - out = outcome.Outcome(excinfo=event.excinfo) - self.repr_failure(event.item, - outcome.ReprOutcome(out.make_repr()), - tbstyle) - - def repr_signal(self, item, outcome): - signal = outcome.signal - self.add_rest(Title('Received signal: %d' % (outcome.signal,), - abovechar='+', belowchar='+')) - if outcome.stdout.strip(): - self.add_rest(Paragraph('Captured process stdout:')) - self.add_rest(LiteralBlock(outcome.stdout)) - if outcome.stderr.strip(): - self.add_rest(Paragraph('Captured process stderr:')) - self.add_rest(LiteralBlock(outcome.stderr)) - - def repr_failure(self, item, outcome, style): - excinfo = outcome.excinfo - traceback = excinfo.traceback - if not traceback: - self.add_rest(Paragraph('empty traceback from item %r' % (item,))) - return - self.repr_traceback(item, excinfo, traceback, style) - if outcome.stdout: - self.add_rest(Title('Captured process stdout:', abovechar='+', - belowchar='+')) - self.add_rest(LiteralBlock(outcome.stdout)) - if outcome.stderr: - self.add_rest(Title('Captured process stderr:', abovechar='+', - belowchar='+')) - self.add_rest(LiteralBlock(outcome.stderr)) - - def repr_traceback(self, item, excinfo, traceback, style): - root = self.get_rootpath(item) - self.add_rest(LinkTarget('traceback%d' % self.traceback_num, "")) - self.traceback_num += 1 - if style == 'long': - for entry in traceback: - link = self.get_linkwriter().get_link(root, - py.path.local(entry.path)) - if link: - self.add_rest(Title(Link(entry.path, link), - 'line %d' % (entry.lineno,), - belowchar='+', abovechar='+')) - else: - self.add_rest(Title('%s line %d' % (entry.path, - entry.lineno,), - belowchar='+', abovechar='+')) - self.add_rest(LiteralBlock(self.prepare_source(entry.relline, - entry.source))) - elif style == 'short': - text = [] - for entry in traceback: - text.append('%s line %d' % (entry.path, entry.lineno)) - text.append(' %s' % (entry.source.strip(),)) - self.add_rest(LiteralBlock('\n'.join(text))) - self.add_rest(Title(excinfo.typename, belowchar='+')) - self.add_rest(LiteralBlock(excinfo.value)) - - def prepare_source(self, relline, source): - text = [] - for num, line in enumerate(source.split('\n')): - if num == relline: - text.append('>>> %s' % (line,)) - else: - text.append(' %s' % (line,)) - return '\n'.join(text) - - def add_rest(self, item): - self.rest.add(item) - self.out.write('%s\n\n' % (item.text(),)) - - def get_path_from_item(self, item): - lns = item.listnames()[1:] - for i, ln in enumerate(lns): - if i > 0 and ln != '()': - lns[i] = '/%s' % (ln,) - itempath = ''.join(lns) - return itempath - -class AbstractLinkWriter(object): - def get_link(self, base, path): - pass - -class NoLinkWriter(AbstractLinkWriter): - def get_link(self, base, path): - return '' - -class LinkWriter(AbstractLinkWriter): - def __init__(self, baseurl): - self.baseurl = baseurl - - def get_link(self, base, path): - relpath = path.relto(base) - return self.baseurl + relpath - -class RelLinkWriter(AbstractLinkWriter): - def get_link(self, base, path): - return path.relto(base) - Deleted: /py/trunk/py/test/rsession/rsession.py.merge.tmp ============================================================================== --- /py/trunk/py/test/rsession/rsession.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,297 +0,0 @@ - -""" Remote session base class -""" - -import os -import py -import sys -import re -import time - -from py.__.test.rsession import report -from py.__.test.rsession.master import \ - setup_slave, MasterNode, dispatch_loop, itemgen, randomgen -from py.__.test.rsession.hostmanage import HostInfo, HostOptions, HostManager - -from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ - box_runner -from py.__.test.rsession.reporter import LocalReporter, RemoteReporter - -class AbstractSession(object): - """ - An abstract session executes collectors/items through a runner. - - """ - def __init__(self, config, optimise_localhost=True): - self.config = config - self.optimise_localhost = optimise_localhost - - def make_colitems(paths, baseon): - # we presume that from the base we can simply get to - # the target paths by joining the basenames - res = [] - for x in paths: - x = py.path.local(x) - current = py.test.collect.Directory(baseon) - relparts = x.relto(baseon).split(x.sep) - assert relparts - for part in relparts: - next = current.join(part) - assert next is not None, (current, part) - current = next - res.append(current) - return res - make_colitems = staticmethod(make_colitems) - - def getpkgdir(path): - path = py.path.local(path) - pkgpath = path.pypkgpath() - if pkgpath is None: - pkgpath = path - return pkgpath - getpkgdir = staticmethod(getpkgdir) - - def init_reporter(self, reporter, sshhosts, reporter_class, arg=""): - """ This initialises so called `reporter` class, which will - handle all event presenting to user. Does not get called - if main received custom reporter - """ - startserverflag = self.config.option.startserver - restflag = self.config.option.restreport - - if startserverflag and reporter is None: - from py.__.test.rsession.web import start_server, exported_methods - if self.config.option.runbrowser: - from socket import INADDR_ANY - port = INADDR_ANY # pick a random port when starting browser - else: - port = 8000 # stick to a fixed port otherwise - - reporter = exported_methods.report - httpd = start_server(server_address = ('', port)) - port = httpd.server_port - if self.config.option.runbrowser: - import webbrowser, thread - # webbrowser.open() may block until the browser finishes or not - url = "http://localhost:%d" % (port,) - thread.start_new_thread(webbrowser.open, (url,)) - elif reporter is None: - if restflag: - from py.__.test.rsession.rest import RestReporter - reporter_class = RestReporter - if arg: - reporter_instance = reporter_class(self.config, sshhosts, self.getpkgdir(arg)) - else: - reporter_instance = reporter_class(self.config, sshhosts) - reporter = reporter_instance.report - else: - startserverflag = False - - return reporter, startserverflag - - def reporterror(reporter, data): - excinfo, item = data - if excinfo is None: - reporter(report.ItemStart(item)) - elif excinfo.type is py.test.Item.Skipped: - reporter(report.SkippedTryiter(excinfo, item)) - else: - reporter(report.FailedTryiter(excinfo, item)) - reporterror = staticmethod(reporterror) - - def kill_server(self, startserverflag): - """ Kill web server - """ - if startserverflag: - from py.__.test.rsession.web import kill_server - kill_server() - - def wrap_reporter(self, reporter): - """ We wrap reporter around, which makes it possible to us to track - number of failures - """ - self.was_failure = False - def new_reporter(event): - if isinstance(event, report.ReceivedItemOutcome) and \ - not event.outcome.passed and \ - not event.outcome.skipped: - self.was_failure = True - return reporter(event) - checkfun = lambda : self.config.option.exitfirst and \ - self.was_failure - # for tests - self.checkfun = checkfun - return new_reporter, checkfun - -def parse_directories(sshhosts): - """ Parse sshadresses of hosts to have distinct hostname/hostdir - """ - directories = {} - for host in sshhosts: - m = re.match("^(.*?):(.*)$", host.hostname) - if m: - host.hostname = m.group(1) - host.relpath = m.group(2) + "-" + host.hostname - else: - host.relpath = "pytestcache-%s" % host.hostname - -class RSession(AbstractSession): - """ Remote version of session - """ - def main(self, reporter=None): - """ main loop for running tests. """ - args = self.config.args - - sshhosts, remotepython, rsync_roots = self.read_distributed_config() - reporter, startserverflag = self.init_reporter(reporter, - sshhosts, RemoteReporter) - reporter, checkfun = self.wrap_reporter(reporter) - - reporter(report.TestStarted(sshhosts)) - - pkgdir = self.getpkgdir(args[0]) - done_dict = {} - hostopts = HostOptions(rsync_roots=rsync_roots, - remote_python=remotepython, - optimise_localhost=self.optimise_localhost) - hostmanager = HostManager(sshhosts, self.config, pkgdir, hostopts) - try: - nodes = hostmanager.init_hosts(reporter, done_dict) - reporter(report.RsyncFinished()) - try: - self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) - except (KeyboardInterrupt, SystemExit): - print >>sys.stderr, "C-c pressed waiting for gateways to teardown..." - channels = [node.channel for node in nodes] - hostmanager.kill_channels(channels) - hostmanager.teardown_gateways(reporter, channels) - print >>sys.stderr, "... Done" - raise - - channels = [node.channel for node in nodes] - hostmanager.teardown_hosts(reporter, channels, nodes, - exitfirst=self.config.option.exitfirst) - reporter(report.Nodes(nodes)) - retval = reporter(report.TestFinished()) - self.kill_server(startserverflag) - return retval - except (KeyboardInterrupt, SystemExit): - reporter(report.InterruptedExecution()) - self.kill_server(startserverflag) - raise - except: - reporter(report.CrashedExecution()) - self.kill_server(startserverflag) - raise - - def read_distributed_config(self): - """ Read from conftest file the configuration of distributed testing - """ - try: - rsync_roots = self.config.getvalue("distrsync_roots") - except: - rsync_roots = None # all files and directories in the pkgdir - sshhosts = [HostInfo(i) for i in - self.config.getvalue("disthosts")] - parse_directories(sshhosts) - remotepython = self.config.getvalue("dist_remotepython") - return sshhosts, remotepython, rsync_roots - - def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): - colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) - keyword = self.config.option.keyword - itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) - - all_tests = dispatch_loop(nodes, itemgenerator, checkfun) - #if all_tests: - # todo = {} - # for key, value in all_tests.items(): - # if key not in done_dict: - # todo[key] = True - # rg = randomgen(todo, done_dict) - # dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) - - -class LSession(AbstractSession): - """ Local version of session - """ - def main(self, reporter=None, runner=None): - # check out if used options makes any sense - args = self.config.args - - sshhosts = [HostInfo('localhost')] # this is just an info to reporter - - if not self.config.option.nomagic: - py.magic.invoke(assertion=1) - - reporter, startserverflag = self.init_reporter(reporter, - sshhosts, LocalReporter, args[0]) - reporter, checkfun = self.wrap_reporter(reporter) - - reporter(report.TestStarted(sshhosts)) - pkgdir = self.getpkgdir(args[0]) - colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) - reporter(report.RsyncFinished()) - - if runner is None: - runner = self.init_runner(pkgdir) - - keyword = self.config.option.keyword - - itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) - local_loop(self, reporter, itemgenerator, checkfun, self.config, runner=runner) - - retval = reporter(report.TestFinished()) - self.kill_server(startserverflag) - - if not self.config.option.nomagic: - py.magic.revoke(assertion=1) - - self.write_docs(pkgdir) - return retval - - def write_docs(self, pkgdir): - if self.config.option.apigen: - from py.__.apigen.tracer.docstorage import DocStorageAccessor - apigen = py.path.local(self.config.option.apigen).pyimport() - print >>sys.stderr, 'building documentation' - capture = py.io.OutErrCapture() - try: - try: - apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) - except (ValueError, AttributeError): - #import traceback - #exc, e, tb = sys.exc_info() - #print '%s - %s' % (exc, e) - #print ''.join(traceback.format_tb(tb)) - #del tb - #print '-' * 79 - raise NotImplementedError("Provided script does not seem " - "to contain build function") - finally: - capture.reset() - - def init_runner(self, pkgdir): - if self.config.option.apigen: - from py.__.apigen.tracer.tracer import Tracer, DocStorage - module = py - try: - apigen = py.path.local(self.config.option.apigen).pyimport() - items = apigen.get_documentable_items(pkgdir) - if isinstance(items, dict): - self.docstorage = DocStorage().from_dict(items) - else: - self.docstorage = DocStorage().from_pkg(items) - except ImportError: - raise ImportError("Provided script cannot be imported") - except (ValueError, AttributeError): - raise NotImplementedError("Provided script does not seem " - "to contain get_documentable_items") - self.tracer = Tracer(self.docstorage) - return apigen_runner - else: - if (self.config.getvalue('dist_boxing') or self.config.option.boxing)\ - and not self.config.option.nocapture: - return box_runner - return plain_runner - Deleted: /py/trunk/py/test/rsession/rsync.py ============================================================================== --- /py/trunk/py/test/rsession/rsync.py Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,137 +0,0 @@ -import py, os, stat, md5 -from Queue import Queue - - -class RSync(object): - - def __init__(self, callback=None, **options): - for name in options: - assert name in ('delete') - self.options = options - self.callback = callback - self.channels = {} - self.receivequeue = Queue() - self.links = [] - - def filter(self, path): - return True - - def add_target(self, gateway, destdir, finishedcallback=None): - def itemcallback(req): - self.receivequeue.put((channel, req)) - channel = gateway.remote_exec(REMOTE_SOURCE) - channel.setcallback(itemcallback, endmarker = None) - channel.send((str(destdir), self.options)) - self.channels[channel] = finishedcallback - - def send(self, sourcedir): - self.sourcedir = str(sourcedir) - # normalize a trailing '/' away - self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x')) - # send directory structure and file timestamps/sizes - self._send_directory_structure(self.sourcedir) - - # paths and to_send are only used for doing - # progress-related callbacks - self.paths = {} - self.to_send = {} - - # send modified file to clients - while self.channels: - channel, req = self.receivequeue.get() - if req is None: - # end-of-channel - if channel in self.channels: - # too early! we must have got an error - channel.waitclose() - # or else we raise one - raise IOError('connection unexpectedly closed: %s ' % ( - channel.gateway,)) - else: - command, data = req - if command == "links": - for link in self.links: - channel.send(link) - # completion marker, this host is done - channel.send(42) - elif command == "done": - finishedcallback = self.channels.pop(channel) - if finishedcallback: - finishedcallback() - elif command == "ack": - if self.callback: - self.callback("ack", self.paths[data], channel) - elif command == "list_done": - # sum up all to send - if self.callback: - s = sum([self.paths[i] for i in self.to_send[channel]]) - self.callback("list", s, channel) - elif command == "send": - modified_rel_path, checksum = data - modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) - f = open(modifiedpath, 'rb') - data = f.read() - - # provide info to progress callback function - modified_rel_path = "/".join(modified_rel_path) - self.paths[modified_rel_path] = len(data) - if channel not in self.to_send: - self.to_send[channel] = [] - self.to_send[channel].append(modified_rel_path) - - f.close() - if checksum is not None and checksum == md5.md5(data).digest(): - data = None # not really modified - else: - # ! there is a reason for the interning: - # sharing multiple copies of the file's data - data = intern(data) - print '%s <= %s' % ( - channel.gateway._getremoteaddress(), - modified_rel_path) - channel.send(data) - del data - else: - assert "Unknown command %s" % command - - def _broadcast(self, msg): - for channel in self.channels: - channel.send(msg) - - def _send_link(self, basename, linkpoint): - self.links.append(("link", basename, linkpoint)) - - def _send_directory_structure(self, path): - st = os.lstat(path) - if stat.S_ISREG(st.st_mode): - # regular file: send a timestamp/size pair - self._broadcast((st.st_mtime, st.st_size)) - elif stat.S_ISDIR(st.st_mode): - # dir: send a list of entries - names = [] - subpaths = [] - for name in os.listdir(path): - p = os.path.join(path, name) - if self.filter(p): - names.append(name) - subpaths.append(p) - self._broadcast(names) - for p in subpaths: - self._send_directory_structure(p) - elif stat.S_ISLNK(st.st_mode): - linkpoint = os.readlink(path) - basename = path[len(self.sourcedir) + 1:] - if not linkpoint.startswith(os.sep): - # relative link, just send it - self._send_link(basename, linkpoint) - elif linkpoint.startswith(self.sourcedir): - self._send_link(basename, linkpoint[len(self.sourcedir) + 1:]) - else: - self._send_link(basename, linkpoint) - self._broadcast(None) - else: - raise ValueError, "cannot sync %r" % (path,) - -REMOTE_SOURCE = py.path.local(__file__).dirpath().\ - join('rsync_remote.py').open().read() + "\nf()" - Deleted: /py/trunk/py/test/rsession/rsync_remote.py ============================================================================== --- /py/trunk/py/test/rsession/rsync_remote.py Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,86 +0,0 @@ - -def f(): - import os, stat, shutil, md5 - destdir, options = channel.receive() - modifiedfiles = [] - - def remove(path): - assert path.startswith(destdir) - try: - os.unlink(path) - except OSError: - # assume it's a dir - shutil.rmtree(path) - - def receive_directory_structure(path, relcomponents): - try: - st = os.lstat(path) - except OSError: - st = None - msg = channel.receive() - if isinstance(msg, list): - if st and not stat.S_ISDIR(st.st_mode): - os.unlink(path) - st = None - if not st: - os.makedirs(path) - entrynames = {} - for entryname in msg: - receive_directory_structure(os.path.join(path, entryname), - relcomponents + [entryname]) - entrynames[entryname] = True - if options.get('delete'): - for othername in os.listdir(path): - if othername not in entrynames: - otherpath = os.path.join(path, othername) - remove(otherpath) - elif msg is not None: - checksum = None - if st: - if stat.S_ISREG(st.st_mode): - msg_mtime, msg_size = msg - if msg_size != st.st_size: - pass - elif msg_mtime != st.st_mtime: - f = open(path, 'rb') - checksum = md5.md5(f.read()).digest() - f.close() - else: - return # already fine - else: - remove(path) - channel.send(("send", (relcomponents, checksum))) - modifiedfiles.append((path, msg)) - receive_directory_structure(destdir, []) - - STRICT_CHECK = False # seems most useful this way for py.test - channel.send(("list_done", None)) - - for path, (time, size) in modifiedfiles: - data = channel.receive() - channel.send(("ack", path[len(destdir) + 1:])) - if data is not None: - if STRICT_CHECK and len(data) != size: - raise IOError('file modified during rsync: %r' % (path,)) - f = open(path, 'wb') - f.write(data) - f.close() - os.utime(path, (time, time)) - del data - channel.send(("links", None)) - - msg = channel.receive() - while msg is not 42: - # we get symlink - _type, relpath, linkpoint = msg - assert _type == "link" - path = os.path.join(destdir, relpath) - try: - os.unlink(path) - except OSError: - pass - - os.symlink(os.path.join(destdir, linkpoint), path) - msg = channel.receive() - channel.send(("done", None)) - Deleted: /py/trunk/py/test/rsession/testing/test_config.py ============================================================================== --- /py/trunk/py/test/rsession/testing/test_config.py Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,24 +0,0 @@ - -""" test session config options -""" - -import py -from py.__.test.rsession.rsession import session_options, SessionOptions,\ - remote_options - -def test_session_opts(): - tmpdir = py.test.ensuretemp("sessionopts") - tmpdir.ensure("conftest.py").write("""class SessionOptions: - max_tasks_per_node = 5 - nice_level = 10 - """) - tmp2 = py.test.ensuretemp("xxx") - args = [str(tmpdir)] - config = py.test.config._reparse(args) - session_options.bind_config(config) - assert session_options.max_tasks_per_node == 5 - assert remote_options.nice_level == 10 - config = py.test.config._reparse([str(tmp2)]) - session_options.bind_config(config) - assert session_options.max_tasks_per_node == \ - SessionOptions.defaults['max_tasks_per_node'] Deleted: /py/trunk/py/test/rsession/testing/test_rsession.py.merge.tmp ============================================================================== --- /py/trunk/py/test/rsession/testing/test_rsession.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,324 +0,0 @@ - -""" Tests various aspects of rsession, like ssh hosts setup/teardown -""" - -import py -from py.__.test.rsession import report -from py.__.test.rsession.rsession import RSession, parse_directories,\ - parse_directories -from py.__.test.rsession.hostmanage import HostOptions, HostManager,\ - HostInfo -from py.__.test.rsession.testing.test_slave import funcfail_spec,\ - funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ - funcoptioncustom_spec - -def setup_module(mod): - mod.pkgdir = py.path.local(py.__file__).dirpath() - -def test_setup_non_existing_hosts(): - setup_events = [] - hosts = [HostInfo("alskdjalsdkjasldkajlsd")] - hm = HostManager(hosts, None, pkgdir) - cmd = "hm.init_hosts(setup_events.append)" - py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) - #assert setup_events - -def test_getpkdir(): - one = pkgdir.join("initpkg.py") - two = pkgdir.join("path", "__init__.py") - p1 = RSession.getpkgdir(one) - p2 = RSession.getpkgdir(two) - assert p1 == p2 - assert p1 == pkgdir - -def test_getpkdir_no_inits(): - tmp = py.test.ensuretemp("getpkdir1") - fn = tmp.ensure("hello.py") - assert RSession.getpkgdir(fn) == fn - -def test_make_colitems(): - one = pkgdir.join("initpkg.py") - two = pkgdir.join("path", "__init__.py") - - cols = RSession.make_colitems([one, two], baseon=pkgdir) - assert len(cols) == 2 - col_one, col_two = cols - assert col_one.listnames() == ["py", "initpkg.py"] - assert col_two.listnames() == ["py", "path", "__init__.py"] - - cols = RSession.make_colitems([one, two], baseon=pkgdir.dirpath()) - assert len(cols) == 2 - col_one, col_two = cols - assert col_one.listnames() == [pkgdir.dirpath().basename, - "py", "initpkg.py"] - assert col_two.listnames() == [pkgdir.dirpath().basename, - "py", "path", "__init__.py"] - -def test_example_tryiter(): - events = [] - tmpdir = py.test.ensuretemp("tryitertest") - tmpdir.ensure("a", "__init__.py") - tmpdir.ensure("conftest.py").write(py.code.Source(""" - import py - py.test.skip("Reason") - """)) - tmpdir.ensure("a", "test_empty.py").write(py.code.Source(""" - def test_empty(): - pass - """)) - rootcol = py.test.collect.Directory(tmpdir) - data = list(rootcol.tryiter(reporterror=events.append)) - assert len(events) == 2 - assert str(events[1][0].value) == "Reason" - -class TestRSessionRemote: - def test_example_distribution_minus_x(self): - tmpdir = py.test.ensuretemp("example_distribution_minus_x") - tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" - disthosts = [%r] - """ % ('localhost',))) - tmpdir.ensure("sub", "__init__.py") - tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" - def test_1(): - pass - def test_x(): - import py - py.test.skip("aaa") - def test_2(): - assert 0 - def test_3(): - raise ValueError(23) - def test_4(someargs): - pass - """)) - args = [str(tmpdir.join("sub")), "-x"] - config = py.test.config._reparse(args) - rsession = RSession(config) - allevents = [] - rsession.main(reporter=allevents.append) - testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] - assert len(testevents) == 3 - assert rsession.checkfun() - - def test_example_distribution(self): - subdir = "sub_example_dist" - tmpdir = py.test.ensuretemp("example_distribution") - tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" - disthosts = [%r] - distrsync_roots = ["%s", "py"] - """ % ('localhost', 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' - """ % (str(tmpdir.join(subdir)), py.__file__))) - tmpdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) - args = [str(tmpdir.join(subdir))] - config = py.test.config._reparse(args) - rsession = RSession(config, optimise_localhost=False) - allevents = [] - rsession.main(reporter=allevents.append) - testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] - assert len(testevents) - passevents = [i for i in testevents if i.outcome.passed] - failevents = [i for i in testevents if i.outcome.excinfo] - skippedevents = [i for i in testevents if i.outcome.skipped] - assert len(testevents) == 6 - assert len(passevents) == 3 - assert len(failevents) == 3 - tb = failevents[0].outcome.excinfo.traceback - assert str(tb[0].path).find("test_one") != -1 - assert tb[0].source.find("test_2") != -1 - assert failevents[0].outcome.excinfo.typename == 'AssertionError' - tb = failevents[1].outcome.excinfo.traceback - assert str(tb[0].path).find("test_one") != -1 - assert tb[0].source.find("test_3") != -1 - assert failevents[1].outcome.excinfo.typename == 'ValueError' - assert failevents[1].outcome.excinfo.value == '23' - tb = failevents[2].outcome.excinfo.traceback - assert failevents[2].outcome.excinfo.typename == 'TypeError' - assert str(tb[0].path).find("executor") != -1 - assert tb[0].source.find("execute") != -1 - - def test_setup_teardown_ssh(self): - hosts = [HostInfo('localhost')] - parse_directories(hosts) - setup_events = [] - teardown_events = [] - - config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) - hm = HostManager(hosts, config, pkgdir, opts) - nodes = hm.init_hosts(setup_events.append) - hm.teardown_hosts(teardown_events.append, - [node.channel for node in nodes], nodes) - - count_rsyn_calls = [i for i in setup_events - if isinstance(i, report.HostRSyncing)] - assert len(count_rsyn_calls) == len([i for i in hosts]) - count_ready_calls = [i for i in setup_events - if isinstance(i, report.HostReady)] - assert len(count_ready_calls) == len([i for i in hosts]) - - # same for teardown events - teardown_wait_starts = [i for i in teardown_events - if isinstance(i, report.CallStart)] - teardown_wait_ends = [i for i in teardown_events - if isinstance(i, report.CallFinish)] - assert len(teardown_wait_starts) == len(hosts) - assert len(teardown_wait_ends) == len(hosts) - - def test_setup_teardown_run_ssh(self): - hosts = [HostInfo('localhost')] - parse_directories(hosts) - allevents = [] - - config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) - hm = HostManager(hosts, config, pkgdir, opts) - nodes = hm.init_hosts(allevents.append) - - from py.__.test.rsession.testing.test_executor \ - import ItemTestPassing, ItemTestFailing, ItemTestSkipping - - rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol.getitembynames(funcpass_spec) - itemfail = rootcol.getitembynames(funcfail_spec) - itemskip = rootcol.getitembynames(funcskip_spec) - itemprint = rootcol.getitembynames(funcprint_spec) - - # actually run some tests - for node in nodes: - node.send(itempass) - node.send(itemfail) - node.send(itemskip) - node.send(itemprint) - - hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) - - events = [i for i in allevents - if isinstance(i, report.ReceivedItemOutcome)] - passed = [i for i in events - if i.outcome.passed] - skipped = [i for i in events - if i.outcome.skipped] - assert len(passed) == 2 * len(nodes) - assert len(skipped) == len(nodes) - assert len(events) == 4 * len(nodes) - # 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_config_pass(self): - """ Tests options object passing master -> server - """ - allevents = [] - hosts = [HostInfo('localhost')] - parse_directories(hosts) - config = py.test.config._reparse([]) - config._overwrite('custom', 'custom') - # we need to overwrite default list to serialize - from py.__.test.rsession.master import defaultconftestnames - defaultconftestnames.append("custom") - try: - opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) - hm = HostManager(hosts, config, pkgdir, opts) - nodes = hm.init_hosts(allevents.append) - - rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol.getitembynames(funcoptioncustom_spec) - - for node in nodes: - node.send(itempass) - - hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) - events = [i for i in allevents - if isinstance(i, report.ReceivedItemOutcome)] - passed = [i for i in events - if i.outcome.passed] - skipped = [i for i in events - if i.outcome.skipped] - assert len(passed) == 1 * len(nodes) - assert len(skipped) == 0 - assert len(events) == len(passed) - finally: - defaultconftestnames.remove("custom") - - def test_nice_level(self): - """ Tests if nice level behaviour is ok - """ - allevents = [] - hosts = [HostInfo('localhost')] - parse_directories(hosts) - tmpdir = py.test.ensuretemp("nice") - tmpdir.ensure("__init__.py") - tmpdir.ensure("conftest.py").write(py.code.Source(""" - disthosts = ['localhost'] - dist_nicelevel = 10 - """)) - tmpdir.ensure("test_one.py").write("""def test_nice(): - import os - assert os.nice(0) == 10 - """) - - config = py.test.config._reparse([tmpdir]) - rsession = RSession(config) - allevents = [] - rsession.main(reporter=allevents.append) - testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] - passevents = [x for x in testevents if x.outcome.passed] - assert len(passevents) == 1 - -class XxxTestDirectories(object): - # need complete rewrite, and unsure if it makes sense at all - def test_simple_parse(self): - sshhosts = [HostInfo(i) for i in ['h1', 'h2', 'h3']] - parse_directories(sshhosts) - - def test_sophisticated_parse(self): - sshhosts = ['a at h1:/tmp', 'h2:tmp', 'h3'] - dirs = parse_directories(sshhosts) - assert py.builtin.sorted( - dirs.values()) == ['/tmp', 'pytestcache', 'tmp'] - - def test_parse_multiple_hosts(self): - hosts = ['h1', 'h1', 'h1:/tmp'] - dirs = parse_directories(hosts) - assert dirs == {(0, 'h1'): 'pytestcache', (1, 'h1'): 'pytestcache', - (2, 'h1'):'/tmp'} - -class TestInithosts(object): - def test_inithosts(self): - testevents = [] - hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] - hosts = [HostInfo(i) for i in hostnames] - parse_directories(hosts) - config = py.test.config._reparse([]) - opts = HostOptions(do_sync=False, create_gateways=False) - hm = HostManager(hosts, config, pkgdir, opts) - nodes = hm.init_hosts(testevents.append) - events = [i for i in testevents if isinstance(i, report.HostRSyncing)] - assert len(events) == 4 - assert events[0].host.hostname == 'h1' - assert events[0].host.relpath == '/tmp-h1' - assert events[1].host.hostname == 'h1' - assert events[1].host.relpath == '/other-h1' - assert events[2].host.hostname == 'h2' - assert events[2].host.relpath == 'pytestcache-h2' - assert events[3].host.hostname == 'h2' - assert events[3].host.relpath == 'home-h2' - Deleted: /py/trunk/py/test/rsession/testing/test_rsync.py ============================================================================== --- /py/trunk/py/test/rsession/testing/test_rsync.py Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,80 +0,0 @@ -import py -from py.__.test.rsession.rsync import RSync - - -def setup_module(mod): - mod.gw = py.execnet.PopenGateway() - mod.gw2 = py.execnet.PopenGateway() - -def teardown_module(mod): - mod.gw.exit() - mod.gw2.exit() - - -def test_dirsync(): - base = py.test.ensuretemp('dirsync') - dest = base.join('dest') - dest2 = base.join('dest2') - source = base.mkdir('source') - - for s in ('content1', 'content2-a-bit-longer'): - source.ensure('subdir', 'file1').write(s) - rsync = RSync() - rsync.add_target(gw, dest) - rsync.add_target(gw2, dest2) - rsync.send(source) - assert dest.join('subdir').check(dir=1) - assert dest.join('subdir', 'file1').check(file=1) - assert dest.join('subdir', 'file1').read() == s - assert dest2.join('subdir').check(dir=1) - assert dest2.join('subdir', 'file1').check(file=1) - assert dest2.join('subdir', 'file1').read() == s - - source.join('subdir').remove('file1') - rsync = RSync() - rsync.add_target(gw2, dest2) - rsync.add_target(gw, dest) - rsync.send(source) - assert dest.join('subdir', 'file1').check(file=1) - assert dest2.join('subdir', 'file1').check(file=1) - rsync = RSync(delete=True) - rsync.add_target(gw2, dest2) - rsync.add_target(gw, dest) - rsync.send(source) - assert not dest.join('subdir', 'file1').check() - assert not dest2.join('subdir', 'file1').check() - -def test_symlink_rsync(): - if py.std.sys.platform == 'win32': - py.test.skip("symlinks are unsupported on Windows.") - base = py.test.ensuretemp('symlinkrsync') - dest = base.join('dest') - source = base.join('source') - source.ensure("existant") - source.join("rellink").mksymlinkto(source.join("existant"), absolute=0) - source.join('abslink').mksymlinkto(source.join("existant")) - - rsync = RSync() - rsync.add_target(gw, dest) - rsync.send(source) - - assert dest.join('rellink').readlink() == dest.join("existant") - assert dest.join('abslink').readlink() == dest.join("existant") - -def test_callback(): - base = py.test.ensuretemp('callback') - dest = base.join("dest") - source = base.join("source") - source.ensure("existant").write("a" * 100) - source.ensure("existant2").write("a" * 10) - total = {} - def callback(cmd, lgt, channel): - total[(cmd, lgt)] = True - - rsync = RSync(callback=callback) - #rsync = RSync() - rsync.add_target(gw, dest) - rsync.send(source) - - assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True} - Deleted: /py/trunk/py/test/rsession/testing/test_webjs.py.merge.tmp ============================================================================== --- /py/trunk/py/test/rsession/testing/test_webjs.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,138 +0,0 @@ -import py - -try: - import pypy - from pypy.translator.js.modules import dom - from pypy.translator.js.tester import schedule_callbacks - from py.__.test.rsession.rsession import session_options - dom.Window # check whether dom was properly imported or is just a - # leftover in sys.modules -except (ImportError, AttributeError): - py.test.skip('PyPy not found') - -from py.__.test.rsession import webjs -from py.__.test.rsession.web import exported_methods -here = py.magic.autopath().dirpath() - -def setup_module(mod): - # load HTML into window object - html = here.join('../webdata/index.html').read() - mod.html = html - from pypy.translator.js.modules import dom - mod.dom = dom - dom.window = dom.Window(html) - dom.document = dom.window.document - config = py.test.config._reparse([]) - config._overwrite('_dist_import_pypy', True) - from py.__.test.rsession import webjs - from py.__.test.rsession.web import exported_methods - mod.webjs = webjs - mod.exported_methods = exported_methods - -def setup_function(f): - dom.window = dom.Window(html) - dom.document = dom.window.document - -def test_html_loaded(): - body = dom.window.document.getElementsByTagName('body')[0] - assert len(body.childNodes) > 0 - assert str(body.childNodes[1].nodeName) == 'A' - -def test_set_msgbox(): - msgbox = dom.window.document.getElementById('messagebox') - assert len(msgbox.childNodes) == 0 - webjs.set_msgbox('foo', 'bar') - assert len(msgbox.childNodes) == 1 - assert msgbox.childNodes[0].nodeName == 'PRE' - assert msgbox.childNodes[0].childNodes[0].nodeValue == 'foo\nbar' - -def test_show_info(): - info = dom.window.document.getElementById('info') - info.style.visibility = 'hidden' - info.innerHTML = '' - webjs.show_info('foobar') - content = info.innerHTML - assert content == 'foobar' - bgcolor = info.style.backgroundColor - assert bgcolor == 'beige' - -def test_hide_info(): - info = dom.window.document.getElementById('info') - info.style.visibility = 'visible' - webjs.hide_info() - assert info.style.visibility == 'hidden' - -def test_process(): - main_t = dom.window.document.getElementById('main_table') - assert len(main_t.getElementsByTagName('tr')) == 0 - assert not webjs.process({}) - - msg = {'type': 'ItemStart', - 'itemtype': 'Module', - 'itemname': 'foo.py', - 'fullitemname': 'modules/foo.py', - 'length': 10, - } - assert webjs.process(msg) - trs = main_t.getElementsByTagName('tr') - assert len(trs) == 1 - tr = trs[0] - assert len(tr.childNodes) == 2 - assert tr.childNodes[0].nodeName == 'TD' - assert tr.childNodes[0].innerHTML == 'foo.py[0/10]' - assert tr.childNodes[1].nodeName == 'TD' - assert tr.childNodes[1].childNodes[0].nodeName == 'TABLE' - assert len(tr.childNodes[1].getElementsByTagName('tr')) == 0 - -def test_process_two(): - main_t = dom.window.document.getElementById('main_table') - msg = {'type': 'ItemStart', - 'itemtype': 'Module', - 'itemname': 'foo.py', - 'fullitemname': 'modules/foo.py', - 'length': 10, - } - webjs.process(msg) - msg = {'type': 'ReceivedItemOutcome', - 'fullmodulename': 'modules/foo.py', - 'passed' : 'True', - 'fullitemname' : 'modules/foo.py/test_item', - 'hostkey': None, - } - webjs.process(msg) - trs = main_t.getElementsByTagName('tr') - tds = trs[0].getElementsByTagName('td') - # two cells in the row, one in the table inside one of the cells - assert len(tds) == 3 - html = tds[0].innerHTML - assert html == 'foo.py[1/10]' - assert tds[2].innerHTML == '.' - -def test_signal(): - main_t = dom.window.document.getElementById('main_table') - msg = {'type': 'ItemStart', - 'itemtype': 'Module', - 'itemname': 'foo.py', - 'fullitemname': 'modules/foo.py', - 'length': 10, - } - webjs.process(msg) - msg = {'type': 'ReceivedItemOutcome', - 'fullmodulename': 'modules/foo.py', - 'passed' : 'False', - 'fullitemname' : 'modules/foo.py/test_item', - 'hostkey': None, - 'signal': '10', - 'skipped': 'False', - } - exported_methods.fail_reasons['modules/foo.py/test_item'] = 'Received signal 10' - exported_methods.stdout['modules/foo.py/test_item'] = '' - exported_methods.stderr['modules/foo.py/test_item'] = '' - webjs.process(msg) - schedule_callbacks(exported_methods) - # ouch - assert dom.document.getElementById('modules/foo.py').childNodes[0].\ - childNodes[0].childNodes[0].childNodes[0].nodeValue == 'F' - -# XXX: Write down test for full run - Deleted: /py/trunk/py/test/rsession/web.py.merge.tmp ============================================================================== --- /py/trunk/py/test/rsession/web.py.merge.tmp Wed Jan 24 17:46:46 2007 +++ (empty file) @@ -1,430 +0,0 @@ - -""" web server for py.test -""" - -from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler - -import thread, threading -import re -import time -import random -import Queue -import os -import sys -import socket - -import py -from py.__.test.rsession.rsession import RSession -from py.__.test.rsession import report -from py.__.test import collect -from py.__.test.rsession.webdata import json - -DATADIR = py.path.local(__file__).dirpath("webdata") -FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info", - "show_host", "hide_host", "hide_messagebox", "opt_scroll"] - -try: - try: - if not py.test.config.getvalue('_dist_import_pypy'): - raise ImportError - except AttributeError: - pass - from pypy.rpython.ootypesystem.bltregistry import MethodDesc, BasicExternal,\ - described - from pypy.translator.js.main import rpython2javascript - from pypy.translator.js import commproxy - from pypy.rpython.extfunc import _callable - - commproxy.USE_MOCHIKIT = False - IMPORTED_PYPY = True -except (ImportError, NameError): - class BasicExternal(object): - pass - - def described(*args, **kwargs): - def decorator(func): - return func - return decorator - - def _callable(*args, **kwargs): - pass - - IMPORTED_PYPY = False - -def add_item(event): - """ A little helper - """ - item = event.item - itemtype = item.__class__.__name__ - itemname = item.name - fullitemname = "/".join(item.listnames()) - d = {'fullitemname': fullitemname, 'itemtype': itemtype, - 'itemname': itemname} - #if itemtype == 'Module': - try: - d['length'] = str(len(list(event.item.tryiter()))) - except: - d['length'] = "?" - return d - -class MultiQueue(object): - """ a tailor-made queue (internally using Queue) for py.test.rsession.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 - - when a data queue for a sessid doesn't yet exist, it is created, and - filled with data that has already been fed to the other clients - """ - def __init__(self): - self._cache = [] - self._session_queues = {} - self._lock = py.std.thread.allocate_lock() - - def put(self, item): - self._lock.acquire() - try: - self._cache.append(item) - for key, q in self._session_queues.items(): - q.put(item) - finally: - self._lock.release() - - def _del(self, sessid): - self._lock.acquire() - try: - del self._session_queues[sessid] - finally: - self._lock.release() - - def get(self, sessid): - self._lock.acquire() - try: - if not sessid in self._session_queues: - self._create_session_queue(sessid) - finally: - self._lock.release() - return self._session_queues[sessid].get(sessid) - - def empty(self): - self._lock.acquire() - try: - if not self._session_queues: - return not len(self._cache) - for q in self._session_queues.values(): - if not q.empty(): - return False - finally: - self._lock.release() - return True - - def empty_queue(self, sessid): - return self._session_queues[sessid].empty() - - def _create_session_queue(self, sessid): - self._session_queues[sessid] = q = Queue.Queue() - for item in self._cache: - q.put(item) - -class ExportedMethods(BasicExternal): - _render_xmlhttp = True - def __init__(self): - self.pending_events = MultiQueue() - self.start_event = threading.Event() - self.end_event = threading.Event() - self.skip_reasons = {} - self.fail_reasons = {} - self.stdout = {} - self.stderr = {} - self.all = 0 - - def findmodule(self, item): - # find the most outwards parent which is module - current = item - while current: - if isinstance(current, collect.Module): - break - current = current.parent - - if current is not None: - return str(current.name), str("/".join(current.listnames())) - else: - return str(item.parent.name), str("/".join(item.parent.listnames())) - - def show_hosts(self): - self.start_event.wait() - to_send = {} - for host in self.hosts: - to_send[host.hostid] = host.hostname - return to_send - show_hosts = described(retval={str:str}, args=[('callback', - _callable([{str:str}]))])(show_hosts) - - def show_skip(self, item_name="aa"): - return {'item_name': item_name, - 'reason': self.skip_reasons[item_name]} - show_skip = described(retval={str:str}, args=[('item_name',str),('callback', - _callable([{str:str}]))])(show_skip) - - def show_fail(self, item_name="aa"): - return {'item_name':item_name, - 'traceback':str(self.fail_reasons[item_name]), - 'stdout':self.stdout[item_name], - 'stderr':self.stderr[item_name]} - show_fail = described(retval={str:str}, args=[('item_name',str),('callback', - _callable([{str:str}]))])(show_fail) - - _sessids = None - _sesslock = py.std.thread.allocate_lock() - def show_sessid(self): - if not self._sessids: - self._sessids = [] - self._sesslock.acquire() - try: - while 1: - chars = list(py.std.string.lowercase) - py.std.random.shuffle(chars) - sessid = ''.join(chars[:8]) - if sessid not in self._sessids: - self._sessids.append(sessid) - break - finally: - self._sesslock.release() - return sessid - show_sessid = described(retval=str, args=[('callback', - _callable([str]))])(show_sessid) - - def failed(self, **kwargs): - if not 'sessid' in kwargs: - return - sessid = kwargs['sessid'] - to_del = -1 - for num, i in enumerate(self._sessids): - if i == sessid: - to_del = num - if to_del != -1: - del self._sessids[to_del] - self.pending_events._del(kwargs['sessid']) - - def show_all_statuses(self, sessid=-1): - retlist = [self.show_status_change(sessid)] - while not self.pending_events.empty_queue(sessid): - retlist.append(self.show_status_change(sessid)) - retval = retlist - return retval - show_all_statuses = described(retval=[{str:str}],args= - [('sessid',str), ('callback',_callable([[{str:str}]]))])(show_all_statuses) - - def show_status_change(self, sessid): - event = self.pending_events.get(sessid) - if event is None: - self.end_event.set() - return {} - # some dispatcher here - if isinstance(event, report.ReceivedItemOutcome): - args = {} - outcome = event.outcome - for key, val in outcome.__dict__.iteritems(): - args[key] = str(val) - args.update(add_item(event)) - mod_name, mod_fullname = self.findmodule(event.item) - args['modulename'] = str(mod_name) - args['fullmodulename'] = str(mod_fullname) - fullitemname = args['fullitemname'] - if outcome.skipped: - self.skip_reasons[fullitemname] = outcome.skipped - elif outcome.excinfo: - self.fail_reasons[fullitemname] = self.repr_failure_tblong( - event.item, outcome.excinfo, outcome.excinfo.traceback) - self.stdout[fullitemname] = outcome.stdout - self.stderr[fullitemname] = outcome.stderr - elif outcome.signal: - self.fail_reasons[fullitemname] = "Received signal %d" % outcome.signal - self.stdout[fullitemname] = outcome.stdout - self.stderr[fullitemname] = outcome.stderr - if event.channel: - args['hostkey'] = event.channel.gateway.host.hostid - else: - args['hostkey'] = '' - elif isinstance(event, report.ItemStart): - args = add_item(event) - elif isinstance(event, report.TestFinished): - args = {} - args['run'] = str(self.all) - args['fails'] = str(len(self.fail_reasons)) - args['skips'] = str(len(self.skip_reasons)) - elif isinstance(event, report.SendItem): - args = add_item(event) - args['hostkey'] = event.channel.gateway.host.hostid - elif isinstance(event, report.HostReady): - self.ready_hosts[event.host] = True - args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid} - elif isinstance(event, report.FailedTryiter): - args = add_item(event) - elif isinstance(event, report.SkippedTryiter): - args = add_item(event) - args['reason'] = str(event.excinfo.value) - else: - args = {} - args['event'] = str(event) - args['type'] = event.__class__.__name__ - return args - - def repr_failure_tblong(self, item, excinfo, traceback): - lines = [] - for index, entry in py.builtin.enumerate(traceback): - lines.append('----------') - lines.append("%s: %s" % (entry.path, entry.lineno)) - lines += self.repr_source(entry.relline, entry.source) - lines.append("%s: %s" % (excinfo.typename, excinfo.value)) - return "\n".join(lines) - - def repr_source(self, relline, source): - lines = [] - for num, line in enumerate(source.split("\n")): - if num == relline: - lines.append(">>>>" + line) - else: - lines.append(" " + line) - return lines - - def report_ReceivedItemOutcome(self, event): - self.all += 1 - self.pending_events.put(event) - - def report_ItemStart(self, event): - if isinstance(event.item, py.test.collect.Module): - self.pending_events.put(event) - - def report_unknown(self, event): - # XXX: right now, we just pass it for showing - self.pending_events.put(event) - - def report_TestStarted(self, event): - # XXX: It overrides out self.hosts - self.hosts = {} - self.ready_hosts = {} - for host in event.hosts: - self.hosts[host] = host - self.ready_hosts[host] = False - self.start_event.set() - self.pending_events.put(event) - - def report(self, what): - repfun = getattr(self, "report_" + what.__class__.__name__, - self.report_unknown) - try: - 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 - -## try: -## self.wait_flag.acquire() -## self.pending_events.insert(0, event) -## self.wait_flag.notify() -## finally: -## self.wait_flag.release() - -exported_methods = ExportedMethods() - -class TestHandler(BaseHTTPRequestHandler): - exported_methods = exported_methods - - def do_GET(self): - path = self.path - if path.endswith("/"): - path = path[:-1] - if path.startswith("/"): - path = path[1:] - m = re.match('^(.*)\?(.*)$', path) - if m: - path = m.group(1) - getargs = m.group(2) - else: - getargs = "" - name_path = path.replace(".", "_") - method_to_call = getattr(self, "run_" + name_path, None) - if method_to_call is None: - exec_meth = getattr(self.exported_methods, name_path, None) - if exec_meth is None: - self.send_error(404, "File %s not found" % path) - else: - try: - self.serve_data('text/json', - json.write(exec_meth(**self.parse_args(getargs)))) - except socket.error: - # client happily disconnected - exported_methods.failed(**self.parse_args(getargs)) - else: - method_to_call() - - def parse_args(self, getargs): - # parse get argument list - if getargs == "": - return {} - - unquote = py.std.urllib.unquote - args = {} - arg_pairs = getargs.split("&") - for arg in arg_pairs: - key, value = arg.split("=") - args[unquote(key)] = unquote(value) - return args - - def log_message(self, format, *args): - # XXX just discard it - pass - - do_POST = do_GET - - def run_(self): - self.run_index() - - def run_index(self): - data = py.path.local(DATADIR).join("index.html").open().read() - self.serve_data("text/html", data) - - def run_jssource(self): - js_name = py.path.local(__file__).dirpath("webdata").join("source.js") - web_name = py.path.local(__file__).dirpath().join("webjs.py") - if IMPORTED_PYPY and web_name.mtime() > js_name.mtime(): - from py.__.test.rsession import webjs - - javascript_source = rpython2javascript(webjs, - FUNCTION_LIST, use_pdb=False) - open(str(js_name), "w").write(javascript_source) - self.serve_data("text/javascript", javascript_source) - else: - js_source = open(str(js_name), "r").read() - self.serve_data("text/javascript", js_source) - - def serve_data(self, content_type, data): - self.send_response(200) - self.send_header("Content-type", content_type) - self.send_header("Content-length", len(data)) - self.end_headers() - self.wfile.write(data) - -def start_server(server_address = ('', 8000), handler=TestHandler, start_new=True): - httpd = HTTPServer(server_address, handler) - - if start_new: - thread.start_new_thread(httpd.serve_forever, ()) - print "Server started, listening on port %d" % (httpd.server_port,) - return httpd - else: - print "Server started, listening on port %d" % (httpd.server_port,) - httpd.serve_forever() - -def kill_server(): - exported_methods.pending_events.put(None) - while not exported_methods.pending_events.empty(): - time.sleep(.1) - exported_methods.end_event.wait() - From hpk at codespeak.net Wed Jan 24 18:10:33 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 18:10:33 +0100 (CET) Subject: [py-svn] r37281 - py/trunk/py/misc/testing Message-ID: <20070124171033.0A83510078@code0.codespeak.net> Author: hpk Date: Wed Jan 24 18:10:32 2007 New Revision: 37281 Modified: py/trunk/py/misc/testing/test_initpkg.py Log: omit tracer test directory for imports (XXX example Filesystem trees should be created from code and not reside in SVN) 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 Wed Jan 24 18:10:32 2007 @@ -51,6 +51,7 @@ nodirs = ( base.join('test', 'tkinter'), base.join('test', 'testing', 'data'), + base.join('apigen', 'tracer', 'testing', 'package'), base.join('test', 'testing', 'test'), base.join('magic', 'greenlet.py'), base.join('path', 'extpy', 'testing', 'test_data'), From hpk at codespeak.net Wed Jan 24 18:16:54 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 18:16:54 +0100 (CET) Subject: [py-svn] r37283 - py/branch/config Message-ID: <20070124171654.9FB6610078@code0.codespeak.net> Author: hpk Date: Wed Jan 24 18:16:53 2007 New Revision: 37283 Removed: py/branch/config/ Log: remove config branch for now to avoid confusion From hpk at codespeak.net Wed Jan 24 18:24:22 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 18:24:22 +0100 (CET) Subject: [py-svn] r37285 - py/trunk/py/test Message-ID: <20070124172422.3442210078@code0.codespeak.net> Author: hpk Date: Wed Jan 24 18:24:21 2007 New Revision: 37285 Removed: py/trunk/py/test/session-options.txt Log: this file describing option is obsolete now Deleted: /py/trunk/py/test/session-options.txt ============================================================================== --- /py/trunk/py/test/session-options.txt Wed Jan 24 18:24:21 2007 +++ (empty file) @@ -1,33 +0,0 @@ -RSession options: - -class SessionOptions: - nice_level - how to nice remote prcesses (RSession) - boxing_policy - run with boxes or not (LSession) - max_tasks_per_node (RSession) - waittime - how long to wait till considering node dead (not fully working, - RSession) - import_pypy - try to import pypy for regenerating web js (RSession, LSession) - -user provided options: - -startserver - run web server for displaying output (RSession, LSession), - implies LSession -runbrowser - use webbrowser - implies startserver -rest - implies LSession -apigen - implies LSession - -other conftest options: - -dist_rsyncroots - RSession -disthosts - RSession -dist_remotepython - RSession - -various stuff: - -we_are_remote - flag to indicate whether we are in remote process, not - available for user. - -options that does not work in L/RSession: - -looponfailing -exec From fijal at codespeak.net Wed Jan 24 18:30:30 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 18:30:30 +0100 (CET) Subject: [py-svn] r37286 - py/trunk/py/test/rsession Message-ID: <20070124173030.38E5B10076@code0.codespeak.net> Author: fijal Date: Wed Jan 24 18:30:29 2007 New Revision: 37286 Modified: py/trunk/py/test/rsession/local.py Log: Pass a config object deeper in hierarchy Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Wed Jan 24 18:30:29 2007 @@ -37,7 +37,7 @@ raise NotImplementedError() def apigen_runner(item, session, reporter): - r = RunExecutor(item, reporter=reporter) + r = RunExecutor(item, reporter=reporter, config=session.config) session.tracer.start_tracing() retval = plain_runner(item, session, reporter) session.tracer.end_tracing() From hpk at codespeak.net Wed Jan 24 18:48:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 18:48:14 +0100 (CET) Subject: [py-svn] r37287 - py/trunk/py/test Message-ID: <20070124174814.8979910069@code0.codespeak.net> Author: hpk Date: Wed Jan 24 18:48:13 2007 New Revision: 37287 Modified: py/trunk/py/test/session.py Log: don't eat exception, simply use finally Modified: py/trunk/py/test/session.py ============================================================================== --- py/trunk/py/test/session.py (original) +++ py/trunk/py/test/session.py Wed Jan 24 18:48:13 2007 @@ -42,13 +42,12 @@ try: self.header(colitems) try: - for colitem in colitems: - self.runtraced(colitem) - except KeyboardInterrupt: - raise - except: - self.footer(colitems) - else: + try: + for colitem in colitems: + self.runtraced(colitem) + except KeyboardInterrupt: + raise + finally: self.footer(colitems) except Exit, ex: pass From hpk at codespeak.net Wed Jan 24 18:56:02 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 18:56:02 +0100 (CET) Subject: [py-svn] r37288 - py/trunk/py/doc Message-ID: <20070124175602.DA69C10069@code0.codespeak.net> Author: hpk Date: Wed Jan 24 18:56:02 2007 New Revision: 37288 Modified: py/trunk/py/doc/TODO.txt Log: added some RSession related TODOs Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Wed Jan 24 18:56:02 2007 @@ -106,9 +106,12 @@ (done) -distributed testing ----------------------- +distributed testing / RSession +------------------------------------ +* cleanup initialisation of config / get rid of pkgdir +* unify option names (dist_*) +* (optional) see if more of py/test/session.py's Session can be reused code quality ----------------- From hpk at codespeak.net Wed Jan 24 20:16:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 20:16:28 +0100 (CET) Subject: [py-svn] r37289 - py/trunk/py/doc Message-ID: <20070124191628.2256410069@code0.codespeak.net> Author: hpk Date: Wed Jan 24 20:16:26 2007 New Revision: 37289 Modified: py/trunk/py/doc/TODO.txt Log: todotodo Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Wed Jan 24 20:16:26 2007 @@ -68,21 +68,18 @@ APIGEN / source viewer ------------------------------------- -* make py.test --apigen=PATH_TO_SCRIPT +* (DONE) make py.test --apigen=PATH_TO_SCRIPT collect tracing information and call the apigen script to produce api and source code documentation - (done) - * deploy the above "py.test --apigen" run on codespeak regularly, determine directory locations and URL namespace design. -* integrate rest directive into py/documentation/conftest.py +* (DONE, XXX functions/methods?) integrate rest directive into + py/documentation/conftest.py with help code from py.__.rest.directive.... make sure that the txt files in py/documentation/ use it - (done XXX functions/methods) - testing ----------- @@ -94,18 +91,11 @@ - running "py.test --looponfailing" - running "py.test" distributed on some hosts -* consider --box on trunk (XXX guido - find tests that depend on each other) - -* tests should not create any tempdirectories in the source code base +* make --box run on the trunk - - (done) - -* try to be as 2.2 compatible as possible +* (done) try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) - (done) - distributed testing / RSession ------------------------------------ From hpk at codespeak.net Wed Jan 24 20:18:47 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 20:18:47 +0100 (CET) Subject: [py-svn] r37290 - in py/trunk/py: doc test test/testing Message-ID: <20070124191847.68BFC10069@code0.codespeak.net> Author: hpk Date: Wed Jan 24 20:18:46 2007 New Revision: 37290 Modified: py/trunk/py/doc/test.txt py/trunk/py/test/config.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/testing/test_config.py Log: * allow only CAPITAL letters for short options * documentation and test for it * streamlined documentation a bit Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Wed Jan 24 20:18:46 2007 @@ -485,6 +485,28 @@ XXX +adding custom options ++++++++++++++++++++++++ + +To register a project-specific command line option +you may have the following code within a ``conftest.py`` file:: + + import py + Option = py.test.config.Option + option = py.test.config.addoptions("pypy options", + Option('-V', '--view', action="store_true", dest="view", default=False, + help="view translation tests' flow graphs with Pygame"), + ) + +and you can then access ``option.view`` like this:: + + if option.view: + print "view this!" + +The option will be available if you type ``py.test -h`` +Note that you may only register upper case short +options. ``py.test`` reserves all lower +case short options for its own cross-project usage. customizing the collecting and running process ----------------------------------------------- @@ -671,7 +693,7 @@ * **`dist_hosts`**: a required list of ssh addresses (which each may include a path, default path is: ``$HOME/pytestcache-HOSTNAME``) -* `dist_rsync_roots` - a list of packages to copy to the remote machines. +* `dist_rsyncroots` - a list of packages to copy to the remote machines. * `dist_remotepython` - the remote python executable to run. * `dist_nicelevel` - process priority of remote nodes. * `dist_boxing` - will run each single test in a separate process @@ -681,7 +703,7 @@ Sample configuration:: dist_hosts = ['localhost', 'user at someserver:/tmp/somedir'] - dist_rsync_roots = ['pypy', 'py'] + dist_rsyncroots = ['pypy', 'py'] dist_remotepython = 'python2.4' dist_nicelevel = 10 dist_boxing = True Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Wed Jan 24 20:18:46 2007 @@ -2,6 +2,7 @@ import py from conftesthandle import Conftest +from py.__.test.defaultconftest import adddefaultoptions optparse = py.compat.optparse @@ -42,8 +43,8 @@ assert not self._initialized, ( "can only parse cmdline args once per Config object") self._initialized = True + adddefaultoptions(self) self.conftest.setinitial(args) - self.conftest.rget('adddefaultoptions')() args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) @@ -89,6 +90,16 @@ """ add a named group of options to the current testing session. This function gets invoked during testing session initialization. """ + for spec in specs: + for shortopt in spec._short_opts: + if not shortopt.isupper(): + raise ValueError( + "custom options must be capital letter " + "got %r" %(spec,) + ) + return self._addoptions(groupname, *specs) + + def _addoptions(self, groupname, *specs): optgroup = optparse.OptionGroup(self._parser, groupname) optgroup.add_options(specs) self._parser.add_option_group(optgroup) Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Wed Jan 24 20:18:46 2007 @@ -28,9 +28,9 @@ # =================================================== -Option = py.test.config.Option -def adddefaultoptions(): - py.test.config.addoptions('general options', +def adddefaultoptions(config): + Option = config.Option + config._addoptions('general options', Option('-v', '--verbose', action="count", dest="verbose", default=0, help="increase verbosity."), @@ -68,7 +68,7 @@ help="trace considerations of conftest.py files."), ) - py.test.config.addoptions('EXPERIMENTAL options', + config._addoptions('EXPERIMENTAL options', Option('-f', '--looponfailing', action="store_true", dest="looponfailing", default=False, help="loop on failing test set."), 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 Wed Jan 24 20:18:46 2007 @@ -17,20 +17,38 @@ option.tdest = True Option = py.test.config.Option option = py.test.config.addoptions("testing group", - Option('-g', '--glong', action="store", default=42, + Option('-G', '--glong', action="store", default=42, type="int", dest="gdest", help="g value."), # XXX note: special case, option without a destination - Option('-t', '--tlong', action="callback", callback=_callback, + Option('-T', '--tlong', action="callback", callback=_callback, help='t value'), ) """)) old = o.chdir() try: - config = py.test.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.test.ensuretemp('test_config_cmdline_options_only_lowercase') + o.ensure("conftest.py").write(py.code.Source(""" + import py + 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.test.raises(ValueError, """ + py.test.config._reparse(['-g', '17']) + """) + finally: + old.chdir() + def test_parsing_again_fails(): dir = py.test.ensuretemp("parsing_again_fails") config = py.test.config._reparse([str(dir)]) @@ -160,10 +178,10 @@ import py Option = py.test.config.Option option = py.test.config.addoptions("testing group", - Option('-g', '--glong', action="store", default=42, + Option('-G', '--glong', action="store", default=42, type="int", dest="gdest", help="g value.")) """)) - config = py.test.config._reparse([tmp, "-g", "11"]) + config = py.test.config._reparse([tmp, "-G", "11"]) assert config.option.gdest == 11 repr = config.make_repr(conftestnames=[]) config = py.test.config._reparse([tmp.dirpath()]) From hpk at codespeak.net Wed Jan 24 20:35:24 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 20:35:24 +0100 (CET) Subject: [py-svn] r37291 - py/trunk/py/doc Message-ID: <20070124193524.66BA110069@code0.codespeak.net> Author: hpk Date: Wed Jan 24 20:35:23 2007 New Revision: 37291 Modified: py/trunk/py/doc/TODO.txt Log: seediff Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Wed Jan 24 20:35:23 2007 @@ -91,9 +91,14 @@ - running "py.test --looponfailing" - running "py.test" distributed on some hosts +* see why startcapture() does not use FD-based + "py.io.OutErrCapture" to isolate standard output. + use that check if all py and PyPy tests pass + as good as they do without. + * make --box run on the trunk -* (done) try to be as 2.2 compatible as possible +* (DONE more or less) try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) distributed testing / RSession @@ -117,12 +122,12 @@ py.test ------- -* adjust py.test documentation to reflect new - collector/session architecture (mostly done) +* (needs review) adjust py.test documentation to reflect new + collector/session architecture -* document py.test's conftest.py approach (somewhat done) +* (in-progress) document py.test's conftest.py approach -* py.test fails to parse strangely formatted code after assertion failure +* (postponed, likely) py.test fails to parse strangely formatted code after assertion failure Missing docstrings ------------------ From fijal at codespeak.net Wed Jan 24 21:23:07 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 21:23:07 +0100 (CET) Subject: [py-svn] r37292 - py/trunk/py/test/rsession Message-ID: <20070124202307.C0CE71006E@code0.codespeak.net> Author: fijal Date: Wed Jan 24 21:23:05 2007 New Revision: 37292 Modified: py/trunk/py/test/rsession/reporter.py Log: SkippedTryiter should be reporter more like in terminal.py Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Wed Jan 24 21:23:05 2007 @@ -258,7 +258,9 @@ def report_SkippedTryiter(self, event): #self.show_item(event.item, False) - self.out.write("- skipped (%s)" % event.excinfo.value) + #self.out.write("- skipped (%s)" % event.excinfo.value) + self.out.write("s") + self.skipped_tests_outcome.append(event) def report_FailedTryiter(self, event): #self.show_item(event.item, False) From fijal at codespeak.net Wed Jan 24 21:25:18 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 21:25:18 +0100 (CET) Subject: [py-svn] r37293 - py/trunk/py/test/rsession Message-ID: <20070124202518.7272D1006E@code0.codespeak.net> Author: fijal Date: Wed Jan 24 21:25:17 2007 New Revision: 37293 Modified: py/trunk/py/test/rsession/reporter.py Log: Another aproximation :-] Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Wed Jan 24 21:25:17 2007 @@ -258,9 +258,11 @@ def report_SkippedTryiter(self, event): #self.show_item(event.item, False) - #self.out.write("- skipped (%s)" % event.excinfo.value) - self.out.write("s") - self.skipped_tests_outcome.append(event) + if isinstance(event.item, py.test.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) From hpk at codespeak.net Wed Jan 24 21:25:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 21:25:50 +0100 (CET) Subject: [py-svn] r37294 - py/trunk/py/doc Message-ID: <20070124202550.79B1410075@code0.codespeak.net> Author: hpk Date: Wed Jan 24 21:25:49 2007 New Revision: 37294 Modified: py/trunk/py/doc/TODO.txt Log: seediff Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Wed Jan 24 21:25:49 2007 @@ -101,6 +101,9 @@ * (DONE more or less) try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) +* have all sessions check their options via Session.fixoptions() + and have session-particular tests and checks accordingly. + distributed testing / RSession ------------------------------------ From guido at codespeak.net Wed Jan 24 22:04:20 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 22:04:20 +0100 (CET) Subject: [py-svn] r37297 - py/trunk/py/apigen/testing Message-ID: <20070124210420.3D95010075@code0.codespeak.net> Author: guido Date: Wed Jan 24 22:04:18 2007 New Revision: 37297 Modified: py/trunk/py/apigen/testing/test_apigen_example.py Log: Fixed dependency on order in the function argument formatting tests. 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 Wed Jan 24 22:04:18 2007 @@ -127,16 +127,25 @@ snippet = apb.build_callable_view('main.sub.func') html = snippet.unicode() print html - run_string_sequence_test(html, [ - 'arg1 : AnyOf(', - 'href="', - 'Class SomeClass', - 'Int>', - 'return value:', - '<None>', - 'source: %s' % (self.fs_root.join('pkg/func.py'),), - 'def func(arg1):', - ]) + # XXX somewhat grokky tests because the order of the items may change + assert 'arg1: AnyOf(' in html + pos1 = html.find('arg1: AnyOf(') + assert pos1 > -1 + pos2 = html.find('href="', pos1) + assert pos2 > pos1 + pos3 = html.find('Class SomeClass', pos2) + assert pos3 > pos2 + pos4 = html.find('Int>', pos1) + assert pos4 > pos1 + pos5 = html.find('return value:', pos4) + assert pos5 > pos4 and pos5 > pos3 + pos6 = html.find('<None>', pos5) + assert pos6 > pos5 + pos7 = html.find('source: %s' % (self.fs_root.join('pkg/func.py'),), + pos6) + assert pos7 > pos6 + pos8 = html.find('def func(arg1):', pos7) + assert pos8 > pos7 _checkhtmlsnippet(html) def test_build_function_pages(self): From fijal at codespeak.net Wed Jan 24 22:04:59 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 22:04:59 +0100 (CET) Subject: [py-svn] r37298 - py/trunk/py/test Message-ID: <20070124210459.31E991006E@code0.codespeak.net> Author: fijal Date: Wed Jan 24 22:04:57 2007 New Revision: 37298 Modified: py/trunk/py/test/representation.py Log: (fijal, hpk) - bugfix Modified: py/trunk/py/test/representation.py ============================================================================== --- py/trunk/py/test/representation.py (original) +++ py/trunk/py/test/representation.py Wed Jan 24 22:04:57 2007 @@ -6,6 +6,7 @@ """ import py +from py.__.code import safe_repr class Presenter(object): """ Class used for presentation of various objects, @@ -84,7 +85,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 = py.__.code.safe_repr._repr(value) + 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)) From guido at codespeak.net Wed Jan 24 22:05:19 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 22:05:19 +0100 (CET) Subject: [py-svn] r37299 - py/trunk/py/apigen Message-ID: <20070124210519.8060810076@code0.codespeak.net> Author: guido Date: Wed Jan 24 22:05:18 2007 New Revision: 37299 Modified: py/trunk/py/apigen/htmlgen.py Log: Removed stupid space. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Jan 24 22:05:18 2007 @@ -539,7 +539,7 @@ def build_sig_value_description(self, name, _type): l = self.process_type_link(_type) items = [] - next = "%s : " % name + next = "%s: " % name for item in l: if isinstance(item, str): next += item From fijal at codespeak.net Wed Jan 24 22:05:34 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 22:05:34 +0100 (CET) Subject: [py-svn] r37300 - in py/trunk/py/test: . rsession rsession/testing Message-ID: <20070124210534.E9B0710077@code0.codespeak.net> Author: fijal Date: Wed Jan 24 22:05:33 2007 New Revision: 37300 Modified: py/trunk/py/test/config.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_rsession.py Log: Make clean complaints about lack of disthosts and call fixoptions if present. Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Wed Jan 24 22:05:33 2007 @@ -123,7 +123,9 @@ """ return an initialized session object. """ cls = self._getsessionclass() session = cls(self) - #session.fixoptions() + # XXX: all sessions should have one + if hasattr(session, 'fixoptions'): + session.fixoptions() return session def _getsessionclass(self): Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Wed Jan 24 22:05:33 2007 @@ -138,6 +138,22 @@ class RSession(AbstractSession): """ Remote version of session """ + def fixoptions(self): + config = self.config + try: + config.getvalue('disthosts') + except KeyError: + print "You're trying to run RSession without disthosts specified" + print "you need to specify it in your conftest.py (ie. ~/conftest.py)" + print "for example:" + print " disthosts = ['localhost'] * 4 # for 3 processors" + print " - or -" + print " disthosts = ['you at some.remote.com'] # for remote testing" + print " # with your remote ssh account" + print "http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" + print " for more info..." + raise SystemExit + def main(self, reporter=None): """ main loop for running tests. """ args = self.config.args Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Wed Jan 24 22:05:33 2007 @@ -322,3 +322,8 @@ assert events[3].host.hostname == 'h2' assert events[3].host.relpath == 'home-h2' +def test_rsession_no_disthost(): + tmpdir = py.test.ensuretemp("rsession_no_disthost") + tmpdir.ensure("conftest.py") + config = py.test.config._reparse([str(tmpdir), '-d']) + py.test.raises(SystemExit, "config.initsession()") From fijal at codespeak.net Wed Jan 24 22:10:02 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 24 Jan 2007 22:10:02 +0100 (CET) Subject: [py-svn] r37301 - py/trunk/py/doc Message-ID: <20070124211002.B4E5A1006E@code0.codespeak.net> Author: fijal Date: Wed Jan 24 22:10:01 2007 New Revision: 37301 Modified: py/trunk/py/doc/test.txt Log: No longer that way (somebody should read that document once again) Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Wed Jan 24 22:10:01 2007 @@ -657,7 +657,7 @@ Hot to use it ----------------------- -When you issue ``py.test --session=R`` then your computer becomes +When you issue ``py.test -d`` then your computer becomes the distributor of tests ("master") and will start collecting and distributing tests to several machines. The machines need to be specified in a ``conftest.py`` file. From hpk at codespeak.net Wed Jan 24 22:11:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 24 Jan 2007 22:11:25 +0100 (CET) Subject: [py-svn] r37302 - py/trunk/py/test Message-ID: <20070124211125.C52261006E@code0.codespeak.net> Author: hpk Date: Wed Jan 24 22:11:24 2007 New Revision: 37302 Modified: py/trunk/py/test/collect.py py/trunk/py/test/item.py Log: experimentally enable FD-based output capturing per-test Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Wed Jan 24 22:11:24 2007 @@ -374,11 +374,11 @@ def startcapture(self): if not self.config.option.nocapture: assert not hasattr(self, '_capture') - #self._capture = py.io.OutErrCapture() + self._capture = py.io.OutErrCapture() # XXX integrate this into py.io / refactor # execnet/py.test capturing mechanisms - from py.__.misc.simplecapture import SimpleOutErrCapture - self._capture = SimpleOutErrCapture() + #from py.__.misc.simplecapture import SimpleOutErrCapture + #self._capture = SimpleOutErrCapture() def finishcapture(self): if hasattr(self, '_capture'): Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Wed Jan 24 22:11:24 2007 @@ -33,9 +33,9 @@ def startcapture(self): if not self.config.option.nocapture: # XXX refactor integrate capturing - #self._capture = py.io.OutErrCapture() - from py.__.misc.simplecapture import SimpleOutErrCapture - self._capture = SimpleOutErrCapture() + self._capture = py.io.OutErrCapture() + #from py.__.misc.simplecapture import SimpleOutErrCapture + #self._capture = SimpleOutErrCapture() def finishcapture(self): if hasattr(self, '_capture'): From guido at codespeak.net Wed Jan 24 22:16:33 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 22:16:33 +0100 (CET) Subject: [py-svn] r37303 - py/trunk/py/apigen/testing Message-ID: <20070124211633.391711006E@code0.codespeak.net> Author: guido Date: Wed Jan 24 22:16:32 2007 New Revision: 37303 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py Log: Made functional test use an absolute path to py.test. Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Wed Jan 24 22:16:32 2007 @@ -73,12 +73,13 @@ def test_apigen_functional(): fs_root, package_name = setup_fs_project() tempdir = py.test.ensuretemp('test_apigen_functional_results') - parentdir = py.magic.autopath().dirpath().dirpath() + pydir = py.magic.autopath().dirpath().dirpath().dirpath() pkgdir = fs_root.join('pkg') try: - output = py.process.cmdexec('APIGEN_TARGET="%s" py.test ' + output = py.process.cmdexec('APIGEN_TARGET="%s" %s/bin/py.test ' '--apigen="%s/apigen.py" "%s"' % ( - tempdir, parentdir, pkgdir)) + tempdir, pydir, pydir.join('apigen'), + pkgdir)) except py.error.Error, e: print e.out raise From guido at codespeak.net Wed Jan 24 22:58:26 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 22:58:26 +0100 (CET) Subject: [py-svn] r37305 - in py/trunk/py/apigen: . testing Message-ID: <20070124215826.82C661006E@code0.codespeak.net> Author: guido Date: Wed Jan 24 22:58:21 2007 New Revision: 37305 Added: py/trunk/py/apigen/api.js Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/layout.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_example.py Log: Added some JavaScript to show and hide function information (source and such). Added: py/trunk/py/apigen/api.js ============================================================================== --- (empty file) +++ py/trunk/py/apigen/api.js Wed Jan 24 22:58:21 2007 @@ -0,0 +1,7 @@ +function showhideel(el) { + if (el.style.display == 'none') { + el.style.display = 'block'; + } else { + el.style.display = 'none'; + }; +}; Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Jan 24 22:58:21 2007 @@ -130,13 +130,16 @@ ret[ns].append(itempath) return ret -def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl): +def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl, + scripturls): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', - stylesheeturl=stylesheeturl) + stylesheeturl=stylesheeturl, scripturls=scripturls) page.set_content(contentel) here = py.magic.autopath().dirpath() style = here.join('style.css').read() outputpath.join('style.css').write(style) + apijs = here.join('api.js').read() + outputpath.join('api.js').write(apijs) return page # the PageBuilder classes take care of producing the docs (using the stuff @@ -146,8 +149,10 @@ targetpath = self.base.join(reltargetpath) stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), self.base.join('style.css').strpath) + scripturls = [relpath('%s/' % (targetpath.dirpath(),), + self.base.join('api.js').strpath)] page = wrap_page(project, title, - tag, nav, self.base, stylesheeturl) + tag, nav, self.base, stylesheeturl, scripturls) content = self.linker.call_withbase(reltargetpath, page.unicode) targetpath.ensure() targetpath.write(content.encode("utf8")) @@ -315,9 +320,11 @@ snippet = H.FunctionDescription( H.FunctionDef(localname, argdesc), - valuedesc, H.Docstring(docstring or H.em('no docstring available')), - csource, + H.div(H.a('show/hide info', + onclick='showhideel(this.parentNode.lastChild);'), + H.div(valuedesc, csource, style='display: none', + class_='funcinfo')), ) return snippet @@ -532,7 +539,7 @@ valuedesc.append(self.build_sig_value_description(name, _type)) if retval: retval = self.process_type_link(retval) - ret = H.div(H.div('where:'), valuedesc, H.div('return value:'), + ret = H.div(H.div('arguments:'), valuedesc, H.div('return value:'), retval or 'None') return ret Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Wed Jan 24 22:58:21 2007 @@ -11,6 +11,7 @@ def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') + self.scripturls = kwargs.pop('scripturls', []) super(LayoutPage, self).__init__(*args, **kwargs) def set_content(self, contentel): @@ -20,4 +21,7 @@ super(LayoutPage, self).fill() self.menubar[:] = [] self.menubar.append(self.nav) + for scripturl in self.scripturls: + self.head.append(py.xml.html.script(type="text/javascript", + src=scripturl)) Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Wed Jan 24 22:58:21 2007 @@ -63,43 +63,49 @@ } .code a { - color: blue; - font-weight: bold; - text-decoration: none; + color: blue; + font-weight: bold; + text-decoration: none; } a:hover { - color: #005; + color: #005; } - + .lineno { - text-align: right; - color: #555; - width: 3em; - padding-right: 1em; - border: 0px solid black; - border-right-width: 1px; + text-align: right; + color: #555; + width: 3em; + padding-right: 1em; + border: 0px solid black; + border-right-width: 1px; } .code { - padding-left: 1em; - white-space: pre; - font-family: monospace, Monaco; + padding-left: 1em; + white-space: pre; + font-family: monospace, Monaco; } .comment { - color: purple; + color: purple; } .string { - color: #777; + color: #777; } .keyword { - color: blue; + color: blue; } .alt_keyword { - color: green; + color: green; +} + +.funcinfo { + border: 1px solid black; + color: black; + padding: 1em; } - + 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 Wed Jan 24 22:58:21 2007 @@ -200,6 +200,7 @@ print html run_string_sequence_test(html, [ 'href="../style.css"', + 'src="../api.js"', 'href="index.html">pkg', 'href="main.html">main', 'href="main.SomeClass.html">SomeClass', From guido at codespeak.net Wed Jan 24 23:09:35 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 24 Jan 2007 23:09:35 +0100 (CET) Subject: [py-svn] r37306 - py/trunk/py/apigen Message-ID: <20070124220935.0BA301006E@code0.codespeak.net> Author: guido Date: Wed Jan 24 23:09:34 2007 New Revision: 37306 Modified: py/trunk/py/apigen/htmlgen.py Log: Fixed link (now properly has an href). Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Jan 24 23:09:34 2007 @@ -322,7 +322,9 @@ H.FunctionDef(localname, argdesc), H.Docstring(docstring or H.em('no docstring available')), H.div(H.a('show/hide info', - onclick='showhideel(this.parentNode.lastChild);'), + href='#', + onclick=('showhideel(this.parentNode.lastChild);' + 'return false;')), H.div(valuedesc, csource, style='display: none', class_='funcinfo')), ) From guido at codespeak.net Thu Jan 25 10:46:27 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 10:46:27 +0100 (CET) Subject: [py-svn] r37314 - py/trunk/py/io Message-ID: <20070125094627.3D33110064@code0.codespeak.net> Author: guido Date: Thu Jan 25 10:46:16 2007 New Revision: 37314 Modified: py/trunk/py/io/capture.py py/trunk/py/io/dupfile.py Log: Added docstring. Modified: py/trunk/py/io/capture.py ============================================================================== --- py/trunk/py/io/capture.py (original) +++ py/trunk/py/io/capture.py Thu Jan 25 10:46:16 2007 @@ -72,3 +72,4 @@ finally: out, err = so.reset() return res, out, err + Modified: py/trunk/py/io/dupfile.py ============================================================================== --- py/trunk/py/io/dupfile.py (original) +++ py/trunk/py/io/dupfile.py Thu Jan 25 10:46:16 2007 @@ -2,6 +2,14 @@ import os def dupfile(f, mode=None, buffering=0, raising=False): + """ return a new open file object that's a duplicate of f + + mode is duplicated if not given, buffering controls + buffer size (defaulting to no buffering) and raising + defines whether an exception is raised when an incompatible + file object is passed in (if raising is False, the file + object itself will be returned) + """ try: fd = f.fileno() except AttributeError: @@ -11,3 +19,4 @@ newfd = os.dup(fd) mode = mode and mode or f.mode return os.fdopen(newfd, mode, buffering) + From guido at codespeak.net Thu Jan 25 10:51:52 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 10:51:52 +0100 (CET) Subject: [py-svn] r37315 - py/trunk/py/misc/testing Message-ID: <20070125095152.999ED10064@code0.codespeak.net> Author: guido Date: Thu Jan 25 10:51:51 2007 New Revision: 37315 Modified: py/trunk/py/misc/testing/test_terminal.py Log: Skipping test on Windows (no fcntl), removing stuff from try/finally body\n(too much crap in there, obfuscates errors). Modified: py/trunk/py/misc/testing/test_terminal.py ============================================================================== --- py/trunk/py/misc/testing/test_terminal.py (original) +++ py/trunk/py/misc/testing/test_terminal.py Thu Jan 25 10:51:51 2007 @@ -1,5 +1,6 @@ import os +import py from py.__.misc.terminal_helper import get_terminal_width def test_terminal_width(): @@ -7,11 +8,14 @@ """ assert get_terminal_width() try: - def f(*args): - raise ValueError import fcntl - ioctl = fcntl.ioctl - fcntl.ioctl = f + except ImportError: + py.test.skip('fcntl not supported on this platform') + def f(*args): + raise ValueError + ioctl = fcntl.ioctl + fcntl.ioctl = f + try: cols = os.environ.get('COLUMNS', None) os.environ['COLUMNS'] = '42' assert get_terminal_width() == 41 From guido at codespeak.net Thu Jan 25 11:16:38 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 11:16:38 +0100 (CET) Subject: [py-svn] r37317 - py/trunk/py/execnet Message-ID: <20070125101638.1AC9E10061@code0.codespeak.net> Author: guido Date: Thu Jan 25 11:16:35 2007 New Revision: 37317 Modified: py/trunk/py/execnet/register.py Log: Fixed execnet tests on Windows, for some reason it uses '0.0.0.0' as IP address if '' is given as hostname Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Thu Jan 25 11:16:35 2007 @@ -145,7 +145,9 @@ given gateway. """ if hostport is None: - host, port = ('', 0) + # XXX not sure about this one... is this what's intended? it used + # to use '' for the hostname, which breaks Windows... + host, port = ('127.0.0.1', 0) else: host, port = hostport socketserverbootstrap = py.code.Source( @@ -160,8 +162,10 @@ # execute the above socketserverbootstrap on the other side channel = gateway.remote_exec(socketserverbootstrap) hostname, (realhost, realport) = channel.receive() - if not hostname: - realhost = hostname + if not hostname: + # XXX this is strange... shouldn't it be 'realhost = hostname' or + # something? + hostname = realhost #gateway._trace("remote_install received" # "port=%r, hostname = %r" %(realport, hostname)) return py.execnet.SocketGateway(realhost, realport) From guido at codespeak.net Thu Jan 25 11:25:41 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 11:25:41 +0100 (CET) Subject: [py-svn] r37318 - py/trunk/py/execnet Message-ID: <20070125102541.41A1D10061@code0.codespeak.net> Author: guido Date: Thu Jan 25 11:25:40 2007 New Revision: 37318 Modified: py/trunk/py/execnet/register.py Log: Rolling back previous change, instead applying what seems to have been the intention from the start: if '' is supplied as hostname from the server, use the IP address as reported by socket.getsockname() Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Thu Jan 25 11:25:40 2007 @@ -145,9 +145,7 @@ given gateway. """ if hostport is None: - # XXX not sure about this one... is this what's intended? it used - # to use '' for the hostname, which breaks Windows... - host, port = ('127.0.0.1', 0) + host, port = ('', 0) else: host, port = hostport socketserverbootstrap = py.code.Source( @@ -163,12 +161,10 @@ channel = gateway.remote_exec(socketserverbootstrap) hostname, (realhost, realport) = channel.receive() if not hostname: - # XXX this is strange... shouldn't it be 'realhost = hostname' or - # something? hostname = realhost #gateway._trace("remote_install received" # "port=%r, hostname = %r" %(realport, hostname)) - return py.execnet.SocketGateway(realhost, realport) + return py.execnet.SocketGateway(hostname, realport) remote_install = classmethod(remote_install) class SshGateway(PopenCmdGateway): From guido at codespeak.net Thu Jan 25 12:59:05 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 12:59:05 +0100 (CET) Subject: [py-svn] r37322 - py/trunk/py/apigen/tracer Message-ID: <20070125115905.955D710071@code0.codespeak.net> Author: guido Date: Thu Jan 25 12:59:02 2007 New Revision: 37322 Modified: py/trunk/py/apigen/tracer/description.py Log: Turned out there was a function for this already, even... Thanks, fijal! Modified: py/trunk/py/apigen/tracer/description.py ============================================================================== --- py/trunk/py/apigen/tracer/description.py (original) +++ py/trunk/py/apigen/tracer/description.py Thu Jan 25 12:59:02 2007 @@ -1,6 +1,7 @@ import py from py.__.apigen.tracer import model +from py.__.code.source import getsource import types import inspect @@ -18,20 +19,7 @@ self.filename = frame.code.raw.co_filename self.lineno = frame.lineno self.firstlineno = frame.code.firstlineno - - fname = frame.code.raw.co_filename - if fname == '': - self.source = '' - elif hasattr(fname, '__source__'): - # is a py.code.Source object - self.source = str(fname.__source__) - # XXX should we do this? - # self.filename = fname.split('<')[1].split('>')[0] - else: - try: - self.source = frame.code.source() - except IOError: - raise IOError(self.filename) + self.source = getsource(frame.code.raw) def _getval(self): return (self.filename, self.lineno) From guido at codespeak.net Thu Jan 25 13:54:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 13:54:53 +0100 (CET) Subject: [py-svn] r37325 - in py/trunk/py/apigen: . testing Message-ID: <20070125125453.1298D10063@code0.codespeak.net> Author: guido Date: Thu Jan 25 13:54:51 2007 New Revision: 37325 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/linker.py py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/testing/test_linker.py Log: Fixed problems with windows path seperators, fixed functional test exec command for win32 Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Thu Jan 25 13:54:51 2007 @@ -1,4 +1,5 @@ import py +import os import inspect from py.__.apigen.layout import LayoutPage from py.__.apigen.source import browser as source_browser @@ -167,11 +168,11 @@ def build_navigation(self, fspath): nav = H.Navigation() relpath = fspath.relto(self.projroot) - path = relpath.split('/') + path = relpath.split(os.path.sep) indent = 0 # build links to parents for i in xrange(len(path)): - dirpath = '/'.join(path[:i]) + dirpath = os.path.sep.join(path[:i]) abspath = self.projroot.join(dirpath).strpath if i == 0: text = 'root' @@ -244,15 +245,17 @@ if fspath.ext in ['.pyc', '.pyo']: continue relfspath = fspath.relto(base) - if relfspath.find('/.') > -1: + if relfspath.find('%s.' % (os.path.sep,)) > -1: # skip hidden dirs and files continue elif fspath.check(dir=True): if relfspath != '': - relfspath += '/' - reloutputpath = 'source/%sindex.html' % (relfspath,) + relfspath += os.path.sep + reloutputpath = 'source%s%sindex.html' % (os.path.sep, + relfspath) else: - reloutputpath = "source/%s.html" % (relfspath,) + reloutputpath = "source%s%s.html" % (os.path.sep, relfspath) + reloutputpath = reloutputpath.replace(os.path.sep, '/') outputpath = self.base.join(reloutputpath) self.linker.set_link(str(fspath), reloutputpath) passed.append((fspath, outputpath)) @@ -279,7 +282,7 @@ else: tag, nav = self.build_nonpython_page(fspath) title = 'sources for %s' % (fspath.basename,) - reltargetpath = outputpath.relto(self.base) + reltargetpath = outputpath.relto(self.base).replace(os.path.sep, '/') self.write_page(title, reltargetpath, project, tag, nav) class ApiPageBuilder(AbstractPageBuilder): Modified: py/trunk/py/apigen/linker.py ============================================================================== --- py/trunk/py/apigen/linker.py (original) +++ py/trunk/py/apigen/linker.py Thu Jan 25 13:54:51 2007 @@ -1,4 +1,5 @@ import py +import os html = py.xml.html def getrelfspath(dotted_name): @@ -41,7 +42,27 @@ finally: del self.fromlocation -def relpath(p1, p2, sep='/', back='..'): +def relpath(p1, p2, sep='/', back='..', normalize=True): + """ create a relative path from p1 to p2 + + sep is the seperator used, set to '\\' on windows (but only + when not using 'normalize'! see below) + + back is the string used to indicate the parent directory + + when 'normalize' is True, any backslashes (\) in the path + will be replaced with forward slashes, resulting in a consistent + output on Windows and the rest of the world (this happens before + the 'sep' argument is used, and therefore renders that useless!) + + paths to directories must end on a / (URL style) + """ + if normalize: + sep = '/' + p1 = p1.replace(os.path.sep, '/') + p2 = p2.replace(os.path.sep, '/') + # XXX would be cool to be able to do long filename expansion and drive + # letter fixes here, and such... iow: windows sucks :( if (p1.startswith(sep) ^ p2.startswith(sep)): raise ValueError("mixed absolute relative path: %r -> %r" %(p1, p2)) fromlist = p1.split(sep) Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Thu Jan 25 13:54:51 2007 @@ -75,10 +75,14 @@ tempdir = py.test.ensuretemp('test_apigen_functional_results') pydir = py.magic.autopath().dirpath().dirpath().dirpath() pkgdir = fs_root.join('pkg') + if py.std.sys.platform == 'win32': + cmd = 'set APIGEN_TARGET=%s && python "%s/bin/py.test"' % (tempdir, + pydir) + else: + cmd = 'APIGEN_TARGET="%s" "%s/bin/py.test"' % (tempdir, pydir) try: - output = py.process.cmdexec('APIGEN_TARGET="%s" %s/bin/py.test ' - '--apigen="%s/apigen.py" "%s"' % ( - tempdir, pydir, pydir.join('apigen'), + output = py.process.cmdexec('%s --apigen="%s/apigen.py" "%s"' % ( + cmd, pydir.join('apigen'), pkgdir)) except py.error.Error, e: print e.out 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 Thu Jan 25 13:54:51 2007 @@ -17,8 +17,6 @@ linker.get_target, 'py.path.local') assert relpath == 'path/local.html' - - testspec = [ 'a a/b a/b', '/a /a/b a/b', @@ -28,17 +26,17 @@ '/a/b /c/d ../c/d', 'a/b a ../a', '/a/b /a ../a', -] + 'c:\\foo\\bar c:\\foo ../foo', +] def gen_check(frompath, topath, expected): result = relpath(frompath, topath) - print "linking", frompath, "to", topath assert result == expected def test_gen_check(): for line in testspec: frompath, topath, expected = line.split() - yield gen_check, frompath, topath, expected + yield gen_check, frompath, topath, expected, def test_check_incompatible(): py.test.raises(ValueError, "relpath('/a', 'b')") From guido at codespeak.net Thu Jan 25 13:56:14 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 13:56:14 +0100 (CET) Subject: [py-svn] r37326 - py/trunk/py/test/testing Message-ID: <20070125125614.089C310063@code0.codespeak.net> Author: guido Date: Thu Jan 25 13:56:12 2007 New Revision: 37326 Modified: py/trunk/py/test/testing/test_config.py Log: Fixed exec command on win32 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 Jan 25 13:56:12 2007 @@ -87,9 +87,11 @@ assert cfg.getvalue(path=o.join('sister1'), name='x') == 2 old = o.chdir() try: - print py.process.cmdexec("py.test sister1") + pytestpath = py.magic.autopath().dirpath().dirpath().dirpath().join( + 'bin/py.test') + print py.process.cmdexec('python "%s" sister1' % (pytestpath,)) o.join('sister1').chdir() - print py.process.cmdexec("py.test") + print py.process.cmdexec('python "%s"' % (pytestpath,)) finally: old.chdir() From guido at codespeak.net Thu Jan 25 14:22:08 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 14:22:08 +0100 (CET) Subject: [py-svn] r37328 - in py/trunk/py/apigen: . testing Message-ID: <20070125132208.4774E10071@code0.codespeak.net> Author: guido Date: Thu Jan 25 14:22:04 2007 New Revision: 37328 Modified: py/trunk/py/apigen/linker.py py/trunk/py/apigen/testing/test_linker.py Log: Fixed problem with Windows paths in tests when running on Linux (and fixed relpath() api to deal with line seps in a better way). Modified: py/trunk/py/apigen/linker.py ============================================================================== --- py/trunk/py/apigen/linker.py (original) +++ py/trunk/py/apigen/linker.py Thu Jan 25 14:22:04 2007 @@ -45,22 +45,21 @@ def relpath(p1, p2, sep='/', back='..', normalize=True): """ create a relative path from p1 to p2 - sep is the seperator used, set to '\\' on windows (but only - when not using 'normalize'! see below) + sep is the seperator used for input and (depending + on the setting of 'normalize', see below) output back is the string used to indicate the parent directory when 'normalize' is True, any backslashes (\) in the path will be replaced with forward slashes, resulting in a consistent - output on Windows and the rest of the world (this happens before - the 'sep' argument is used, and therefore renders that useless!) + output on Windows and the rest of the world paths to directories must end on a / (URL style) """ if normalize: + p1 = p1.replace(sep, '/') + p2 = p2.replace(sep, '/') sep = '/' - p1 = p1.replace(os.path.sep, '/') - p2 = p2.replace(os.path.sep, '/') # XXX would be cool to be able to do long filename expansion and drive # letter fixes here, and such... iow: windows sucks :( if (p1.startswith(sep) ^ p2.startswith(sep)): 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 Thu Jan 25 14:22:04 2007 @@ -18,25 +18,25 @@ assert relpath == 'path/local.html' testspec = [ - 'a a/b a/b', - '/a /a/b a/b', - 'a b b', - '/a /b b', - 'a/b c/d ../c/d', - '/a/b /c/d ../c/d', - 'a/b a ../a', - '/a/b /a ../a', - 'c:\\foo\\bar c:\\foo ../foo', + 'a a/b a/b /', + '/a /a/b a/b /', + 'a b b /', + '/a /b b /', + 'a/b c/d ../c/d /', + '/a/b /c/d ../c/d /', + 'a/b a ../a /', + '/a/b /a ../a /', + 'c:\\foo\\bar c:\\foo ../foo \\', ] -def gen_check(frompath, topath, expected): - result = relpath(frompath, topath) +def gen_check(frompath, topath, sep, expected): + result = relpath(frompath, topath, sep=sep) assert result == expected def test_gen_check(): for line in testspec: - frompath, topath, expected = line.split() - yield gen_check, frompath, topath, expected, + frompath, topath, expected, sep = line.split() + yield gen_check, frompath, topath, sep, expected def test_check_incompatible(): py.test.raises(ValueError, "relpath('/a', 'b')") From guido at codespeak.net Thu Jan 25 15:08:50 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 15:08:50 +0100 (CET) Subject: [py-svn] r37330 - py/trunk/py/doc Message-ID: <20070125140850.AD45910063@code0.codespeak.net> Author: guido Date: Thu Jan 25 15:08:48 2007 New Revision: 37330 Modified: py/trunk/py/doc/conftest.py py/trunk/py/doc/test_conftest.py Log: Replacing any non-Unix newlines with plain \n in doctests, this fixes some win32 problem. Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Thu Jan 25 15:08:48 2007 @@ -62,18 +62,20 @@ class DoctestText(py.test.Item): def run(self): - s = self.fspath.read() + # XXX quite nasty... but it works (fixes win32 issues) + s = self._normalize_linesep() l = [] prefix = '.. >>> ' mod = py.std.types.ModuleType(self.fspath.purebasename) - for line in s.split('\n'): - if line.startswith(prefix): - exec py.code.Source(line[len(prefix):]).compile() in mod.__dict__ + for line in s.split('\n'): + if line.startswith(prefix): + exec py.code.Source(line[len(prefix):]).compile() in \ + mod.__dict__ line = "" - else: + else: l.append(line) - docstring = "\n".join(l) - self.execute(mod, docstring) + docstring = "\n".join(l) + self.execute(mod, docstring) def execute(self, mod, docstring): mod.__doc__ = docstring @@ -81,6 +83,20 @@ if failed: py.test.fail("doctest %s: %s failed out of %s" %( self.fspath, failed, tot)) + + def _normalize_linesep(self): + s = self.fspath.read() + linesep = '\n' + if '\r' in s: + if '\n' not in s: + linesep = '\r' + else: + linesep = '\r\n' + print 'linesep:', repr(linesep) + s = s.replace(linesep, '\n') + self.fspath.write(s) + print 's:', repr(s) + return s class LinkCheckerMaker(py.test.collect.Collector): def run(self): Modified: py/trunk/py/doc/test_conftest.py ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py Thu Jan 25 15:08:48 2007 @@ -36,6 +36,21 @@ l2 = session.getitemoutcomepairs(py.test.Item.Skipped) assert len(l+l2) == 2 +def test_doctest_eol(): + # XXX get rid of the next line: + py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) + + ytxt = tmpdir.join('y.txt') + ytxt.write(py.code.Source(".. >>> 1 + 1\r\n 2\r\n\r\n")) + config = py.test.config._reparse([ytxt]) + session = config.initsession() + session.main() + l = session.getitemoutcomepairs(py.test.Item.Failed) + assert len(l) == 0 + l = session.getitemoutcomepairs(py.test.Item.Passed) + l2 = session.getitemoutcomepairs(py.test.Item.Skipped) + assert len(l+l2) == 2 + def test_js_ignore(): py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) tmpdir.ensure('__init__.py') From guido at codespeak.net Thu Jan 25 16:23:19 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 16:23:19 +0100 (CET) Subject: [py-svn] r37337 - py/trunk/py/test/rsession Message-ID: <20070125152319.3A99F1007B@code0.codespeak.net> Author: guido Date: Thu Jan 25 16:23:18 2007 New Revision: 37337 Modified: py/trunk/py/test/rsession/rsession.py Log: Replaced try/except with a hasattr (makes that unrelated errors are no longer obfuscated). Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Thu Jan 25 16:23:18 2007 @@ -270,20 +270,13 @@ if self.config.option.apigen: from py.__.apigen.tracer.docstorage import DocStorageAccessor apigen = py.path.local(self.config.option.apigen).pyimport() + if not hasattr(apigen, 'build'): + raise NotImplementedError("Provided script does not seem " + "to contain build function") print >>sys.stderr, 'building documentation' capture = py.io.OutErrCapture() try: - try: - apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) - except (ValueError, AttributeError): - #import traceback - #exc, e, tb = sys.exc_info() - #print '%s - %s' % (exc, e) - #print ''.join(traceback.format_tb(tb)) - #del tb - #print '-' * 79 - raise NotImplementedError("Provided script does not seem " - "to contain build function") + apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) finally: capture.reset() From fijal at codespeak.net Thu Jan 25 17:46:58 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 25 Jan 2007 17:46:58 +0100 (CET) Subject: [py-svn] r37343 - py/trunk/py/apigen Message-ID: <20070125164658.878A610077@code0.codespeak.net> Author: fijal Date: Thu Jan 25 17:46:56 2007 New Revision: 37343 Modified: py/trunk/py/apigen/apigen.py Log: Hours later and tons of debug print changes.... I HATE PYTHON IMPORTS, I HATE PYTHON IMPORTS, I HATE PYTHON IMPORTS Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Thu Jan 25 17:46:56 2007 @@ -6,19 +6,15 @@ import os import py +import sys from py.__.apigen import htmlgen from py.__.apigen import linker from py.__.apigen import project -def import_pkgdir(pkgdir): - if pkgdir.check(dir=True): - return pkgdir.join('__init__.py').getpymodule() - else: - # XXX not sure if this is ever used normally... - return pkgdir.getpymodule() - def get_documentable_items(pkgdir): - rootmod = import_pkgdir(pkgdir) + sys.path.insert(0, str(pkgdir.dirpath())) + rootmod = __import__(pkgdir.basename) + #rootmod = import_pkgdir(pkgdir) if hasattr(rootmod, '__package__'): return rootmod # XXX fix non-initpkg situations(?) From fijal at codespeak.net Thu Jan 25 18:02:04 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 25 Jan 2007 18:02:04 +0100 (CET) Subject: [py-svn] r37345 - py/trunk/py/test/terminal Message-ID: <20070125170204.2F62C1007A@code0.codespeak.net> Author: fijal Date: Thu Jan 25 18:02:01 2007 New Revision: 37345 Modified: py/trunk/py/test/terminal/terminal.py Log: Remove unneeded import Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Thu Jan 25 18:02:01 2007 @@ -3,7 +3,6 @@ from time import time as now Item = py.test.Item from py.__.test.terminal.out import getout -import py.__.code.safe_repr from py.__.test.representation import Presenter def getrelpath(source, dest): From fijal at codespeak.net Thu Jan 25 18:02:33 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 25 Jan 2007 18:02:33 +0100 (CET) Subject: [py-svn] r37346 - py/trunk/py/code Message-ID: <20070125170233.CC61B1007D@code0.codespeak.net> Author: fijal Date: Thu Jan 25 18:02:32 2007 New Revision: 37346 Modified: py/trunk/py/code/code.py Log: Add __ne__ Modified: py/trunk/py/code/code.py ============================================================================== --- py/trunk/py/code/code.py (original) +++ py/trunk/py/code/code.py Thu Jan 25 18:02:32 2007 @@ -13,7 +13,10 @@ self.name = rawcode.co_name def __eq__(self, other): - return self.raw == other.raw + return self.raw == other.raw + + def __ne__(self): + return not self == other def new(self, rec=False, **kwargs): """ return new code object with modified attributes. From fijal at codespeak.net Thu Jan 25 18:03:55 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 25 Jan 2007 18:03:55 +0100 (CET) Subject: [py-svn] r37347 - py/trunk/py/apigen/tracer/testing Message-ID: <20070125170355.A38AF1007B@code0.codespeak.net> Author: fijal Date: Thu Jan 25 18:03:54 2007 New Revision: 37347 Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py Log: Enchance the test. Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_docgen.py (original) +++ py/trunk/py/apigen/tracer/testing/test_docgen.py Thu Jan 25 18:03:54 2007 @@ -385,8 +385,8 @@ from someclass import SomeClass class SomeSubClass(SomeClass): " docstring somesubclass " - def get_somevar(self): - return self.somevar + 1 + #def get_somevar(self): + # return self.somevar + 1 """)) temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ from pkg.main.sub import func @@ -405,8 +405,7 @@ initpkg(__name__, exportdefs = { 'main.sub.func': ("./func.py", "func"), 'main.SomeClass': ('./someclass.py', 'SomeClass'), - 'main.SomeInstance': ('./someclass.py', 'SomeInstance'), - 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), + #'main.SomeInstance': ('./someclass.py', 'SomeInstance'), 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), 'other': ('./somenamespace.py', '*'), }) @@ -423,6 +422,22 @@ def test_get_initpkg_star_items(): pkg, ds = setup_pkg_docstorage() sit = ds.get_star_import_tree(pkg.other, 'pkg.other') - print sit assert sorted(sit.keys()) == ['pkg.other.baz', 'pkg.other.foo'] + t = Tracer(ds) + t.start_tracing() + pkg.main.sub.func("a1") + pkg.main.SomeClass(3).get_somevar() + pkg.main.SomeSubClass("xxx").get_somevar() + t.end_tracing() + assert isinstance(ds.descs['main.sub.func'].inputcells[0], model.SomeString) + desc = ds.descs['main.SomeClass'] + assert ds.descs['main.SomeClass.get_somevar'] is desc.fields['get_somevar'] + cell = desc.fields['get_somevar'].inputcells[0] + assert isinstance(cell, model.SomeInstance) + assert cell.classdef.cls is desc.pyobj + desc = ds.descs['main.SomeSubClass'] + assert ds.descs['main.SomeSubClass.get_somevar'] is desc.fields['get_somevar'] + cell = desc.fields['get_somevar'].inputcells[0] + assert isinstance(cell, model.SomeInstance) + assert cell.classdef.cls is desc.pyobj From fijal at codespeak.net Thu Jan 25 18:13:26 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 25 Jan 2007 18:13:26 +0100 (CET) Subject: [py-svn] r37348 - py/trunk/py/test/rsession Message-ID: <20070125171326.16EAF1007A@code0.codespeak.net> Author: fijal Date: Thu Jan 25 18:13:24 2007 New Revision: 37348 Modified: py/trunk/py/test/rsession/outcome.py Log: Fallback Modified: py/trunk/py/test/rsession/outcome.py ============================================================================== --- py/trunk/py/test/rsession/outcome.py (original) +++ py/trunk/py/test/rsession/outcome.py Thu Jan 25 18:13:24 2007 @@ -34,10 +34,13 @@ relline = lineno - tb_entry.frame.code.firstlineno path = str(tb_entry.path) #try: - if tb_style == 'long': - source = str(tb_entry.getsource()) - else: - source = str(tb_entry.getsource()).split("\n")[relline] + try: + if tb_style == 'long': + source = str(tb_entry.getsource()) + else: + source = str(tb_entry.getsource()).split("\n")[relline] + except py.error.ENOENT: + source = "[cannot get source]" name = tb_entry.frame.code.raw.co_name # XXX: Bare except. What can getsource() raise anyway? # SyntaxError, AttributeError, IndentationError for sure, check it From fijal at codespeak.net Thu Jan 25 20:59:20 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 25 Jan 2007 20:59:20 +0100 (CET) Subject: [py-svn] r37354 - py/trunk/py/test/rsession Message-ID: <20070125195920.22D0210075@code0.codespeak.net> Author: fijal Date: Thu Jan 25 20:59:17 2007 New Revision: 37354 Modified: py/trunk/py/test/rsession/local.py Log: Move tracing a bit down (performance, no change in behaviour) Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Thu Jan 25 20:59:17 2007 @@ -38,10 +38,15 @@ def apigen_runner(item, session, reporter): r = RunExecutor(item, reporter=reporter, config=session.config) + startcapture(session) + #retval = plain_runner(item, session, reporter) + r = RunExecutor(item, reporter=reporter, config=session.config) session.tracer.start_tracing() - retval = plain_runner(item, session, reporter) + outcome = r.execute() session.tracer.end_tracing() - return retval + outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) + outcome.stdout, outcome.stderr = finishcapture(session) + return outcome def exec_runner(item, session, reporter): raise NotImplementedError() From guido at codespeak.net Thu Jan 25 21:39:45 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 25 Jan 2007 21:39:45 +0100 (CET) Subject: [py-svn] r37355 - py/trunk/py/apigen/tracer/testing Message-ID: <20070125203945.4F28910068@code0.codespeak.net> Author: guido Date: Thu Jan 25 21:39:43 2007 New Revision: 37355 Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py Log: Fixing test (not sure why it was in this state?). Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_docgen.py (original) +++ py/trunk/py/apigen/tracer/testing/test_docgen.py Thu Jan 25 21:39:43 2007 @@ -427,7 +427,7 @@ t.start_tracing() pkg.main.sub.func("a1") pkg.main.SomeClass(3).get_somevar() - pkg.main.SomeSubClass("xxx").get_somevar() + pkg.main.SomeSubClass(4).get_somevar() t.end_tracing() assert isinstance(ds.descs['main.sub.func'].inputcells[0], model.SomeString) desc = ds.descs['main.SomeClass'] From fijal at codespeak.net Fri Jan 26 12:50:03 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 26 Jan 2007 12:50:03 +0100 (CET) Subject: [py-svn] r37380 - in py/trunk/py/test/rsession: . testing Message-ID: <20070126115003.12F841008A@code0.codespeak.net> Author: fijal Date: Fri Jan 26 12:49:59 2007 New Revision: 37380 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/slave.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_rsession.py py/trunk/py/test/rsession/testing/test_slave.py Log: This is rather huge checkin of a rsession refactoring. Got rid of most of the pkgdir and changed to use get_collector_trail. Some tests need to be rewritten or killed, right now they're skips. There are no checks (yet) whether this works with topdir being something else than previous pkgdir, we'll see... Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Fri Jan 26 12:49:59 2007 @@ -74,15 +74,14 @@ self.create_gateways = create_gateways class HostManager(object): - def __init__(self, sshhosts, config, pkgdir, options=HostOptions()): + def __init__(self, sshhosts, config, options=HostOptions()): self.sshhosts = sshhosts - self.pkgdir = pkgdir self.config = config self.options = options if not options.create_gateways: self.prepare_gateways = self.prepare_dummy_gateways - assert pkgdir.join("__init__.py").check(), ( - "%s probably wrong" %(pkgdir,)) + #assert pkgdir.join("__init__.py").check(), ( + # "%s probably wrong" %(pkgdir,)) def prepare_dummy_gateways(self): for host in self.sshhosts: @@ -113,7 +112,7 @@ gw = py.execnet.PopenGateway() else: gw = py.execnet.PopenGateway(python=self.options.remote_python) - host.relpath = str(self.pkgdir.dirpath()) + host.relpath = str(self.config.topdir) return gw def prepare_gateways(self): @@ -159,15 +158,14 @@ rsync.add_target(host.gw, host.relpath, done) if not self.options.do_sync: return # for testing only - rsync.send(self.pkgdir.dirpath()) + rsync.send(self.config.topdir) # hosts ready return self.setup_nodes(reporter, done_dict) def setup_nodes(self, reporter, done_dict): nodes = [] for host in self.sshhosts: - ch = setup_slave(host.gw, os.path.join(host.relpath,\ - self.pkgdir.basename), self.config) + ch = setup_slave(host.gw, host.relpath, self.config) nodes.append(MasterNode(ch, reporter, done_dict)) return nodes Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Fri Jan 26 12:49:59 2007 @@ -29,8 +29,8 @@ self.channel.send(42) else: self.pending.insert(0, item) - itemspec = item.listnames()[1:] - self.channel.send(itemspec) + #itemspec = item.listnames()[1:] + self.channel.send(item.config.get_collector_trail(item)) # send start report self.reporter(report.SendItem(self.channel, item)) Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Fri Jan 26 12:49:59 2007 @@ -16,9 +16,8 @@ import sys class AbstractReporter(object): - def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)): + def __init__(self, config, hosts): self.config = config - self.pkgdir = pkgdir self.hosts = hosts self.failed_tests_outcome = [] self.skipped_tests_outcome = [] @@ -296,7 +295,7 @@ # print names relative to current workdir name = "/".join(item.listnames()) local = str(py.path.local()) - d = str(self.pkgdir.dirpath().dirpath()) + d = str(self.config.topdir) if local.startswith(d): local = local[len(d) + 1:] if local and name.startswith(local): Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Fri Jan 26 12:49:59 2007 @@ -26,23 +26,6 @@ self.config = config self.optimise_localhost = optimise_localhost - def make_colitems(paths, baseon): - # we presume that from the base we can simply get to - # the target paths by joining the basenames - res = [] - for x in paths: - x = py.path.local(x) - current = py.test.collect.Directory(baseon) - relparts = x.relto(baseon).split(x.sep) - assert relparts - for part in relparts: - next = current.join(part) - assert next is not None, (current, part) - current = next - res.append(current) - return res - make_colitems = staticmethod(make_colitems) - def getpkgdir(path): path = py.path.local(path) pkgpath = path.pypkgpath() @@ -80,7 +63,7 @@ from py.__.test.rsession.rest import RestReporter reporter_class = RestReporter if arg: - reporter_instance = reporter_class(self.config, sshhosts, self.getpkgdir(arg)) + reporter_instance = reporter_class(self.config, sshhosts) else: reporter_instance = reporter_class(self.config, sshhosts) reporter = reporter_instance.report @@ -165,17 +148,16 @@ reporter(report.TestStarted(sshhosts)) - pkgdir = self.getpkgdir(args[0]) done_dict = {} hostopts = HostOptions(rsync_roots=rsync_roots, remote_python=remotepython, optimise_localhost=self.optimise_localhost) - hostmanager = HostManager(sshhosts, self.config, pkgdir, hostopts) + hostmanager = HostManager(sshhosts, self.config, hostopts) try: nodes = hostmanager.init_hosts(reporter, done_dict) reporter(report.RsyncFinished()) try: - self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict) + self.dispatch_tests(nodes, reporter, checkfun, done_dict) except (KeyboardInterrupt, SystemExit): print >>sys.stderr, "C-c pressed waiting for gateways to teardown..." channels = [node.channel for node in nodes] @@ -213,8 +195,8 @@ remotepython = self.config.getvalue("dist_remotepython") return sshhosts, remotepython, rsync_roots - def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict): - colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) + def dispatch_tests(self, nodes, reporter, checkfun, done_dict): + colitems = self.config.getcolitems() keyword = self.config.option.keyword itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) @@ -246,7 +228,7 @@ reporter(report.TestStarted(sshhosts)) pkgdir = self.getpkgdir(args[0]) - colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) + colitems = self.config.getcolitems() reporter(report.RsyncFinished()) if runner is None: @@ -276,7 +258,17 @@ print >>sys.stderr, 'building documentation' capture = py.io.OutErrCapture() try: - apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) + try: + apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) + except AttributeError: + import traceback + exc, e, tb = sys.exc_info() + print '%s - %s' % (exc, e) + print ''.join(traceback.format_tb(tb)) + del tb + print '-' * 79 + raise NotImplementedError("Provided script does not seem " + "to contain build function") finally: capture.reset() @@ -292,8 +284,20 @@ else: self.docstorage = DocStorage().from_pkg(items) except ImportError: + import traceback + exc, e, tb = sys.exc_info() + print '%s - %s' % (exc, e) + print ''.join(traceback.format_tb(tb)) + del tb + print '-' * 79 raise ImportError("Provided script cannot be imported") except (ValueError, AttributeError): + import traceback + exc, e, tb = sys.exc_info() + print '%s - %s' % (exc, e) + print ''.join(traceback.format_tb(tb)) + del tb + print '-' * 79 raise NotImplementedError("Provided script does not seem " "to contain get_documentable_items") self.tracer = Tracer(self.docstorage) Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Fri Jan 26 12:49:59 2007 @@ -45,14 +45,15 @@ self.lock.release() class SlaveNode(object): - def __init__(self, rootcollector, config, pidinfo, executor=AsyncExecutor): - self.rootcollector = rootcollector + def __init__(self, config, pidinfo, executor=AsyncExecutor): + #self.rootcollector = rootcollector self.config = config self.executor = executor self.pidinfo = pidinfo def execute(self, itemspec): - item = self.rootcollector.getitembynames(itemspec) + #item = self.rootcollector.getitembynames(itemspec) + item = self.config._getcollector(itemspec) #if isinstance(item, py.test.Function): # ex = Executor(item.obj, setup=item.setup) #else: @@ -84,7 +85,7 @@ if node is not None: return node col = py.test.collect.Directory(str(py.path.local(path).join(item[0]))) - node = nodes[item[0]] = SlaveNode(col, config, pidinfo) + node = nodes[item[0]] = SlaveNode(config, pidinfo) return node while 1: nextitem = receive() @@ -92,7 +93,7 @@ break try: node = getnode(nextitem) - res = node.run(nextitem[1:]) + res = node.run(nextitem) except py.test.Item.Skipped, s: send(Outcome(skipped=str(s)).make_repr()) except: @@ -120,10 +121,8 @@ return callback import os, sys - pkgdir = channel.receive() # path is ready - config_repr = channel.receive() - basedir = os.path.dirname(pkgdir) - pkgname = os.path.basename(pkgdir) + basedir = channel.receive() # path is ready + config_repr = channel.receive() # setup defaults... sys.path.insert(0, basedir) import py @@ -133,11 +132,8 @@ config.merge_repr(config_repr) else: config.initdirect(basedir, config_repr) - #config.conftest.lget('adddefaultoptions')() if not config.option.nomagic: py.magic.invoke(assertion=1) - mod = __import__(pkgname) - assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir) from py.__.test.rsession.slave import slave_main, PidInfo queue = py.std.Queue.Queue() pidinfo = PidInfo() Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Fri Jan 26 12:49:59 2007 @@ -19,7 +19,8 @@ # bind an empty config config = py.test.config._reparse([]) config._overwrite('dist_taskspernode', 10) - mod.pkgdir = py.path.local(py.__file__).dirpath() + mod.pkgdir = py.path.local(py.__file__).dirpath().dirpath() + mod.rootcol = py.test.collect.Directory(mod.pkgdir) class DummyGateway(object): def __init__(self): @@ -38,6 +39,7 @@ self.sent.append(item) def test_masternode(): + py.test.skip("cannot send non-fs items nowadays") try: raise ValueError() except ValueError: @@ -57,6 +59,7 @@ assert not received[1].outcome.passed def test_unique_nodes(): + py.test.skip("cannot send non-fs items nowadays") ch = DummyChannel() reportlist = [] mnode = MasterNode(ch, reportlist.append, {}) @@ -91,7 +94,8 @@ gw = py.execnet.PopenGateway() config = py.test.config._reparse([]) channel = setup_slave(gw, pkgdir, config) - channel.send(funcpass_spec) + spec = rootcol.getitembynames(funcpass_spec).get_collector_trail() + channel.send(spec) output = ReprOutcome(channel.receive()) assert output.passed channel.send(42) @@ -118,7 +122,6 @@ return mn master_nodes = [open_gw(), open_gw(), open_gw()] - rootcol = py.test.collect.Directory(pkgdir.dirpath()) funcpass_item = rootcol.getitembynames(funcpass_spec) funcfail_item = rootcol.getitembynames(funcfail_spec) itemgenerator = iter([funcfail_item] + @@ -147,7 +150,7 @@ return mn, gw, channel mn, gw, channel = open_gw() - rootcol = py.test.collect.Directory(pkgdir.dirpath()) + rootcol = py.test.collect.Directory(pkgdir) funchang_item = rootcol.getitembynames(funchang_spec) mn.send(funchang_item) mn.send(StopIteration) Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Fri Jan 26 12:49:59 2007 @@ -18,7 +18,8 @@ def test_setup_non_existing_hosts(): setup_events = [] hosts = [HostInfo("alskdjalsdkjasldkajlsd")] - hm = HostManager(hosts, None, pkgdir) + config = py.test.config._reparse([]) + hm = HostManager(hosts, config) cmd = "hm.init_hosts(setup_events.append)" py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) #assert setup_events @@ -36,23 +37,23 @@ fn = tmp.ensure("hello.py") assert RSession.getpkgdir(fn) == fn -def test_make_colitems(): - one = pkgdir.join("initpkg.py") - two = pkgdir.join("path", "__init__.py") - - cols = RSession.make_colitems([one, two], baseon=pkgdir) - assert len(cols) == 2 - col_one, col_two = cols - assert col_one.listnames() == ["py", "initpkg.py"] - assert col_two.listnames() == ["py", "path", "__init__.py"] - - cols = RSession.make_colitems([one, two], baseon=pkgdir.dirpath()) - assert len(cols) == 2 - col_one, col_two = cols - assert col_one.listnames() == [pkgdir.dirpath().basename, - "py", "initpkg.py"] - assert col_two.listnames() == [pkgdir.dirpath().basename, - "py", "path", "__init__.py"] +#def test_make_colitems(): +# one = pkgdir.join("initpkg.py") +# two = pkgdir.join("path", "__init__.py")# + +# cols = RSession.make_colitems([one, two], baseon=pkgdir) +# assert len(cols) == 2 +# col_one, col_two = cols +# assert col_one.listnames() == ["py", "initpkg.py"] +# assert col_two.listnames() == ["py", "path", "__init__.py"]# +# +# cols = RSession.make_colitems([one, two], baseon=pkgdir.dirpath()) +# assert len(cols) == 2 +# col_one, col_two = cols +# assert col_one.listnames() == [pkgdir.dirpath().basename, +# "py", "initpkg.py"] +# assert col_two.listnames() == [pkgdir.dirpath().basename, +# "py", "path", "__init__.py"] def test_example_tryiter(): events = [] @@ -161,7 +162,7 @@ config = py.test.config._reparse([]) opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) - hm = HostManager(hosts, config, pkgdir, opts) + hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(setup_events.append) hm.teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -188,7 +189,7 @@ config = py.test.config._reparse([]) opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) - hm = HostManager(hosts, config, pkgdir, opts) + hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(allevents.append) from py.__.test.rsession.testing.test_executor \ @@ -235,7 +236,7 @@ defaultconftestnames.append("custom") try: opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) - hm = HostManager(hosts, config, pkgdir, opts) + hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(allevents.append) rootcol = py.test.collect.Directory(pkgdir.dirpath()) @@ -309,7 +310,7 @@ parse_directories(hosts) config = py.test.config._reparse([]) opts = HostOptions(do_sync=False, create_gateways=False) - hm = HostManager(hosts, config, pkgdir, opts) + hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(testevents.append) events = [i for i in testevents if isinstance(i, report.HostRSyncing)] assert len(events) == 4 Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Fri Jan 26 12:49:59 2007 @@ -12,6 +12,7 @@ def setup_module(module): module.rootdir = py.path.local(py.__file__).dirpath().dirpath() + module.rootcol = py.test.collect.Directory(rootdir) # ---------------------------------------------------------------------- # inlined testing functions used below @@ -53,15 +54,15 @@ from py.__.test.rsession.executor import RunExecutor def gettestnode(): - rootcol = py.test.collect.Directory(rootdir) config = py.test.config._reparse([rootdir]) pidinfo = PidInfo() - node = SlaveNode(rootcol, config, pidinfo, executor=RunExecutor) + node = SlaveNode(config, pidinfo, executor=RunExecutor) return node def test_slave_run_passing(): node = gettestnode() - outcome = node.execute(funcpass_spec) + item = rootcol.getitembynames(funcpass_spec) + outcome = node.execute(item.get_collector_trail()) assert outcome.passed assert not outcome.setupfailure @@ -72,7 +73,8 @@ def test_slave_run_failing(): node = gettestnode() - outcome = node.execute(funcfail_spec) + item = rootcol.getitembynames(funcfail_spec) + outcome = node.execute(item.get_collector_trail()) assert not outcome.passed assert not outcome.setupfailure assert len(outcome.excinfo.traceback) == 1 @@ -86,7 +88,8 @@ def test_slave_run_skipping(): node = gettestnode() - outcome = node.execute(funcskip_spec) + item = rootcol.getitembynames(funcskip_spec) + outcome = node.execute(item.get_collector_trail()) assert not outcome.passed assert outcome.skipped @@ -97,7 +100,8 @@ def test_slave_run_failing_wrapped(): node = gettestnode() - repr_outcome = node.run(funcfail_spec) + item = rootcol.getitembynames(funcfail_spec) + repr_outcome = node.run(item.get_collector_trail()) outcome = ReprOutcome(repr_outcome) assert not outcome.passed assert not outcome.setupfailure @@ -105,9 +109,11 @@ def test_slave_main_simple(): res = [] + failitem = rootcol.getitembynames(funcfail_spec) + passitem = rootcol.getitembynames(funcpass_spec) q = [None, - funcpass_spec, - funcfail_spec + passitem.get_collector_trail(), + failitem.get_collector_trail() ] config = py.test.config._reparse([]) pidinfo = PidInfo() @@ -118,9 +124,11 @@ def test_slave_run_different_stuff(): node = gettestnode() - node.run("py doc log.txt".split()) + node.run(rootcol.getitembynames("py doc log.txt".split()). + get_collector_trail()) def test_slave_setup_fails_on_import_error(): + py.test.skip("WIP") tmp = py.test.ensuretemp("slavesetup") config = py.test.config._reparse([tmp]) class C: @@ -139,6 +147,12 @@ def close(self): pass + + def setcallback(self, cb): + pass + + def send(self, x): + pass try: exec py.code.Source(setup, "setup()").compile() in { @@ -149,6 +163,7 @@ py.test.fail("missing exception") def test_slave_setup_exit(): + py.test.skip("WIP") tmp = py.test.ensuretemp("slaveexit") tmp.ensure("__init__.py") q = py.std.Queue.Queue() @@ -187,6 +202,7 @@ py.test.fail("Did not exit") def test_slave_setup_fails_on_missing_pkg(): + py.test.skip("WIP") tmp = py.test.ensuretemp("slavesetup2") config = py.test.config._reparse([tmp]) x = tmp.ensure("sometestpackage", "__init__.py") From fijal at codespeak.net Fri Jan 26 12:50:23 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 26 Jan 2007 12:50:23 +0100 (CET) Subject: [py-svn] r37381 - py/trunk/py/test Message-ID: <20070126115023.EB19F1008F@code0.codespeak.net> Author: fijal Date: Fri Jan 26 12:50:22 2007 New Revision: 37381 Modified: py/trunk/py/test/collect.py Log: Add a shortcut. Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Fri Jan 26 12:50:22 2007 @@ -250,7 +250,12 @@ def finishcapture(self): return None # by default collectors don't capture output def getouterr(self): - return self.captured_out, self.captured_err + return self.captured_out, self.captured_err + + def get_collector_trail(self): + """ Shortcut + """ + return self.config.get_collector_trail(self) class FSCollector(Collector): def __init__(self, fspath, parent=None): From guido at codespeak.net Fri Jan 26 14:01:29 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 26 Jan 2007 14:01:29 +0100 (CET) Subject: [py-svn] r37383 - in py/trunk/py/apigen: . testing Message-ID: <20070126130129.9C74810070@code0.codespeak.net> Author: guido Date: Fri Jan 26 14:01:27 2007 New Revision: 37383 Modified: py/trunk/py/apigen/api.js py/trunk/py/apigen/apigen.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_apigen_functional.py Log: Added call stacks, made that method pages aren't built at all anymore, made that build_namespace_view isn't called for anything but namespaces anymore, made that parts of documents are hidden at first (with a link to 'open' them). Modified: py/trunk/py/apigen/api.js ============================================================================== --- py/trunk/py/apigen/api.js (original) +++ py/trunk/py/apigen/api.js Fri Jan 26 14:01:27 2007 @@ -1,7 +1,22 @@ function showhideel(el) { + /* show or hide the element + + sets the value of el.style.display to 'none' or 'block' depending + on the current value + */ if (el.style.display == 'none') { el.style.display = 'block'; } else { el.style.display = 'none'; }; }; + +function getnextsibling(el) { + /* return next non-text sibling (or null) */ + var node = el.nextSibling; + while (node && node.nodeType != 1) { + node = node.nextSibling; + }; + return node; +}; + Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Fri Jan 26 14:01:27 2007 @@ -37,7 +37,7 @@ ns_data = apb.prepare_namespace_pages(namespace_tree) class_names = dsa.get_class_names() - class_data, method_data = apb.prepare_class_pages(namespace_tree, + class_data = apb.prepare_class_pages(namespace_tree, class_names) function_names = dsa.get_function_names() func_data = apb.prepare_function_pages(namespace_tree, function_names) @@ -45,7 +45,7 @@ apb.build_namespace_pages(ns_data, proj) apb.build_class_pages(class_data, proj) - apb.build_method_pages(method_data, proj) + #apb.build_method_pages(method_data, proj) apb.build_function_pages(func_data, proj) spb.build_pages(source_data, proj, pkgdir) Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Fri Jan 26 14:01:27 2007 @@ -14,8 +14,11 @@ # HTML related stuff class H(html): - class Description(html.div): + class Content(html.div): style = html.Style(margin_left='15em') + + class Description(html.div): + pass class NamespaceDescription(Description): pass @@ -79,6 +82,12 @@ class ValueDescItem(html.li): pass + class CallStackDescription(Description): + pass + + class CallStackItem(html.div): + class_ = 'callstackitem' + def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect @@ -321,14 +330,23 @@ else: csource = H.SourceDef('could not get source file') + csdiv = H.div(style='display: none') + for cs, _ in self.dsa.get_function_callpoints(dotted_name): + csdiv.append(self.build_callsite(dotted_name, cs)) + callstack = H.CallStackDescription( + H.a('show/hide call sites', + href='#', + onclick='showhideel(getnextsibling(this)); return false;'), + csdiv, + ) snippet = H.FunctionDescription( H.FunctionDef(localname, argdesc), H.Docstring(docstring or H.em('no docstring available')), H.div(H.a('show/hide info', href='#', - onclick=('showhideel(this.parentNode.lastChild);' + onclick=('showhideel(getnextsibling(this));' 'return false;')), - H.div(valuedesc, csource, style='display: none', + H.div(valuedesc, csource, callstack, style='display: none', class_='funcinfo')), ) @@ -397,7 +415,7 @@ H.NamespaceDef(namespace_dotted_name), H.Docstring(docstring or H.em('no docstring available')) ) - for dotted_name in item_dotted_names: + for dotted_name in sorted(item_dotted_names): itemname = dotted_name.split('.')[-1] if is_private(itemname): continue @@ -412,26 +430,20 @@ def prepare_class_pages(self, namespace_tree, classes_dotted_names): passed = [] - methodsdata = [] - for dotted_name in classes_dotted_names: + for dotted_name in sorted(classes_dotted_names): parent_dotted_name, _ = split_of_last_part(dotted_name) try: sibling_dotted_names = namespace_tree[parent_dotted_name] except KeyError: # no siblings (built-in module or sth) sibling_dotted_names = [] - tag = self.build_class_view(dotted_name) + tag = H.Content(self.build_class_view(dotted_name)) nav = self.build_navigation(parent_dotted_name, sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) - method_dotted_names = ['%s.%s' % (dotted_name, method_name) for - method_name in - self.dsa.get_class_methods(dotted_name)] - methodsdata += self.prepare_method_pages(namespace_tree, - method_dotted_names) - return passed, methodsdata + return passed def build_class_pages(self, data, project): """ build the full api pages for a set of classes """ @@ -443,7 +455,7 @@ # XXX note that even though these pages are still built, there's no nav # pointing to them anymore... passed = [] - for dotted_name in method_dotted_names: + for dotted_name in sorted(method_dotted_names): parent_dotted_name, _ = split_of_last_part(dotted_name) module_dotted_name, _ = split_of_last_part(parent_dotted_name) sibling_dotted_names = namespace_tree[module_dotted_name] @@ -462,11 +474,11 @@ def prepare_function_pages(self, namespace_tree, method_dotted_names): passed = [] - for dotted_name in method_dotted_names: + for dotted_name in sorted(method_dotted_names): # XXX should we create a build_function_view instead? parent_dotted_name, _ = split_of_last_part(dotted_name) sibling_dotted_names = namespace_tree[parent_dotted_name] - tag = self.build_callable_view(dotted_name) + tag = H.Content(self.build_callable_view(dotted_name)) nav = self.build_navigation(parent_dotted_name, sibling_dotted_names, dotted_name) reltargetpath = "api/%s.html" % (dotted_name,) @@ -485,9 +497,14 @@ names = namespace_tree.keys() names.sort() - for dotted_name in names: + function_names = self.dsa.get_function_names() + class_names = self.dsa.get_class_names() + for dotted_name in sorted(names): + if dotted_name in function_names or dotted_name in class_names: + continue subitem_dotted_names = namespace_tree[dotted_name] - tag = self.build_namespace_view(dotted_name, subitem_dotted_names) + tag = H.Content(self.build_namespace_view(dotted_name, + subitem_dotted_names)) nav = self.build_navigation(dotted_name, subitem_dotted_names, dotted_name) if dotted_name == '': @@ -588,3 +605,42 @@ def is_in_pkg(self, sourcefile): return py.path.local(sourcefile).relto(self.projpath) + def build_callsite(self, functionname, call_site): + tbtag = self.gen_traceback(functionname, call_site) + tag = H.CallStackItem( + H.a("%s - line %s" % (call_site[0].filename, call_site[0].lineno + 1), + href='#', + onclick="showhideel(getnextsibling(this)); return false;"), + H.div(tbtag, style='display: none') + ) + return tag + + def gen_traceback(self, funcname, call_site): + tbdiv = H.div() + for line in call_site: + lineno = line.lineno - line.firstlineno + source = line.source + sourcefile = line.filename + mangled = [] + for i, sline in enumerate(str(source).split('\n')): + if i == lineno: + l = '-> %s' % (sline,) + else: + l = ' %s' % (sline,) + mangled.append(l) + if sourcefile: + linktext = '%s - line %s' % (sourcefile, line.lineno + 1) + # skip py.code.Source objects and source files outside of the + # package + if (not sourcefile.startswith('None') and + self.is_in_pkg(sourcefile)): + href = self.linker.get_lazyhref(sourcefile) + sourcelink = H.a(linktext, href=href) + else: + sourcelink = H.div(linktext) + else: + sourcelink = H.div('source unknown') + tbdiv.append(sourcelink) + tbdiv.append(H.pre('\n'.join(mangled))) + return tbdiv + 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 Fri Jan 26 14:01:27 2007 @@ -163,9 +163,9 @@ _checkhtmlsnippet(html) def test_build_class_pages(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeClass', - 'main.SomeSubClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeClass', + 'main.SomeSubClass']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeClass.html') assert clsfile.check() @@ -173,10 +173,10 @@ _checkhtml(html) def test_build_class_pages_instance(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeClass', - 'main.SomeSubClass', - 'main.SomeInstance']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeInstance.html') assert clsfile.check() @@ -187,9 +187,9 @@ ]) def test_build_class_pages_nav_links(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', - 'main.SomeClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeSubClass', + 'main.SomeClass']) # fake some stuff that would be built from other methods self.linker.set_link('', 'api/index.html') self.linker.set_link('main', 'api/main.html') @@ -212,9 +212,9 @@ _checkhtml(html) def test_build_class_pages_base_link(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', - 'main.SomeClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeSubClass', + 'main.SomeClass']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeSubClass.html') assert clsfile.check() @@ -227,9 +227,9 @@ _checkhtml(html) def test_source_links(self): - data, methodsdata = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', - 'main.SomeClass']) + data = self.apb.prepare_class_pages(self.namespace_tree, + ['main.SomeSubClass', + 'main.SomeClass']) sourcedata = self.spb.prepare_pages(self.fs_root) self.apb.build_class_pages(data, self.project) self.spb.build_pages(sourcedata, self.project, self.fs_root) Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Fri Jan 26 14:01:27 2007 @@ -113,9 +113,9 @@ html = sometestclass_source.read() assert '
sources for sometestclass.py
' in html - # XXX later... - #index = sourcedir.join('index.html') - #assert index.check(file=True) - #html = index.read() - #assert 'main' in html + index = sourcedir.join('index.html') + assert index.check(file=True) + html = index.read() + print html + assert 'test' in html From guido at codespeak.net Fri Jan 26 16:34:30 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 26 Jan 2007 16:34:30 +0100 (CET) Subject: [py-svn] r37395 - in py/trunk/py/apigen: . testing Message-ID: <20070126153430.9E7AC10079@code0.codespeak.net> Author: guido Date: Fri Jan 26 16:34:28 2007 New Revision: 37395 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_apigen_functional.py Log: Fixed some small issues in source nav, turned namespace_tree into an instance var (finally! :), changed the api nav so it now shows the full tree with the current item unfolded + siblings of itself and all parents (+ children if it's a namespace item). Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Fri Jan 26 16:34:28 2007 @@ -14,11 +14,7 @@ def get_documentable_items(pkgdir): sys.path.insert(0, str(pkgdir.dirpath())) rootmod = __import__(pkgdir.basename) - #rootmod = import_pkgdir(pkgdir) - if hasattr(rootmod, '__package__'): - return rootmod - # XXX fix non-initpkg situations(?) - return {} + return rootmod def build(pkgdir, dsa): l = linker.Linker() @@ -32,15 +28,14 @@ all_names = dsa._get_names(filter=lambda x, y: True) namespace_tree = htmlgen.create_namespace_tree(all_names) - apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir) + apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree) spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir) - ns_data = apb.prepare_namespace_pages(namespace_tree) + ns_data = apb.prepare_namespace_pages() class_names = dsa.get_class_names() - class_data = apb.prepare_class_pages(namespace_tree, - class_names) + class_data = apb.prepare_class_pages(class_names) function_names = dsa.get_function_names() - func_data = apb.prepare_function_pages(namespace_tree, function_names) + func_data = apb.prepare_function_pages(function_names) source_data = spb.prepare_pages(pkgdir) apb.build_namespace_pages(ns_data, proj) Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Fri Jan 26 16:34:28 2007 @@ -180,16 +180,17 @@ path = relpath.split(os.path.sep) indent = 0 # build links to parents - for i in xrange(len(path)): - dirpath = os.path.sep.join(path[:i]) - abspath = self.projroot.join(dirpath).strpath - if i == 0: - text = 'root' - else: - text = path[i-1] - nav.append(build_navitem_html(self.linker, text, abspath, - indent, False)) - indent += 1 + if relpath != '': + for i in xrange(len(path)): + dirpath = os.path.sep.join(path[:i]) + abspath = self.projroot.join(dirpath).strpath + if i == 0: + text = self.projroot.basename + else: + text = path[i-1] + nav.append(build_navitem_html(self.linker, text, abspath, + indent, False)) + indent += 1 # build siblings or children and self if fspath.check(dir=True): # we're a dir, build ourselves and our children @@ -296,12 +297,13 @@ class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ - def __init__(self, base, linker, dsa, projroot): + def __init__(self, base, linker, dsa, projroot, namespace_tree): self.base = base self.linker = linker self.dsa = dsa self.projroot = projroot self.projpath = py.path.local(projroot) + self.namespace_tree = namespace_tree def build_callable_view(self, dotted_name): """ build the html for a class method """ @@ -428,18 +430,17 @@ ) return snippet - def prepare_class_pages(self, namespace_tree, classes_dotted_names): + def prepare_class_pages(self, classes_dotted_names): passed = [] for dotted_name in sorted(classes_dotted_names): parent_dotted_name, _ = split_of_last_part(dotted_name) try: - sibling_dotted_names = namespace_tree[parent_dotted_name] + sibling_dotted_names = self.namespace_tree[parent_dotted_name] except KeyError: # no siblings (built-in module or sth) sibling_dotted_names = [] tag = H.Content(self.build_class_view(dotted_name)) - nav = self.build_navigation(parent_dotted_name, - sibling_dotted_names, dotted_name) + nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -451,17 +452,16 @@ title = 'api documentation for %s' % (dotted_name,) self.write_page(title, reltargetpath, project, tag, nav) - def prepare_method_pages(self, namespace_tree, method_dotted_names): + def prepare_method_pages(self, method_dotted_names): # XXX note that even though these pages are still built, there's no nav # pointing to them anymore... passed = [] for dotted_name in sorted(method_dotted_names): parent_dotted_name, _ = split_of_last_part(dotted_name) module_dotted_name, _ = split_of_last_part(parent_dotted_name) - sibling_dotted_names = namespace_tree[module_dotted_name] + sibling_dotted_names = self.namespace_tree[module_dotted_name] tag = self.build_callable_view(dotted_name) - nav = self.build_navigation(parent_dotted_name, - sibling_dotted_names, dotted_name) + nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -472,15 +472,14 @@ title = 'api documentation for %s' % (dotted_name,) self.write_page(title, reltargetpath, project, tag, nav) - def prepare_function_pages(self, namespace_tree, method_dotted_names): + def prepare_function_pages(self, method_dotted_names): passed = [] for dotted_name in sorted(method_dotted_names): # XXX should we create a build_function_view instead? parent_dotted_name, _ = split_of_last_part(dotted_name) - sibling_dotted_names = namespace_tree[parent_dotted_name] + sibling_dotted_names = self.namespace_tree[parent_dotted_name] tag = H.Content(self.build_callable_view(dotted_name)) - nav = self.build_navigation(parent_dotted_name, - sibling_dotted_names, dotted_name) + nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) passed.append((dotted_name, tag, nav, reltargetpath)) @@ -491,22 +490,21 @@ title = 'api documentation for %s' % (dotted_name,) self.write_page(title, reltargetpath, project, tag, nav) - def prepare_namespace_pages(self, namespace_tree): + def prepare_namespace_pages(self): passed = [] module_name = self.dsa.get_module_name().split('/')[-1] - names = namespace_tree.keys() + names = self.namespace_tree.keys() names.sort() function_names = self.dsa.get_function_names() class_names = self.dsa.get_class_names() for dotted_name in sorted(names): if dotted_name in function_names or dotted_name in class_names: continue - subitem_dotted_names = namespace_tree[dotted_name] + subitem_dotted_names = self.namespace_tree[dotted_name] tag = H.Content(self.build_namespace_view(dotted_name, subitem_dotted_names)) - nav = self.build_navigation(dotted_name, subitem_dotted_names, - dotted_name) + nav = self.build_navigation(dotted_name, True) if dotted_name == '': reltargetpath = 'api/index.html' else: @@ -522,7 +520,39 @@ title = 'index of %s namespace' % (dotted_name,) self.write_page(title, reltargetpath, project, tag, nav) - def build_navigation(self, dotted_name, item_dotted_names, selection): + def build_navigation(self, dotted_name, build_children=True): + navitems = [] + + # top namespace, index.html + module_name = self.dsa.get_module_name().split('/')[-1] + navitems.append(build_navitem_html(self.linker, module_name, '', 0, + True)) + def build_nav_level(dotted_name, depth=1): + navitems = [] + path = dotted_name.split('.')[:depth] + siblings = self.namespace_tree.get('.'.join(path[:-1])) + for dn in sorted(siblings): + selected = dn == '.'.join(path) + sibpath = dn.split('.') + navitems.append(build_navitem_html(self.linker, sibpath[-1], + dn, depth, + selected)) + if selected: + lastlevel = dn.count('.') == dotted_name.count('.') + if not lastlevel: + navitems += build_nav_level(dotted_name, depth+1) + elif lastlevel and build_children: + # XXX hack + navitems += build_nav_level('%s.' % (dotted_name,), + depth+2) + + return navitems + + navitems += build_nav_level(dotted_name) + return H.Navigation(*navitems) + + + navitems = [] # top namespace, index.html 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 Fri Jan 26 16:34:28 2007 @@ -104,15 +104,19 @@ self.base = base = py.test.ensuretemp('%s_%s' % ( self.__class__.__name__, meth.im_func.func_name)) self.linker = linker = LinkerForTests() - self.apb = ApiPageBuilder(base, linker, self.dsa, self.fs_root) - self.spb = SourcePageBuilder(base, linker, self.fs_root) - self.namespace_tree = create_namespace_tree(['main.sub', - 'main.sub.func', - 'main.SomeClass', - 'main.SomeSubClass', - 'main.SomeInstance', - 'other.foo', - 'other.bar']) + namespace_tree = create_namespace_tree(['main.sub', + 'main.sub.func', + 'main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance', + 'other.foo', + 'other.bar']) + self.namespace_tree = namespace_tree + self.apb = ApiPageBuilder(base, linker, self.dsa, + self.fs_root.join(self.pkg_name), + namespace_tree) + self.spb = SourcePageBuilder(base, linker, + self.fs_root.join(self.pkg_name)) class TestApiPageBuilder(AbstractBuilderTest): def test_build_callable_view(self): @@ -123,7 +127,8 @@ pkg.main.sub.func(10) pkg.main.sub.func(pkg.main.SomeClass(10)) t.end_tracing() - apb = ApiPageBuilder(self.base, self.linker, dsa, self.fs_root) + apb = ApiPageBuilder(self.base, self.linker, dsa, self.fs_root, + self.namespace_tree) snippet = apb.build_callable_view('main.sub.func') html = snippet.unicode() print html @@ -149,8 +154,7 @@ _checkhtmlsnippet(html) def test_build_function_pages(self): - data = self.apb.prepare_function_pages(self.namespace_tree, - ['main.sub.func']) + data = self.apb.prepare_function_pages(['main.sub.func']) self.apb.build_function_pages(data, self.project) funcfile = self.base.join('api/main.sub.func.html') assert funcfile.check() @@ -163,8 +167,7 @@ _checkhtmlsnippet(html) def test_build_class_pages(self): - data = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeClass', + data = self.apb.prepare_class_pages(['main.SomeClass', 'main.SomeSubClass']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeClass.html') @@ -173,8 +176,7 @@ _checkhtml(html) def test_build_class_pages_instance(self): - data = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeClass', + data = self.apb.prepare_class_pages(['main.SomeClass', 'main.SomeSubClass', 'main.SomeInstance']) self.apb.build_class_pages(data, self.project) @@ -187,8 +189,7 @@ ]) def test_build_class_pages_nav_links(self): - data = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', + data = self.apb.prepare_class_pages(['main.SomeSubClass', 'main.SomeClass']) # fake some stuff that would be built from other methods self.linker.set_link('', 'api/index.html') @@ -212,8 +213,7 @@ _checkhtml(html) def test_build_class_pages_base_link(self): - data = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', + data = self.apb.prepare_class_pages(['main.SomeSubClass', 'main.SomeClass']) self.apb.build_class_pages(data, self.project) clsfile = self.base.join('api/main.SomeSubClass.html') @@ -227,8 +227,7 @@ _checkhtml(html) def test_source_links(self): - data = self.apb.prepare_class_pages(self.namespace_tree, - ['main.SomeSubClass', + data = self.apb.prepare_class_pages(['main.SomeSubClass', 'main.SomeClass']) sourcedata = self.spb.prepare_pages(self.fs_root) self.apb.build_class_pages(data, self.project) @@ -238,7 +237,7 @@ _checkhtml(funchtml) def test_build_namespace_pages(self): - data = self.apb.prepare_namespace_pages(self.namespace_tree) + data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages(data, self.project) mainfile = self.base.join('api/main.html') assert mainfile.check() @@ -258,7 +257,7 @@ _checkhtml(otherhtml) def test_build_namespace_pages_index(self): - data = self.apb.prepare_namespace_pages(self.namespace_tree) + data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages(data, self.project) pkgfile = self.base.join('api/index.html') assert pkgfile.check() @@ -267,7 +266,7 @@ _checkhtml(html) def test_build_namespace_pages_subnamespace(self): - data = self.apb.prepare_namespace_pages(self.namespace_tree) + data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages(data, self.project) subfile = self.base.join('api/main.sub.html') assert subfile.check() @@ -275,8 +274,7 @@ _checkhtml(html) def test_build_function_api_pages_nav(self): - data = self.apb.prepare_function_pages(self.namespace_tree, - ['main.sub.func']) + data = self.apb.prepare_function_pages(['main.sub.func']) self.linker.set_link('', 'api/index.html') self.linker.set_link('main', 'api/main.html') self.linker.set_link('main.sub', 'api/main.sub.html') @@ -293,22 +291,30 @@ _checkhtml(html) def test_build_function_navigation(self): - self.apb.prepare_namespace_pages(self.namespace_tree) - self.apb.prepare_function_pages(self.namespace_tree, ['main.sub.func']) - nav = self.apb.build_navigation('main.sub', ['main.sub.func'], - 'main.sub.func') + self.apb.prepare_namespace_pages() + self.apb.prepare_function_pages(['main.sub.func']) + self.apb.prepare_class_pages(['main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance']) + nav = self.apb.build_navigation('main.sub.func', False) html = nav.unicode(indent=0) print html.encode('UTF-8') - assert (u'' - u'
\xa0\xa0main
' - u'
\xa0\xa0\xa0\xa0' + assert (u'' + u'
\xa0\xa0main
' + u'
\xa0\xa0\xa0\xa0' + u'SomeClass
' + u'
\xa0\xa0\xa0\xa0' + u'SomeInstance
' + u'
\xa0\xa0\xa0\xa0' + u'SomeSubClass
' + u'
\xa0\xa0\xa0\xa0' u'sub
' u'
\xa0\xa0\xa0\xa0\xa0\xa0' u'func
' ) in html def test_build_root_namespace_view(self): - data = self.apb.prepare_namespace_pages(self.namespace_tree) + data = self.apb.prepare_namespace_pages() self.apb.build_namespace_pages(data, self.project) rootfile = self.base.join('api/index.html') assert rootfile.check() @@ -332,7 +338,6 @@ print html run_string_sequence_test(html, [ 'href="../../style.css"', - 'root', 'pkg', 'someclass.py', 'somesubclass.py', @@ -347,7 +352,6 @@ print html run_string_sequence_test(html, [ 'href="../../style.css"', - 'root', 'pkg', 'func.py', 'someclass.py', @@ -362,7 +366,6 @@ html = nav.unicode(indent=0) print html.encode('UTF-8') run_string_sequence_test(html, [ - 'href="source/index.html">root', 'href="source/pkg/index.html">pkg', 'href="source/pkg/func.py.html">func.py', 'href="source/pkg/someclass.py.html">someclass.py', Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Fri Jan 26 16:34:28 2007 @@ -4,6 +4,7 @@ """ import py +from py.__.apigen import apigen def setup_fs_project(): temp = py.test.ensuretemp('apigen_functional') @@ -70,6 +71,13 @@ """)) return temp, 'pkg' +def test_get_documentable_items(): + fs_root, package_name = setup_fs_project() + documentable = apigen.get_documentable_items(fs_root.join(package_name)) + assert documentable.__package__.exportdefs.keys() == [ + 'main.sub.func', 'main.func', 'main.SomeTestSubClass', + 'main.SomeTestClass'] + def test_apigen_functional(): fs_root, package_name = setup_fs_project() tempdir = py.test.ensuretemp('test_apigen_functional_results') From fijal at codespeak.net Fri Jan 26 22:46:13 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 26 Jan 2007 22:46:13 +0100 (CET) Subject: [py-svn] r37407 - in py/trunk/py/test/rsession: . testing Message-ID: <20070126214613.A3FC310071@code0.codespeak.net> Author: fijal Date: Fri Jan 26 22:46:11 2007 New Revision: 37407 Modified: py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/testing/test_master.py Log: Unskip two tests. Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Fri Jan 26 22:46:11 2007 @@ -30,7 +30,7 @@ else: self.pending.insert(0, item) #itemspec = item.listnames()[1:] - self.channel.send(item.config.get_collector_trail(item)) + self.channel.send(item.get_collector_trail()) # send start report self.reporter(report.SendItem(self.channel, item)) Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Fri Jan 26 22:46:11 2007 @@ -38,8 +38,11 @@ assert py.std.marshal.dumps(item) self.sent.append(item) +class Item(py.test.Item): + def get_collector_trail(self): + return (self.name,) + def test_masternode(): - py.test.skip("cannot send non-fs items nowadays") try: raise ValueError() except ValueError: @@ -48,8 +51,8 @@ ch = DummyChannel() reportlist = [] mnode = MasterNode(ch, reportlist.append, {}) - mnode.send(py.test.Item("ok")) - mnode.send(py.test.Item("notok")) + mnode.send(Item("ok")) + mnode.send(Item("notok")) ch.callback(Outcome().make_repr()) ch.callback(Outcome(excinfo=excinfo).make_repr()) assert len(reportlist) == 4 @@ -59,12 +62,11 @@ assert not received[1].outcome.passed def test_unique_nodes(): - py.test.skip("cannot send non-fs items nowadays") ch = DummyChannel() reportlist = [] mnode = MasterNode(ch, reportlist.append, {}) - mnode.send(py.test.Item("ok")) - mnode.send(py.test.Item("ok")) + mnode.send(Item("ok")) + mnode.send(Item("ok")) ch.callback(Outcome().make_repr()) ch.callback(Outcome().make_repr()) assert len(reportlist) == 3 From fijal at codespeak.net Fri Jan 26 22:54:55 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 26 Jan 2007 22:54:55 +0100 (CET) Subject: [py-svn] r37408 - py/trunk/py/test/rsession/testing Message-ID: <20070126215455.BAE9510071@code0.codespeak.net> Author: fijal Date: Fri Jan 26 22:54:52 2007 New Revision: 37408 Modified: py/trunk/py/test/rsession/testing/test_slave.py Log: Kill two unnecessary tests now. Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Fri Jan 26 22:54:52 2007 @@ -127,43 +127,7 @@ node.run(rootcol.getitembynames("py doc log.txt".split()). get_collector_trail()) -def test_slave_setup_fails_on_import_error(): - py.test.skip("WIP") - tmp = py.test.ensuretemp("slavesetup") - config = py.test.config._reparse([tmp]) - class C: - def __init__(self): - self.count = 0 - - def receive(self): - if self.count == 0: - retval = str(tmp) - elif self.count == 1: - retval = config.make_repr(conftestnames=['dist_nicelevel']) - else: - raise NotImplementedError("more data") - self.count += 1 - return retval - - def close(self): - pass - - def setcallback(self, cb): - pass - - def send(self, x): - pass - - try: - exec py.code.Source(setup, "setup()").compile() in { - 'channel': C()} - except ImportError: - pass # expected - else: - py.test.fail("missing exception") - def test_slave_setup_exit(): - py.test.skip("WIP") tmp = py.test.ensuretemp("slaveexit") tmp.ensure("__init__.py") q = py.std.Queue.Queue() @@ -201,43 +165,6 @@ else: py.test.fail("Did not exit") -def test_slave_setup_fails_on_missing_pkg(): - py.test.skip("WIP") - tmp = py.test.ensuretemp("slavesetup2") - config = py.test.config._reparse([tmp]) - x = tmp.ensure("sometestpackage", "__init__.py") - class C: - def __init__(self): - self.count = 0 - - def receive(self): - if self.count == 0: - retval = str(x.dirpath()) - elif self.count == 1: - retval = config.make_repr(conftestnames=['dist_nicelevel']) - else: - raise NotImplementedError("more data") - self.count += 1 - return retval - try: - exec py.code.Source(setup, "setup()").compile() in {'channel': C()} - except AttributeError: # channel.send - pass - else: - py.test.fail("missing exception") - - # now create a parallel structure - tmp = py.test.ensuretemp("slavesetup3") - x = tmp.ensure("sometestpackage", "__init__.py") - try: - exec py.code.Source(setup, "setup()").compile() in { - 'channel': C()} - except AssertionError: - pass # expected - else: - py.test.fail("missing exception") - - def test_pidinfo(): if not hasattr(os, 'fork') or not hasattr(os, 'waitpid'): py.test.skip("Platform does not support fork") From fijal at codespeak.net Fri Jan 26 22:56:52 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 26 Jan 2007 22:56:52 +0100 (CET) Subject: [py-svn] r37409 - py/trunk/py/test/rsession/testing Message-ID: <20070126215652.EEBE510071@code0.codespeak.net> Author: fijal Date: Fri Jan 26 22:56:50 2007 New Revision: 37409 Modified: py/trunk/py/test/rsession/testing/test_webjs.py Log: Fix the test Modified: py/trunk/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py Fri Jan 26 22:56:50 2007 @@ -4,7 +4,6 @@ import pypy from pypy.translator.js.modules import dom from pypy.translator.js.tester import schedule_callbacks - from py.__.test.rsession.rsession import session_options dom.Window # check whether dom was properly imported or is just a # leftover in sys.modules except (ImportError, AttributeError): From fijal at codespeak.net Fri Jan 26 23:35:35 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 26 Jan 2007 23:35:35 +0100 (CET) Subject: [py-svn] r37411 - in py/trunk/py/test/rsession: . testing Message-ID: <20070126223535.782AD10079@code0.codespeak.net> Author: fijal Date: Fri Jan 26 23:35:20 2007 New Revision: 37411 Modified: py/trunk/py/test/rsession/rest.py py/trunk/py/test/rsession/testing/test_rest.py Log: Fix a bit rest and test_rest. Note that actually these tests are testing anything from time to time, need closer look. Modified: py/trunk/py/test/rsession/rest.py ============================================================================== --- py/trunk/py/test/rsession/rest.py (original) +++ py/trunk/py/test/rsession/rest.py Fri Jan 26 23:35:20 2007 @@ -37,20 +37,21 @@ return self.hosts[0] def report_SendItem(self, item): - address = self.gethost(item) + address = self.gethost(item).hostname if self.config.option.verbose: self.add_rest(Paragraph('sending item %s to %s' % (item.item, address))) def report_HostRSyncing(self, item): self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10], - item.remoterootpath))) + item.host.relpath))) def report_HostReady(self, item): self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],))) def report_TestStarted(self, event): - txt = "Running tests on hosts: %s" % ", ".join(event.hosts) + txt = "Running tests on hosts: %s" % ", ".join([i.hostname for i in + event.hosts]) self.add_rest(Title(txt, abovechar='=', belowchar='=')) self.timestart = event.timestart Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Fri Jan 26 23:35:20 2007 @@ -9,24 +9,13 @@ from py.__.test.rsession.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * from py.__.test.rsession.hostmanage import HostInfo - -py.test.skip("This tests are not really testing, needs rewrite") +from py.__.test.rsession.outcome import Outcome class RestTestReporter(RestReporter): def __init__(self, *args, **kwargs): if args: super(RestReporter, self).__init__(*args, **kwargs) -class Container(object): - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - -class FakeOutcome(Container, report.ReceivedItemOutcome): - pass - -class FakeTryiter(Container, report.SkippedTryiter): - pass - class TestRestUnits(object): def setup_method(self, method): config = py.test.config._reparse(["some_sub"]) @@ -47,30 +36,31 @@ self.config.option.verbose = False def test_report_SendItem(self): - item = Container(item='foo/bar.py', channel=ch) - reporter.report_SendItem(item) + event = report.SendItem(item='foo/bar.py', channel=ch) + reporter.report(event) assert stdout.getvalue() == '' stdout.seek(0) stdout.truncate() reporter.config.option.verbose = True - reporter.report_SendItem(item) + reporter.report(event) assert stdout.getvalue() == ('sending item foo/bar.py to ' 'localhost\n\n') def test_report_HostRSyncing(self): - item = Container(hostname='localhost', remoterootpath='/foo/bar') - reporter.report_HostRSyncing(item) + event = report.HostRSyncing(HostInfo('localhost', '/foo/bar')) + reporter.report(event) assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' '/foo/bar\n\n') def test_report_HostReady(self): - item = Container(hostname='localhost') - reporter.report_HostReady(item) + event = report.HostReady(HostInfo('localhost')) + reporter.report(event) assert stdout.getvalue() == '::\n\n localhost: READY\n\n' def test_report_TestStarted(self): - event = Container(hosts=['localhost', 'foo.com'], timestart=0) - reporter.report_TestStarted(event) + event = report.TestStarted(hosts=[HostInfo('localhost'), + HostInfo('foo.com')]) + reporter.report(event) assert stdout.getvalue() == """\ =========================================== Running tests on hosts\: localhost, foo.com @@ -87,9 +77,15 @@ return ['test_foo', 'test_bar'] def listnames(self): return ['package', 'foo', 'bar.py'] + + class Container(object): + def __init__(self, **args): + for arg, val in args.items(): + setattr(self, arg, val) + parent = Container(parent=None, fspath=py.path.local('.')) - event = Container(item=FakeModule(parent)) - reporter.report_ItemStart(event) + event = report.ItemStart(item=FakeModule(parent)) + reporter.report(event) assert stdout.getvalue() == """\ Testing module foo/bar.py (2 items) ----------------------------------- @@ -108,32 +104,58 @@ """ def test_ReceivedItemOutcome_PASSED(self): - outcome = Container(passed=True, excinfo=False) + outcome = Outcome() + class Container(object): + def __init__(self, **args): + for arg, val in args.items(): + setattr(self, arg, val) + item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = Container(channel=ch, outcome=outcome, item=item) - reporter.report_ReceivedItemOutcome(event) + event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) + reporter.report(event) assert stdout.getvalue() == ('* localhost\\: **PASSED** ' 'foo.py/bar()/baz\n\n') def test_ReceivedItemOutcome_SKIPPED(self): - outcome = Container(passed=False, skipped=True, excinfo=False) + class Container(object): + def __init__(self, **args): + for arg, val in args.items(): + setattr(self, arg, val) + + outcome = Outcome(skipped="reason") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = Container(channel=ch, outcome=outcome, item=item) - reporter.report_ReceivedItemOutcome(event) + event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) + reporter.report(event) assert stdout.getvalue() == ('* localhost\\: **SKIPPED** ' 'foo.py/bar()/baz\n\n') def test_ReceivedItemOutcome_FAILED(self): - outcome = Container(passed=False, skipped=False) + class Container(object): + def __init__(self, **args): + for arg, val in args.items(): + setattr(self, arg, val) + + outcome = Outcome(excinfo="xxx") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = Container(channel=ch, outcome=outcome, item=item) - reporter.report_ReceivedItemOutcome(event) + event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) + reporter.report(event) assert stdout.getvalue() == """\ * localhost\: **FAILED** `traceback0`_ foo.py/bar()/baz """ def test_skips(self): + class Container(object): + def __init__(self, **args): + for arg, val in args.items(): + setattr(self, arg, val) + + class FakeOutcome(Container, report.ReceivedItemOutcome): + pass + + class FakeTryiter(Container, report.SkippedTryiter): + pass + reporter.skips() assert stdout.getvalue() == '' reporter.skipped_tests_outcome = [ @@ -153,6 +175,15 @@ """ def test_failures(self): + py.test.skip("This one is totally artificial, needs to be rewritten") + class Container(object): + def __init__(self, **args): + for arg, val in args.items(): + setattr(self, arg, val) + + class FakeOutcome(Container, report.ReceivedItemOutcome): + pass + parent = Container(parent=None, fspath=py.path.local('.')) reporter.failed_tests_outcome = [ FakeOutcome( From guido at codespeak.net Sat Jan 27 13:29:41 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 13:29:41 +0100 (CET) Subject: [py-svn] r37425 - in py/trunk/py/code: . testing Message-ID: <20070127122941.192F510076@code0.codespeak.net> Author: guido Date: Sat Jan 27 13:29:39 2007 New Revision: 37425 Modified: py/trunk/py/code/code.py py/trunk/py/code/testing/test_code.py Log: Fixed Code.__ne__() (wrong function sig, missing 'other' argument). Modified: py/trunk/py/code/code.py ============================================================================== --- py/trunk/py/code/code.py (original) +++ py/trunk/py/code/code.py Sat Jan 27 13:29:39 2007 @@ -15,7 +15,7 @@ def __eq__(self, other): return self.raw == other.raw - def __ne__(self): + def __ne__(self, other): return not self == other def new(self, rec=False, **kwargs): Modified: py/trunk/py/code/testing/test_code.py ============================================================================== --- py/trunk/py/code/testing/test_code.py (original) +++ py/trunk/py/code/testing/test_code.py Sat Jan 27 13:29:39 2007 @@ -8,6 +8,12 @@ newco = code.new() assert co == newco +def test_ne(): + code1 = py.code.Code(compile('foo = "bar"', '', 'exec')) + assert code1 == code1 + code2 = py.code.Code(compile('foo = "baz"', '', 'exec')) + assert code2 != code1 + def test_newcode_unknown_args(): code = py.code.Code(compile("", '', 'exec')) py.test.raises(TypeError, 'code.new(filename="hello")') From fijal at codespeak.net Sat Jan 27 14:02:18 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 27 Jan 2007 14:02:18 +0100 (CET) Subject: [py-svn] r37427 - in py/trunk/py/test/rsession: . testing Message-ID: <20070127130218.40ED61007D@code0.codespeak.net> Author: fijal Date: Sat Jan 27 14:01:58 2007 New Revision: 37427 Modified: py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_lsession.py Log: Kill the last occurencies of pkgdir Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Sat Jan 27 14:01:58 2007 @@ -227,12 +227,11 @@ reporter, checkfun = self.wrap_reporter(reporter) reporter(report.TestStarted(sshhosts)) - pkgdir = self.getpkgdir(args[0]) colitems = self.config.getcolitems() reporter(report.RsyncFinished()) if runner is None: - runner = self.init_runner(pkgdir) + runner = self.init_runner() keyword = self.config.option.keyword @@ -245,10 +244,10 @@ if not self.config.option.nomagic: py.magic.revoke(assertion=1) - self.write_docs(pkgdir) + self.write_docs() return retval - def write_docs(self, pkgdir): + def write_docs(self): if self.config.option.apigen: from py.__.apigen.tracer.docstorage import DocStorageAccessor apigen = py.path.local(self.config.option.apigen).pyimport() @@ -259,7 +258,8 @@ capture = py.io.OutErrCapture() try: try: - apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) + apigen.build(self.config.topdir, + DocStorageAccessor(self.docstorage)) except AttributeError: import traceback exc, e, tb = sys.exc_info() @@ -272,13 +272,13 @@ finally: capture.reset() - def init_runner(self, pkgdir): + def init_runner(self): if self.config.option.apigen: from py.__.apigen.tracer.tracer import Tracer, DocStorage module = py try: apigen = py.path.local(self.config.option.apigen).pyimport() - items = apigen.get_documentable_items(pkgdir) + items = apigen.get_documentable_items(self.config.topdir) if isinstance(items, dict): self.docstorage = DocStorage().from_dict(items) else: Modified: py/trunk/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_lsession.py (original) +++ py/trunk/py/test/rsession/testing/test_lsession.py Sat Jan 27 14:01:58 2007 @@ -286,7 +286,7 @@ all = str(tmpdir) config = py.test.config._reparse(all.split(" ")) lsession = LSession(config) - assert lsession.init_runner(pkgdir) is opt_mapping[opt] + assert lsession.init_runner() is opt_mapping[opt] #tmpdir.dirpath().ensure("conftest.py").write(py.code.Source(""" #dist_boxing=True #""")) From hpk at codespeak.net Sat Jan 27 14:09:18 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 14:09:18 +0100 (CET) Subject: [py-svn] r37428 - py/trunk/py/doc Message-ID: <20070127130918.4FB491007D@code0.codespeak.net> Author: hpk Date: Sat Jan 27 14:09:09 2007 New Revision: 37428 Modified: py/trunk/py/doc/TODO.txt Log: added a note about dist_rsyncroots semantics Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Jan 27 14:09:09 2007 @@ -110,6 +110,14 @@ * cleanup initialisation of config / get rid of pkgdir * unify option names (dist_*) * (optional) see if more of py/test/session.py's Session can be reused +* have dist_rsyncroots be relative to the conftest.py file + so that projects can define it for themselves, e.g. + pypy/conftest.py would contain + dist_rsyncroots = ['../pypy', '../py'] + for this there probably needs to be a + config.getvalue_and_conftestpath() + method with tests and documentation, and with providing + the right example. code quality ----------------- From fijal at codespeak.net Sat Jan 27 14:12:41 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 27 Jan 2007 14:12:41 +0100 (CET) Subject: [py-svn] r37429 - py/trunk/py/test/rsession Message-ID: <20070127131241.292851007D@code0.codespeak.net> Author: fijal Date: Sat Jan 27 14:12:23 2007 New Revision: 37429 Modified: py/trunk/py/test/rsession/rsession.py Log: Ugh, revert. Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Sat Jan 27 14:12:23 2007 @@ -258,7 +258,8 @@ capture = py.io.OutErrCapture() try: try: - apigen.build(self.config.topdir, + pkgdir = self.getpkgdir(self.config.args[0]) + apigen.build(pkgdir, DocStorageAccessor(self.docstorage)) except AttributeError: import traceback @@ -277,8 +278,9 @@ from py.__.apigen.tracer.tracer import Tracer, DocStorage module = py try: + pkgdir = self.getpkgdir(self.config.args[0]) apigen = py.path.local(self.config.option.apigen).pyimport() - items = apigen.get_documentable_items(self.config.topdir) + items = apigen.get_documentable_items(pkgdir) if isinstance(items, dict): self.docstorage = DocStorage().from_dict(items) else: From guido at codespeak.net Sat Jan 27 14:20:58 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 14:20:58 +0100 (CET) Subject: [py-svn] r37430 - py/trunk/py/apigen/testing Message-ID: <20070127132058.3FD621007D@code0.codespeak.net> Author: guido Date: Sat Jan 27 14:20:56 2007 New Revision: 37430 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py Log: Fixed test, was depending on dict order and values had changed. Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Sat Jan 27 14:20:56 2007 @@ -74,9 +74,9 @@ def test_get_documentable_items(): fs_root, package_name = setup_fs_project() documentable = apigen.get_documentable_items(fs_root.join(package_name)) - assert documentable.__package__.exportdefs.keys() == [ - 'main.sub.func', 'main.func', 'main.SomeTestSubClass', - 'main.SomeTestClass'] + assert sorted(documentable.__package__.exportdefs.keys()) == [ + 'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func', + 'main.sub.func'] def test_apigen_functional(): fs_root, package_name = setup_fs_project() From guido at codespeak.net Sat Jan 27 14:26:58 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 14:26:58 +0100 (CET) Subject: [py-svn] r37431 - py/trunk/py/apigen/testing Message-ID: <20070127132658.903DE10075@code0.codespeak.net> Author: guido Date: Sat Jan 27 14:26:57 2007 New Revision: 37431 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py Log: Fixed problem that made that the wrong package was imported when ran after test_apigen_example. Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Sat Jan 27 14:26:57 2007 @@ -6,16 +6,17 @@ import py from py.__.apigen import apigen -def setup_fs_project(): - temp = py.test.ensuretemp('apigen_functional') - temp.ensure("pkg/func.py").write(py.code.Source("""\ +def setup_fs_project(name): + temp = py.test.ensuretemp(name) + assert temp.listdir() == [] + temp.ensure("pak/func.py").write(py.code.Source("""\ def func(arg1): "docstring" def func_2(arg1, arg2): return arg1(arg2) """)) - temp.ensure('pkg/sometestclass.py').write(py.code.Source("""\ + temp.ensure('pak/sometestclass.py').write(py.code.Source("""\ class SomeTestClass(object): " docstring sometestclass " def __init__(self, somevar): @@ -25,20 +26,20 @@ " get_somevar docstring " return self.somevar """)) - temp.ensure('pkg/sometestsubclass.py').write(py.code.Source("""\ + temp.ensure('pak/sometestsubclass.py').write(py.code.Source("""\ from sometestclass import SomeTestClass class SomeTestSubClass(SomeTestClass): " docstring sometestsubclass " def get_somevar(self): return self.somevar + 1 """)) - temp.ensure('pkg/somenamespace.py').write(py.code.Source("""\ + temp.ensure('pak/somenamespace.py').write(py.code.Source("""\ def foo(): return 'bar' def baz(qux): return qux """)) - temp.ensure("pkg/__init__.py").write(py.code.Source("""\ + temp.ensure("pak/__init__.py").write(py.code.Source("""\ from py.initpkg import initpkg initpkg(__name__, exportdefs = { 'main.sub.func': ("./func.py", "func"), @@ -48,41 +49,41 @@ 'SomeTestSubClass'), }) """)) - temp.ensure('pkg/test/test_pkg.py').write(py.code.Source("""\ + temp.ensure('pak/test/test_pak.py').write(py.code.Source("""\ import py py.std.sys.path.insert(0, py.magic.autopath().dirpath().dirpath().dirpath().strpath) - import pkg + import pak # this mainly exists to provide some data to the tracer - def test_pkg(): - s = pkg.main.SomeTestClass(10) + def test_pak(): + s = pak.main.SomeTestClass(10) assert s.get_somevar() == 10 - s = pkg.main.SomeTestClass('10') + s = pak.main.SomeTestClass('10') assert s.get_somevar() == '10' - s = pkg.main.SomeTestSubClass(10) + s = pak.main.SomeTestSubClass(10) assert s.get_somevar() == 11 - s = pkg.main.SomeTestSubClass('10') + s = pak.main.SomeTestSubClass('10') py.test.raises(TypeError, 's.get_somevar()') - assert pkg.main.sub.func(10) is None - assert pkg.main.sub.func(20) is None - s = pkg.main.func(pkg.main.SomeTestClass, 10) - assert isinstance(s, pkg.main.SomeTestClass) + assert pak.main.sub.func(10) is None + assert pak.main.sub.func(20) is None + s = pak.main.func(pak.main.SomeTestClass, 10) + assert isinstance(s, pak.main.SomeTestClass) """)) - return temp, 'pkg' + return temp, 'pak' def test_get_documentable_items(): - fs_root, package_name = setup_fs_project() + fs_root, package_name = setup_fs_project('test_get_documentable_items') documentable = apigen.get_documentable_items(fs_root.join(package_name)) assert sorted(documentable.__package__.exportdefs.keys()) == [ 'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func', 'main.sub.func'] def test_apigen_functional(): - fs_root, package_name = setup_fs_project() + fs_root, package_name = setup_fs_project('test_apigen_functional') tempdir = py.test.ensuretemp('test_apigen_functional_results') pydir = py.magic.autopath().dirpath().dirpath().dirpath() - pkgdir = fs_root.join('pkg') + pakdir = fs_root.join('pak') if py.std.sys.platform == 'win32': cmd = 'set APIGEN_TARGET=%s && python "%s/bin/py.test"' % (tempdir, pydir) @@ -91,7 +92,7 @@ try: output = py.process.cmdexec('%s --apigen="%s/apigen.py" "%s"' % ( cmd, pydir.join('apigen'), - pkgdir)) + pakdir)) except py.error.Error, e: print e.out raise From guido at codespeak.net Sat Jan 27 14:33:41 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 14:33:41 +0100 (CET) Subject: [py-svn] r37432 - py/trunk/py/doc Message-ID: <20070127133341.91A7410075@code0.codespeak.net> Author: guido Date: Sat Jan 27 14:33:40 2007 New Revision: 37432 Modified: py/trunk/py/doc/TODO.txt Log: Some updates on stuff I've worked on, fixed ReST. Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Jan 27 14:33:40 2007 @@ -37,13 +37,14 @@ py.path.extpy -> py.path._extpy py.log -> py._log (pypy!) -* review py.io and write py.io.dupfile docstring (XXX guido) +* review py.io and write py.io.dupfile docstring + + (guido - added docstring, and code looks okay (what was the reason for the + review? anything specific?) * re-consider what to do with read and write methods of py.path classes (since there are places that check for file-ness by doing hasattr(... 'write')) - (XXX guido talk to holger) - packaging ------------------------------------- @@ -83,7 +84,9 @@ testing ----------- -* windows tests (rev 36514 passes without errors, many skips) (XXX guido) +* windows tests (rev 36514 passes without errors, many skips) + + (guido tested again, and fixed newly broken tests, all work again now) * these should all work on 0.9 and on the py lib and pypy: - running "py.test -s" @@ -91,6 +94,9 @@ - running "py.test --looponfailing" - running "py.test" distributed on some hosts + (guido tested all on win32, everything works except --dist (requires + os.fork to work)) + * see why startcapture() does not use FD-based "py.io.OutErrCapture" to isolate standard output. use that check if all py and PyPy tests pass @@ -112,10 +118,14 @@ * (optional) see if more of py/test/session.py's Session can be reused * have dist_rsyncroots be relative to the conftest.py file so that projects can define it for themselves, e.g. - pypy/conftest.py would contain + pypy/conftest.py would contain:: + dist_rsyncroots = ['../pypy', '../py'] - for this there probably needs to be a + + for this there probably needs to be a:: + config.getvalue_and_conftestpath() + method with tests and documentation, and with providing the right example. @@ -202,7 +212,6 @@ execnet.PopenGateway.__repr__ misses a docstring execnet.PopenGateway.__init__ misses a docstring initpkg misses a docstring - io.dupfile misses a docstring log.setconsumer misses a docstring log.get misses a docstring log.Syslog misses a docstring From fijal at codespeak.net Sat Jan 27 15:02:08 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 27 Jan 2007 15:02:08 +0100 (CET) Subject: [py-svn] r37433 - in py/trunk/py/test: . testing Message-ID: <20070127140208.C5AB310075@code0.codespeak.net> Author: fijal Date: Sat Jan 27 15:01:45 2007 New Revision: 37433 Modified: py/trunk/py/test/config.py py/trunk/py/test/conftesthandle.py py/trunk/py/test/testing/test_conftesthandle.py Log: Add a helper which returns value + path of a dir where value was found Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sat Jan 27 15:01:45 2007 @@ -117,7 +117,14 @@ try: return self._overwrite_dict[name] except KeyError: - return self.conftest.rget(name, path) + return self.conftest.rget(name, path) + + def getvalue_and_confpath(self, name, path=None): + """ same as previous, but returns conftest's path + as well + """ + val, mod = self.conftest.rget_path(name, path) + return val, py.path.local(mod.__file__).dirpath() def initsession(self): """ return an initialized session object. """ Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Sat Jan 27 15:01:45 2007 @@ -57,19 +57,24 @@ # return self._get(name, modules) def rget(self, name, path=None): + return self._rget(name, path)[0] + + def _rget(self, name, path=None): modules = self.getconftestmodules(path) modules.reverse() - return self._get(name, modules) + return self._get(name, modules) + + def rget_path(self, name, path=None): + return self._rget(name, path) def _get(self, name, modules): - for mod in modules: + for mod in modules: try: - return getattr(mod, name) + return getattr(mod, name), mod except AttributeError: continue raise KeyError, name - def importconfig(configpath): if not configpath.dirpath('__init__.py').check(file=1): # HACK: we don't want any "globally" imported conftest.py, Modified: py/trunk/py/test/testing/test_conftesthandle.py ============================================================================== --- py/trunk/py/test/testing/test_conftesthandle.py (original) +++ py/trunk/py/test/testing/test_conftesthandle.py Sat Jan 27 15:01:45 2007 @@ -65,6 +65,12 @@ #conftest.lget("a") == 1 #conftest.lget("b") == 1 + def test_value_access_path(self): + topdir = self.basedir.join("adir", "b") + conftest = Conftest(topdir) + _, mod = conftest.rget_path("a") + assert py.path.local(mod.__file__).dirpath() == topdir + class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): def setup_class(cls): TestConftestValueAccessGlobal.__dict__['setup_class'](cls) From guido at codespeak.net Sat Jan 27 15:11:55 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 15:11:55 +0100 (CET) Subject: [py-svn] r37434 - in py/trunk/py/rest: . testing Message-ID: <20070127141155.E30E210075@code0.codespeak.net> Author: guido Date: Sat Jan 27 15:11:52 2007 New Revision: 37434 Modified: py/trunk/py/rest/rst.py py/trunk/py/rest/testing/test_rst.py Log: Fixed bug in add() that made that the to-be-added item didn't get added (and yes, I've added a test now ): ), made some error message a bit clearer. Modified: py/trunk/py/rest/rst.py ============================================================================== --- py/trunk/py/rest/rst.py (original) +++ py/trunk/py/rest/rst.py Sat Jan 27 15:11:52 2007 @@ -79,6 +79,7 @@ returns a reference to the child """ + self._add(child) return child def _add(self, child): @@ -349,7 +350,8 @@ if self.rest is None: self.rest = self.find_rest() if self.rest.links.get(self._text, self.target) != self.target: - raise ValueError('link name already in use for a different target') + raise ValueError('link name %r already in use for a different ' + 'target' % (self.target,)) self.rest.links[self._text] = self.target return AbstractText.text(self) Modified: py/trunk/py/rest/testing/test_rst.py ============================================================================== --- py/trunk/py/rest/testing/test_rst.py (original) +++ py/trunk/py/rest/testing/test_rst.py Sat Jan 27 15:11:52 2007 @@ -88,7 +88,7 @@ txt = Strong('bar').text() assert txt == '**bar**' -def test_text_join(): +def test_text_multiple_arguments(): txt = Paragraph(Text("dupa"), Text("dupa")).text() assert txt == "dupa dupa" @@ -97,6 +97,12 @@ txt = txt.join(Text("happen at sea"), Text("you know")) assert txt.text() == "worse things happen at sea you know" +def test_text_add(): + p = Paragraph(Text('grmbl')) + p2 = p.add(Text('grmbl too')) + assert p2.text() == 'grmbl too' + assert p.text() == 'grmbl grmbl too' + def test_paragraph_basic(): txt = Paragraph(Text('spam')).text() assert txt == 'spam' From guido at codespeak.net Sat Jan 27 15:29:20 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 15:29:20 +0100 (CET) Subject: [py-svn] r37435 - py/trunk/py/test Message-ID: <20070127142920.A558810075@code0.codespeak.net> Author: guido Date: Sat Jan 27 15:29:17 2007 New Revision: 37435 Modified: py/trunk/py/test/conftesthandle.py Log: Removing debug print (ended up in the ReST). Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Sat Jan 27 15:29:17 2007 @@ -26,7 +26,7 @@ for arg in args + [current]: anchor = current.join(arg, abs=1) if anchor.check(): # we found some file object - print "initializing conftest from", anchor + #print "initializing conftest from", anchor # conftest-lookups without a path actually mean # lookups with our initial path. self._path2confmods[None] = self.getconftestmodules(anchor) From guido at codespeak.net Sat Jan 27 16:36:23 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 16:36:23 +0100 (CET) Subject: [py-svn] r37437 - in py/trunk/py/rest: . testing Message-ID: <20070127153623.DE24410068@code0.codespeak.net> Author: guido Date: Sat Jan 27 16:36:20 2007 New Revision: 37437 Modified: py/trunk/py/rest/rst.py py/trunk/py/rest/testing/test_rst.py Log: Made stuff a bit more robust by converting non-strings to strings in some places. Modified: py/trunk/py/rest/rst.py ============================================================================== --- py/trunk/py/rest/rst.py (original) +++ py/trunk/py/rest/rst.py Sat Jan 27 16:36:20 2007 @@ -15,6 +15,8 @@ def escape(txt): """escape ReST markup""" + if not isinstance(txt, str) and not isinstance(txt, unicode): + txt = str(txt) # XXX this takes a very naive approach to escaping, but it seems to be # sufficient... for c in '\\*`|:_': @@ -231,6 +233,8 @@ return self.start + text + self.end def escape(self, text): + if not isinstance(text, str) and not isinstance(text, unicode): + text = str(text) if self.start: text = text.replace(self.start, '\\%s' % (self.start,)) if self.end and self.end != self.start: Modified: py/trunk/py/rest/testing/test_rst.py ============================================================================== --- py/trunk/py/rest/testing/test_rst.py (original) +++ py/trunk/py/rest/testing/test_rst.py Sat Jan 27 16:36:20 2007 @@ -429,16 +429,25 @@ def test_title_following_links_empty_line(): expected = """\ -Foo, bar and `baz`_. - -.. _`baz`: http://www.baz.com +Foo, bar and `baz`_ Spam ----- +==== Spam, eggs and spam. + +.. _`baz`: http://www.baz.com + """ txt = Rest(Paragraph("Foo, bar and ", Link("baz", "http://www.baz.com")), - Title('Spam'), Paragraph('Spam, eggs and spam.')) + Title('Spam'), Paragraph('Spam, eggs and spam.')).text() + assert txt == expected checkrest(txt) +def test_nonstring_text(): + expected = """\ +/foo/bar.py +""" + txt = Rest(Paragraph(Text(py.path.local('/foo/bar.py')))).text() + assert txt == expected + From guido at codespeak.net Sat Jan 27 16:37:45 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 16:37:45 +0100 (CET) Subject: [py-svn] r37438 - in py/trunk/py/test: . rsession rsession/testing Message-ID: <20070127153745.A7FBC10068@code0.codespeak.net> Author: guido Date: Sat Jan 27 16:37:43 2007 New Revision: 37438 Modified: py/trunk/py/test/conftesthandle.py py/trunk/py/test/rsession/outcome.py py/trunk/py/test/rsession/rest.py py/trunk/py/test/rsession/testing/test_rest.py Log: Fiddle-fixed --rest, although it works now some cleanups would be nice... Cleaned up the tests a bit and re-enabled some (sucky tests are better than no tests at all, I guess... :| ), re-enabled a debug print after complaints from cfbolz ;) but this time it prints to stderr (to avoid ReST pollution). Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Sat Jan 27 16:37:43 2007 @@ -26,7 +26,7 @@ for arg in args + [current]: anchor = current.join(arg, abs=1) if anchor.check(): # we found some file object - #print "initializing conftest from", anchor + 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) Modified: py/trunk/py/test/rsession/outcome.py ============================================================================== --- py/trunk/py/test/rsession/outcome.py (original) +++ py/trunk/py/test/rsession/outcome.py Sat Jan 27 16:37:43 2007 @@ -27,7 +27,10 @@ tb_info = [self.traceback_entry_repr(x, tbstyle) for x in excinfo.traceback] rec_index = excinfo.traceback.recursionindex() - return (excinfo.type.__name__, str(excinfo.value), (tb_info, rec_index)) + etype = excinfo.type + if hasattr(etype, '__name__'): + etype = etype.__name__ + return (etype, str(excinfo.value), (tb_info, rec_index)) def traceback_entry_repr(self, tb_entry, tb_style): lineno = tb_entry.lineno Modified: py/trunk/py/test/rsession/rest.py ============================================================================== --- py/trunk/py/test/rsession/rest.py (original) +++ py/trunk/py/test/rsession/rest.py Sat Jan 27 16:37:43 2007 @@ -140,7 +140,10 @@ self.add_rest(ListItem('%s: %s' % (item, text))) def get_host(self, event): - return event.channel.gateway.host + try: + return event.channel.gateway.host + except AttributeError: + return None def failures(self): self.traceback_num = 0 @@ -160,7 +163,8 @@ t.add(Link(itempath, link)) else: t.add(Text(itempath)) - t.add(Text('on %s' % (host.hostname,))) + if host: + t.add(Text('on %s' % (host.hostname,))) self.add_rest(t) if event.outcome.signal: self.repr_signal(event.item, event.outcome) Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Sat Jan 27 16:37:43 2007 @@ -11,6 +11,11 @@ from py.__.test.rsession.hostmanage import HostInfo from py.__.test.rsession.outcome import Outcome +class Container(object): + def __init__(self, **args): + for arg, val in args.items(): + setattr(self, arg, val) + class RestTestReporter(RestReporter): def __init__(self, *args, **kwargs): if args: @@ -78,11 +83,6 @@ def listnames(self): return ['package', 'foo', 'bar.py'] - class Container(object): - def __init__(self, **args): - for arg, val in args.items(): - setattr(self, arg, val) - parent = Container(parent=None, fspath=py.path.local('.')) event = report.ItemStart(item=FakeModule(parent)) reporter.report(event) @@ -105,11 +105,6 @@ def test_ReceivedItemOutcome_PASSED(self): outcome = Outcome() - class Container(object): - def __init__(self, **args): - for arg, val in args.items(): - setattr(self, arg, val) - item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) reporter.report(event) @@ -117,11 +112,6 @@ 'foo.py/bar()/baz\n\n') def test_ReceivedItemOutcome_SKIPPED(self): - class Container(object): - def __init__(self, **args): - for arg, val in args.items(): - setattr(self, arg, val) - outcome = Outcome(skipped="reason") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) @@ -130,11 +120,6 @@ 'foo.py/bar()/baz\n\n') def test_ReceivedItemOutcome_FAILED(self): - class Container(object): - def __init__(self, **args): - for arg, val in args.items(): - setattr(self, arg, val) - outcome = Outcome(excinfo="xxx") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) @@ -143,13 +128,45 @@ * localhost\: **FAILED** `traceback0`_ foo.py/bar()/baz """ - - def test_skips(self): - class Container(object): - def __init__(self, **args): - for arg, val in args.items(): - setattr(self, arg, val) + def test_ReceivedItemOutcome_FAILED_stdout(self): + excinfo = Container( + typename='FooError', + value='A foo has occurred', + traceback=[ + Container( + path='foo/bar.py', + lineno=1, + relline=1, + source='foo()', + ), + Container( + path='foo/baz.py', + lineno=4, + relline=1, + source='raise FooError("A foo has occurred")', + ), + ] + ) + outcome = Outcome(excinfo=excinfo) + outcome.stdout = '' + outcome.stderr = '' + parent = Container(parent=None, fspath=py.path.local('.')) + item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz'], + parent=parent, fspath=py.path.local('foo')) + event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, + item=item) + reporter.report(event) + reporter.timestart = 10 + reporter.timeend = 20 + reporter.timersync = 15 + reporter.print_summary(10, '', '') + + reporter.print_summary(1, 'skipped', 'failed') + out = stdout.getvalue() + assert out.find('') > -1 + + def test_skips(self): class FakeOutcome(Container, report.ReceivedItemOutcome): pass @@ -175,12 +192,6 @@ """ def test_failures(self): - py.test.skip("This one is totally artificial, needs to be rewritten") - class Container(object): - def __init__(self, **args): - for arg, val in args.items(): - setattr(self, arg, val) - class FakeOutcome(Container, report.ReceivedItemOutcome): pass From guido at codespeak.net Sat Jan 27 16:40:26 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 16:40:26 +0100 (CET) Subject: [py-svn] r37439 - py/trunk/py/test Message-ID: <20070127154026.B7CEA10068@code0.codespeak.net> Author: guido Date: Sat Jan 27 16:40:25 2007 New Revision: 37439 Modified: py/trunk/py/test/conftesthandle.py Log: Sorry, cfbolz, seems the print to stderr breaks some test... :| Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Sat Jan 27 16:40:25 2007 @@ -26,7 +26,7 @@ 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 + #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) From fijal at codespeak.net Sat Jan 27 18:33:45 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 27 Jan 2007 18:33:45 +0100 (CET) Subject: [py-svn] r37442 - py/trunk/py/doc Message-ID: <20070127173345.7E60910075@code0.codespeak.net> Author: fijal Date: Sat Jan 27 18:33:41 2007 New Revision: 37442 Modified: py/trunk/py/doc/TODO.txt Log: update Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Jan 27 18:33:41 2007 @@ -102,18 +102,21 @@ use that check if all py and PyPy tests pass as good as they do without. -* make --box run on the trunk +* make --box run on the trunk. This requires having some sort of + is_boxed() function, while having it on config object seems + to be not that smart idea. * (DONE more or less) try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) -* have all sessions check their options via Session.fixoptions() - and have session-particular tests and checks accordingly. +* (DONE for rsession) have all sessions check their options via + Session.fixoptions() and have session-particular tests and checks + accordingly. distributed testing / RSession ------------------------------------ -* cleanup initialisation of config / get rid of pkgdir +* (DONE, except apigen) cleanup initialisation of config / get rid of pkgdir * unify option names (dist_*) * (optional) see if more of py/test/session.py's Session can be reused * have dist_rsyncroots be relative to the conftest.py file From hpk at codespeak.net Sat Jan 27 19:41:41 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 19:41:41 +0100 (CET) Subject: [py-svn] r37443 - py/trunk/py/test/terminal Message-ID: <20070127184141.3078810075@code0.codespeak.net> Author: hpk Date: Sat Jan 27 19:41:39 2007 New Revision: 37443 Modified: py/trunk/py/test/terminal/remote.py Log: slightly re-organizing RemoteTerminalSession for easier subclassing. Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Sat Jan 27 19:41:39 2007 @@ -90,30 +90,40 @@ print "# ", "^" * len(str(rootdir)) return failures - def run_remote_session(self, failures): + def _initslavegateway(self): print "* opening PopenGateway: ", self.executable - gw = py.execnet.PopenGateway(self.executable) + topdir = self.config.topdir + return py.execnet.PopenGateway(self.executable), topdir + + def run_remote_session(self, failures): + gw, topdir = self._initslavegateway() channel = gw.remote_exec(""" from py.__.test.terminal.remote import slaverun_TerminalSession slaverun_TerminalSession(channel) """, stdout=self.out, stderr=self.out) - print "MASTER: triggered slave terminal session ->" + print "MASTER: initiated slave terminal session ->" repr = self.config.make_repr(conftestnames=[]) - channel.send((str(self.config.topdir), repr, failures)) - print "MASTER: send start info" + channel.send((str(topdir), repr, failures)) + print "MASTER: send start info, 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 [] def slaverun_TerminalSession(channel): """ we run this on the other side. """ - print "SLAVE: starting" + print "SLAVE: initializing ..." topdir, repr, failures = channel.receive() - print "SLAVE: received configuration" + print "SLAVE: received configuration, using topdir:", topdir config = py.test.config + import sys + sys.stdout.flush() config.initdirect(topdir, repr, failures) + config.option.session = None config.option.looponfailing = False config.option.usepdb = False config.option.executable = None From hpk at codespeak.net Sat Jan 27 19:57:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 19:57:07 +0100 (CET) Subject: [py-svn] r37444 - py/trunk/py/execnet/script Message-ID: <20070127185707.6AC9B10068@code0.codespeak.net> Author: hpk Date: Sat Jan 27 19:57:05 2007 New Revision: 37444 Added: py/trunk/py/execnet/script/loop_socketserver.py Modified: py/trunk/py/execnet/script/socketserver.py Log: a try at a wrapping loop_socketserver.py that restarts the actual socketserver.py Added: py/trunk/py/execnet/script/loop_socketserver.py ============================================================================== --- (empty file) +++ py/trunk/py/execnet/script/loop_socketserver.py Sat Jan 27 19:57:05 2007 @@ -0,0 +1,12 @@ + +import os, sys + +if __name__ == '__main__': + directory = os.path.dirname(os.path.abspath(sys.argv[0])) + script = os.path.join(directory, 'socketserver.py') + while 1: + cmd = "python %s %s" % (script, " ".join(sys.argv[1:])) + print "starting subcommand:", cmd + f = os.popen(cmd) + for line in f: + print line, Modified: py/trunk/py/execnet/script/socketserver.py ============================================================================== --- py/trunk/py/execnet/script/socketserver.py (original) +++ py/trunk/py/execnet/script/socketserver.py Sat Jan 27 19:57:05 2007 @@ -53,8 +53,8 @@ old = fcntl.fcntl(serversock.fileno(), fcntl.F_GETFD) fcntl.fcntl(serversock.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) # allow the address to be re-used in a reasonable amount of time - if os.name == 'posix' and sys.platform != 'cygwin': - serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + #if os.name == 'posix' and sys.platform != 'cygwin': + serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) serversock.bind(hostport) serversock.listen(5) @@ -83,5 +83,5 @@ else: hostport = ':8888' serversock = bind_and_listen(hostport) - startserver(serversock, loop=True) + startserver(serversock, loop=False) From hpk at codespeak.net Sat Jan 27 20:13:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 20:13:23 +0100 (CET) Subject: [py-svn] r37445 - py/trunk/py/execnet/script Message-ID: <20070127191323.00B2D10071@code0.codespeak.net> Author: hpk Date: Sat Jan 27 20:13:17 2007 New Revision: 37445 Modified: py/trunk/py/execnet/script/loop_socketserver.py Log: use suprocess module to start socketserver subprocess Modified: py/trunk/py/execnet/script/loop_socketserver.py ============================================================================== --- py/trunk/py/execnet/script/loop_socketserver.py (original) +++ py/trunk/py/execnet/script/loop_socketserver.py Sat Jan 27 20:13:17 2007 @@ -1,12 +1,13 @@ import os, sys +import subprocess if __name__ == '__main__': directory = os.path.dirname(os.path.abspath(sys.argv[0])) script = os.path.join(directory, 'socketserver.py') while 1: - cmd = "python %s %s" % (script, " ".join(sys.argv[1:])) - print "starting subcommand:", cmd - f = os.popen(cmd) - for line in f: - print line, + cmdlist = ["python", script] + cmdlist.extend(sys.argv[1:]) + print "starting subcommand:", " ".join(cmdlist) + process = subprocess.Popen(cmdlist) + process.wait() From hpk at codespeak.net Sat Jan 27 20:22:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 20:22:25 +0100 (CET) Subject: [py-svn] r37446 - py/trunk/py/execnet/script Message-ID: <20070127192225.682B810071@code0.codespeak.net> Author: hpk Date: Sat Jan 27 20:22:23 2007 New Revision: 37446 Modified: py/trunk/py/execnet/script/socketserver.py Log: don't try this on windows Modified: py/trunk/py/execnet/script/socketserver.py ============================================================================== --- py/trunk/py/execnet/script/socketserver.py (original) +++ py/trunk/py/execnet/script/socketserver.py Sat Jan 27 20:22:23 2007 @@ -53,8 +53,8 @@ old = fcntl.fcntl(serversock.fileno(), fcntl.F_GETFD) fcntl.fcntl(serversock.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) # allow the address to be re-used in a reasonable amount of time - #if os.name == 'posix' and sys.platform != 'cygwin': - serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + if os.name == 'posix' and sys.platform != 'cygwin': + serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) serversock.bind(hostport) serversock.listen(5) From hpk at codespeak.net Sat Jan 27 20:24:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 20:24:42 +0100 (CET) Subject: [py-svn] r37447 - py/trunk/py/test/testing Message-ID: <20070127192442.B04EF10071@code0.codespeak.net> Author: hpk Date: Sat Jan 27 20:24:41 2007 New Revision: 37447 Modified: py/trunk/py/test/testing/test_repr.py Log: config._reparse() from current dir can drag in conftest.py files (happened to me because i had two py lib checkouts) Modified: py/trunk/py/test/testing/test_repr.py ============================================================================== --- py/trunk/py/test/testing/test_repr.py (original) +++ py/trunk/py/test/testing/test_repr.py Sat Jan 27 20:24:41 2007 @@ -5,12 +5,18 @@ from StringIO import StringIO import sys +def newconfig(*args): + tmpdir = py.test.ensuretemp("newconfig") + args = list(args) + args.append(tmpdir) + return py.test.config._reparse(args) + def test_repr_source(): source = py.code.Source(""" def f(x): pass """).strip() - config = py.test.config._reparse([]) + config = newconfig() s = StringIO() out = getout(s) p = Presenter(out, config) @@ -32,7 +38,7 @@ except: e = py.code.ExceptionInfo() return e - config = py.test.config._reparse([]) + config = newconfig() s = StringIO() out = getout(s) p = Presenter(out, config) @@ -42,7 +48,7 @@ assert s.getvalue().startswith("> ") def test_repr_local(): - config = py.test.config._reparse(['--showlocals']) + config = newconfig('--showlocals') s = StringIO() out = getout(s) p = Presenter(out, config) From hpk at codespeak.net Sat Jan 27 21:11:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 21:11:34 +0100 (CET) Subject: [py-svn] r37449 - py/trunk/py/doc Message-ID: <20070127201134.1437410071@code0.codespeak.net> Author: hpk Date: Sat Jan 27 21:11:26 2007 New Revision: 37449 Modified: py/trunk/py/doc/conftest.py Log: fix failures in case of docutils not being installed (related to importing 'py.__.rest.directive') Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Sat Jan 27 21:11:26 2007 @@ -1,7 +1,6 @@ from __future__ import generators import py from py.__.misc import rest -from py.__.rest import directive Option = py.test.config.Option option = py.test.config.addoptions("documentation check options", @@ -15,11 +14,18 @@ ) ) +_initialized = False def checkdocutils(): + global _initialized try: import docutils except ImportError: py.test.skip("docutils not importable") + if not _initialized: + from py.__.rest import directive + directive.register_linkrole('api', resolve_linkrole) + directive.register_linkrole('source', resolve_linkrole) + _initialized = True def restcheck(path): localpath = path @@ -251,6 +257,4 @@ else: relpath += '.html' return text, '../../apigen/source/%s' % (relpath,) -directive.register_linkrole('api', resolve_linkrole) -directive.register_linkrole('source', resolve_linkrole) From hpk at codespeak.net Sat Jan 27 21:15:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 27 Jan 2007 21:15:25 +0100 (CET) Subject: [py-svn] r37450 - py/trunk/py/misc/testing Message-ID: <20070127201525.B58BD10071@code0.codespeak.net> Author: hpk Date: Sat Jan 27 21:15:21 2007 New Revision: 37450 Modified: py/trunk/py/misc/testing/test_initpkg.py Log: skip auto-import checks of directive.py because it requires docutils 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 Sat Jan 27 21:15:21 2007 @@ -57,6 +57,7 @@ base.join('path', 'extpy', 'testing', 'test_data'), base.join('path', 'gateway',), base.join('doc',), + base.join('rest', 'directive.py'), base.join('test', 'testing', 'import_test'), base.join('c-extension',), base.join('magic', 'greenlet.py'), From guido at codespeak.net Sat Jan 27 23:50:51 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 27 Jan 2007 23:50:51 +0100 (CET) Subject: [py-svn] r37453 - py/trunk/py/rest/testing Message-ID: <20070127225051.1E54F10071@code0.codespeak.net> Author: guido Date: Sat Jan 27 23:50:49 2007 New Revision: 37453 Modified: py/trunk/py/rest/testing/test_rst.py Log: Changed test a bit to avoid issues on non-UNIX platforms. Modified: py/trunk/py/rest/testing/test_rst.py ============================================================================== --- py/trunk/py/rest/testing/test_rst.py (original) +++ py/trunk/py/rest/testing/test_rst.py Sat Jan 27 23:50:49 2007 @@ -446,8 +446,11 @@ def test_nonstring_text(): expected = """\ -/foo/bar.py + """ - txt = Rest(Paragraph(Text(py.path.local('/foo/bar.py')))).text() + class FooBar(object): + def __str__(self): + return '' + txt = Rest(Paragraph(Text(FooBar()))).text() assert txt == expected From hpk at codespeak.net Sun Jan 28 09:46:36 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 09:46:36 +0100 (CET) Subject: [py-svn] r37455 - py/trunk/py/misc Message-ID: <20070128084636.853A810068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 09:46:34 2007 New Revision: 37455 Added: py/trunk/py/misc/conftest-socketgatewayrun.py Log: added a sample conftest.py to run windows tests remotely, note the module docstring. Added: py/trunk/py/misc/conftest-socketgatewayrun.py ============================================================================== --- (empty file) +++ py/trunk/py/misc/conftest-socketgatewayrun.py Sun Jan 28 09:46:34 2007 @@ -0,0 +1,58 @@ +""" + +Put this file as 'conftest.py' somewhere upwards from py-trunk, +modify the "socketserveradr" below to point to a windows/linux +host running "py/execnet/script/loop_socketserver.py" +and invoke e.g. from linux: + + py.test --session=MySession some_path_to_what_you_want_to_test + +This should ad-hoc distribute the running of tests to +the remote machine (including rsyncing your WC). + +""" +import py +from py.__.test.terminal.remote import RemoteTerminalSession + +import os + +class MyRSync(py.execnet.RSync): + def filter(self, path): + if path.endswith('.pyc') or path.endswith('~'): + return False + dir, base = os.path.split(path) + # we may want to have revision info on the other side, + # so let's not exclude .svn directories + #if base == '.svn': + # return False + return True + +class MySession(RemoteTerminalSession): + socketserveradr = ('10.9.4.148', 8888) + + def _initslavegateway(self): + print "MASTER: initializing remote socket gateway" + gw = py.execnet.SocketGateway(*self.socketserveradr) + rsync = MyRSync(delete=True) + channel = gw.remote_exec(""" + import os + path = os.path.join(os.environ['HOMEPATH'], 'pytestcache') + channel.send(path) + """) + remotetopdir = channel.receive() + rsync.add_target(gw, remotetopdir) + sendpath = py.path.local(py.__file__).dirpath().dirpath() + rsync.send(sendpath) + channel = gw.remote_exec(""" + import os, sys + path = %r # os.path.abspath + sys.path.insert(0, path) + os.chdir(path) + import py + channel.send((path, py.__file__)) + """ % remotetopdir) + topdir, remotepypath = channel.receive() + assert topdir == remotetopdir, (topdir, remotetopdir) + assert remotepypath.startswith(topdir), (remotepypath, topdir) + #print "remote side has rsynced pythonpath ready: %r" %(topdir,) + return gw, topdir From hpk at codespeak.net Sun Jan 28 09:49:00 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 09:49:00 +0100 (CET) Subject: [py-svn] r37456 - py/trunk/py/execnet Message-ID: <20070128084900.64A1B10068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 09:48:59 2007 New Revision: 37456 Modified: py/trunk/py/execnet/gateway.py py/trunk/py/execnet/register.py Log: some small adjustments/cleanups regarding intiialisation Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Sun Jan 28 09:48:59 2007 @@ -55,12 +55,16 @@ addr = '[%s]' % (addr,) else: addr = '' - r = (len(self.pool.getstarted('receiver')) - and "receiving" or "not receiving") - s = (len(self.pool.getstarted('sender')) - and "sending" or "not sending") - i = len(self.channelfactory.channels()) - return "<%s%s %s/%s (%d active channels)>" %( + try: + r = (len(self.pool.getstarted('receiver')) + and "receiving" or "not receiving") + s = (len(self.pool.getstarted('sender')) + and "sending" or "not sending") + i = len(self.channelfactory.channels()) + except AttributeError: + r = s = "uninitialized" + i = "no" + return "<%s%s %s/%s (%s active channels)>" %( self.__class__.__name__, addr, r, s, i) def _getremoteaddress(self): Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Sun Jan 28 09:48:59 2007 @@ -40,7 +40,7 @@ gateway starts with odd numbers. This allows to uniquely identify channels across both sides. """ - bootstrap = ["we_are_remote=True", extra] + bootstrap = [extra] bootstrap += [getsource(x) for x in startup_modules] bootstrap += [io.server_stmt, "Gateway(io=io, startcount=2).join(joinexec=False)",] source = "\n".join(bootstrap) @@ -135,7 +135,7 @@ self.port = port = int(port) sock.connect((host, port)) io = inputoutput.SocketIO(sock) - InstallableGateway.__init__(self, io=io) + super(SocketGateway, self).__init__(io=io) def _getremoteaddress(self): return '%s:%d' % (self.host, self.port) From hpk at codespeak.net Sun Jan 28 09:49:29 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 09:49:29 +0100 (CET) Subject: [py-svn] r37457 - py/trunk/py/test/terminal Message-ID: <20070128084929.F004110071@code0.codespeak.net> Author: hpk Date: Sun Jan 28 09:49:28 2007 New Revision: 37457 Modified: py/trunk/py/test/terminal/remote.py Log: explicitely shutdown the gateway for the remote session after each run. Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Sun Jan 28 09:49:28 2007 @@ -101,18 +101,21 @@ from py.__.test.terminal.remote import slaverun_TerminalSession slaverun_TerminalSession(channel) """, stdout=self.out, stderr=self.out) - print "MASTER: initiated slave terminal session ->" - repr = self.config.make_repr(conftestnames=[]) - channel.send((str(topdir), repr, failures)) - print "MASTER: send start info, 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 [] + print "MASTER: initiated slave terminal session ->" + repr = self.config.make_repr(conftestnames=[]) + channel.send((str(topdir), repr, failures)) + print "MASTER: send start info, 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): """ we run this on the other side. """ From hpk at codespeak.net Sun Jan 28 10:07:24 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 10:07:24 +0100 (CET) Subject: [py-svn] r37458 - py/trunk/py/misc Message-ID: <20070128090724.5BD7410064@code0.codespeak.net> Author: hpk Date: Sun Jan 28 10:07:22 2007 New Revision: 37458 Modified: py/trunk/py/misc/conftest-socketgatewayrun.py Log: try to only rsync the py lib, not the dirpath() of it Modified: py/trunk/py/misc/conftest-socketgatewayrun.py ============================================================================== --- py/trunk/py/misc/conftest-socketgatewayrun.py (original) +++ py/trunk/py/misc/conftest-socketgatewayrun.py Sun Jan 28 10:07:22 2007 @@ -34,14 +34,16 @@ print "MASTER: initializing remote socket gateway" gw = py.execnet.SocketGateway(*self.socketserveradr) rsync = MyRSync(delete=True) + pkgname = 'py' # xxx flexibilize channel = gw.remote_exec(""" import os - path = os.path.join(os.environ['HOMEPATH'], 'pytestcache') - channel.send(path) - """) - remotetopdir = channel.receive() - rsync.add_target(gw, remotetopdir) - sendpath = py.path.local(py.__file__).dirpath().dirpath() + topdir = os.path.join(os.environ['HOMEPATH'], 'pytestcache') + pkgdir = os.path.join(topdir, %r) + channel.send((topdir, pkgdir)) + """ % (pkgname,)) + remotetopdir, remotepkgdir = channel.receive() + rsync.add_target(gw, remotepkgdir) + sendpath = py.path.local(py.__file__).dirpath() rsync.send(sendpath) channel = gw.remote_exec(""" import os, sys From hpk at codespeak.net Sun Jan 28 11:14:24 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 11:14:24 +0100 (CET) Subject: [py-svn] r37459 - in py/trunk/py/execnet: . testing Message-ID: <20070128101424.27C9C10068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 11:14:22 2007 New Revision: 37459 Modified: py/trunk/py/execnet/register.py py/trunk/py/execnet/testing/test_gateway.py Log: fix socketgateway test to run on both win32 and linux Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Sun Jan 28 11:14:22 2007 @@ -145,7 +145,7 @@ given gateway. """ if hostport is None: - host, port = ('', 0) + host, port = ('', 0) # XXX works on all platforms? else: host, port = hostport socketserverbootstrap = py.code.Source( @@ -153,18 +153,16 @@ """ import socket sock = bind_and_listen((%r, %r)) - hostname = socket.gethostname() - channel.send((hostname, sock.getsockname())) + port = sock.getsockname() + channel.send(port) startserver(sock) """ % (host, port)) # execute the above socketserverbootstrap on the other side channel = gateway.remote_exec(socketserverbootstrap) - hostname, (realhost, realport) = channel.receive() - if not hostname: - hostname = realhost + (realhost, realport) = channel.receive() #gateway._trace("remote_install received" # "port=%r, hostname = %r" %(realport, hostname)) - return py.execnet.SocketGateway(hostname, realport) + return py.execnet.SocketGateway(host, realport) remote_install = classmethod(remote_install) class SshGateway(PopenCmdGateway): 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 Sun Jan 28 11:14:22 2007 @@ -437,7 +437,9 @@ def setup_class(cls): # open a gateway to a fresh child process cls.proxygw = py.execnet.PopenGateway() - cls.gw = py.execnet.SocketGateway.remote_install(cls.proxygw) + cls.gw = py.execnet.SocketGateway.remote_install(cls.proxygw, + ("127.0.0.1", 0) + ) ## def teardown_class(cls): ## cls.gw.exit() From hpk at codespeak.net Sun Jan 28 11:32:02 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 11:32:02 +0100 (CET) Subject: [py-svn] r37460 - py/trunk/py/test/rsession/testing Message-ID: <20070128103202.1AAD710068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 11:32:01 2007 New Revision: 37460 Modified: py/trunk/py/test/rsession/testing/test_reporter.py Log: these tests rely on exact formatting, so skipping them again (fail for me on pypy2) Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Sun Jan 28 11:32:01 2007 @@ -202,7 +202,7 @@ self._test_still_to_go() def test_report_received_item_outcome(self): - #py.test.skip("XXX rewrite test to not rely on exact formatting") + py.test.skip("XXX rewrite test to not rely on exact formatting") val = self.report_received_item_outcome() expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass localhost: SKIPPED py.test.rsession.testing.test_slave.py funcpass @@ -212,6 +212,7 @@ assert val.find(expected) != -1 def test_module(self): + py.test.skip("XXX rewrite test to not rely on exact formatting") val = self._test_module() print val expected = """ localhost: FAILED py.test.rsession.testing.test_slave.py funcpass From hpk at codespeak.net Sun Jan 28 11:42:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 11:42:14 +0100 (CET) Subject: [py-svn] r37461 - py/trunk/py/apigen/testing Message-ID: <20070128104214.B474F10068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 11:42:13 2007 New Revision: 37461 Modified: py/trunk/py/apigen/testing/test_apigen_example.py Log: skip these tests on windows (some reliances on unix-paths and i don't think apigen can run on windows anyway at the moment) 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 Sun Jan 28 11:42:13 2007 @@ -8,6 +8,10 @@ from py.__.test.web import webcheck from py.__.apigen.conftest import option +def setup_module(mod): + if py.std.sys.platform == "win32": + py.test.skip("apigen does not fully support win32 yet") + def run_string_sequence_test(data, seq): currpos = -1 for s in seq: From hpk at codespeak.net Sun Jan 28 12:00:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 12:00:19 +0100 (CET) Subject: [py-svn] r37462 - in py/trunk/py/test: . terminal Message-ID: <20070128110019.F1A3010068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 12:00:17 2007 New Revision: 37462 Modified: py/trunk/py/test/config.py py/trunk/py/test/terminal/remote.py Log: allow the remote side to run in verbose mode Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Jan 28 12:00:17 2007 @@ -226,6 +226,7 @@ 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) Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Sun Jan 28 12:00:17 2007 @@ -130,6 +130,8 @@ config.option.looponfailing = False config.option.usepdb = False config.option.executable = None + if failures: + config.option.verbose = True session = config.initsession() session.shouldclose = channel.isclosed From hpk at codespeak.net Sun Jan 28 12:02:21 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 12:02:21 +0100 (CET) Subject: [py-svn] r37463 - py/trunk/py/apigen/tracer/testing Message-ID: <20070128110221.0C6DD10063@code0.codespeak.net> Author: hpk Date: Sun Jan 28 12:02:19 2007 New Revision: 37463 Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py Log: test_get_initpkg_star_items() fails for me when run as part of the whole py lib tests, succeeds when ran as a single test. Pruning sys.modules fixes this (but i am not sure why the tracer depends on seeing the import happen) Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_docgen.py (original) +++ py/trunk/py/apigen/tracer/testing/test_docgen.py Sun Jan 28 12:02:19 2007 @@ -415,6 +415,11 @@ def setup_pkg_docstorage(): pkgdir, pkgname = setup_fs_project() py.std.sys.path.insert(0, str(pkgdir)) + # XXX test_get_initpkg_star_items depends on package not + # being imported already + for key in py.std.sys.modules.keys(): + if key == pkgname or key.startswith(pkgname + "."): + del py.std.sys.modules[key] pkg = __import__(pkgname) ds = DocStorage().from_pkg(pkg) return pkg, ds From hpk at codespeak.net Sun Jan 28 14:25:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 14:25:07 +0100 (CET) Subject: [py-svn] r37465 - in py/trunk/py/test: . rsession terminal testing Message-ID: <20070128132507.AC1FE10068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 14:25:03 2007 New Revision: 37465 Added: py/trunk/py/test/testing/test_remote.py - copied, changed from r37463, py/trunk/py/test/testing/test_session.py Modified: py/trunk/py/test/config.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/session.py py/trunk/py/test/terminal/remote.py py/trunk/py/test/testing/setupdata.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_session.py Log: * all Sessions now have fixoptions() * added some tests for implied and conflicting options * make all Session objects inherit from the base session * small cleanups with respect to test setup * separate tests a bit Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Jan 28 14:25:03 2007 @@ -48,7 +48,6 @@ args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) - fixoptions(self.option) # XXX fixing should be moved to sessions if not args: args.append(py.std.os.getcwd()) self.topdir = gettopdir(args) @@ -130,9 +129,7 @@ """ return an initialized session object. """ cls = self._getsessionclass() session = cls(self) - # XXX: all sessions should have one - if hasattr(session, 'fixoptions'): - session.fixoptions() + session.fixoptions() return session def _getsessionclass(self): @@ -272,28 +269,6 @@ except ValueError: raise ValueError("%s=%r is not marshallable" %(name, value)) -def fixoptions(option): - """ sanity checks and making option values canonical. """ - - # implied options - if option.usepdb: - if not option.nocapture: - #print "--pdb implies --nocapture" - option.nocapture = True - - if option.runbrowser and not option.startserver: - #print "--runbrowser implies --startserver" - option.startserver = 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." - - def gettopdir(args): """ return the top directory for the given paths. if the common base dir resides in a python package Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Sun Jan 28 14:25:03 2007 @@ -16,15 +16,23 @@ from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner from py.__.test.rsession.reporter import LocalReporter, RemoteReporter +from py.__.test.session import Session -class AbstractSession(object): +class AbstractSession(Session): """ An abstract session executes collectors/items through a runner. """ def __init__(self, config, optimise_localhost=True): - self.config = config + super(AbstractSession, self).__init__(config=config) self.optimise_localhost = optimise_localhost + + def fixoptions(self): + option = self.config.option + if option.runbrowser and not option.startserver: + #print "--runbrowser implies --startserver" + option.startserver = True + super(AbstractSession, self).fixoptions() def getpkgdir(path): path = py.path.local(path) @@ -122,6 +130,7 @@ """ Remote version of session """ def fixoptions(self): + super(RSession, self).fixoptions() config = self.config try: config.getvalue('disthosts') Modified: py/trunk/py/test/session.py ============================================================================== --- py/trunk/py/test/session.py (original) +++ py/trunk/py/test/session.py Sun Jan 28 14:25:03 2007 @@ -23,6 +23,21 @@ if not self.config.option.nomagic: py.magic.revoke(assertion=1) + def fixoptions(self): + """ check, fix and determine conflicting options. """ + option = self.config.option + # implied options + if option.usepdb: + if not option.nocapture: + option.nocapture = 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." + def start(self, colitem): """ hook invoked before each colitem.run() invocation. """ Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Sun Jan 28 14:25:03 2007 @@ -1,7 +1,7 @@ from __future__ import generators import py +from py.__.test.session import Session from py.__.test.terminal.out import getout -import sys def checkpyfilechange(rootdir, statcache={}): """ wait until project files are changed. """ @@ -51,9 +51,9 @@ l.append(current) return l -class RemoteTerminalSession(object): +class RemoteTerminalSession(Session): def __init__(self, config, file=None): - self.config = config + super(RemoteTerminalSession, self).__init__(config=config) self._setexecutable() if file is None: file = py.std.sys.stdout @@ -123,8 +123,6 @@ topdir, repr, failures = channel.receive() print "SLAVE: received configuration, using topdir:", topdir config = py.test.config - import sys - sys.stdout.flush() config.initdirect(topdir, repr, failures) config.option.session = None config.option.looponfailing = False Modified: py/trunk/py/test/testing/setupdata.py ============================================================================== --- py/trunk/py/test/testing/setupdata.py (original) +++ py/trunk/py/test/testing/setupdata.py Sun Jan 28 14:25:03 2007 @@ -1,13 +1,20 @@ import py +def setup_module(mod): + mod.datadir = setupdatadir() + mod.tmpdir = py.test.ensuretemp(mod.__name__) + def setupdatadir(): datadir = py.test.ensuretemp("datadir") - if not datadir.listdir(): - for name, content in namecontent: + names = [x.basename for x in datadir.listdir()] + for name, content in namecontent: + if name not in names: datadir.join(name).write(content) return datadir namecontent = [ +('syntax_error.py', "this is really not python\n"), + ('disabled_module.py', py.code.Source(''' disabled = True 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 Sun Jan 28 14:25:03 2007 @@ -231,7 +231,8 @@ def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" - class MySession: + from py.__.test.session import Session + class MySession(Session): def __init__(self, config): self.config = config """)) @@ -357,3 +358,4 @@ col3 = config._getcollector(config.topdir.dirpath()) py.test.raises(ValueError, "config.get_collector_trail(col3)") + Copied: py/trunk/py/test/testing/test_remote.py (from r37463, py/trunk/py/test/testing/test_session.py) ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_remote.py Sun Jan 28 14:25:03 2007 @@ -1,316 +1,7 @@ import py -from setupdata import setupdatadir - -def setup_module(mod): - mod.datadir = setupdatadir() - mod.tmpdir = py.test.ensuretemp(mod.__name__) - -def test_default_session_options(): - for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], - ['--tb=long'], ['--fulltrace'], ['--nomagic'], - ['--traceconfig'], ['-v'], ['-v', '-v']): - yield runfiletest, opts - -def runfiletest(opts): - config = py.test.config._reparse(opts + [datadir/'filetest.py']) - session = config.initsession() - session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 2 - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert not l - -class TestKeywordSelection: - def test_select_simple(self): - for keyword in ['test_one', 'est_on']: - config = py.test.config._reparse([datadir/'filetest.py', - '-k', keyword]) - session = config._getsessionclass()(config, py.std.sys.stdout) - session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 1 - item = l[0][0] - assert item.name == 'test_one' - l = session.getitemoutcomepairs(py.test.Item.Skipped) - assert len(l) == 1 - - def test_select_extra_keywords(self): - o = tmpdir.ensure('selecttest', dir=1) - tfile = o.join('test_select.py').write(py.code.Source(""" - def test_1(): - pass - class TestClass: - def test_2(self): - pass - """)) - conftest = o.join('conftest.py').write(py.code.Source(""" - import py - class Class(py.test.collect.Class): - def haskeyword(self, keyword): - return keyword == 'xxx' or \ - super(Class, self).haskeyword(keyword) - """)) - for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', - 'TestClass test_2', 'xxx TestClass test_2',): - f = py.std.StringIO.StringIO() - config = py.test.config._reparse([o, '-k', keyword]) - session = config._getsessionclass()(config, f) - session.main() - print "keyword", repr(keyword) - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert len(l) == 1 - assert l[0][0].name == 'test_2' - l = session.getitemoutcomepairs(py.test.Item.Skipped) - assert l[0][0].name == 'test_1' - -#f = open('/tmp/logfile', 'wa') -class TestTerminalSession: - - def setup_class(cls): - (datadir / 'syntax_error.py').write("\nthis is really not python\n") - - def teardown_class(cls): - (datadir / 'syntax_error.py').remove() - - def mainsession(self, *args): - from py.__.test.terminal.terminal import TerminalSession - self.file = py.std.StringIO.StringIO() - config = py.test.config._reparse(list(args)) - session = TerminalSession(config, file=self.file) - session.main() - return session - - def test_terminal(self): - session = self.mainsession(datadir / 'filetest.py') - out = self.file.getvalue() - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 2 - assert out.find('2 failed') != -1 - - def test_syntax_error_module(self): - session = self.mainsession(datadir / 'syntax_error.py') - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 1 - out = self.file.getvalue() - assert out.find(str('syntax_error.py')) != -1 - assert out.find(str('not python')) != -1 - - def test_exit_first_problem(self): - session = self.mainsession("--exitfirst", - datadir / 'filetest.py') - assert session.config.option.exitfirst - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 1 - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert not l - - def test_collectonly(self): - session = self.mainsession("--collectonly", - datadir / 'filetest.py') - assert session.config.option.collectonly - out = self.file.getvalue() - #print out - l = session.getitemoutcomepairs(py.test.Item.Failed) - #if l: - # x = l[0][1].excinfo - # print x.exconly() - # print x.traceback - assert len(l) == 0 - for line in ('filetest.py', 'test_one', - 'TestClass', 'test_method_one'): - assert out.find(line) - - def test_recursion_detection(self): - o = tmpdir.ensure('recursiontest', dir=1) - tfile = o.join('test_recursion.py') - tfile.write(py.code.Source(""" - def test_1(): - def f(): - g() - def g(): - f() - f() - """)) - session = self.mainsession(o) - print "back from main", o - out = self.file.getvalue() - #print out - i = out.find('Recursion detected') - assert i != -1 - - def test_generator_yields_None(self): - o = tmpdir.ensure('generatornonetest', dir=1) - tfile = o.join('test_generatornone.py') - tfile.write(py.code.Source(""" - def test_1(): - yield None - """)) - session = self.mainsession(o) - out = self.file.getvalue() - #print out - i = out.find('TypeError') - assert i != -1 - - def test_capturing_hooks_simple(self): - o = tmpdir.ensure('capturing', dir=1) - tfile = o.join('test_capturing.py').write(py.code.Source(""" - 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 - """)) - conftest = o.join('conftest.py').write(py.code.Source(""" - import py - class Function(py.test.Function): - def startcapture(self): - self._mycapture = py.io.OutErrCapture() - - def finishcapture(self): - self._testmycapture = self._mycapture.reset() - """)) - session = self.mainsession(o) - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert len(l) == 1 - item = l[0][0] - assert hasattr(item, '_testmycapture') - print item._testmycapture - out, err = item._testmycapture - assert int(out.strip()) == 42 - assert int(err.strip()) == 23 - - assert isinstance(item.parent, py.test.collect.Module) - out, err = item.parent.getouterr() - assert out.find('module level output') != -1 - allout = self.file.getvalue() - print "allout:", allout - assert allout.find('module level output') != -1, ( - "session didn't show module output") - - def test_raises_output(self): - o = tmpdir.ensure('raisestest', dir=1) - tfile = o.join('test_raisesoutput.py') - tfile.write(py.code.Source(""" - import py - def test_raises_doesnt(): - py.test.raises(ValueError, int, "3") - """)) - session = self.mainsession(o) - out = self.file.getvalue() - if not out.find("DID NOT RAISE") != -1: - print out - py.test.fail("incorrect raises() output") - - def test_order_of_execution(self): - o = tmpdir.ensure('ordertest', dir=1) - tfile = o.join('test_orderofexecution.py') - tfile.write(py.code.Source(""" - l = [] - def test_1(): - l.append(1) - def test_2(): - l.append(2) - def test_3(): - assert l == [1,2] - class Testmygroup: - reslist = l - def test_1(self): - self.reslist.append(1) - def test_2(self): - self.reslist.append(2) - def test_3(self): - self.reslist.append(3) - def test_4(self): - assert self.reslist == [1,2,1,2,3] - """)) - - session = self.mainsession(o) - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 0 - l = session.getitemoutcomepairs(py.test.Item.Passed) - assert len(l) == 7 - # also test listnames() here ... - item, result = l[-1] - assert item.name == 'test_4' - names = item.listnames() - assert names == ['ordertest', 'test_orderofexecution.py', 'Testmygroup', '()', 'test_4'] - - def test_nested_import_error(self): - o = tmpdir.ensure('Ians_importfailure', dir=1) - tfile = o.join('test_import_fail.py') - tfile.write(py.code.Source(""" - import import_fails - def test_this(): - assert import_fails.a == 1 - """)) - o.join('import_fails.py').write(py.code.Source(""" - import does_not_work - a = 1 - """)) - session = self.mainsession(o) - l = session.getitemoutcomepairs(py.test.Item.Failed) - assert len(l) == 1 - item, outcome = l[0] - assert str(outcome.excinfo).find('does_not_work') != -1 - - def test_safe_repr(self): - session = self.mainsession(datadir/'brokenrepr.py') - out = self.file.getvalue() - print 'Output of simulated "py.test brokenrepr.py":' - print out - - l = session.getitemoutcomepairs(py.test.Item.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 - - def test_E_on_correct_line(self): - o = tmpdir.ensure('E_on_correct_line', dir=1) - tfile = o.join('test_correct_line.py') - source = py.code.Source(""" - import py - def test_hello(): - assert (None == - ['a', - 'b', - 'c']) - """) - tfile.write(source) - session = self.mainsession(o) - out = self.file.getvalue() - print 'Output of simulated "py.test test_correct_line.py":' - print out - i = out.find('test_correct_line.py:') - assert i >= 0 - linenum = int(out[i+len('test_correct_line.py:')]) # a single char - line_to_report = source[linenum-1] - expected_output = '\nE ' + line_to_report + '\n' - print 'Looking for:', expected_output - assert expected_output in out - +from py.__.test.testing.setupdata import setup_module class TestRemote: - def XXXtest_rootdir_is_package(self): - d = tmpdir.ensure('rootdirtest1', dir=1) - d.ensure('__init__.py') - x1 = d.ensure('subdir', '__init__.py') - x2 = d.ensure('subdir2', '__init__.py') - x3 = d.ensure('subdir3', 'noinit', '__init__.py') - assert getrootdir([x1]) == d - assert getrootdir([x2]) == d - assert getrootdir([x1,x2]) == d - assert getrootdir([x3,x2]) == d - assert getrootdir([x2,x3]) == d - - def XXXtest_rootdir_is_not_package(self): - one = tmpdir.ensure('rootdirtest1', 'hello') - rootdir = getrootdir([one]) - assert rootdir == one.dirpath() - def test_exec(self): o = tmpdir.ensure('remote', dir=1) tfile = o.join('test_exec.py') 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 Sun Jan 28 14:25:03 2007 @@ -1,9 +1,38 @@ import py -from setupdata import setupdatadir +from setupdata import setup_module # sets up global 'tmpdir' -def setup_module(mod): - mod.datadir = setupdatadir() - mod.tmpdir = py.test.ensuretemp(mod.__name__) +implied_options = { + '--pdb': 'usepdb and nocapture', + '-v': 'verbose', + '-l': 'showlocals', + '--runbrowser': 'startserver and runbrowser', +} + +conflict_options = ('--looponfailing --pdb', + '--dist --pdb', + '--exec=%s --pdb' % py.std.sys.executable, + ) + +def test_conflict_options(): + for spec in conflict_options: + opts = spec.split() + yield check_conflict_option, opts + +def check_conflict_option(opts): + print "testing if options conflict:", " ".join(opts) + config = py.test.config._reparse(opts + [datadir/'filetest.py']) + py.test.raises((ValueError, SystemExit), """ + config.initsession() + """) + +def test_implied_options(): + for key, expr in implied_options.items(): + yield check_implied_option, [key], expr + +def check_implied_option(opts, expr): + config = py.test.config._reparse(opts + [datadir/'filetest.py']) + session = config.initsession() + assert eval(expr, session.config.option.__dict__) def test_default_session_options(): for opts in ([], ['-l'], ['-s'], ['--tb=no'], ['--tb=short'], @@ -63,15 +92,7 @@ l = session.getitemoutcomepairs(py.test.Item.Skipped) assert l[0][0].name == 'test_1' -#f = open('/tmp/logfile', 'wa') class TestTerminalSession: - - def setup_class(cls): - (datadir / 'syntax_error.py').write("\nthis is really not python\n") - - def teardown_class(cls): - (datadir / 'syntax_error.py').remove() - def mainsession(self, *args): from py.__.test.terminal.terminal import TerminalSession self.file = py.std.StringIO.StringIO() @@ -291,72 +312,3 @@ expected_output = '\nE ' + line_to_report + '\n' print 'Looking for:', expected_output assert expected_output in out - - -class TestRemote: - def XXXtest_rootdir_is_package(self): - d = tmpdir.ensure('rootdirtest1', dir=1) - d.ensure('__init__.py') - x1 = d.ensure('subdir', '__init__.py') - x2 = d.ensure('subdir2', '__init__.py') - x3 = d.ensure('subdir3', 'noinit', '__init__.py') - assert getrootdir([x1]) == d - assert getrootdir([x2]) == d - assert getrootdir([x1,x2]) == d - assert getrootdir([x3,x2]) == d - assert getrootdir([x2,x3]) == d - - def XXXtest_rootdir_is_not_package(self): - one = tmpdir.ensure('rootdirtest1', 'hello') - rootdir = getrootdir([one]) - assert rootdir == one.dirpath() - - def test_exec(self): - o = tmpdir.ensure('remote', dir=1) - tfile = o.join('test_exec.py') - tfile.write(py.code.Source(""" - def test_1(): - assert 1 == 0 - """)) - print py.std.sys.executable - config = py.test.config._reparse( - ['--exec=' + py.std.sys.executable, - o]) - cls = config._getsessionclass() - out = [] # out = py.std.Queue.Queue() - session = cls(config, out.append) - session.main() - for s in out: - if s.find('1 failed') != -1: - break - else: - py.test.fail("did not see test_1 failure") - - def test_looponfailing(self): - 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.test.config._reparse(['--looponfailing', str(o)]) - 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=1.0) - if s.find('1 failed') != -1: - break - print s - else: - py.test.fail("did not see test_1 failure") - # XXX we would like to have a cleaner way to finish - try: - reply.get(timeout=0.5) - except IOError, e: - assert str(e).lower().find('timeout') != -1 - - From cfbolz at codespeak.net Sun Jan 28 17:34:52 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sun, 28 Jan 2007 17:34:52 +0100 (CET) Subject: [py-svn] r37477 - py/trunk/py/rest Message-ID: <20070128163452.E610310068@code0.codespeak.net> Author: cfbolz Date: Sun Jan 28 17:34:52 2007 New Revision: 37477 Modified: py/trunk/py/rest/latex.py Log: use relative paths to try to make the py-lib both work in paths that have quotable characters _and_ with some versions of latex that don't like quotes in \input. let's see whether it works... Modified: py/trunk/py/rest/latex.py ============================================================================== --- py/trunk/py/rest/latex.py (original) +++ py/trunk/py/rest/latex.py Sun Jan 28 17:34:52 2007 @@ -122,7 +122,7 @@ if stylesheet is not None: sty = path.join(stylesheet) if sty.check(): - options.append('--stylesheet="%s"' % (sty, )) + options.append('--stylesheet=%s' % (sty.relto(f), )) options.append(f.new(basename=tex)) options = map(str, options) if rest_options is not None: From hpk at codespeak.net Sun Jan 28 17:56:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 17:56:44 +0100 (CET) Subject: [py-svn] r37478 - py/trunk/py/path/svn/testing Message-ID: <20070128165644.962F810068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 17:56:42 2007 New Revision: 37478 Modified: py/trunk/py/path/svn/testing/svntestbase.py py/trunk/py/path/svn/testing/test_urlcommand.py Log: refactored/streamlined tests slightly i think svn testing needs some more refinements (not before 0.9) 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 Sun Jan 28 17:56:42 2007 @@ -103,13 +103,14 @@ res = url.info() assert res.size > len("samplefile") and res.created_rev >= 0 - def xxxtest_info_log(self): + def test_log_simple(self): + py.test.skip("XXX: does not work at least on svn below 1.3") url = self.root.join("samplefile") - res = url.log(rev_start=1155, rev_end=1155, verbose=True) - assert res[0].revision == 1155 and res[0].author == "jum" - from time import gmtime - t = gmtime(res[0].date) - assert t.tm_year == 2003 and t.tm_mon == 7 and t.tm_mday == 17 + logentries = url.log() + for logentry in logentries: + assert logentry.rev == 1 + assert hasattr(logentry, 'author') + assert hasattr(logentry, 'date') class CommonCommandAndBindingTests(CommonSvnTests): def test_trailing_slash_is_stripped(self): Modified: py/trunk/py/path/svn/testing/test_urlcommand.py ============================================================================== --- py/trunk/py/path/svn/testing/test_urlcommand.py (original) +++ py/trunk/py/path/svn/testing/test_urlcommand.py Sun Jan 28 17:56:42 2007 @@ -5,9 +5,9 @@ import datetime import time - -if py.path.local.sysfind('svn') is None: - py.test.skip("cannot test py.path.svn, 'svn' binary not found") +def setup_module(mod): + if py.path.local.sysfind('svn') is None: + py.test.skip("cannot test py.path.svn, 'svn' binary not found") class TestSvnCommandPath(CommonCommandAndBindingTests): def setup_class(cls): @@ -32,6 +32,9 @@ def test_svnurl_characters_colon_port(self): py.path.svnurl("http://host.com:8080/some/dir") + def test_svnurl_characters_tilde_end(self): + py.path.svnurl("http://host.com/some/file~") + def test_svnurl_characters_colon_path(self): if py.std.sys.platform == 'win32': # colons are allowed on win32, because they're part of the drive @@ -39,24 +42,6 @@ # other parts, I think py.test.skip('XXX fixme win32') py.test.raises(ValueError, 'py.path.svnurl("http://host.com/foo:bar")') - - def test_svnurl_characters_tilde_end(self): - py.path.svnurl("http://host.com/some/file~") - - # XXX - def xtest_copy_file(self): - raise py.test.Skipped(msg="XXX fix svnurl first") - - def xtest_copy_dir(self): - py.test.skipp("XXX fix svnurl first") - - def XXXtest_info_log(self): - url = self.root.join("samplefile") - res = url.log(rev_start=1155, rev_end=1155, verbose=True) - assert res[0].revision == 1155 and res[0].author == "jum" - from time import gmtime - t = gmtime(res[0].date) - assert t.tm_year == 2003 and t.tm_mon == 7 and t.tm_mday == 17 class TestSvnInfoCommand: From cfbolz at codespeak.net Sun Jan 28 18:16:43 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sun, 28 Jan 2007 18:16:43 +0100 (CET) Subject: [py-svn] r37481 - py/trunk/py/rest Message-ID: <20070128171643.ADBFD10068@code0.codespeak.net> Author: cfbolz Date: Sun Jan 28 18:16:43 2007 New Revision: 37481 Modified: py/trunk/py/rest/latex.py Log: new attempt :-( Modified: py/trunk/py/rest/latex.py ============================================================================== --- py/trunk/py/rest/latex.py (original) +++ py/trunk/py/rest/latex.py Sun Jan 28 18:16:43 2007 @@ -122,7 +122,7 @@ if stylesheet is not None: sty = path.join(stylesheet) if sty.check(): - options.append('--stylesheet=%s' % (sty.relto(f), )) + options.append('--stylesheet=%s' % (sty.relto(f.dirpath()), )) options.append(f.new(basename=tex)) options = map(str, options) if rest_options is not None: From hpk at codespeak.net Sun Jan 28 18:53:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 18:53:46 +0100 (CET) Subject: [py-svn] r37494 - in py/trunk/py/test: . testing Message-ID: <20070128175346.313AB10079@code0.codespeak.net> Author: hpk Date: Sun Jan 28 18:53:43 2007 New Revision: 37494 Added: py/trunk/py/test/host.py py/trunk/py/test/testing/test_host.py Log: the start of factoring out common and unified host handling within py.test Added: py/trunk/py/test/host.py ============================================================================== --- (empty file) +++ py/trunk/py/test/host.py Sun Jan 28 18:53:43 2007 @@ -0,0 +1,37 @@ + +class InvalidHostSpec(ValueError): + pass + +def parsehostspec(spec): + parts = spec.split(":", 2) + if len(parts) < 2: + raise InvalidHostSpec(spec) + type = parts.pop(0) + if type == 'ssh': + sshaddress = parts.pop(0) + basedir = parts and ":".join(parts) or '' + return SpecSSH(sshaddress, basedir) + elif type == 'socket': + host = parts.pop(0) + if not parts: + raise InvalidHostSpec(spec) + remainder = parts.pop(0) + parts = remainder.split(":", 1) + port = int(parts.pop(0)) + basedir = parts and ":".join(parts) or '' + return SpecSocket(host, port, basedir) + else: + raise InvalidHostSpec(spec) + +class SpecSSH(object): + def __init__(self, sshaddress, basedir): + self.sshaddress = sshaddress + self.basedir = basedir + self.host = sshaddress.split('@', 1)[-1] + +class SpecSocket(object): + def __init__(self, host, port, basedir): + self.host = host + self.port = port + self.basedir = basedir + Added: py/trunk/py/test/testing/test_host.py ============================================================================== --- (empty file) +++ py/trunk/py/test/testing/test_host.py Sun Jan 28 18:53:43 2007 @@ -0,0 +1,56 @@ +""" + +Testing Host setup and rsyncing operations. + +""" + +import py +from py.__.test.host import parsehostspec + +def test_parse_hostspec_ssh(): + hostspec = parsehostspec("ssh:xyz at domain.net:directory") + assert hostspec.host == "domain.net" + assert hostspec.basedir == "directory" + assert hostspec.sshaddress == "xyz at domain.net" + + hostspec = parsehostspec("ssh:xyz at domain.net:direc:tory") + assert hostspec.host == "domain.net" + assert hostspec.basedir == "direc:tory" + assert hostspec.sshaddress == "xyz at domain.net" + + hostspec = parsehostspec("ssh:xyz at domain.net") + assert hostspec.host == "domain.net" + assert hostspec.basedir == "" + assert hostspec.sshaddress == "xyz at domain.net" + + hostspec = parsehostspec("ssh:domain.net:directory") + assert hostspec.host == "domain.net" + assert hostspec.basedir == "directory" + assert hostspec.sshaddress == "domain.net" + + hostspec = parsehostspec("ssh:domain.net:/tmp/hello") + assert hostspec.host == "domain.net" + assert hostspec.basedir == "/tmp/hello" + assert hostspec.sshaddress == "domain.net" + +def test_parse_hostspec_socket(): + hostspec = parsehostspec("socket:domain.net:1234:directory") + assert hostspec.host == "domain.net" + assert hostspec.port == 1234 + assert hostspec.basedir == "directory" + + hostspec = parsehostspec("socket:domain.net:1234") + assert hostspec.host == "domain.net" + assert hostspec.port == 1234 + assert hostspec.basedir == "" + +def test_parse_hostspec_error(): + py.test.raises(ValueError, """ + parsehostspec('alksd:qweqwe') + """) + py.test.raises(ValueError, """ + parsehostspec('ssh') + """) + py.test.raises(ValueError, """ + parsehostspec('socket:qweqwe') + """) From hpk at codespeak.net Sun Jan 28 21:09:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 21:09:05 +0100 (CET) Subject: [py-svn] r37498 - py/trunk/py/misc Message-ID: <20070128200905.38C8710068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 21:09:02 2007 New Revision: 37498 Modified: py/trunk/py/misc/conftest-socketgatewayrun.py Log: bah, my local config but i want to have it committed to get rid of the "M" state, ehem. Modified: py/trunk/py/misc/conftest-socketgatewayrun.py ============================================================================== --- py/trunk/py/misc/conftest-socketgatewayrun.py (original) +++ py/trunk/py/misc/conftest-socketgatewayrun.py Sun Jan 28 21:09:02 2007 @@ -29,6 +29,7 @@ class MySession(RemoteTerminalSession): socketserveradr = ('10.9.4.148', 8888) + socketserveradr = ('10.9.2.62', 8888) def _initslavegateway(self): print "MASTER: initializing remote socket gateway" From hpk at codespeak.net Sun Jan 28 21:25:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 21:25:16 +0100 (CET) Subject: [py-svn] r37499 - py/trunk/py/test/rsession Message-ID: <20070128202516.7687B10068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 21:25:11 2007 New Revision: 37499 Modified: py/trunk/py/test/rsession/rsession.py Log: concreter error message Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Sun Jan 28 21:25:11 2007 @@ -261,8 +261,8 @@ from py.__.apigen.tracer.docstorage import DocStorageAccessor apigen = py.path.local(self.config.option.apigen).pyimport() if not hasattr(apigen, 'build'): - raise NotImplementedError("Provided script does not seem " - "to contain build function") + raise NotImplementedError("%s does not contain 'build' " + "function" %(apigen,)) print >>sys.stderr, 'building documentation' capture = py.io.OutErrCapture() try: From hpk at codespeak.net Sun Jan 28 21:25:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 21:25:49 +0100 (CET) Subject: [py-svn] r37500 - in py/trunk/py/test: . testing Message-ID: <20070128202549.95B7510071@code0.codespeak.net> Author: hpk Date: Sun Jan 28 21:25:48 2007 New Revision: 37500 Removed: py/trunk/py/test/host.py py/trunk/py/test/testing/test_host.py Log: postponing generalizing of host specifications for 0.9 Deleted: /py/trunk/py/test/host.py ============================================================================== --- /py/trunk/py/test/host.py Sun Jan 28 21:25:48 2007 +++ (empty file) @@ -1,37 +0,0 @@ - -class InvalidHostSpec(ValueError): - pass - -def parsehostspec(spec): - parts = spec.split(":", 2) - if len(parts) < 2: - raise InvalidHostSpec(spec) - type = parts.pop(0) - if type == 'ssh': - sshaddress = parts.pop(0) - basedir = parts and ":".join(parts) or '' - return SpecSSH(sshaddress, basedir) - elif type == 'socket': - host = parts.pop(0) - if not parts: - raise InvalidHostSpec(spec) - remainder = parts.pop(0) - parts = remainder.split(":", 1) - port = int(parts.pop(0)) - basedir = parts and ":".join(parts) or '' - return SpecSocket(host, port, basedir) - else: - raise InvalidHostSpec(spec) - -class SpecSSH(object): - def __init__(self, sshaddress, basedir): - self.sshaddress = sshaddress - self.basedir = basedir - self.host = sshaddress.split('@', 1)[-1] - -class SpecSocket(object): - def __init__(self, host, port, basedir): - self.host = host - self.port = port - self.basedir = basedir - Deleted: /py/trunk/py/test/testing/test_host.py ============================================================================== --- /py/trunk/py/test/testing/test_host.py Sun Jan 28 21:25:48 2007 +++ (empty file) @@ -1,56 +0,0 @@ -""" - -Testing Host setup and rsyncing operations. - -""" - -import py -from py.__.test.host import parsehostspec - -def test_parse_hostspec_ssh(): - hostspec = parsehostspec("ssh:xyz at domain.net:directory") - assert hostspec.host == "domain.net" - assert hostspec.basedir == "directory" - assert hostspec.sshaddress == "xyz at domain.net" - - hostspec = parsehostspec("ssh:xyz at domain.net:direc:tory") - assert hostspec.host == "domain.net" - assert hostspec.basedir == "direc:tory" - assert hostspec.sshaddress == "xyz at domain.net" - - hostspec = parsehostspec("ssh:xyz at domain.net") - assert hostspec.host == "domain.net" - assert hostspec.basedir == "" - assert hostspec.sshaddress == "xyz at domain.net" - - hostspec = parsehostspec("ssh:domain.net:directory") - assert hostspec.host == "domain.net" - assert hostspec.basedir == "directory" - assert hostspec.sshaddress == "domain.net" - - hostspec = parsehostspec("ssh:domain.net:/tmp/hello") - assert hostspec.host == "domain.net" - assert hostspec.basedir == "/tmp/hello" - assert hostspec.sshaddress == "domain.net" - -def test_parse_hostspec_socket(): - hostspec = parsehostspec("socket:domain.net:1234:directory") - assert hostspec.host == "domain.net" - assert hostspec.port == 1234 - assert hostspec.basedir == "directory" - - hostspec = parsehostspec("socket:domain.net:1234") - assert hostspec.host == "domain.net" - assert hostspec.port == 1234 - assert hostspec.basedir == "" - -def test_parse_hostspec_error(): - py.test.raises(ValueError, """ - parsehostspec('alksd:qweqwe') - """) - py.test.raises(ValueError, """ - parsehostspec('ssh') - """) - py.test.raises(ValueError, """ - parsehostspec('socket:qweqwe') - """) From hpk at codespeak.net Sun Jan 28 21:37:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 21:37:31 +0100 (CET) Subject: [py-svn] r37501 - in py/trunk/py/test: . testing Message-ID: <20070128203731.4C3B910068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 21:37:30 2007 New Revision: 37501 Modified: py/trunk/py/test/config.py py/trunk/py/test/conftesthandle.py py/trunk/py/test/testing/test_conftesthandle.py Log: rename for clarity Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Jan 28 21:37:30 2007 @@ -122,7 +122,7 @@ """ same as previous, but returns conftest's path as well """ - val, mod = self.conftest.rget_path(name, path) + val, mod = self.conftest.rget_with_confmod(name, path) return val, py.path.local(mod.__file__).dirpath() def initsession(self): Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Sun Jan 28 21:37:30 2007 @@ -64,7 +64,7 @@ modules.reverse() return self._get(name, modules) - def rget_path(self, name, path=None): + def rget_with_confmod(self, name, path=None): return self._rget(name, path) def _get(self, name, modules): Modified: py/trunk/py/test/testing/test_conftesthandle.py ============================================================================== --- py/trunk/py/test/testing/test_conftesthandle.py (original) +++ py/trunk/py/test/testing/test_conftesthandle.py Sun Jan 28 21:37:30 2007 @@ -68,7 +68,7 @@ def test_value_access_path(self): topdir = self.basedir.join("adir", "b") conftest = Conftest(topdir) - _, mod = conftest.rget_path("a") + _, mod = conftest.rget_with_confmod("a") assert py.path.local(mod.__file__).dirpath() == topdir class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): From hpk at codespeak.net Sun Jan 28 22:54:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 28 Jan 2007 22:54:14 +0100 (CET) Subject: [py-svn] r37502 - in py/trunk/py: doc test test/rsession test/rsession/testing Message-ID: <20070128215414.C027310068@code0.codespeak.net> Author: hpk Date: Sun Jan 28 22:54:12 2007 New Revision: 37502 Modified: py/trunk/py/doc/test.txt py/trunk/py/test/config.py py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_rsession.py Log: unify dist_* options and give dist_rsync_roots more precision: you now specify relative paths (relative to the conftest.py where a dist_rsync_root setting resides) or absolute paths. Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Sun Jan 28 22:54:12 2007 @@ -691,9 +691,8 @@ The options that you need to specify in that conftest.py file are: -* **`dist_hosts`**: a required list of ssh addresses (which each may - include a path, default path is: ``$HOME/pytestcache-HOSTNAME``) -* `dist_rsyncroots` - a list of packages to copy to the remote machines. +* `dist_hosts`: a required list of host specifications +* `dist_rsync_roots` - a list of relative locations to copy to the remote machines. * `dist_remotepython` - the remote python executable to run. * `dist_nicelevel` - process priority of remote nodes. * `dist_boxing` - will run each single test in a separate process @@ -703,7 +702,7 @@ Sample configuration:: dist_hosts = ['localhost', 'user at someserver:/tmp/somedir'] - dist_rsyncroots = ['pypy', 'py'] + dist_rsyncroots = ['../pypy', '../py'] dist_remotepython = 'python2.4' dist_nicelevel = 10 dist_boxing = True Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Jan 28 22:54:12 2007 @@ -118,13 +118,6 @@ except KeyError: return self.conftest.rget(name, path) - def getvalue_and_confpath(self, name, path=None): - """ same as previous, but returns conftest's path - as well - """ - val, mod = self.conftest.rget_with_confmod(name, path) - return val, py.path.local(mod.__file__).dirpath() - def initsession(self): """ return an initialized session object. """ cls = self._getsessionclass() Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sun Jan 28 22:54:12 2007 @@ -51,10 +51,10 @@ dir, base = os.path.split(path) if base == '.svn': return False - if self.rsync_roots is None or dir != self.sourcedir: + if dir != self.sourcedir: return True else: - return base in self.rsync_roots + return self.rsync_roots is None or path in self.rsync_roots class DummyGateway(object): pass Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Sun Jan 28 22:54:12 2007 @@ -133,17 +133,17 @@ super(RSession, self).fixoptions() config = self.config try: - config.getvalue('disthosts') + config.getvalue('dist_hosts') except KeyError: - print "You're trying to run RSession without disthosts specified" - print "you need to specify it in your conftest.py (ie. ~/conftest.py)" + 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 " disthosts = ['localhost'] * 4 # for 3 processors" - print " - or -" - print " disthosts = ['you at some.remote.com'] # for remote testing" - print " # with your remote ssh account" - print "http://codespeak.net/py/current/doc/test.html#automated-distributed-testing" - print " for more info..." + 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, reporter=None): @@ -195,11 +195,22 @@ """ Read from conftest file the configuration of distributed testing """ try: - rsync_roots = self.config.getvalue("distrsync_roots") - except: - rsync_roots = None # all files and directories in the pkgdir + conftest = self.config.conftest + value, mod = conftest.rget_with_confmod('dist_rsync_roots') + except KeyError: + rsync_roots = [self.config.topdir] # our best guess likely + else: + assert isinstance(value, (list,tuple)), value + base = py.path.local(mod.__file__).dirpath() + print "base", base + rsync_roots = [base.join(path, abs=True) + for path in value] + for root in rsync_roots: + assert root.check(dir=1) + #rsync_roots = value + print "rsync_roots", rsync_roots sshhosts = [HostInfo(i) for i in - self.config.getvalue("disthosts")] + self.config.getvalue("dist_hosts")] parse_directories(sshhosts) remotepython = self.config.getvalue("dist_remotepython") return sshhosts, remotepython, rsync_roots Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Sun Jan 28 22:54:12 2007 @@ -76,7 +76,7 @@ def test_example_distribution_minus_x(self): tmpdir = py.test.ensuretemp("example_distribution_minus_x") tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" - disthosts = [%r] + dist_hosts = [%r] """ % ('localhost',))) tmpdir.ensure("sub", "__init__.py") tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" @@ -106,9 +106,9 @@ subdir = "sub_example_dist" tmpdir = py.test.ensuretemp("example_distribution") tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" - disthosts = [%r] - distrsync_roots = ["%s", "py"] - """ % ('localhost', subdir))) + dist_hosts = [%r] + dist_rsync_roots = ["%s", "../py"] + """ % ('localhost', tmpdir.join(subdir), ))) tmpdir.ensure(subdir, "__init__.py") tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" def test_1(): @@ -161,7 +161,8 @@ teardown_events = [] config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + opts = HostOptions(optimise_localhost=False, + rsync_roots=[py.path.local(py.__file__).dirpath()]) hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(setup_events.append) hm.teardown_hosts(teardown_events.append, @@ -188,7 +189,8 @@ allevents = [] config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + opts = HostOptions(optimise_localhost=False, + rsync_roots=[py.path.local(py.__file__).dirpath()]) hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(allevents.append) @@ -235,7 +237,8 @@ from py.__.test.rsession.master import defaultconftestnames defaultconftestnames.append("custom") try: - opts = HostOptions(optimise_localhost=False, rsync_roots=['py']) + opts = HostOptions(optimise_localhost=False, + rsync_roots=[py.path.local(py.__file__).dirpath()]) hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(allevents.append) @@ -267,7 +270,7 @@ tmpdir = py.test.ensuretemp("nice") tmpdir.ensure("__init__.py") tmpdir.ensure("conftest.py").write(py.code.Source(""" - disthosts = ['localhost'] + dist_hosts = ['localhost'] dist_nicelevel = 10 """)) tmpdir.ensure("test_one.py").write("""def test_nice(): From guido at codespeak.net Mon Jan 29 13:53:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 29 Jan 2007 13:53:53 +0100 (CET) Subject: [py-svn] r37531 - py/trunk/py/test/rsession Message-ID: <20070129125353.05D9710063@code0.codespeak.net> Author: guido Date: Mon Jan 29 13:53:52 2007 New Revision: 37531 Modified: py/trunk/py/test/rsession/rsession.py Log: Removing try/except to make debugging a little bit easier. Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Mon Jan 29 13:53:52 2007 @@ -277,19 +277,9 @@ print >>sys.stderr, 'building documentation' capture = py.io.OutErrCapture() try: - try: - pkgdir = self.getpkgdir(self.config.args[0]) - apigen.build(pkgdir, - DocStorageAccessor(self.docstorage)) - except AttributeError: - import traceback - exc, e, tb = sys.exc_info() - print '%s - %s' % (exc, e) - print ''.join(traceback.format_tb(tb)) - del tb - print '-' * 79 - raise NotImplementedError("Provided script does not seem " - "to contain build function") + pkgdir = self.getpkgdir(self.config.args[0]) + apigen.build(pkgdir, + DocStorageAccessor(self.docstorage)) finally: capture.reset() @@ -330,3 +320,4 @@ return box_runner return plain_runner + From fijal at codespeak.net Mon Jan 29 14:06:24 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 29 Jan 2007 14:06:24 +0100 (CET) Subject: [py-svn] r37533 - in py/trunk/py/test: . rsession rsession/testing testing Message-ID: <20070129130624.C887610063@code0.codespeak.net> Author: fijal Date: Mon Jan 29 14:06:19 2007 New Revision: 37533 Added: py/trunk/py/test/rsession/testing/test_rsync.py Modified: py/trunk/py/test/config.py py/trunk/py/test/conftesthandle.py py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_rsession.py py/trunk/py/test/rsession/testing/test_slave.py py/trunk/py/test/testing/test_conftesthandle.py Log: Make distribution working with custom conftests at exact levels. This is very explicit - dist_rsyncroots works only in that conftest directory. Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Mon Jan 29 14:06:19 2007 @@ -118,6 +118,14 @@ except KeyError: return self.conftest.rget(name, path) + def getvalue_from_confpath(self, name, path): + """ same as previous, but returns only value from explicit + conftest path + """ + if isinstance(path, str): + path = py.path.local(path) + return self.conftest.rget_path(name, path) + def initsession(self): """ return an initialized session object. """ cls = self._getsessionclass() Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Mon Jan 29 14:06:19 2007 @@ -11,6 +11,7 @@ """ def __init__(self, path=None): self._path2confmods = {} + self._path2conftest_files = {} # direct cache of conftest files, to avoid confusion if path is not None: self.setinitial([path]) @@ -41,45 +42,62 @@ except KeyError: dp = path.dirpath() if dp == path: - return [importconfig(defaultconftestpath)] + return [self.importconfig(defaultconftestpath)] clist = self.getconftestmodules(dp) conftestpath = path.join("conftest.py") if conftestpath.check(file=1): - clist.append(importconfig(conftestpath)) + clist.append(self.importconfig(conftestpath)) self._path2confmods[path] = clist # be defensive: avoid changes from caller side to # affect us by always returning a copy of the actual list return clist[:] + def getconftest(self, path): + """ Return a direct module of that path + """ + try: + return self._path2conftest_files[path] + except KeyError: + conftestpath = path.join("conftest.py") + if conftestpath.check(file=1): + return self.importconfig(conftestpath) + raise AttributeError + # we raise here AttributeError to unify error reporting in case + # of lack of variable in conftest or lack of file, but we do not want to + # hide ImportError + # XXX no real use case, may probably go #def lget(self, name, path=None): # modules = self.getconftestmodules(path) # return self._get(name, modules) def rget(self, name, path=None): - return self._rget(name, path)[0] - - def _rget(self, name, path=None): modules = self.getconftestmodules(path) modules.reverse() return self._get(name, modules) - def rget_with_confmod(self, name, path=None): - return self._rget(name, path) + def rget_path(self, name, path): + """ can raise AttributeError + """ + return getattr(self.getconftest(path), name) def _get(self, name, modules): for mod in modules: try: - return getattr(mod, name), mod + return getattr(mod, name) except AttributeError: continue raise KeyError, name -def importconfig(configpath): - if not configpath.dirpath('__init__.py').check(file=1): - # HACK: we don't want any "globally" imported conftest.py, - # prone to conflicts and subtle problems - modname = str(configpath).replace('.', configpath.sep) - return configpath.pyimport(modname=modname) - else: - return configpath.pyimport() + def importconfig(self, configpath): + # We could have used caching here, but it's redundant since + # they're cached on path anyway, so we use it only when doing rget_path + if not configpath.dirpath('__init__.py').check(file=1): + # HACK: we don't want any "globally" imported conftest.py, + # prone to conflicts and subtle problems + modname = str(configpath).replace('.', configpath.sep) + mod = configpath.pyimport(modname=modname) + else: + mod = configpath.pyimport() + self._path2conftest_files[configpath.dirpath()] = mod + return mod Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Jan 29 14:06:19 2007 @@ -41,20 +41,24 @@ class HostRSync(py.execnet.RSync): """ An rsync wrapper which filters out *~, .svn/ and *.pyc """ - def __init__(self, rsync_roots): + def __init__(self, config): py.execnet.RSync.__init__(self, delete=True) - self.rsync_roots = rsync_roots + self.config = config def filter(self, path): if path.endswith('.pyc') or path.endswith('~'): return False dir, base = os.path.split(path) + try: + rsync_roots = self.config.getvalue_from_confpath("dist_rsyncroots", + dir) + except AttributeError: + rsync_roots = None if base == '.svn': return False - if dir != self.sourcedir: + if rsync_roots is None: return True - else: - return self.rsync_roots is None or path in self.rsync_roots + return base in rsync_roots class DummyGateway(object): pass @@ -64,10 +68,9 @@ as different function parameters, mostly related to tests only """ - def __init__(self, rsync_roots=None, remote_python="python", + def __init__(self, remote_python="python", optimise_localhost=True, do_sync=True, create_gateways=True): - self.rsync_roots = rsync_roots self.remote_python = remote_python self.optimise_localhost = optimise_localhost self.do_sync = do_sync @@ -145,7 +148,7 @@ rsynced = {} if self.options.do_sync: - rsync = HostRSync(self.options.rsync_roots) + rsync = HostRSync(self.config) for host in hosts: if not self.need_rsync(rsynced, host.hostname, host.relpath): reporter(report.HostReady(host)) Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Mon Jan 29 14:06:19 2007 @@ -150,7 +150,7 @@ """ main loop for running tests. """ args = self.config.args - sshhosts, remotepython, rsync_roots = self.read_distributed_config() + sshhosts, remotepython = self.read_distributed_config() reporter, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) reporter, checkfun = self.wrap_reporter(reporter) @@ -158,8 +158,7 @@ reporter(report.TestStarted(sshhosts)) done_dict = {} - hostopts = HostOptions(rsync_roots=rsync_roots, - remote_python=remotepython, + hostopts = HostOptions(remote_python=remotepython, optimise_localhost=self.optimise_localhost) hostmanager = HostManager(sshhosts, self.config, hostopts) try: @@ -194,26 +193,11 @@ def read_distributed_config(self): """ Read from conftest file the configuration of distributed testing """ - try: - conftest = self.config.conftest - value, mod = conftest.rget_with_confmod('dist_rsync_roots') - except KeyError: - rsync_roots = [self.config.topdir] # our best guess likely - else: - assert isinstance(value, (list,tuple)), value - base = py.path.local(mod.__file__).dirpath() - print "base", base - rsync_roots = [base.join(path, abs=True) - for path in value] - for root in rsync_roots: - assert root.check(dir=1) - #rsync_roots = value - print "rsync_roots", rsync_roots sshhosts = [HostInfo(i) for i in self.config.getvalue("dist_hosts")] parse_directories(sshhosts) remotepython = self.config.getvalue("dist_remotepython") - return sshhosts, remotepython, rsync_roots + return sshhosts, remotepython def dispatch_tests(self, nodes, reporter, checkfun, done_dict): colitems = self.config.getcolitems() Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Mon Jan 29 14:06:19 2007 @@ -161,8 +161,7 @@ teardown_events = [] config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False, - rsync_roots=[py.path.local(py.__file__).dirpath()]) + opts = HostOptions(optimise_localhost=False) hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(setup_events.append) hm.teardown_hosts(teardown_events.append, @@ -189,8 +188,7 @@ allevents = [] config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False, - rsync_roots=[py.path.local(py.__file__).dirpath()]) + opts = HostOptions(optimise_localhost=False) hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(allevents.append) @@ -237,8 +235,7 @@ from py.__.test.rsession.master import defaultconftestnames defaultconftestnames.append("custom") try: - opts = HostOptions(optimise_localhost=False, - rsync_roots=[py.path.local(py.__file__).dirpath()]) + opts = HostOptions(optimise_localhost=False) hm = HostManager(hosts, config, opts) nodes = hm.init_hosts(allevents.append) Added: py/trunk/py/test/rsession/testing/test_rsync.py ============================================================================== --- (empty file) +++ py/trunk/py/test/rsession/testing/test_rsync.py Mon Jan 29 14:06:19 2007 @@ -0,0 +1,30 @@ + +""" RSync filter test +""" + +import py +from py.__.test.rsession.hostmanage import HostRSync + +def test_rsync(): + tmpdir = py.test.ensuretemp("rsync_rsession") + tmpdir.ensure("a", dir=True) + tmpdir.ensure("b", dir=True) + tmpdir.ensure("conftest.py").write(py.code.Source(""" + dist_rsyncroots = ['a'] + """)) + tmpdir.join("a").ensure("x") + adir = tmpdir.join("a").ensure("xy", dir=True) + adir.ensure("conftest.py").write(py.code.Source(""" + dist_rsyncroots = ['b', 'conftest.py'] + """)) + adir.ensure("a", dir=True) + adir.ensure("b", dir=True) + config = py.test.config._reparse([str(tmpdir)]) + h = HostRSync(config) + h.sourcedir = config.topdir + assert h.filter(str(tmpdir.join("a"))) + assert not h.filter(str(tmpdir.join("b"))) + assert h.filter(str(tmpdir.join("a").join("x"))) + assert h.filter(str(adir.join("conftest.py"))) + assert not h.filter(str(adir.join("a"))) + assert h.filter(str(adir.join("b"))) Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Mon Jan 29 14:06:19 2007 @@ -179,4 +179,3 @@ os._exit(0) # check if this really exits py.test.raises(OSError, "os.waitpid(pid, 0)") - Modified: py/trunk/py/test/testing/test_conftesthandle.py ============================================================================== --- py/trunk/py/test/testing/test_conftesthandle.py (original) +++ py/trunk/py/test/testing/test_conftesthandle.py Mon Jan 29 14:06:19 2007 @@ -67,9 +67,12 @@ def test_value_access_path(self): topdir = self.basedir.join("adir", "b") + topdir.ensure("xx", dir=True) conftest = Conftest(topdir) - _, mod = conftest.rget_with_confmod("a") - assert py.path.local(mod.__file__).dirpath() == topdir + assert conftest.rget_path("a", topdir) == 1.5 + assert conftest.rget_path("a", topdir.dirpath()) == 1 + py.test.raises(AttributeError, "conftest.rget_path('a', topdir.join('xx'))") + #assert py.path.local(mod.__file__).dirpath() == topdir class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): def setup_class(cls): From guido at codespeak.net Mon Jan 29 14:09:35 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 29 Jan 2007 14:09:35 +0100 (CET) Subject: [py-svn] r37534 - py/trunk/py/apigen Message-ID: <20070129130935.E1C9510063@code0.codespeak.net> Author: guido Date: Mon Jan 29 14:09:33 2007 New Revision: 37534 Modified: py/trunk/py/apigen/htmlgen.py Log: Some minor visual modifications for stack traces, fixed check for py.code.Source object filenames. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Mon Jan 29 14:09:33 2007 @@ -636,15 +636,17 @@ return py.path.local(sourcefile).relto(self.projpath) def build_callsite(self, functionname, call_site): - tbtag = self.gen_traceback(functionname, call_site) + tbtag = self.gen_traceback(functionname, reversed(call_site)) tag = H.CallStackItem( - H.a("%s - line %s" % (call_site[0].filename, call_site[0].lineno + 1), + H.a("show/hide stack trace %s - line %s" % ( + call_site[0].filename, call_site[0].lineno + 1), href='#', onclick="showhideel(getnextsibling(this)); return false;"), H.div(tbtag, style='display: none') ) return tag + _reg_source = py.std.re.compile(r'([^>]*)<(.*)>') def gen_traceback(self, funcname, call_site): tbdiv = H.div() for line in call_site: @@ -662,8 +664,8 @@ linktext = '%s - line %s' % (sourcefile, line.lineno + 1) # skip py.code.Source objects and source files outside of the # package - if (not sourcefile.startswith('None') and - self.is_in_pkg(sourcefile)): + is_code_source = self._reg_source.match(sourcefile) + if (not is_code_source and self.is_in_pkg(sourcefile)): href = self.linker.get_lazyhref(sourcefile) sourcelink = H.a(linktext, href=href) else: From fijal at codespeak.net Mon Jan 29 14:10:53 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 29 Jan 2007 14:10:53 +0100 (CET) Subject: [py-svn] r37535 - py/trunk/py/doc Message-ID: <20070129131053.D181310063@code0.codespeak.net> Author: fijal Date: Mon Jan 29 14:10:51 2007 New Revision: 37535 Modified: py/trunk/py/doc/TODO.txt Log: update on that Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Mon Jan 29 14:10:51 2007 @@ -119,7 +119,8 @@ * (DONE, except apigen) cleanup initialisation of config / get rid of pkgdir * unify option names (dist_*) * (optional) see if more of py/test/session.py's Session can be reused -* have dist_rsyncroots be relative to the conftest.py file +* (DONE, but slightly different way) + have dist_rsyncroots be relative to the conftest.py file so that projects can define it for themselves, e.g. pypy/conftest.py would contain:: @@ -132,6 +133,15 @@ method with tests and documentation, and with providing the right example. + way it's done: + + dist_rsyncroots refer *only* to a directory where it's placed. + This avoids tons of confusion, like what to do if different conftests + point to the same place. So by now pypy rootdir should contain + conftest.py with:: + + dist_rsyncroots = ['pypy', 'pylib', 'lib-python'] + code quality ----------------- From guido at codespeak.net Mon Jan 29 15:20:33 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 29 Jan 2007 15:20:33 +0100 (CET) Subject: [py-svn] r37540 - in py/trunk/py/apigen: . testing tracer Message-ID: <20070129142033.CE9F410071@code0.codespeak.net> Author: guido Date: Mon Jan 29 15:20:31 2007 New Revision: 37540 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/testing/test_htmlgen.py py/trunk/py/apigen/tracer/description.py Log: Nicer formatting of docstrings (de-indented and such), fixed problem getting to frame source (IOError that popped up when building the py lib's api docs) in description.py. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Mon Jan 29 15:20:31 2007 @@ -12,6 +12,41 @@ html = py.xml.html raw = py.xml.raw +def deindent(str, linesep=os.linesep): + """ de-indent string + + can be used to de-indent Python docstrings, it de-indents the first + line to the side always, and determines the indentation of the rest + of the text by taking that of the least indented (filled) line + """ + lines = str.split(linesep) + normalized = [] + deindent = None + normalized.append(lines[0].strip()) + for line in lines[1:]: + if not line.strip(): + normalized.append('') + else: + line = line.rstrip() + line = line.replace('\t', ' ') + indent = 0 + for c in line: + if c != ' ': + break + indent += 1 + if deindent is None or indent < deindent: + deindent = indent + normalized.append(line) + while normalized[-1] == '': + normalized.pop() + ret = [normalized[0]] + for line in normalized[1:]: + if not line: + ret.append(line) + else: + ret.append(line[deindent:]) + return '%s\n' % (linesep.join(ret),) + # HTML related stuff class H(html): class Content(html.div): @@ -50,8 +85,9 @@ class ParameterDescription(html.div): pass - class Docstring(html.div): - style = html.Style(white_space='pre', min_height='3em') + class Docstring(html.pre): + #style = html.Style(white_space='pre', min_height='3em') + pass class Navigation(html.div): style = html.Style(min_height='99%', float='left', margin_top='1.2em', @@ -309,7 +345,9 @@ """ build the html for a class method """ # XXX we may want to have seperate func = self.dsa.get_obj(dotted_name) - docstring = func.__doc__ + docstring = func.__doc__ + if docstring: + docstring = deindent(docstring) localname = func.__name__ argdesc = get_param_htmldesc(self.linker, func) valuedesc = self.build_callable_signature_description(dotted_name) @@ -343,7 +381,7 @@ ) snippet = H.FunctionDescription( H.FunctionDef(localname, argdesc), - H.Docstring(docstring or H.em('no docstring available')), + H.Docstring(docstring or '*no docstring available*'), H.div(H.a('show/hide info', href='#', onclick=('showhideel(getnextsibling(this));' @@ -372,6 +410,8 @@ href=self.linker.get_lazyhref(sourcefile))) docstring = cls.__doc__ + if docstring: + docstring = deindent(docstring) methods = self.dsa.get_class_methods(dotted_name) basehtml = [] bases = self.dsa.get_possible_base_classes(dotted_name) @@ -394,7 +434,7 @@ snippet = H.ClassDescription( # XXX bases HTML H.ClassDef('%s(' % (clsname,), *basehtml), - H.Docstring(docstring or H.em('no docstring available')), + H.Docstring(docstring or '*no docstring available*'), sourcelink, ) if methods: @@ -413,9 +453,11 @@ docstring = None else: docstring = obj.__doc__ + if docstring: + docstring = deindent(docstring) snippet = H.NamespaceDescription( H.NamespaceDef(namespace_dotted_name), - H.Docstring(docstring or H.em('no docstring available')) + H.Docstring(docstring or '*no docstring available*') ) for dotted_name in sorted(item_dotted_names): itemname = dotted_name.split('.')[-1] Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Mon Jan 29 15:20:31 2007 @@ -69,6 +69,13 @@ assert pak.main.sub.func(20) is None s = pak.main.func(pak.main.SomeTestClass, 10) assert isinstance(s, pak.main.SomeTestClass) + + # some nice things to confuse the tracer/storage + source = py.code.Source('''\ + pak.main.sub.func(10) + ''') + c = compile(str(source), '', 'exec') + exec c in globals() """)) return temp, 'pak' Modified: py/trunk/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/trunk/py/apigen/testing/test_htmlgen.py (original) +++ py/trunk/py/apigen/testing/test_htmlgen.py Mon Jan 29 15:20:31 2007 @@ -43,3 +43,12 @@ assert dirnames == ['sub'] assert filenames == ['file1.py', 'file3.c'] +def test_deindent(): + assert htmlgen.deindent('foo\n\n bar\n ') == 'foo\n\nbar\n' + assert htmlgen.deindent(' foo\n\n bar\n ') == 'foo\n\nbar\n' + assert htmlgen.deindent('foo\n\n bar\n baz') == 'foo\n\nbar\nbaz\n' + assert htmlgen.deindent(' foo\n\n bar\n baz\n') == ( + 'foo\n\nbar\n baz\n') + assert htmlgen.deindent('foo\n\n bar\n baz\n') == ( + 'foo\n\n bar\nbaz\n') + Modified: py/trunk/py/apigen/tracer/description.py ============================================================================== --- py/trunk/py/apigen/tracer/description.py (original) +++ py/trunk/py/apigen/tracer/description.py Mon Jan 29 15:20:31 2007 @@ -19,7 +19,10 @@ self.filename = frame.code.raw.co_filename self.lineno = frame.lineno self.firstlineno = frame.code.firstlineno - self.source = getsource(frame.code.raw) + try: + self.source = getsource(frame.code.raw) + except IOError: + self.source = "could not get to source" def _getval(self): return (self.filename, self.lineno) From guido at codespeak.net Mon Jan 29 17:11:23 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 29 Jan 2007 17:11:23 +0100 (CET) Subject: [py-svn] r37545 - in py/trunk/py/apigen: . testing tracer tracer/testing Message-ID: <20070129161123.A454A10064@code0.codespeak.net> Author: guido Date: Mon Jan 29 17:11:15 2007 New Revision: 37545 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/tracer/docstorage.py py/trunk/py/apigen/tracer/testing/test_docgen.py Log: Moved get_star_import_tree out of the DocStorage class, created 'pkg_to_dict' function in docstorage.py and using that from apigen.py (to later simplify py.test integration, and to improve testability). Simplified 'deindent()' a bit, made the wrap_page use the filenames from the stylesheeturl and the scripturls instead of having them hard-coded, some minor HTML/CSS changes. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Mon Jan 29 17:11:15 2007 @@ -10,11 +10,12 @@ from py.__.apigen import htmlgen from py.__.apigen import linker from py.__.apigen import project +from py.__.apigen.tracer.docstorage import pkg_to_dict def get_documentable_items(pkgdir): sys.path.insert(0, str(pkgdir.dirpath())) rootmod = __import__(pkgdir.basename) - return rootmod + return pkg_to_dict(rootmod) def build(pkgdir, dsa): l = linker.Linker() Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Mon Jan 29 17:11:15 2007 @@ -19,26 +19,23 @@ line to the side always, and determines the indentation of the rest of the text by taking that of the least indented (filled) line """ - lines = str.split(linesep) + lines = str.strip().split(linesep) normalized = [] deindent = None normalized.append(lines[0].strip()) + # replace tabs with spaces, empty lines that contain spaces only, and + # find out what the smallest indentation is for line in lines[1:]: - if not line.strip(): + line = line.replace('\t', ' ') + stripped = line.strip() + if not stripped: normalized.append('') else: - line = line.rstrip() - line = line.replace('\t', ' ') - indent = 0 - for c in line: - if c != ' ': - break - indent += 1 + rstripped = line.rstrip() + indent = len(rstripped) - len(stripped) if deindent is None or indent < deindent: deindent = indent normalized.append(line) - while normalized[-1] == '': - normalized.pop() ret = [normalized[0]] for line in normalized[1:]: if not line: @@ -182,10 +179,12 @@ stylesheeturl=stylesheeturl, scripturls=scripturls) page.set_content(contentel) here = py.magic.autopath().dirpath() - style = here.join('style.css').read() - outputpath.join('style.css').write(style) - apijs = here.join('api.js').read() - outputpath.join('api.js').write(apijs) + style = here.join(stylesheeturl.split('/')[-1]).read() + outputpath.join(stylesheeturl.split('/')[-1]).write(style) + for spath in scripturls: + sname = spath.split('/')[-1] + sdata = here.join(sname).read() + outputpath.join(sname).write(sdata) return page # the PageBuilder classes take care of producing the docs (using the stuff @@ -281,6 +280,7 @@ try: tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8')) except UnicodeError: + # XXX we should fix non-ascii support here!! tag = H.NonPythonSource('no source available (binary file?)') nav = self.build_navigation(fspath) return tag, nav @@ -380,7 +380,7 @@ csdiv, ) snippet = H.FunctionDescription( - H.FunctionDef(localname, argdesc), + H.FunctionDef('def %s' % (localname,), argdesc), H.Docstring(docstring or '*no docstring available*'), H.div(H.a('show/hide info', href='#', @@ -684,7 +684,7 @@ call_site[0].filename, call_site[0].lineno + 1), href='#', onclick="showhideel(getnextsibling(this)); return false;"), - H.div(tbtag, style='display: none') + H.div(tbtag, style='display: none', class_='callstackitem'), ) return tag Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Mon Jan 29 17:11:15 2007 @@ -42,12 +42,15 @@ font-weight: bold; } -body, div, p, h1, h2, h3, h4 { - font-family: Trebuchet MS, Verdana, Arial; +body { background-color: #FFE; color: black; } +body, div, p, h1, h2, h3, h4 { + font-family: Trebuchet MS, Verdana, Arial; +} + a { color: #006; text-decoration: none; @@ -107,5 +110,11 @@ border: 1px solid black; color: black; padding: 1em; + background-color: white; +} + +.callstackitem { + border: 1px solid black; + margin-bottom: 1em; } Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Mon Jan 29 17:11:15 2007 @@ -47,6 +47,7 @@ 'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'), 'main.SomeTestSubClass': ('./sometestsubclass.py', 'SomeTestSubClass'), + 'somenamespace': ('./somenamespace.py', '*'), }) """)) temp.ensure('pak/test/test_pak.py').write(py.code.Source("""\ @@ -82,9 +83,9 @@ def test_get_documentable_items(): fs_root, package_name = setup_fs_project('test_get_documentable_items') documentable = apigen.get_documentable_items(fs_root.join(package_name)) - assert sorted(documentable.__package__.exportdefs.keys()) == [ + assert sorted(documentable.keys()) == [ 'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func', - 'main.sub.func'] + 'main.sub.func', 'somenamespace.baz', 'somenamespace.foo'] def test_apigen_functional(): fs_root, package_name = setup_fs_project('test_apigen_functional') Modified: py/trunk/py/apigen/tracer/docstorage.py ============================================================================== --- py/trunk/py/apigen/tracer/docstorage.py (original) +++ py/trunk/py/apigen/tracer/docstorage.py Mon Jan 29 17:11:15 2007 @@ -15,6 +15,44 @@ sorted = py.builtin.sorted +def pkg_to_dict(module): + defs = module.__package__.exportdefs + d = {} + for key, value in defs.iteritems(): + chain = key.split('.') + base = module + for elem in chain: + base = getattr(base, elem) + if value[1] == '*': + d.update(get_star_import_tree(base, key)) + else: + d[key] = base + return d + +def get_star_import_tree(module, modname): + """ deal with '*' entries in an initpkg situation """ + ret = {} + modpath = py.path.local(inspect.getsourcefile(module)) + pkgpath = module.__package__.getpath() + for objname in dir(module): + if objname.startswith('_'): + continue # also skip __*__ attributes + obj = getattr(module, objname) + if (isinstance(obj, types.ClassType) or + isinstance(obj, types.ObjectType)): + try: + sourcefile_object = py.path.local( + inspect.getsourcefile(obj)) + except TypeError: + continue + else: + if sourcefile_object.strpath != modpath.strpath: + # not in this package + continue + dotted_name = '%s.%s' % (modname, objname) + ret[dotted_name] = obj + return ret + class DocStorage(object): """ Class storing info about API """ @@ -117,45 +155,10 @@ def from_pkg(self, module, keep_frames=False): self.module = module - defs = module.__package__.exportdefs - d = {} - for key, value in defs.iteritems(): - chain = key.split('.') - base = module - for elem in chain: - base = getattr(base, elem) - if value[1] == '*': - d.update(self.get_star_import_tree(base, key)) - else: - d[key] = base - self.from_dict(d, keep_frames) + self.from_dict(pkg_to_dict(module), keep_frames) # XXX return self - def get_star_import_tree(self, module, modname): - """ deal with '*' entries in an initpkg situation """ - ret = {} - modpath = py.path.local(inspect.getsourcefile(module)) - pkgpath = module.__package__.getpath() - for objname in dir(module): - if objname.startswith('_'): - continue # also skip __*__ attributes - obj = getattr(module, objname) - if (isinstance(obj, types.ClassType) or - isinstance(obj, types.ObjectType)): - try: - sourcefile_object = py.path.local( - inspect.getsourcefile(obj)) - except TypeError: - continue - else: - if sourcefile_object.strpath != modpath.strpath: - # not in this package - continue - dotted_name = '%s.%s' % (modname, objname) - ret[dotted_name] = obj - return ret - def from_module(self, func): raise NotImplementedError("From module") Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_docgen.py (original) +++ py/trunk/py/apigen/tracer/testing/test_docgen.py Mon Jan 29 17:11:15 2007 @@ -6,8 +6,9 @@ import sys #try: -from py.__.apigen.tracer.tracer import DocStorage, Tracer -from py.__.apigen.tracer.docstorage import DocStorageAccessor +from py.__.apigen.tracer.tracer import Tracer +from py.__.apigen.tracer.docstorage import DocStorageAccessor, DocStorage, \ + get_star_import_tree, pkg_to_dict from py.__.apigen.tracer.testing.runtest import cut_pyc from py.__.apigen.tracer.description import FunctionDesc from py.__.apigen.tracer import model @@ -426,7 +427,7 @@ def test_get_initpkg_star_items(): pkg, ds = setup_pkg_docstorage() - sit = ds.get_star_import_tree(pkg.other, 'pkg.other') + sit = get_star_import_tree(pkg.other, 'pkg.other') assert sorted(sit.keys()) == ['pkg.other.baz', 'pkg.other.foo'] t = Tracer(ds) t.start_tracing() @@ -446,3 +447,11 @@ assert isinstance(cell, model.SomeInstance) assert cell.classdef.cls is desc.pyobj +def test_pkg_to_dict(): + pkg, ds = setup_pkg_docstorage() + assert sorted(pkg_to_dict(pkg).keys()) == ['main.SomeClass', + 'main.SomeSubClass', + 'main.sub.func', + 'other.baz', + 'other.foo'] + From fijal at codespeak.net Mon Jan 29 18:11:15 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 29 Jan 2007 18:11:15 +0100 (CET) Subject: [py-svn] r37553 - py/trunk/py/path/local/testing Message-ID: <20070129171115.E8DF110074@code0.codespeak.net> Author: fijal Date: Mon Jan 29 18:11:13 2007 New Revision: 37553 Modified: py/trunk/py/path/local/testing/test_local.py Log: Skip if boxed Modified: py/trunk/py/path/local/testing/test_local.py ============================================================================== --- py/trunk/py/path/local/testing/test_local.py (original) +++ py/trunk/py/path/local/testing/test_local.py Mon Jan 29 18:11:13 2007 @@ -261,6 +261,8 @@ assert not numdir.new(ext=str(i-3)).check() def test_locked_make_numbered_dir(self): + if py.test.config.is_boxed(): + py.test.skip("Fails under boxing") root = self.tmpdir for i in range(10): numdir = local.make_numbered_dir(prefix='base.', rootdir=root, From fijal at codespeak.net Mon Jan 29 18:12:22 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 29 Jan 2007 18:12:22 +0100 (CET) Subject: [py-svn] r37554 - py/trunk/py/execnet Message-ID: <20070129171222.4035D10075@code0.codespeak.net> Author: fijal Date: Mon Jan 29 18:12:17 2007 New Revision: 37554 Modified: py/trunk/py/execnet/rsync.py Log: add XXX Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Mon Jan 29 18:12:17 2007 @@ -139,6 +139,7 @@ basename = path[len(self.sourcedir) + 1:] if not linkpoint.startswith(os.sep): # relative link, just send it + # XXX: do sth with ../ links self._send_link(basename, linkpoint) elif linkpoint.startswith(self.sourcedir): self._send_link(basename, linkpoint[len(self.sourcedir) + 1:]) From fijal at codespeak.net Mon Jan 29 18:14:30 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 29 Jan 2007 18:14:30 +0100 (CET) Subject: [py-svn] r37555 - py/trunk/py/doc Message-ID: <20070129171430.C004710075@code0.codespeak.net> Author: fijal Date: Mon Jan 29 18:14:27 2007 New Revision: 37555 Modified: py/trunk/py/doc/TODO.txt Log: update Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Mon Jan 29 18:14:27 2007 @@ -102,14 +102,15 @@ use that check if all py and PyPy tests pass as good as they do without. -* make --box run on the trunk. This requires having some sort of +* (DONE, except py.path.svn) make --box run on the trunk. + This requires having some sort of is_boxed() function, while having it on config object seems to be not that smart idea. * (DONE more or less) try to be as 2.2 compatible as possible (use e.g. py.builtin.enumerate instead of "enumerate" directly) -* (DONE for rsession) have all sessions check their options via +* (DONE) have all sessions check their options via Session.fixoptions() and have session-particular tests and checks accordingly. From guido at codespeak.net Tue Jan 30 11:34:11 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 11:34:11 +0100 (CET) Subject: [py-svn] r37579 - in py/trunk/py: apigen apigen/testing apigen/tracer test/rsession Message-ID: <20070130103411.2A82C10063@code0.codespeak.net> Author: guido Date: Tue Jan 30 11:34:08 2007 New Revision: 37579 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/tracer/docstorage.py py/trunk/py/test/rsession/rsession.py Log: Made that the package name can be set when using from_dict() to fill a DocStorage. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Tue Jan 30 11:34:08 2007 @@ -15,7 +15,7 @@ def get_documentable_items(pkgdir): sys.path.insert(0, str(pkgdir.dirpath())) rootmod = __import__(pkgdir.basename) - return pkg_to_dict(rootmod) + return 'py', pkg_to_dict(rootmod) def build(pkgdir, dsa): l = linker.Linker() Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Tue Jan 30 11:34:08 2007 @@ -82,7 +82,9 @@ def test_get_documentable_items(): fs_root, package_name = setup_fs_project('test_get_documentable_items') - documentable = apigen.get_documentable_items(fs_root.join(package_name)) + pkgname, documentable = apigen.get_documentable_items( + fs_root.join(package_name)) + assert pkgname == 'py' assert sorted(documentable.keys()) == [ 'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func', 'main.sub.func', 'somenamespace.baz', 'somenamespace.foo'] Modified: py/trunk/py/apigen/tracer/docstorage.py ============================================================================== --- py/trunk/py/apigen/tracer/docstorage.py (original) +++ py/trunk/py/apigen/tracer/docstorage.py Tue Jan 30 11:34:08 2007 @@ -56,6 +56,9 @@ class DocStorage(object): """ Class storing info about API """ + def __init__(self): + self.module_name = None + def consider_call(self, frame, caller_frame, upward_cut_frame=None): assert isinstance(frame, py.code.Frame) desc = self.find_desc(frame.code, frame.raw.f_locals) @@ -102,7 +105,8 @@ for key, desc in self.descs.iteritems(): self.desc_cache[desc] = desc - def from_dict(self, _dict, keep_frames = False): + def from_dict(self, _dict, keep_frames=False, module_name=None): + self.module_name = module_name self.descs = {} for key, val in _dict.iteritems(): to_key, to_val = self.make_desc(key, val) @@ -155,7 +159,7 @@ def from_pkg(self, module, keep_frames=False): self.module = module - self.from_dict(pkg_to_dict(module), keep_frames) + self.from_dict(pkg_to_dict(module), keep_frames, module.__name__) # XXX return self @@ -260,7 +264,9 @@ return sorted([i.__name__ for i in self.ds.descs[name].exceptions.keys()]) def get_module_name(self): - if hasattr(self.ds, 'module'): + if self.ds.module_name is not None: + return self.ds.module_name + elif hasattr(self.ds, 'module'): return self.ds.module.__name__ return "Unknown module" Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Tue Jan 30 11:34:08 2007 @@ -270,32 +270,14 @@ def init_runner(self): if self.config.option.apigen: from py.__.apigen.tracer.tracer import Tracer, DocStorage - module = py - try: - pkgdir = self.getpkgdir(self.config.args[0]) - apigen = py.path.local(self.config.option.apigen).pyimport() - items = apigen.get_documentable_items(pkgdir) - if isinstance(items, dict): - self.docstorage = DocStorage().from_dict(items) - else: - self.docstorage = DocStorage().from_pkg(items) - except ImportError: - import traceback - exc, e, tb = sys.exc_info() - print '%s - %s' % (exc, e) - print ''.join(traceback.format_tb(tb)) - del tb - print '-' * 79 - raise ImportError("Provided script cannot be imported") - except (ValueError, AttributeError): - import traceback - exc, e, tb = sys.exc_info() - print '%s - %s' % (exc, e) - print ''.join(traceback.format_tb(tb)) - del tb - print '-' * 79 + pkgdir = self.getpkgdir(self.config.args[0]) + apigen = py.path.local(self.config.option.apigen).pyimport() + if not hasattr(apigen, 'get_documentable_items'): raise NotImplementedError("Provided script does not seem " "to contain get_documentable_items") + pkgname, items = apigen.get_documentable_items(pkgdir) + self.docstorage = DocStorage().from_dict(items, + module_name=pkgname) self.tracer = Tracer(self.docstorage) return apigen_runner else: From guido at codespeak.net Tue Jan 30 11:45:48 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 11:45:48 +0100 (CET) Subject: [py-svn] r37582 - py/trunk/py/test/rsession/testing Message-ID: <20070130104548.DA7A310071@code0.codespeak.net> Author: guido Date: Tue Jan 30 11:45:46 2007 New Revision: 37582 Modified: py/trunk/py/test/rsession/testing/test_lsession.py Log: Updated mock-up function return value. Modified: py/trunk/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_lsession.py (original) +++ py/trunk/py/test/rsession/testing/test_lsession.py Tue Jan 30 11:45:46 2007 @@ -271,7 +271,7 @@ tmpdir = py.test.ensuretemp("lsession_runner_selection") tmpdir.ensure("apigen.py").write(py.code.Source(""" def get_documentable_items(*args): - return {} + return 'foo', {} """)) opt_mapping = { '': plain_runner, From guido at codespeak.net Tue Jan 30 14:24:32 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 14:24:32 +0100 (CET) Subject: [py-svn] r37594 - in py/trunk/py/apigen: . source source/testing testing Message-ID: <20070130132432.CE33710083@code0.codespeak.net> Author: guido Date: Tue Jan 30 14:24:27 2007 New Revision: 37594 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/source/html.py py/trunk/py/apigen/source/testing/test_html.py py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/todo-apigen.txt Log: Fixed unicode issues in apigen/htmlgen.py and apigen/source, moved some methods out of HTMLDocument and added support for finding out the encoding of a Python file in order to accomplish that (both in source/html.py), fixed some minor issues (items with a name starting with _ are now hidden from nav, fixed indentation issue in nav) in htmlgen.py. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Jan 30 14:24:27 2007 @@ -361,12 +361,12 @@ H.a('source: %s' % (sourcefile,), href=self.linker.get_lazyhref(sourcefile)), H.br(), - H.SourceDef(H.pre(callable_source))) + H.SourceDef(H.pre(unicode(callable_source, 'UTF-8')))) elif not is_in_pkg and sourcefile and callable_source: csource = H.div(H.br(), H.em('source: %s' % (sourcefile,)), H.br(), - H.SourceDef(H.pre(callable_source))) + H.SourceDef(H.pre(unicode(callable_source, 'UTF-8')))) else: csource = H.SourceDef('could not get source file') @@ -460,6 +460,8 @@ H.Docstring(docstring or '*no docstring available*') ) for dotted_name in sorted(item_dotted_names): + if dotted_name.startswith('_'): + continue itemname = dotted_name.split('.')[-1] if is_private(itemname): continue @@ -586,7 +588,7 @@ elif lastlevel and build_children: # XXX hack navitems += build_nav_level('%s.' % (dotted_name,), - depth+2) + depth+1) return navitems @@ -698,9 +700,9 @@ mangled = [] for i, sline in enumerate(str(source).split('\n')): if i == lineno: - l = '-> %s' % (sline,) + l = '-> %s' % (unicode(sline, 'UTF-8'),) else: - l = ' %s' % (sline,) + l = ' %s' % (unicode(sline, 'UTF-8'),) mangled.append(l) if sourcefile: linktext = '%s - line %s' % (sourcefile, line.lineno + 1) Modified: py/trunk/py/apigen/source/html.py ============================================================================== --- py/trunk/py/apigen/source/html.py (original) +++ py/trunk/py/apigen/source/html.py Tue Jan 30 14:24:27 2007 @@ -2,14 +2,13 @@ """ html - generating ad-hoc html out of source browser """ +import py from py.xml import html, raw from compiler import ast import time from py.__.apigen.source.color import Tokenizer, PythonSchema class HtmlEnchanter(object): - reserved_words = ['if', 'for', 'return', 'yield'] - def __init__(self, mod): self.mod = mod self.create_caches() @@ -37,8 +36,30 @@ except KeyError: return [row] # no more info +def prepare_line(text, tokenizer, encoding): + """ adds html formatting to text items (list) + + only processes items if they're of a string type (or unicode) + """ + ret = [] + for item in text: + if type(item) in [str, unicode]: + tokens = tokenizer.tokenize(item) + for t in tokens: + data = unicode(t.data, encoding) + if t.type in ['keyword', 'alt_keyword', 'number', + 'string', 'comment']: + ret.append(html.span(data, class_=t.type)) + else: + ret.append(data) + else: + ret.append(item) + return ret + class HTMLDocument(object): - def __init__(self, tokenizer=None): + def __init__(self, encoding, tokenizer=None): + self.encoding = encoding + self.html = root = html.html() self.head = head = self.create_head() root.append(head) @@ -119,30 +140,11 @@ table.append(tbody) return table, tbody - def prepare_line(self, text): - """ adds html formatting to text items (list) - - only processes items if they're of a string type (or unicode) - """ - ret = [] - for item in text: - if type(item) in [str, unicode]: - tokens = self.tokenizer.tokenize(item) - for t in tokens: - if t.type in ['keyword', 'alt_keyword', 'number', - 'string', 'comment']: - ret.append(html.span(t.data, class_=t.type)) - else: - ret.append(t.data) - else: - ret.append(item) - return ret - def add_row(self, lineno, text): if text == ['']: text = [raw(' ')] else: - text = self.prepare_line(text) + text = prepare_line(text, self.tokenizer, self.encoding) self.tbody.append(html.tr(html.td(str(lineno), class_='lineno'), html.td(class_='code', *text))) @@ -157,7 +159,8 @@ lines = mod.path.open().readlines() enchanter = HtmlEnchanter(mod) - doc = HTMLDocument() + enc = get_module_encoding(mod.path) + doc = HTMLDocument(enc) for i, row in enumerate(lines): row = enchanter.enchant_row(i + 1, row) doc.add_row(i + 1, row) @@ -248,3 +251,16 @@ ) return h.unicode() +_reg_enc = py.std.re.compile(r'coding[:=]\s*([-\w.]+)') +def get_module_encoding(path): + if hasattr(path, 'strpath'): + path = path.strpath + if path[-1] in ['c', 'o']: + path = path[:-1] + fpath = py.path.local(path) + code = fpath.read() + match = _reg_enc.search(code) + if match: + return match.group(1) + return 'ISO-8859-1' + Modified: py/trunk/py/apigen/source/testing/test_html.py ============================================================================== --- py/trunk/py/apigen/source/testing/test_html.py (original) +++ py/trunk/py/apigen/source/testing/test_html.py Tue Jan 30 14:24:27 2007 @@ -1,9 +1,12 @@ +# -*- coding: UTF-8 -*- """ test of html generation """ -from py.__.apigen.source.html import create_html, HTMLDocument +from py.__.apigen.source.html import prepare_line, create_html, HTMLDocument, \ + get_module_encoding from py.__.apigen.source.browser import parse_path +from py.__.apigen.source.color import Tokenizer, PythonSchema from py.xml import html import py @@ -49,7 +52,7 @@ class _HTMLDocument(HTMLDocument): def __init__(self): - pass + self.encoding = 'ascii' class TestHTMLDocument(object): def test_head(self): @@ -73,51 +76,8 @@ assert isinstance(tbody, html.tbody) assert tbody == table[0] - def prepare_line(self, line, doc=None): - if doc is None: - doc = HTMLDocument() - l = doc.prepare_line(line) - return ''.join([unicode(i) for i in l]) - - def test_prepare_line_basic(self): - result = self.prepare_line(['see if this works']) - assert result == 'see if this works' - result = self.prepare_line(['see if this ', - html.a('works', name='works'),' too']) - assert result == ('see if this ' - 'works too') - result = self.prepare_line(['see if something else works']) - assert result == ('see if something ' - 'else works') - result = self.prepare_line(['see if something ', - html.a('else', name='else'), ' works too']) - assert result == ('see if something ' - 'else works too') - - def test_prepare_line_strings(self): - result = self.prepare_line(['foo = "bar"']) - assert result == 'foo = "bar"' - - result = self.prepare_line(['"spam"']) - assert result == '"spam"' - - # test multiline strings - doc = HTMLDocument() - result = self.prepare_line(['"""start of multiline'], doc) - assert result == ('"""start of ' - 'multiline') - # doc should now be in 'string mode' - result = self.prepare_line(['see if it doesn\'t touch this'], doc) - assert result == ('see if it doesn't touch ' - 'this') - result = self.prepare_line(['"""'], doc) - assert result == '"""' - result = self.prepare_line(['see if it colours this again'], doc) - assert result == ('see if it colours ' - 'this again') - def test_add_row(self): - doc = HTMLDocument() + doc = HTMLDocument('ascii') doc.add_row(1, ['""" this is a foo implementation """']) doc.add_row(2, ['']) doc.add_row(3, ['class ', html.a('Foo', name='Foo'), ':']) @@ -141,9 +101,79 @@ '') def test_unicode(self): - doc = HTMLDocument() + doc = HTMLDocument('ascii') h = unicode(doc) print h assert py.std.re.match(r'\s*\s*[^<]+' '.*\w*$', h, py.std.re.S) +def prepare_line_helper(line, tokenizer=None, encoding='ascii'): + if tokenizer is None: + tokenizer = Tokenizer(PythonSchema) + l = prepare_line(line, tokenizer, encoding) + return ''.join([unicode(i) for i in l]) + +def test_prepare_line_basic(): + result = prepare_line_helper(['see if this works']) + assert result == 'see if this works' + result = prepare_line_helper(['see if this ', + html.a('works', name='works'),' too']) + assert result == ('see if this ' + 'works too') + result = prepare_line_helper(['see if something else works']) + assert result == ('see if something ' + 'else works') + result = prepare_line_helper(['see if something ', + html.a('else', name='else'), ' works too']) + assert result == ('see if something ' + 'else works too') + +def test_prepare_line_strings(): + result = prepare_line_helper(['foo = "bar"']) + assert result == 'foo = "bar"' + + result = prepare_line_helper(['"spam"']) + assert result == '"spam"' + +def test_prepare_line_multiline_strings(): + # test multiline strings + t = Tokenizer(PythonSchema) + result = prepare_line_helper(['"""start of multiline'], t) + assert result == ('"""start of ' + 'multiline') + result = prepare_line_helper(['see if it doesn\'t touch this'], t) + assert result == ('see if it doesn't touch ' + 'this') + result = prepare_line_helper(['"""'], t) + assert result == '"""' + result = prepare_line_helper(['see if it colours this again'], t) + assert result == ('see if it colours ' + 'this again') + +def test_prepare_line_nonascii(): + result = prepare_line_helper(['"f??"'], encoding='UTF-8') + assert (result == + unicode('"f??"', 'UTF-8')) + +def test_get_encoding_ascii(): + temp = py.test.ensuretemp('test_get_encoding') + fpath = temp.join('ascii.py') + fpath.write(str(py.code.Source("""\ + def foo(): + return 'foo' + """))) + # XXX I think the specs say we have to assume latin-1 here... + assert get_module_encoding(fpath.strpath) == 'ISO-8859-1' + +def test_get_encoding_for_real(): + temp = py.test.ensuretemp('test_get_encoding') + fpath = temp.join('utf-8.py') + fpath.write(str(py.code.Source("""\ + #!/usr/bin/env python + # -*- coding: UTF-8 -*- + + def foo(): + return 'f??' + """))) + assert get_module_encoding(fpath.strpath) == 'UTF-8' + Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Tue Jan 30 14:24:27 2007 @@ -38,6 +38,8 @@ return 'bar' def baz(qux): return qux + def _hidden(): + return 'quux' """)) temp.ensure("pak/__init__.py").write(py.code.Source("""\ from py.initpkg import initpkg @@ -77,6 +79,8 @@ ''') c = compile(str(source), '', 'exec') exec c in globals() + + assert pak.somenamespace._hidden() == 'quux' """)) return temp, 'pak' Modified: py/trunk/py/apigen/todo-apigen.txt ============================================================================== --- py/trunk/py/apigen/todo-apigen.txt (original) +++ py/trunk/py/apigen/todo-apigen.txt Tue Jan 30 14:24:27 2007 @@ -1,5 +1,5 @@ -* format docstrings more nicely (with tests) +* format docstrings more nicely (with tests) - DONE I guess * have the API function view be as informative as possible without having to go to the "single method" view @@ -10,7 +10,9 @@ viewed. method views (when navigating there through the class view) should also have the source there -* have class-level attributes be displayed + DONE I guess (todo: add syntax coloring) + +* have class-level attributes be displayed * use "inherited" doc strings, i.e. for class A: @@ -30,11 +32,11 @@ be separately tested and the caller should not need to guess what it will get, i think) + DONE + * look out for and streamline all apigen/source-viewer documentation into one document - - * consider automating dependencies: e.g. something like: queue_render(page, fspath, linker, ...) @@ -61,8 +63,22 @@ ... raise ... + NOT SURE if this is still required + * also we might have a support function for tests that fills the linker with "dummy hrefs" for certain types like source links + + KIND OF DONE, the tests now use a linker that just doesn't + barf on non-existing linkids anymore, which seems to be + good enough (we may want to add more sophisticated debugging + later, but for now this works) -* XXX list more here +* add syntax coloring for Python source snippets + +* remove py.test/apigen cruft from stack traces + +* fix non-ascii source encoding support + +* XXX + From hpk at codespeak.net Tue Jan 30 14:38:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Jan 2007 14:38:50 +0100 (CET) Subject: [py-svn] r37595 - py/dist/py/test/terminal Message-ID: <20070130133850.EA51B10080@code0.codespeak.net> Author: hpk Date: Tue Jan 30 14:38:49 2007 New Revision: 37595 Modified: py/dist/py/test/terminal/remote.py Log: waiting longer for re-checking on modified files Modified: py/dist/py/test/terminal/remote.py ============================================================================== --- py/dist/py/test/terminal/remote.py (original) +++ py/dist/py/test/terminal/remote.py Tue Jan 30 14:38:49 2007 @@ -135,7 +135,7 @@ while 1: if config.option.looponfailing and (failures or not wasfailing): while not checkpyfilechange(rootdir): - py.std.time.sleep(0.4) + py.std.time.sleep(2.4) wasfailing = len(failures) failures = failure_master(config.option.executable, out, args, failures) if not config.option.looponfailing: From hpk at codespeak.net Tue Jan 30 14:44:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Jan 2007 14:44:53 +0100 (CET) Subject: [py-svn] r37596 - py/trunk/py/test/terminal Message-ID: <20070130134453.0407610080@code0.codespeak.net> Author: hpk Date: Tue Jan 30 14:44:52 2007 New Revision: 37596 Modified: py/trunk/py/test/terminal/remote.py Log: bah, waiting longer also in py-trunk Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Tue Jan 30 14:44:52 2007 @@ -76,7 +76,7 @@ while 1: if self.config.option.looponfailing and (failures or not wasfailing): while not checkpyfilechange(rootdir): - py.std.time.sleep(0.4) + py.std.time.sleep(2.4) wasfailing = len(failures) failures = self.run_remote_session(failures) if not self.config.option.looponfailing: From hpk at codespeak.net Tue Jan 30 15:02:29 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Jan 2007 15:02:29 +0100 (CET) Subject: [py-svn] r37598 - in py/trunk/py/test: . testing tkinter Message-ID: <20070130140229.CE59C1008A@code0.codespeak.net> Author: hpk Date: Tue Jan 30 15:02:28 2007 New Revision: 37598 Removed: py/trunk/py/test/tkinter/ Modified: py/trunk/py/test/config.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/testing/test_config.py Log: removing broken tkinter session support ahead of the 0.9 release (might be re-introduced later) Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Tue Jan 30 15:02:28 2007 @@ -150,8 +150,6 @@ name = 'TerminalSession' if self.option.dist: name = 'RSession' - elif self.option.tkinter: - name = 'TkinterSession' else: optnames = 'startserver runbrowser apigen restreport boxing'.split() for opt in optnames: @@ -253,8 +251,6 @@ # default import paths for sessions -TkinterSession = 'py.__.test.tkinter.reportsession' -TerminalSession = 'py.__.test.terminal.terminal' TerminalSession = 'py.__.test.terminal.terminal' RemoteTerminalSession = 'py.__.test.terminal.remote' RSession = 'py.__.test.rsession.rsession' Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Tue Jan 30 15:02:28 2007 @@ -86,9 +86,6 @@ action="store_true", dest="runbrowser", default=False, help="run browser (implies --startserver)." ), - Option('', '--tkinter', - action="store_true", dest="tkinter", default=False, - help="use tkinter test session frontend."), Option('', '--box', action="store_true", dest="boxing", help="use boxing (running each test in external process)"), 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 Tue Jan 30 15:02:28 2007 @@ -224,11 +224,6 @@ config = py.test.config._reparse([self.tmpdir, '--dist', '--exec=x']) assert config._getsessionname() == 'RSession' - def test_tkintersession(self): - config = py.test.config._reparse([self.tmpdir, '--tkinter']) - assert config._getsessionname() == 'TkinterSession' - config = py.test.config._reparse([self.tmpdir, '--dist']) - def test_sessionname_lookup_custom(self): self.tmpdir.join("conftest.py").write(py.code.Source(""" from py.__.test.session import Session From hpk at codespeak.net Tue Jan 30 15:08:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Jan 2007 15:08:48 +0100 (CET) Subject: [py-svn] r37599 - py/trunk/py/apigen Message-ID: <20070130140848.9BD6810089@code0.codespeak.net> Author: hpk Date: Tue Jan 30 15:08:47 2007 New Revision: 37599 Modified: py/trunk/py/apigen/htmlgen.py Log: fixing a windows-error: i believe that most strings passed into htmlgen.deindent() actually have a normalized '\n' line-ending already (e.g. if they come from python docstrings). So the caller now needs to specify otherwise. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Jan 30 15:08:47 2007 @@ -12,7 +12,7 @@ html = py.xml.html raw = py.xml.raw -def deindent(str, linesep=os.linesep): +def deindent(str, linesep='\n'): """ de-indent string can be used to de-indent Python docstrings, it de-indents the first @@ -26,7 +26,7 @@ # replace tabs with spaces, empty lines that contain spaces only, and # find out what the smallest indentation is for line in lines[1:]: - line = line.replace('\t', ' ') + line = line.replace('\t', ' ' * 4) stripped = line.strip() if not stripped: normalized.append('') From guido at codespeak.net Tue Jan 30 15:46:59 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 15:46:59 +0100 (CET) Subject: [py-svn] r37602 - in py/trunk/py/apigen/source: . testing Message-ID: <20070130144659.4AC211008F@code0.codespeak.net> Author: guido Date: Tue Jan 30 15:46:57 2007 New Revision: 37602 Modified: py/trunk/py/apigen/source/color.py py/trunk/py/apigen/source/html.py py/trunk/py/apigen/source/testing/test_color.py Log: Fixed support for files ending on a comment rather than a newline, fixed some unicode() call so it's not done on objects that are already unicode. Modified: py/trunk/py/apigen/source/color.py ============================================================================== --- py/trunk/py/apigen/source/color.py (original) +++ py/trunk/py/apigen/source/color.py Tue Jan 30 15:46:57 2007 @@ -4,7 +4,7 @@ class PythonSchema(object): """ contains information for syntax coloring """ - comment = [('#', '\n')] + comment = [('#', '\n'), ('#', '$')] multiline_string = ['"""', "'''"] string = ['"""', "'''", '"', "'"] # XXX not complete @@ -125,7 +125,6 @@ break return data, token - def _check_comments(self, data): # fortunately we don't have to deal with multi-line comments token = None Modified: py/trunk/py/apigen/source/html.py ============================================================================== --- py/trunk/py/apigen/source/html.py (original) +++ py/trunk/py/apigen/source/html.py Tue Jan 30 15:46:57 2007 @@ -46,7 +46,10 @@ if type(item) in [str, unicode]: tokens = tokenizer.tokenize(item) for t in tokens: - data = unicode(t.data, encoding) + if not isinstance(t.data, unicode): + data = unicode(t.data, encoding) + else: + data = t.data if t.type in ['keyword', 'alt_keyword', 'number', 'string', 'comment']: ret.append(html.span(data, class_=t.type)) Modified: py/trunk/py/apigen/source/testing/test_color.py ============================================================================== --- py/trunk/py/apigen/source/testing/test_color.py (original) +++ py/trunk/py/apigen/source/testing/test_color.py Tue Jan 30 15:46:57 2007 @@ -45,8 +45,12 @@ assert self.tokens('foo # bar\n') == [Token('foo', type='word'), Token(' ', type='whitespace'), Token('# bar\n', type='comment')] + assert self.tokens("# foo 'bar\n") == [Token("# foo 'bar\n", + type='comment')] + assert self.tokens('# foo') == [Token('# foo', type='comment')] def test_string_simple(self): + assert self.tokens('""') == [Token('""', type='string')] assert self.tokens('"foo"') == [Token('"foo"', type='string')] assert self.tokens('"foo"\'bar\'') == [Token('"foo"', type='string'), Token("'bar'", type='string')] From guido at codespeak.net Tue Jan 30 16:42:22 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 16:42:22 +0100 (CET) Subject: [py-svn] r37608 - in py/trunk/py/xmlobj: . testing Message-ID: <20070130154222.95EEC100B5@code0.codespeak.net> Author: guido Date: Tue Jan 30 16:42:19 2007 New Revision: 37608 Modified: py/trunk/py/xmlobj/html.py py/trunk/py/xmlobj/testing/test_html.py py/trunk/py/xmlobj/testing/test_xml.py py/trunk/py/xmlobj/visit.py Log: Added some code to make sure 'inline' elements aren't indented (gotta love 'ignorable' whitespace). Modified: py/trunk/py/xmlobj/html.py ============================================================================== --- py/trunk/py/xmlobj/html.py (original) +++ py/trunk/py/xmlobj/html.py Tue Jan 30 16:42:19 2007 @@ -10,6 +10,10 @@ single = dict([(x, 1) for x in ('br,img,area,param,col,hr,meta,link,base,' 'input,frame').split(',')]) + inline = dict([(x, 1) for x in + ('a abbr acronym b basefont bdo big br cite code dfn em font ' + 'i img input kbd label q s samp select small span strike ' + 'strong sub sup textarea tt u var'.split(' '))]) def repr_attribute(self, attrs, name): if name == 'class_': @@ -21,6 +25,9 @@ def _issingleton(self, tagname): return tagname in self.single + def _isinline(self, tagname): + return tagname in self.inline + class HtmlTag(Tag): def unicode(self, indent=2): l = [] Modified: py/trunk/py/xmlobj/testing/test_html.py ============================================================================== --- py/trunk/py/xmlobj/testing/test_html.py (original) +++ py/trunk/py/xmlobj/testing/test_html.py Tue Jan 30 16:42:19 2007 @@ -46,3 +46,9 @@ h = html.head(html.script(src="foo")) assert unicode(h) == '' + +def test_inline(): + h = html.div(html.span('foo'), html.span('bar')) + assert (h.unicode(indent=2) == + '
foobar
') + Modified: py/trunk/py/xmlobj/testing/test_xml.py ============================================================================== --- py/trunk/py/xmlobj/testing/test_xml.py (original) +++ py/trunk/py/xmlobj/testing/test_xml.py Tue Jan 30 16:42:19 2007 @@ -55,3 +55,4 @@ x = ns.some(py.xml.raw("

literal

")) u = unicode(x) assert u == "

literal

" + Modified: py/trunk/py/xmlobj/visit.py ============================================================================== --- py/trunk/py/xmlobj/visit.py (original) +++ py/trunk/py/xmlobj/visit.py Tue Jan 30 16:42:19 2007 @@ -50,7 +50,7 @@ tag.parent = None self.visited[id(tag)] = 1 tagname = getattr(tag, 'xmlname', tag.__class__.__name__) - if self.curindent: + if self.curindent and not self._isinline(tagname): self.write("\n" + u' ' * self.curindent) if tag: self.curindent += self.indent @@ -100,3 +100,7 @@ """can (and will) be overridden in subclasses""" return self.shortempty + def _isinline(self, tagname): + """can (and will) be overridden in subclasses""" + return False + From guido at codespeak.net Tue Jan 30 16:43:01 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 16:43:01 +0100 (CET) Subject: [py-svn] r37609 - py/trunk/py/rest/testing Message-ID: <20070130154301.4F3E9100B7@code0.codespeak.net> Author: guido Date: Tue Jan 30 16:43:00 2007 New Revision: 37609 Modified: py/trunk/py/rest/testing/test_transform.py Log: Oops, forgot to check this in: update to deal with change in py.xml.html. Modified: py/trunk/py/rest/testing/test_transform.py ============================================================================== --- py/trunk/py/rest/testing/test_transform.py (original) +++ py/trunk/py/rest/testing/test_transform.py Tue Jan 30 16:43:00 2007 @@ -22,8 +22,7 @@ '
foo\tbar
'), (Rest(Paragraph(Link('foo', 'http://www.foo.com/'))), - ('

\n foo' - '

'))): + '

foo

')): html = convert_to_html(rest) assert html == expected From guido at codespeak.net Tue Jan 30 16:44:32 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 16:44:32 +0100 (CET) Subject: [py-svn] r37610 - py/trunk/py/apigen Message-ID: <20070130154432.C6261100B6@code0.codespeak.net> Author: guido Date: Tue Jan 30 16:44:30 2007 New Revision: 37610 Modified: py/trunk/py/apigen/htmlgen.py Log: Stack trace code snippets are now syntax colored. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Jan 30 16:44:30 2007 @@ -4,6 +4,7 @@ from py.__.apigen.layout import LayoutPage from py.__.apigen.source import browser as source_browser from py.__.apigen.source import html as source_html +from py.__.apigen.source import color as source_color from py.__.apigen.tracer.description import is_private from py.__.apigen.rest.genrest import split_of_last_part from py.__.apigen.linker import relpath @@ -356,17 +357,21 @@ callable_source = self.dsa.get_function_source(dotted_name) # i assume they're both either available or unavailable(XXX ?) is_in_pkg = self.is_in_pkg(sourcefile) - if is_in_pkg and sourcefile and callable_source: - csource = H.div(H.br(), - H.a('source: %s' % (sourcefile,), - href=self.linker.get_lazyhref(sourcefile)), - H.br(), - H.SourceDef(H.pre(unicode(callable_source, 'UTF-8')))) - elif not is_in_pkg and sourcefile and callable_source: - csource = H.div(H.br(), - H.em('source: %s' % (sourcefile,)), - H.br(), - H.SourceDef(H.pre(unicode(callable_source, 'UTF-8')))) + if sourcefile and callable_source: + enc = source_html.get_module_encoding(sourcefile) + if is_in_pkg: + csource = H.div(H.br(), + H.a('source: %s' % (sourcefile,), + href=self.linker.get_lazyhref(sourcefile)), + H.br(), + H.SourceDef(H.pre(unicode(callable_source, + enc)))) + elif not is_in_pkg: + csource = H.div(H.br(), + H.em('source: %s' % (sourcefile,)), + H.br(), + H.SourceDef(H.pre(unicode(callable_source, + enc)))) else: csource = H.SourceDef('could not get source file') @@ -693,30 +698,45 @@ _reg_source = py.std.re.compile(r'([^>]*)<(.*)>') def gen_traceback(self, funcname, call_site): tbdiv = H.div() - for line in call_site: - lineno = line.lineno - line.firstlineno - source = line.source - sourcefile = line.filename + for frame in call_site: + lineno = frame.lineno - frame.firstlineno + source = frame.source + sourcefile = frame.filename + + tokenizer = source_color.Tokenizer(source_color.PythonSchema) mangled = [] for i, sline in enumerate(str(source).split('\n')): if i == lineno: - l = '-> %s' % (unicode(sline, 'UTF-8'),) + l = '-> %s' % (sline,) else: - l = ' %s' % (unicode(sline, 'UTF-8'),) + l = ' %s' % (sline,) mangled.append(l) if sourcefile: - linktext = '%s - line %s' % (sourcefile, line.lineno + 1) + linktext = '%s - line %s' % (sourcefile, frame.lineno + 1) # skip py.code.Source objects and source files outside of the # package is_code_source = self._reg_source.match(sourcefile) if (not is_code_source and self.is_in_pkg(sourcefile)): + enc = source_html.get_module_encoding(sourcefile) href = self.linker.get_lazyhref(sourcefile) sourcelink = H.a(linktext, href=href) else: + enc = 'latin-1' sourcelink = H.div(linktext) + colored = [] + for i, line in enumerate(mangled): + try: + colored.append(H.span('%02s: ' % + (i + frame.firstlineno + 1))) + colored.append(source_html.prepare_line( + [line], tokenizer, enc)) + colored.append('\n') + except py.error.ENOENT: + colored = mangled + break else: sourcelink = H.div('source unknown') tbdiv.append(sourcelink) - tbdiv.append(H.pre('\n'.join(mangled))) + tbdiv.append(H.div(class_='code', *colored)) return tbdiv From hpk at codespeak.net Tue Jan 30 17:28:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Jan 2007 17:28:44 +0100 (CET) Subject: [py-svn] r37615 - py/trunk/py/apigen/testing Message-ID: <20070130162844.C8B18100C5@code0.codespeak.net> Author: hpk Date: Tue Jan 30 17:28:43 2007 New Revision: 37615 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py Log: skip this test on windows, it should work but doesn't and it's not too important (apigen does not fully run on win32 because of LSession not working there) Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Tue Jan 30 17:28:43 2007 @@ -94,6 +94,8 @@ 'main.sub.func', 'somenamespace.baz', 'somenamespace.foo'] def test_apigen_functional(): + if py.std.sys.platform == "win32": + py.test.skip("XXX test fails on windows") fs_root, package_name = setup_fs_project('test_apigen_functional') tempdir = py.test.ensuretemp('test_apigen_functional_results') pydir = py.magic.autopath().dirpath().dirpath().dirpath() From guido at codespeak.net Tue Jan 30 17:29:36 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 17:29:36 +0100 (CET) Subject: [py-svn] r37616 - in py/trunk/py/apigen: . testing Message-ID: <20070130162936.A81A8100C8@code0.codespeak.net> Author: guido Date: Tue Jan 30 17:29:33 2007 New Revision: 37616 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_example.py Log: All code snippets are colored now, also properly fixed hiding of _* names in the navigation. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Jan 30 17:29:33 2007 @@ -332,6 +332,20 @@ reltargetpath = outputpath.relto(self.base).replace(os.path.sep, '/') self.write_page(title, reltargetpath, project, tag, nav) +def enumerate_and_color(codelines, firstlineno, enc): + tokenizer = source_color.Tokenizer(source_color.PythonSchema) + colored = [] + for i, line in enumerate(codelines): + try: + colored.append(H.span('%04s: ' % (i + firstlineno + 1))) + colored.append(source_html.prepare_line([line], tokenizer, enc)) + colored.append('\n') + except py.error.ENOENT: + # error reading source code, giving up + colored = org + break + return colored + class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ def __init__(self, base, linker, dsa, projroot, namespace_tree): @@ -359,19 +373,17 @@ is_in_pkg = self.is_in_pkg(sourcefile) if sourcefile and callable_source: enc = source_html.get_module_encoding(sourcefile) + tokenizer = source_color.Tokenizer(source_color.PythonSchema) + firstlineno = func.func_code.co_firstlineno + org = callable_source.split('\n') + colored = enumerate_and_color(org, firstlineno, enc) if is_in_pkg: - csource = H.div(H.br(), - H.a('source: %s' % (sourcefile,), - href=self.linker.get_lazyhref(sourcefile)), - H.br(), - H.SourceDef(H.pre(unicode(callable_source, - enc)))) - elif not is_in_pkg: - csource = H.div(H.br(), - H.em('source: %s' % (sourcefile,)), - H.br(), - H.SourceDef(H.pre(unicode(callable_source, - enc)))) + slink = H.a('source: %s' % (sourcefile,), + href=self.linker.get_lazyhref(sourcefile)) + else: + slink = H.em('source: %s' % (sourcefile,)) + csource = H.div(H.br(), slink, H.br(), + H.SourceDef(H.div(class_='code', *colored))) else: csource = H.SourceDef('could not get source file') @@ -465,8 +477,6 @@ H.Docstring(docstring or '*no docstring available*') ) for dotted_name in sorted(item_dotted_names): - if dotted_name.startswith('_'): - continue itemname = dotted_name.split('.')[-1] if is_private(itemname): continue @@ -583,9 +593,11 @@ for dn in sorted(siblings): selected = dn == '.'.join(path) sibpath = dn.split('.') - navitems.append(build_navitem_html(self.linker, sibpath[-1], - dn, depth, - selected)) + sibname = sibpath[-1] + if is_private(sibname): + continue + navitems.append(build_navitem_html(self.linker, sibname, + dn, depth, selected)) if selected: lastlevel = dn.count('.') == dotted_name.count('.') if not lastlevel: @@ -723,17 +735,7 @@ else: enc = 'latin-1' sourcelink = H.div(linktext) - colored = [] - for i, line in enumerate(mangled): - try: - colored.append(H.span('%02s: ' % - (i + frame.firstlineno + 1))) - colored.append(source_html.prepare_line( - [line], tokenizer, enc)) - colored.append('\n') - except py.error.ENOENT: - colored = mangled - break + colored = enumerate_and_color(mangled, frame.firstlineno, enc) else: sourcelink = H.div('source unknown') tbdiv.append(sourcelink) Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Tue Jan 30 17:29:33 2007 @@ -88,6 +88,8 @@ padding-left: 1em; white-space: pre; font-family: monospace, Monaco; + margin-top: 0.5em; + margin-bottom: 0.5em; } .comment { 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 Tue Jan 30 17:29:33 2007 @@ -63,6 +63,7 @@ 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), 'other': ('./somenamespace.py', '*'), + '_test': ('./somenamespace.py', '*'), }) """)) return temp, 'pkg' @@ -114,7 +115,8 @@ 'main.SomeSubClass', 'main.SomeInstance', 'other.foo', - 'other.bar']) + 'other.bar', + '_test']) self.namespace_tree = namespace_tree self.apb = ApiPageBuilder(base, linker, self.dsa, self.fs_root.join(self.pkg_name), @@ -195,6 +197,7 @@ def test_build_class_pages_nav_links(self): data = self.apb.prepare_class_pages(['main.SomeSubClass', 'main.SomeClass']) + self.apb.prepare_namespace_pages() # fake some stuff that would be built from other methods self.linker.set_link('', 'api/index.html') self.linker.set_link('main', 'api/main.html') @@ -211,8 +214,8 @@ 'href="main.SomeClass.html">SomeClass', 'href="main.SomeSubClass.html">SomeSubClass', ]) - assert not 'href="main.sub.func.html"' in html - py.test.skip('WOP from here') + assert 'href="main.sub.func.html"' not in html + assert 'href="_test' not in html assert 'href="main.sub.html">sub' in html _checkhtml(html) From fijal at codespeak.net Tue Jan 30 17:54:16 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 30 Jan 2007 17:54:16 +0100 (CET) Subject: [py-svn] r37620 - py/trunk/py/compat Message-ID: <20070130165416.EA02F1009B@code0.codespeak.net> Author: fijal Date: Tue Jan 30 17:54:14 2007 New Revision: 37620 Modified: py/trunk/py/compat/doctest.py Log: ensure 2.5 compatibility Modified: py/trunk/py/compat/doctest.py ============================================================================== --- py/trunk/py/compat/doctest.py (original) +++ py/trunk/py/compat/doctest.py Tue Jan 30 17:54:14 2007 @@ -1324,7 +1324,7 @@ __LINECACHE_FILENAME_RE = re.compile(r'[\w\.]+)' r'\[(?P\d+)\]>$') - def __patched_linecache_getlines(self, filename): + def __patched_linecache_getlines(self, filename, additional_arg=None): m = self.__LINECACHE_FILENAME_RE.match(filename) if m and m.group('name') == self.test.name: example = self.test.examples[int(m.group('examplenum'))] From fijal at codespeak.net Tue Jan 30 17:55:10 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 30 Jan 2007 17:55:10 +0100 (CET) Subject: [py-svn] r37621 - py/trunk/py/doc/future Message-ID: <20070130165510.B5413100A7@code0.codespeak.net> Author: fijal Date: Tue Jan 30 17:55:04 2007 New Revision: 37621 Modified: py/trunk/py/doc/future/planning.txt Log: all tests works for me Modified: py/trunk/py/doc/future/planning.txt ============================================================================== --- py/trunk/py/doc/future/planning.txt (original) +++ py/trunk/py/doc/future/planning.txt Tue Jan 30 17:55:04 2007 @@ -6,8 +6,6 @@ 2.3 - 2.4.2 work -2.5 has obscure problems - with setuptools: 2.3 - 2.4.2 as 'develop' regular installation: works mostly, strange test-failures From fijal at codespeak.net Tue Jan 30 17:57:00 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 30 Jan 2007 17:57:00 +0100 (CET) Subject: [py-svn] r37622 - py/trunk/py/doc/future Message-ID: <20070130165700.043F4100CA@code0.codespeak.net> Author: fijal Date: Tue Jan 30 17:56:58 2007 New Revision: 37622 Modified: py/trunk/py/doc/future/future.txt Log: Don't use previous versions in future examples Modified: py/trunk/py/doc/future/future.txt ============================================================================== --- py/trunk/py/doc/future/future.txt (original) +++ py/trunk/py/doc/future/future.txt Tue Jan 30 17:56:58 2007 @@ -188,8 +188,8 @@ might or should look like:: """ - the py lib version 0.8 - http://codespeak.net/py/0.8 + the py lib version 1.0 + http://codespeak.net/py/1.0 """ from py import pkg From fijal at codespeak.net Tue Jan 30 17:58:14 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 30 Jan 2007 17:58:14 +0100 (CET) Subject: [py-svn] r37623 - py/trunk/py/apigen/tracer Message-ID: <20070130165814.64BE2100C9@code0.codespeak.net> Author: fijal Date: Tue Jan 30 17:58:11 2007 New Revision: 37623 Modified: py/trunk/py/apigen/tracer/description.py Log: Get rid of last frame if possible (the frame containing start_tracing should not be there, unless this is the last frame) Modified: py/trunk/py/apigen/tracer/description.py ============================================================================== --- py/trunk/py/apigen/tracer/description.py (original) +++ py/trunk/py/apigen/tracer/description.py Tue Jan 30 17:58:11 2007 @@ -78,8 +78,11 @@ if upward_frame: if hasattr(upward_frame, 'raw'): upward_frame = upward_frame.raw - return CallStack([py.code.Frame(i) for i in stack[stack.index(frame):\ - stack.index(upward_frame)+1]]) + lst = [py.code.Frame(i) for i in stack[stack.index(frame):\ + stack.index(upward_frame)+1]] + if len(lst) > 1: + return CallStack(lst[:-1]) + return CallStack(lst) return CallStack([py.code.Frame(i) for i in stack[stack.index(frame):]]) ##class CallSite(object): From fijal at codespeak.net Tue Jan 30 17:59:37 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 30 Jan 2007 17:59:37 +0100 (CET) Subject: [py-svn] r37624 - py/trunk/py/test/rsession Message-ID: <20070130165937.35D8E100C9@code0.codespeak.net> Author: fijal Date: Tue Jan 30 17:59:32 2007 New Revision: 37624 Modified: py/trunk/py/test/rsession/executor.py py/trunk/py/test/rsession/local.py Log: Slightly hackish way to put all tracing calls as low as possible. This should kill unnecessary frames. Right now tracing is done only for py.test.Function objects, I don't know what to do with sth else, let's leave it alone. Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Tue Jan 30 17:59:32 2007 @@ -18,10 +18,13 @@ self.reporter = reporter self.config = config assert self.config + + def run(self): + self.item.run() def execute(self): try: - self.item.run() + self.run() outcome = Outcome() except py.test.Item.Skipped, e: outcome = Outcome(skipped=str(e)) @@ -49,8 +52,33 @@ outcome.stderr = "" return outcome +class ApigenExecutor(RunExecutor): + """ Same as RunExecutor, but takes tracer to trace calls as + an argument to execute + """ + def execute(self, tracer): + self.tracer = tracer + return super(ApigenExecutor, self).execute() + + def wrap_underlaying(self, target): + def f(*args): + try: + self.tracer.start_tracing() + return target(*args) + finally: + self.tracer.end_tracing() + return f + + def run(self): + """ We want to trace *only* function objects here. Unsure + what to do with custom collectors at all + """ + if hasattr(self.item, 'obj') and type(self.item.obj) is py.test.Function: + self.item.obj = self.wrap_underlaying(self.item.obj) + self.item.run() + class BoxExecutor(RunExecutor): - """ Same as run executor, but boxes test instead + """ Same as RunExecutor, but boxes test instead """ wraps = True Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Tue Jan 30 17:59:32 2007 @@ -2,7 +2,8 @@ """ local-only operations """ -from py.__.test.rsession.executor import BoxExecutor, RunExecutor +from py.__.test.rsession.executor import BoxExecutor, RunExecutor,\ + ApigenExecutor from py.__.test.rsession import report from py.__.test.rsession.outcome import ReprOutcome @@ -37,13 +38,10 @@ raise NotImplementedError() def apigen_runner(item, session, reporter): - r = RunExecutor(item, reporter=reporter, config=session.config) startcapture(session) #retval = plain_runner(item, session, reporter) - r = RunExecutor(item, reporter=reporter, config=session.config) - session.tracer.start_tracing() - outcome = r.execute() - session.tracer.end_tracing() + r = ApigenExecutor(item, reporter=reporter, config=session.config) + outcome = r.execute(session.tracer) outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) outcome.stdout, outcome.stderr = finishcapture(session) return outcome From fijal at codespeak.net Tue Jan 30 18:55:10 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 30 Jan 2007 18:55:10 +0100 (CET) Subject: [py-svn] r37631 - py/trunk/py/test Message-ID: <20070130175510.11EFE100CC@code0.codespeak.net> Author: fijal Date: Tue Jan 30 18:55:08 2007 New Revision: 37631 Modified: py/trunk/py/test/conftesthandle.py Log: Kill unnecessarry cache Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Tue Jan 30 18:55:08 2007 @@ -11,7 +11,6 @@ """ def __init__(self, path=None): self._path2confmods = {} - self._path2conftest_files = {} # direct cache of conftest files, to avoid confusion if path is not None: self.setinitial([path]) @@ -55,12 +54,14 @@ def getconftest(self, path): """ Return a direct module of that path """ + if isinstance(path, str): + path = py.path.local(path) try: - return self._path2conftest_files[path] + conftestmod = self.getconftestmodules(path)[-1] + if py.path.local(conftestmod.__file__).dirpath() != path: + raise AttributeError + return conftestmod except KeyError: - conftestpath = path.join("conftest.py") - if conftestpath.check(file=1): - return self.importconfig(conftestpath) raise AttributeError # we raise here AttributeError to unify error reporting in case # of lack of variable in conftest or lack of file, but we do not want to @@ -99,5 +100,4 @@ mod = configpath.pyimport(modname=modname) else: mod = configpath.pyimport() - self._path2conftest_files[configpath.dirpath()] = mod return mod From guido at codespeak.net Tue Jan 30 21:45:22 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 21:45:22 +0100 (CET) Subject: [py-svn] r37637 - in py/trunk/py/apigen: . testing Message-ID: <20070130204522.481AD100A9@code0.codespeak.net> Author: guido Date: Tue Jan 30 21:45:18 2007 New Revision: 37637 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/linker.py py/trunk/py/apigen/testing/test_apigen_functional.py Log: Fixed Windows path problems for apigen; test_apigen_functional.py now works (couldn't test with the py lib itself, though, as it wants to build greenlets and i don't have a suitable c compiler installed). Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Jan 30 21:45:18 2007 @@ -193,9 +193,9 @@ class AbstractPageBuilder(object): def write_page(self, title, reltargetpath, project, tag, nav): targetpath = self.base.join(reltargetpath) - stylesheeturl = relpath('%s/' % (targetpath.dirpath(),), + stylesheeturl = relpath('%s%s' % (targetpath.dirpath(), os.path.sep), self.base.join('style.css').strpath) - scripturls = [relpath('%s/' % (targetpath.dirpath(),), + scripturls = [relpath('%s%s' % (targetpath.dirpath(), os.path.sep), self.base.join('api.js').strpath)] page = wrap_page(project, title, tag, nav, self.base, stylesheeturl, scripturls) Modified: py/trunk/py/apigen/linker.py ============================================================================== --- py/trunk/py/apigen/linker.py (original) +++ py/trunk/py/apigen/linker.py Tue Jan 30 21:45:18 2007 @@ -42,7 +42,7 @@ finally: del self.fromlocation -def relpath(p1, p2, sep='/', back='..', normalize=True): +def relpath(p1, p2, sep=os.path.sep, back='..', normalize=True): """ create a relative path from p1 to p2 sep is the seperator used for input and (depending Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Tue Jan 30 21:45:18 2007 @@ -94,8 +94,8 @@ 'main.sub.func', 'somenamespace.baz', 'somenamespace.foo'] def test_apigen_functional(): - if py.std.sys.platform == "win32": - py.test.skip("XXX test fails on windows") + #if py.std.sys.platform == "win32": + # py.test.skip("XXX test fails on windows") fs_root, package_name = setup_fs_project('test_apigen_functional') tempdir = py.test.ensuretemp('test_apigen_functional_results') pydir = py.magic.autopath().dirpath().dirpath().dirpath() From guido at codespeak.net Tue Jan 30 21:52:00 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 21:52:00 +0100 (CET) Subject: [py-svn] r37638 - py/trunk/py/apigen/testing Message-ID: <20070130205200.18726100B0@code0.codespeak.net> Author: guido Date: Tue Jan 30 21:51:57 2007 New Revision: 37638 Modified: py/trunk/py/apigen/testing/test_apigen_example.py Log: Re-enabled the tests on win32 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 Tue Jan 30 21:51:57 2007 @@ -8,10 +8,6 @@ from py.__.test.web import webcheck from py.__.apigen.conftest import option -def setup_module(mod): - if py.std.sys.platform == "win32": - py.test.skip("apigen does not fully support win32 yet") - def run_string_sequence_test(data, seq): currpos = -1 for s in seq: From guido at codespeak.net Tue Jan 30 23:00:34 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 23:00:34 +0100 (CET) Subject: [py-svn] r37639 - py/trunk/py/doc Message-ID: <20070130220034.B43E710093@code0.codespeak.net> Author: guido Date: Tue Jan 30 23:00:33 2007 New Revision: 37639 Modified: py/trunk/py/doc/conftest.py Log: Not sure why this was here... :| Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Tue Jan 30 23:00:33 2007 @@ -98,10 +98,7 @@ linesep = '\r' else: linesep = '\r\n' - print 'linesep:', repr(linesep) s = s.replace(linesep, '\n') - self.fspath.write(s) - print 's:', repr(s) return s class LinkCheckerMaker(py.test.collect.Collector): From guido at codespeak.net Tue Jan 30 23:01:05 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 30 Jan 2007 23:01:05 +0100 (CET) Subject: [py-svn] r37640 - py/trunk/py/doc Message-ID: <20070130220105.8CF0D1009D@code0.codespeak.net> Author: guido Date: Tue Jan 30 23:01:04 2007 New Revision: 37640 Added: py/trunk/py/doc/path.txt Log: Added first bits of a document about py.path. Added: py/trunk/py/doc/path.txt ============================================================================== --- (empty file) +++ py/trunk/py/doc/path.txt Tue Jan 30 23:01:04 2007 @@ -0,0 +1,109 @@ +======= +py.path +======= + +.. contents:: +.. sectnum:: + +The 'py' lib provides an elegant, high-level api to deal with filesystems +and filesystem-like interfaces: :api:`py.path`. Here a simple but powerful +interface to deal with object trees (reading from and writing to nodes, adding +nodes and examining the structure, etc.) in a filesystem-oriented way is +defined, along with a number of readily available implementations. + +source: :source:`py/path/` + +Path implementations provided by :api:`py.path` +=============================================== + +:api:`py.path.local` +-------------------- + +The first and most obvious of the implementations is a wrapper around a local +filesystem. It's just a bit nicer in usage than the regular Python APIs, and +of course all the functionality is bundled together rather than spread over a +number of modules. + +Example usage, here we use the :api:`py.test.ensuretemp()` function to create +a :api:`py.path.local` object for us (which wraps a directory):: + + >>> import py + >>> temppath = py.test.ensuretemp('py.path_documentation') + >>> foopath = temppath.join('foo') # get child 'foo' (lazily) + >>> foopath.check() # check if child 'foo' exists + False + >>> foopath.write('bar') # write some data to it + >>> foopath.check() + True + >>> foopath.read() + 'bar' + +:api:`py.path.svnurl` and :api:`py.path.svnwc` +---------------------------------------------- + +Two other :api:`py.path` implementations that the py lib provides wrap the +popular `Subversion`_ revision control system: the first (called 'svnurl') +by interfacing with a remote server, the second by wrapping a local checkout. +Both allow you to access relatively advanced features such as metadata and +versioning, and both in a way more user-friendly manner than existing other +solutions. + +Some example usage of :api:`py.path.svnurl`:: + + >>> url = py.path.svnurl('http://codespeak.net/svn/py') + >>> info = url.info() + >>> info.kind + 'dir' + >>> firstentry = url.log()[-1] + >>> import time + >>> time.strftime('%Y-%m-%d', time.gmtime(firstentry.date)) + '2004-10-02' + +Example usage of :api:`py.path.svnwc`:: + + >>> 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') + >>> wc.join('local.py').check() + True + +.. _`Subversion`: http://subversion.tigris.org/ + +:api:`py.path.extpy`/gateway? +----------------------------- + +XXX do we want any of this? + +Common vs. specific API +======================= + +If required, backend-specific extensions are allowed, but the common API is +already quite rich and should be enough for most of the use cases. This +common set includes functions such as 'path.read()' to read data from a node, +'path.write()' to write data, 'path.listdir()' to get a list of a node's +children, 'path.check()' to examine if a node exists, 'path.join()' to get +to a (grand)child, 'path.visit()' to recursively walk through a node's +children, etc. Only things that are not common on filesystems, such as handling +metadata, will require extensions. + +Extending the path interface +---------------------------- + +XXX do we want to go here at all?!? :| + +Future plans +============ + +Not sure here. + +Known problems +============== + +There are certain known problems, mostly related to cleanups and consistency +issues: + + * XXX find known issues + +We hope to have these solved as good as possible in the 1.0 release of the +py lib. + From hpk at codespeak.net Tue Jan 30 23:22:17 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 30 Jan 2007 23:22:17 +0100 (CET) Subject: [py-svn] r37641 - in py/trunk/py: . doc misc/testing path path/extpy path/testing Message-ID: <20070130222217.5AADE1009D@code0.codespeak.net> Author: hpk Date: Tue Jan 30 23:22:15 2007 New Revision: 37641 Removed: py/trunk/py/path/extpy/ Modified: py/trunk/py/__init__.py py/trunk/py/doc/TODO.txt py/trunk/py/doc/path.txt py/trunk/py/misc/testing/test_api.py py/trunk/py/path/common.py py/trunk/py/path/testing/common.py py/trunk/py/path/testing/test_api.py Log: removing py.path.extpy after all Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Tue Jan 30 23:22:15 2007 @@ -61,7 +61,6 @@ 'path.svnwc' : ('./path/svn/wccommand.py', 'SvnWCCommandPath'), 'path.svnurl' : ('./path/svn/urlcommand.py', 'SvnCommandPath'), 'path.local' : ('./path/local/local.py', 'LocalPath'), - 'path.extpy' : ('./path/extpy/extpy.py', 'Extpy'), # some nice slightly magic APIs 'magic.greenlet' : ('./magic/greenlet.py', 'greenlet'), Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Tue Jan 30 23:22:15 2007 @@ -32,9 +32,9 @@ * remove from public namespace: XXX consider py.magic. invoke/revoke/patch/revert + (DONE) remove py.path.extpy * make "_" namespace: - py.path.extpy -> py.path._extpy py.log -> py._log (pypy!) * review py.io and write py.io.dupfile docstring @@ -97,7 +97,7 @@ (guido tested all on win32, everything works except --dist (requires os.fork to work)) -* see why startcapture() does not use FD-based +* see why startcapture() used to not use FD-based "py.io.OutErrCapture" to isolate standard output. use that check if all py and PyPy tests pass as good as they do without. @@ -118,7 +118,6 @@ ------------------------------------ * (DONE, except apigen) cleanup initialisation of config / get rid of pkgdir -* unify option names (dist_*) * (optional) see if more of py/test/session.py's Session can be reused * (DONE, but slightly different way) have dist_rsyncroots be relative to the conftest.py file @@ -291,25 +290,6 @@ path.local.__cmp__ misses a docstring path.local.__add__ misses a docstring path.local.Checkers misses a docstring - path.extpy.visit misses a docstring - path.extpy.samefile misses a docstring - path.extpy.relto misses a docstring - path.extpy.listobj misses a docstring - path.extpy.listdir misses a docstring - path.extpy.join misses a docstring - path.extpy.getpymodule misses a docstring - path.extpy.getfilelineno misses a docstring - path.extpy.dirpath misses a docstring - path.extpy.check misses a docstring - path.extpy.__str__ misses a docstring - path.extpy.__repr__ misses a docstring - path.extpy.__new__ misses a docstring - path.extpy.__iter__ misses a docstring - path.extpy.__hash__ misses a docstring - path.extpy.__contains__ misses a docstring - path.extpy.__cmp__ misses a docstring - path.extpy.__add__ misses a docstring - path.extpy.Checkers misses a docstring test.rest.RestReporter misses a docstring test.rest.RestReporter.summary misses a docstring test.rest.RestReporter.skips misses a docstring Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Tue Jan 30 23:22:15 2007 @@ -69,10 +69,6 @@ .. _`Subversion`: http://subversion.tigris.org/ -:api:`py.path.extpy`/gateway? ------------------------------ - -XXX do we want any of this? Common vs. specific API ======================= Modified: py/trunk/py/misc/testing/test_api.py ============================================================================== --- py/trunk/py/misc/testing/test_api.py (original) +++ py/trunk/py/misc/testing/test_api.py Tue Jan 30 23:22:15 2007 @@ -10,7 +10,6 @@ assert_class('py.path', 'local') assert_class('py.path', 'svnwc') assert_class('py.path', 'svnurl') - assert_class('py.path', 'extpy') def test_magic_entrypoints(self): assert_function('py.magic', 'invoke') Modified: py/trunk/py/path/common.py ============================================================================== --- py/trunk/py/path/common.py (original) +++ py/trunk/py/path/common.py Tue Jan 30 23:22:15 2007 @@ -7,7 +7,7 @@ import py def checktype(pathinstance, kw): - names = ('local', 'svnwc', 'svnurl', 'py', 'extpy') + names = ('local', 'svnwc', 'svnurl', 'py', ) for name,value in kw.items(): if name in names: cls = getattr(py.path, name) Modified: py/trunk/py/path/testing/common.py ============================================================================== --- py/trunk/py/path/testing/common.py (original) +++ py/trunk/py/path/testing/common.py Tue Jan 30 23:22:15 2007 @@ -54,10 +54,6 @@ x = other.common(self.root) assert x == self.root - third = py.path.extpy('x', 'whatever') - x = other.common(third) - assert x is None - #def test_parents_nonexisting_file(self): # newpath = self.root / 'dirnoexist' / 'nonexisting file' Modified: py/trunk/py/path/testing/test_api.py ============================================================================== --- py/trunk/py/path/testing/test_api.py (original) +++ py/trunk/py/path/testing/test_api.py Tue Jan 30 23:22:15 2007 @@ -9,7 +9,7 @@ def repr_eval_test(self, p): r = repr(p) - from py.path import local,svnurl, svnwc, extpy + from py.path import local,svnurl, svnwc y = eval(r) assert y == p @@ -21,7 +21,6 @@ assert p.check() assert p.check(local=1) assert p.check(svnwc=0) - assert p.check(extpy=0) assert not p.check(svnwc=1) self.repr_eval_test(p) From arigo at codespeak.net Wed Jan 31 00:07:50 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 31 Jan 2007 00:07:50 +0100 (CET) Subject: [py-svn] r37642 - py/trunk/py/c-extension/greenlet Message-ID: <20070130230750.3AF98100A3@code0.codespeak.net> Author: arigo Date: Wed Jan 31 00:07:48 2007 New Revision: 37642 Modified: py/trunk/py/c-extension/greenlet/greenlet.c py/trunk/py/c-extension/greenlet/test_greenlet.py Log: issue40 resolved Thanks ghazel. I fixed this by directly capturing ts_current in a local variable instead of reloading it. I also reviewed the code a bit and found another place where ts_current could be used out of date. Modified: py/trunk/py/c-extension/greenlet/greenlet.c ============================================================================== --- py/trunk/py/c-extension/greenlet/greenlet.c (original) +++ py/trunk/py/c-extension/greenlet/greenlet.c Wed Jan 31 00:07:48 2007 @@ -394,7 +394,8 @@ /* in the new greenlet */ PyObject* args; PyObject* result; - ts_current->stack_start = (char*) 1; /* running */ + PyGreenlet* ts_self = ts_current; + ts_self->stack_start = (char*) 1; /* running */ args = ts_passaround; if (args == NULL) /* pending exception */ @@ -407,8 +408,8 @@ Py_DECREF(run); result = g_handle_exit(result); /* jump back to parent */ - ts_current->stack_start = NULL; /* dead */ - g_switch(ts_current->parent, result); + ts_self->stack_start = NULL; /* dead */ + g_switch(ts_self->parent, result); /* must not return from here! */ Py_FatalError("XXX memory exhausted at a very bad moment"); } @@ -463,6 +464,8 @@ because the 'parent' field chain would hold a reference */ PyObject* result; + if (!STATE_OK) + return -1; Py_INCREF(ts_current); self->parent = ts_current; /* Send the greenlet a GreenletExit exception. */ Modified: py/trunk/py/c-extension/greenlet/test_greenlet.py ============================================================================== --- py/trunk/py/c-extension/greenlet/test_greenlet.py (original) +++ py/trunk/py/c-extension/greenlet/test_greenlet.py Wed Jan 31 00:07:48 2007 @@ -142,3 +142,17 @@ assert not g assert next == "meaning of life" assert g.gr_frame is None + +def test_thread_bug(): + if not thread: + py.test.skip("this is a test about thread") + import time + def runner(x): + g = greenlet(lambda: time.sleep(x)) + g.switch() + t1 = threading.Thread(target=runner, args=(0.2,)) + t2 = threading.Thread(target=runner, args=(0.3,)) + t1.start() + t2.start() + t1.join() + t2.join() From hpk at codespeak.net Wed Jan 31 00:13:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 00:13:50 +0100 (CET) Subject: [py-svn] r37643 - py/trunk/py/doc Message-ID: <20070130231350.6FCA1100A3@code0.codespeak.net> Author: hpk Date: Wed Jan 31 00:13:49 2007 New Revision: 37643 Modified: py/trunk/py/doc/execnet.txt Log: stroke the historical notes, and streamlined various parts of execnet documentation, added some api references. Modified: py/trunk/py/doc/execnet.txt ============================================================================== --- py/trunk/py/doc/execnet.txt (original) +++ py/trunk/py/doc/execnet.txt Wed Jan 31 00:13:49 2007 @@ -4,7 +4,7 @@ .. contents:: .. sectnum:: -Execnet deals with letting your python programs execute and +``py.execnet`` deals with letting your python programs execute and communicate across process and computer barriers. At the core it is a very simple and powerful mechanism: executing source code at "the other side" and communicating with @@ -15,35 +15,18 @@ there yet. You may refer to the `py API`_ reference for further information. -The "shpy" way, historical remarks ----------------------------------- - -Some of you may have seen the pygame-based editor **shpy** -that Armin Rigo, Holger Krekel and Michael Hudson demonstrated -at EuroPython 2004 during the lightning talks. It is a -multiline interactive python environment which allows multiple -remote users to have their own cursors and shared interaction -with the graphical shell which can execute python code, of -course. - -**py.execnet** extracts and refines the basic mechanism that -was originally developed from the one-week hack that is the -current **shpy**. No, the latter is not released but hopefully -Armin, Holger and others get to do a second one-week sprint at -some point to be able to release it. If you are interested -drop them a note. This is real fun if you are willing to -bend your mind a bit. A new view on distributed execution ----------------------------------- -**py.execnet** takes the view of **asynchronously executing -client-provided code fragments** to help address a core -problem of distributed programs. A core feature of -**py.execnet** is that **the communication protocols can be -defined by the client side**. Usually, with server/client -apps and especially RMI systems you often have to upgrade your -server if you upgrade your client. +**py.execnet** lets you asynchronously execute source code on +remote places. The sending and receiving side communicate via +Channels that transport marshallable objects. A core feature +of **py.execnet** is that **the communication protocols can be +completely defined by the client side**. Usually, with +server/client apps and especially Remote Method Based (RMI) +approaches you have to define interfaces and have to +upgrade your server and client and restart both. What about Security? Are you completely nuts? --------------------------------------------- @@ -68,24 +51,29 @@ High Level Interface: **remote_exec** ------------------------------------- -These gateways offer one main high level interface:: +All gateways offer one main high level interface, +e.g. def remote_exec(source): """return channel object for communicating with the asynchronously executing 'source' code which will have a corresponding 'channel' object in its executing namespace.""" - -The most important property of execnet gateways is that the -protocol they speak with each other ``is determined by the -client side``. If you open a gateway on some server in order -to coordinate with some other programs you determine your -communication protocol. Multiple clients can run their own -gateway versions in the same remote process (e.g. connecting -through their version of a SocketGateway). + +With `remote_exec` you send source code to the other +side and get both a local and a remote Channel_ object, +which you can use to have the local and remote site +communicate data in a structured way. + +This approach implements the idea to ``determining +protocol and remote code from the client/local side``. +This makes distributing a program run in an ad-hoc +manner (using e.g. :api:`py.path.SshGateway`) very easy. You should not need to maintain software on the other sides -you are running your code at. +you are running your code at, other than the Python +executable itself. +.. _`Channel`: .. _`channel-api`: .. _`exchange data`: @@ -128,7 +116,8 @@ A remote side blocking on receive() on this channel will get woken up and see an EOFError exception. -A simple and useful Example for Channels + +The complete Fileserver example ........................................ problem: retrieving contents of remote files:: @@ -155,6 +144,7 @@ # later you can exit / close down the gateway contentgateway.exit() + A more complicated "nested" Gateway Example ........................................... From cfbolz at codespeak.net Wed Jan 31 00:20:51 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Wed, 31 Jan 2007 00:20:51 +0100 (CET) Subject: [py-svn] r37644 - py/trunk/py/doc Message-ID: <20070130232051.1B424100B7@code0.codespeak.net> Author: cfbolz Date: Wed Jan 31 00:20:50 2007 New Revision: 37644 Modified: py/trunk/py/doc/execnet.txt Log: typo Modified: py/trunk/py/doc/execnet.txt ============================================================================== --- py/trunk/py/doc/execnet.txt (original) +++ py/trunk/py/doc/execnet.txt Wed Jan 31 00:20:50 2007 @@ -67,7 +67,7 @@ This approach implements the idea to ``determining protocol and remote code from the client/local side``. This makes distributing a program run in an ad-hoc -manner (using e.g. :api:`py.path.SshGateway`) very easy. +manner (using e.g. :api:`py.execnet.SshGateway`) very easy. You should not need to maintain software on the other sides you are running your code at, other than the Python From hpk at codespeak.net Wed Jan 31 00:24:00 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 00:24:00 +0100 (CET) Subject: [py-svn] r37645 - py/trunk/py/doc Message-ID: <20070130232400.7EF06100BF@code0.codespeak.net> Author: hpk Date: Wed Jan 31 00:23:59 2007 New Revision: 37645 Modified: py/trunk/py/doc/execnet.txt Log: adding an example for execnet (passes doctest) Modified: py/trunk/py/doc/execnet.txt ============================================================================== --- py/trunk/py/doc/execnet.txt (original) +++ py/trunk/py/doc/execnet.txt Wed Jan 31 00:23:59 2007 @@ -48,11 +48,30 @@ doesn't run in a kernel-level jail [#]_ in which case even that is virtualized. -High Level Interface: **remote_exec** + +Available Gateways/Connection methods +----------------------------------------- + +You may use one of the following connection methods: + +* :api:`py.execnet.PopenGateway` a subprocess on the local + machine. Useful for jailing certain parts of a program + or for making use of multiple processors. + +* :api:`py.execnet.SshGateway` a way to connect to + a remote ssh server and distribute execution to it. + +* :api:`py.execnet.SocketGateway` a way to connect to + a remote Socket based server. *Note* that this method + requires a manually started + :source:py/execnet/script/socketserver.py + script. You can run this "server script" without + having the py lib installed on that remote system. + +Remote execution approach ------------------------------------- -All gateways offer one main high level interface, -e.g. +All gateways offer one main high level function: def remote_exec(source): """return channel object for communicating with the asynchronously @@ -62,9 +81,17 @@ With `remote_exec` you send source code to the other side and get both a local and a remote Channel_ object, which you can use to have the local and remote site -communicate data in a structured way. +communicate data in a structured way. Here is +an example: + + >>> import py + >>> gw = py.execnet.PopenGateway(python="python2.3") + >>> channel = gw.remote_exec("import sys ; channel.send(sys.version_info)") + >>> channel.receive()[:2] + (2, 3) + >>> gw.exit() -This approach implements the idea to ``determining +`remote_exec` implements the idea to ``determine protocol and remote code from the client/local side``. This makes distributing a program run in an ad-hoc manner (using e.g. :api:`py.execnet.SshGateway`) very easy. From arigo at codespeak.net Wed Jan 31 00:29:46 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 31 Jan 2007 00:29:46 +0100 (CET) Subject: [py-svn] r37646 - py/trunk/py/doc Message-ID: <20070130232946.0D0F1100B7@code0.codespeak.net> Author: arigo Date: Wed Jan 31 00:29:45 2007 New Revision: 37646 Modified: py/trunk/py/doc/greenlet.txt Log: Wording, and missing attribute in the greenlet documentation. Modified: py/trunk/py/doc/greenlet.txt ============================================================================== --- py/trunk/py/doc/greenlet.txt (original) +++ py/trunk/py/doc/greenlet.txt Wed Jan 31 00:29:45 2007 @@ -229,7 +229,7 @@ sent to its parent. If ``run()`` terminates with an exception, the exception is propagated to its parent (unless it is a ``greenlet.GreenletExit`` exception, in which case the exception - object itself is sent to the parent). + object is caught and *returned* to the parent). Apart from the cases described above, the target greenlet normally receives the object as the return value of the call to ``switch()`` in @@ -263,6 +263,9 @@ ``g.gr_frame`` The current top frame, or None. +``g.dead`` + True if ``g`` is dead (i.e. it finished its execution). + ``bool(g)`` True if ``g`` is active, False if it is dead or not yet started. From hpk at codespeak.net Wed Jan 31 00:46:21 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 00:46:21 +0100 (CET) Subject: [py-svn] r37647 - py/trunk/py/doc Message-ID: <20070130234621.B5D30100BE@code0.codespeak.net> Author: hpk Date: Wed Jan 31 00:46:20 2007 New Revision: 37647 Modified: py/trunk/py/doc/execnet.txt Log: make the example independent from the installed python binaries Modified: py/trunk/py/doc/execnet.txt ============================================================================== --- py/trunk/py/doc/execnet.txt (original) +++ py/trunk/py/doc/execnet.txt Wed Jan 31 00:46:20 2007 @@ -85,11 +85,14 @@ an example: >>> import py - >>> gw = py.execnet.PopenGateway(python="python2.3") - >>> channel = gw.remote_exec("import sys ; channel.send(sys.version_info)") - >>> channel.receive()[:2] - (2, 3) - >>> gw.exit() + >>> gw = py.execnet.PopenGateway() + >>> channel = gw.remote_exec(""" + ... import os + ... channel.send(os.getpid()) + ... """) + >>> remote_pid = channel.receive() + >>> remote_pid != py.std.os.getpid() + True `remote_exec` implements the idea to ``determine protocol and remote code from the client/local side``. From hpk at codespeak.net Wed Jan 31 12:27:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 12:27:27 +0100 (CET) Subject: [py-svn] r37661 - py/trunk/py/bin Message-ID: <20070131112727.0ABBE100AD@code0.codespeak.net> Author: hpk Date: Wed Jan 31 12:27:25 2007 New Revision: 37661 Modified: py/trunk/py/bin/py.cleanup Log: fixing this script (IMO it was broken if you provided arguments) Modified: py/trunk/py/bin/py.cleanup ============================================================================== --- py/trunk/py/bin/py.cleanup (original) +++ py/trunk/py/bin/py.cleanup Wed Jan 31 12:27:25 2007 @@ -9,17 +9,16 @@ """ from _findpy import py import py -from py.compat import optparse -parser = optparse.OptionParser(usage=__doc__) +parser = py.compat.optparse.OptionParser(usage=__doc__) if __name__ == '__main__': (options, args) = parser.parse_args() - if len(args) >= 1: - path = py.path.local(args) - else: - path = py.path.local() - print "cleaning path", path - for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)): - x.remove() + 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() From hpk at codespeak.net Wed Jan 31 12:49:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 12:49:26 +0100 (CET) Subject: [py-svn] r37664 - py/trunk/py/doc Message-ID: <20070131114926.62B281008D@code0.codespeak.net> Author: hpk Date: Wed Jan 31 12:49:25 2007 New Revision: 37664 Modified: py/trunk/py/doc/path.txt Log: some streamlining, additions and "de-marketing" :) and adding some XXX Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Wed Jan 31 12:49:25 2007 @@ -5,13 +5,10 @@ .. contents:: .. sectnum:: -The 'py' lib provides an elegant, high-level api to deal with filesystems -and filesystem-like interfaces: :api:`py.path`. Here a simple but powerful -interface to deal with object trees (reading from and writing to nodes, adding -nodes and examining the structure, etc.) in a filesystem-oriented way is -defined, along with a number of readily available implementations. - -source: :source:`py/path/` +The 'py' lib provides a uniform high-level api to deal with filesystems +and filesystem-like interfaces: :api:`py.path`. It aims to offer a central +object to fs-like object trees (reading from and writing to files, adding +files/directories, examining the types and structure, etc.). Path implementations provided by :api:`py.path` =============================================== @@ -69,37 +66,84 @@ .. _`Subversion`: http://subversion.tigris.org/ - Common vs. specific API ======================= -If required, backend-specific extensions are allowed, but the common API is -already quite rich and should be enough for most of the use cases. This -common set includes functions such as 'path.read()' to read data from a node, -'path.write()' to write data, 'path.listdir()' to get a list of a node's -children, 'path.check()' to examine if a node exists, 'path.join()' to get -to a (grand)child, 'path.visit()' to recursively walk through a node's -children, etc. Only things that are not common on filesystems, such as handling -metadata, will require extensions. +All Path objects support a common set of operations, suitable +for many use cases and allowing to transparently switch the +path object within an application (e.g. from "local" to "svnwc"). +The common set includes functions such as `path.read()` to read all data +from a file, `path.write()` to write data, `path.listdir()` to get a list +of directory entries, and `path.check()` to check if a node exists +and is of a particular type, `path.join()` to get +to a (grand)child, `path.visit()` to recursively walk through a node's +children, etc. Only things that are not common on all filesystems, such +as handling metadata (e.g. the subversion "properties") require +using specific APIs. -Extending the path interface ----------------------------- +Examples +--------------------------------- -XXX do we want to go here at all?!? :| +Searching `.txt` files ++++++++++++++++++++++++++++++++++++++ -Future plans -============ +XXX example + + +Joining and checking path types ++++++++++++++++++++++++++++++++++++++ + +XXX example + +setting svn-properties ++++++++++++++++++++++++++++++++++++++++ -Not sure here. +XXX example -Known problems -============== -There are certain known problems, mostly related to cleanups and consistency -issues: +XXX more examples (look at API) ++++++++++++++++++++++++++++++++++++++++ + +XXX + +Known problems / limitations +=================================== + +There are some known issues, most importantly +that using the Subversion Paths requires the +command line `svn` binary and parsing its output +is a bit fragile across versions and locales +(it basically only works with an english locale!). + +XXX note more here + + +Future plans +============ - * XXX find known issues +The Subversion path implementations are based +on the `svn` command line, not on the bindings. +It makes sense now to directly use the bindings. + +Moreover, it would be good, also considering +`py.execnet`_ distribution of programs, to +be able to manipulate Windows Paths on Linux +and vice versa. So we'd like to consider +refactoring the path implementations +to provide this choice (and getting rid +of platform-dependencies as much as possible). + +There are various hacks out there to have +Memory-Filesystems and even path objects +being directly mountable under Linux (via `fuse`). +However, the Path object implementations +do not internally have a clean abstraction +of going to the filesystem - so with some +refactoring it should become easier to +have very custom Path objects, still offering +the quite full interface without requiring +to know about all details of the full path +implementation. -We hope to have these solved as good as possible in the 1.0 release of the -py lib. +.. _`py.execnet`: execnet.html From guido at codespeak.net Wed Jan 31 16:29:21 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 31 Jan 2007 16:29:21 +0100 (CET) Subject: [py-svn] r37674 - in py/trunk/py: code doc Message-ID: <20070131152921.9575B10087@code0.codespeak.net> Author: guido Date: Wed Jan 31 16:29:18 2007 New Revision: 37674 Added: py/trunk/py/doc/code.txt Modified: py/trunk/py/code/code.py py/trunk/py/code/excinfo.py py/trunk/py/code/frame.py py/trunk/py/code/traceback2.py Log: Added document 'code.txt' that describes py.code, added docstrings to py.code public items. Modified: py/trunk/py/code/code.py ============================================================================== --- py/trunk/py/code/code.py (original) +++ py/trunk/py/code/code.py Wed Jan 31 16:29:18 2007 @@ -1,6 +1,7 @@ import py class Code(object): + """ wrapper around Python code objects """ def __init__(self, rawcode): rawcode = getattr(rawcode, 'im_func', rawcode) rawcode = getattr(rawcode, 'func_code', rawcode) @@ -57,6 +58,7 @@ ) def path(self): + """ return a py.path.local object wrapping the source of the code """ try: return self.raw.co_filename.__path__ except AttributeError: @@ -64,6 +66,8 @@ path = property(path, None, None, "path of this code object") def fullsource(self): + """ return a py.code.Source object for the full source file of the code + """ fn = self.raw.co_filename try: return fn.__source__ @@ -73,11 +77,16 @@ "full source containing this code object") def source(self): + """ return a py.code.Source object for the code object's source only + """ # return source only for that part of code import inspect return py.code.Source(inspect.getsource(self.raw)) def getargs(self): + """ return a tuple with the argument names for the code object + """ # handfull shortcut for getting args raw = self.raw return raw.co_varnames[:raw.co_argcount] + Modified: py/trunk/py/code/excinfo.py ============================================================================== --- py/trunk/py/code/excinfo.py (original) +++ py/trunk/py/code/excinfo.py Wed Jan 31 16:29:18 2007 @@ -22,6 +22,13 @@ self.traceback = py.code.Traceback(tb) def exconly(self, tryshort=False): + """ return the exception as a string + + when 'tryshort' resolves to True, and the exception is a + py.magic.AssertionError, only the actual exception part of + the exception representation is returned (so 'AssertionError: ' is + removed from the beginning) + """ lines = py.std.traceback.format_exception_only(self.type, self.value) text = ''.join(lines) if text.endswith('\n'): @@ -32,6 +39,7 @@ return text def errisinstance(self, exc): + """ return True if the exception is an instance of exc """ return isinstance(self.value, exc) def __str__(self): Modified: py/trunk/py/code/frame.py ============================================================================== --- py/trunk/py/code/frame.py (original) +++ py/trunk/py/code/frame.py Wed Jan 31 16:29:18 2007 @@ -18,23 +18,38 @@ "statement this frame is at") def eval(self, code, **vars): + """ evaluate 'code' in the frame + + 'vars' are optional additional local variables + + returns the result of the evaluation + """ f_locals = self.f_locals.copy() f_locals.update(vars) return eval(code, self.f_globals, f_locals) def exec_(self, code, **vars): + """ exec 'code' in the frame + + 'vars' are optiona; additional local variables + """ f_locals = self.f_locals.copy() f_locals.update(vars) exec code in self.f_globals, f_locals def repr(self, object): + """ return a 'safe' (non-recursive, one-line) string repr for 'object' + """ return py.__.code.safe_repr._repr(object) def is_true(self, object): return object def getargs(self): + """ return a list of tuples (name, value) for all arguments + """ retval = [] for arg in self.code.getargs(): retval.append((arg, self.f_locals[arg])) return retval + Modified: py/trunk/py/code/traceback2.py ============================================================================== --- py/trunk/py/code/traceback2.py (original) +++ py/trunk/py/code/traceback2.py Wed Jan 31 16:29:18 2007 @@ -2,6 +2,8 @@ import py class TracebackEntry(object): + """ a single entry in a traceback """ + exprinfo = None def __init__(self, rawentry): @@ -14,6 +16,7 @@ return "" %(self.frame.code.path, self.lineno+1) def statement(self): + """ return a py.code.Source object for the current statement """ source = self.frame.code.fullsource return source.getstatement(self.lineno) statement = property(statement, None, None, @@ -63,6 +66,11 @@ source = property(getsource) def ishidden(self): + """ return True if the current frame has a var __tracebackhide__ + resolving to True + + mostly for internal use + """ try: return self.frame.eval("__tracebackhide__") except (SystemExit, KeyboardInterrupt): @@ -103,6 +111,15 @@ list.__init__(self, tb) def cut(self, path=None, lineno=None, firstlineno=None): + """ return a Traceback instance wrapping part of this Traceback + + by provding any combination of path, lineno and firstlineno, the + first frame to start the to-be-returned traceback is determined + + this allows cutting the first part of a Traceback instance e.g. + for formatting reasons (removing some uninteresting bits that deal + with handling of the exception/traceback) + """ for x in self: if ((path is None or x.frame.code.path == path) and (lineno is None or x.lineno == lineno) and @@ -114,9 +131,18 @@ val = super(Traceback, self).__getitem__(key) if isinstance(key, type(slice(0))): val = self.__class__(val) - return val + return val def filter(self, fn=lambda x: not x.ishidden()): + """ return a Traceback instance with certain items removed + + fn is a function that gets a single argument, a TracebackItem + instance, and should return True when the item should be added + to the Traceback, False when not + + by default this removes all the TracebackItems which are hidden + (see ishidden() above) + """ return Traceback(filter(fn, self)) def getcrashentry(self): @@ -129,6 +155,9 @@ return tb[-1] def recursionindex(self): + """ return the index of the frame/TracebackItem where recursion + originates if appropriate, None if no recursion occurred + """ cache = {} for i, entry in py.builtin.enumerate(self): key = entry.frame.code.path, entry.lineno @@ -155,3 +184,4 @@ co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', '?', 'eval') + Added: py/trunk/py/doc/code.txt ============================================================================== --- (empty file) +++ py/trunk/py/doc/code.txt Wed Jan 31 16:29:18 2007 @@ -0,0 +1,141 @@ +============== +:api:`py.code` +============== + +The :api:`py.code` part of the 'py lib' contains some functionality to help +dealing with Python code objects. Even though working with Python's internal +code objects (as found on frames and callables) can be very powerful, it's +usually also quite cumbersome, because the API provided by core Python is +relatively low level and not very accessible. + +The :api:`py.code` library tries to simplify accessing the code objects as well +as creating them. There is a small set of interfaces a user needs to deal with, +all nicely bundled together, and with a rich set of 'Pythonic' functionality. + +source: :source:`py/code/` + +Contents of the library +======================= + +Every object in the :api:`py.code` library wraps a code Python object related +to code objects, source code, frames and tracebacks: the :api:`py.code.Code` +class wraps code objects, :api:`py.code.Source` source snippets, +:api:`py.code.Traceback` exception tracebacks, :api:`py.code.Frame` frame +objects (as found in e.g. tracebacks) and :api:`py.code.ExceptionInfo` the +tuple provided by sys.exc_info() (containing exception and traceback +information when an exception occurs). Also in the library is a helper function +:api:`py.code.compile()` that provides the same functionality as Python's +built-in 'compile()' function, but returns a wrapped code object. + +The wrappers +============ + +:api:`py.code.Code` +------------------- + +Code objects are instantiated with a code object or a callable as argument, +and provide functionality to compare themselves with other Code objects, get to +the source file or its contents, create new Code objects from scratch, etc. + +A quick example:: + + >>> import py + >>> c = py.code.Code(py.path.local.read) + >>> c.path.basename + 'common.py' + >>> isinstance(c.source(), py.code.Source) + True + >>> str(c.source()).split('\n')[0] + "def read(self, mode='rb'):" + +source: :source:`py/code/code.py` + +:api:`py.code.Source` +--------------------- + +Source objects wrap snippets of Python source code, providing a simple yet +powerful interface to read, deindent, slice, compare, compile and manipulate +them, things that are not so easy in core Python. + +Example:: + + >>> s = py.code.Source("""\ + ... def foo(): + ... print "foo" + ... """) + >>> str(s).startswith('def') # automatic de-indentation! + True + >>> s.isparseable() + True + >>> sub = s.getstatement(1) # get the statement starting at line 1 + >>> str(sub).strip() # XXX why is the strip() required?!? + 'print "foo"' + +source: :source:`py/code/source.py` + +:api:`py.code.Traceback` +------------------------ + +Tracebacks are usually not very easy to examine, you need to access certain +somewhat hidden attributes of the traceback's items (resulting in expressions +such as 'fname = tb.tb_next.tb_frame.f_code.co_filename'). The Traceback +interface (and its TracebackItem children) tries to improve this. + +Example:: + + >>> import sys + >>> try: + ... py.path.local(100) # illegal argument + ... except: + ... exc, e, tb = sys.exc_info() + >>> t = py.code.Traceback(tb) + >>> first = t[1] # get the second entry (first is in this doc) + >>> first.path.basename # second is in py/path/local.py + 'local.py' + >>> isinstance(first.statement, py.code.Source) + True + >>> str(first.statement).strip().startswith('raise ValueError') + True + +source: :source:`py/code/traceback2.py` + +:api:`py.code.Frame` +-------------------- + +Frame wrappers are used in :api:`py.code.Traceback` items, and will usually not +directly be instantiated. They provide some nice methods to evaluate code +'inside' the frame (using the frame's local variables), get to the underlying +code (frames have a code attribute that points to a :api:`py.code.Code` object) +and examine the arguments. + +Example (using the 'first' TracebackItem instance created above):: + + >>> frame = first.frame + >>> isinstance(frame.code, py.code.Code) + True + >>> isinstance(frame.eval('self'), py.__.path.local.local.LocalPath) + True + >>> [namevalue[0] for namevalue in frame.getargs()] + ['cls', 'path'] + +:api:`py.code.ExceptionInfo` +---------------------------- + +A wrapper around the tuple returned by sys.exc_info() (will call sys.exc_info() +itself if the tuple is not provided as an argument), provides some handy +attributes to easily access the traceback and exception string. + +Example:: + + >>> import sys + >>> try: + ... foobar() + ... except: + ... excinfo = py.code.ExceptionInfo() + >>> excinfo.typename + 'exceptions.NameError' + >>> isinstance(excinfo.traceback, py.code.Traceback) + True + >>> excinfo.exconly() + "NameError: name 'foobar' is not defined" + From hpk at codespeak.net Wed Jan 31 21:34:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 21:34:31 +0100 (CET) Subject: [py-svn] r37693 - py/trunk/py/path/gateway Message-ID: <20070131203431.8650210082@code0.codespeak.net> Author: hpk Date: Wed Jan 31 21:34:29 2007 New Revision: 37693 Added: py/trunk/py/path/gateway/TODO.txt (contents, props changed) py/trunk/py/path/gateway/__init__.py (contents, props changed) Log: Some considerations regarding the experimental Gateway/Remote Path implementation code. Also making it "py.__." importable but not advertising it for 0.9 at all. It's an interesting feature for 1.0 :) Added: py/trunk/py/path/gateway/TODO.txt ============================================================================== --- (empty file) +++ py/trunk/py/path/gateway/TODO.txt Wed Jan 31 21:34:29 2007 @@ -0,0 +1,24 @@ +I think the nice code in this directory +should be refactored so that you can use +it like this: + + rp = gateway.get_remote_path(relpath) + +and relpath could be absolute, relative (should +follow remote-platform syntax) or None/"." (the +current dir on the other side). + +The tricky part probably is defining sensible +setup/teardown semantics with respect to +starting the "Path" server on the other side, +we at least don't want to have multiple threads +that serve path requests and maybe we want +to be able to explicitely shutdown a once +started RemotePath server (not sure though). + +For a single-threaded py.execnet it might be helpful to be +able to install new network messages (which are lower level +than remote_exec() and work with callbacks, so don't follow +the nice "synchronous" programming model that you get with +threads/greenlets/tasklets). + Added: py/trunk/py/path/gateway/__init__.py ============================================================================== --- (empty file) +++ py/trunk/py/path/gateway/__init__.py Wed Jan 31 21:34:29 2007 @@ -0,0 +1 @@ +# From hpk at codespeak.net Wed Jan 31 22:55:32 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 22:55:32 +0100 (CET) Subject: [py-svn] r37696 - in py/trunk/py: . misc/testing test test/testing Message-ID: <20070131215532.D6F3710079@code0.codespeak.net> Author: hpk Date: Wed Jan 31 22:55:30 2007 New Revision: 37696 Removed: py/trunk/py/test/compat.py py/trunk/py/test/testing/test_compat.py Modified: py/trunk/py/__init__.py py/trunk/py/misc/testing/test_initpkg.py Log: removing py.test.compat.TestCase - it's small code but nobody uses it i think and there is no documentation. Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Wed Jan 31 22:55:30 2007 @@ -29,7 +29,6 @@ 'test.skip' : ('./test/item.py', 'skip'), 'test.fail' : ('./test/item.py', 'fail'), 'test.exit' : ('./test/session.py', 'exit'), - 'test.compat.TestCase' : ('./test/compat.py', 'TestCase'), # configuration/initialization related test api 'test.config' : ('./test/config.py', 'config_per_process'), 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 Wed Jan 31 22:55:30 2007 @@ -49,12 +49,10 @@ def test_importall(): base = py.path.local(py.__file__).dirpath() nodirs = ( - base.join('test', 'tkinter'), base.join('test', 'testing', 'data'), base.join('apigen', 'tracer', 'testing', 'package'), base.join('test', 'testing', 'test'), base.join('magic', 'greenlet.py'), - base.join('path', 'extpy', 'testing', 'test_data'), base.join('path', 'gateway',), base.join('doc',), base.join('rest', 'directive.py'), @@ -63,7 +61,6 @@ base.join('magic', 'greenlet.py'), base.join('bin'), base.join('execnet', 'script'), - base.join('compat'), ) for p in base.visit('*.py', lambda x: x.check(dotfile=0)): if p.basename == '__init__.py': Deleted: /py/trunk/py/test/compat.py ============================================================================== --- /py/trunk/py/test/compat.py Wed Jan 31 22:55:30 2007 +++ (empty file) @@ -1,58 +0,0 @@ -from __future__ import generators -import py - -class TestCaseUnit(py.test.Function): - """ compatibility Unit executor for TestCase methods - honouring setUp and tearDown semantics. - """ - def execute(self, session): - boundmethod = self.obj - instance = boundmethod.im_self - instance.setUp() - try: - boundmethod() - finally: - instance.tearDown() - return py.test.Item.Passed() - -class TestCase: - """compatibility class of unittest's TestCase. """ - Function = TestCaseUnit - - def setUp(self): - pass - - def tearDown(self): - pass - - def fail(self, msg=None): - """ fail immediate with given message. """ - raise py.test.Item.Failed(msg=msg) - - def assertRaises(self, excclass, func, *args, **kwargs): - py.test.raises(excclass, func, *args, **kwargs) - failUnlessRaises = assertRaises - - # dynamically construct (redundant) methods - aliasmap = [ - ('x', 'not x', 'assert_, failUnless'), - ('x', 'x', 'failIf'), - ('x,y', 'x!=y', 'failUnlessEqual,assertEqual, assertEquals'), - ('x,y', 'x==y', 'failIfEqual,assertNotEqual, assertNotEquals'), - ] - items = [] - for sig, expr, names in aliasmap: - names = map(str.strip, names.split(',')) - sigsubst = expr.replace('y', '%s').replace('x', '%s') - for name in names: - items.append(""" - def %(name)s(self, %(sig)s): - __tracebackhide__ = True - if %(expr)s: - raise py.test.Item.Failed(msg=%(sigsubst)r %% (%(sig)s)) - """ % locals() ) - - source = "".join(items) - exec py.code.Source(source).compile() - -__all__ = ['TestCase'] Deleted: /py/trunk/py/test/testing/test_compat.py ============================================================================== --- /py/trunk/py/test/testing/test_compat.py Wed Jan 31 22:55:30 2007 +++ (empty file) @@ -1,51 +0,0 @@ -from __future__ import generators -import py - -class TestCompatTestCaseSetupSemantics(py.test.compat.TestCase): - globlist = [] - - def setUp(self): - self.__dict__.setdefault('l', []).append(42) - self.globlist.append(self) - - def tearDown(self): - self.l.pop() - - def test_issetup(self): - l = self.l - assert len(l) == 1 - assert l[-1] == 42 - #self.checkmultipleinstances() - - def test_issetup2(self): - l = self.l - assert len(l) == 1 - assert l[-1] == 42 - #self.checkmultipleinstances() - - #def checkmultipleinstances(self): - # for x,y in zip(self.globlist, self.globlist[1:]): - # assert x is not y - -class TestCompatAssertions(py.test.compat.TestCase): - nameparamdef = { - 'failUnlessEqual,assertEqual,assertEquals': ('1, 1', '1, 0'), - 'assertNotEquals,failIfEqual': ('0, 1', '0,0'), - 'failUnless,assert_': ('1', 'None'), - 'failIf': ('0', '1'), - } - - sourcelist = [] - for names, (paramok, paramfail) in nameparamdef.items(): - for name in names.split(','): - source = """ - def test_%(name)s(self): - self.%(name)s(%(paramok)s) - #self.%(name)s(%(paramfail)s) - - def test_%(name)s_failing(self): - self.assertRaises(py.test.Item.Failed, - self.%(name)s, %(paramfail)s) - """ % locals() - co = py.code.Source(source).compile() - exec co From hpk at codespeak.net Wed Jan 31 23:20:35 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 23:20:35 +0100 (CET) Subject: [py-svn] r37698 - py/trunk/py/test/terminal Message-ID: <20070131222035.7BEC910077@code0.codespeak.net> Author: hpk Date: Wed Jan 31 23:20:33 2007 New Revision: 37698 Modified: py/trunk/py/test/terminal/remote.py Log: waiting even a bit longer for loop-on-failing Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Wed Jan 31 23:20:33 2007 @@ -76,7 +76,7 @@ while 1: if self.config.option.looponfailing and (failures or not wasfailing): while not checkpyfilechange(rootdir): - py.std.time.sleep(2.4) + py.std.time.sleep(4.4) wasfailing = len(failures) failures = self.run_remote_session(failures) if not self.config.option.looponfailing: From hpk at codespeak.net Wed Jan 31 23:23:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 23:23:26 +0100 (CET) Subject: [py-svn] r37699 - in py/trunk: . py py/bin Message-ID: <20070131222326.9A3CA10082@code0.codespeak.net> Author: hpk Date: Wed Jan 31 23:23:24 2007 New Revision: 37699 Modified: py/trunk/py/__init__.py py/trunk/py/bin/_makepyrelease.py py/trunk/setup.py Log: removing py._dist export (only accessed from setup.py and py/bin/_makepyrelease.py) Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Wed Jan 31 23:23:24 2007 @@ -21,8 +21,6 @@ long_description = globals()['__doc__'], exportdefs = { - '_dist.setup' : ('./misc/_dist.py', 'setup'), - # helpers for use from test functions or collectors 'test.raises' : ('./test/raises.py', 'raises'), 'test.deprecated_call' : ('./test/deprecate.py', 'deprecated_call'), Modified: py/trunk/py/bin/_makepyrelease.py ============================================================================== --- py/trunk/py/bin/_makepyrelease.py (original) +++ py/trunk/py/bin/_makepyrelease.py Wed Jan 31 23:23:24 2007 @@ -51,16 +51,17 @@ distdir = tmpdir.ensure('dist', dir=1) oldir = rootdir.chdir() try: + from py.__.misc.dist import setup trace("invoking sdist, generating into %s" % (distdir,)) - py._dist.setup(py, script_name="setup.py", - script_args=('-q', 'sdist', '--no-prune', - '-m', str(manifest), - '--formats=gztar,zip', - '-d', str(distdir))) - py._dist.setup(py, script_name="setup.py", - script_args=('-q', 'bdist_wininst', - #'-m', str(manifest), - '-d', str(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 Modified: py/trunk/setup.py ============================================================================== --- py/trunk/setup.py (original) +++ py/trunk/setup.py Wed Jan 31 23:23:24 2007 @@ -1,2 +1,3 @@ -import py -py._dist.setup(py) +import py +from py.__.misc._dist import setup +setup(py) From hpk at codespeak.net Wed Jan 31 23:43:32 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 23:43:32 +0100 (CET) Subject: [py-svn] r37700 - py/trunk/py/doc Message-ID: <20070131224332.12ADF10077@code0.codespeak.net> Author: hpk Date: Wed Jan 31 23:43:32 2007 New Revision: 37700 Modified: py/trunk/py/doc/path.txt Log: refer to experimental gateway path code for substantiating future directions. Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Wed Jan 31 23:43:32 2007 @@ -133,6 +133,12 @@ to provide this choice (and getting rid of platform-dependencies as much as possible). +There is some experimental small approach +(:source:`py/path/gateway`) aiming at having +a convenient Remote Path implementation +and some considerations about future +works in the according :source:`py/path/gateway/todo.txt` + There are various hacks out there to have Memory-Filesystems and even path objects being directly mountable under Linux (via `fuse`). From hpk at codespeak.net Wed Jan 31 23:44:04 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 23:44:04 +0100 (CET) Subject: [py-svn] r37701 - py/trunk/py/doc Message-ID: <20070131224404.62D5010077@code0.codespeak.net> Author: hpk Date: Wed Jan 31 23:44:03 2007 New Revision: 37701 Modified: py/trunk/py/doc/TODO.txt Log: note on removal of compat.TestCase Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Wed Jan 31 23:44:03 2007 @@ -1,4 +1,4 @@ -Things to do before 0.9.0 +Things to do for 0.9.0 ========================= @@ -25,8 +25,7 @@ * (hpk, should be done) move not-to-be-exported Gateway() methods to _ - methods. * docstrings for all exported API -* remove: - test.compat.TestCAse +* (DONE) remove: test.compat.TestCAse * check and likely remove already deprecated API From hpk at codespeak.net Wed Jan 31 23:56:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 31 Jan 2007 23:56:19 +0100 (CET) Subject: [py-svn] r37703 - py/trunk/py/doc Message-ID: <20070131225619.81F461008E@code0.codespeak.net> Author: hpk Date: Wed Jan 31 23:56:18 2007 New Revision: 37703 Modified: py/trunk/py/doc/index.txt Log: adding references to path and code documentation Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Wed Jan 31 23:56:18 2007 @@ -7,6 +7,10 @@ `py.magic.greenlet`_: Lightweight in-process concurrent programming (aka Stackless) +`py.path`_: Path and Filesystem access and manipulation for local and svn based trees + +`py.code`_: High-level access/manipulation of Python code and traceback objects. + `py.xml`_ a fast'n'easy way to generate xml/html documents (including CSS-styling) `py.log`_ an alpha document about the ad-hoc logging facilities @@ -29,6 +33,8 @@ .. _`py.execnet`: execnet.html .. _`py.magic.greenlet`: greenlet.html .. _`py.log`: log.html +.. _`py.path`: path.html +.. _`py.code`: code.html .. _`py.test`: test.html .. _`py lib scripts`: bin.html .. _`py.xml`: xml.html