From fijal at codespeak.net Wed Nov 1 15:32:49 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 1 Nov 2006 15:32:49 +0100 (CET) Subject: [py-svn] r34030 - py/dist/py/apigen/tracer/testing Message-ID: <20061101143249.3113010077@code0.codespeak.net> Author: fijal Date: Wed Nov 1 15:32:48 2006 New Revision: 34030 Modified: py/dist/py/apigen/tracer/testing/test_model.py Log: (stakkars, fijal) - Fixed order. 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 Wed Nov 1 15:32:48 2006 @@ -105,8 +105,8 @@ pass g = guess_type(A).unionof(guess_type(A())) - l = list(g.striter()) - assert l[0] == "AnyOf(" - assert isinstance(l[1], SomeClass) - assert l[2] == ", " - assert isinstance(l[3], SomeInstance) + l = sorted(list(g.striter())) + assert l[4] == "AnyOf(" + assert isinstance(l[0], SomeClass) + assert l[3] == ", " + assert isinstance(l[1], SomeInstance) From fijal at codespeak.net Fri Nov 3 11:38:48 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 3 Nov 2006 11:38:48 +0100 (CET) Subject: [py-svn] r34089 - in py/dist/py/apigen: rest tracer tracer/testing Message-ID: <20061103103848.2B5091005A@code0.codespeak.net> Author: fijal Date: Fri Nov 3 11:38:47 2006 New Revision: 34089 Modified: py/dist/py/apigen/rest/genrest.py py/dist/py/apigen/tracer/description.py py/dist/py/apigen/tracer/testing/test_docgen.py Log: (guido, fijal) - Intermediate checkin of somehow changing semantics of __dict__ monitoring. Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Fri Nov 3 11:38:47 2006 @@ -390,7 +390,10 @@ tbrest.append(Paragraph(Link(linkname, linktarget))) else: tbrest.append(Paragraph(linkname)) - source = line.code.source() + try: + source = line.code.source() + except IOError: + source = "*Cannot get source*" mangled = [] for i, sline in enumerate(str(source).split('\n')): if i == lineno: Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Fri Nov 3 11:38:47 2006 @@ -4,6 +4,7 @@ import types import inspect +import copy MAX_CALL_SITES = 20 @@ -42,9 +43,6 @@ def __cmp__(self, other): return cmp(self._getval(), other._getval()) -class NoValue(object): - """used in MethodDesc.get_local_changes() when there is no value""" - def cut_stack(stack, frame, upward_frame=None): if hasattr(frame, 'raw'): frame = frame.raw @@ -234,7 +232,7 @@ def __init__(self, *args, **kwargs): super(MethodDesc, self).__init__(*args, **kwargs) self.old_dict = {} - self.new_dict = {} + self.changeset = {} # right now it's not different than method desc, only code is different def getcode(self): @@ -245,28 +243,47 @@ def consider_start_locals(self, frame): # XXX recursion issues? - obj = frame.f_locals.get('self') - if not obj: - # static method + obj = frame.f_locals[self.pyobj.im_func.func_code.co_varnames[0]] + try: + if not obj: + # static method + return + except AttributeError: return - self.old_dict = obj.__dict__.copy() + #self.old_dict = self.perform_dict_copy(obj.__dict__) + self.old_dict = copy.deepcopy(obj.__dict__) def consider_end_locals(self, frame): - obj = frame.f_locals.get('self') - if not obj: - # static method + obj = frame.f_locals[self.pyobj.im_func.func_code.co_varnames[0]] + try: + if not obj: + # static method + return + except AttributeError: return - self.new_dict = obj.__dict__.copy() + # store the local changes + # update self.changeset + self.update_changeset(obj.__dict__) def get_local_changes(self): - changeset = {} + return self.changeset + + def set_changeset(changeset, key, value): + if key not in changeset: + changeset[key] = set([value]) + else: + changeset[key].add(value) + set_changeset = staticmethod(set_changeset) + + def update_changeset(self, new_dict): + changeset = self.changeset for k, v in self.old_dict.iteritems(): - if k not in self.new_dict: - changeset[k] = (v, NoValue) - elif self.new_dict[k] != v: - changeset[k] = (v, self.new_dict[k]) - for k, v in self.new_dict.iteritems(): + if k not in new_dict: + self.set_changeset(changeset, k, "deleted") + elif new_dict[k] != v: + self.set_changeset(changeset, k, "changed") + for k, v in new_dict.iteritems(): if k not in self.old_dict: - changeset[k] = (NoValue, v) + self.set_changeset(changeset, k, "created") return changeset 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 Nov 3 11:38:47 2006 @@ -160,8 +160,8 @@ t.end_tracing() desc = ds.descs['testclass'] methdesc = desc.fields['bar'] - assert methdesc.old_dict != methdesc.new_dict - assert methdesc.get_local_changes() == {'foo': (0, 1)} + #assert methdesc.old_dict != methdesc.new_dict + assert methdesc.get_local_changes() == {'foo': set(['changed'])} def test_local_changes_nochange(): class testclass(object): @@ -177,4 +177,3 @@ desc = ds.descs['testclass'] methdesc = desc.fields['bar'] assert methdesc.get_local_changes() == {} - From guido at codespeak.net Fri Nov 3 14:08:18 2006 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 3 Nov 2006 14:08:18 +0100 (CET) Subject: [py-svn] r34097 - in py/dist/py/apigen: rest tracer Message-ID: <20061103130818.92CC010079@code0.codespeak.net> Author: guido Date: Fri Nov 3 14:08:16 2006 New Revision: 34097 Modified: py/dist/py/apigen/rest/genrest.py py/dist/py/apigen/tracer/description.py Log: Fixed problems with deepcopy() not being able to copy everything, adjusted genrest to cope with the changes in tracer. Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Fri Nov 3 14:08:16 2006 @@ -332,14 +332,8 @@ local_changes = self.dsa.get_function_local_changes(functionname) lst.append(Paragraph('Changes in __dict__:')) - from py.__.apigen.tracer.description import NoValue - for k, (oldvalue, newvalue) in local_changes.iteritems(): - description = 'value changed' - if oldvalue is NoValue: - description = 'newly added' - elif newvalue is NoValue: - description = 'deleted' - lst.append(ListItem('%s: %s' % (k, description))) + for k, changeset in local_changes.iteritems(): + lst.append(ListItem('%s: %s' % (k, ', '.join(changeset)))) # XXX missing implementation of dsa.get_function_location() #filename, lineno = self.dsa.get_function_location(functionname) Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Fri Nov 3 14:08:16 2006 @@ -250,8 +250,19 @@ return except AttributeError: return - #self.old_dict = self.perform_dict_copy(obj.__dict__) - self.old_dict = copy.deepcopy(obj.__dict__) + 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 def consider_end_locals(self, frame): obj = frame.f_locals[self.pyobj.im_func.func_code.co_varnames[0]] From arigo at codespeak.net Sun Nov 5 12:31:14 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 5 Nov 2006 12:31:14 +0100 (CET) Subject: [py-svn] r34227 - in py/dist/py/test: . rsession Message-ID: <20061105113114.4478F10084@code0.codespeak.net> Author: arigo Date: Sun Nov 5 12:31:05 2006 New Revision: 34227 Modified: py/dist/py/test/cmdline.py py/dist/py/test/rsession/box.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/slave.py Log: (fijal, arigo) Kill some imports and dead code. Modified: py/dist/py/test/cmdline.py ============================================================================== --- py/dist/py/test/cmdline.py (original) +++ py/dist/py/test/cmdline.py Sun Nov 5 12:31:05 2006 @@ -4,8 +4,6 @@ # main entry point # -from py.__.test.rsession.rsession import AbstractSession - def main(args=None): warn_about_missing_assertion() if args is None: @@ -17,12 +15,16 @@ session = sessionclass(config) # ok, some option checks - if config.option.startserver and not isinstance(session, AbstractSession): - print "Cannot use web server without (R|L)Session" - raise SystemExit, 2 - if config.option.apigen and not isinstance(session, AbstractSession): - print "Cannot generate API without (R|L)Session" - raise SystemExit, 2 + if config.option.startserver: + from py.__.test.rsession.rsession import AbstractSession + if not isinstance(session, AbstractSession): + print "Cannot use web server without (R|L)Session" + raise SystemExit, 2 + if config.option.apigen: + from py.__.test.rsession.rsession import AbstractSession + if not isinstance(session, AbstractSession): + print "Cannot generate API without (R|L)Session" + raise SystemExit, 2 if config.option.runbrowser and not config.option.startserver: print "Cannot point browser when not starting server" raise SystemExit, 2 Modified: py/dist/py/test/rsession/box.py ============================================================================== --- py/dist/py/test/rsession/box.py (original) +++ py/dist/py/test/rsession/box.py Sun Nov 5 12:31:05 2006 @@ -7,7 +7,6 @@ import os import sys import marshal -import thread NICE_LEVEL = 0 # XXX make it a conftest option @@ -36,101 +35,6 @@ self.exitstat = 0 return 123 -class FifoBox(object): - def __init__(self, fun, args = [], kwargs = {}): - self.fun = fun - self.args = args - self.kwargs = kwargs - - def run(self): - dirname = tempfile.mkdtemp("pytest") - self.dirname = dirname - self.PYTESTRETVAL = os.path.join(dirname, PYTESTRETVAL) - self.PYTESTSTDERR = os.path.join(dirname, PYTESTSTDERR) - self.PYTESTSTDOUT = os.path.join(dirname, PYTESTSTDOUT) - os.mkfifo(self.PYTESTSTDOUT) - os.mkfifo(self.PYTESTSTDERR) - os.mkfifo(self.PYTESTRETVAL) - pid = os.fork() - if pid: - self.parent() - else: - try: - outcome = self.children() - except: - excinfo = py.code.ExceptionInfo() - print "Internal box error" - for i in excinfo.traceback: - print str(i)[2:-1] - print excinfo - os._exit(1) - os.close(1) - os.close(2) - os._exit(0) - return pid - - def children(self): - # right now we need to call a function, but first we need to - # map all IO that might happen - # make sure sys.stdout points to file descriptor one - fdstdout = os.open(self.PYTESTSTDOUT, os.O_WRONLY) - if fdstdout != 1: - os.dup2(fdstdout, 1) - fdstderr = os.open(self.PYTESTSTDERR, os.O_WRONLY) - if fdstderr != 2: - os.dup2(fdstderr, 2) - sys.stdout = os.fdopen(1, "w", 0) - sys.stderr = os.fdopen(2, "w", 0) - retvalf = open(self.PYTESTRETVAL, "w") - if NICE_LEVEL: - os.nice(NICE_LEVEL) - retval = self.fun(*self.args, **self.kwargs) - retvalf.write(marshal.dumps(retval)) - retvalf.close() - - def parent(self): - stdoutlock = thread.allocate_lock() - stdoutlock.acquire() - stderrlock = thread.allocate_lock() - stderrlock.acquire() - - def readsome(name, field, lock): - fd = open(name, "r") - setattr(self, field, fd.read()) - fd.close() - lock.release() - - thread.start_new_thread(readsome, (self.PYTESTSTDOUT, 'stdoutrepr', stdoutlock)) - thread.start_new_thread(readsome, (self.PYTESTSTDERR, 'stderrrepr', stderrlock)) - - retval = open(self.PYTESTRETVAL, "r") - pid, exitstat = os.wait() - self.signal = exitstat & 0x7f - self.exitstat = exitstat & 0xff00 - - if not exitstat: - retval_data = retval.read() - self.retval = marshal.loads(retval_data) - retval.close() - else: - self.retval = None - - stdoutlock.acquire() - stdoutlock.release() - stderrlock.acquire() - stderrlock.release() - - self.clear() - return self.stdoutrepr, self.stderrrepr - - def clear(self): - try: - os.unlink(self.PYTESTSTDOUT) - os.unlink(self.PYTESTSTDERR) - os.unlink(self.PYTESTRETVAL) - os.rmdir(self.dirname) - except OSError: - pass class FileBox(object): count = 0 Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Sun Nov 5 12:31:05 2006 @@ -4,8 +4,6 @@ import os import py -import thread -import threading import sys import re import time Modified: py/dist/py/test/rsession/slave.py ============================================================================== --- py/dist/py/test/rsession/slave.py (original) +++ py/dist/py/test/rsession/slave.py Sun Nov 5 12:31:05 2006 @@ -56,14 +56,6 @@ else: send(res) -def setup_screen(): - # We cannot easily just assume that we do have full communication - # channels, so we have to provide a new ones. - import thread - import os, sys - # the idea is simple: we create another process in which we perform - # read/write operations on both channels - def setup(): default_options = {'nomagic':False} # XXX should come from somewhere else From arigo at codespeak.net Sun Nov 5 16:05:00 2006 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 5 Nov 2006 16:05:00 +0100 (CET) Subject: [py-svn] r34242 - py/dist/py/c-extension/greenlet Message-ID: <20061105150500.BB28210082@code0.codespeak.net> Author: arigo Date: Sun Nov 5 16:04:58 2006 New Revision: 34242 Modified: py/dist/py/c-extension/greenlet/greenlet.c Log: (tismer, arigo) A greenlet bug, as shown by an assert. On Linux the bug silently does nothing, but on Windows we get a crash. 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 Sun Nov 5 16:04:58 2006 @@ -177,6 +177,7 @@ */ long sz1 = g->stack_saved; long sz2 = stop - g->stack_start; + assert(g->stack_start != NULL); if (sz2 > sz1) { char* c = PyMem_Realloc(g->stack_copy, sz2); if (!c) { @@ -378,7 +379,10 @@ /* start the greenlet */ ts_target->stack_start = NULL; ts_target->stack_stop = (char*) mark; - ts_target->stack_prev = ts_current; + if (ts_current->stack_start == NULL) /* ts_current is dying */ + ts_target->stack_prev = ts_current->stack_prev; + else + ts_target->stack_prev = ts_current; ts_target->top_frame = NULL; ts_target->recursion_depth = PyThreadState_GET()->recursion_depth; err = _PyGreen_switchstack(); From mwh at codespeak.net Sun Nov 5 16:38:12 2006 From: mwh at codespeak.net (mwh at codespeak.net) Date: Sun, 5 Nov 2006 16:38:12 +0100 (CET) Subject: [py-svn] r34244 - in py/dist/py: . documentation xmlobj xmlobj/testing Message-ID: <20061105153812.EE4A41007C@code0.codespeak.net> Author: mwh Date: Sun Nov 5 16:38:10 2006 New Revision: 34244 Modified: py/dist/py/__init__.py py/dist/py/documentation/confrest.py py/dist/py/xmlobj/testing/test_xml.py py/dist/py/xmlobj/visit.py py/dist/py/xmlobj/xml.py Log: add a py.xml.raw() object that can include html/xml directly in a py.xml.Tag() (i.e. without any escaping). use this in confrest.Project.process, which will hopefully unbreak http://codespeak.net/py/current/doc/ (my first py lib checkin, i think :-) (guido: this was your fault) Modified: py/dist/py/__init__.py ============================================================================== --- py/dist/py/__init__.py (original) +++ py/dist/py/__init__.py Sun Nov 5 16:38:10 2006 @@ -109,6 +109,7 @@ # 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'), Modified: py/dist/py/documentation/confrest.py ============================================================================== --- py/dist/py/documentation/confrest.py (original) +++ py/dist/py/documentation/confrest.py Sun Nov 5 16:38:10 2006 @@ -116,7 +116,7 @@ html.div(html.div(modified, style="float: right; font-style: italic;"), id = 'docinfoline')) - page.contentspace.append(content) + page.contentspace.append(py.xml.raw(content)) htmlpath = txtpath.new(ext='.html') htmlpath.write(page.unicode().encode(encoding)) Modified: py/dist/py/xmlobj/testing/test_xml.py ============================================================================== --- py/dist/py/xmlobj/testing/test_xml.py (original) +++ py/dist/py/xmlobj/testing/test_xml.py Sun Nov 5 16:38:10 2006 @@ -51,3 +51,7 @@ u = unicode(x) assert u == '' +def test_raw(): + x = ns.some(py.xml.raw("

literal

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

literal

" Modified: py/dist/py/xmlobj/visit.py ============================================================================== --- py/dist/py/xmlobj/visit.py (original) +++ py/dist/py/xmlobj/visit.py Sun Nov 5 16:38:10 2006 @@ -34,6 +34,9 @@ #self.write(obj) self.write(escape(unicode(obj))) + def raw(self, obj): + self.write(obj.uniobj) + def list(self, obj): assert id(obj) not in self.visited self.visited[id(obj)] = 1 Modified: py/dist/py/xmlobj/xml.py ============================================================================== --- py/dist/py/xmlobj/xml.py (original) +++ py/dist/py/xmlobj/xml.py Sun Nov 5 16:38:10 2006 @@ -24,6 +24,12 @@ name = self.__class__.__name__ return "<%r tag object %d>" % (name, id(self)) +class raw(object): + """just a box that can contain a unicode string that will be + included directly in the output""" + def __init__(self, uniobj): + self.uniobj = uniobj + # the generic xml namespace # provides Tag classes on the fly optionally checking for # a tagspecification From fijal at codespeak.net Thu Nov 9 15:19:39 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 9 Nov 2006 15:19:39 +0100 (CET) Subject: [py-svn] r34413 - py/dist/py/test Message-ID: <20061109141939.9501C10070@code0.codespeak.net> Author: fijal Date: Thu Nov 9 15:19:35 2006 New Revision: 34413 Modified: py/dist/py/test/config.py Log: Improved caching of configs. Modified: py/dist/py/test/config.py ============================================================================== --- py/dist/py/test/config.py (original) +++ py/dist/py/test/config.py Thu Nov 9 15:19:35 2006 @@ -172,19 +172,26 @@ 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 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): @@ -199,7 +206,7 @@ l = [current] return l -def importconfig(configpath): +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 From fijal at codespeak.net Thu Nov 9 15:20:09 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 9 Nov 2006 15:20:09 +0100 (CET) Subject: [py-svn] r34414 - py/dist/py/path/local Message-ID: <20061109142009.73A8710070@code0.codespeak.net> Author: fijal Date: Thu Nov 9 15:20:05 2006 New Revision: 34414 Modified: py/dist/py/path/local/local.py Log: Make pyimport cache modules. This speeds up py.test --collectonly about 3 times. Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Thu Nov 9 15:20:05 2006 @@ -19,6 +19,8 @@ """ Local path implementation offering access/modification methods similar to os.path. """ + _path_cache = {} + sep = os.sep class Checkers(common.FSCheckers): def _stat(self): @@ -53,6 +55,10 @@ Note also that passing in a local path object will simply return the exact same path object. Use new() to get a new copy. """ + #try: + # return cls._path_cache[path] + #except KeyError: + # pass if isinstance(path, common.FSPathBase): if path.__class__ == cls: return path @@ -68,6 +74,7 @@ "can only pass None, Path instances " "or non-empty strings to LocalPath") assert isinstance(self.strpath, str) + #cls._path_cache[path] = self return self def __hash__(self): @@ -120,7 +127,7 @@ obj.strpath = os.path.normpath( "%(drive)s%(dirname)s%(sep)s%(basename)s" % kw) return obj - + def _getbyspec(self, spec): """ return a sequence of specified path parts. 'spec' is a comma separated string containing path part names. @@ -374,6 +381,11 @@ #print "trying to import", self pkgpath = None if modname is None: + if modname is None: + try: + return self.module + except AttributeError: + pass pkgpath = self.pypkgpath() if pkgpath is not None: if ensuresyspath: @@ -394,7 +406,9 @@ if ensuresyspath: self._prependsyspath(self.dirpath()) modname = self.purebasename - return __import__(modname, None, None, ['__doc__']) + mod = __import__(modname, None, None, ['__doc__']) + self.module = mod + return mod else: try: return sys.modules[modname] From fijal at codespeak.net Thu Nov 9 15:25:39 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 9 Nov 2006 15:25:39 +0100 (CET) Subject: [py-svn] r34415 - py/dist/py/path/local Message-ID: <20061109142539.7399B10050@code0.codespeak.net> Author: fijal Date: Thu Nov 9 15:25:34 2006 New Revision: 34415 Modified: py/dist/py/path/local/local.py Log: Removed debug code (commented anyway). Modified: py/dist/py/path/local/local.py ============================================================================== --- py/dist/py/path/local/local.py (original) +++ py/dist/py/path/local/local.py Thu Nov 9 15:25:34 2006 @@ -55,10 +55,6 @@ Note also that passing in a local path object will simply return the exact same path object. Use new() to get a new copy. """ - #try: - # return cls._path_cache[path] - #except KeyError: - # pass if isinstance(path, common.FSPathBase): if path.__class__ == cls: return path @@ -74,7 +70,6 @@ "can only pass None, Path instances " "or non-empty strings to LocalPath") assert isinstance(self.strpath, str) - #cls._path_cache[path] = self return self def __hash__(self): From fijal at codespeak.net Thu Nov 9 16:36:10 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 9 Nov 2006 16:36:10 +0100 (CET) Subject: [py-svn] r34418 - in py/dist/py/test/rsession: . testing Message-ID: <20061109153610.781761008D@code0.codespeak.net> Author: fijal Date: Thu Nov 9 16:36:07 2006 New Revision: 34418 Modified: py/dist/py/test/rsession/conftest.py py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_rsession.py Log: Refactored hostmanage a bit, fixing the tests, so they don't need to be run over Ssh. Modified: py/dist/py/test/rsession/conftest.py ============================================================================== --- py/dist/py/test/rsession/conftest.py (original) +++ py/dist/py/test/rsession/conftest.py Thu Nov 9 16:36:07 2006 @@ -4,9 +4,9 @@ defaultwait = 100.0 option = py.test.Config.addoptions("distributed testing options", - Option('-D', '--disthosts', - action="store", dest="disthosts", default=None, - help="comma separated list of testhosts"), +# 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" Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Thu Nov 9 16:36:07 2006 @@ -27,24 +27,10 @@ else: return base in self.rsync_roots - -def init_hosts(reporter, sshhosts, relpath, pkgdir, rsync_roots=None, \ - remote_python=None, remote_options={}): - assert pkgdir.join("__init__.py").check(), ( - "%s probably wrong" %(pkgdir,)) - assert relpath, relpath - - nodes = [] - exc_info = [None] +def prepare_gateway(sshosts, relpath, rsync_roots, optimise_localhost, remote_python): hosts = [] - - for host in sshhosts: - - if host != 'localhost': - if remote_python is None: - gw = py.execnet.SshGateway(host) - else: - gw = py.execnet.SshGateway(host, remotepython=remote_python) + for host in sshosts: + if host != 'localhost' or not optimise_localhost: if isinstance(relpath, str): assert not os.path.isabs(relpath), relpath remoterootpath = relpath @@ -53,7 +39,18 @@ # XXX: because of NFS we do create different directories # otherwise, .pyc files overlap remoterootpath += "-" + host - + # for tests we want to use somtehing different + if host == 'localhost' and optimise_localhost is False: + from py.__.execnet.register import PopenCmdGateway + gw = PopenCmdGateway("cd ~/%s; python -u -c 'exec input()'" % remoterootpath) + if not remoterootpath.startswith("/"): + remoteroopath = "~/" + remoterootpath + else: + if remote_python is None: + gw = py.execnet.SshGateway(host) + else: + gw = py.execnet.SshGateway(host, remotepython=remote_python) + hosts.append((host, gw, remoterootpath)) else: if remote_python is None: @@ -62,9 +59,24 @@ gw = py.execnet.PopenGateway(remotepython=remote_python) gw.sshaddress = 'localhost' hosts.append((host, gw, str(pkgdir.dirpath()))) + return hosts +def init_hosts(reporter, sshhosts, relpath, pkgdir, rsync_roots=None, \ + remote_python=None, remote_options={}, optimise_localhost=True): + assert pkgdir.join("__init__.py").check(), ( + "%s probably wrong" %(pkgdir,)) + assert relpath, relpath + + nodes = [] + exc_info = [None] + hosts = prepare_gateway(sshhosts, relpath, rsync_roots, optimise_localhost, + remote_python) + # rsyncing - rsynced = {'localhost':True} + if optimise_localhost: + rsynced = {'localhost':True} + else: + rsynced = {} rsync = HostRSync(rsync_roots) for host, gw, remoterootpath in hosts: Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Thu Nov 9 16:36:07 2006 @@ -36,8 +36,9 @@ An abstract session executes collectors/items through a runner. """ - def __init__(self, config): + 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 @@ -139,7 +140,8 @@ remote_options['nomagic'] = self.config.option.nomagic nodes = init_hosts(reporter, sshhosts, directories, pkgdir, - rsync_roots, remotepython, remote_options=remote_options.d) + rsync_roots, remotepython, remote_options=remote_options.d, + optimise_localhost=self.optimise_localhost) reporter(report.RsyncFinished()) keyword = self.config.option.keyword 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 Nov 9 16:36:07 2006 @@ -69,11 +69,11 @@ assert str(events[1][0].value) == "Reason" class TestWithRealSshHosts: - 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 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] @@ -112,9 +112,9 @@ # 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(""" - disthosts = %r + disthosts = [%r] distrsync_roots = ["sub", "py"] - """ % self.hosts[:1])) + """ % 'localhost')) tmpdir.ensure("sub", "__init__.py") tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" def test_1(): @@ -128,7 +128,7 @@ """)) args = [str(tmpdir.join("sub"))] config, args = py.test.Config.parse(args) - rsession = RSession(config) + rsession = RSession(config, optimise_localhost=False) allevents = [] rsession.main(args, reporter=allevents.append) testevents = [x for x in allevents @@ -154,21 +154,21 @@ assert tb[0].source.find("execute") != -1 def test_setup_teardown_ssh(self): - hosts = self.hosts + hosts = ['localhost'] setup_events = [] teardown_events = [] nodes = init_hosts(setup_events.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"]) + rsync_roots=["py"], optimise_localhost=False) 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 if i != 'localhost']) + 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 if i != 'localhost']) + assert len(count_ready_calls) == len([i for i in hosts]) # same for teardown events teardown_wait_starts = [i for i in teardown_events @@ -179,11 +179,11 @@ assert len(teardown_wait_ends) == len(hosts) def test_setup_teardown_run_ssh(self): - hosts = self.hosts + hosts = ['localhost'] allevents = [] nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"]) + rsync_roots=["py"], optimise_localhost=False) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping @@ -220,9 +220,10 @@ """ Tests options object passing master -> server """ allevents = [] - hosts = self.hosts + hosts = ['localhost'] nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"], remote_options={'custom':'custom'}) + rsync_roots=["py"], remote_options={'custom':'custom'}, + optimise_localhost=False) rootcol = py.test.collect.Directory(pkgdir.dirpath()) itempass = rootcol.getitembynames(funcoption_spec) From fijal at codespeak.net Thu Nov 9 17:24:40 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 9 Nov 2006 17:24:40 +0100 (CET) Subject: [py-svn] r34424 - in py/dist/py/test/rsession: . testing Message-ID: <20061109162440.5D80D10094@code0.codespeak.net> Author: fijal Date: Thu Nov 9 17:24:38 2006 New Revision: 34424 Modified: py/dist/py/test/rsession/reporter.py py/dist/py/test/rsession/testing/test_reporter.py Log: Reporter tests refactoring. Now FailedTryiter works for both reporters (and is tested) Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Thu Nov 9 17:24:38 2006 @@ -11,6 +11,7 @@ from py.__.test.terminal.out import getout from py.__.test.rsession import report +from py.__.test.rsession import outcome class AbstractReporter(object): def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)): @@ -75,7 +76,8 @@ self.timeend = item.timeend self.skips() self.failures() - self.hangs() + if hasattr(self, 'nodes'): # XXX: Testing + self.hangs() self.summary() def hangs(self): @@ -91,13 +93,18 @@ if self.failed_tests_outcome: self.out.sep("=", " FAILURES ") for event in self.failed_tests_outcome: - host = self.get_host(event) - self.out.sep('_', "%s on %s" % - (" ".join(event.item.listnames()), host)) - if event.outcome.signal: - self.repr_signal(event.item, event.outcome) + if isinstance(event, report.ReceivedItemOutcome): + host = self.get_host(event) + self.out.sep('_', "%s on %s" % + (" ".join(event.item.listnames()), host)) + if event.outcome.signal: + self.repr_signal(event.item, event.outcome) + else: + self.repr_failure(event.item, event.outcome) else: - self.repr_failure(event.item, event.outcome) + self.out.sep('_', " ".join(event.item.listnames())) + out = outcome.Outcome(excinfo=event.excinfo) + self.repr_failure(event.item, outcome.ReprOutcome(out.make_repr())) def repr_failure(self, item, outcome): excinfo = outcome.excinfo @@ -221,14 +228,18 @@ class RemoteReporter(AbstractReporter): def get_host(self, item): - return item.channel.gateway.sshaddress + if item.channel: + return item.channel.gateway.sshaddress + # XXX: Testing purposes only + return 'localhost' def get_item_name(self, event, colitem): - return event.channel.gateway.sshaddress + ":" + \ + return self.get_host(event) + ":" + \ "/".join(colitem.listnames()) - + def report_FailedTryiter(self, event): self.out.line("FAILED TO LOAD MODULE: %s\n" % "/".join(event.item.listnames())) + self.failed_tests_outcome.append(event) def report_SkippedTryiter(self, event): self.out.line("Skipped (%s) %s\n" % (str(event.excinfo.value), "/". @@ -248,6 +259,7 @@ def report_FailedTryiter(self, event): #self.show_item(event.item, False) self.out.write("- FAILED TO LOAD MODULE") + self.failed_tests_outcome.append(event) def report_ReceivedItemOutcome(self, event): if event.outcome.passed: 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 Nov 9 17:24:38 2006 @@ -4,8 +4,9 @@ """ import py, os -from py.__.test.rsession.rsession import LocalReporter, AbstractSession -from py.__.test.rsession.report import ReceivedItemOutcome, ItemStart +from py.__.test.rsession.rsession import LocalReporter, AbstractSession,\ + RemoteReporter +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.box import Box @@ -16,7 +17,7 @@ def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() -class TestReporter(object): +class AbstractTestReporter(object): def prepare_outcomes(self): # possible outcomes try: @@ -35,7 +36,7 @@ return outcomes - def test_report_received_item_outcome(self): + def report_received_item_outcome(self): config, args = py.test.Config.parse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(pkgdir.dirpath()) @@ -43,19 +44,19 @@ outcomes = self.prepare_outcomes() def boxfun(config, item, outcomes): - r = LocalReporter(config, ["localhost"]) + r = self.reporter(config, ["localhost"]) for outcome in outcomes: - r.report(ReceivedItemOutcome(None, item, outcome)) + r.report(report.ReceivedItemOutcome(None, item, outcome)) s = StringIO() stdoutcopy = sys.stdout sys.stdout = s boxfun(config, item, outcomes) - sys.stdoud = stdoutcopy + sys.stdout = stdoutcopy - assert s.getvalue() == 'FsF.' + return s.getvalue() - def test_module(self): + def _test_module(self): config, args = py.test.Config.parse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(pkgdir.dirpath()) @@ -64,22 +65,22 @@ outcomes = self.prepare_outcomes() def boxfun(pkgdir, config, item, funcitem, outcomes): - r = LocalReporter(config, ["localhost"]) + r = self.reporter(config, ["localhost"]) #r.pkgdir = pkdgir - r.report(ItemStart(item)) + r.report(report.ItemStart(item)) for outcome in outcomes: - r.report(ReceivedItemOutcome(None, funcitem, outcome)) + r.report(report.ReceivedItemOutcome(None, funcitem, outcome)) s = StringIO() stdoutcopy = sys.stdout sys.stdout = s boxfun(pkgdir, config, moditem, funcitem, outcomes) - sys.stdoud = stdoutcopy + sys.stdout = stdoutcopy + + return s.getvalue() - assert s.getvalue().endswith("test_slave.py[8] FsF."),\ - s.getvalue() - def test_full_module(self): + def _test_full_module(self): tmpdir = py.test.ensuretemp("repmod") tmpdir.ensure("__init__.py") tmpdir.ensure("test_one.py").write(py.code.Source(""" @@ -97,7 +98,7 @@ def boxfun(): config, args = py.test.Config.parse([str(tmpdir)]) rootcol = py.test.collect.Directory(tmpdir) - r = LocalReporter(config, ["localhost"]) + r = self.reporter(config, ["localhost"]) list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) #b = Box(boxfun) @@ -106,9 +107,71 @@ stdoutcopy = sys.stdout sys.stdout = s boxfun() - sys.stdoud = stdoutcopy + sys.stdout = stdoutcopy - assert s.getvalue() == """ + return s.getvalue() + + def test_failed_to_load(self): + tmpdir = py.test.ensuretemp("failedtoload") + tmpdir.ensure("__init__.py") + tmpdir.ensure("test_three.py").write(py.code.Source(""" + sadsadsa + """)) + 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'])) + r.report(report.RsyncFinished()) + list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) + r.report(report.TestFinished()) + + s = StringIO() + stdoutcopy = sys.stdout + sys.stdout = s + boxfun() + sys.stdout = stdoutcopy + assert s.getvalue().find("NameError: name 'sadsadsa' is not defined") != -1 + +class TestLocalReporter(AbstractTestReporter): + reporter = LocalReporter + + def test_report_received_item_outcome(self): + assert self.report_received_item_outcome() == 'FsF.' + + def test_module(self): + assert self._test_module().endswith("test_slave.py[8] FsF."),\ + self._test_module() + + def test_full_module(self): + assert self._test_full_module() == """ repmod/test_one.py[1] repmod/test_three.py[0] - FAILED TO LOAD MODULE repmod/test_two.py[0] - skipped (reason)""" + +class TestRemoteReporter(AbstractTestReporter): + reporter = RemoteReporter + + def test_report_received_item_outcome(self): + 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 +""" + assert val == expected + + 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 +""" + assert val == expected + + def test_full_module(self): + val = self._test_full_module() + assert val == 'FAILED TO LOAD MODULE: repmod/test_three.py\n'\ + '\nSkipped (reason) repmod/test_two.py\n\n' From guido at codespeak.net Fri Nov 10 11:33:37 2006 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 10 Nov 2006 11:33:37 +0100 (CET) Subject: [py-svn] r34451 - in py/dist/py/test/rsession: . testing webdata Message-ID: <20061110103337.C9C93100BE@code0.codespeak.net> Author: guido Date: Fri Nov 10 11:33:34 2006 New Revision: 34451 Added: py/dist/py/test/rsession/testing/test_webjs.py Modified: py/dist/py/test/rsession/webdata/index.html py/dist/py/test/rsession/webdata/source.js py/dist/py/test/rsession/webjs.py Log: Added first tests for code that is translated to JS (using the mock DOM API from PyPy), added missing end tag in index.html. Added: py/dist/py/test/rsession/testing/test_webjs.py ============================================================================== --- (empty file) +++ py/dist/py/test/rsession/testing/test_webjs.py Fri Nov 10 11:33:34 2006 @@ -0,0 +1,28 @@ +import py +try: + import pypy +except ImportError: + py.test.skip('missing PyPy') +from pypy.translator.js.modules import dom +from py.__.test.rsession import webjs + +here = py.magic.autopath().dirpath() + +def setup_module(mod): + # load HTML into window object + html = here.join('../webdata/index.html').read() + dom.window = dom.Window(html) + +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' + 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 Fri Nov 10 11:33:34 2006 @@ -2,6 +2,7 @@ Py.test 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 Fri Nov 10 11:33:34 2006 @@ -5,7 +5,7 @@ import py from py.__.test.rsession.web import exported_methods try: - from pypy.translator.js.modules import _dom as dom + from pypy.translator.js.modules import dom from pypy.translator.js.helper import __show_traceback from pypy.translator.transformer.debug import traceback_handler except ImportError: From fijal at codespeak.net Sat Nov 11 14:03:57 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 11 Nov 2006 14:03:57 +0100 (CET) Subject: [py-svn] r34484 - py/dist/py/test/rsession Message-ID: <20061111130357.1970410105@code0.codespeak.net> Author: fijal Date: Sat Nov 11 14:03:56 2006 New Revision: 34484 Modified: py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/web.py Log: Ooops, forgotten checkin. Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Sat Nov 11 14:03:56 2006 @@ -27,7 +27,7 @@ else: return base in self.rsync_roots -def prepare_gateway(sshosts, relpath, rsync_roots, optimise_localhost, remote_python): +def prepare_gateway(sshosts, relpath, rsync_roots, optimise_localhost, remote_python, pkgdir): hosts = [] for host in sshosts: if host != 'localhost' or not optimise_localhost: @@ -70,7 +70,7 @@ nodes = [] exc_info = [None] hosts = prepare_gateway(sshhosts, relpath, rsync_roots, optimise_localhost, - remote_python) + remote_python, pkgdir) # rsyncing if optimise_localhost: Modified: py/dist/py/test/rsession/web.py ============================================================================== --- py/dist/py/test/rsession/web.py (original) +++ py/dist/py/test/rsession/web.py Sat Nov 11 14:03:56 2006 @@ -33,7 +33,7 @@ from pypy.translator.js.main import rpython2javascript, Options from pypy.translator.js import commproxy - Options.debug_transform = True + Options.debug_transform = False commproxy.USE_MOCHIKIT = False IMPORTED_PYPY = True except ImportError: From fijal at codespeak.net Sat Nov 11 14:30:36 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 11 Nov 2006 14:30:36 +0100 (CET) Subject: [py-svn] r34487 - py/dist/py/test/rsession Message-ID: <20061111133036.243CE1010D@code0.codespeak.net> Author: fijal Date: Sat Nov 11 14:30:34 2006 New Revision: 34487 Modified: py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/slave.py Log: Make easier teardown when using -x. Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Sat Nov 11 14:30:34 2006 @@ -97,11 +97,12 @@ return nodes -def teardown_hosts(reporter, channels, nodes, waiter=lambda : time.sleep(.1)): +def teardown_hosts(reporter, channels, nodes, waiter=lambda : time.sleep(.1), + exitfirst=False): for channel in channels: channel.send(None) - clean = False + clean = exitfirst while not clean: clean = True for node in nodes: Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Sat Nov 11 14:30:34 2006 @@ -139,6 +139,7 @@ remotepython = None remote_options['nomagic'] = self.config.option.nomagic + remote_options['exitfirst'] = self.config.option.exitfirst nodes = init_hosts(reporter, sshhosts, directories, pkgdir, rsync_roots, remotepython, remote_options=remote_options.d, optimise_localhost=self.optimise_localhost) @@ -155,7 +156,8 @@ #assert 0, "\n".join([",".join(x.listnames()) for x in # list(itemgenerator)]) dispatch_loop(nodes, itemgenerator, checkfun) - teardown_hosts(reporter, [node.channel for node in nodes], nodes) + teardown_hosts(reporter, [node.channel for node in nodes], nodes, + exitfirst=self.config.option.exitfirst) reporter(report.Nodes(nodes)) reporter(report.TestFinished()) if startserverflag: Modified: py/dist/py/test/rsession/slave.py ============================================================================== --- py/dist/py/test/rsession/slave.py (original) +++ py/dist/py/test/rsession/slave.py Sat Nov 11 14:30:34 2006 @@ -54,8 +54,14 @@ excinfo = py.code.ExceptionInfo() send(Outcome(excinfo=excinfo, is_critical=True).make_repr()) else: + if not res[0] and py.test.remote.exitfirst: + # we're finished, but need to eat what we can + send(res) + break send(res) - + + while nextitem is not None: + nextitem = receive() def setup(): default_options = {'nomagic':False} # XXX should come from somewhere else From fijal at codespeak.net Sat Nov 11 20:42:02 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 11 Nov 2006 20:42:02 +0100 (CET) Subject: [py-svn] r34506 - in py/dist/py/test/rsession: . testing Message-ID: <20061111194202.2F6D91011E@code0.codespeak.net> Author: fijal Date: Sat Nov 11 20:42:00 2006 New Revision: 34506 Modified: py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_rsession.py Log: Test for -x on RSession. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Sat Nov 11 20:42:00 2006 @@ -138,10 +138,11 @@ except: remotepython = None - remote_options['nomagic'] = self.config.option.nomagic - remote_options['exitfirst'] = self.config.option.exitfirst + rem_options = RemoteOptions({}) + rem_options['nomagic'] = self.config.option.nomagic + rem_options['exitfirst'] = self.config.option.exitfirst nodes = init_hosts(reporter, sshhosts, directories, pkgdir, - rsync_roots, remotepython, remote_options=remote_options.d, + rsync_roots, remotepython, remote_options=rem_options.d, optimise_localhost=self.optimise_localhost) reporter(report.RsyncFinished()) 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 Nov 11 20:42:00 2006 @@ -107,7 +107,34 @@ ## channel.send(None) ## res = channel.receive() ## assert res == "ok" - + + def test_example_distribution_minus_x(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(""" + disthosts = [%r] + distrsync_roots = ["sub", "py"] + """ % 'localhost')) + tmpdir.ensure("sub", "__init__.py") + tmpdir.ensure("sub", "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 + """)) + args = [str(tmpdir.join("sub")), "-x"] + config, args = py.test.Config.parse(args) + rsession = RSession(config) + allevents = [] + rsession.main(args, reporter=allevents.append) + testevents = [x for x in allevents + 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() @@ -159,7 +186,7 @@ teardown_events = [] nodes = init_hosts(setup_events.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"], optimise_localhost=False) + rsync_roots=["py"], optimise_localhost=False, remote_options={'exitfirst':False}) teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -183,7 +210,7 @@ allevents = [] nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"], optimise_localhost=False) + rsync_roots=["py"], optimise_localhost=False, remote_options={'exitfirst':False}) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping @@ -222,7 +249,7 @@ allevents = [] hosts = ['localhost'] nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"], remote_options={'custom':'custom'}, + rsync_roots=["py"], remote_options={'custom':'custom', 'exitfirst':False}, optimise_localhost=False) rootcol = py.test.collect.Directory(pkgdir.dirpath()) From fijal at codespeak.net Sat Nov 11 21:08:05 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 11 Nov 2006 21:08:05 +0100 (CET) Subject: [py-svn] r34510 - py/dist/py/test/rsession Message-ID: <20061111200805.B7EB010130@code0.codespeak.net> Author: fijal Date: Sat Nov 11 21:08:04 2006 New Revision: 34510 Modified: py/dist/py/test/rsession/rsession.py Log: Ooops, forgotten to commit that one. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Sat Nov 11 21:08:04 2006 @@ -29,7 +29,8 @@ def __setitem__(self, item, val): self.d[item] = val -remote_options = RemoteOptions({'we_are_remote':False}) +# XXX: Must be initialised somehow +remote_options = RemoteOptions({'we_are_remote':False, 'exitfirst':False}) class AbstractSession(object): """ From fijal at codespeak.net Sun Nov 12 00:25:20 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sun, 12 Nov 2006 00:25:20 +0100 (CET) Subject: [py-svn] r34517 - py/dist/py/test Message-ID: <20061111232520.3009D10135@code0.codespeak.net> Author: fijal Date: Sun Nov 12 00:25:18 2006 New Revision: 34517 Modified: py/dist/py/test/config.py Log: Deholgerizing. Modified: py/dist/py/test/config.py ============================================================================== --- py/dist/py/test/config.py (original) +++ py/dist/py/test/config.py Sun Nov 12 00:25:18 2006 @@ -162,7 +162,7 @@ # access to the resulting config values? config = Config._config - # trigger loading conftest files which might add options! + # trigger loading conftest files which might add options! for configpath in configpaths: config.loadconfig(configpath) config.loadconfig(defaultconfig).adddefaultoptions() From fijal at codespeak.net Sun Nov 12 00:27:06 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sun, 12 Nov 2006 00:27:06 +0100 (CET) Subject: [py-svn] r34518 - in py/dist/py/test/rsession: . testing Message-ID: <20061111232706.5696610136@code0.codespeak.net> Author: fijal Date: Sun Nov 12 00:27:04 2006 New Revision: 34518 Modified: py/dist/py/test/rsession/hostmanage.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/testing/test_rsession.py Log: Major refactoring of rsession rsync attempt. Now you can specify multiple times the same host and control how many times you would like to rsync. And test for the above ;-) Modified: py/dist/py/test/rsession/hostmanage.py ============================================================================== --- py/dist/py/test/rsession/hostmanage.py (original) +++ py/dist/py/test/rsession/hostmanage.py Sun Nov 12 00:27:04 2006 @@ -29,13 +29,13 @@ def prepare_gateway(sshosts, relpath, rsync_roots, optimise_localhost, remote_python, pkgdir): hosts = [] - for host in sshosts: + 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[host] + remoterootpath = relpath[(num, host)] # XXX: because of NFS we do create different directories # otherwise, .pyc files overlap remoterootpath += "-" + host @@ -51,46 +51,53 @@ else: gw = py.execnet.SshGateway(host, remotepython=remote_python) - hosts.append((host, gw, remoterootpath)) + 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.sshaddress = 'localhost' - hosts.append((host, gw, str(pkgdir.dirpath()))) + 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 def init_hosts(reporter, sshhosts, relpath, pkgdir, rsync_roots=None, \ - remote_python=None, remote_options={}, optimise_localhost=True): + remote_python=None, remote_options={}, optimise_localhost=True,\ + do_sync=True): assert pkgdir.join("__init__.py").check(), ( "%s probably wrong" %(pkgdir,)) assert relpath, relpath - nodes = [] exc_info = [None] hosts = prepare_gateway(sshhosts, relpath, rsync_roots, optimise_localhost, remote_python, pkgdir) # rsyncing - if optimise_localhost: - rsynced = {'localhost':True} - else: - rsynced = {} - - rsync = HostRSync(rsync_roots) - for host, gw, remoterootpath in hosts: - if host in rsynced: + rsynced = {} + + 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): continue - rsynced[host] = True + rsynced[(host, remoterootpath)] = True def done(host=host): reporter(report.HostReady(host)) reporter(report.HostRSyncing(host, remoterootpath)) - rsync.add_target(gw, remoterootpath, done) + if do_sync: + rsync.add_target(gw, remoterootpath, done) + if not do_sync: + return # for testing only rsync.send(pkgdir.dirpath()) # hosts ready - for host, gw, remoterootpath in hosts: + return setup_nodes(hosts, pkgdir, remote_options, reporter) + +def setup_nodes(hosts, pkgdir, remote_options, reporter): + 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)) Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Sun Nov 12 00:27:04 2006 @@ -106,6 +106,19 @@ reporter(report.FailedTryiter(excinfo, item)) reporterror = staticmethod(reporterror) +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) + if m: + directories[(num, m.group(1))] = m.group(2) + sshhosts[num] = m.group(1) + else: + directories[(num, host)] = "pytestcache" + return directories + class RSession(AbstractSession): """ Remote version of session """ @@ -114,16 +127,7 @@ if not args: args = [py.path.local()] sshhosts = self.config.getinitialvalue("disthosts") - # 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) - if m: - directories[m.group(1)] = m.group(2) - sshhosts[num] = m.group(1) - else: - directories[host] = "pytestcache" + directories = parse_directories(sshhosts) try: rsync_roots = self.config.getinitialvalue("distrsync_roots") except: @@ -155,8 +159,6 @@ yield y itemgenerator = itemgen() - #assert 0, "\n".join([",".join(x.listnames()) for x in - # list(itemgenerator)]) dispatch_loop(nodes, itemgenerator, checkfun) teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) 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 Sun Nov 12 00:27:04 2006 @@ -4,7 +4,7 @@ import py from py.__.test.rsession import report -from py.__.test.rsession.rsession import RSession +from py.__.test.rsession.rsession import RSession, parse_directories from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ @@ -270,3 +270,39 @@ assert len(passed) == 2 * len(nodes) assert len(skipped) == 0 assert len(events) == len(passed) + +class TestDirectories(object): + 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')] + + def test_sophisticated_parse(self): + sshhosts = ['a at h1:/tmp', 'h2:tmp', 'h3'] + dirs = parse_directories(sshhosts) + assert 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 = [] + hosts = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] + dirs = parse_directories(hosts) + init_hosts(testevents.append, hosts, dirs, 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' + From fijal at codespeak.net Sun Nov 12 00:29:29 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sun, 12 Nov 2006 00:29:29 +0100 (CET) Subject: [py-svn] r34519 - py/dist/py/test/rsession Message-ID: <20061111232929.C360D1013C@code0.codespeak.net> Author: fijal Date: Sun Nov 12 00:29:28 2006 New Revision: 34519 Modified: py/dist/py/test/rsession/rsession.py Log: Changed default LSession runner. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Sun Nov 12 00:29:28 2006 @@ -201,10 +201,12 @@ self.docstorage = DocStorage().from_pkg(module) 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 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 = box_runner + runner = plain_runner keyword = self.config.option.keyword From fijal at codespeak.net Sun Nov 12 21:24:16 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sun, 12 Nov 2006 21:24:16 +0100 (CET) Subject: [py-svn] r34534 - py/dist/py/test Message-ID: <20061112202416.16D661015D@code0.codespeak.net> Author: fijal Date: Sun Nov 12 21:24:15 2006 New Revision: 34534 Modified: py/dist/py/test/collect.py Log: Add a bit of heuristic for collection order (it's better than it was before). Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Sun Nov 12 21:24:15 2006 @@ -120,8 +120,8 @@ return property(fget, fset, None, "underlying object") obj = obj() - def _getobj(self): - return getattr(self.parent.obj, self.name) + def _getobj(self): + return getattr(self.parent.obj, self.name) def multijoin(self, namelist): """ return a list of colitems for the given namelist. """ @@ -421,8 +421,16 @@ teardown_class = getattr(teardown_class, 'im_func', teardown_class) teardown_class(self.obj) - def getsortvalue(self): - for x in self.tryiter((py.test.collect.Generator, py.test.Item)): + def getsortvalue(self): + try: + for key, val in self.obj.__dict__.iteritems(): + import types + if type(val) is (types.FunctionType, types.GeneratorType): + return val.func_code.co_firstlineno + except AttributeError: + pass + # fall back... + for x in self.tryiter((py.test.collect.Generator, py.test.Item)): return x.getsortvalue() class Instance(PyCollectorMixin, Collector): From fijal at codespeak.net Mon Nov 13 12:21:50 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 12:21:50 +0100 (CET) Subject: [py-svn] r34550 - py/dist/py/test/rsession/testing Message-ID: <20061113112150.ECB1810182@code0.codespeak.net> Author: fijal Date: Mon Nov 13 12:21:48 2006 New Revision: 34550 Modified: py/dist/py/test/rsession/testing/test_web.py Log: Skip the test till guido fix it. 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 Mon Nov 13 12:21:48 2006 @@ -15,6 +15,7 @@ py.test.skip("No PyPy detected") def test_js_generate(): + py.test.skip("XXX") from py.__.test.rsession import webjs from py.__.test.rsession.web import FUNCTION_LIST From fijal at codespeak.net Mon Nov 13 12:22:16 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 12:22:16 +0100 (CET) Subject: [py-svn] r34551 - in py/dist/py/test/rsession: . testing Message-ID: <20061113112216.4FF4310184@code0.codespeak.net> Author: fijal Date: Mon Nov 13 12:22:13 2006 New Revision: 34551 Added: py/dist/py/test/rsession/testing/test_config.py (contents, props changed) Modified: py/dist/py/test/rsession/local.py py/dist/py/test/rsession/master.py py/dist/py/test/rsession/rsession.py Log: Refactored session options (starting point) for having central point for those. Modified: py/dist/py/test/rsession/local.py ============================================================================== --- py/dist/py/test/rsession/local.py (original) +++ py/dist/py/test/rsession/local.py Mon Nov 13 12:22:13 2006 @@ -33,6 +33,11 @@ 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/dist/py/test/rsession/master.py ============================================================================== --- py/dist/py/test/rsession/master.py (original) +++ py/dist/py/test/rsession/master.py Mon Nov 13 12:22:13 2006 @@ -5,8 +5,6 @@ from py.__.test.rsession.outcome import ReprOutcome from py.__.test.rsession import report -MAX_TASKS_PER_NODE = 15 - class MasterNode(object): def __init__(self, channel, reporter): self.channel = channel @@ -29,12 +27,14 @@ self.reporter(report.SendItem(self.channel, item)) def dispatch_loop(masternodes, itemgenerator, shouldstop, - waiter = lambda: py.std.time.sleep(0.1), - maxtasks_per_node = MAX_TASKS_PER_NODE): + waiter = lambda: py.std.time.sleep(0.1)): + from py.__.test.rsession.rsession import session_options + + max_tasks_per_node = session_options.max_tasks_per_node while 1: try: for node in masternodes: - if len(node.pending) < maxtasks_per_node: + if len(node.pending) < max_tasks_per_node: item = itemgenerator.next() if shouldstop(): return Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Nov 13 12:22:13 2006 @@ -14,7 +14,7 @@ from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ - box_runner + box_runner, RunnerPolicy from py.__.test.rsession.reporter import LocalReporter, RemoteReporter class RemoteOptions(object): @@ -31,6 +31,36 @@ # XXX: Must be initialised somehow remote_options = RemoteOptions({'we_are_remote':False, 'exitfirst':False}) + +class SessionOptions: + defaults = { + 'max_tasks_per_node' : 15, + 'runner_policy' : 'plain_runner' + } + + config = None + + def getvalue(self, opt): + try: + return getattr(self.config.getinitialvalue('SessionOptions'), opt) + except (ValueError, AttributeError): + try: + return self.defaults[opt] + except KeyError: + raise AttributeError("Option %s undeclared" % opt) + + def bind_config(self, config): + self.config = config + + def __repr__(self): + return "" % self.config + + def __getattr__(self, attr): + if self.config is None: + raise NotImplementedError("Need to set up config first") + return self.getvalue(attr) + +session_options = SessionOptions() class AbstractSession(object): """ @@ -158,7 +188,8 @@ for y in x.tryiter(reporterror = lambda x: self.reporterror(reporter, x), keyword = keyword): yield y - itemgenerator = itemgen() + itemgenerator = itemgen() + session_options.bind_config(self.config) dispatch_loop(nodes, itemgenerator, checkfun) teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) @@ -191,6 +222,7 @@ pkgdir = self.getpkgdir(args[0]) colitems = self.make_colitems(args, baseon=pkgdir.dirpath()) reporter(report.RsyncFinished()) + session_options.bind_config(self.config) if runner is None and self.config.option.apigen: from py.__.apigen.tracer.docstorage import DocStorage @@ -206,7 +238,7 @@ #elif runner is None: # runner = box_runner elif runner is None: - runner = plain_runner + runner = RunnerPolicy[session_options.runner_policy] keyword = self.config.option.keyword Added: py/dist/py/test/rsession/testing/test_config.py ============================================================================== --- (empty file) +++ py/dist/py/test/rsession/testing/test_config.py Mon Nov 13 12:22:13 2006 @@ -0,0 +1,21 @@ + +""" test session config options +""" + +import py +from py.__.test.rsession.rsession import session_options, SessionOptions + +def test_session_opts(): + tmpdir = py.test.ensuretemp("sessionopts") + tmpdir.ensure("conftest.py").write("""class SessionOptions: + max_tasks_per_node = 5 + """) + tmp2 = py.test.ensuretemp("xxx") + args = [str(tmpdir)] + config, args = py.test.Config.parse(args) + session_options.bind_config(config) + assert session_options.max_tasks_per_node == 5 + config, args = py.test.Config.parse([str(tmp2)]) + session_options.bind_config(config) + assert session_options.max_tasks_per_node == \ + SessionOptions.defaults['max_tasks_per_node'] From fijal at codespeak.net Mon Nov 13 13:20:16 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 13:20:16 +0100 (CET) Subject: [py-svn] r34555 - in py/dist/py/test/rsession: . testing Message-ID: <20061113122016.3383310190@code0.codespeak.net> Author: fijal Date: Mon Nov 13 13:20:13 2006 New Revision: 34555 Modified: py/dist/py/test/rsession/box.py py/dist/py/test/rsession/master.py py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/slave.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 py/dist/py/test/rsession/testing/test_slave.py Log: A lot of refactoring of SessionOptions. Now nice level should work. Modified: py/dist/py/test/rsession/box.py ============================================================================== --- py/dist/py/test/rsession/box.py (original) +++ py/dist/py/test/rsession/box.py Mon Nov 13 13:20:13 2006 @@ -8,7 +8,7 @@ import sys import marshal -NICE_LEVEL = 0 # XXX make it a conftest option +#NICE_LEVEL = 0 # XXX make it a conftest option PYTESTSTDOUT = "pyteststdout" PYTESTSTDERR = "pyteststderr" @@ -55,12 +55,14 @@ self.PYTESTRETVAL = tempdir.join('retval') self.PYTESTSTDOUT = tempdir.join('stdout') self.PYTESTSTDERR = tempdir.join('stderr') + + nice_level = py.test.remote.nice_level pid = os.fork() if pid: self.parent() else: try: - outcome = self.children() + outcome = self.children(nice_level) except: excinfo = py.code.ExceptionInfo() print "Internal box error" @@ -73,7 +75,7 @@ os._exit(0) return pid - def children(self): + def children(self, nice_level): # right now we need to call a function, but first we need to # map all IO that might happen # make sure sys.stdout points to file descriptor one @@ -88,8 +90,9 @@ os.dup2(fdstderr, 2) retvalf = self.PYTESTRETVAL.open("w") try: - if NICE_LEVEL: - os.nice(NICE_LEVEL) + from py.__.test.rsession.rsession import remote_options + if nice_level: + os.nice(nice_level) retval = self.fun(*self.args, **self.kwargs) retvalf.write(marshal.dumps(retval)) finally: Modified: py/dist/py/test/rsession/master.py ============================================================================== --- py/dist/py/test/rsession/master.py (original) +++ py/dist/py/test/rsession/master.py Mon Nov 13 13:20:13 2006 @@ -43,7 +43,7 @@ break waiter() -def setup_slave(gateway, pkgpath, options={}): +def setup_slave(gateway, pkgpath, options): from py.__.test.rsession import slave import os ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()"))) Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Nov 13 13:20:13 2006 @@ -30,12 +30,13 @@ self.d[item] = val # XXX: Must be initialised somehow -remote_options = RemoteOptions({'we_are_remote':False, 'exitfirst':False}) - +remote_options = RemoteOptions({'we_are_remote':False}) + class SessionOptions: defaults = { 'max_tasks_per_node' : 15, - 'runner_policy' : 'plain_runner' + 'runner_policy' : 'plain_runner', + 'nice_level' : 0, } config = None @@ -51,6 +52,20 @@ 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 + try: + ses_opt = self.config.getinitialvalue('SessionOptions').__dict__ + except ValueError: + ses_opt = self.defaults + for key in self.defaults: + try: + val = getattr(ses_opt, key) + except AttributeError: + val = self.defaults[key] + remote_options[key] = val def __repr__(self): return "" % self.config @@ -173,11 +188,9 @@ except: remotepython = None - rem_options = RemoteOptions({}) - rem_options['nomagic'] = self.config.option.nomagic - rem_options['exitfirst'] = self.config.option.exitfirst + session_options.bind_config(self.config) nodes = init_hosts(reporter, sshhosts, directories, pkgdir, - rsync_roots, remotepython, remote_options=rem_options.d, + rsync_roots, remotepython, remote_options=remote_options.d, optimise_localhost=self.optimise_localhost) reporter(report.RsyncFinished()) @@ -189,7 +202,6 @@ yield y itemgenerator = itemgen() - session_options.bind_config(self.config) dispatch_loop(nodes, itemgenerator, checkfun) teardown_hosts(reporter, [node.channel for node in nodes], nodes, exitfirst=self.config.option.exitfirst) Modified: py/dist/py/test/rsession/slave.py ============================================================================== --- py/dist/py/test/rsession/slave.py (original) +++ py/dist/py/test/rsession/slave.py Mon Nov 13 13:20:13 2006 @@ -64,9 +64,6 @@ nextitem = receive() def setup(): - default_options = {'nomagic':False} # XXX should come from somewhere else - # but I don't want to mess with conftest at this point - import os, sys pkgdir = channel.receive() # path is ready options = channel.receive() # options stuff, should be dictionary @@ -76,9 +73,6 @@ sys.path.insert(0, basedir) import py options['we_are_remote'] = True - for opt, val in default_options.items(): - if opt not in options: - options[opt] = val from py.__.test.rsession.rsession import RemoteOptions py.test.remote = RemoteOptions(options) # XXX the following assumes that py lib is there, a bit 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 Mon Nov 13 13:20:13 2006 @@ -11,6 +11,10 @@ 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/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 Nov 13 13:20:13 2006 @@ -13,8 +13,13 @@ 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 def setup_module(mod): + # bind an empty config + config, args = py.test.Config.parse([]) + session_options.bind_config(config) + #assert not remote_options.exitfirst mod.pkgdir = py.path.local(py.__file__).dirpath() class DummyChannel(object): @@ -70,7 +75,7 @@ def test_slave_setup(): gw = py.execnet.PopenGateway() - channel = setup_slave(gw, pkgdir) + channel = setup_slave(gw, pkgdir, remote_options.d) channel.send(funcpass_spec) output = ReprOutcome(channel.receive()) assert output.passed @@ -90,7 +95,7 @@ def open_gw(): gw = py.execnet.PopenGateway() - channel = setup_slave(gw, pkgdir) + channel = setup_slave(gw, pkgdir, remote_options.d) mn = MasterNode(channel, simple_report) return mn 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 Nov 13 13:20:13 2006 @@ -4,7 +4,8 @@ import py from py.__.test.rsession import report -from py.__.test.rsession.rsession import RSession, parse_directories +from py.__.test.rsession.rsession import RSession, parse_directories,\ + session_options, remote_options from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ @@ -185,8 +186,10 @@ setup_events = [] teardown_events = [] + config, args = py.test.Config.parse([]) + session_options.bind_config(config) nodes = init_hosts(setup_events.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"], optimise_localhost=False, remote_options={'exitfirst':False}) + rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -209,8 +212,10 @@ hosts = ['localhost'] allevents = [] + config, args = py.test.Config.parse([]) + session_options.bind_config(config) nodes = init_hosts(allevents.append, hosts, 'pytesttest', pkgdir, - rsync_roots=["py"], optimise_localhost=False, remote_options={'exitfirst':False}) + rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping @@ -248,8 +253,12 @@ """ allevents = [] hosts = ['localhost'] + 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, - rsync_roots=["py"], remote_options={'custom':'custom', 'exitfirst':False}, + rsync_roots=["py"], remote_options=d, optimise_localhost=False) rootcol = py.test.collect.Directory(pkgdir.dirpath()) 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 Mon Nov 13 13:20:13 2006 @@ -11,7 +11,10 @@ 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, args = py.test.Config.parse([]) + session_options.bind_config(config) # ---------------------------------------------------------------------- # inlined testing functions used below @@ -109,8 +112,7 @@ slave_main(q.pop, res.append, str(rootdir)) assert len(res) == 2 res_repr = [ReprOutcome(r) for r in res] - assert (not res_repr[0].passed and res_repr[1].passed) or \ - (not res_repr[1].passed and res_repr[0].passed) + assert not res_repr[0].passed and res_repr[1].passed def test_slave_run_different_stuff(): node = gettestnode() @@ -127,7 +129,8 @@ if self.count == 0: retval = str(tmp) elif self.count == 1: - retval = {} + from py.__.test.rsession.rsession import remote_options + retval = remote_options.d else: raise NotImplementedError("mora data") self.count += 1 @@ -152,7 +155,8 @@ if self.count == 0: retval = str(x.dirpath()) elif self.count == 1: - retval = {} + from py.__.test.rsession.rsession import remote_options + retval = remote_options.d else: raise NotImplementedError("mora data") self.count += 1 From fijal at codespeak.net Mon Nov 13 13:53:18 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 13:53:18 +0100 (CET) Subject: [py-svn] r34557 - in py/dist/py/test/rsession: . testing Message-ID: <20061113125318.BCB8D1018E@code0.codespeak.net> Author: fijal Date: Mon Nov 13 13:53:16 2006 New Revision: 34557 Modified: py/dist/py/test/rsession/rsession.py py/dist/py/test/rsession/slave.py py/dist/py/test/rsession/testing/test_config.py py/dist/py/test/rsession/testing/test_master.py py/dist/py/test/rsession/testing/test_rsession.py Log: Some fixes of options and some tests for that. Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Nov 13 13:53:16 2006 @@ -62,8 +62,8 @@ ses_opt = self.defaults for key in self.defaults: try: - val = getattr(ses_opt, key) - except AttributeError: + val = ses_opt[key] + except KeyError: val = self.defaults[key] remote_options[key] = val Modified: py/dist/py/test/rsession/slave.py ============================================================================== --- py/dist/py/test/rsession/slave.py (original) +++ py/dist/py/test/rsession/slave.py Mon Nov 13 13:53:16 2006 @@ -43,7 +43,6 @@ while 1: nextitem = receive() if nextitem is None: - #py.test.Function.state.teardown_all() break try: node = getnode(nextitem) 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 Mon Nov 13 13:53:16 2006 @@ -3,18 +3,21 @@ """ import py -from py.__.test.rsession.rsession import session_options, SessionOptions +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, args = py.test.Config.parse(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)]) session_options.bind_config(config) assert session_options.max_tasks_per_node == \ 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 Nov 13 13:53:16 2006 @@ -18,6 +18,7 @@ def setup_module(mod): # bind an empty config config, args = py.test.Config.parse([]) + config.options.max_tasks_per_node = 10 session_options.bind_config(config) #assert not remote_options.exitfirst mod.pkgdir = py.path.local(py.__file__).dirpath() 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 Nov 13 13:53:16 2006 @@ -279,6 +279,29 @@ assert len(passed) == 2 * len(nodes) assert len(skipped) == 0 assert len(events) == len(passed) + + def test_nice_level(self): + """ Tests if nice level behaviour is ok + """ + allevents = [] + hosts = ['localhost'] + tmpdir = py.test.ensuretemp("nice") + tmpdir.ensure("__init__.py") + tmpdir.ensure("conftest.py").write("""disthosts = ['localhost']""") + tmpdir.ensure("test_one.py").write("""def test_nice(): + import os + assert os.nice(0) == 10 + """) + + config, args = py.test.Config.parse([str(tmpdir)]) + config.option.nice_level = 10 + rsession = RSession(config) + allevents = [] + rsession.main(args, 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 TestDirectories(object): def test_simple_parse(self): From fijal at codespeak.net Mon Nov 13 14:00:19 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 14:00:19 +0100 (CET) Subject: [py-svn] r34558 - py/dist/py/documentation Message-ID: <20061113130019.06DA410193@code0.codespeak.net> Author: fijal Date: Mon Nov 13 14:00:17 2006 New Revision: 34558 Modified: py/dist/py/documentation/test.txt Log: Updated documentation a bit. Modified: py/dist/py/documentation/test.txt ============================================================================== --- py/dist/py/documentation/test.txt (original) +++ py/dist/py/documentation/test.txt Mon Nov 13 14:00:17 2006 @@ -733,20 +733,29 @@ The options are: -* disthosts - a list of ssh addresses (including a specific path if it +* `disthosts` - 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_remotepython - the remote python to run. +* `distrsync_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_remotepython = 'python2.4' + class SessionOptions: + nice_level = 10 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. + Development Notes ----------------- From fijal at codespeak.net Mon Nov 13 17:02:17 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 17:02:17 +0100 (CET) Subject: [py-svn] r34572 - py/dist/py/test/rsession/testing Message-ID: <20061113160217.C1D09101B0@code0.codespeak.net> Author: fijal Date: Mon Nov 13 17:02:15 2006 New Revision: 34572 Modified: py/dist/py/test/rsession/testing/test_master.py py/dist/py/test/rsession/testing/test_web.py Log: Fixed tests. 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 Nov 13 17:02:15 2006 @@ -18,7 +18,7 @@ def setup_module(mod): # bind an empty config config, args = py.test.Config.parse([]) - config.options.max_tasks_per_node = 10 + config.option.max_tasks_per_node = 10 session_options.bind_config(config) #assert not remote_options.exitfirst mod.pkgdir = py.path.local(py.__file__).dirpath() 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 Mon Nov 13 17:02:15 2006 @@ -15,8 +15,8 @@ py.test.skip("No PyPy detected") def test_js_generate(): - py.test.skip("XXX") from py.__.test.rsession import webjs from py.__.test.rsession.web import FUNCTION_LIST source = rpython2javascript(webjs, FUNCTION_LIST, Options) + assert source From fijal at codespeak.net Mon Nov 13 17:02:43 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 17:02:43 +0100 (CET) Subject: [py-svn] r34574 - py/dist/py/test/rsession Message-ID: <20061113160243.170CA101B7@code0.codespeak.net> Author: fijal Date: Mon Nov 13 17:02:41 2006 New Revision: 34574 Modified: py/dist/py/test/rsession/outcome.py py/dist/py/test/rsession/reporter.py Log: Added short/no traceback outcome. Modified: py/dist/py/test/rsession/outcome.py ============================================================================== --- py/dist/py/test/rsession/outcome.py (original) +++ py/dist/py/test/rsession/outcome.py Mon Nov 13 17:02:41 2006 @@ -7,6 +7,7 @@ # internal bug. import sys +import py class Outcome(object): def __init__(self, setupfailure=False, excinfo=None, skipped=None, @@ -31,7 +32,10 @@ relline = lineno - tb_entry.frame.code.firstlineno path = str(tb_entry.path) try: - source = str(tb_entry.getsource()) + if py.test.remote.tbstyle == 'long': + source = str(tb_entry.getsource()) + else: + source = str(tb_entry.getsource()).split("\n")[relline] except: source = "" return (relline, lineno, source, path) Modified: py/dist/py/test/rsession/reporter.py ============================================================================== --- py/dist/py/test/rsession/reporter.py (original) +++ py/dist/py/test/rsession/reporter.py Mon Nov 13 17:02:41 2006 @@ -115,8 +115,7 @@ self.out.line("empty traceback from item %r" % (item,)) return #handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle) - handler = self.repr_failure_tblong - handler(item, excinfo, traceback) + self.repr_traceback(item, excinfo, traceback) if outcome.stdout: self.out.sep('-', " Captured process stdout: ") self.out.write(outcome.stdout) @@ -134,11 +133,16 @@ self.out.sep('-', " Captured process stderr: ") self.out.write(outcome.stderr) - def repr_failure_tblong(self, item, excinfo, traceback): - 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)) + 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)) + self.repr_source(entry.relline, str(entry.source)) + 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): From fijal at codespeak.net Mon Nov 13 19:34:34 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 19:34:34 +0100 (CET) Subject: [py-svn] r34577 - py/dist/py/test Message-ID: <20061113183434.716DD101AD@code0.codespeak.net> Author: fijal Date: Mon Nov 13 19:34:32 2006 New Revision: 34577 Modified: py/dist/py/test/collect.py Log: Add generic __ne__. Modified: py/dist/py/test/collect.py ============================================================================== --- py/dist/py/test/collect.py (original) +++ py/dist/py/test/collect.py Mon Nov 13 19:34:32 2006 @@ -101,6 +101,9 @@ return self.name == other.name and self.parent == other.parent except AttributeError: return False + + def __ne__(self, other): + return not self == other def __cmp__(self, other): s1 = self.getsortvalue() From fijal at codespeak.net Mon Nov 13 19:55:08 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 19:55:08 +0100 (CET) Subject: [py-svn] r34578 - in py/dist/py: . apigen/tracer path test/rsession Message-ID: <20061113185508.9325D101B5@code0.codespeak.net> Author: fijal Date: Mon Nov 13 19:55:04 2006 New Revision: 34578 Modified: py/dist/py/apigen/tracer/description.py py/dist/py/conftest.py py/dist/py/path/common.py py/dist/py/test/rsession/rsession.py Log: Moved rest generation out of py.test into conftest. This is now local to pylib. Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Mon Nov 13 19:55:04 2006 @@ -253,15 +253,23 @@ 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 = copy.deepcopy(d) - except: c = {} for k, v in d.iteritems(): - try: - c[k] = copy.deepcopy(v) - except: - c[k] = v + c[k] = v + except: + # cannot perform this + c = d return c def consider_end_locals(self, frame): Modified: py/dist/py/conftest.py ============================================================================== --- py/dist/py/conftest.py (original) +++ py/dist/py/conftest.py Mon Nov 13 19:55:04 2006 @@ -24,3 +24,14 @@ action="store", dest="sshtarget", default=None, 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, DirWriter + RestGen(ds, DirectPaste(), DirWriter("/tmp/output")).write() + write_docs = staticmethod(write_docs) Modified: py/dist/py/path/common.py ============================================================================== --- py/dist/py/path/common.py (original) +++ py/dist/py/path/common.py Mon Nov 13 19:55:04 2006 @@ -124,10 +124,12 @@ """ if not isinstance(relpath, (str, PathBase)): raise TypeError("%r: not a string or path object" %(relpath,)) - strrelpath = str(relpath) - if strrelpath and strrelpath[-1] != self.sep: - strrelpath += self.sep - strself = str(self) + strrelpath = str(relpath) + if strrelpath and strrelpath[-1] != self.sep: + strrelpath += self.sep + #assert strrelpath[-1] == self.sep + #assert strrelpath[-2] != self.sep + strself = str(self) if strself.startswith(strrelpath): return strself[len(strrelpath):] return "" Modified: py/dist/py/test/rsession/rsession.py ============================================================================== --- py/dist/py/test/rsession/rsession.py (original) +++ py/dist/py/test/rsession/rsession.py Mon Nov 13 19:55:04 2006 @@ -237,12 +237,15 @@ session_options.bind_config(self.config) if runner is None and self.config.option.apigen: - from py.__.apigen.tracer.docstorage import DocStorage from py.__.apigen.tracer.tracer import Tracer # XXX module = py #module = __import__(str(pkgdir.join('__init__.py'))) - self.docstorage = DocStorage().from_pkg(module) + 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 and (self.config.option.usepdb or self.config.option.nocapture): @@ -275,6 +278,8 @@ py.magic.revoke(assertion=1) if self.config.option.apigen: - from py.__.apigen.rest.genrest import DirectPaste, RestGen, DirWriter - RestGen(self.docstorage, DirectPaste(), DirWriter("/tmp/output")).write() - + try: + self.config.getinitialvalue('ApiGen').write_docs(self.docstorage) + except ValueError: + raise NotImplementedError("Cannot create docs - didn't " + "provided way of doing that in conftest") From fijal at codespeak.net Mon Nov 13 19:59:39 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 19:59:39 +0100 (CET) Subject: [py-svn] r34579 - py/dist/py/apigen/tracer Message-ID: <20061113185939.90981101B9@code0.codespeak.net> Author: fijal Date: Mon Nov 13 19:59:38 2006 New Revision: 34579 Modified: py/dist/py/apigen/tracer/description.py Log: Get rid of very-bare except. Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Mon Nov 13 19:59:38 2006 @@ -267,6 +267,8 @@ c = {} for k, v in d.iteritems(): c[k] = v + except (KeyboardInterrupt, SystemExit): + raise except: # cannot perform this c = d From fijal at codespeak.net Mon Nov 13 20:45:17 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 20:45:17 +0100 (CET) Subject: [py-svn] r34580 - in py/dist/py/apigen/tracer: . testing Message-ID: <20061113194517.1B7CD101BC@code0.codespeak.net> Author: fijal Date: Mon Nov 13 20:45:15 2006 New Revision: 34580 Modified: py/dist/py/apigen/tracer/description.py py/dist/py/apigen/tracer/docstorage.py py/dist/py/apigen/tracer/testing/test_docgen.py Log: Added some heuristics which generate a bit more acurate info. Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Mon Nov 13 20:45:15 2006 @@ -106,6 +106,8 @@ return self.code is other.code if isinstance(other, types.CodeType): return self.code is other + if isinstance(other, tuple) and len(other) == 2: + return self.code == other return False def __ne__(self, other): @@ -186,10 +188,10 @@ except: # XXX UUuuuu bare except here. What can it really rise??? try: hash(self.pyobj) - return self.pyobj + result = self.pyobj except: - return self - return result + result = self + return (result, self.pyobj) code = property(getcode) def consider_call(self, inputcells): @@ -204,10 +206,14 @@ pass # we *know* what return value we do have def consider_start_locals(self, frame): - pass + if '__init__' in self.fields: + md = self.fields['__init__'] + md.consider_start_locals(frame) def consider_end_locals(self, frame): - pass + if '__init__' in self.fields: + md = self.fields['__init__'] + md.consider_end_locals(frame) def consider_call_site(self, frame, cut_frame): self.fields['__init__'].consider_call_site(frame, cut_frame) Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Mon Nov 13 20:45:15 2006 @@ -17,7 +17,7 @@ """ def consider_call(self, frame, caller_frame, upward_cut_frame=None): assert isinstance(frame, py.code.Frame) - desc = self.find_desc(frame.code) + desc = self.find_desc(frame.code, frame.raw.f_locals) if desc: self.generalize_args(desc, frame) desc.consider_call_site(caller_frame, upward_cut_frame) @@ -33,13 +33,18 @@ def consider_return(self, frame, arg): assert isinstance(frame, py.code.Frame) - desc = self.find_desc(frame.code) + desc = self.find_desc(frame.code, frame.raw.f_locals) if desc: self.generalize_retval(desc, arg) desc.consider_end_locals(frame) - def find_desc(self, code): - return self.desc_cache.get(code.raw, None) + def find_desc(self, code, locals): + if code.name == '__init__': + # argh, very fragile specialcasing + key = (code.raw, locals[code.raw.co_varnames[0]].__class__) + else: + key = code.raw + return self.desc_cache.get(key, None) #for desc in self.descs.values(): # if desc.has_code(frame.code.raw): # return desc 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 Mon Nov 13 20:45:15 2006 @@ -177,3 +177,20 @@ desc = ds.descs['testclass'] methdesc = desc.fields['bar'] assert methdesc.get_local_changes() == {} + +def test_multiple_classes_with_same_init(): + class A: + def __init__(self, x): + self.x = x + + class B(A): + pass + + ds = DocStorage().from_dict({'A':A, 'B':B}) + t = Tracer(ds) + t.start_tracing() + c = A(3) + d = B(4) + t.end_tracing() + assert len(ds.descs['A'].fields['__init__'].call_sites) == 1 + assert len(ds.descs['B'].fields['__init__'].call_sites) == 1 From fijal at codespeak.net Mon Nov 13 22:37:51 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 13 Nov 2006 22:37:51 +0100 (CET) Subject: [py-svn] r34588 - py/dist/py/apigen/tracer Message-ID: <20061113213751.8F061101C8@code0.codespeak.net> Author: fijal Date: Mon Nov 13 22:37:49 2006 New Revision: 34588 Modified: py/dist/py/apigen/tracer/tracer.py Log: Fixed stuff for greenlets. Modified: py/dist/py/apigen/tracer/tracer.py ============================================================================== --- py/dist/py/apigen/tracer/tracer.py (original) +++ py/dist/py/apigen/tracer/tracer.py Mon Nov 13 22:37:49 2006 @@ -30,9 +30,12 @@ frame = py.code.Frame(frame) if event == 'call': assert arg is None - self.docstorage.consider_call(frame, + try: + self.docstorage.consider_call(frame, py.code.Frame(sys._getframe(2)), self.frame) + except ValueError: + self.docstorage.consider_call(frame, None, self.frame) elif event == 'return': self.docstorage.consider_return(frame, arg) return self._tracer From fijal at codespeak.net Tue Nov 14 00:30:02 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 14 Nov 2006 00:30:02 +0100 (CET) Subject: [py-svn] r34590 - py/dist/py/apigen/rest/testing Message-ID: <20061113233002.3AC59100CC@code0.codespeak.net> Author: fijal Date: Tue Nov 14 00:30:01 2006 New Revision: 34590 Modified: py/dist/py/apigen/rest/testing/test_rest.py Log: Fixed test, which was actually broken. 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 Nov 14 00:30:01 2006 @@ -181,8 +181,8 @@ 'method_SomeSubClass.method.txt', 'module_Unknown module.txt', 'traceback_SomeClass.__init__.0.txt', - 'traceback_SomeClass.__init__.1.txt', 'traceback_SomeClass.method.0.txt', + 'traceback_SomeSubClass.__init__.0.txt', 'traceback_fun.0.txt', 'traceback_fun.1.txt', ] @@ -211,7 +211,7 @@ 'module_somemodule.txt', 'module_someothermodule.txt', 'traceback_somemodule.SomeClass.__init__.0.txt', - 'traceback_somemodule.SomeClass.__init__.1.txt', + 'traceback_someothermodule.SomeSubClass.__init__.0.txt', 'traceback_someothermodule.SomeSubClass.method.0.txt', 'traceback_someothermodule.fun.0.txt', 'traceback_someothermodule.fun.1.txt', From fijal at codespeak.net Tue Nov 14 10:50:06 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 14 Nov 2006 10:50:06 +0100 (CET) Subject: [py-svn] r34591 - in py/dist/py/apigen/tracer: . testing Message-ID: <20061114095006.3B11D101D0@code0.codespeak.net> Author: fijal Date: Tue Nov 14 10:50:03 2006 New Revision: 34591 Modified: py/dist/py/apigen/tracer/description.py py/dist/py/apigen/tracer/docstorage.py py/dist/py/apigen/tracer/testing/test_docgen.py py/dist/py/apigen/tracer/tracer.py Log: Added exception handling code. Modified: py/dist/py/apigen/tracer/description.py ============================================================================== --- py/dist/py/apigen/tracer/description.py (original) +++ py/dist/py/apigen/tracer/description.py Tue Nov 14 10:50:03 2006 @@ -124,6 +124,7 @@ self.keep_frames = kwargs.get('keep_frames', False) self.frame_copier = kwargs.get('frame_copier', lambda x:x) self.retval = model.s_ImpossibleValue + self.exceptions = {} def consider_call(self, inputcells): for cell_num, cell in enumerate(inputcells): @@ -136,6 +137,9 @@ cs = cut_stack(stack, frame, cut_frame) self.call_sites[cs] = cs + def consider_exception(self, exc, value): + self.exceptions[exc] = True + def get_call_sites(self): # convinient accessor for various data which we keep there if not self.keep_frames: @@ -204,6 +208,14 @@ def consider_return(self, arg): pass # we *know* what return value we do have + + def consider_exception(self, exc, value): + if '__init__' in self.fields: + md = self.fields['__init__'] + else: + md = MethodDesc(self.name + '.__init__', self.pyobj.__init__) + self.fields['__init__'] = md + md.consider_exception(exc, value) def consider_start_locals(self, frame): if '__init__' in self.fields: Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Tue Nov 14 10:50:03 2006 @@ -37,6 +37,12 @@ if desc: self.generalize_retval(desc, arg) desc.consider_end_locals(frame) + + def consider_exception(self, frame, arg): + desc = self.find_desc(frame.code, frame.raw.f_locals) + if desc: + exc_class, value, _ = arg + desc.consider_exception(exc_class, value) def find_desc(self, code, locals): if code.name == '__init__': 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 Nov 14 10:50:03 2006 @@ -194,3 +194,25 @@ t.end_tracing() assert len(ds.descs['A'].fields['__init__'].call_sites) == 1 assert len(ds.descs['B'].fields['__init__'].call_sites) == 1 + +def test_exception_raise(): + def x(): + 1/0 + + def y(): + try: + x() + except ZeroDivisionError: + pass + + def z(): + y() + + ds = DocStorage().from_dict({'x':x, 'y':y, 'z':z}) + t = Tracer(ds) + t.start_tracing() + z() + t.end_tracing() + assert ds.descs['x'].exceptions.keys() == [ZeroDivisionError] + assert ds.descs['y'].exceptions.keys() == [ZeroDivisionError] + assert ds.descs['z'].exceptions.keys() == [] Modified: py/dist/py/apigen/tracer/tracer.py ============================================================================== --- py/dist/py/apigen/tracer/tracer.py (original) +++ py/dist/py/apigen/tracer/tracer.py Tue Nov 14 10:50:03 2006 @@ -38,6 +38,8 @@ self.docstorage.consider_call(frame, None, self.frame) elif event == 'return': self.docstorage.consider_return(frame, arg) + elif event == 'exception': + self.docstorage.consider_exception(frame, arg) return self._tracer def start_tracing(self): From fijal at codespeak.net Tue Nov 14 10:58:03 2006 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 14 Nov 2006 10:58:03 +0100 (CET) Subject: [py-svn] r34592 - in py/dist/py/apigen: rest rest/testing tracer Message-ID: <20061114095803.02C39101D2@code0.codespeak.net> Author: fijal Date: Tue Nov 14 10:58:01 2006 New Revision: 34592 Modified: py/dist/py/apigen/rest/genrest.py py/dist/py/apigen/rest/testing/test_rest.py py/dist/py/apigen/tracer/docstorage.py Log: Added generation of exception info into rest. Modified: py/dist/py/apigen/rest/genrest.py ============================================================================== --- py/dist/py/apigen/rest/genrest.py (original) +++ py/dist/py/apigen/rest/genrest.py Tue Nov 14 10:58:01 2006 @@ -335,6 +335,11 @@ for k, changeset in local_changes.iteritems(): lst.append(ListItem('%s: %s' % (k, ', '.join(changeset)))) + lst.append(Paragraph('Exceptions that might appear in function body:')) + for exc in self.dsa.get_function_exceptions(functionname): + lst.append(ListItem(exc.__name__)) + # XXX: right now we leave it alone + # XXX missing implementation of dsa.get_function_location() #filename, lineno = self.dsa.get_function_location(functionname) #linkname, linktarget = self.linkgen.getlink(filename, lineno) 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 Nov 14 10:58:01 2006 @@ -346,3 +346,23 @@ assert call_point != -1 assert source.find("x \:\: ") < call_point self.check_rest(tempdir) + + def test_exc_raising(self): + def x(): + try: + 1/0 + except: + pass + + descs = {'x':x} + ds = DocStorage().from_dict(descs) + t = Tracer(ds) + t.start_tracing() + x() + t.end_tracing() + lg = DirectPaste() + tempdir = temppath.ensure("exc_raising", dir=True) + r = RestGen(ds, lg, DirWriter(tempdir)) + r.write() + source = tempdir.join('function_x.txt').open().read() + assert source.find('ZeroDivisionError') < source.find('Call sites\:') Modified: py/dist/py/apigen/tracer/docstorage.py ============================================================================== --- py/dist/py/apigen/tracer/docstorage.py (original) +++ py/dist/py/apigen/tracer/docstorage.py Tue Nov 14 10:58:01 2006 @@ -251,3 +251,6 @@ except AttributeError: pass return retval + + def get_function_exceptions(self, name): + return sorted(self.ds.descs[name].exceptions.keys()) From guido at codespeak.net Tue Nov 14 13:54:05 2006 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 14 Nov 2006 13:54:05 +0100 (CET) Subject: [py-svn] r34595 - in py/dist/py/rst: . testing Message-ID: <20061114125405.33F95101B6@code0.codespeak.net> Author: guido Date: Tue Nov 14 13:54:03 2006 New Revision: 34595 Modified: py/dist/py/rst/rst.py py/dist/py/rst/testing/test_rst.py Log: Added support for ReST directives (code I wrote on the sprint and during the train ride back), changed LiteralBlock so it subclasses from AbstractText (no need to allow anything but plain text in there, only makes it harder), added support for nested lists. Modified: py/dist/py/rst/rst.py ============================================================================== --- py/dist/py/rst/rst.py (original) +++ py/dist/py/rst/rst.py Tue Nov 14 13:54:03 2006 @@ -34,6 +34,8 @@ class_list = [parent_cls] else: class_list = parent_cls + if obj.allow_nesting: + class_list.append(obj) for _class in class_list: if not _class.allowed_child: @@ -49,6 +51,7 @@ __metaclass__ = AbstractMetaclass parentclass = None # this exists to allow parent to know what # children can exist + allow_nesting = False allowed_child = {} defaults = {} @@ -185,16 +188,6 @@ class SubParagraph(Paragraph): indent = " " -class LiteralBlock(Paragraph): - indent = " " - sep = "" - - def text(self): - all_txt = AbstractNode.text(self) - all_txts = all_txt.split('\n') - return '::\n\n%s' % ("\n".join([self.indent + i for i in - all_txts]),) - class Title(Paragraph): parentclass = Rest belowchar = "" @@ -233,6 +226,18 @@ text = escape(self._text) return self._reg_whitespace.split(text) +class LiteralBlock(AbstractText): + parentclass = Rest + start = '::\n\n' + + def text(self): + text = self.escape(self._text).split('\n') + print text + for i, line in enumerate(text): + if line.strip(): + text[i] = ' %s' % (line,) + return self.start + '\n'.join(text) + class Em(AbstractText): start = "*" end = "*" @@ -258,25 +263,43 @@ raise NotImplemented('XXX') class ListItem(Paragraph): - item_char = "*" + allow_nesting = True + item_chars = '*+-' def text(self): - oldindent = self.indent - self.indent = oldindent + ' ' - try: - txt = Paragraph.text(self) - finally: - self.indent = oldindent - txt = self.item_char + txt[1:] - return txt + idepth = self.get_indent_depth() + indent = self.indent + (idepth + 1) * ' ' + txt = [] + for child in self.children: + if isinstance(child, AbstractText): + p = Paragraph(child, indent=indent) + txt.append(p.text()) + else: + txt.append(child.text()) + txt = '\n'.join(txt) + ret = [] + if idepth: + ret.append('\n') + item_char = self.item_chars[idepth] + ret += [indent[2:], item_char, ' ', txt[len(indent):]] + print repr(ret) + return ''.join(ret) + + def get_indent_depth(self): + depth = 0 + current = self + while current.parent is not None: + depth += 1 + current = current.parent + return depth - 1 class OrderedListItem(ListItem): - item_char = "#." + item_chars = ("#.",) class DListItem(ListItem): - item_char = None + item_chars = None def __init__(self, term, definition, *args, **kwargs): - self.item_char = '%s\n ' % (term,) + self.item_chars = ('%s\n ' % (term,),) super(DListItem, self).__init__(definition, *args, **kwargs) class Link(AbstractText): @@ -307,3 +330,33 @@ def __init__(self, text, **kwargs): raise NotImplemented('XXX') +class Directive(Paragraph): + indent = ' ' + def __init__(self, name, *args, **options): + self.name = name + self.content = options.pop('content', []) + children = list(args) + super(Directive, self).__init__(*children) + self.options = options + + def text(self): + # XXX not very pretty... + namechunksize = len(self.name) + 2 + self.children.insert(0, Text('X' * namechunksize)) + txt = super(Directive, self).text() + txt = '.. %s::%s' % (self.name, txt[namechunksize + 3:],) + options = '\n'.join([' :%s: %s' % (k, v) for (k, v) in + self.options.iteritems()]) + if options: + txt += '\n%s' % (options,) + + if self.content: + txt += '\n' + for item in self.content: + assert item.parentclass == Rest, 'only top-level items allowed' + assert not item.indent + item.indent = ' ' + txt += '\n' + item.text() + + return txt + Modified: py/dist/py/rst/testing/test_rst.py ============================================================================== --- py/dist/py/rst/testing/test_rst.py (original) +++ py/dist/py/rst/testing/test_rst.py Tue Nov 14 13:54:03 2006 @@ -49,7 +49,7 @@ def test_escape_literal(): txt = LiteralBlock('*escape* ``test``').text() - assert txt == '::\n\n *escape* ``test``' + assert txt == '::\n\n *escape* ``test``' html = checkrest(txt) assert '>\n*escape* ``test``\n' in html @@ -153,8 +153,8 @@ :: - def fun(): - some + def fun(): + some Paragraph """ @@ -272,6 +272,18 @@ assert txt == expected checkrest(txt) +def test_nested_lists(): + expected = """\ +* foo + +* bar + + + baz +""" + txt = Rest(ListItem('foo'), ListItem('bar', ListItem('baz'))).text() + assert txt == expected + checkrest(txt) + def test_title_following_links_empty_line(): expected = """\ Foo, bar and `baz`_. @@ -312,3 +324,33 @@ py.test.raises(ValueError, 'Rest(Transition(), Paragraph("foo")).text()') py.test.raises(ValueError, 'Rest(Paragraph("foo"), Transition()).text()') +def test_directive_simple(): + txt = Rest(Directive('image', 'images/foo.png')).text() + asse